turbo-rails 1.5.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,7 @@
1
- (function() {
2
- if (window.Reflect === undefined || window.customElements === undefined || window.customElements.polyfillWrapFlushCallback) {
3
- return;
4
- }
5
- const BuiltInHTMLElement = HTMLElement;
6
- const wrapperForTheName = {
7
- HTMLElement: function HTMLElement() {
8
- return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
9
- }
10
- };
11
- window.HTMLElement = wrapperForTheName["HTMLElement"];
12
- HTMLElement.prototype = BuiltInHTMLElement.prototype;
13
- HTMLElement.prototype.constructor = HTMLElement;
14
- Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
15
- })();
16
-
1
+ /*!
2
+ Turbo 8.0.0
3
+ Copyright © 2024 37signals LLC
4
+ */
17
5
  (function(prototype) {
18
6
  if (typeof prototype.requestSubmit == "function") return;
19
7
  prototype.requestSubmit = function(submitter) {
@@ -44,7 +32,7 @@ const submittersByForm = new WeakMap;
44
32
  function findSubmitterFromClickTarget(target) {
45
33
  const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
46
34
  const candidate = element ? element.closest("input, button") : null;
47
- return (candidate === null || candidate === void 0 ? void 0 : candidate.type) == "submit" ? candidate : null;
35
+ return candidate?.type == "submit" ? candidate : null;
48
36
  }
49
37
 
50
38
  function clickCaptured(event) {
@@ -57,10 +45,13 @@ function clickCaptured(event) {
57
45
  (function() {
58
46
  if ("submitter" in Event.prototype) return;
59
47
  let prototype = window.Event.prototype;
60
- if ("SubmitEvent" in window && /Apple Computer/.test(navigator.vendor)) {
61
- prototype = window.SubmitEvent.prototype;
62
- } else if ("SubmitEvent" in window) {
63
- return;
48
+ if ("SubmitEvent" in window) {
49
+ const prototypeOfSubmitEvent = window.SubmitEvent.prototype;
50
+ if (/Apple Computer/.test(navigator.vendor) && !("submitter" in prototypeOfSubmitEvent)) {
51
+ prototype = prototypeOfSubmitEvent;
52
+ } else {
53
+ return;
54
+ }
64
55
  }
65
56
  addEventListener("click", clickCaptured, true);
66
57
  Object.defineProperty(prototype, "submitter", {
@@ -72,20 +63,19 @@ function clickCaptured(event) {
72
63
  });
73
64
  })();
74
65
 
75
- var FrameLoadingStyle;
76
-
77
- (function(FrameLoadingStyle) {
78
- FrameLoadingStyle["eager"] = "eager";
79
- FrameLoadingStyle["lazy"] = "lazy";
80
- })(FrameLoadingStyle || (FrameLoadingStyle = {}));
66
+ const FrameLoadingStyle = {
67
+ eager: "eager",
68
+ lazy: "lazy"
69
+ };
81
70
 
82
71
  class FrameElement extends HTMLElement {
72
+ static delegateConstructor=undefined;
73
+ loaded=Promise.resolve();
83
74
  static get observedAttributes() {
84
75
  return [ "disabled", "complete", "loading", "src" ];
85
76
  }
86
77
  constructor() {
87
78
  super();
88
- this.loaded = Promise.resolve();
89
79
  this.delegate = new FrameElement.delegateConstructor(this);
90
80
  }
91
81
  connectedCallback() {
@@ -118,6 +108,16 @@ class FrameElement extends HTMLElement {
118
108
  this.removeAttribute("src");
119
109
  }
120
110
  }
111
+ get refresh() {
112
+ return this.getAttribute("refresh");
113
+ }
114
+ set refresh(value) {
115
+ if (value) {
116
+ this.setAttribute("refresh", value);
117
+ } else {
118
+ this.removeAttribute("refresh");
119
+ }
120
+ }
121
121
  get loading() {
122
122
  return frameLoadingStyleFromString(this.getAttribute("loading") || "");
123
123
  }
@@ -155,8 +155,7 @@ class FrameElement extends HTMLElement {
155
155
  return this.ownerDocument === document && !this.isPreview;
156
156
  }
157
157
  get isPreview() {
158
- var _a, _b;
159
- return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.hasAttribute("data-turbo-preview");
158
+ return this.ownerDocument?.documentElement?.hasAttribute("data-turbo-preview");
160
159
  }
161
160
  }
162
161
 
@@ -183,8 +182,8 @@ function getAnchor(url) {
183
182
  }
184
183
  }
185
184
 
186
- function getAction(form, submitter) {
187
- const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formaction")) || form.getAttribute("action") || form.action;
185
+ function getAction$1(form, submitter) {
186
+ const action = submitter?.getAttribute("formaction") || form.getAttribute("action") || form.action;
188
187
  return expandURL(action);
189
188
  }
190
189
 
@@ -323,6 +322,14 @@ function dispatch(eventName, {target: target, cancelable: cancelable, detail: de
323
322
  return event;
324
323
  }
325
324
 
325
+ function nextRepaint() {
326
+ if (document.visibilityState === "hidden") {
327
+ return nextEventLoopTick();
328
+ } else {
329
+ return nextAnimationFrame();
330
+ }
331
+ }
332
+
326
333
  function nextAnimationFrame() {
327
334
  return new Promise((resolve => requestAnimationFrame((() => resolve()))));
328
335
  }
@@ -370,7 +377,7 @@ function uuid() {
370
377
  }
371
378
 
372
379
  function getAttribute(attributeName, ...elements) {
373
- for (const value of elements.map((element => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName)))) {
380
+ for (const value of elements.map((element => element?.getAttribute(attributeName)))) {
374
381
  if (typeof value == "string") return value;
375
382
  }
376
383
  return null;
@@ -456,21 +463,83 @@ function setMetaContent(name, content) {
456
463
  }
457
464
 
458
465
  function findClosestRecursively(element, selector) {
459
- var _a;
460
466
  if (element instanceof Element) {
461
- return element.closest(selector) || findClosestRecursively(element.assignedSlot || ((_a = element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host), selector);
467
+ return element.closest(selector) || findClosestRecursively(element.assignedSlot || element.getRootNode()?.host, selector);
468
+ }
469
+ }
470
+
471
+ function elementIsFocusable(element) {
472
+ const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
473
+ return !!element && element.closest(inertDisabledOrHidden) == null && typeof element.focus == "function";
474
+ }
475
+
476
+ function queryAutofocusableElement(elementOrDocumentFragment) {
477
+ return Array.from(elementOrDocumentFragment.querySelectorAll("[autofocus]")).find(elementIsFocusable);
478
+ }
479
+
480
+ async function around(callback, reader) {
481
+ const before = reader();
482
+ callback();
483
+ await nextAnimationFrame();
484
+ const after = reader();
485
+ return [ before, after ];
486
+ }
487
+
488
+ function doesNotTargetIFrame(anchor) {
489
+ if (anchor.hasAttribute("target")) {
490
+ for (const element of document.getElementsByName(anchor.target)) {
491
+ if (element instanceof HTMLIFrameElement) return false;
492
+ }
493
+ }
494
+ return true;
495
+ }
496
+
497
+ function findLinkFromClickTarget(target) {
498
+ return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
499
+ }
500
+
501
+ function getLocationForLink(link) {
502
+ return expandURL(link.getAttribute("href") || "");
503
+ }
504
+
505
+ function debounce(fn, delay) {
506
+ let timeoutId = null;
507
+ return (...args) => {
508
+ const callback = () => fn.apply(this, args);
509
+ clearTimeout(timeoutId);
510
+ timeoutId = setTimeout(callback, delay);
511
+ };
512
+ }
513
+
514
+ class LimitedSet extends Set {
515
+ constructor(maxSize) {
516
+ super();
517
+ this.maxSize = maxSize;
518
+ }
519
+ add(value) {
520
+ if (this.size >= this.maxSize) {
521
+ const iterator = this.values();
522
+ const oldestValue = iterator.next().value;
523
+ this.delete(oldestValue);
524
+ }
525
+ super.add(value);
462
526
  }
463
527
  }
464
528
 
465
- var FetchMethod;
529
+ const recentRequests = new LimitedSet(20);
466
530
 
467
- (function(FetchMethod) {
468
- FetchMethod[FetchMethod["get"] = 0] = "get";
469
- FetchMethod[FetchMethod["post"] = 1] = "post";
470
- FetchMethod[FetchMethod["put"] = 2] = "put";
471
- FetchMethod[FetchMethod["patch"] = 3] = "patch";
472
- FetchMethod[FetchMethod["delete"] = 4] = "delete";
473
- })(FetchMethod || (FetchMethod = {}));
531
+ const nativeFetch = window.fetch;
532
+
533
+ function fetchWithTurboHeaders(url, options = {}) {
534
+ const modifiedHeaders = new Headers(options.headers || {});
535
+ const requestUID = uuid();
536
+ recentRequests.add(requestUID);
537
+ modifiedHeaders.append("X-Turbo-Request-Id", requestUID);
538
+ return nativeFetch(url, {
539
+ ...options,
540
+ headers: modifiedHeaders
541
+ });
542
+ }
474
543
 
475
544
  function fetchMethodFromString(method) {
476
545
  switch (method.toLowerCase()) {
@@ -491,16 +560,81 @@ function fetchMethodFromString(method) {
491
560
  }
492
561
  }
493
562
 
563
+ const FetchMethod = {
564
+ get: "get",
565
+ post: "post",
566
+ put: "put",
567
+ patch: "patch",
568
+ delete: "delete"
569
+ };
570
+
571
+ function fetchEnctypeFromString(encoding) {
572
+ switch (encoding.toLowerCase()) {
573
+ case FetchEnctype.multipart:
574
+ return FetchEnctype.multipart;
575
+
576
+ case FetchEnctype.plain:
577
+ return FetchEnctype.plain;
578
+
579
+ default:
580
+ return FetchEnctype.urlEncoded;
581
+ }
582
+ }
583
+
584
+ const FetchEnctype = {
585
+ urlEncoded: "application/x-www-form-urlencoded",
586
+ multipart: "multipart/form-data",
587
+ plain: "text/plain"
588
+ };
589
+
494
590
  class FetchRequest {
495
- constructor(delegate, method, location, body = new URLSearchParams, target = null) {
496
- this.abortController = new AbortController;
497
- this.resolveRequestPromise = _value => {};
591
+ abortController=new AbortController;
592
+ #resolveRequestPromise=_value => {};
593
+ constructor(delegate, method, location, requestBody = new URLSearchParams, target = null, enctype = FetchEnctype.urlEncoded) {
594
+ const [url, body] = buildResourceAndBody(expandURL(location), method, requestBody, enctype);
498
595
  this.delegate = delegate;
499
- this.method = method;
500
- this.headers = this.defaultHeaders;
501
- this.body = body;
502
- this.url = location;
596
+ this.url = url;
503
597
  this.target = target;
598
+ this.fetchOptions = {
599
+ credentials: "same-origin",
600
+ redirect: "follow",
601
+ method: method,
602
+ headers: {
603
+ ...this.defaultHeaders
604
+ },
605
+ body: body,
606
+ signal: this.abortSignal,
607
+ referrer: this.delegate.referrer?.href
608
+ };
609
+ this.enctype = enctype;
610
+ }
611
+ get method() {
612
+ return this.fetchOptions.method;
613
+ }
614
+ set method(value) {
615
+ const fetchBody = this.isSafe ? this.url.searchParams : this.fetchOptions.body || new FormData;
616
+ const fetchMethod = fetchMethodFromString(value) || FetchMethod.get;
617
+ this.url.search = "";
618
+ const [url, body] = buildResourceAndBody(this.url, fetchMethod, fetchBody, this.enctype);
619
+ this.url = url;
620
+ this.fetchOptions.body = body;
621
+ this.fetchOptions.method = fetchMethod;
622
+ }
623
+ get headers() {
624
+ return this.fetchOptions.headers;
625
+ }
626
+ set headers(value) {
627
+ this.fetchOptions.headers = value;
628
+ }
629
+ get body() {
630
+ if (this.isSafe) {
631
+ return this.url.searchParams;
632
+ } else {
633
+ return this.fetchOptions.body;
634
+ }
635
+ }
636
+ set body(value) {
637
+ this.fetchOptions.body = value;
504
638
  }
505
639
  get location() {
506
640
  return this.url;
@@ -517,14 +651,19 @@ class FetchRequest {
517
651
  async perform() {
518
652
  const {fetchOptions: fetchOptions} = this;
519
653
  this.delegate.prepareRequest(this);
520
- await this.allowRequestToBeIntercepted(fetchOptions);
654
+ const event = await this.#allowRequestToBeIntercepted(fetchOptions);
521
655
  try {
522
656
  this.delegate.requestStarted(this);
523
- const response = await fetch(this.url.href, fetchOptions);
657
+ if (event.detail.fetchRequest) {
658
+ this.response = event.detail.fetchRequest.response;
659
+ } else {
660
+ this.response = fetchWithTurboHeaders(this.url.href, fetchOptions);
661
+ }
662
+ const response = await this.response;
524
663
  return await this.receive(response);
525
664
  } catch (error) {
526
665
  if (error.name !== "AbortError") {
527
- if (this.willDelegateErrorHandling(error)) {
666
+ if (this.#willDelegateErrorHandling(error)) {
528
667
  this.delegate.requestErrored(this, error);
529
668
  }
530
669
  throw error;
@@ -551,25 +690,13 @@ class FetchRequest {
551
690
  }
552
691
  return fetchResponse;
553
692
  }
554
- get fetchOptions() {
555
- var _a;
556
- return {
557
- method: FetchMethod[this.method].toUpperCase(),
558
- credentials: "same-origin",
559
- headers: this.headers,
560
- redirect: "follow",
561
- body: this.isSafe ? null : this.body,
562
- signal: this.abortSignal,
563
- referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
564
- };
565
- }
566
693
  get defaultHeaders() {
567
694
  return {
568
695
  Accept: "text/html, application/xhtml+xml"
569
696
  };
570
697
  }
571
698
  get isSafe() {
572
- return this.method === FetchMethod.get;
699
+ return isSafe(this.method);
573
700
  }
574
701
  get abortSignal() {
575
702
  return this.abortController.signal;
@@ -577,20 +704,22 @@ class FetchRequest {
577
704
  acceptResponseType(mimeType) {
578
705
  this.headers["Accept"] = [ mimeType, this.headers["Accept"] ].join(", ");
579
706
  }
580
- async allowRequestToBeIntercepted(fetchOptions) {
581
- const requestInterception = new Promise((resolve => this.resolveRequestPromise = resolve));
707
+ async #allowRequestToBeIntercepted(fetchOptions) {
708
+ const requestInterception = new Promise((resolve => this.#resolveRequestPromise = resolve));
582
709
  const event = dispatch("turbo:before-fetch-request", {
583
710
  cancelable: true,
584
711
  detail: {
585
712
  fetchOptions: fetchOptions,
586
713
  url: this.url,
587
- resume: this.resolveRequestPromise
714
+ resume: this.#resolveRequestPromise
588
715
  },
589
716
  target: this.target
590
717
  });
718
+ this.url = event.detail.url;
591
719
  if (event.defaultPrevented) await requestInterception;
720
+ return event;
592
721
  }
593
- willDelegateErrorHandling(error) {
722
+ #willDelegateErrorHandling(error) {
594
723
  const event = dispatch("turbo:fetch-request-error", {
595
724
  target: this.target,
596
725
  cancelable: true,
@@ -603,15 +732,38 @@ class FetchRequest {
603
732
  }
604
733
  }
605
734
 
735
+ function isSafe(fetchMethod) {
736
+ return fetchMethodFromString(fetchMethod) == FetchMethod.get;
737
+ }
738
+
739
+ function buildResourceAndBody(resource, method, requestBody, enctype) {
740
+ const searchParams = Array.from(requestBody).length > 0 ? new URLSearchParams(entriesExcludingFiles(requestBody)) : resource.searchParams;
741
+ if (isSafe(method)) {
742
+ return [ mergeIntoURLSearchParams(resource, searchParams), null ];
743
+ } else if (enctype == FetchEnctype.urlEncoded) {
744
+ return [ resource, searchParams ];
745
+ } else {
746
+ return [ resource, requestBody ];
747
+ }
748
+ }
749
+
750
+ function entriesExcludingFiles(requestBody) {
751
+ const entries = [];
752
+ for (const [name, value] of requestBody) {
753
+ if (value instanceof File) continue; else entries.push([ name, value ]);
754
+ }
755
+ return entries;
756
+ }
757
+
758
+ function mergeIntoURLSearchParams(url, requestBody) {
759
+ const searchParams = new URLSearchParams(entriesExcludingFiles(requestBody));
760
+ url.search = searchParams.toString();
761
+ return url;
762
+ }
763
+
606
764
  class AppearanceObserver {
765
+ started=false;
607
766
  constructor(delegate, element) {
608
- this.started = false;
609
- this.intersect = entries => {
610
- const lastEntry = entries.slice(-1)[0];
611
- if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
612
- this.delegate.elementAppearedInViewport(this.element);
613
- }
614
- };
615
767
  this.delegate = delegate;
616
768
  this.element = element;
617
769
  this.intersectionObserver = new IntersectionObserver(this.intersect);
@@ -628,9 +780,16 @@ class AppearanceObserver {
628
780
  this.intersectionObserver.unobserve(this.element);
629
781
  }
630
782
  }
783
+ intersect=entries => {
784
+ const lastEntry = entries.slice(-1)[0];
785
+ if (lastEntry?.isIntersecting) {
786
+ this.delegate.elementAppearedInViewport(this.element);
787
+ }
788
+ };
631
789
  }
632
790
 
633
791
  class StreamMessage {
792
+ static contentType="text/vnd.turbo-stream.html";
634
793
  static wrap(message) {
635
794
  if (typeof message == "string") {
636
795
  return new this(createDocumentFragment(message));
@@ -643,8 +802,6 @@ class StreamMessage {
643
802
  }
644
803
  }
645
804
 
646
- StreamMessage.contentType = "text/vnd.turbo-stream.html";
647
-
648
805
  function importStreamElements(fragment) {
649
806
  for (const element of fragment.querySelectorAll("turbo-stream")) {
650
807
  const streamElement = document.importNode(element, true);
@@ -656,85 +813,89 @@ function importStreamElements(fragment) {
656
813
  return fragment;
657
814
  }
658
815
 
659
- var FormSubmissionState;
660
-
661
- (function(FormSubmissionState) {
662
- FormSubmissionState[FormSubmissionState["initialized"] = 0] = "initialized";
663
- FormSubmissionState[FormSubmissionState["requesting"] = 1] = "requesting";
664
- FormSubmissionState[FormSubmissionState["waiting"] = 2] = "waiting";
665
- FormSubmissionState[FormSubmissionState["receiving"] = 3] = "receiving";
666
- FormSubmissionState[FormSubmissionState["stopping"] = 4] = "stopping";
667
- FormSubmissionState[FormSubmissionState["stopped"] = 5] = "stopped";
668
- })(FormSubmissionState || (FormSubmissionState = {}));
669
-
670
- var FormEnctype;
816
+ const PREFETCH_DELAY = 100;
671
817
 
672
- (function(FormEnctype) {
673
- FormEnctype["urlEncoded"] = "application/x-www-form-urlencoded";
674
- FormEnctype["multipart"] = "multipart/form-data";
675
- FormEnctype["plain"] = "text/plain";
676
- })(FormEnctype || (FormEnctype = {}));
818
+ class PrefetchCache {
819
+ #prefetchTimeout=null;
820
+ #prefetched=null;
821
+ get(url) {
822
+ if (this.#prefetched && this.#prefetched.url === url && this.#prefetched.expire > Date.now()) {
823
+ return this.#prefetched.request;
824
+ }
825
+ }
826
+ setLater(url, request, ttl) {
827
+ this.clear();
828
+ this.#prefetchTimeout = setTimeout((() => {
829
+ request.perform();
830
+ this.set(url, request, ttl);
831
+ this.#prefetchTimeout = null;
832
+ }), PREFETCH_DELAY);
833
+ }
834
+ set(url, request, ttl) {
835
+ this.#prefetched = {
836
+ url: url,
837
+ request: request,
838
+ expire: new Date((new Date).getTime() + ttl)
839
+ };
840
+ }
841
+ clear() {
842
+ if (this.#prefetchTimeout) clearTimeout(this.#prefetchTimeout);
843
+ this.#prefetched = null;
844
+ }
845
+ }
677
846
 
678
- function formEnctypeFromString(encoding) {
679
- switch (encoding.toLowerCase()) {
680
- case FormEnctype.multipart:
681
- return FormEnctype.multipart;
847
+ const cacheTtl = 10 * 1e3;
682
848
 
683
- case FormEnctype.plain:
684
- return FormEnctype.plain;
849
+ const prefetchCache = new PrefetchCache;
685
850
 
686
- default:
687
- return FormEnctype.urlEncoded;
688
- }
689
- }
851
+ const FormSubmissionState = {
852
+ initialized: "initialized",
853
+ requesting: "requesting",
854
+ waiting: "waiting",
855
+ receiving: "receiving",
856
+ stopping: "stopping",
857
+ stopped: "stopped"
858
+ };
690
859
 
691
860
  class FormSubmission {
861
+ state=FormSubmissionState.initialized;
692
862
  static confirmMethod(message, _element, _submitter) {
693
863
  return Promise.resolve(confirm(message));
694
864
  }
695
865
  constructor(delegate, formElement, submitter, mustRedirect = false) {
696
- this.state = FormSubmissionState.initialized;
866
+ const method = getMethod(formElement, submitter);
867
+ const action = getAction(getFormAction(formElement, submitter), method);
868
+ const body = buildFormData(formElement, submitter);
869
+ const enctype = getEnctype(formElement, submitter);
697
870
  this.delegate = delegate;
698
871
  this.formElement = formElement;
699
872
  this.submitter = submitter;
700
- this.formData = buildFormData(formElement, submitter);
701
- this.location = expandURL(this.action);
702
- if (this.method == FetchMethod.get) {
703
- mergeFormDataEntries(this.location, [ ...this.body.entries() ]);
704
- }
705
- this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
873
+ this.fetchRequest = new FetchRequest(this, method, action, body, formElement, enctype);
706
874
  this.mustRedirect = mustRedirect;
707
875
  }
708
876
  get method() {
709
- var _a;
710
- const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
711
- return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
877
+ return this.fetchRequest.method;
878
+ }
879
+ set method(value) {
880
+ this.fetchRequest.method = value;
712
881
  }
713
882
  get action() {
714
- var _a;
715
- const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
716
- if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
717
- return this.submitter.getAttribute("formaction") || "";
718
- } else {
719
- return this.formElement.getAttribute("action") || formElementAction || "";
720
- }
883
+ return this.fetchRequest.url.toString();
884
+ }
885
+ set action(value) {
886
+ this.fetchRequest.url = expandURL(value);
721
887
  }
722
888
  get body() {
723
- if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
724
- return new URLSearchParams(this.stringFormData);
725
- } else {
726
- return this.formData;
727
- }
889
+ return this.fetchRequest.body;
728
890
  }
729
891
  get enctype() {
730
- var _a;
731
- return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
892
+ return this.fetchRequest.enctype;
732
893
  }
733
894
  get isSafe() {
734
895
  return this.fetchRequest.isSafe;
735
896
  }
736
- get stringFormData() {
737
- return [ ...this.formData ].reduce(((entries, [name, value]) => entries.concat(typeof value == "string" ? [ [ name, value ] ] : [])), []);
897
+ get location() {
898
+ return this.fetchRequest.url;
738
899
  }
739
900
  async start() {
740
901
  const {initialized: initialized, requesting: requesting} = FormSubmissionState;
@@ -770,10 +931,10 @@ class FormSubmission {
770
931
  }
771
932
  }
772
933
  requestStarted(_request) {
773
- var _a;
774
934
  this.state = FormSubmissionState.waiting;
775
- (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
935
+ this.submitter?.setAttribute("disabled", "");
776
936
  this.setSubmitsWith();
937
+ markAsBusy(this.formElement);
777
938
  dispatch("turbo:submit-start", {
778
939
  target: this.formElement,
779
940
  detail: {
@@ -783,6 +944,7 @@ class FormSubmission {
783
944
  this.delegate.formSubmissionStarted(this);
784
945
  }
785
946
  requestPreventedHandlingResponse(request, response) {
947
+ prefetchCache.clear();
786
948
  this.result = {
787
949
  success: response.succeeded,
788
950
  fetchResponse: response
@@ -791,7 +953,10 @@ class FormSubmission {
791
953
  requestSucceededWithResponse(request, response) {
792
954
  if (response.clientError || response.serverError) {
793
955
  this.delegate.formSubmissionFailedWithResponse(this, response);
794
- } else if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
956
+ return;
957
+ }
958
+ prefetchCache.clear();
959
+ if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
795
960
  const error = new Error("Form responses must redirect to another location");
796
961
  this.delegate.formSubmissionErrored(this, error);
797
962
  } else {
@@ -818,15 +983,16 @@ class FormSubmission {
818
983
  this.delegate.formSubmissionErrored(this, error);
819
984
  }
820
985
  requestFinished(_request) {
821
- var _a;
822
986
  this.state = FormSubmissionState.stopped;
823
- (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
987
+ this.submitter?.removeAttribute("disabled");
824
988
  this.resetSubmitterText();
989
+ clearBusyState(this.formElement);
825
990
  dispatch("turbo:submit-end", {
826
991
  target: this.formElement,
827
- detail: Object.assign({
828
- formSubmission: this
829
- }, this.result)
992
+ detail: {
993
+ formSubmission: this,
994
+ ...this.result
995
+ }
830
996
  });
831
997
  this.delegate.formSubmissionFinished(this);
832
998
  }
@@ -857,15 +1023,14 @@ class FormSubmission {
857
1023
  return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
858
1024
  }
859
1025
  get submitsWith() {
860
- var _a;
861
- return (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-submits-with");
1026
+ return this.submitter?.getAttribute("data-turbo-submits-with");
862
1027
  }
863
1028
  }
864
1029
 
865
1030
  function buildFormData(formElement, submitter) {
866
1031
  const formData = new FormData(formElement);
867
- const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
868
- const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
1032
+ const name = submitter?.getAttribute("name");
1033
+ const value = submitter?.getAttribute("value");
869
1034
  if (name) {
870
1035
  formData.append(name, value || "");
871
1036
  }
@@ -887,14 +1052,30 @@ function responseSucceededWithoutRedirect(response) {
887
1052
  return response.statusCode == 200 && !response.redirected;
888
1053
  }
889
1054
 
890
- function mergeFormDataEntries(url, entries) {
891
- const searchParams = new URLSearchParams;
892
- for (const [name, value] of entries) {
893
- if (value instanceof File) continue;
894
- searchParams.append(name, value);
1055
+ function getFormAction(formElement, submitter) {
1056
+ const formElementAction = typeof formElement.action === "string" ? formElement.action : null;
1057
+ if (submitter?.hasAttribute("formaction")) {
1058
+ return submitter.getAttribute("formaction") || "";
1059
+ } else {
1060
+ return formElement.getAttribute("action") || formElementAction || "";
895
1061
  }
896
- url.search = searchParams.toString();
897
- return url;
1062
+ }
1063
+
1064
+ function getAction(formAction, fetchMethod) {
1065
+ const action = expandURL(formAction);
1066
+ if (isSafe(fetchMethod)) {
1067
+ action.search = "";
1068
+ }
1069
+ return action;
1070
+ }
1071
+
1072
+ function getMethod(formElement, submitter) {
1073
+ const method = submitter?.getAttribute("formmethod") || formElement.getAttribute("method") || "";
1074
+ return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
1075
+ }
1076
+
1077
+ function getEnctype(formElement, submitter) {
1078
+ return fetchEnctypeFromString(submitter?.getAttribute("formenctype") || formElement.enctype);
898
1079
  }
899
1080
 
900
1081
  class Snapshot {
@@ -917,11 +1098,7 @@ class Snapshot {
917
1098
  return this.element.isConnected;
918
1099
  }
919
1100
  get firstAutofocusableElement() {
920
- const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
921
- for (const element of this.element.querySelectorAll("[autofocus]")) {
922
- if (element.closest(inertDisabledOrHidden) == null) return element; else continue;
923
- }
924
- return null;
1101
+ return queryAutofocusableElement(this.element);
925
1102
  }
926
1103
  get permanentElements() {
927
1104
  return queryPermanentElementsAll(this.element);
@@ -951,23 +1128,8 @@ function queryPermanentElementsAll(node) {
951
1128
  }
952
1129
 
953
1130
  class FormSubmitObserver {
1131
+ started=false;
954
1132
  constructor(delegate, eventTarget) {
955
- this.started = false;
956
- this.submitCaptured = () => {
957
- this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
958
- this.eventTarget.addEventListener("submit", this.submitBubbled, false);
959
- };
960
- this.submitBubbled = event => {
961
- if (!event.defaultPrevented) {
962
- const form = event.target instanceof HTMLFormElement ? event.target : undefined;
963
- const submitter = event.submitter || undefined;
964
- if (form && submissionDoesNotDismissDialog(form, submitter) && submissionDoesNotTargetIFrame(form, submitter) && this.delegate.willSubmitForm(form, submitter)) {
965
- event.preventDefault();
966
- event.stopImmediatePropagation();
967
- this.delegate.formSubmitted(form, submitter);
968
- }
969
- }
970
- };
971
1133
  this.delegate = delegate;
972
1134
  this.eventTarget = eventTarget;
973
1135
  }
@@ -983,16 +1145,31 @@ class FormSubmitObserver {
983
1145
  this.started = false;
984
1146
  }
985
1147
  }
1148
+ submitCaptured=() => {
1149
+ this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
1150
+ this.eventTarget.addEventListener("submit", this.submitBubbled, false);
1151
+ };
1152
+ submitBubbled=event => {
1153
+ if (!event.defaultPrevented) {
1154
+ const form = event.target instanceof HTMLFormElement ? event.target : undefined;
1155
+ const submitter = event.submitter || undefined;
1156
+ if (form && submissionDoesNotDismissDialog(form, submitter) && submissionDoesNotTargetIFrame(form, submitter) && this.delegate.willSubmitForm(form, submitter)) {
1157
+ event.preventDefault();
1158
+ event.stopImmediatePropagation();
1159
+ this.delegate.formSubmitted(form, submitter);
1160
+ }
1161
+ }
1162
+ };
986
1163
  }
987
1164
 
988
1165
  function submissionDoesNotDismissDialog(form, submitter) {
989
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
1166
+ const method = submitter?.getAttribute("formmethod") || form.getAttribute("method");
990
1167
  return method != "dialog";
991
1168
  }
992
1169
 
993
1170
  function submissionDoesNotTargetIFrame(form, submitter) {
994
- if ((submitter === null || submitter === void 0 ? void 0 : submitter.hasAttribute("formtarget")) || form.hasAttribute("target")) {
995
- const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
1171
+ if (submitter?.hasAttribute("formtarget") || form.hasAttribute("target")) {
1172
+ const target = submitter?.getAttribute("formtarget") || form.target;
996
1173
  for (const element of document.getElementsByName(target)) {
997
1174
  if (element instanceof HTMLIFrameElement) return false;
998
1175
  }
@@ -1003,9 +1180,9 @@ function submissionDoesNotTargetIFrame(form, submitter) {
1003
1180
  }
1004
1181
 
1005
1182
  class View {
1183
+ #resolveRenderPromise=_value => {};
1184
+ #resolveInterceptionPromise=_value => {};
1006
1185
  constructor(delegate, element) {
1007
- this.resolveRenderPromise = _value => {};
1008
- this.resolveInterceptionPromise = _value => {};
1009
1186
  this.delegate = delegate;
1010
1187
  this.element = element;
1011
1188
  }
@@ -1051,29 +1228,31 @@ class View {
1051
1228
  return window;
1052
1229
  }
1053
1230
  async render(renderer) {
1054
- const {isPreview: isPreview, shouldRender: shouldRender, newSnapshot: snapshot} = renderer;
1231
+ const {isPreview: isPreview, shouldRender: shouldRender, willRender: willRender, newSnapshot: snapshot} = renderer;
1232
+ const shouldInvalidate = willRender;
1055
1233
  if (shouldRender) {
1056
1234
  try {
1057
- this.renderPromise = new Promise((resolve => this.resolveRenderPromise = resolve));
1235
+ this.renderPromise = new Promise((resolve => this.#resolveRenderPromise = resolve));
1058
1236
  this.renderer = renderer;
1059
1237
  await this.prepareToRenderSnapshot(renderer);
1060
- const renderInterception = new Promise((resolve => this.resolveInterceptionPromise = resolve));
1238
+ const renderInterception = new Promise((resolve => this.#resolveInterceptionPromise = resolve));
1061
1239
  const options = {
1062
- resume: this.resolveInterceptionPromise,
1063
- render: this.renderer.renderElement
1240
+ resume: this.#resolveInterceptionPromise,
1241
+ render: this.renderer.renderElement,
1242
+ renderMethod: this.renderer.renderMethod
1064
1243
  };
1065
1244
  const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
1066
1245
  if (!immediateRender) await renderInterception;
1067
1246
  await this.renderSnapshot(renderer);
1068
- this.delegate.viewRenderedSnapshot(snapshot, isPreview);
1247
+ this.delegate.viewRenderedSnapshot(snapshot, isPreview, this.renderer.renderMethod);
1069
1248
  this.delegate.preloadOnLoadLinksForView(this.element);
1070
1249
  this.finishRenderingSnapshot(renderer);
1071
1250
  } finally {
1072
1251
  delete this.renderer;
1073
- this.resolveRenderPromise(undefined);
1252
+ this.#resolveRenderPromise(undefined);
1074
1253
  delete this.renderPromise;
1075
1254
  }
1076
- } else {
1255
+ } else if (shouldInvalidate) {
1077
1256
  this.invalidate(renderer.reloadReason);
1078
1257
  }
1079
1258
  }
@@ -1091,6 +1270,12 @@ class View {
1091
1270
  this.element.removeAttribute("data-turbo-preview");
1092
1271
  }
1093
1272
  }
1273
+ markVisitDirection(direction) {
1274
+ this.element.setAttribute("data-turbo-visit-direction", direction);
1275
+ }
1276
+ unmarkVisitDirection() {
1277
+ this.element.removeAttribute("data-turbo-visit-direction");
1278
+ }
1094
1279
  async renderSnapshot(renderer) {
1095
1280
  await renderer.render();
1096
1281
  }
@@ -1110,26 +1295,6 @@ class FrameView extends View {
1110
1295
 
1111
1296
  class LinkInterceptor {
1112
1297
  constructor(delegate, element) {
1113
- this.clickBubbled = event => {
1114
- if (this.respondsToEventTarget(event.target)) {
1115
- this.clickEvent = event;
1116
- } else {
1117
- delete this.clickEvent;
1118
- }
1119
- };
1120
- this.linkClicked = event => {
1121
- if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
1122
- if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
1123
- this.clickEvent.preventDefault();
1124
- event.preventDefault();
1125
- this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
1126
- }
1127
- }
1128
- delete this.clickEvent;
1129
- };
1130
- this.willVisit = _event => {
1131
- delete this.clickEvent;
1132
- };
1133
1298
  this.delegate = delegate;
1134
1299
  this.element = element;
1135
1300
  }
@@ -1143,6 +1308,26 @@ class LinkInterceptor {
1143
1308
  document.removeEventListener("turbo:click", this.linkClicked);
1144
1309
  document.removeEventListener("turbo:before-visit", this.willVisit);
1145
1310
  }
1311
+ clickBubbled=event => {
1312
+ if (this.respondsToEventTarget(event.target)) {
1313
+ this.clickEvent = event;
1314
+ } else {
1315
+ delete this.clickEvent;
1316
+ }
1317
+ };
1318
+ linkClicked=event => {
1319
+ if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
1320
+ if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
1321
+ this.clickEvent.preventDefault();
1322
+ event.preventDefault();
1323
+ this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
1324
+ }
1325
+ }
1326
+ delete this.clickEvent;
1327
+ };
1328
+ willVisit=_event => {
1329
+ delete this.clickEvent;
1330
+ };
1146
1331
  respondsToEventTarget(target) {
1147
1332
  const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
1148
1333
  return element && element.closest("turbo-frame, html") == this.element;
@@ -1150,25 +1335,8 @@ class LinkInterceptor {
1150
1335
  }
1151
1336
 
1152
1337
  class LinkClickObserver {
1338
+ started=false;
1153
1339
  constructor(delegate, eventTarget) {
1154
- this.started = false;
1155
- this.clickCaptured = () => {
1156
- this.eventTarget.removeEventListener("click", this.clickBubbled, false);
1157
- this.eventTarget.addEventListener("click", this.clickBubbled, false);
1158
- };
1159
- this.clickBubbled = event => {
1160
- if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
1161
- const target = event.composedPath && event.composedPath()[0] || event.target;
1162
- const link = this.findLinkFromClickTarget(target);
1163
- if (link && doesNotTargetIFrame(link)) {
1164
- const location = this.getLocationForLink(link);
1165
- if (this.delegate.willFollowLinkToLocation(link, location, event)) {
1166
- event.preventDefault();
1167
- this.delegate.followedLinkToLocation(link, location);
1168
- }
1169
- }
1170
- }
1171
- };
1172
1340
  this.delegate = delegate;
1173
1341
  this.eventTarget = eventTarget;
1174
1342
  }
@@ -1184,26 +1352,26 @@ class LinkClickObserver {
1184
1352
  this.started = false;
1185
1353
  }
1186
1354
  }
1355
+ clickCaptured=() => {
1356
+ this.eventTarget.removeEventListener("click", this.clickBubbled, false);
1357
+ this.eventTarget.addEventListener("click", this.clickBubbled, false);
1358
+ };
1359
+ clickBubbled=event => {
1360
+ if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
1361
+ const target = event.composedPath && event.composedPath()[0] || event.target;
1362
+ const link = findLinkFromClickTarget(target);
1363
+ if (link && doesNotTargetIFrame(link)) {
1364
+ const location = getLocationForLink(link);
1365
+ if (this.delegate.willFollowLinkToLocation(link, location, event)) {
1366
+ event.preventDefault();
1367
+ this.delegate.followedLinkToLocation(link, location);
1368
+ }
1369
+ }
1370
+ }
1371
+ };
1187
1372
  clickEventIsSignificant(event) {
1188
1373
  return !(event.target && event.target.isContentEditable || event.defaultPrevented || event.which > 1 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey);
1189
1374
  }
1190
- findLinkFromClickTarget(target) {
1191
- return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
1192
- }
1193
- getLocationForLink(link) {
1194
- return expandURL(link.getAttribute("href") || "");
1195
- }
1196
- }
1197
-
1198
- function doesNotTargetIFrame(anchor) {
1199
- if (anchor.hasAttribute("target")) {
1200
- for (const element of document.getElementsByName(anchor.target)) {
1201
- if (element instanceof HTMLIFrameElement) return false;
1202
- }
1203
- return true;
1204
- } else {
1205
- return true;
1206
- }
1207
1375
  }
1208
1376
 
1209
1377
  class FormLinkClickObserver {
@@ -1217,8 +1385,14 @@ class FormLinkClickObserver {
1217
1385
  stop() {
1218
1386
  this.linkInterceptor.stop();
1219
1387
  }
1388
+ canPrefetchRequestToLocation(link, location) {
1389
+ return false;
1390
+ }
1391
+ prefetchAndCacheRequestToLocation(link, location) {
1392
+ return;
1393
+ }
1220
1394
  willFollowLinkToLocation(link, location, originalEvent) {
1221
- return this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) && link.hasAttribute("data-turbo-method");
1395
+ return this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) && (link.hasAttribute("data-turbo-method") || link.hasAttribute("data-turbo-stream"));
1222
1396
  }
1223
1397
  followedLinkToLocation(link, location) {
1224
1398
  const form = document.createElement("form");
@@ -1291,7 +1465,7 @@ class Bardo {
1291
1465
  }
1292
1466
  replacePlaceholderWithPermanentElement(permanentElement) {
1293
1467
  const placeholder = this.getPlaceholderById(permanentElement.id);
1294
- placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
1468
+ placeholder?.replaceWith(permanentElement);
1295
1469
  }
1296
1470
  getPlaceholderById(id) {
1297
1471
  return this.placeholders.find((element => element.content == id));
@@ -1309,8 +1483,8 @@ function createPlaceholderForPermanentElement(permanentElement) {
1309
1483
  }
1310
1484
 
1311
1485
  class Renderer {
1486
+ #activeElement=null;
1312
1487
  constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1313
- this.activeElement = null;
1314
1488
  this.currentSnapshot = currentSnapshot;
1315
1489
  this.newSnapshot = newSnapshot;
1316
1490
  this.isPreview = isPreview;
@@ -1330,6 +1504,7 @@ class Renderer {
1330
1504
  prepareToRender() {
1331
1505
  return;
1332
1506
  }
1507
+ render() {}
1333
1508
  finishRendering() {
1334
1509
  if (this.resolvingFunctions) {
1335
1510
  this.resolvingFunctions.resolve();
@@ -1341,20 +1516,20 @@ class Renderer {
1341
1516
  }
1342
1517
  focusFirstAutofocusableElement() {
1343
1518
  const element = this.connectedSnapshot.firstAutofocusableElement;
1344
- if (elementIsFocusable(element)) {
1519
+ if (element) {
1345
1520
  element.focus();
1346
1521
  }
1347
1522
  }
1348
1523
  enteringBardo(currentPermanentElement) {
1349
- if (this.activeElement) return;
1524
+ if (this.#activeElement) return;
1350
1525
  if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
1351
- this.activeElement = this.currentSnapshot.activeElement;
1526
+ this.#activeElement = this.currentSnapshot.activeElement;
1352
1527
  }
1353
1528
  }
1354
1529
  leavingBardo(currentPermanentElement) {
1355
- if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
1356
- this.activeElement.focus();
1357
- this.activeElement = null;
1530
+ if (currentPermanentElement.contains(this.#activeElement) && this.#activeElement instanceof HTMLElement) {
1531
+ this.#activeElement.focus();
1532
+ this.#activeElement = null;
1358
1533
  }
1359
1534
  }
1360
1535
  get connectedSnapshot() {
@@ -1369,20 +1544,18 @@ class Renderer {
1369
1544
  get permanentElementMap() {
1370
1545
  return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
1371
1546
  }
1372
- }
1373
-
1374
- function elementIsFocusable(element) {
1375
- return element && typeof element.focus == "function";
1547
+ get renderMethod() {
1548
+ return "replace";
1549
+ }
1376
1550
  }
1377
1551
 
1378
1552
  class FrameRenderer extends Renderer {
1379
1553
  static renderElement(currentElement, newElement) {
1380
- var _a;
1381
1554
  const destinationRange = document.createRange();
1382
1555
  destinationRange.selectNodeContents(currentElement);
1383
1556
  destinationRange.deleteContents();
1384
1557
  const frameElement = newElement;
1385
- const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1558
+ const sourceRange = frameElement.ownerDocument?.createRange();
1386
1559
  if (sourceRange) {
1387
1560
  sourceRange.selectNodeContents(frameElement);
1388
1561
  currentElement.appendChild(sourceRange.extractContents());
@@ -1396,14 +1569,14 @@ class FrameRenderer extends Renderer {
1396
1569
  return true;
1397
1570
  }
1398
1571
  async render() {
1399
- await nextAnimationFrame();
1572
+ await nextRepaint();
1400
1573
  this.preservingPermanentElements((() => {
1401
1574
  this.loadFrameElement();
1402
1575
  }));
1403
1576
  this.scrollFrameIntoView();
1404
- await nextAnimationFrame();
1577
+ await nextRepaint();
1405
1578
  this.focusFirstAutofocusableElement();
1406
- await nextAnimationFrame();
1579
+ await nextRepaint();
1407
1580
  this.activateScriptElements();
1408
1581
  }
1409
1582
  loadFrameElement() {
@@ -1453,6 +1626,7 @@ function readScrollBehavior(value, defaultValue) {
1453
1626
  }
1454
1627
 
1455
1628
  class ProgressBar {
1629
+ static animationDuration=300;
1456
1630
  static get defaultCSS() {
1457
1631
  return unindent`
1458
1632
  .turbo-progress-bar {
@@ -1470,13 +1644,10 @@ class ProgressBar {
1470
1644
  }
1471
1645
  `;
1472
1646
  }
1647
+ hiding=false;
1648
+ value=0;
1649
+ visible=false;
1473
1650
  constructor() {
1474
- this.hiding = false;
1475
- this.value = 0;
1476
- this.visible = false;
1477
- this.trickle = () => {
1478
- this.setValue(this.value + Math.random() / 100);
1479
- };
1480
1651
  this.stylesheetElement = this.createStylesheetElement();
1481
1652
  this.progressElement = this.createProgressElement();
1482
1653
  this.installStylesheetElement();
@@ -1531,6 +1702,9 @@ class ProgressBar {
1531
1702
  window.clearInterval(this.trickleInterval);
1532
1703
  delete this.trickleInterval;
1533
1704
  }
1705
+ trickle=() => {
1706
+ this.setValue(this.value + Math.random() / 100);
1707
+ };
1534
1708
  refresh() {
1535
1709
  requestAnimationFrame((() => {
1536
1710
  this.progressElement.style.width = `${10 + this.value * 90}%`;
@@ -1555,25 +1729,22 @@ class ProgressBar {
1555
1729
  }
1556
1730
  }
1557
1731
 
1558
- ProgressBar.animationDuration = 300;
1559
-
1560
1732
  class HeadSnapshot extends Snapshot {
1561
- constructor() {
1562
- super(...arguments);
1563
- this.detailsByOuterHTML = this.children.filter((element => !elementIsNoscript(element))).map((element => elementWithoutNonce(element))).reduce(((result, element) => {
1564
- const {outerHTML: outerHTML} = element;
1565
- const details = outerHTML in result ? result[outerHTML] : {
1566
- type: elementType(element),
1567
- tracked: elementIsTracked(element),
1568
- elements: []
1569
- };
1570
- return Object.assign(Object.assign({}, result), {
1571
- [outerHTML]: Object.assign(Object.assign({}, details), {
1572
- elements: [ ...details.elements, element ]
1573
- })
1574
- });
1575
- }), {});
1576
- }
1733
+ detailsByOuterHTML=this.children.filter((element => !elementIsNoscript(element))).map((element => elementWithoutNonce(element))).reduce(((result, element) => {
1734
+ const {outerHTML: outerHTML} = element;
1735
+ const details = outerHTML in result ? result[outerHTML] : {
1736
+ type: elementType(element),
1737
+ tracked: elementIsTracked(element),
1738
+ elements: []
1739
+ };
1740
+ return {
1741
+ ...result,
1742
+ [outerHTML]: {
1743
+ ...details,
1744
+ elements: [ ...details.elements, element ]
1745
+ }
1746
+ };
1747
+ }), {});
1577
1748
  get trackedElementSignature() {
1578
1749
  return Object.keys(this.detailsByOuterHTML).filter((outerHTML => this.detailsByOuterHTML[outerHTML].tracked)).join("");
1579
1750
  }
@@ -1606,7 +1777,7 @@ class HeadSnapshot extends Snapshot {
1606
1777
  return Object.keys(this.detailsByOuterHTML).reduce(((result, outerHTML) => {
1607
1778
  const {elements: [element]} = this.detailsByOuterHTML[outerHTML];
1608
1779
  return elementIsMetaElementWithName(element, name) ? element : result;
1609
- }), undefined);
1780
+ }), undefined | undefined);
1610
1781
  }
1611
1782
  }
1612
1783
 
@@ -1656,11 +1827,12 @@ class PageSnapshot extends Snapshot {
1656
1827
  static fromElement(element) {
1657
1828
  return this.fromDocument(element.ownerDocument);
1658
1829
  }
1659
- static fromDocument({head: head, body: body}) {
1660
- return new this(body, new HeadSnapshot(head));
1830
+ static fromDocument({documentElement: documentElement, body: body, head: head}) {
1831
+ return new this(documentElement, body, new HeadSnapshot(head));
1661
1832
  }
1662
- constructor(element, headSnapshot) {
1663
- super(element);
1833
+ constructor(documentElement, body, headSnapshot) {
1834
+ super(body);
1835
+ this.documentElement = documentElement;
1664
1836
  this.headSnapshot = headSnapshot;
1665
1837
  }
1666
1838
  clone() {
@@ -1675,14 +1847,16 @@ class PageSnapshot extends Snapshot {
1675
1847
  for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
1676
1848
  clonedPasswordInput.value = "";
1677
1849
  }
1678
- return new PageSnapshot(clonedElement, this.headSnapshot);
1850
+ return new PageSnapshot(this.documentElement, clonedElement, this.headSnapshot);
1851
+ }
1852
+ get lang() {
1853
+ return this.documentElement.getAttribute("lang");
1679
1854
  }
1680
1855
  get headElement() {
1681
1856
  return this.headSnapshot.element;
1682
1857
  }
1683
1858
  get rootLocation() {
1684
- var _a;
1685
- const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
1859
+ const root = this.getSetting("root") ?? "/";
1686
1860
  return expandURL(root);
1687
1861
  }
1688
1862
  get cacheControlValue() {
@@ -1697,29 +1871,38 @@ class PageSnapshot extends Snapshot {
1697
1871
  get isVisitable() {
1698
1872
  return this.getSetting("visit-control") != "reload";
1699
1873
  }
1874
+ get prefersViewTransitions() {
1875
+ return this.headSnapshot.getMetaValue("view-transition") === "same-origin";
1876
+ }
1877
+ get shouldMorphPage() {
1878
+ return this.getSetting("refresh-method") === "morph";
1879
+ }
1880
+ get shouldPreserveScrollPosition() {
1881
+ return this.getSetting("refresh-scroll") === "preserve";
1882
+ }
1700
1883
  getSetting(name) {
1701
1884
  return this.headSnapshot.getMetaValue(`turbo-${name}`);
1702
1885
  }
1703
1886
  }
1704
1887
 
1705
- var TimingMetric;
1706
-
1707
- (function(TimingMetric) {
1708
- TimingMetric["visitStart"] = "visitStart";
1709
- TimingMetric["requestStart"] = "requestStart";
1710
- TimingMetric["requestEnd"] = "requestEnd";
1711
- TimingMetric["visitEnd"] = "visitEnd";
1712
- })(TimingMetric || (TimingMetric = {}));
1713
-
1714
- var VisitState;
1715
-
1716
- (function(VisitState) {
1717
- VisitState["initialized"] = "initialized";
1718
- VisitState["started"] = "started";
1719
- VisitState["canceled"] = "canceled";
1720
- VisitState["failed"] = "failed";
1721
- VisitState["completed"] = "completed";
1722
- })(VisitState || (VisitState = {}));
1888
+ class ViewTransitioner {
1889
+ #viewTransitionStarted=false;
1890
+ #lastOperation=Promise.resolve();
1891
+ renderChange(useViewTransition, render) {
1892
+ if (useViewTransition && this.viewTransitionsAvailable && !this.#viewTransitionStarted) {
1893
+ this.#viewTransitionStarted = true;
1894
+ this.#lastOperation = this.#lastOperation.then((async () => {
1895
+ await document.startViewTransition(render).finished;
1896
+ }));
1897
+ } else {
1898
+ this.#lastOperation = this.#lastOperation.then(render);
1899
+ }
1900
+ return this.#lastOperation;
1901
+ }
1902
+ get viewTransitionsAvailable() {
1903
+ return document.startViewTransition;
1904
+ }
1905
+ }
1723
1906
 
1724
1907
  const defaultOptions = {
1725
1908
  action: "advance",
@@ -1731,29 +1914,52 @@ const defaultOptions = {
1731
1914
  acceptsStreamResponse: false
1732
1915
  };
1733
1916
 
1734
- var SystemStatusCode;
1917
+ const TimingMetric = {
1918
+ visitStart: "visitStart",
1919
+ requestStart: "requestStart",
1920
+ requestEnd: "requestEnd",
1921
+ visitEnd: "visitEnd"
1922
+ };
1923
+
1924
+ const VisitState = {
1925
+ initialized: "initialized",
1926
+ started: "started",
1927
+ canceled: "canceled",
1928
+ failed: "failed",
1929
+ completed: "completed"
1930
+ };
1931
+
1932
+ const SystemStatusCode = {
1933
+ networkFailure: 0,
1934
+ timeoutFailure: -1,
1935
+ contentTypeMismatch: -2
1936
+ };
1735
1937
 
1736
- (function(SystemStatusCode) {
1737
- SystemStatusCode[SystemStatusCode["networkFailure"] = 0] = "networkFailure";
1738
- SystemStatusCode[SystemStatusCode["timeoutFailure"] = -1] = "timeoutFailure";
1739
- SystemStatusCode[SystemStatusCode["contentTypeMismatch"] = -2] = "contentTypeMismatch";
1740
- })(SystemStatusCode || (SystemStatusCode = {}));
1938
+ const Direction = {
1939
+ advance: "forward",
1940
+ restore: "back",
1941
+ replace: "none"
1942
+ };
1741
1943
 
1742
1944
  class Visit {
1945
+ identifier=uuid();
1946
+ timingMetrics={};
1947
+ followedRedirect=false;
1948
+ historyChanged=false;
1949
+ scrolled=false;
1950
+ shouldCacheSnapshot=true;
1951
+ acceptsStreamResponse=false;
1952
+ snapshotCached=false;
1953
+ state=VisitState.initialized;
1954
+ viewTransitioner=new ViewTransitioner;
1743
1955
  constructor(delegate, location, restorationIdentifier, options = {}) {
1744
- this.identifier = uuid();
1745
- this.timingMetrics = {};
1746
- this.followedRedirect = false;
1747
- this.historyChanged = false;
1748
- this.scrolled = false;
1749
- this.shouldCacheSnapshot = true;
1750
- this.acceptsStreamResponse = false;
1751
- this.snapshotCached = false;
1752
- this.state = VisitState.initialized;
1753
1956
  this.delegate = delegate;
1754
1957
  this.location = location;
1755
1958
  this.restorationIdentifier = restorationIdentifier || uuid();
1756
- const {action: action, historyChanged: historyChanged, referrer: referrer, snapshot: snapshot, snapshotHTML: snapshotHTML, response: response, visitCachedSnapshot: visitCachedSnapshot, willRender: willRender, updateHistory: updateHistory, shouldCacheSnapshot: shouldCacheSnapshot, acceptsStreamResponse: acceptsStreamResponse} = Object.assign(Object.assign({}, defaultOptions), options);
1959
+ const {action: action, historyChanged: historyChanged, referrer: referrer, snapshot: snapshot, snapshotHTML: snapshotHTML, response: response, visitCachedSnapshot: visitCachedSnapshot, willRender: willRender, updateHistory: updateHistory, shouldCacheSnapshot: shouldCacheSnapshot, acceptsStreamResponse: acceptsStreamResponse, direction: direction} = {
1960
+ ...defaultOptions,
1961
+ ...options
1962
+ };
1757
1963
  this.action = action;
1758
1964
  this.historyChanged = historyChanged;
1759
1965
  this.referrer = referrer;
@@ -1761,12 +1967,14 @@ class Visit {
1761
1967
  this.snapshotHTML = snapshotHTML;
1762
1968
  this.response = response;
1763
1969
  this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
1970
+ this.isPageRefresh = this.view.isPageRefresh(this);
1764
1971
  this.visitCachedSnapshot = visitCachedSnapshot;
1765
1972
  this.willRender = willRender;
1766
1973
  this.updateHistory = updateHistory;
1767
1974
  this.scrolled = !willRender;
1768
1975
  this.shouldCacheSnapshot = shouldCacheSnapshot;
1769
1976
  this.acceptsStreamResponse = acceptsStreamResponse;
1977
+ this.direction = direction || Direction[action];
1770
1978
  }
1771
1979
  get adapter() {
1772
1980
  return this.delegate.adapter;
@@ -1815,12 +2023,12 @@ class Visit {
1815
2023
  if (this.state == VisitState.started) {
1816
2024
  this.state = VisitState.failed;
1817
2025
  this.adapter.visitFailed(this);
2026
+ this.delegate.visitCompleted(this);
1818
2027
  }
1819
2028
  }
1820
2029
  changeHistory() {
1821
- var _a;
1822
2030
  if (!this.historyChanged && this.updateHistory) {
1823
- const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
2031
+ const actionForHistory = this.location.href === this.referrer?.href ? "replace" : this.action;
1824
2032
  const method = getHistoryMethodForAction(actionForHistory);
1825
2033
  this.history.update(method, this.location, this.restorationIdentifier);
1826
2034
  this.historyChanged = true;
@@ -1867,8 +2075,8 @@ class Visit {
1867
2075
  if (this.shouldCacheSnapshot) this.cacheSnapshot();
1868
2076
  if (this.view.renderPromise) await this.view.renderPromise;
1869
2077
  if (isSuccessful(statusCode) && responseHTML != null) {
1870
- await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
1871
- this.performScroll();
2078
+ const snapshot = PageSnapshot.fromHTMLString(responseHTML);
2079
+ await this.renderPageSnapshot(snapshot, false);
1872
2080
  this.adapter.visitRendered(this);
1873
2081
  this.complete();
1874
2082
  } else {
@@ -1901,12 +2109,11 @@ class Visit {
1901
2109
  const isPreview = this.shouldIssueRequest();
1902
2110
  this.render((async () => {
1903
2111
  this.cacheSnapshot();
1904
- if (this.isSamePage) {
2112
+ if (this.isSamePage || this.isPageRefresh) {
1905
2113
  this.adapter.visitRendered(this);
1906
2114
  } else {
1907
2115
  if (this.view.renderPromise) await this.view.renderPromise;
1908
- await this.view.renderPage(snapshot, isPreview, this.willRender, this);
1909
- this.performScroll();
2116
+ await this.renderPageSnapshot(snapshot, isPreview);
1910
2117
  this.adapter.visitRendered(this);
1911
2118
  if (!isPreview) {
1912
2119
  this.complete();
@@ -1916,8 +2123,7 @@ class Visit {
1916
2123
  }
1917
2124
  }
1918
2125
  followRedirect() {
1919
- var _a;
1920
- if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
2126
+ if (this.redirectedToLocation && !this.followedRedirect && this.response?.redirected) {
1921
2127
  this.adapter.visitProposedToLocation(this.redirectedToLocation, {
1922
2128
  action: "replace",
1923
2129
  response: this.response,
@@ -1989,7 +2195,7 @@ class Visit {
1989
2195
  this.finishRequest();
1990
2196
  }
1991
2197
  performScroll() {
1992
- if (!this.scrolled && !this.view.forceReloaded) {
2198
+ if (!this.scrolled && !this.view.forceReloaded && !this.view.shouldPreserveScrollPosition(this)) {
1993
2199
  if (this.action == "restore") {
1994
2200
  this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
1995
2201
  } else {
@@ -2019,7 +2225,9 @@ class Visit {
2019
2225
  this.timingMetrics[metric] = (new Date).getTime();
2020
2226
  }
2021
2227
  getTimingMetrics() {
2022
- return Object.assign({}, this.timingMetrics);
2228
+ return {
2229
+ ...this.timingMetrics
2230
+ };
2023
2231
  }
2024
2232
  getHistoryMethodForAction(action) {
2025
2233
  switch (action) {
@@ -2051,12 +2259,16 @@ class Visit {
2051
2259
  }
2052
2260
  async render(callback) {
2053
2261
  this.cancelRender();
2054
- await new Promise((resolve => {
2055
- this.frame = requestAnimationFrame((() => resolve()));
2056
- }));
2262
+ this.frame = await nextRepaint();
2057
2263
  await callback();
2058
2264
  delete this.frame;
2059
2265
  }
2266
+ async renderPageSnapshot(snapshot, isPreview) {
2267
+ await this.viewTransitioner.renderChange(this.view.shouldTransitionTo(snapshot), (async () => {
2268
+ await this.view.renderPage(snapshot, isPreview, this.willRender, this);
2269
+ this.performScroll();
2270
+ }));
2271
+ }
2060
2272
  cancelRender() {
2061
2273
  if (this.frame) {
2062
2274
  cancelAnimationFrame(this.frame);
@@ -2070,15 +2282,16 @@ function isSuccessful(statusCode) {
2070
2282
  }
2071
2283
 
2072
2284
  class BrowserAdapter {
2285
+ progressBar=new ProgressBar;
2073
2286
  constructor(session) {
2074
- this.progressBar = new ProgressBar;
2075
- this.showProgressBar = () => {
2076
- this.progressBar.show();
2077
- };
2078
2287
  this.session = session;
2079
2288
  }
2080
2289
  visitProposedToLocation(location, options) {
2081
- this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
2290
+ if (locationIsVisitable(location, this.navigator.rootLocation)) {
2291
+ this.navigator.startVisit(location, options?.restorationIdentifier || uuid(), options);
2292
+ } else {
2293
+ window.location.href = location.toString();
2294
+ }
2082
2295
  }
2083
2296
  visitStarted(visit) {
2084
2297
  this.location = visit.location;
@@ -2113,15 +2326,18 @@ class BrowserAdapter {
2113
2326
  return visit.loadResponse();
2114
2327
  }
2115
2328
  }
2116
- visitRequestFinished(_visit) {
2329
+ visitRequestFinished(_visit) {}
2330
+ visitCompleted(_visit) {
2117
2331
  this.progressBar.setValue(1);
2118
2332
  this.hideVisitProgressBar();
2119
2333
  }
2120
- visitCompleted(_visit) {}
2121
2334
  pageInvalidated(reason) {
2122
2335
  this.reload(reason);
2123
2336
  }
2124
- visitFailed(_visit) {}
2337
+ visitFailed(_visit) {
2338
+ this.progressBar.setValue(1);
2339
+ this.hideVisitProgressBar();
2340
+ }
2125
2341
  visitRendered(_visit) {}
2126
2342
  formSubmissionStarted(_formSubmission) {
2127
2343
  this.progressBar.setValue(0);
@@ -2153,12 +2369,14 @@ class BrowserAdapter {
2153
2369
  delete this.formProgressBarTimeout;
2154
2370
  }
2155
2371
  }
2372
+ showProgressBar=() => {
2373
+ this.progressBar.show();
2374
+ };
2156
2375
  reload(reason) {
2157
- var _a;
2158
2376
  dispatch("turbo:reload", {
2159
2377
  detail: reason
2160
2378
  });
2161
- window.location.href = ((_a = this.location) === null || _a === void 0 ? void 0 : _a.toString()) || window.location.href;
2379
+ window.location.href = this.location?.toString() || window.location.href;
2162
2380
  }
2163
2381
  get navigator() {
2164
2382
  return this.session.navigator;
@@ -2166,16 +2384,9 @@ class BrowserAdapter {
2166
2384
  }
2167
2385
 
2168
2386
  class CacheObserver {
2169
- constructor() {
2170
- this.selector = "[data-turbo-temporary]";
2171
- this.deprecatedSelector = "[data-turbo-cache=false]";
2172
- this.started = false;
2173
- this.removeTemporaryElements = _event => {
2174
- for (const element of this.temporaryElements) {
2175
- element.remove();
2176
- }
2177
- };
2178
- }
2387
+ selector="[data-turbo-temporary]";
2388
+ deprecatedSelector="[data-turbo-cache=false]";
2389
+ started=false;
2179
2390
  start() {
2180
2391
  if (!this.started) {
2181
2392
  this.started = true;
@@ -2188,6 +2399,11 @@ class CacheObserver {
2188
2399
  removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
2189
2400
  }
2190
2401
  }
2402
+ removeTemporaryElements=_event => {
2403
+ for (const element of this.temporaryElements) {
2404
+ element.remove();
2405
+ }
2406
+ };
2191
2407
  get temporaryElements() {
2192
2408
  return [ ...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation ];
2193
2409
  }
@@ -2216,41 +2432,40 @@ class FrameRedirector {
2216
2432
  this.formSubmitObserver.stop();
2217
2433
  }
2218
2434
  shouldInterceptLinkClick(element, _location, _event) {
2219
- return this.shouldRedirect(element);
2435
+ return this.#shouldRedirect(element);
2220
2436
  }
2221
2437
  linkClickIntercepted(element, url, event) {
2222
- const frame = this.findFrameElement(element);
2438
+ const frame = this.#findFrameElement(element);
2223
2439
  if (frame) {
2224
2440
  frame.delegate.linkClickIntercepted(element, url, event);
2225
2441
  }
2226
2442
  }
2227
2443
  willSubmitForm(element, submitter) {
2228
- return element.closest("turbo-frame") == null && this.shouldSubmit(element, submitter) && this.shouldRedirect(element, submitter);
2444
+ return element.closest("turbo-frame") == null && this.#shouldSubmit(element, submitter) && this.#shouldRedirect(element, submitter);
2229
2445
  }
2230
2446
  formSubmitted(element, submitter) {
2231
- const frame = this.findFrameElement(element, submitter);
2447
+ const frame = this.#findFrameElement(element, submitter);
2232
2448
  if (frame) {
2233
2449
  frame.delegate.formSubmitted(element, submitter);
2234
2450
  }
2235
2451
  }
2236
- shouldSubmit(form, submitter) {
2237
- var _a;
2238
- const action = getAction(form, submitter);
2452
+ #shouldSubmit(form, submitter) {
2453
+ const action = getAction$1(form, submitter);
2239
2454
  const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
2240
- const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
2241
- return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
2455
+ const rootLocation = expandURL(meta?.content ?? "/");
2456
+ return this.#shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
2242
2457
  }
2243
- shouldRedirect(element, submitter) {
2458
+ #shouldRedirect(element, submitter) {
2244
2459
  const isNavigatable = element instanceof HTMLFormElement ? this.session.submissionIsNavigatable(element, submitter) : this.session.elementIsNavigatable(element);
2245
2460
  if (isNavigatable) {
2246
- const frame = this.findFrameElement(element, submitter);
2461
+ const frame = this.#findFrameElement(element, submitter);
2247
2462
  return frame ? frame != element.closest("turbo-frame") : false;
2248
2463
  } else {
2249
2464
  return false;
2250
2465
  }
2251
2466
  }
2252
- findFrameElement(element, submitter) {
2253
- const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
2467
+ #findFrameElement(element, submitter) {
2468
+ const id = submitter?.getAttribute("data-turbo-frame") || element.getAttribute("data-turbo-frame");
2254
2469
  if (id && id != "_top") {
2255
2470
  const frame = this.element.querySelector(`#${id}:not([disabled])`);
2256
2471
  if (frame instanceof FrameElement) {
@@ -2261,32 +2476,20 @@ class FrameRedirector {
2261
2476
  }
2262
2477
 
2263
2478
  class History {
2479
+ location;
2480
+ restorationIdentifier=uuid();
2481
+ restorationData={};
2482
+ started=false;
2483
+ pageLoaded=false;
2484
+ currentIndex=0;
2264
2485
  constructor(delegate) {
2265
- this.restorationIdentifier = uuid();
2266
- this.restorationData = {};
2267
- this.started = false;
2268
- this.pageLoaded = false;
2269
- this.onPopState = event => {
2270
- if (this.shouldHandlePopState()) {
2271
- const {turbo: turbo} = event.state || {};
2272
- if (turbo) {
2273
- this.location = new URL(window.location.href);
2274
- const {restorationIdentifier: restorationIdentifier} = turbo;
2275
- this.restorationIdentifier = restorationIdentifier;
2276
- this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location, restorationIdentifier);
2277
- }
2278
- }
2279
- };
2280
- this.onPageLoad = async _event => {
2281
- await nextMicrotask();
2282
- this.pageLoaded = true;
2283
- };
2284
2486
  this.delegate = delegate;
2285
2487
  }
2286
2488
  start() {
2287
2489
  if (!this.started) {
2288
2490
  addEventListener("popstate", this.onPopState, false);
2289
2491
  addEventListener("load", this.onPageLoad, false);
2492
+ this.currentIndex = history.state?.turbo?.restorationIndex || 0;
2290
2493
  this.started = true;
2291
2494
  this.replace(new URL(window.location.href));
2292
2495
  }
@@ -2305,9 +2508,11 @@ class History {
2305
2508
  this.update(history.replaceState, location, restorationIdentifier);
2306
2509
  }
2307
2510
  update(method, location, restorationIdentifier = uuid()) {
2511
+ if (method === history.pushState) ++this.currentIndex;
2308
2512
  const state = {
2309
2513
  turbo: {
2310
- restorationIdentifier: restorationIdentifier
2514
+ restorationIdentifier: restorationIdentifier,
2515
+ restorationIndex: this.currentIndex
2311
2516
  }
2312
2517
  };
2313
2518
  method.call(history, state, "", location.href);
@@ -2320,12 +2525,14 @@ class History {
2320
2525
  updateRestorationData(additionalData) {
2321
2526
  const {restorationIdentifier: restorationIdentifier} = this;
2322
2527
  const restorationData = this.restorationData[restorationIdentifier];
2323
- this.restorationData[restorationIdentifier] = Object.assign(Object.assign({}, restorationData), additionalData);
2528
+ this.restorationData[restorationIdentifier] = {
2529
+ ...restorationData,
2530
+ ...additionalData
2531
+ };
2324
2532
  }
2325
2533
  assumeControlOfScrollRestoration() {
2326
- var _a;
2327
2534
  if (!this.previousScrollRestoration) {
2328
- this.previousScrollRestoration = (_a = history.scrollRestoration) !== null && _a !== void 0 ? _a : "auto";
2535
+ this.previousScrollRestoration = history.scrollRestoration ?? "auto";
2329
2536
  history.scrollRestoration = "manual";
2330
2537
  }
2331
2538
  }
@@ -2335,6 +2542,23 @@ class History {
2335
2542
  delete this.previousScrollRestoration;
2336
2543
  }
2337
2544
  }
2545
+ onPopState=event => {
2546
+ if (this.shouldHandlePopState()) {
2547
+ const {turbo: turbo} = event.state || {};
2548
+ if (turbo) {
2549
+ this.location = new URL(window.location.href);
2550
+ const {restorationIdentifier: restorationIdentifier, restorationIndex: restorationIndex} = turbo;
2551
+ this.restorationIdentifier = restorationIdentifier;
2552
+ const direction = restorationIndex > this.currentIndex ? "forward" : "back";
2553
+ this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location, restorationIdentifier, direction);
2554
+ this.currentIndex = restorationIndex;
2555
+ }
2556
+ }
2557
+ };
2558
+ onPageLoad=async _event => {
2559
+ await nextMicrotask();
2560
+ this.pageLoaded = true;
2561
+ };
2338
2562
  shouldHandlePopState() {
2339
2563
  return this.pageIsLoaded();
2340
2564
  }
@@ -2343,24 +2567,152 @@ class History {
2343
2567
  }
2344
2568
  }
2345
2569
 
2570
+ class LinkPrefetchObserver {
2571
+ started=false;
2572
+ #prefetchedLink=null;
2573
+ constructor(delegate, eventTarget) {
2574
+ this.delegate = delegate;
2575
+ this.eventTarget = eventTarget;
2576
+ }
2577
+ start() {
2578
+ if (this.started) return;
2579
+ if (this.eventTarget.readyState === "loading") {
2580
+ this.eventTarget.addEventListener("DOMContentLoaded", this.#enable, {
2581
+ once: true
2582
+ });
2583
+ } else {
2584
+ this.#enable();
2585
+ }
2586
+ }
2587
+ stop() {
2588
+ if (!this.started) return;
2589
+ this.eventTarget.removeEventListener("mouseenter", this.#tryToPrefetchRequest, {
2590
+ capture: true,
2591
+ passive: true
2592
+ });
2593
+ this.eventTarget.removeEventListener("mouseleave", this.#cancelRequestIfObsolete, {
2594
+ capture: true,
2595
+ passive: true
2596
+ });
2597
+ this.eventTarget.removeEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true);
2598
+ this.started = false;
2599
+ }
2600
+ #enable=() => {
2601
+ this.eventTarget.addEventListener("mouseenter", this.#tryToPrefetchRequest, {
2602
+ capture: true,
2603
+ passive: true
2604
+ });
2605
+ this.eventTarget.addEventListener("mouseleave", this.#cancelRequestIfObsolete, {
2606
+ capture: true,
2607
+ passive: true
2608
+ });
2609
+ this.eventTarget.addEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true);
2610
+ this.started = true;
2611
+ };
2612
+ #tryToPrefetchRequest=event => {
2613
+ if (getMetaContent("turbo-prefetch") === "false") return;
2614
+ const target = event.target;
2615
+ const isLink = target.matches && target.matches("a[href]:not([target^=_]):not([download])");
2616
+ if (isLink && this.#isPrefetchable(target)) {
2617
+ const link = target;
2618
+ const location = getLocationForLink(link);
2619
+ if (this.delegate.canPrefetchRequestToLocation(link, location)) {
2620
+ this.#prefetchedLink = link;
2621
+ const fetchRequest = new FetchRequest(this, FetchMethod.get, location, new URLSearchParams, target);
2622
+ prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl);
2623
+ }
2624
+ }
2625
+ };
2626
+ #cancelRequestIfObsolete=event => {
2627
+ if (event.target === this.#prefetchedLink) this.#cancelPrefetchRequest();
2628
+ };
2629
+ #cancelPrefetchRequest=() => {
2630
+ prefetchCache.clear();
2631
+ this.#prefetchedLink = null;
2632
+ };
2633
+ #tryToUsePrefetchedRequest=event => {
2634
+ if (event.target.tagName !== "FORM" && event.detail.fetchOptions.method === "get") {
2635
+ const cached = prefetchCache.get(event.detail.url.toString());
2636
+ if (cached) {
2637
+ event.detail.fetchRequest = cached;
2638
+ }
2639
+ prefetchCache.clear();
2640
+ }
2641
+ };
2642
+ prepareRequest(request) {
2643
+ const link = request.target;
2644
+ request.headers["X-Sec-Purpose"] = "prefetch";
2645
+ const turboFrame = link.closest("turbo-frame");
2646
+ const turboFrameTarget = link.getAttribute("data-turbo-frame") || turboFrame?.getAttribute("target") || turboFrame?.id;
2647
+ if (turboFrameTarget && turboFrameTarget !== "_top") {
2648
+ request.headers["Turbo-Frame"] = turboFrameTarget;
2649
+ }
2650
+ if (link.hasAttribute("data-turbo-stream")) {
2651
+ request.acceptResponseType(StreamMessage.contentType);
2652
+ }
2653
+ }
2654
+ requestSucceededWithResponse() {}
2655
+ requestStarted(fetchRequest) {}
2656
+ requestErrored(fetchRequest) {}
2657
+ requestFinished(fetchRequest) {}
2658
+ requestPreventedHandlingResponse(fetchRequest, fetchResponse) {}
2659
+ requestFailedWithResponse(fetchRequest, fetchResponse) {}
2660
+ get #cacheTtl() {
2661
+ return Number(getMetaContent("turbo-prefetch-cache-time")) || cacheTtl;
2662
+ }
2663
+ #isPrefetchable(link) {
2664
+ const href = link.getAttribute("href");
2665
+ if (!href || href.startsWith("#") || link.getAttribute("data-turbo") === "false" || link.getAttribute("data-turbo-prefetch") === "false") {
2666
+ return false;
2667
+ }
2668
+ const event = dispatch("turbo:before-prefetch", {
2669
+ target: link,
2670
+ cancelable: true
2671
+ });
2672
+ if (event.defaultPrevented) {
2673
+ return false;
2674
+ }
2675
+ if (link.origin !== document.location.origin) {
2676
+ return false;
2677
+ }
2678
+ if (![ "http:", "https:" ].includes(link.protocol)) {
2679
+ return false;
2680
+ }
2681
+ if (link.pathname + link.search === document.location.pathname + document.location.search) {
2682
+ return false;
2683
+ }
2684
+ const turboMethod = link.getAttribute("data-turbo-method");
2685
+ if (turboMethod && turboMethod !== "get") {
2686
+ return false;
2687
+ }
2688
+ if (targetsIframe(link)) {
2689
+ return false;
2690
+ }
2691
+ const turboPrefetchParent = findClosestRecursively(link, "[data-turbo-prefetch]");
2692
+ if (turboPrefetchParent && turboPrefetchParent.getAttribute("data-turbo-prefetch") === "false") {
2693
+ return false;
2694
+ }
2695
+ return true;
2696
+ }
2697
+ }
2698
+
2699
+ const targetsIframe = link => !doesNotTargetIFrame(link);
2700
+
2346
2701
  class Navigator {
2347
2702
  constructor(delegate) {
2348
2703
  this.delegate = delegate;
2349
2704
  }
2350
2705
  proposeVisit(location, options = {}) {
2351
2706
  if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
2352
- if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
2353
- this.delegate.visitProposedToLocation(location, options);
2354
- } else {
2355
- window.location.href = location.toString();
2356
- }
2707
+ this.delegate.visitProposedToLocation(location, options);
2357
2708
  }
2358
2709
  }
2359
2710
  startVisit(locatable, restorationIdentifier, options = {}) {
2360
2711
  this.stop();
2361
- this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({
2362
- referrer: this.location
2363
- }, options));
2712
+ this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, {
2713
+ referrer: this.location,
2714
+ ...options
2715
+ });
2364
2716
  this.currentVisit.start();
2365
2717
  }
2366
2718
  submitForm(form, submitter) {
@@ -2384,6 +2736,9 @@ class Navigator {
2384
2736
  get view() {
2385
2737
  return this.delegate.view;
2386
2738
  }
2739
+ get rootLocation() {
2740
+ return this.view.snapshot.rootLocation;
2741
+ }
2387
2742
  get history() {
2388
2743
  return this.delegate.history;
2389
2744
  }
@@ -2401,7 +2756,7 @@ class Navigator {
2401
2756
  this.view.clearSnapshotCache();
2402
2757
  }
2403
2758
  const {statusCode: statusCode, redirected: redirected} = fetchResponse;
2404
- const action = this.getActionForFormSubmission(formSubmission);
2759
+ const action = this.#getActionForFormSubmission(formSubmission, fetchResponse);
2405
2760
  const visitOptions = {
2406
2761
  action: action,
2407
2762
  shouldCacheSnapshot: shouldCacheSnapshot,
@@ -2424,7 +2779,9 @@ class Navigator {
2424
2779
  } else {
2425
2780
  await this.view.renderPage(snapshot, false, true, this.currentVisit);
2426
2781
  }
2427
- this.view.scrollToTop();
2782
+ if (!snapshot.shouldPreserveScrollPosition) {
2783
+ this.view.scrollToTop();
2784
+ }
2428
2785
  this.view.clearSnapshotCache();
2429
2786
  }
2430
2787
  }
@@ -2457,35 +2814,27 @@ class Navigator {
2457
2814
  get restorationIdentifier() {
2458
2815
  return this.history.restorationIdentifier;
2459
2816
  }
2460
- getActionForFormSubmission({submitter: submitter, formElement: formElement}) {
2461
- return getVisitAction(submitter, formElement) || "advance";
2817
+ #getActionForFormSubmission(formSubmission, fetchResponse) {
2818
+ const {submitter: submitter, formElement: formElement} = formSubmission;
2819
+ return getVisitAction(submitter, formElement) || this.#getDefaultAction(fetchResponse);
2820
+ }
2821
+ #getDefaultAction(fetchResponse) {
2822
+ const sameLocationRedirect = fetchResponse.redirected && fetchResponse.location.href === this.location?.href;
2823
+ return sameLocationRedirect ? "replace" : "advance";
2462
2824
  }
2463
2825
  }
2464
2826
 
2465
- var PageStage;
2466
-
2467
- (function(PageStage) {
2468
- PageStage[PageStage["initial"] = 0] = "initial";
2469
- PageStage[PageStage["loading"] = 1] = "loading";
2470
- PageStage[PageStage["interactive"] = 2] = "interactive";
2471
- PageStage[PageStage["complete"] = 3] = "complete";
2472
- })(PageStage || (PageStage = {}));
2827
+ const PageStage = {
2828
+ initial: 0,
2829
+ loading: 1,
2830
+ interactive: 2,
2831
+ complete: 3
2832
+ };
2473
2833
 
2474
2834
  class PageObserver {
2835
+ stage=PageStage.initial;
2836
+ started=false;
2475
2837
  constructor(delegate) {
2476
- this.stage = PageStage.initial;
2477
- this.started = false;
2478
- this.interpretReadyState = () => {
2479
- const {readyState: readyState} = this;
2480
- if (readyState == "interactive") {
2481
- this.pageIsInteractive();
2482
- } else if (readyState == "complete") {
2483
- this.pageIsComplete();
2484
- }
2485
- };
2486
- this.pageWillUnload = () => {
2487
- this.delegate.pageWillUnload();
2488
- };
2489
2838
  this.delegate = delegate;
2490
2839
  }
2491
2840
  start() {
@@ -2505,6 +2854,14 @@ class PageObserver {
2505
2854
  this.started = false;
2506
2855
  }
2507
2856
  }
2857
+ interpretReadyState=() => {
2858
+ const {readyState: readyState} = this;
2859
+ if (readyState == "interactive") {
2860
+ this.pageIsInteractive();
2861
+ } else if (readyState == "complete") {
2862
+ this.pageIsComplete();
2863
+ }
2864
+ };
2508
2865
  pageIsInteractive() {
2509
2866
  if (this.stage == PageStage.loading) {
2510
2867
  this.stage = PageStage.interactive;
@@ -2518,20 +2875,17 @@ class PageObserver {
2518
2875
  this.delegate.pageLoaded();
2519
2876
  }
2520
2877
  }
2878
+ pageWillUnload=() => {
2879
+ this.delegate.pageWillUnload();
2880
+ };
2521
2881
  get readyState() {
2522
2882
  return document.readyState;
2523
2883
  }
2524
2884
  }
2525
2885
 
2526
2886
  class ScrollObserver {
2887
+ started=false;
2527
2888
  constructor(delegate) {
2528
- this.started = false;
2529
- this.onScroll = () => {
2530
- this.updatePosition({
2531
- x: window.pageXOffset,
2532
- y: window.pageYOffset
2533
- });
2534
- };
2535
2889
  this.delegate = delegate;
2536
2890
  }
2537
2891
  start() {
@@ -2547,6 +2901,12 @@ class ScrollObserver {
2547
2901
  this.started = false;
2548
2902
  }
2549
2903
  }
2904
+ onScroll=() => {
2905
+ this.updatePosition({
2906
+ x: window.pageXOffset,
2907
+ y: window.pageYOffset
2908
+ });
2909
+ };
2550
2910
  updatePosition(position) {
2551
2911
  this.delegate.scrollPositionChanged(position);
2552
2912
  }
@@ -2554,7 +2914,13 @@ class ScrollObserver {
2554
2914
 
2555
2915
  class StreamMessageRenderer {
2556
2916
  render({fragment: fragment}) {
2557
- Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), (() => document.documentElement.appendChild(fragment)));
2917
+ Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), (() => {
2918
+ withAutofocusFromFragment(fragment, (() => {
2919
+ withPreservedFocus((() => {
2920
+ document.documentElement.appendChild(fragment);
2921
+ }));
2922
+ }));
2923
+ }));
2558
2924
  }
2559
2925
  enteringBardo(currentPermanentElement, newPermanentElement) {
2560
2926
  newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
@@ -2577,33 +2943,67 @@ function getPermanentElementMapForFragment(fragment) {
2577
2943
  return permanentElementMap;
2578
2944
  }
2579
2945
 
2946
+ async function withAutofocusFromFragment(fragment, callback) {
2947
+ const generatedID = `turbo-stream-autofocus-${uuid()}`;
2948
+ const turboStreams = fragment.querySelectorAll("turbo-stream");
2949
+ const elementWithAutofocus = firstAutofocusableElementInStreams(turboStreams);
2950
+ let willAutofocusId = null;
2951
+ if (elementWithAutofocus) {
2952
+ if (elementWithAutofocus.id) {
2953
+ willAutofocusId = elementWithAutofocus.id;
2954
+ } else {
2955
+ willAutofocusId = generatedID;
2956
+ }
2957
+ elementWithAutofocus.id = willAutofocusId;
2958
+ }
2959
+ callback();
2960
+ await nextRepaint();
2961
+ const hasNoActiveElement = document.activeElement == null || document.activeElement == document.body;
2962
+ if (hasNoActiveElement && willAutofocusId) {
2963
+ const elementToAutofocus = document.getElementById(willAutofocusId);
2964
+ if (elementIsFocusable(elementToAutofocus)) {
2965
+ elementToAutofocus.focus();
2966
+ }
2967
+ if (elementToAutofocus && elementToAutofocus.id == generatedID) {
2968
+ elementToAutofocus.removeAttribute("id");
2969
+ }
2970
+ }
2971
+ }
2972
+
2973
+ async function withPreservedFocus(callback) {
2974
+ const [activeElementBeforeRender, activeElementAfterRender] = await around(callback, (() => document.activeElement));
2975
+ const restoreFocusTo = activeElementBeforeRender && activeElementBeforeRender.id;
2976
+ if (restoreFocusTo) {
2977
+ const elementToFocus = document.getElementById(restoreFocusTo);
2978
+ if (elementIsFocusable(elementToFocus) && elementToFocus != activeElementAfterRender) {
2979
+ elementToFocus.focus();
2980
+ }
2981
+ }
2982
+ }
2983
+
2984
+ function firstAutofocusableElementInStreams(nodeListOfStreamElements) {
2985
+ for (const streamElement of nodeListOfStreamElements) {
2986
+ const elementWithAutofocus = queryAutofocusableElement(streamElement.templateElement.content);
2987
+ if (elementWithAutofocus) return elementWithAutofocus;
2988
+ }
2989
+ return null;
2990
+ }
2991
+
2580
2992
  class StreamObserver {
2993
+ sources=new Set;
2994
+ #started=false;
2581
2995
  constructor(delegate) {
2582
- this.sources = new Set;
2583
- this.started = false;
2584
- this.inspectFetchResponse = event => {
2585
- const response = fetchResponseFromEvent(event);
2586
- if (response && fetchResponseIsStream(response)) {
2587
- event.preventDefault();
2588
- this.receiveMessageResponse(response);
2589
- }
2590
- };
2591
- this.receiveMessageEvent = event => {
2592
- if (this.started && typeof event.data == "string") {
2593
- this.receiveMessageHTML(event.data);
2594
- }
2595
- };
2596
2996
  this.delegate = delegate;
2597
2997
  }
2598
2998
  start() {
2599
- if (!this.started) {
2600
- this.started = true;
2999
+ if (!this.#started) {
3000
+ this.#started = true;
2601
3001
  addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
2602
3002
  }
2603
3003
  }
2604
3004
  stop() {
2605
- if (this.started) {
2606
- this.started = false;
3005
+ if (this.#started) {
3006
+ this.#started = false;
2607
3007
  removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
2608
3008
  }
2609
3009
  }
@@ -2622,6 +3022,18 @@ class StreamObserver {
2622
3022
  streamSourceIsConnected(source) {
2623
3023
  return this.sources.has(source);
2624
3024
  }
3025
+ inspectFetchResponse=event => {
3026
+ const response = fetchResponseFromEvent(event);
3027
+ if (response && fetchResponseIsStream(response)) {
3028
+ event.preventDefault();
3029
+ this.receiveMessageResponse(response);
3030
+ }
3031
+ };
3032
+ receiveMessageEvent=event => {
3033
+ if (this.#started && typeof event.data == "string") {
3034
+ this.receiveMessageHTML(event.data);
3035
+ }
3036
+ };
2625
3037
  async receiveMessageResponse(response) {
2626
3038
  const html = await response.responseHTML;
2627
3039
  if (html) {
@@ -2634,16 +3046,14 @@ class StreamObserver {
2634
3046
  }
2635
3047
 
2636
3048
  function fetchResponseFromEvent(event) {
2637
- var _a;
2638
- const fetchResponse = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchResponse;
3049
+ const fetchResponse = event.detail?.fetchResponse;
2639
3050
  if (fetchResponse instanceof FetchResponse) {
2640
3051
  return fetchResponse;
2641
3052
  }
2642
3053
  }
2643
3054
 
2644
3055
  function fetchResponseIsStream(response) {
2645
- var _a;
2646
- const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
3056
+ const contentType = response.contentType ?? "";
2647
3057
  return contentType.startsWith(StreamMessage.contentType);
2648
3058
  }
2649
3059
 
@@ -2678,6 +3088,546 @@ class ErrorRenderer extends Renderer {
2678
3088
  }
2679
3089
  }
2680
3090
 
3091
+ var Idiomorph = function() {
3092
+ let EMPTY_SET = new Set;
3093
+ let defaults = {
3094
+ morphStyle: "outerHTML",
3095
+ callbacks: {
3096
+ beforeNodeAdded: noOp,
3097
+ afterNodeAdded: noOp,
3098
+ beforeNodeMorphed: noOp,
3099
+ afterNodeMorphed: noOp,
3100
+ beforeNodeRemoved: noOp,
3101
+ afterNodeRemoved: noOp,
3102
+ beforeAttributeUpdated: noOp
3103
+ },
3104
+ head: {
3105
+ style: "merge",
3106
+ shouldPreserve: function(elt) {
3107
+ return elt.getAttribute("im-preserve") === "true";
3108
+ },
3109
+ shouldReAppend: function(elt) {
3110
+ return elt.getAttribute("im-re-append") === "true";
3111
+ },
3112
+ shouldRemove: noOp,
3113
+ afterHeadMorphed: noOp
3114
+ }
3115
+ };
3116
+ function morph(oldNode, newContent, config = {}) {
3117
+ if (oldNode instanceof Document) {
3118
+ oldNode = oldNode.documentElement;
3119
+ }
3120
+ if (typeof newContent === "string") {
3121
+ newContent = parseContent(newContent);
3122
+ }
3123
+ let normalizedContent = normalizeContent(newContent);
3124
+ let ctx = createMorphContext(oldNode, normalizedContent, config);
3125
+ return morphNormalizedContent(oldNode, normalizedContent, ctx);
3126
+ }
3127
+ function morphNormalizedContent(oldNode, normalizedNewContent, ctx) {
3128
+ if (ctx.head.block) {
3129
+ let oldHead = oldNode.querySelector("head");
3130
+ let newHead = normalizedNewContent.querySelector("head");
3131
+ if (oldHead && newHead) {
3132
+ let promises = handleHeadElement(newHead, oldHead, ctx);
3133
+ Promise.all(promises).then((function() {
3134
+ morphNormalizedContent(oldNode, normalizedNewContent, Object.assign(ctx, {
3135
+ head: {
3136
+ block: false,
3137
+ ignore: true
3138
+ }
3139
+ }));
3140
+ }));
3141
+ return;
3142
+ }
3143
+ }
3144
+ if (ctx.morphStyle === "innerHTML") {
3145
+ morphChildren(normalizedNewContent, oldNode, ctx);
3146
+ return oldNode.children;
3147
+ } else if (ctx.morphStyle === "outerHTML" || ctx.morphStyle == null) {
3148
+ let bestMatch = findBestNodeMatch(normalizedNewContent, oldNode, ctx);
3149
+ let previousSibling = bestMatch?.previousSibling;
3150
+ let nextSibling = bestMatch?.nextSibling;
3151
+ let morphedNode = morphOldNodeTo(oldNode, bestMatch, ctx);
3152
+ if (bestMatch) {
3153
+ return insertSiblings(previousSibling, morphedNode, nextSibling);
3154
+ } else {
3155
+ return [];
3156
+ }
3157
+ } else {
3158
+ throw "Do not understand how to morph style " + ctx.morphStyle;
3159
+ }
3160
+ }
3161
+ function ignoreValueOfActiveElement(possibleActiveElement, ctx) {
3162
+ return ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body;
3163
+ }
3164
+ function morphOldNodeTo(oldNode, newContent, ctx) {
3165
+ if (ctx.ignoreActive && oldNode === document.activeElement) ; else if (newContent == null) {
3166
+ if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode;
3167
+ oldNode.remove();
3168
+ ctx.callbacks.afterNodeRemoved(oldNode);
3169
+ return null;
3170
+ } else if (!isSoftMatch(oldNode, newContent)) {
3171
+ if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode;
3172
+ if (ctx.callbacks.beforeNodeAdded(newContent) === false) return oldNode;
3173
+ oldNode.parentElement.replaceChild(newContent, oldNode);
3174
+ ctx.callbacks.afterNodeAdded(newContent);
3175
+ ctx.callbacks.afterNodeRemoved(oldNode);
3176
+ return newContent;
3177
+ } else {
3178
+ if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) return oldNode;
3179
+ if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) ; else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") {
3180
+ handleHeadElement(newContent, oldNode, ctx);
3181
+ } else {
3182
+ syncNodeFrom(newContent, oldNode, ctx);
3183
+ if (!ignoreValueOfActiveElement(oldNode, ctx)) {
3184
+ morphChildren(newContent, oldNode, ctx);
3185
+ }
3186
+ }
3187
+ ctx.callbacks.afterNodeMorphed(oldNode, newContent);
3188
+ return oldNode;
3189
+ }
3190
+ }
3191
+ function morphChildren(newParent, oldParent, ctx) {
3192
+ let nextNewChild = newParent.firstChild;
3193
+ let insertionPoint = oldParent.firstChild;
3194
+ let newChild;
3195
+ while (nextNewChild) {
3196
+ newChild = nextNewChild;
3197
+ nextNewChild = newChild.nextSibling;
3198
+ if (insertionPoint == null) {
3199
+ if (ctx.callbacks.beforeNodeAdded(newChild) === false) return;
3200
+ oldParent.appendChild(newChild);
3201
+ ctx.callbacks.afterNodeAdded(newChild);
3202
+ removeIdsFromConsideration(ctx, newChild);
3203
+ continue;
3204
+ }
3205
+ if (isIdSetMatch(newChild, insertionPoint, ctx)) {
3206
+ morphOldNodeTo(insertionPoint, newChild, ctx);
3207
+ insertionPoint = insertionPoint.nextSibling;
3208
+ removeIdsFromConsideration(ctx, newChild);
3209
+ continue;
3210
+ }
3211
+ let idSetMatch = findIdSetMatch(newParent, oldParent, newChild, insertionPoint, ctx);
3212
+ if (idSetMatch) {
3213
+ insertionPoint = removeNodesBetween(insertionPoint, idSetMatch, ctx);
3214
+ morphOldNodeTo(idSetMatch, newChild, ctx);
3215
+ removeIdsFromConsideration(ctx, newChild);
3216
+ continue;
3217
+ }
3218
+ let softMatch = findSoftMatch(newParent, oldParent, newChild, insertionPoint, ctx);
3219
+ if (softMatch) {
3220
+ insertionPoint = removeNodesBetween(insertionPoint, softMatch, ctx);
3221
+ morphOldNodeTo(softMatch, newChild, ctx);
3222
+ removeIdsFromConsideration(ctx, newChild);
3223
+ continue;
3224
+ }
3225
+ if (ctx.callbacks.beforeNodeAdded(newChild) === false) return;
3226
+ oldParent.insertBefore(newChild, insertionPoint);
3227
+ ctx.callbacks.afterNodeAdded(newChild);
3228
+ removeIdsFromConsideration(ctx, newChild);
3229
+ }
3230
+ while (insertionPoint !== null) {
3231
+ let tempNode = insertionPoint;
3232
+ insertionPoint = insertionPoint.nextSibling;
3233
+ removeNode(tempNode, ctx);
3234
+ }
3235
+ }
3236
+ function ignoreAttribute(attr, to, updateType, ctx) {
3237
+ if (attr === "value" && ctx.ignoreActiveValue && to === document.activeElement) {
3238
+ return true;
3239
+ }
3240
+ return ctx.callbacks.beforeAttributeUpdated(attr, to, updateType) === false;
3241
+ }
3242
+ function syncNodeFrom(from, to, ctx) {
3243
+ let type = from.nodeType;
3244
+ if (type === 1) {
3245
+ const fromAttributes = from.attributes;
3246
+ const toAttributes = to.attributes;
3247
+ for (const fromAttribute of fromAttributes) {
3248
+ if (ignoreAttribute(fromAttribute.name, to, "update", ctx)) {
3249
+ continue;
3250
+ }
3251
+ if (to.getAttribute(fromAttribute.name) !== fromAttribute.value) {
3252
+ to.setAttribute(fromAttribute.name, fromAttribute.value);
3253
+ }
3254
+ }
3255
+ for (let i = toAttributes.length - 1; 0 <= i; i--) {
3256
+ const toAttribute = toAttributes[i];
3257
+ if (ignoreAttribute(toAttribute.name, to, "remove", ctx)) {
3258
+ continue;
3259
+ }
3260
+ if (!from.hasAttribute(toAttribute.name)) {
3261
+ to.removeAttribute(toAttribute.name);
3262
+ }
3263
+ }
3264
+ }
3265
+ if (type === 8 || type === 3) {
3266
+ if (to.nodeValue !== from.nodeValue) {
3267
+ to.nodeValue = from.nodeValue;
3268
+ }
3269
+ }
3270
+ if (!ignoreValueOfActiveElement(to, ctx)) {
3271
+ syncInputValue(from, to, ctx);
3272
+ }
3273
+ }
3274
+ function syncBooleanAttribute(from, to, attributeName, ctx) {
3275
+ if (from[attributeName] !== to[attributeName]) {
3276
+ let ignoreUpdate = ignoreAttribute(attributeName, to, "update", ctx);
3277
+ if (!ignoreUpdate) {
3278
+ to[attributeName] = from[attributeName];
3279
+ }
3280
+ if (from[attributeName]) {
3281
+ if (!ignoreUpdate) {
3282
+ to.setAttribute(attributeName, from[attributeName]);
3283
+ }
3284
+ } else {
3285
+ if (!ignoreAttribute(attributeName, to, "remove", ctx)) {
3286
+ to.removeAttribute(attributeName);
3287
+ }
3288
+ }
3289
+ }
3290
+ }
3291
+ function syncInputValue(from, to, ctx) {
3292
+ if (from instanceof HTMLInputElement && to instanceof HTMLInputElement && from.type !== "file") {
3293
+ let fromValue = from.value;
3294
+ let toValue = to.value;
3295
+ syncBooleanAttribute(from, to, "checked", ctx);
3296
+ syncBooleanAttribute(from, to, "disabled", ctx);
3297
+ if (!from.hasAttribute("value")) {
3298
+ if (!ignoreAttribute("value", to, "remove", ctx)) {
3299
+ to.value = "";
3300
+ to.removeAttribute("value");
3301
+ }
3302
+ } else if (fromValue !== toValue) {
3303
+ if (!ignoreAttribute("value", to, "update", ctx)) {
3304
+ to.setAttribute("value", fromValue);
3305
+ to.value = fromValue;
3306
+ }
3307
+ }
3308
+ } else if (from instanceof HTMLOptionElement) {
3309
+ syncBooleanAttribute(from, to, "selected", ctx);
3310
+ } else if (from instanceof HTMLTextAreaElement && to instanceof HTMLTextAreaElement) {
3311
+ let fromValue = from.value;
3312
+ let toValue = to.value;
3313
+ if (ignoreAttribute("value", to, "update", ctx)) {
3314
+ return;
3315
+ }
3316
+ if (fromValue !== toValue) {
3317
+ to.value = fromValue;
3318
+ }
3319
+ if (to.firstChild && to.firstChild.nodeValue !== fromValue) {
3320
+ to.firstChild.nodeValue = fromValue;
3321
+ }
3322
+ }
3323
+ }
3324
+ function handleHeadElement(newHeadTag, currentHead, ctx) {
3325
+ let added = [];
3326
+ let removed = [];
3327
+ let preserved = [];
3328
+ let nodesToAppend = [];
3329
+ let headMergeStyle = ctx.head.style;
3330
+ let srcToNewHeadNodes = new Map;
3331
+ for (const newHeadChild of newHeadTag.children) {
3332
+ srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
3333
+ }
3334
+ for (const currentHeadElt of currentHead.children) {
3335
+ let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
3336
+ let isReAppended = ctx.head.shouldReAppend(currentHeadElt);
3337
+ let isPreserved = ctx.head.shouldPreserve(currentHeadElt);
3338
+ if (inNewContent || isPreserved) {
3339
+ if (isReAppended) {
3340
+ removed.push(currentHeadElt);
3341
+ } else {
3342
+ srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
3343
+ preserved.push(currentHeadElt);
3344
+ }
3345
+ } else {
3346
+ if (headMergeStyle === "append") {
3347
+ if (isReAppended) {
3348
+ removed.push(currentHeadElt);
3349
+ nodesToAppend.push(currentHeadElt);
3350
+ }
3351
+ } else {
3352
+ if (ctx.head.shouldRemove(currentHeadElt) !== false) {
3353
+ removed.push(currentHeadElt);
3354
+ }
3355
+ }
3356
+ }
3357
+ }
3358
+ nodesToAppend.push(...srcToNewHeadNodes.values());
3359
+ let promises = [];
3360
+ for (const newNode of nodesToAppend) {
3361
+ let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild;
3362
+ if (ctx.callbacks.beforeNodeAdded(newElt) !== false) {
3363
+ if (newElt.href || newElt.src) {
3364
+ let resolve = null;
3365
+ let promise = new Promise((function(_resolve) {
3366
+ resolve = _resolve;
3367
+ }));
3368
+ newElt.addEventListener("load", (function() {
3369
+ resolve();
3370
+ }));
3371
+ promises.push(promise);
3372
+ }
3373
+ currentHead.appendChild(newElt);
3374
+ ctx.callbacks.afterNodeAdded(newElt);
3375
+ added.push(newElt);
3376
+ }
3377
+ }
3378
+ for (const removedElement of removed) {
3379
+ if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) {
3380
+ currentHead.removeChild(removedElement);
3381
+ ctx.callbacks.afterNodeRemoved(removedElement);
3382
+ }
3383
+ }
3384
+ ctx.head.afterHeadMorphed(currentHead, {
3385
+ added: added,
3386
+ kept: preserved,
3387
+ removed: removed
3388
+ });
3389
+ return promises;
3390
+ }
3391
+ function noOp() {}
3392
+ function mergeDefaults(config) {
3393
+ let finalConfig = {};
3394
+ Object.assign(finalConfig, defaults);
3395
+ Object.assign(finalConfig, config);
3396
+ finalConfig.callbacks = {};
3397
+ Object.assign(finalConfig.callbacks, defaults.callbacks);
3398
+ Object.assign(finalConfig.callbacks, config.callbacks);
3399
+ finalConfig.head = {};
3400
+ Object.assign(finalConfig.head, defaults.head);
3401
+ Object.assign(finalConfig.head, config.head);
3402
+ return finalConfig;
3403
+ }
3404
+ function createMorphContext(oldNode, newContent, config) {
3405
+ config = mergeDefaults(config);
3406
+ return {
3407
+ target: oldNode,
3408
+ newContent: newContent,
3409
+ config: config,
3410
+ morphStyle: config.morphStyle,
3411
+ ignoreActive: config.ignoreActive,
3412
+ ignoreActiveValue: config.ignoreActiveValue,
3413
+ idMap: createIdMap(oldNode, newContent),
3414
+ deadIds: new Set,
3415
+ callbacks: config.callbacks,
3416
+ head: config.head
3417
+ };
3418
+ }
3419
+ function isIdSetMatch(node1, node2, ctx) {
3420
+ if (node1 == null || node2 == null) {
3421
+ return false;
3422
+ }
3423
+ if (node1.nodeType === node2.nodeType && node1.tagName === node2.tagName) {
3424
+ if (node1.id !== "" && node1.id === node2.id) {
3425
+ return true;
3426
+ } else {
3427
+ return getIdIntersectionCount(ctx, node1, node2) > 0;
3428
+ }
3429
+ }
3430
+ return false;
3431
+ }
3432
+ function isSoftMatch(node1, node2) {
3433
+ if (node1 == null || node2 == null) {
3434
+ return false;
3435
+ }
3436
+ return node1.nodeType === node2.nodeType && node1.tagName === node2.tagName;
3437
+ }
3438
+ function removeNodesBetween(startInclusive, endExclusive, ctx) {
3439
+ while (startInclusive !== endExclusive) {
3440
+ let tempNode = startInclusive;
3441
+ startInclusive = startInclusive.nextSibling;
3442
+ removeNode(tempNode, ctx);
3443
+ }
3444
+ removeIdsFromConsideration(ctx, endExclusive);
3445
+ return endExclusive.nextSibling;
3446
+ }
3447
+ function findIdSetMatch(newContent, oldParent, newChild, insertionPoint, ctx) {
3448
+ let newChildPotentialIdCount = getIdIntersectionCount(ctx, newChild, oldParent);
3449
+ let potentialMatch = null;
3450
+ if (newChildPotentialIdCount > 0) {
3451
+ let potentialMatch = insertionPoint;
3452
+ let otherMatchCount = 0;
3453
+ while (potentialMatch != null) {
3454
+ if (isIdSetMatch(newChild, potentialMatch, ctx)) {
3455
+ return potentialMatch;
3456
+ }
3457
+ otherMatchCount += getIdIntersectionCount(ctx, potentialMatch, newContent);
3458
+ if (otherMatchCount > newChildPotentialIdCount) {
3459
+ return null;
3460
+ }
3461
+ potentialMatch = potentialMatch.nextSibling;
3462
+ }
3463
+ }
3464
+ return potentialMatch;
3465
+ }
3466
+ function findSoftMatch(newContent, oldParent, newChild, insertionPoint, ctx) {
3467
+ let potentialSoftMatch = insertionPoint;
3468
+ let nextSibling = newChild.nextSibling;
3469
+ let siblingSoftMatchCount = 0;
3470
+ while (potentialSoftMatch != null) {
3471
+ if (getIdIntersectionCount(ctx, potentialSoftMatch, newContent) > 0) {
3472
+ return null;
3473
+ }
3474
+ if (isSoftMatch(newChild, potentialSoftMatch)) {
3475
+ return potentialSoftMatch;
3476
+ }
3477
+ if (isSoftMatch(nextSibling, potentialSoftMatch)) {
3478
+ siblingSoftMatchCount++;
3479
+ nextSibling = nextSibling.nextSibling;
3480
+ if (siblingSoftMatchCount >= 2) {
3481
+ return null;
3482
+ }
3483
+ }
3484
+ potentialSoftMatch = potentialSoftMatch.nextSibling;
3485
+ }
3486
+ return potentialSoftMatch;
3487
+ }
3488
+ function parseContent(newContent) {
3489
+ let parser = new DOMParser;
3490
+ let contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, "");
3491
+ if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) {
3492
+ let content = parser.parseFromString(newContent, "text/html");
3493
+ if (contentWithSvgsRemoved.match(/<\/html>/)) {
3494
+ content.generatedByIdiomorph = true;
3495
+ return content;
3496
+ } else {
3497
+ let htmlElement = content.firstChild;
3498
+ if (htmlElement) {
3499
+ htmlElement.generatedByIdiomorph = true;
3500
+ return htmlElement;
3501
+ } else {
3502
+ return null;
3503
+ }
3504
+ }
3505
+ } else {
3506
+ let responseDoc = parser.parseFromString("<body><template>" + newContent + "</template></body>", "text/html");
3507
+ let content = responseDoc.body.querySelector("template").content;
3508
+ content.generatedByIdiomorph = true;
3509
+ return content;
3510
+ }
3511
+ }
3512
+ function normalizeContent(newContent) {
3513
+ if (newContent == null) {
3514
+ const dummyParent = document.createElement("div");
3515
+ return dummyParent;
3516
+ } else if (newContent.generatedByIdiomorph) {
3517
+ return newContent;
3518
+ } else if (newContent instanceof Node) {
3519
+ const dummyParent = document.createElement("div");
3520
+ dummyParent.append(newContent);
3521
+ return dummyParent;
3522
+ } else {
3523
+ const dummyParent = document.createElement("div");
3524
+ for (const elt of [ ...newContent ]) {
3525
+ dummyParent.append(elt);
3526
+ }
3527
+ return dummyParent;
3528
+ }
3529
+ }
3530
+ function insertSiblings(previousSibling, morphedNode, nextSibling) {
3531
+ let stack = [];
3532
+ let added = [];
3533
+ while (previousSibling != null) {
3534
+ stack.push(previousSibling);
3535
+ previousSibling = previousSibling.previousSibling;
3536
+ }
3537
+ while (stack.length > 0) {
3538
+ let node = stack.pop();
3539
+ added.push(node);
3540
+ morphedNode.parentElement.insertBefore(node, morphedNode);
3541
+ }
3542
+ added.push(morphedNode);
3543
+ while (nextSibling != null) {
3544
+ stack.push(nextSibling);
3545
+ added.push(nextSibling);
3546
+ nextSibling = nextSibling.nextSibling;
3547
+ }
3548
+ while (stack.length > 0) {
3549
+ morphedNode.parentElement.insertBefore(stack.pop(), morphedNode.nextSibling);
3550
+ }
3551
+ return added;
3552
+ }
3553
+ function findBestNodeMatch(newContent, oldNode, ctx) {
3554
+ let currentElement;
3555
+ currentElement = newContent.firstChild;
3556
+ let bestElement = currentElement;
3557
+ let score = 0;
3558
+ while (currentElement) {
3559
+ let newScore = scoreElement(currentElement, oldNode, ctx);
3560
+ if (newScore > score) {
3561
+ bestElement = currentElement;
3562
+ score = newScore;
3563
+ }
3564
+ currentElement = currentElement.nextSibling;
3565
+ }
3566
+ return bestElement;
3567
+ }
3568
+ function scoreElement(node1, node2, ctx) {
3569
+ if (isSoftMatch(node1, node2)) {
3570
+ return .5 + getIdIntersectionCount(ctx, node1, node2);
3571
+ }
3572
+ return 0;
3573
+ }
3574
+ function removeNode(tempNode, ctx) {
3575
+ removeIdsFromConsideration(ctx, tempNode);
3576
+ if (ctx.callbacks.beforeNodeRemoved(tempNode) === false) return;
3577
+ tempNode.remove();
3578
+ ctx.callbacks.afterNodeRemoved(tempNode);
3579
+ }
3580
+ function isIdInConsideration(ctx, id) {
3581
+ return !ctx.deadIds.has(id);
3582
+ }
3583
+ function idIsWithinNode(ctx, id, targetNode) {
3584
+ let idSet = ctx.idMap.get(targetNode) || EMPTY_SET;
3585
+ return idSet.has(id);
3586
+ }
3587
+ function removeIdsFromConsideration(ctx, node) {
3588
+ let idSet = ctx.idMap.get(node) || EMPTY_SET;
3589
+ for (const id of idSet) {
3590
+ ctx.deadIds.add(id);
3591
+ }
3592
+ }
3593
+ function getIdIntersectionCount(ctx, node1, node2) {
3594
+ let sourceSet = ctx.idMap.get(node1) || EMPTY_SET;
3595
+ let matchCount = 0;
3596
+ for (const id of sourceSet) {
3597
+ if (isIdInConsideration(ctx, id) && idIsWithinNode(ctx, id, node2)) {
3598
+ ++matchCount;
3599
+ }
3600
+ }
3601
+ return matchCount;
3602
+ }
3603
+ function populateIdMapForNode(node, idMap) {
3604
+ let nodeParent = node.parentElement;
3605
+ let idElements = node.querySelectorAll("[id]");
3606
+ for (const elt of idElements) {
3607
+ let current = elt;
3608
+ while (current !== nodeParent && current != null) {
3609
+ let idSet = idMap.get(current);
3610
+ if (idSet == null) {
3611
+ idSet = new Set;
3612
+ idMap.set(current, idSet);
3613
+ }
3614
+ idSet.add(elt.id);
3615
+ current = current.parentElement;
3616
+ }
3617
+ }
3618
+ }
3619
+ function createIdMap(oldContent, newContent) {
3620
+ let idMap = new Map;
3621
+ populateIdMapForNode(oldContent, idMap);
3622
+ populateIdMapForNode(newContent, idMap);
3623
+ return idMap;
3624
+ }
3625
+ return {
3626
+ morph: morph,
3627
+ defaults: defaults
3628
+ };
3629
+ }();
3630
+
2681
3631
  class PageRenderer extends Renderer {
2682
3632
  static renderElement(currentElement, newElement) {
2683
3633
  if (document.body && newElement instanceof HTMLBodyElement) {
@@ -2702,6 +3652,7 @@ class PageRenderer extends Renderer {
2702
3652
  }
2703
3653
  }
2704
3654
  async prepareToRender() {
3655
+ this.#setLanguage();
2705
3656
  await this.mergeHead();
2706
3657
  }
2707
3658
  async render() {
@@ -2724,12 +3675,24 @@ class PageRenderer extends Renderer {
2724
3675
  get newElement() {
2725
3676
  return this.newSnapshot.element;
2726
3677
  }
3678
+ #setLanguage() {
3679
+ const {documentElement: documentElement} = this.currentSnapshot;
3680
+ const {lang: lang} = this.newSnapshot;
3681
+ if (lang) {
3682
+ documentElement.setAttribute("lang", lang);
3683
+ } else {
3684
+ documentElement.removeAttribute("lang");
3685
+ }
3686
+ }
2727
3687
  async mergeHead() {
2728
3688
  const mergedHeadElements = this.mergeProvisionalElements();
2729
3689
  const newStylesheetElements = this.copyNewHeadStylesheetElements();
2730
3690
  this.copyNewHeadScriptElements();
2731
3691
  await mergedHeadElements;
2732
3692
  await newStylesheetElements;
3693
+ if (this.willRender) {
3694
+ this.removeUnusedDynamicStylesheetElements();
3695
+ }
2733
3696
  }
2734
3697
  async replaceBody() {
2735
3698
  await this.preservingPermanentElements((async () => {
@@ -2753,6 +3716,11 @@ class PageRenderer extends Renderer {
2753
3716
  document.head.appendChild(activateScriptElement(element));
2754
3717
  }
2755
3718
  }
3719
+ removeUnusedDynamicStylesheetElements() {
3720
+ for (const element of this.unusedDynamicStylesheetElements) {
3721
+ document.head.removeChild(element);
3722
+ }
3723
+ }
2756
3724
  async mergeProvisionalElements() {
2757
3725
  const newHeadElements = [ ...this.newHeadProvisionalElements ];
2758
3726
  for (const element of this.currentHeadProvisionalElements) {
@@ -2805,6 +3773,12 @@ class PageRenderer extends Renderer {
2805
3773
  async assignNewBody() {
2806
3774
  await this.renderElement(this.currentElement, this.newElement);
2807
3775
  }
3776
+ get unusedDynamicStylesheetElements() {
3777
+ return this.oldHeadStylesheetElements.filter((element => element.getAttribute("data-turbo-track") === "dynamic"));
3778
+ }
3779
+ get oldHeadStylesheetElements() {
3780
+ return this.currentHeadSnapshot.getStylesheetElementsNotInSnapshot(this.newHeadSnapshot);
3781
+ }
2808
3782
  get newHeadStylesheetElements() {
2809
3783
  return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
2810
3784
  }
@@ -2822,10 +3796,113 @@ class PageRenderer extends Renderer {
2822
3796
  }
2823
3797
  }
2824
3798
 
3799
+ class MorphRenderer extends PageRenderer {
3800
+ async render() {
3801
+ if (this.willRender) await this.#morphBody();
3802
+ }
3803
+ get renderMethod() {
3804
+ return "morph";
3805
+ }
3806
+ async #morphBody() {
3807
+ this.#morphElements(this.currentElement, this.newElement);
3808
+ this.#reloadRemoteFrames();
3809
+ dispatch("turbo:morph", {
3810
+ detail: {
3811
+ currentElement: this.currentElement,
3812
+ newElement: this.newElement
3813
+ }
3814
+ });
3815
+ }
3816
+ #morphElements(currentElement, newElement, morphStyle = "outerHTML") {
3817
+ this.isMorphingTurboFrame = this.#isFrameReloadedWithMorph(currentElement);
3818
+ Idiomorph.morph(currentElement, newElement, {
3819
+ ignoreActiveValue: true,
3820
+ morphStyle: morphStyle,
3821
+ callbacks: {
3822
+ beforeNodeAdded: this.#shouldAddElement,
3823
+ beforeNodeMorphed: this.#shouldMorphElement,
3824
+ beforeAttributeUpdated: this.#shouldUpdateAttribute,
3825
+ beforeNodeRemoved: this.#shouldRemoveElement,
3826
+ afterNodeMorphed: this.#didMorphElement
3827
+ }
3828
+ });
3829
+ }
3830
+ #shouldAddElement=node => !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id));
3831
+ #shouldMorphElement=(oldNode, newNode) => {
3832
+ if (oldNode instanceof HTMLElement) {
3833
+ if (!oldNode.hasAttribute("data-turbo-permanent") && (this.isMorphingTurboFrame || !this.#isFrameReloadedWithMorph(oldNode))) {
3834
+ const event = dispatch("turbo:before-morph-element", {
3835
+ cancelable: true,
3836
+ target: oldNode,
3837
+ detail: {
3838
+ newElement: newNode
3839
+ }
3840
+ });
3841
+ return !event.defaultPrevented;
3842
+ } else {
3843
+ return false;
3844
+ }
3845
+ }
3846
+ };
3847
+ #shouldUpdateAttribute=(attributeName, target, mutationType) => {
3848
+ const event = dispatch("turbo:before-morph-attribute", {
3849
+ cancelable: true,
3850
+ target: target,
3851
+ detail: {
3852
+ attributeName: attributeName,
3853
+ mutationType: mutationType
3854
+ }
3855
+ });
3856
+ return !event.defaultPrevented;
3857
+ };
3858
+ #didMorphElement=(oldNode, newNode) => {
3859
+ if (newNode instanceof HTMLElement) {
3860
+ dispatch("turbo:morph-element", {
3861
+ target: oldNode,
3862
+ detail: {
3863
+ newElement: newNode
3864
+ }
3865
+ });
3866
+ }
3867
+ };
3868
+ #shouldRemoveElement=node => this.#shouldMorphElement(node);
3869
+ #reloadRemoteFrames() {
3870
+ this.#remoteFrames().forEach((frame => {
3871
+ if (this.#isFrameReloadedWithMorph(frame)) {
3872
+ this.#renderFrameWithMorph(frame);
3873
+ frame.reload();
3874
+ }
3875
+ }));
3876
+ }
3877
+ #renderFrameWithMorph(frame) {
3878
+ frame.addEventListener("turbo:before-frame-render", (event => {
3879
+ event.detail.render = this.#morphFrameUpdate;
3880
+ }), {
3881
+ once: true
3882
+ });
3883
+ }
3884
+ #morphFrameUpdate=(currentElement, newElement) => {
3885
+ dispatch("turbo:before-frame-morph", {
3886
+ target: currentElement,
3887
+ detail: {
3888
+ currentElement: currentElement,
3889
+ newElement: newElement
3890
+ }
3891
+ });
3892
+ this.#morphElements(currentElement, newElement.children, "innerHTML");
3893
+ };
3894
+ #isFrameReloadedWithMorph(element) {
3895
+ return element.src && element.refresh === "morph";
3896
+ }
3897
+ #remoteFrames() {
3898
+ return Array.from(document.querySelectorAll("turbo-frame[src]")).filter((frame => !frame.closest("[data-turbo-permanent]")));
3899
+ }
3900
+ }
3901
+
2825
3902
  class SnapshotCache {
3903
+ keys=[];
3904
+ snapshots={};
2826
3905
  constructor(size) {
2827
- this.keys = [];
2828
- this.snapshots = {};
2829
3906
  this.size = size;
2830
3907
  }
2831
3908
  has(location) {
@@ -2867,23 +3944,25 @@ class SnapshotCache {
2867
3944
  }
2868
3945
 
2869
3946
  class PageView extends View {
2870
- constructor() {
2871
- super(...arguments);
2872
- this.snapshotCache = new SnapshotCache(10);
2873
- this.lastRenderedLocation = new URL(location.href);
2874
- this.forceReloaded = false;
3947
+ snapshotCache=new SnapshotCache(10);
3948
+ lastRenderedLocation=new URL(location.href);
3949
+ forceReloaded=false;
3950
+ shouldTransitionTo(newSnapshot) {
3951
+ return this.snapshot.prefersViewTransitions && newSnapshot.prefersViewTransitions;
2875
3952
  }
2876
3953
  renderPage(snapshot, isPreview = false, willRender = true, visit) {
2877
- const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
3954
+ const shouldMorphPage = this.isPageRefresh(visit) && this.snapshot.shouldMorphPage;
3955
+ const rendererClass = shouldMorphPage ? MorphRenderer : PageRenderer;
3956
+ const renderer = new rendererClass(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
2878
3957
  if (!renderer.shouldRender) {
2879
3958
  this.forceReloaded = true;
2880
3959
  } else {
2881
- visit === null || visit === void 0 ? void 0 : visit.changeHistory();
3960
+ visit?.changeHistory();
2882
3961
  }
2883
3962
  return this.render(renderer);
2884
3963
  }
2885
3964
  renderError(snapshot, visit) {
2886
- visit === null || visit === void 0 ? void 0 : visit.changeHistory();
3965
+ visit?.changeHistory();
2887
3966
  const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
2888
3967
  return this.render(renderer);
2889
3968
  }
@@ -2903,31 +3982,38 @@ class PageView extends View {
2903
3982
  getCachedSnapshotForLocation(location) {
2904
3983
  return this.snapshotCache.get(location);
2905
3984
  }
3985
+ isPageRefresh(visit) {
3986
+ return !visit || this.lastRenderedLocation.pathname === visit.location.pathname && visit.action === "replace";
3987
+ }
3988
+ shouldPreserveScrollPosition(visit) {
3989
+ return this.isPageRefresh(visit) && this.snapshot.shouldPreserveScrollPosition;
3990
+ }
2906
3991
  get snapshot() {
2907
3992
  return PageSnapshot.fromElement(this.element);
2908
3993
  }
2909
3994
  }
2910
3995
 
2911
3996
  class Preloader {
2912
- constructor(delegate) {
2913
- this.selector = "a[data-turbo-preload]";
3997
+ selector="a[data-turbo-preload]";
3998
+ constructor(delegate, snapshotCache) {
2914
3999
  this.delegate = delegate;
2915
- }
2916
- get snapshotCache() {
2917
- return this.delegate.navigator.view.snapshotCache;
4000
+ this.snapshotCache = snapshotCache;
2918
4001
  }
2919
4002
  start() {
2920
4003
  if (document.readyState === "loading") {
2921
- return document.addEventListener("DOMContentLoaded", (() => {
2922
- this.preloadOnLoadLinksForView(document.body);
2923
- }));
4004
+ document.addEventListener("DOMContentLoaded", this.#preloadAll);
2924
4005
  } else {
2925
4006
  this.preloadOnLoadLinksForView(document.body);
2926
4007
  }
2927
4008
  }
4009
+ stop() {
4010
+ document.removeEventListener("DOMContentLoaded", this.#preloadAll);
4011
+ }
2928
4012
  preloadOnLoadLinksForView(element) {
2929
4013
  for (const link of element.querySelectorAll(this.selector)) {
2930
- this.preloadURL(link);
4014
+ if (this.delegate.shouldPreloadLink(link)) {
4015
+ this.preloadURL(link);
4016
+ }
2931
4017
  }
2932
4018
  }
2933
4019
  async preloadURL(link) {
@@ -2935,46 +4021,83 @@ class Preloader {
2935
4021
  if (this.snapshotCache.has(location)) {
2936
4022
  return;
2937
4023
  }
4024
+ const fetchRequest = new FetchRequest(this, FetchMethod.get, location, new URLSearchParams, link);
4025
+ await fetchRequest.perform();
4026
+ }
4027
+ prepareRequest(fetchRequest) {
4028
+ fetchRequest.headers["X-Sec-Purpose"] = "prefetch";
4029
+ }
4030
+ async requestSucceededWithResponse(fetchRequest, fetchResponse) {
2938
4031
  try {
2939
- const response = await fetch(location.toString(), {
2940
- headers: {
2941
- "VND.PREFETCH": "true",
2942
- Accept: "text/html"
2943
- }
2944
- });
2945
- const responseText = await response.text();
2946
- const snapshot = PageSnapshot.fromHTMLString(responseText);
2947
- this.snapshotCache.put(location, snapshot);
4032
+ const responseHTML = await fetchResponse.responseHTML;
4033
+ const snapshot = PageSnapshot.fromHTMLString(responseHTML);
4034
+ this.snapshotCache.put(fetchRequest.url, snapshot);
2948
4035
  } catch (_) {}
2949
4036
  }
4037
+ requestStarted(fetchRequest) {}
4038
+ requestErrored(fetchRequest) {}
4039
+ requestFinished(fetchRequest) {}
4040
+ requestPreventedHandlingResponse(fetchRequest, fetchResponse) {}
4041
+ requestFailedWithResponse(fetchRequest, fetchResponse) {}
4042
+ #preloadAll=() => {
4043
+ this.preloadOnLoadLinksForView(document.body);
4044
+ };
4045
+ }
4046
+
4047
+ class Cache {
4048
+ constructor(session) {
4049
+ this.session = session;
4050
+ }
4051
+ clear() {
4052
+ this.session.clearCache();
4053
+ }
4054
+ resetCacheControl() {
4055
+ this.#setCacheControl("");
4056
+ }
4057
+ exemptPageFromCache() {
4058
+ this.#setCacheControl("no-cache");
4059
+ }
4060
+ exemptPageFromPreview() {
4061
+ this.#setCacheControl("no-preview");
4062
+ }
4063
+ #setCacheControl(value) {
4064
+ setMetaContent("turbo-cache-control", value);
4065
+ }
2950
4066
  }
2951
4067
 
2952
4068
  class Session {
2953
- constructor() {
2954
- this.navigator = new Navigator(this);
2955
- this.history = new History(this);
2956
- this.preloader = new Preloader(this);
2957
- this.view = new PageView(this, document.documentElement);
2958
- this.adapter = new BrowserAdapter(this);
2959
- this.pageObserver = new PageObserver(this);
2960
- this.cacheObserver = new CacheObserver;
2961
- this.linkClickObserver = new LinkClickObserver(this, window);
2962
- this.formSubmitObserver = new FormSubmitObserver(this, document);
2963
- this.scrollObserver = new ScrollObserver(this);
2964
- this.streamObserver = new StreamObserver(this);
2965
- this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
2966
- this.frameRedirector = new FrameRedirector(this, document.documentElement);
2967
- this.streamMessageRenderer = new StreamMessageRenderer;
2968
- this.drive = true;
2969
- this.enabled = true;
2970
- this.progressBarDelay = 500;
2971
- this.started = false;
2972
- this.formMode = "on";
4069
+ navigator=new Navigator(this);
4070
+ history=new History(this);
4071
+ view=new PageView(this, document.documentElement);
4072
+ adapter=new BrowserAdapter(this);
4073
+ pageObserver=new PageObserver(this);
4074
+ cacheObserver=new CacheObserver;
4075
+ linkPrefetchObserver=new LinkPrefetchObserver(this, document);
4076
+ linkClickObserver=new LinkClickObserver(this, window);
4077
+ formSubmitObserver=new FormSubmitObserver(this, document);
4078
+ scrollObserver=new ScrollObserver(this);
4079
+ streamObserver=new StreamObserver(this);
4080
+ formLinkClickObserver=new FormLinkClickObserver(this, document.documentElement);
4081
+ frameRedirector=new FrameRedirector(this, document.documentElement);
4082
+ streamMessageRenderer=new StreamMessageRenderer;
4083
+ cache=new Cache(this);
4084
+ drive=true;
4085
+ enabled=true;
4086
+ progressBarDelay=500;
4087
+ started=false;
4088
+ formMode="on";
4089
+ #pageRefreshDebouncePeriod=150;
4090
+ constructor(recentRequests) {
4091
+ this.recentRequests = recentRequests;
4092
+ this.preloader = new Preloader(this, this.view.snapshotCache);
4093
+ this.debouncedRefresh = this.refresh;
4094
+ this.pageRefreshDebouncePeriod = this.pageRefreshDebouncePeriod;
2973
4095
  }
2974
4096
  start() {
2975
4097
  if (!this.started) {
2976
4098
  this.pageObserver.start();
2977
4099
  this.cacheObserver.start();
4100
+ this.linkPrefetchObserver.start();
2978
4101
  this.formLinkClickObserver.start();
2979
4102
  this.linkClickObserver.start();
2980
4103
  this.formSubmitObserver.start();
@@ -2994,6 +4117,7 @@ class Session {
2994
4117
  if (this.started) {
2995
4118
  this.pageObserver.stop();
2996
4119
  this.cacheObserver.stop();
4120
+ this.linkPrefetchObserver.stop();
2997
4121
  this.formLinkClickObserver.stop();
2998
4122
  this.linkClickObserver.stop();
2999
4123
  this.formSubmitObserver.stop();
@@ -3001,6 +4125,7 @@ class Session {
3001
4125
  this.streamObserver.stop();
3002
4126
  this.frameRedirector.stop();
3003
4127
  this.history.stop();
4128
+ this.preloader.stop();
3004
4129
  this.started = false;
3005
4130
  }
3006
4131
  }
@@ -3010,12 +4135,22 @@ class Session {
3010
4135
  visit(location, options = {}) {
3011
4136
  const frameElement = options.frame ? document.getElementById(options.frame) : null;
3012
4137
  if (frameElement instanceof FrameElement) {
4138
+ const action = options.action || getVisitAction(frameElement);
4139
+ frameElement.delegate.proposeVisitIfNavigatedWithAction(frameElement, action);
3013
4140
  frameElement.src = location.toString();
3014
- frameElement.loaded;
3015
4141
  } else {
3016
4142
  this.navigator.proposeVisit(expandURL(location), options);
3017
4143
  }
3018
4144
  }
4145
+ refresh(url, requestId) {
4146
+ const isRecentRequest = requestId && this.recentRequests.has(requestId);
4147
+ if (!isRecentRequest) {
4148
+ this.cache.exemptPageFromPreview();
4149
+ this.visit(url, {
4150
+ action: "replace"
4151
+ });
4152
+ }
4153
+ }
3019
4154
  connectStreamSource(source) {
3020
4155
  this.streamObserver.connectStreamSource(source);
3021
4156
  }
@@ -3040,11 +4175,31 @@ class Session {
3040
4175
  get restorationIdentifier() {
3041
4176
  return this.history.restorationIdentifier;
3042
4177
  }
3043
- historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
4178
+ get pageRefreshDebouncePeriod() {
4179
+ return this.#pageRefreshDebouncePeriod;
4180
+ }
4181
+ set pageRefreshDebouncePeriod(value) {
4182
+ this.refresh = debounce(this.debouncedRefresh.bind(this), value);
4183
+ this.#pageRefreshDebouncePeriod = value;
4184
+ }
4185
+ shouldPreloadLink(element) {
4186
+ const isUnsafe = element.hasAttribute("data-turbo-method");
4187
+ const isStream = element.hasAttribute("data-turbo-stream");
4188
+ const frameTarget = element.getAttribute("data-turbo-frame");
4189
+ const frame = frameTarget == "_top" ? null : document.getElementById(frameTarget) || findClosestRecursively(element, "turbo-frame:not([disabled])");
4190
+ if (isUnsafe || isStream || frame instanceof FrameElement) {
4191
+ return false;
4192
+ } else {
4193
+ const location = new URL(element.href);
4194
+ return this.elementIsNavigatable(element) && locationIsVisitable(location, this.snapshot.rootLocation);
4195
+ }
4196
+ }
4197
+ historyPoppedToLocationWithRestorationIdentifierAndDirection(location, restorationIdentifier, direction) {
3044
4198
  if (this.enabled) {
3045
4199
  this.navigator.startVisit(location, restorationIdentifier, {
3046
4200
  action: "restore",
3047
- historyChanged: true
4201
+ historyChanged: true,
4202
+ direction: direction
3048
4203
  });
3049
4204
  } else {
3050
4205
  this.adapter.pageInvalidated({
@@ -3061,6 +4216,9 @@ class Session {
3061
4216
  return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
3062
4217
  }
3063
4218
  submittedFormLinkToLocation() {}
4219
+ canPrefetchRequestToLocation(link, location) {
4220
+ return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
4221
+ }
3064
4222
  willFollowLinkToLocation(link, location, event) {
3065
4223
  return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation) && this.applicationAllowsFollowingLinkToLocation(link, location, event);
3066
4224
  }
@@ -3082,6 +4240,7 @@ class Session {
3082
4240
  visitStarted(visit) {
3083
4241
  if (!visit.acceptsStreamResponse) {
3084
4242
  markAsBusy(document.documentElement);
4243
+ this.view.markVisitDirection(visit.direction);
3085
4244
  }
3086
4245
  extendURLWithDeprecatedProperties(visit.location);
3087
4246
  if (!visit.silent) {
@@ -3089,6 +4248,7 @@ class Session {
3089
4248
  }
3090
4249
  }
3091
4250
  visitCompleted(visit) {
4251
+ this.view.unmarkVisitDirection();
3092
4252
  clearBusyState(document.documentElement);
3093
4253
  this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
3094
4254
  }
@@ -3099,7 +4259,7 @@ class Session {
3099
4259
  this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
3100
4260
  }
3101
4261
  willSubmitForm(form, submitter) {
3102
- const action = getAction(form, submitter);
4262
+ const action = getAction$1(form, submitter);
3103
4263
  return this.submissionIsNavigatable(form, submitter) && locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
3104
4264
  }
3105
4265
  formSubmitted(form, submitter) {
@@ -3119,8 +4279,7 @@ class Session {
3119
4279
  this.renderStreamMessage(message);
3120
4280
  }
3121
4281
  viewWillCacheSnapshot() {
3122
- var _a;
3123
- if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
4282
+ if (!this.navigator.currentVisit?.silent) {
3124
4283
  this.notifyApplicationBeforeCachingSnapshot();
3125
4284
  }
3126
4285
  }
@@ -3132,9 +4291,9 @@ class Session {
3132
4291
  }
3133
4292
  return !defaultPrevented;
3134
4293
  }
3135
- viewRenderedSnapshot(_snapshot, _isPreview) {
4294
+ viewRenderedSnapshot(_snapshot, _isPreview, renderMethod) {
3136
4295
  this.view.lastRenderedLocation = this.history.location;
3137
- this.notifyApplicationAfterRender();
4296
+ this.notifyApplicationAfterRender(renderMethod);
3138
4297
  }
3139
4298
  preloadOnLoadLinksForView(element) {
3140
4299
  this.preloader.preloadOnLoadLinksForView(element);
@@ -3187,14 +4346,19 @@ class Session {
3187
4346
  }
3188
4347
  notifyApplicationBeforeRender(newBody, options) {
3189
4348
  return dispatch("turbo:before-render", {
3190
- detail: Object.assign({
3191
- newBody: newBody
3192
- }, options),
4349
+ detail: {
4350
+ newBody: newBody,
4351
+ ...options
4352
+ },
3193
4353
  cancelable: true
3194
4354
  });
3195
4355
  }
3196
- notifyApplicationAfterRender() {
3197
- return dispatch("turbo:render");
4356
+ notifyApplicationAfterRender(renderMethod) {
4357
+ return dispatch("turbo:render", {
4358
+ detail: {
4359
+ renderMethod: renderMethod
4360
+ }
4361
+ });
3198
4362
  }
3199
4363
  notifyApplicationAfterPageLoad(timing = {}) {
3200
4364
  return dispatch("turbo:load", {
@@ -3273,67 +4437,9 @@ const deprecatedLocationPropertyDescriptors = {
3273
4437
  }
3274
4438
  };
3275
4439
 
3276
- class Cache {
3277
- constructor(session) {
3278
- this.session = session;
3279
- }
3280
- clear() {
3281
- this.session.clearCache();
3282
- }
3283
- resetCacheControl() {
3284
- this.setCacheControl("");
3285
- }
3286
- exemptPageFromCache() {
3287
- this.setCacheControl("no-cache");
3288
- }
3289
- exemptPageFromPreview() {
3290
- this.setCacheControl("no-preview");
3291
- }
3292
- setCacheControl(value) {
3293
- setMetaContent("turbo-cache-control", value);
3294
- }
3295
- }
3296
-
3297
- const StreamActions = {
3298
- after() {
3299
- this.targetElements.forEach((e => {
3300
- var _a;
3301
- return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling);
3302
- }));
3303
- },
3304
- append() {
3305
- this.removeDuplicateTargetChildren();
3306
- this.targetElements.forEach((e => e.append(this.templateContent)));
3307
- },
3308
- before() {
3309
- this.targetElements.forEach((e => {
3310
- var _a;
3311
- return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e);
3312
- }));
3313
- },
3314
- prepend() {
3315
- this.removeDuplicateTargetChildren();
3316
- this.targetElements.forEach((e => e.prepend(this.templateContent)));
3317
- },
3318
- remove() {
3319
- this.targetElements.forEach((e => e.remove()));
3320
- },
3321
- replace() {
3322
- this.targetElements.forEach((e => e.replaceWith(this.templateContent)));
3323
- },
3324
- update() {
3325
- this.targetElements.forEach((targetElement => {
3326
- targetElement.innerHTML = "";
3327
- targetElement.append(this.templateContent);
3328
- }));
3329
- }
3330
- };
3331
-
3332
- const session = new Session;
4440
+ const session = new Session(recentRequests);
3333
4441
 
3334
- const cache = new Cache(session);
3335
-
3336
- const {navigator: navigator$1} = session;
4442
+ const {cache: cache, navigator: navigator$1} = session;
3337
4443
 
3338
4444
  function start() {
3339
4445
  session.start();
@@ -3384,6 +4490,7 @@ var Turbo = Object.freeze({
3384
4490
  PageRenderer: PageRenderer,
3385
4491
  PageSnapshot: PageSnapshot,
3386
4492
  FrameRenderer: FrameRenderer,
4493
+ fetch: fetchWithTurboHeaders,
3387
4494
  start: start,
3388
4495
  registerAdapter: registerAdapter,
3389
4496
  visit: visit,
@@ -3393,28 +4500,20 @@ var Turbo = Object.freeze({
3393
4500
  clearCache: clearCache,
3394
4501
  setProgressBarDelay: setProgressBarDelay,
3395
4502
  setConfirmMethod: setConfirmMethod,
3396
- setFormMode: setFormMode,
3397
- StreamActions: StreamActions
4503
+ setFormMode: setFormMode
3398
4504
  });
3399
4505
 
3400
4506
  class TurboFrameMissingError extends Error {}
3401
4507
 
3402
4508
  class FrameController {
4509
+ fetchResponseLoaded=_fetchResponse => Promise.resolve();
4510
+ #currentFetchRequest=null;
4511
+ #resolveVisitPromise=() => {};
4512
+ #connected=false;
4513
+ #hasBeenLoaded=false;
4514
+ #ignoredAttributes=new Set;
4515
+ action=null;
3403
4516
  constructor(element) {
3404
- this.fetchResponseLoaded = _fetchResponse => {};
3405
- this.currentFetchRequest = null;
3406
- this.resolveVisitPromise = () => {};
3407
- this.connected = false;
3408
- this.hasBeenLoaded = false;
3409
- this.ignoredAttributes = new Set;
3410
- this.action = null;
3411
- this.visitCachedSnapshot = ({element: element}) => {
3412
- const frame = element.querySelector("#" + this.element.id);
3413
- if (frame && this.previousFrameElement) {
3414
- frame.replaceChildren(...this.previousFrameElement.children);
3415
- }
3416
- delete this.previousFrameElement;
3417
- };
3418
4517
  this.element = element;
3419
4518
  this.view = new FrameView(this, this.element);
3420
4519
  this.appearanceObserver = new AppearanceObserver(this, this.element);
@@ -3424,12 +4523,12 @@ class FrameController {
3424
4523
  this.formSubmitObserver = new FormSubmitObserver(this, this.element);
3425
4524
  }
3426
4525
  connect() {
3427
- if (!this.connected) {
3428
- this.connected = true;
4526
+ if (!this.#connected) {
4527
+ this.#connected = true;
3429
4528
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
3430
4529
  this.appearanceObserver.start();
3431
4530
  } else {
3432
- this.loadSourceURL();
4531
+ this.#loadSourceURL();
3433
4532
  }
3434
4533
  this.formLinkClickObserver.start();
3435
4534
  this.linkInterceptor.start();
@@ -3437,8 +4536,8 @@ class FrameController {
3437
4536
  }
3438
4537
  }
3439
4538
  disconnect() {
3440
- if (this.connected) {
3441
- this.connected = false;
4539
+ if (this.#connected) {
4540
+ this.#connected = false;
3442
4541
  this.appearanceObserver.stop();
3443
4542
  this.formLinkClickObserver.stop();
3444
4543
  this.linkInterceptor.stop();
@@ -3447,21 +4546,21 @@ class FrameController {
3447
4546
  }
3448
4547
  disabledChanged() {
3449
4548
  if (this.loadingStyle == FrameLoadingStyle.eager) {
3450
- this.loadSourceURL();
4549
+ this.#loadSourceURL();
3451
4550
  }
3452
4551
  }
3453
4552
  sourceURLChanged() {
3454
- if (this.isIgnoringChangesTo("src")) return;
4553
+ if (this.#isIgnoringChangesTo("src")) return;
3455
4554
  if (this.element.isConnected) {
3456
4555
  this.complete = false;
3457
4556
  }
3458
- if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
3459
- this.loadSourceURL();
4557
+ if (this.loadingStyle == FrameLoadingStyle.eager || this.#hasBeenLoaded) {
4558
+ this.#loadSourceURL();
3460
4559
  }
3461
4560
  }
3462
4561
  sourceURLReloaded() {
3463
4562
  const {src: src} = this.element;
3464
- this.ignoringChangesToAttribute("complete", (() => {
4563
+ this.#ignoringChangesToAttribute("complete", (() => {
3465
4564
  this.element.removeAttribute("complete");
3466
4565
  }));
3467
4566
  this.element.src = null;
@@ -3469,23 +4568,23 @@ class FrameController {
3469
4568
  return this.element.loaded;
3470
4569
  }
3471
4570
  completeChanged() {
3472
- if (this.isIgnoringChangesTo("complete")) return;
3473
- this.loadSourceURL();
4571
+ if (this.#isIgnoringChangesTo("complete")) return;
4572
+ this.#loadSourceURL();
3474
4573
  }
3475
4574
  loadingStyleChanged() {
3476
4575
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
3477
4576
  this.appearanceObserver.start();
3478
4577
  } else {
3479
4578
  this.appearanceObserver.stop();
3480
- this.loadSourceURL();
4579
+ this.#loadSourceURL();
3481
4580
  }
3482
4581
  }
3483
- async loadSourceURL() {
4582
+ async #loadSourceURL() {
3484
4583
  if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
3485
- this.element.loaded = this.visit(expandURL(this.sourceURL));
4584
+ this.element.loaded = this.#visit(expandURL(this.sourceURL));
3486
4585
  this.appearanceObserver.stop();
3487
4586
  await this.element.loaded;
3488
- this.hasBeenLoaded = true;
4587
+ this.#hasBeenLoaded = true;
3489
4588
  }
3490
4589
  }
3491
4590
  async loadResponse(fetchResponse) {
@@ -3498,34 +4597,34 @@ class FrameController {
3498
4597
  const document = parseHTMLDocument(html);
3499
4598
  const pageSnapshot = PageSnapshot.fromDocument(document);
3500
4599
  if (pageSnapshot.isVisitable) {
3501
- await this.loadFrameResponse(fetchResponse, document);
4600
+ await this.#loadFrameResponse(fetchResponse, document);
3502
4601
  } else {
3503
- await this.handleUnvisitableFrameResponse(fetchResponse);
4602
+ await this.#handleUnvisitableFrameResponse(fetchResponse);
3504
4603
  }
3505
4604
  }
3506
4605
  } finally {
3507
- this.fetchResponseLoaded = () => {};
4606
+ this.fetchResponseLoaded = () => Promise.resolve();
3508
4607
  }
3509
4608
  }
3510
4609
  elementAppearedInViewport(element) {
3511
- this.proposeVisitIfNavigatedWithAction(element, element);
3512
- this.loadSourceURL();
4610
+ this.proposeVisitIfNavigatedWithAction(element, getVisitAction(element));
4611
+ this.#loadSourceURL();
3513
4612
  }
3514
4613
  willSubmitFormLinkToLocation(link) {
3515
- return this.shouldInterceptNavigation(link);
4614
+ return this.#shouldInterceptNavigation(link);
3516
4615
  }
3517
4616
  submittedFormLinkToLocation(link, _location, form) {
3518
- const frame = this.findFrameElement(link);
4617
+ const frame = this.#findFrameElement(link);
3519
4618
  if (frame) form.setAttribute("data-turbo-frame", frame.id);
3520
4619
  }
3521
4620
  shouldInterceptLinkClick(element, _location, _event) {
3522
- return this.shouldInterceptNavigation(element);
4621
+ return this.#shouldInterceptNavigation(element);
3523
4622
  }
3524
4623
  linkClickIntercepted(element, location) {
3525
- this.navigateFrame(element, location);
4624
+ this.#navigateFrame(element, location);
3526
4625
  }
3527
4626
  willSubmitForm(element, submitter) {
3528
- return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
4627
+ return element.closest("turbo-frame") == this.element && this.#shouldInterceptNavigation(element, submitter);
3529
4628
  }
3530
4629
  formSubmitted(element, submitter) {
3531
4630
  if (this.formSubmission) {
@@ -3537,9 +4636,8 @@ class FrameController {
3537
4636
  this.formSubmission.start();
3538
4637
  }
3539
4638
  prepareRequest(request) {
3540
- var _a;
3541
4639
  request.headers["Turbo-Frame"] = this.id;
3542
- if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
4640
+ if (this.currentNavigationElement?.hasAttribute("data-turbo-stream")) {
3543
4641
  request.acceptResponseType(StreamMessage.contentType);
3544
4642
  }
3545
4643
  }
@@ -3547,29 +4645,29 @@ class FrameController {
3547
4645
  markAsBusy(this.element);
3548
4646
  }
3549
4647
  requestPreventedHandlingResponse(_request, _response) {
3550
- this.resolveVisitPromise();
4648
+ this.#resolveVisitPromise();
3551
4649
  }
3552
4650
  async requestSucceededWithResponse(request, response) {
3553
4651
  await this.loadResponse(response);
3554
- this.resolveVisitPromise();
4652
+ this.#resolveVisitPromise();
3555
4653
  }
3556
4654
  async requestFailedWithResponse(request, response) {
3557
4655
  await this.loadResponse(response);
3558
- this.resolveVisitPromise();
4656
+ this.#resolveVisitPromise();
3559
4657
  }
3560
4658
  requestErrored(request, error) {
3561
4659
  console.error(error);
3562
- this.resolveVisitPromise();
4660
+ this.#resolveVisitPromise();
3563
4661
  }
3564
4662
  requestFinished(_request) {
3565
4663
  clearBusyState(this.element);
3566
4664
  }
3567
4665
  formSubmissionStarted({formElement: formElement}) {
3568
- markAsBusy(formElement, this.findFrameElement(formElement));
4666
+ markAsBusy(formElement, this.#findFrameElement(formElement));
3569
4667
  }
3570
4668
  formSubmissionSucceededWithResponse(formSubmission, response) {
3571
- const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
3572
- frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
4669
+ const frame = this.#findFrameElement(formSubmission.formElement, formSubmission.submitter);
4670
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(formSubmission.submitter, formSubmission.formElement, frame));
3573
4671
  frame.delegate.loadResponse(response);
3574
4672
  if (!formSubmission.isSafe) {
3575
4673
  session.clearCache();
@@ -3583,14 +4681,15 @@ class FrameController {
3583
4681
  console.error(error);
3584
4682
  }
3585
4683
  formSubmissionFinished({formElement: formElement}) {
3586
- clearBusyState(formElement, this.findFrameElement(formElement));
4684
+ clearBusyState(formElement, this.#findFrameElement(formElement));
3587
4685
  }
3588
4686
  allowsImmediateRender({element: newFrame}, options) {
3589
4687
  const event = dispatch("turbo:before-frame-render", {
3590
4688
  target: this.element,
3591
- detail: Object.assign({
3592
- newFrame: newFrame
3593
- }, options),
4689
+ detail: {
4690
+ newFrame: newFrame,
4691
+ ...options
4692
+ },
3594
4693
  cancelable: true
3595
4694
  });
3596
4695
  const {defaultPrevented: defaultPrevented, detail: {render: render}} = event;
@@ -3599,7 +4698,7 @@ class FrameController {
3599
4698
  }
3600
4699
  return !defaultPrevented;
3601
4700
  }
3602
- viewRenderedSnapshot(_snapshot, _isPreview) {}
4701
+ viewRenderedSnapshot(_snapshot, _isPreview, _renderMethod) {}
3603
4702
  preloadOnLoadLinksForView(element) {
3604
4703
  session.preloadOnLoadLinksForView(element);
3605
4704
  }
@@ -3607,7 +4706,14 @@ class FrameController {
3607
4706
  willRenderFrame(currentElement, _newElement) {
3608
4707
  this.previousFrameElement = currentElement.cloneNode(true);
3609
4708
  }
3610
- async loadFrameResponse(fetchResponse, document) {
4709
+ visitCachedSnapshot=({element: element}) => {
4710
+ const frame = element.querySelector("#" + this.element.id);
4711
+ if (frame && this.previousFrameElement) {
4712
+ frame.replaceChildren(...this.previousFrameElement.children);
4713
+ }
4714
+ delete this.previousFrameElement;
4715
+ };
4716
+ async #loadFrameResponse(fetchResponse, document) {
3611
4717
  const newFrameElement = await this.extractForeignFrameElement(document.body);
3612
4718
  if (newFrameElement) {
3613
4719
  const snapshot = new Snapshot(newFrameElement);
@@ -3618,41 +4724,40 @@ class FrameController {
3618
4724
  this.complete = true;
3619
4725
  session.frameRendered(fetchResponse, this.element);
3620
4726
  session.frameLoaded(this.element);
3621
- this.fetchResponseLoaded(fetchResponse);
3622
- } else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
3623
- this.handleFrameMissingFromResponse(fetchResponse);
4727
+ await this.fetchResponseLoaded(fetchResponse);
4728
+ } else if (this.#willHandleFrameMissingFromResponse(fetchResponse)) {
4729
+ this.#handleFrameMissingFromResponse(fetchResponse);
3624
4730
  }
3625
4731
  }
3626
- async visit(url) {
3627
- var _a;
4732
+ async #visit(url) {
3628
4733
  const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams, this.element);
3629
- (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
3630
- this.currentFetchRequest = request;
4734
+ this.#currentFetchRequest?.cancel();
4735
+ this.#currentFetchRequest = request;
3631
4736
  return new Promise((resolve => {
3632
- this.resolveVisitPromise = () => {
3633
- this.resolveVisitPromise = () => {};
3634
- this.currentFetchRequest = null;
4737
+ this.#resolveVisitPromise = () => {
4738
+ this.#resolveVisitPromise = () => {};
4739
+ this.#currentFetchRequest = null;
3635
4740
  resolve();
3636
4741
  };
3637
4742
  request.perform();
3638
4743
  }));
3639
4744
  }
3640
- navigateFrame(element, url, submitter) {
3641
- const frame = this.findFrameElement(element, submitter);
3642
- frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
3643
- this.withCurrentNavigationElement(element, (() => {
4745
+ #navigateFrame(element, url, submitter) {
4746
+ const frame = this.#findFrameElement(element, submitter);
4747
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(submitter, element, frame));
4748
+ this.#withCurrentNavigationElement(element, (() => {
3644
4749
  frame.src = url;
3645
4750
  }));
3646
4751
  }
3647
- proposeVisitIfNavigatedWithAction(frame, element, submitter) {
3648
- this.action = getVisitAction(submitter, element, frame);
4752
+ proposeVisitIfNavigatedWithAction(frame, action = null) {
4753
+ this.action = action;
3649
4754
  if (this.action) {
3650
4755
  const pageSnapshot = PageSnapshot.fromElement(frame).clone();
3651
4756
  const {visitCachedSnapshot: visitCachedSnapshot} = frame.delegate;
3652
- frame.delegate.fetchResponseLoaded = fetchResponse => {
4757
+ frame.delegate.fetchResponseLoaded = async fetchResponse => {
3653
4758
  if (frame.src) {
3654
4759
  const {statusCode: statusCode, redirected: redirected} = fetchResponse;
3655
- const responseHTML = frame.ownerDocument.documentElement.outerHTML;
4760
+ const responseHTML = await fetchResponse.responseHTML;
3656
4761
  const response = {
3657
4762
  statusCode: statusCode,
3658
4763
  redirected: redirected,
@@ -3678,16 +4783,16 @@ class FrameController {
3678
4783
  session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
3679
4784
  }
3680
4785
  }
3681
- async handleUnvisitableFrameResponse(fetchResponse) {
4786
+ async #handleUnvisitableFrameResponse(fetchResponse) {
3682
4787
  console.warn(`The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`);
3683
- await this.visitResponse(fetchResponse.response);
4788
+ await this.#visitResponse(fetchResponse.response);
3684
4789
  }
3685
- willHandleFrameMissingFromResponse(fetchResponse) {
4790
+ #willHandleFrameMissingFromResponse(fetchResponse) {
3686
4791
  this.element.setAttribute("complete", "");
3687
4792
  const response = fetchResponse.response;
3688
- const visit = async (url, options = {}) => {
4793
+ const visit = async (url, options) => {
3689
4794
  if (url instanceof Response) {
3690
- this.visitResponse(url);
4795
+ this.#visitResponse(url);
3691
4796
  } else {
3692
4797
  session.visit(url, options);
3693
4798
  }
@@ -3702,15 +4807,15 @@ class FrameController {
3702
4807
  });
3703
4808
  return !event.defaultPrevented;
3704
4809
  }
3705
- handleFrameMissingFromResponse(fetchResponse) {
4810
+ #handleFrameMissingFromResponse(fetchResponse) {
3706
4811
  this.view.missing();
3707
- this.throwFrameMissingError(fetchResponse);
4812
+ this.#throwFrameMissingError(fetchResponse);
3708
4813
  }
3709
- throwFrameMissingError(fetchResponse) {
4814
+ #throwFrameMissingError(fetchResponse) {
3710
4815
  const message = `The response (${fetchResponse.statusCode}) did not contain the expected <turbo-frame id="${this.element.id}"> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;
3711
4816
  throw new TurboFrameMissingError(message);
3712
4817
  }
3713
- async visitResponse(response) {
4818
+ async #visitResponse(response) {
3714
4819
  const wrapped = new FetchResponse(response);
3715
4820
  const responseHTML = await wrapped.responseHTML;
3716
4821
  const {location: location, redirected: redirected, statusCode: statusCode} = wrapped;
@@ -3722,10 +4827,9 @@ class FrameController {
3722
4827
  }
3723
4828
  });
3724
4829
  }
3725
- findFrameElement(element, submitter) {
3726
- var _a;
4830
+ #findFrameElement(element, submitter) {
3727
4831
  const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
3728
- return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
4832
+ return getFrameElementById(id) ?? this.element;
3729
4833
  }
3730
4834
  async extractForeignFrameElement(container) {
3731
4835
  let element;
@@ -3746,13 +4850,13 @@ class FrameController {
3746
4850
  }
3747
4851
  return null;
3748
4852
  }
3749
- formActionIsVisitable(form, submitter) {
3750
- const action = getAction(form, submitter);
4853
+ #formActionIsVisitable(form, submitter) {
4854
+ const action = getAction$1(form, submitter);
3751
4855
  return locationIsVisitable(expandURL(action), this.rootLocation);
3752
4856
  }
3753
- shouldInterceptNavigation(element, submitter) {
4857
+ #shouldInterceptNavigation(element, submitter) {
3754
4858
  const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
3755
- if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
4859
+ if (element instanceof HTMLFormElement && !this.#formActionIsVisitable(element, submitter)) {
3756
4860
  return false;
3757
4861
  }
3758
4862
  if (!this.enabled || id == "_top") {
@@ -3784,21 +4888,21 @@ class FrameController {
3784
4888
  }
3785
4889
  }
3786
4890
  set sourceURL(sourceURL) {
3787
- this.ignoringChangesToAttribute("src", (() => {
3788
- this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
4891
+ this.#ignoringChangesToAttribute("src", (() => {
4892
+ this.element.src = sourceURL ?? null;
3789
4893
  }));
3790
4894
  }
3791
4895
  get loadingStyle() {
3792
4896
  return this.element.loading;
3793
4897
  }
3794
4898
  get isLoading() {
3795
- return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
4899
+ return this.formSubmission !== undefined || this.#resolveVisitPromise() !== undefined;
3796
4900
  }
3797
4901
  get complete() {
3798
4902
  return this.element.hasAttribute("complete");
3799
4903
  }
3800
4904
  set complete(value) {
3801
- this.ignoringChangesToAttribute("complete", (() => {
4905
+ this.#ignoringChangesToAttribute("complete", (() => {
3802
4906
  if (value) {
3803
4907
  this.element.setAttribute("complete", "");
3804
4908
  } else {
@@ -3807,23 +4911,22 @@ class FrameController {
3807
4911
  }));
3808
4912
  }
3809
4913
  get isActive() {
3810
- return this.element.isActive && this.connected;
4914
+ return this.element.isActive && this.#connected;
3811
4915
  }
3812
4916
  get rootLocation() {
3813
- var _a;
3814
4917
  const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
3815
- const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
4918
+ const root = meta?.content ?? "/";
3816
4919
  return expandURL(root);
3817
4920
  }
3818
- isIgnoringChangesTo(attributeName) {
3819
- return this.ignoredAttributes.has(attributeName);
4921
+ #isIgnoringChangesTo(attributeName) {
4922
+ return this.#ignoredAttributes.has(attributeName);
3820
4923
  }
3821
- ignoringChangesToAttribute(attributeName, callback) {
3822
- this.ignoredAttributes.add(attributeName);
4924
+ #ignoringChangesToAttribute(attributeName, callback) {
4925
+ this.#ignoredAttributes.add(attributeName);
3823
4926
  callback();
3824
- this.ignoredAttributes.delete(attributeName);
4927
+ this.#ignoredAttributes.delete(attributeName);
3825
4928
  }
3826
- withCurrentNavigationElement(element, callback) {
4929
+ #withCurrentNavigationElement(element, callback) {
3827
4930
  this.currentNavigationElement = element;
3828
4931
  callback();
3829
4932
  delete this.currentNavigationElement;
@@ -3856,6 +4959,38 @@ function activateElement(element, currentURL) {
3856
4959
  }
3857
4960
  }
3858
4961
 
4962
+ const StreamActions = {
4963
+ after() {
4964
+ this.targetElements.forEach((e => e.parentElement?.insertBefore(this.templateContent, e.nextSibling)));
4965
+ },
4966
+ append() {
4967
+ this.removeDuplicateTargetChildren();
4968
+ this.targetElements.forEach((e => e.append(this.templateContent)));
4969
+ },
4970
+ before() {
4971
+ this.targetElements.forEach((e => e.parentElement?.insertBefore(this.templateContent, e)));
4972
+ },
4973
+ prepend() {
4974
+ this.removeDuplicateTargetChildren();
4975
+ this.targetElements.forEach((e => e.prepend(this.templateContent)));
4976
+ },
4977
+ remove() {
4978
+ this.targetElements.forEach((e => e.remove()));
4979
+ },
4980
+ replace() {
4981
+ this.targetElements.forEach((e => e.replaceWith(this.templateContent)));
4982
+ },
4983
+ update() {
4984
+ this.targetElements.forEach((targetElement => {
4985
+ targetElement.innerHTML = "";
4986
+ targetElement.append(this.templateContent);
4987
+ }));
4988
+ },
4989
+ refresh() {
4990
+ session.refresh(this.baseURI, this.requestId);
4991
+ }
4992
+ };
4993
+
3859
4994
  class StreamElement extends HTMLElement {
3860
4995
  static async renderElement(newElement) {
3861
4996
  await newElement.performAction();
@@ -3870,11 +5005,10 @@ class StreamElement extends HTMLElement {
3870
5005
  }
3871
5006
  }
3872
5007
  async render() {
3873
- var _a;
3874
- return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : this.renderPromise = (async () => {
5008
+ return this.renderPromise ??= (async () => {
3875
5009
  const event = this.beforeRenderEvent;
3876
5010
  if (this.dispatchEvent(event)) {
3877
- await nextAnimationFrame();
5011
+ await nextRepaint();
3878
5012
  await event.detail.render(this);
3879
5013
  }
3880
5014
  })();
@@ -3882,15 +5016,14 @@ class StreamElement extends HTMLElement {
3882
5016
  disconnect() {
3883
5017
  try {
3884
5018
  this.remove();
3885
- } catch (_a) {}
5019
+ } catch {}
3886
5020
  }
3887
5021
  removeDuplicateTargetChildren() {
3888
5022
  this.duplicateChildren.forEach((c => c.remove()));
3889
5023
  }
3890
5024
  get duplicateChildren() {
3891
- var _a;
3892
5025
  const existingChildren = this.targetElements.flatMap((e => [ ...e.children ])).filter((c => !!c.id));
3893
- const newChildrenIds = [ ...((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [] ].filter((c => !!c.id)).map((c => c.id));
5026
+ const newChildrenIds = [ ...this.templateContent?.children || [] ].filter((c => !!c.id)).map((c => c.id));
3894
5027
  return existingChildren.filter((c => newChildrenIds.includes(c.id)));
3895
5028
  }
3896
5029
  get performAction() {
@@ -3899,9 +5032,9 @@ class StreamElement extends HTMLElement {
3899
5032
  if (actionFunction) {
3900
5033
  return actionFunction;
3901
5034
  }
3902
- this.raise("unknown action");
5035
+ this.#raise("unknown action");
3903
5036
  }
3904
- this.raise("action attribute is missing");
5037
+ this.#raise("action attribute is missing");
3905
5038
  }
3906
5039
  get targetElements() {
3907
5040
  if (this.target) {
@@ -3909,7 +5042,7 @@ class StreamElement extends HTMLElement {
3909
5042
  } else if (this.targets) {
3910
5043
  return this.targetElementsByQuery;
3911
5044
  } else {
3912
- this.raise("target or targets attribute is missing");
5045
+ this.#raise("target or targets attribute is missing");
3913
5046
  }
3914
5047
  }
3915
5048
  get templateContent() {
@@ -3923,7 +5056,7 @@ class StreamElement extends HTMLElement {
3923
5056
  } else if (this.firstElementChild instanceof HTMLTemplateElement) {
3924
5057
  return this.firstElementChild;
3925
5058
  }
3926
- this.raise("first child element must be a <template> element");
5059
+ this.#raise("first child element must be a <template> element");
3927
5060
  }
3928
5061
  get action() {
3929
5062
  return this.getAttribute("action");
@@ -3934,12 +5067,14 @@ class StreamElement extends HTMLElement {
3934
5067
  get targets() {
3935
5068
  return this.getAttribute("targets");
3936
5069
  }
3937
- raise(message) {
5070
+ get requestId() {
5071
+ return this.getAttribute("request-id");
5072
+ }
5073
+ #raise(message) {
3938
5074
  throw new Error(`${this.description}: ${message}`);
3939
5075
  }
3940
5076
  get description() {
3941
- var _a, _b;
3942
- return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
5077
+ return (this.outerHTML.match(/<[^>]+>/) ?? [])[0] ?? "<turbo-stream>";
3943
5078
  }
3944
5079
  get beforeRenderEvent() {
3945
5080
  return new CustomEvent("turbo:before-stream-render", {
@@ -3952,8 +5087,7 @@ class StreamElement extends HTMLElement {
3952
5087
  });
3953
5088
  }
3954
5089
  get targetElementsById() {
3955
- var _a;
3956
- const element = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
5090
+ const element = this.ownerDocument?.getElementById(this.target);
3957
5091
  if (element !== null) {
3958
5092
  return [ element ];
3959
5093
  } else {
@@ -3961,8 +5095,7 @@ class StreamElement extends HTMLElement {
3961
5095
  }
3962
5096
  }
3963
5097
  get targetElementsByQuery() {
3964
- var _a;
3965
- const elements = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.querySelectorAll(this.targets);
5098
+ const elements = this.ownerDocument?.querySelectorAll(this.targets);
3966
5099
  if (elements.length !== 0) {
3967
5100
  return Array.prototype.slice.call(elements);
3968
5101
  } else {
@@ -3972,16 +5105,14 @@ class StreamElement extends HTMLElement {
3972
5105
  }
3973
5106
 
3974
5107
  class StreamSourceElement extends HTMLElement {
3975
- constructor() {
3976
- super(...arguments);
3977
- this.streamSource = null;
3978
- }
5108
+ streamSource=null;
3979
5109
  connectedCallback() {
3980
5110
  this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
3981
5111
  connectStreamSource(this.streamSource);
3982
5112
  }
3983
5113
  disconnectedCallback() {
3984
5114
  if (this.streamSource) {
5115
+ this.streamSource.close();
3985
5116
  disconnectStreamSource(this.streamSource);
3986
5117
  }
3987
5118
  }
@@ -4026,16 +5157,21 @@ if (customElements.get("turbo-stream-source") === undefined) {
4026
5157
  }
4027
5158
  })();
4028
5159
 
4029
- window.Turbo = Turbo;
5160
+ window.Turbo = {
5161
+ ...Turbo,
5162
+ StreamActions: StreamActions
5163
+ };
4030
5164
 
4031
5165
  start();
4032
5166
 
4033
- var turbo_es2017Esm = Object.freeze({
5167
+ var Turbo$1 = Object.freeze({
4034
5168
  __proto__: null,
5169
+ FetchEnctype: FetchEnctype,
5170
+ FetchMethod: FetchMethod,
5171
+ FetchRequest: FetchRequest,
5172
+ FetchResponse: FetchResponse,
4035
5173
  FrameElement: FrameElement,
4036
- get FrameLoadingStyle() {
4037
- return FrameLoadingStyle;
4038
- },
5174
+ FrameLoadingStyle: FrameLoadingStyle,
4039
5175
  FrameRenderer: FrameRenderer,
4040
5176
  PageRenderer: PageRenderer,
4041
5177
  PageSnapshot: PageSnapshot,
@@ -4046,6 +5182,10 @@ var turbo_es2017Esm = Object.freeze({
4046
5182
  clearCache: clearCache,
4047
5183
  connectStreamSource: connectStreamSource,
4048
5184
  disconnectStreamSource: disconnectStreamSource,
5185
+ fetch: fetchWithTurboHeaders,
5186
+ fetchEnctypeFromString: fetchEnctypeFromString,
5187
+ fetchMethodFromString: fetchMethodFromString,
5188
+ isSafe: isSafe,
4049
5189
  navigator: navigator$1,
4050
5190
  registerAdapter: registerAdapter,
4051
5191
  renderStreamMessage: renderStreamMessage,
@@ -4060,14 +5200,14 @@ var turbo_es2017Esm = Object.freeze({
4060
5200
  let consumer;
4061
5201
 
4062
5202
  async function getConsumer() {
4063
- return consumer || setConsumer(createConsumer().then(setConsumer));
5203
+ return consumer || setConsumer(createConsumer$1().then(setConsumer));
4064
5204
  }
4065
5205
 
4066
5206
  function setConsumer(newConsumer) {
4067
5207
  return consumer = newConsumer;
4068
5208
  }
4069
5209
 
4070
- async function createConsumer() {
5210
+ async function createConsumer$1() {
4071
5211
  const {createConsumer: createConsumer} = await Promise.resolve().then((function() {
4072
5212
  return index;
4073
5213
  }));
@@ -4083,7 +5223,7 @@ var cable = Object.freeze({
4083
5223
  __proto__: null,
4084
5224
  getConsumer: getConsumer,
4085
5225
  setConsumer: setConsumer,
4086
- createConsumer: createConsumer,
5226
+ createConsumer: createConsumer$1,
4087
5227
  subscribeTo: subscribeTo
4088
5228
  });
4089
5229
 
@@ -4193,6 +5333,8 @@ function isBodyInit(body) {
4193
5333
  return body instanceof FormData || body instanceof URLSearchParams;
4194
5334
  }
4195
5335
 
5336
+ window.Turbo = Turbo$1;
5337
+
4196
5338
  addEventListener("turbo:before-fetch-request", encodeMethodIntoRequestBody);
4197
5339
 
4198
5340
  var adapters = {
@@ -4309,6 +5451,8 @@ ConnectionMonitor.staleThreshold = 6;
4309
5451
 
4310
5452
  ConnectionMonitor.reconnectionBackoffRate = .15;
4311
5453
 
5454
+ var ConnectionMonitor$1 = ConnectionMonitor;
5455
+
4312
5456
  var INTERNAL = {
4313
5457
  message_types: {
4314
5458
  welcome: "welcome",
@@ -4320,7 +5464,8 @@ var INTERNAL = {
4320
5464
  disconnect_reasons: {
4321
5465
  unauthorized: "unauthorized",
4322
5466
  invalid_request: "invalid_request",
4323
- server_restart: "server_restart"
5467
+ server_restart: "server_restart",
5468
+ remote: "remote"
4324
5469
  },
4325
5470
  default_mount_path: "/cable",
4326
5471
  protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
@@ -4337,7 +5482,7 @@ class Connection {
4337
5482
  this.open = this.open.bind(this);
4338
5483
  this.consumer = consumer;
4339
5484
  this.subscriptions = this.consumer.subscriptions;
4340
- this.monitor = new ConnectionMonitor(this);
5485
+ this.monitor = new ConnectionMonitor$1(this);
4341
5486
  this.disconnected = true;
4342
5487
  }
4343
5488
  send(data) {
@@ -4353,11 +5498,12 @@ class Connection {
4353
5498
  logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
4354
5499
  return false;
4355
5500
  } else {
4356
- logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${protocols}`);
5501
+ const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
5502
+ logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
4357
5503
  if (this.webSocket) {
4358
5504
  this.uninstallEventHandlers();
4359
5505
  }
4360
- this.webSocket = new adapters.WebSocket(this.consumer.url, protocols);
5506
+ this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
4361
5507
  this.installEventHandlers();
4362
5508
  this.monitor.start();
4363
5509
  return true;
@@ -4369,7 +5515,7 @@ class Connection {
4369
5515
  if (!allowReconnect) {
4370
5516
  this.monitor.stop();
4371
5517
  }
4372
- if (this.isActive()) {
5518
+ if (this.isOpen()) {
4373
5519
  return this.webSocket.close();
4374
5520
  }
4375
5521
  }
@@ -4399,6 +5545,9 @@ class Connection {
4399
5545
  isActive() {
4400
5546
  return this.isState("open", "connecting");
4401
5547
  }
5548
+ triedToReconnect() {
5549
+ return this.monitor.reconnectAttempts > 0;
5550
+ }
4402
5551
  isProtocolSupported() {
4403
5552
  return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
4404
5553
  }
@@ -4438,6 +5587,9 @@ Connection.prototype.events = {
4438
5587
  const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
4439
5588
  switch (type) {
4440
5589
  case message_types.welcome:
5590
+ if (this.triedToReconnect()) {
5591
+ this.reconnectAttempted = true;
5592
+ }
4441
5593
  this.monitor.recordConnect();
4442
5594
  return this.subscriptions.reload();
4443
5595
 
@@ -4452,7 +5604,16 @@ Connection.prototype.events = {
4452
5604
 
4453
5605
  case message_types.confirmation:
4454
5606
  this.subscriptions.confirmSubscription(identifier);
4455
- return this.subscriptions.notify(identifier, "connected");
5607
+ if (this.reconnectAttempted) {
5608
+ this.reconnectAttempted = false;
5609
+ return this.subscriptions.notify(identifier, "connected", {
5610
+ reconnected: true
5611
+ });
5612
+ } else {
5613
+ return this.subscriptions.notify(identifier, "connected", {
5614
+ reconnected: false
5615
+ });
5616
+ }
4456
5617
 
4457
5618
  case message_types.rejection:
4458
5619
  return this.subscriptions.reject(identifier);
@@ -4487,6 +5648,8 @@ Connection.prototype.events = {
4487
5648
  }
4488
5649
  };
4489
5650
 
5651
+ var Connection$1 = Connection;
5652
+
4490
5653
  const extend = function(object, properties) {
4491
5654
  if (properties != null) {
4492
5655
  for (let key in properties) {
@@ -4556,10 +5719,12 @@ class SubscriptionGuarantor {
4556
5719
  }
4557
5720
  }
4558
5721
 
5722
+ var SubscriptionGuarantor$1 = SubscriptionGuarantor;
5723
+
4559
5724
  class Subscriptions {
4560
5725
  constructor(consumer) {
4561
5726
  this.consumer = consumer;
4562
- this.guarantor = new SubscriptionGuarantor(this);
5727
+ this.guarantor = new SubscriptionGuarantor$1(this);
4563
5728
  this.subscriptions = [];
4564
5729
  }
4565
5730
  create(channelName, mixin) {
@@ -4636,7 +5801,8 @@ class Consumer {
4636
5801
  constructor(url) {
4637
5802
  this._url = url;
4638
5803
  this.subscriptions = new Subscriptions(this);
4639
- this.connection = new Connection(this);
5804
+ this.connection = new Connection$1(this);
5805
+ this.subprotocols = [];
4640
5806
  }
4641
5807
  get url() {
4642
5808
  return createWebSocketURL(this._url);
@@ -4657,6 +5823,9 @@ class Consumer {
4657
5823
  return this.connection.open();
4658
5824
  }
4659
5825
  }
5826
+ addSubProtocol(subprotocol) {
5827
+ this.subprotocols = [ ...this.subprotocols, subprotocol ];
5828
+ }
4660
5829
  }
4661
5830
 
4662
5831
  function createWebSocketURL(url) {
@@ -4674,7 +5843,7 @@ function createWebSocketURL(url) {
4674
5843
  }
4675
5844
  }
4676
5845
 
4677
- function createConsumer$1(url = getConfig("url") || INTERNAL.default_mount_path) {
5846
+ function createConsumer(url = getConfig("url") || INTERNAL.default_mount_path) {
4678
5847
  return new Consumer(url);
4679
5848
  }
4680
5849
 
@@ -4687,18 +5856,18 @@ function getConfig(name) {
4687
5856
 
4688
5857
  var index = Object.freeze({
4689
5858
  __proto__: null,
4690
- Connection: Connection,
4691
- ConnectionMonitor: ConnectionMonitor,
5859
+ Connection: Connection$1,
5860
+ ConnectionMonitor: ConnectionMonitor$1,
4692
5861
  Consumer: Consumer,
4693
5862
  INTERNAL: INTERNAL,
4694
5863
  Subscription: Subscription,
4695
5864
  Subscriptions: Subscriptions,
4696
- SubscriptionGuarantor: SubscriptionGuarantor,
5865
+ SubscriptionGuarantor: SubscriptionGuarantor$1,
4697
5866
  adapters: adapters,
4698
5867
  createWebSocketURL: createWebSocketURL,
4699
5868
  logger: logger,
4700
- createConsumer: createConsumer$1,
5869
+ createConsumer: createConsumer,
4701
5870
  getConfig: getConfig
4702
5871
  });
4703
5872
 
4704
- export { turbo_es2017Esm as Turbo, cable };
5873
+ export { Turbo$1 as Turbo, cable };