masks 0.3.0 → 0.3.2

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.
@@ -14,8 +14,8 @@
14
14
  var init_adapters = __esm({
15
15
  "node_modules/@rails/actioncable/src/adapters.js"() {
16
16
  adapters_default = {
17
- logger: self.console,
18
- WebSocket: self.WebSocket
17
+ logger: typeof console !== "undefined" ? console : void 0,
18
+ WebSocket: typeof WebSocket !== "undefined" ? WebSocket : void 0
19
19
  };
20
20
  }
21
21
  });
@@ -596,21 +596,38 @@
596
596
  });
597
597
 
598
598
  // node_modules/@hotwired/turbo/dist/turbo.es2017-esm.js
599
- (function() {
600
- if (window.Reflect === void 0 || window.customElements === void 0 || window.customElements.polyfillWrapFlushCallback) {
601
- return;
602
- }
603
- const BuiltInHTMLElement = HTMLElement;
604
- const wrapperForTheName = {
605
- HTMLElement: function HTMLElement2() {
606
- return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
607
- }
608
- };
609
- window.HTMLElement = wrapperForTheName["HTMLElement"];
610
- HTMLElement.prototype = BuiltInHTMLElement.prototype;
611
- HTMLElement.prototype.constructor = HTMLElement;
612
- Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
613
- })();
599
+ var turbo_es2017_esm_exports = {};
600
+ __export(turbo_es2017_esm_exports, {
601
+ FetchEnctype: () => FetchEnctype,
602
+ FetchMethod: () => FetchMethod,
603
+ FetchRequest: () => FetchRequest,
604
+ FetchResponse: () => FetchResponse,
605
+ FrameElement: () => FrameElement,
606
+ FrameLoadingStyle: () => FrameLoadingStyle,
607
+ FrameRenderer: () => FrameRenderer,
608
+ PageRenderer: () => PageRenderer,
609
+ PageSnapshot: () => PageSnapshot,
610
+ StreamActions: () => StreamActions,
611
+ StreamElement: () => StreamElement,
612
+ StreamSourceElement: () => StreamSourceElement,
613
+ cache: () => cache,
614
+ clearCache: () => clearCache,
615
+ connectStreamSource: () => connectStreamSource,
616
+ disconnectStreamSource: () => disconnectStreamSource,
617
+ fetch: () => fetchWithTurboHeaders,
618
+ fetchEnctypeFromString: () => fetchEnctypeFromString,
619
+ fetchMethodFromString: () => fetchMethodFromString,
620
+ isSafe: () => isSafe,
621
+ navigator: () => navigator$1,
622
+ registerAdapter: () => registerAdapter,
623
+ renderStreamMessage: () => renderStreamMessage,
624
+ session: () => session,
625
+ setConfirmMethod: () => setConfirmMethod,
626
+ setFormMode: () => setFormMode,
627
+ setProgressBarDelay: () => setProgressBarDelay,
628
+ start: () => start,
629
+ visit: () => visit
630
+ });
614
631
  (function(prototype) {
615
632
  if (typeof prototype.requestSubmit == "function")
616
633
  return;
@@ -640,7 +657,7 @@
640
657
  function findSubmitterFromClickTarget(target) {
641
658
  const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
642
659
  const candidate = element ? element.closest("input, button") : null;
643
- return (candidate === null || candidate === void 0 ? void 0 : candidate.type) == "submit" ? candidate : null;
660
+ return candidate?.type == "submit" ? candidate : null;
644
661
  }
645
662
  function clickCaptured(event) {
646
663
  const submitter = findSubmitterFromClickTarget(event.target);
@@ -652,10 +669,13 @@
652
669
  if ("submitter" in Event.prototype)
653
670
  return;
654
671
  let prototype = window.Event.prototype;
655
- if ("SubmitEvent" in window && /Apple Computer/.test(navigator.vendor)) {
656
- prototype = window.SubmitEvent.prototype;
657
- } else if ("SubmitEvent" in window) {
658
- return;
672
+ if ("SubmitEvent" in window) {
673
+ const prototypeOfSubmitEvent = window.SubmitEvent.prototype;
674
+ if (/Apple Computer/.test(navigator.vendor) && !("submitter" in prototypeOfSubmitEvent)) {
675
+ prototype = prototypeOfSubmitEvent;
676
+ } else {
677
+ return;
678
+ }
659
679
  }
660
680
  addEventListener("click", clickCaptured, true);
661
681
  Object.defineProperty(prototype, "submitter", {
@@ -666,18 +686,18 @@
666
686
  }
667
687
  });
668
688
  })();
669
- var FrameLoadingStyle;
670
- (function(FrameLoadingStyle2) {
671
- FrameLoadingStyle2["eager"] = "eager";
672
- FrameLoadingStyle2["lazy"] = "lazy";
673
- })(FrameLoadingStyle || (FrameLoadingStyle = {}));
689
+ var FrameLoadingStyle = {
690
+ eager: "eager",
691
+ lazy: "lazy"
692
+ };
674
693
  var FrameElement = class _FrameElement extends HTMLElement {
694
+ static delegateConstructor = void 0;
695
+ loaded = Promise.resolve();
675
696
  static get observedAttributes() {
676
- return ["disabled", "complete", "loading", "src"];
697
+ return ["disabled", "loading", "src"];
677
698
  }
678
699
  constructor() {
679
700
  super();
680
- this.loaded = Promise.resolve();
681
701
  this.delegate = new _FrameElement.delegateConstructor(this);
682
702
  }
683
703
  connectedCallback() {
@@ -692,17 +712,21 @@
692
712
  attributeChangedCallback(name) {
693
713
  if (name == "loading") {
694
714
  this.delegate.loadingStyleChanged();
695
- } else if (name == "complete") {
696
- this.delegate.completeChanged();
697
715
  } else if (name == "src") {
698
716
  this.delegate.sourceURLChanged();
699
- } else {
717
+ } else if (name == "disabled") {
700
718
  this.delegate.disabledChanged();
701
719
  }
702
720
  }
721
+ /**
722
+ * Gets the URL to lazily load source HTML from
723
+ */
703
724
  get src() {
704
725
  return this.getAttribute("src");
705
726
  }
727
+ /**
728
+ * Sets the URL to lazily load source HTML from
729
+ */
706
730
  set src(value) {
707
731
  if (value) {
708
732
  this.setAttribute("src", value);
@@ -710,9 +734,31 @@
710
734
  this.removeAttribute("src");
711
735
  }
712
736
  }
737
+ /**
738
+ * Gets the refresh mode for the frame.
739
+ */
740
+ get refresh() {
741
+ return this.getAttribute("refresh");
742
+ }
743
+ /**
744
+ * Sets the refresh mode for the frame.
745
+ */
746
+ set refresh(value) {
747
+ if (value) {
748
+ this.setAttribute("refresh", value);
749
+ } else {
750
+ this.removeAttribute("refresh");
751
+ }
752
+ }
753
+ /**
754
+ * Determines if the element is loading
755
+ */
713
756
  get loading() {
714
757
  return frameLoadingStyleFromString(this.getAttribute("loading") || "");
715
758
  }
759
+ /**
760
+ * Sets the value of if the element is loading
761
+ */
716
762
  set loading(value) {
717
763
  if (value) {
718
764
  this.setAttribute("loading", value);
@@ -720,9 +766,19 @@
720
766
  this.removeAttribute("loading");
721
767
  }
722
768
  }
769
+ /**
770
+ * Gets the disabled state of the frame.
771
+ *
772
+ * If disabled, no requests will be intercepted by the frame.
773
+ */
723
774
  get disabled() {
724
775
  return this.hasAttribute("disabled");
725
776
  }
777
+ /**
778
+ * Sets the disabled state of the frame.
779
+ *
780
+ * If disabled, no requests will be intercepted by the frame.
781
+ */
726
782
  set disabled(value) {
727
783
  if (value) {
728
784
  this.setAttribute("disabled", "");
@@ -730,9 +786,19 @@
730
786
  this.removeAttribute("disabled");
731
787
  }
732
788
  }
789
+ /**
790
+ * Gets the autoscroll state of the frame.
791
+ *
792
+ * If true, the frame will be scrolled into view automatically on update.
793
+ */
733
794
  get autoscroll() {
734
795
  return this.hasAttribute("autoscroll");
735
796
  }
797
+ /**
798
+ * Sets the autoscroll state of the frame.
799
+ *
800
+ * If true, the frame will be scrolled into view automatically on update.
801
+ */
736
802
  set autoscroll(value) {
737
803
  if (value) {
738
804
  this.setAttribute("autoscroll", "");
@@ -740,15 +806,27 @@
740
806
  this.removeAttribute("autoscroll");
741
807
  }
742
808
  }
809
+ /**
810
+ * Determines if the element has finished loading
811
+ */
743
812
  get complete() {
744
813
  return !this.delegate.isLoading;
745
814
  }
815
+ /**
816
+ * Gets the active state of the frame.
817
+ *
818
+ * If inactive, source changes will not be observed.
819
+ */
746
820
  get isActive() {
747
821
  return this.ownerDocument === document && !this.isPreview;
748
822
  }
823
+ /**
824
+ * Sets the active state of the frame.
825
+ *
826
+ * If inactive, source changes will not be observed.
827
+ */
749
828
  get isPreview() {
750
- var _a, _b;
751
- return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.hasAttribute("data-turbo-preview");
829
+ return this.ownerDocument?.documentElement?.hasAttribute("data-turbo-preview");
752
830
  }
753
831
  };
754
832
  function frameLoadingStyleFromString(style) {
@@ -770,8 +848,8 @@
770
848
  return anchorMatch[1];
771
849
  }
772
850
  }
773
- function getAction(form, submitter) {
774
- const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formaction")) || form.getAttribute("action") || form.action;
851
+ function getAction$1(form, submitter) {
852
+ const action = submitter?.getAttribute("formaction") || form.getAttribute("action") || form.action;
775
853
  return expandURL(action);
776
854
  }
777
855
  function getExtension(url) {
@@ -893,6 +971,13 @@
893
971
  }
894
972
  return event;
895
973
  }
974
+ function nextRepaint() {
975
+ if (document.visibilityState === "hidden") {
976
+ return nextEventLoopTick();
977
+ } else {
978
+ return nextAnimationFrame();
979
+ }
980
+ }
896
981
  function nextAnimationFrame() {
897
982
  return new Promise((resolve) => requestAnimationFrame(() => resolve()));
898
983
  }
@@ -931,7 +1016,7 @@
931
1016
  }).join("");
932
1017
  }
933
1018
  function getAttribute(attributeName, ...elements) {
934
- for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
1019
+ for (const value of elements.map((element) => element?.getAttribute(attributeName))) {
935
1020
  if (typeof value == "string")
936
1021
  return value;
937
1022
  }
@@ -1002,19 +1087,73 @@
1002
1087
  return element;
1003
1088
  }
1004
1089
  function findClosestRecursively(element, selector) {
1005
- var _a;
1006
1090
  if (element instanceof Element) {
1007
- return element.closest(selector) || findClosestRecursively(element.assignedSlot || ((_a = element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host), selector);
1091
+ return element.closest(selector) || findClosestRecursively(element.assignedSlot || element.getRootNode()?.host, selector);
1092
+ }
1093
+ }
1094
+ function elementIsFocusable(element) {
1095
+ const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
1096
+ return !!element && element.closest(inertDisabledOrHidden) == null && typeof element.focus == "function";
1097
+ }
1098
+ function queryAutofocusableElement(elementOrDocumentFragment) {
1099
+ return Array.from(elementOrDocumentFragment.querySelectorAll("[autofocus]")).find(elementIsFocusable);
1100
+ }
1101
+ async function around(callback, reader) {
1102
+ const before = reader();
1103
+ callback();
1104
+ await nextAnimationFrame();
1105
+ const after = reader();
1106
+ return [before, after];
1107
+ }
1108
+ function doesNotTargetIFrame(anchor) {
1109
+ if (anchor.hasAttribute("target")) {
1110
+ for (const element of document.getElementsByName(anchor.target)) {
1111
+ if (element instanceof HTMLIFrameElement)
1112
+ return false;
1113
+ }
1114
+ }
1115
+ return true;
1116
+ }
1117
+ function findLinkFromClickTarget(target) {
1118
+ return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
1119
+ }
1120
+ function getLocationForLink(link) {
1121
+ return expandURL(link.getAttribute("href") || "");
1122
+ }
1123
+ function debounce(fn, delay) {
1124
+ let timeoutId = null;
1125
+ return (...args) => {
1126
+ const callback = () => fn.apply(this, args);
1127
+ clearTimeout(timeoutId);
1128
+ timeoutId = setTimeout(callback, delay);
1129
+ };
1130
+ }
1131
+ var LimitedSet = class extends Set {
1132
+ constructor(maxSize) {
1133
+ super();
1134
+ this.maxSize = maxSize;
1008
1135
  }
1136
+ add(value) {
1137
+ if (this.size >= this.maxSize) {
1138
+ const iterator = this.values();
1139
+ const oldestValue = iterator.next().value;
1140
+ this.delete(oldestValue);
1141
+ }
1142
+ super.add(value);
1143
+ }
1144
+ };
1145
+ var recentRequests = new LimitedSet(20);
1146
+ var nativeFetch = window.fetch;
1147
+ function fetchWithTurboHeaders(url, options = {}) {
1148
+ const modifiedHeaders = new Headers(options.headers || {});
1149
+ const requestUID = uuid();
1150
+ recentRequests.add(requestUID);
1151
+ modifiedHeaders.append("X-Turbo-Request-Id", requestUID);
1152
+ return nativeFetch(url, {
1153
+ ...options,
1154
+ headers: modifiedHeaders
1155
+ });
1009
1156
  }
1010
- var FetchMethod;
1011
- (function(FetchMethod2) {
1012
- FetchMethod2[FetchMethod2["get"] = 0] = "get";
1013
- FetchMethod2[FetchMethod2["post"] = 1] = "post";
1014
- FetchMethod2[FetchMethod2["put"] = 2] = "put";
1015
- FetchMethod2[FetchMethod2["patch"] = 3] = "patch";
1016
- FetchMethod2[FetchMethod2["delete"] = 4] = "delete";
1017
- })(FetchMethod || (FetchMethod = {}));
1018
1157
  function fetchMethodFromString(method) {
1019
1158
  switch (method.toLowerCase()) {
1020
1159
  case "get":
@@ -1029,17 +1168,75 @@
1029
1168
  return FetchMethod.delete;
1030
1169
  }
1031
1170
  }
1171
+ var FetchMethod = {
1172
+ get: "get",
1173
+ post: "post",
1174
+ put: "put",
1175
+ patch: "patch",
1176
+ delete: "delete"
1177
+ };
1178
+ function fetchEnctypeFromString(encoding) {
1179
+ switch (encoding.toLowerCase()) {
1180
+ case FetchEnctype.multipart:
1181
+ return FetchEnctype.multipart;
1182
+ case FetchEnctype.plain:
1183
+ return FetchEnctype.plain;
1184
+ default:
1185
+ return FetchEnctype.urlEncoded;
1186
+ }
1187
+ }
1188
+ var FetchEnctype = {
1189
+ urlEncoded: "application/x-www-form-urlencoded",
1190
+ multipart: "multipart/form-data",
1191
+ plain: "text/plain"
1192
+ };
1032
1193
  var FetchRequest = class {
1033
- constructor(delegate, method, location2, body = new URLSearchParams(), target = null) {
1034
- this.abortController = new AbortController();
1035
- this.resolveRequestPromise = (_value) => {
1036
- };
1194
+ abortController = new AbortController();
1195
+ #resolveRequestPromise = (_value) => {
1196
+ };
1197
+ constructor(delegate, method, location2, requestBody = new URLSearchParams(), target = null, enctype = FetchEnctype.urlEncoded) {
1198
+ const [url, body] = buildResourceAndBody(expandURL(location2), method, requestBody, enctype);
1037
1199
  this.delegate = delegate;
1038
- this.method = method;
1039
- this.headers = this.defaultHeaders;
1040
- this.body = body;
1041
- this.url = location2;
1200
+ this.url = url;
1042
1201
  this.target = target;
1202
+ this.fetchOptions = {
1203
+ credentials: "same-origin",
1204
+ redirect: "follow",
1205
+ method,
1206
+ headers: { ...this.defaultHeaders },
1207
+ body,
1208
+ signal: this.abortSignal,
1209
+ referrer: this.delegate.referrer?.href
1210
+ };
1211
+ this.enctype = enctype;
1212
+ }
1213
+ get method() {
1214
+ return this.fetchOptions.method;
1215
+ }
1216
+ set method(value) {
1217
+ const fetchBody = this.isSafe ? this.url.searchParams : this.fetchOptions.body || new FormData();
1218
+ const fetchMethod = fetchMethodFromString(value) || FetchMethod.get;
1219
+ this.url.search = "";
1220
+ const [url, body] = buildResourceAndBody(this.url, fetchMethod, fetchBody, this.enctype);
1221
+ this.url = url;
1222
+ this.fetchOptions.body = body;
1223
+ this.fetchOptions.method = fetchMethod;
1224
+ }
1225
+ get headers() {
1226
+ return this.fetchOptions.headers;
1227
+ }
1228
+ set headers(value) {
1229
+ this.fetchOptions.headers = value;
1230
+ }
1231
+ get body() {
1232
+ if (this.isSafe) {
1233
+ return this.url.searchParams;
1234
+ } else {
1235
+ return this.fetchOptions.body;
1236
+ }
1237
+ }
1238
+ set body(value) {
1239
+ this.fetchOptions.body = value;
1043
1240
  }
1044
1241
  get location() {
1045
1242
  return this.url;
@@ -1056,14 +1253,19 @@
1056
1253
  async perform() {
1057
1254
  const { fetchOptions } = this;
1058
1255
  this.delegate.prepareRequest(this);
1059
- await this.allowRequestToBeIntercepted(fetchOptions);
1256
+ const event = await this.#allowRequestToBeIntercepted(fetchOptions);
1060
1257
  try {
1061
1258
  this.delegate.requestStarted(this);
1062
- const response = await fetch(this.url.href, fetchOptions);
1259
+ if (event.detail.fetchRequest) {
1260
+ this.response = event.detail.fetchRequest.response;
1261
+ } else {
1262
+ this.response = fetchWithTurboHeaders(this.url.href, fetchOptions);
1263
+ }
1264
+ const response = await this.response;
1063
1265
  return await this.receive(response);
1064
1266
  } catch (error2) {
1065
1267
  if (error2.name !== "AbortError") {
1066
- if (this.willDelegateErrorHandling(error2)) {
1268
+ if (this.#willDelegateErrorHandling(error2)) {
1067
1269
  this.delegate.requestErrored(this, error2);
1068
1270
  }
1069
1271
  throw error2;
@@ -1088,25 +1290,13 @@
1088
1290
  }
1089
1291
  return fetchResponse;
1090
1292
  }
1091
- get fetchOptions() {
1092
- var _a;
1093
- return {
1094
- method: FetchMethod[this.method].toUpperCase(),
1095
- credentials: "same-origin",
1096
- headers: this.headers,
1097
- redirect: "follow",
1098
- body: this.isSafe ? null : this.body,
1099
- signal: this.abortSignal,
1100
- referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
1101
- };
1102
- }
1103
1293
  get defaultHeaders() {
1104
1294
  return {
1105
1295
  Accept: "text/html, application/xhtml+xml"
1106
1296
  };
1107
1297
  }
1108
1298
  get isSafe() {
1109
- return this.method === FetchMethod.get;
1299
+ return isSafe(this.method);
1110
1300
  }
1111
1301
  get abortSignal() {
1112
1302
  return this.abortController.signal;
@@ -1114,21 +1304,23 @@
1114
1304
  acceptResponseType(mimeType) {
1115
1305
  this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
1116
1306
  }
1117
- async allowRequestToBeIntercepted(fetchOptions) {
1118
- const requestInterception = new Promise((resolve) => this.resolveRequestPromise = resolve);
1307
+ async #allowRequestToBeIntercepted(fetchOptions) {
1308
+ const requestInterception = new Promise((resolve) => this.#resolveRequestPromise = resolve);
1119
1309
  const event = dispatch("turbo:before-fetch-request", {
1120
1310
  cancelable: true,
1121
1311
  detail: {
1122
1312
  fetchOptions,
1123
1313
  url: this.url,
1124
- resume: this.resolveRequestPromise
1314
+ resume: this.#resolveRequestPromise
1125
1315
  },
1126
1316
  target: this.target
1127
1317
  });
1318
+ this.url = event.detail.url;
1128
1319
  if (event.defaultPrevented)
1129
1320
  await requestInterception;
1321
+ return event;
1130
1322
  }
1131
- willDelegateErrorHandling(error2) {
1323
+ #willDelegateErrorHandling(error2) {
1132
1324
  const event = dispatch("turbo:fetch-request-error", {
1133
1325
  target: this.target,
1134
1326
  cancelable: true,
@@ -1137,15 +1329,37 @@
1137
1329
  return !event.defaultPrevented;
1138
1330
  }
1139
1331
  };
1332
+ function isSafe(fetchMethod) {
1333
+ return fetchMethodFromString(fetchMethod) == FetchMethod.get;
1334
+ }
1335
+ function buildResourceAndBody(resource, method, requestBody, enctype) {
1336
+ const searchParams = Array.from(requestBody).length > 0 ? new URLSearchParams(entriesExcludingFiles(requestBody)) : resource.searchParams;
1337
+ if (isSafe(method)) {
1338
+ return [mergeIntoURLSearchParams(resource, searchParams), null];
1339
+ } else if (enctype == FetchEnctype.urlEncoded) {
1340
+ return [resource, searchParams];
1341
+ } else {
1342
+ return [resource, requestBody];
1343
+ }
1344
+ }
1345
+ function entriesExcludingFiles(requestBody) {
1346
+ const entries = [];
1347
+ for (const [name, value] of requestBody) {
1348
+ if (value instanceof File)
1349
+ continue;
1350
+ else
1351
+ entries.push([name, value]);
1352
+ }
1353
+ return entries;
1354
+ }
1355
+ function mergeIntoURLSearchParams(url, requestBody) {
1356
+ const searchParams = new URLSearchParams(entriesExcludingFiles(requestBody));
1357
+ url.search = searchParams.toString();
1358
+ return url;
1359
+ }
1140
1360
  var AppearanceObserver = class {
1361
+ started = false;
1141
1362
  constructor(delegate, element) {
1142
- this.started = false;
1143
- this.intersect = (entries) => {
1144
- const lastEntry = entries.slice(-1)[0];
1145
- if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
1146
- this.delegate.elementAppearedInViewport(this.element);
1147
- }
1148
- };
1149
1363
  this.delegate = delegate;
1150
1364
  this.element = element;
1151
1365
  this.intersectionObserver = new IntersectionObserver(this.intersect);
@@ -1162,8 +1376,15 @@
1162
1376
  this.intersectionObserver.unobserve(this.element);
1163
1377
  }
1164
1378
  }
1379
+ intersect = (entries) => {
1380
+ const lastEntry = entries.slice(-1)[0];
1381
+ if (lastEntry?.isIntersecting) {
1382
+ this.delegate.elementAppearedInViewport(this.element);
1383
+ }
1384
+ };
1165
1385
  };
1166
1386
  var StreamMessage = class {
1387
+ static contentType = "text/vnd.turbo-stream.html";
1167
1388
  static wrap(message) {
1168
1389
  if (typeof message == "string") {
1169
1390
  return new this(createDocumentFragment(message));
@@ -1175,7 +1396,6 @@
1175
1396
  this.fragment = importStreamElements(fragment);
1176
1397
  }
1177
1398
  };
1178
- StreamMessage.contentType = "text/vnd.turbo-stream.html";
1179
1399
  function importStreamElements(fragment) {
1180
1400
  for (const element of fragment.querySelectorAll("turbo-stream")) {
1181
1401
  const streamElement = document.importNode(element, true);
@@ -1186,81 +1406,83 @@
1186
1406
  }
1187
1407
  return fragment;
1188
1408
  }
1189
- var FormSubmissionState;
1190
- (function(FormSubmissionState2) {
1191
- FormSubmissionState2[FormSubmissionState2["initialized"] = 0] = "initialized";
1192
- FormSubmissionState2[FormSubmissionState2["requesting"] = 1] = "requesting";
1193
- FormSubmissionState2[FormSubmissionState2["waiting"] = 2] = "waiting";
1194
- FormSubmissionState2[FormSubmissionState2["receiving"] = 3] = "receiving";
1195
- FormSubmissionState2[FormSubmissionState2["stopping"] = 4] = "stopping";
1196
- FormSubmissionState2[FormSubmissionState2["stopped"] = 5] = "stopped";
1197
- })(FormSubmissionState || (FormSubmissionState = {}));
1198
- var FormEnctype;
1199
- (function(FormEnctype2) {
1200
- FormEnctype2["urlEncoded"] = "application/x-www-form-urlencoded";
1201
- FormEnctype2["multipart"] = "multipart/form-data";
1202
- FormEnctype2["plain"] = "text/plain";
1203
- })(FormEnctype || (FormEnctype = {}));
1204
- function formEnctypeFromString(encoding) {
1205
- switch (encoding.toLowerCase()) {
1206
- case FormEnctype.multipart:
1207
- return FormEnctype.multipart;
1208
- case FormEnctype.plain:
1209
- return FormEnctype.plain;
1210
- default:
1211
- return FormEnctype.urlEncoded;
1409
+ var PREFETCH_DELAY = 100;
1410
+ var PrefetchCache = class {
1411
+ #prefetchTimeout = null;
1412
+ #prefetched = null;
1413
+ get(url) {
1414
+ if (this.#prefetched && this.#prefetched.url === url && this.#prefetched.expire > Date.now()) {
1415
+ return this.#prefetched.request;
1416
+ }
1212
1417
  }
1213
- }
1418
+ setLater(url, request, ttl) {
1419
+ this.clear();
1420
+ this.#prefetchTimeout = setTimeout(() => {
1421
+ request.perform();
1422
+ this.set(url, request, ttl);
1423
+ this.#prefetchTimeout = null;
1424
+ }, PREFETCH_DELAY);
1425
+ }
1426
+ set(url, request, ttl) {
1427
+ this.#prefetched = { url, request, expire: new Date((/* @__PURE__ */ new Date()).getTime() + ttl) };
1428
+ }
1429
+ clear() {
1430
+ if (this.#prefetchTimeout)
1431
+ clearTimeout(this.#prefetchTimeout);
1432
+ this.#prefetched = null;
1433
+ }
1434
+ };
1435
+ var cacheTtl = 10 * 1e3;
1436
+ var prefetchCache = new PrefetchCache();
1437
+ var FormSubmissionState = {
1438
+ initialized: "initialized",
1439
+ requesting: "requesting",
1440
+ waiting: "waiting",
1441
+ receiving: "receiving",
1442
+ stopping: "stopping",
1443
+ stopped: "stopped"
1444
+ };
1214
1445
  var FormSubmission = class _FormSubmission {
1446
+ state = FormSubmissionState.initialized;
1215
1447
  static confirmMethod(message, _element, _submitter) {
1216
1448
  return Promise.resolve(confirm(message));
1217
1449
  }
1218
1450
  constructor(delegate, formElement, submitter, mustRedirect = false) {
1219
- this.state = FormSubmissionState.initialized;
1451
+ const method = getMethod(formElement, submitter);
1452
+ const action = getAction(getFormAction(formElement, submitter), method);
1453
+ const body = buildFormData(formElement, submitter);
1454
+ const enctype = getEnctype(formElement, submitter);
1220
1455
  this.delegate = delegate;
1221
1456
  this.formElement = formElement;
1222
1457
  this.submitter = submitter;
1223
- this.formData = buildFormData(formElement, submitter);
1224
- this.location = expandURL(this.action);
1225
- if (this.method == FetchMethod.get) {
1226
- mergeFormDataEntries(this.location, [...this.body.entries()]);
1227
- }
1228
- this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
1458
+ this.fetchRequest = new FetchRequest(this, method, action, body, formElement, enctype);
1229
1459
  this.mustRedirect = mustRedirect;
1230
1460
  }
1231
1461
  get method() {
1232
- var _a;
1233
- const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
1234
- return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
1462
+ return this.fetchRequest.method;
1463
+ }
1464
+ set method(value) {
1465
+ this.fetchRequest.method = value;
1235
1466
  }
1236
1467
  get action() {
1237
- var _a;
1238
- const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
1239
- if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
1240
- return this.submitter.getAttribute("formaction") || "";
1241
- } else {
1242
- return this.formElement.getAttribute("action") || formElementAction || "";
1243
- }
1468
+ return this.fetchRequest.url.toString();
1469
+ }
1470
+ set action(value) {
1471
+ this.fetchRequest.url = expandURL(value);
1244
1472
  }
1245
1473
  get body() {
1246
- if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
1247
- return new URLSearchParams(this.stringFormData);
1248
- } else {
1249
- return this.formData;
1250
- }
1474
+ return this.fetchRequest.body;
1251
1475
  }
1252
1476
  get enctype() {
1253
- var _a;
1254
- return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
1477
+ return this.fetchRequest.enctype;
1255
1478
  }
1256
1479
  get isSafe() {
1257
1480
  return this.fetchRequest.isSafe;
1258
1481
  }
1259
- get stringFormData() {
1260
- return [...this.formData].reduce((entries, [name, value]) => {
1261
- return entries.concat(typeof value == "string" ? [[name, value]] : []);
1262
- }, []);
1482
+ get location() {
1483
+ return this.fetchRequest.url;
1263
1484
  }
1485
+ // The submission process
1264
1486
  async start() {
1265
1487
  const { initialized, requesting } = FormSubmissionState;
1266
1488
  const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
@@ -1283,6 +1505,7 @@
1283
1505
  return true;
1284
1506
  }
1285
1507
  }
1508
+ // Fetch request delegate
1286
1509
  prepareRequest(request) {
1287
1510
  if (!request.isSafe) {
1288
1511
  const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
@@ -1295,10 +1518,10 @@
1295
1518
  }
1296
1519
  }
1297
1520
  requestStarted(_request) {
1298
- var _a;
1299
1521
  this.state = FormSubmissionState.waiting;
1300
- (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
1522
+ this.submitter?.setAttribute("disabled", "");
1301
1523
  this.setSubmitsWith();
1524
+ markAsBusy(this.formElement);
1302
1525
  dispatch("turbo:submit-start", {
1303
1526
  target: this.formElement,
1304
1527
  detail: { formSubmission: this }
@@ -1306,12 +1529,16 @@
1306
1529
  this.delegate.formSubmissionStarted(this);
1307
1530
  }
1308
1531
  requestPreventedHandlingResponse(request, response) {
1532
+ prefetchCache.clear();
1309
1533
  this.result = { success: response.succeeded, fetchResponse: response };
1310
1534
  }
1311
1535
  requestSucceededWithResponse(request, response) {
1312
1536
  if (response.clientError || response.serverError) {
1313
1537
  this.delegate.formSubmissionFailedWithResponse(this, response);
1314
- } else if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
1538
+ return;
1539
+ }
1540
+ prefetchCache.clear();
1541
+ if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
1315
1542
  const error2 = new Error("Form responses must redirect to another location");
1316
1543
  this.delegate.formSubmissionErrored(this, error2);
1317
1544
  } else {
@@ -1329,16 +1556,17 @@
1329
1556
  this.delegate.formSubmissionErrored(this, error2);
1330
1557
  }
1331
1558
  requestFinished(_request) {
1332
- var _a;
1333
1559
  this.state = FormSubmissionState.stopped;
1334
- (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
1560
+ this.submitter?.removeAttribute("disabled");
1335
1561
  this.resetSubmitterText();
1562
+ clearBusyState(this.formElement);
1336
1563
  dispatch("turbo:submit-end", {
1337
1564
  target: this.formElement,
1338
- detail: Object.assign({ formSubmission: this }, this.result)
1565
+ detail: { formSubmission: this, ...this.result }
1339
1566
  });
1340
1567
  this.delegate.formSubmissionFinished(this);
1341
1568
  }
1569
+ // Private
1342
1570
  setSubmitsWith() {
1343
1571
  if (!this.submitter || !this.submitsWith)
1344
1572
  return;
@@ -1368,14 +1596,13 @@
1368
1596
  return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
1369
1597
  }
1370
1598
  get submitsWith() {
1371
- var _a;
1372
- return (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-submits-with");
1599
+ return this.submitter?.getAttribute("data-turbo-submits-with");
1373
1600
  }
1374
1601
  };
1375
1602
  function buildFormData(formElement, submitter) {
1376
1603
  const formData = new FormData(formElement);
1377
- const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
1378
- const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
1604
+ const name = submitter?.getAttribute("name");
1605
+ const value = submitter?.getAttribute("value");
1379
1606
  if (name) {
1380
1607
  formData.append(name, value || "");
1381
1608
  }
@@ -1394,15 +1621,27 @@
1394
1621
  function responseSucceededWithoutRedirect(response) {
1395
1622
  return response.statusCode == 200 && !response.redirected;
1396
1623
  }
1397
- function mergeFormDataEntries(url, entries) {
1398
- const searchParams = new URLSearchParams();
1399
- for (const [name, value] of entries) {
1400
- if (value instanceof File)
1401
- continue;
1402
- searchParams.append(name, value);
1624
+ function getFormAction(formElement, submitter) {
1625
+ const formElementAction = typeof formElement.action === "string" ? formElement.action : null;
1626
+ if (submitter?.hasAttribute("formaction")) {
1627
+ return submitter.getAttribute("formaction") || "";
1628
+ } else {
1629
+ return formElement.getAttribute("action") || formElementAction || "";
1403
1630
  }
1404
- url.search = searchParams.toString();
1405
- return url;
1631
+ }
1632
+ function getAction(formAction, fetchMethod) {
1633
+ const action = expandURL(formAction);
1634
+ if (isSafe(fetchMethod)) {
1635
+ action.search = "";
1636
+ }
1637
+ return action;
1638
+ }
1639
+ function getMethod(formElement, submitter) {
1640
+ const method = submitter?.getAttribute("formmethod") || formElement.getAttribute("method") || "";
1641
+ return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
1642
+ }
1643
+ function getEnctype(formElement, submitter) {
1644
+ return fetchEnctypeFromString(submitter?.getAttribute("formenctype") || formElement.enctype);
1406
1645
  }
1407
1646
  var Snapshot = class {
1408
1647
  constructor(element) {
@@ -1424,14 +1663,7 @@
1424
1663
  return this.element.isConnected;
1425
1664
  }
1426
1665
  get firstAutofocusableElement() {
1427
- const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
1428
- for (const element of this.element.querySelectorAll("[autofocus]")) {
1429
- if (element.closest(inertDisabledOrHidden) == null)
1430
- return element;
1431
- else
1432
- continue;
1433
- }
1434
- return null;
1666
+ return queryAutofocusableElement(this.element);
1435
1667
  }
1436
1668
  get permanentElements() {
1437
1669
  return queryPermanentElementsAll(this.element);
@@ -1458,23 +1690,8 @@
1458
1690
  return node.querySelectorAll("[id][data-turbo-permanent]");
1459
1691
  }
1460
1692
  var FormSubmitObserver = class {
1693
+ started = false;
1461
1694
  constructor(delegate, eventTarget) {
1462
- this.started = false;
1463
- this.submitCaptured = () => {
1464
- this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
1465
- this.eventTarget.addEventListener("submit", this.submitBubbled, false);
1466
- };
1467
- this.submitBubbled = (event) => {
1468
- if (!event.defaultPrevented) {
1469
- const form = event.target instanceof HTMLFormElement ? event.target : void 0;
1470
- const submitter = event.submitter || void 0;
1471
- if (form && submissionDoesNotDismissDialog(form, submitter) && submissionDoesNotTargetIFrame(form, submitter) && this.delegate.willSubmitForm(form, submitter)) {
1472
- event.preventDefault();
1473
- event.stopImmediatePropagation();
1474
- this.delegate.formSubmitted(form, submitter);
1475
- }
1476
- }
1477
- };
1478
1695
  this.delegate = delegate;
1479
1696
  this.eventTarget = eventTarget;
1480
1697
  }
@@ -1490,14 +1707,29 @@
1490
1707
  this.started = false;
1491
1708
  }
1492
1709
  }
1710
+ submitCaptured = () => {
1711
+ this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
1712
+ this.eventTarget.addEventListener("submit", this.submitBubbled, false);
1713
+ };
1714
+ submitBubbled = (event) => {
1715
+ if (!event.defaultPrevented) {
1716
+ const form = event.target instanceof HTMLFormElement ? event.target : void 0;
1717
+ const submitter = event.submitter || void 0;
1718
+ if (form && submissionDoesNotDismissDialog(form, submitter) && submissionDoesNotTargetIFrame(form, submitter) && this.delegate.willSubmitForm(form, submitter)) {
1719
+ event.preventDefault();
1720
+ event.stopImmediatePropagation();
1721
+ this.delegate.formSubmitted(form, submitter);
1722
+ }
1723
+ }
1724
+ };
1493
1725
  };
1494
1726
  function submissionDoesNotDismissDialog(form, submitter) {
1495
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
1727
+ const method = submitter?.getAttribute("formmethod") || form.getAttribute("method");
1496
1728
  return method != "dialog";
1497
1729
  }
1498
1730
  function submissionDoesNotTargetIFrame(form, submitter) {
1499
- if ((submitter === null || submitter === void 0 ? void 0 : submitter.hasAttribute("formtarget")) || form.hasAttribute("target")) {
1500
- const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
1731
+ if (submitter?.hasAttribute("formtarget") || form.hasAttribute("target")) {
1732
+ const target = submitter?.getAttribute("formtarget") || form.target;
1501
1733
  for (const element of document.getElementsByName(target)) {
1502
1734
  if (element instanceof HTMLIFrameElement)
1503
1735
  return false;
@@ -1508,14 +1740,15 @@
1508
1740
  }
1509
1741
  }
1510
1742
  var View = class {
1743
+ #resolveRenderPromise = (_value) => {
1744
+ };
1745
+ #resolveInterceptionPromise = (_value) => {
1746
+ };
1511
1747
  constructor(delegate, element) {
1512
- this.resolveRenderPromise = (_value) => {
1513
- };
1514
- this.resolveInterceptionPromise = (_value) => {
1515
- };
1516
1748
  this.delegate = delegate;
1517
1749
  this.element = element;
1518
1750
  }
1751
+ // Scrolling
1519
1752
  scrollToAnchor(anchor) {
1520
1753
  const element = this.snapshot.getElementForAnchor(anchor);
1521
1754
  if (element) {
@@ -1551,28 +1784,30 @@
1551
1784
  get scrollRoot() {
1552
1785
  return window;
1553
1786
  }
1787
+ // Rendering
1554
1788
  async render(renderer) {
1555
- const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
1789
+ const { isPreview, shouldRender, willRender, newSnapshot: snapshot } = renderer;
1790
+ const shouldInvalidate = willRender;
1556
1791
  if (shouldRender) {
1557
1792
  try {
1558
- this.renderPromise = new Promise((resolve) => this.resolveRenderPromise = resolve);
1793
+ this.renderPromise = new Promise((resolve) => this.#resolveRenderPromise = resolve);
1559
1794
  this.renderer = renderer;
1560
1795
  await this.prepareToRenderSnapshot(renderer);
1561
- const renderInterception = new Promise((resolve) => this.resolveInterceptionPromise = resolve);
1562
- const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
1796
+ const renderInterception = new Promise((resolve) => this.#resolveInterceptionPromise = resolve);
1797
+ const options = { resume: this.#resolveInterceptionPromise, render: this.renderer.renderElement, renderMethod: this.renderer.renderMethod };
1563
1798
  const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
1564
1799
  if (!immediateRender)
1565
1800
  await renderInterception;
1566
1801
  await this.renderSnapshot(renderer);
1567
- this.delegate.viewRenderedSnapshot(snapshot, isPreview);
1802
+ this.delegate.viewRenderedSnapshot(snapshot, isPreview, this.renderer.renderMethod);
1568
1803
  this.delegate.preloadOnLoadLinksForView(this.element);
1569
1804
  this.finishRenderingSnapshot(renderer);
1570
1805
  } finally {
1571
1806
  delete this.renderer;
1572
- this.resolveRenderPromise(void 0);
1807
+ this.#resolveRenderPromise(void 0);
1573
1808
  delete this.renderPromise;
1574
1809
  }
1575
- } else {
1810
+ } else if (shouldInvalidate) {
1576
1811
  this.invalidate(renderer.reloadReason);
1577
1812
  }
1578
1813
  }
@@ -1590,6 +1825,12 @@
1590
1825
  this.element.removeAttribute("data-turbo-preview");
1591
1826
  }
1592
1827
  }
1828
+ markVisitDirection(direction) {
1829
+ this.element.setAttribute("data-turbo-visit-direction", direction);
1830
+ }
1831
+ unmarkVisitDirection() {
1832
+ this.element.removeAttribute("data-turbo-visit-direction");
1833
+ }
1593
1834
  async renderSnapshot(renderer) {
1594
1835
  await renderer.render();
1595
1836
  }
@@ -1607,26 +1848,6 @@
1607
1848
  };
1608
1849
  var LinkInterceptor = class {
1609
1850
  constructor(delegate, element) {
1610
- this.clickBubbled = (event) => {
1611
- if (this.respondsToEventTarget(event.target)) {
1612
- this.clickEvent = event;
1613
- } else {
1614
- delete this.clickEvent;
1615
- }
1616
- };
1617
- this.linkClicked = (event) => {
1618
- if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
1619
- if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
1620
- this.clickEvent.preventDefault();
1621
- event.preventDefault();
1622
- this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
1623
- }
1624
- }
1625
- delete this.clickEvent;
1626
- };
1627
- this.willVisit = (_event) => {
1628
- delete this.clickEvent;
1629
- };
1630
1851
  this.delegate = delegate;
1631
1852
  this.element = element;
1632
1853
  }
@@ -1640,31 +1861,34 @@
1640
1861
  document.removeEventListener("turbo:click", this.linkClicked);
1641
1862
  document.removeEventListener("turbo:before-visit", this.willVisit);
1642
1863
  }
1864
+ clickBubbled = (event) => {
1865
+ if (this.respondsToEventTarget(event.target)) {
1866
+ this.clickEvent = event;
1867
+ } else {
1868
+ delete this.clickEvent;
1869
+ }
1870
+ };
1871
+ linkClicked = (event) => {
1872
+ if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
1873
+ if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url, event.detail.originalEvent)) {
1874
+ this.clickEvent.preventDefault();
1875
+ event.preventDefault();
1876
+ this.delegate.linkClickIntercepted(event.target, event.detail.url, event.detail.originalEvent);
1877
+ }
1878
+ }
1879
+ delete this.clickEvent;
1880
+ };
1881
+ willVisit = (_event) => {
1882
+ delete this.clickEvent;
1883
+ };
1643
1884
  respondsToEventTarget(target) {
1644
1885
  const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
1645
1886
  return element && element.closest("turbo-frame, html") == this.element;
1646
1887
  }
1647
1888
  };
1648
1889
  var LinkClickObserver = class {
1890
+ started = false;
1649
1891
  constructor(delegate, eventTarget) {
1650
- this.started = false;
1651
- this.clickCaptured = () => {
1652
- this.eventTarget.removeEventListener("click", this.clickBubbled, false);
1653
- this.eventTarget.addEventListener("click", this.clickBubbled, false);
1654
- };
1655
- this.clickBubbled = (event) => {
1656
- if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
1657
- const target = event.composedPath && event.composedPath()[0] || event.target;
1658
- const link = this.findLinkFromClickTarget(target);
1659
- if (link && doesNotTargetIFrame(link)) {
1660
- const location2 = this.getLocationForLink(link);
1661
- if (this.delegate.willFollowLinkToLocation(link, location2, event)) {
1662
- event.preventDefault();
1663
- this.delegate.followedLinkToLocation(link, location2);
1664
- }
1665
- }
1666
- }
1667
- };
1668
1892
  this.delegate = delegate;
1669
1893
  this.eventTarget = eventTarget;
1670
1894
  }
@@ -1680,27 +1904,27 @@
1680
1904
  this.started = false;
1681
1905
  }
1682
1906
  }
1907
+ clickCaptured = () => {
1908
+ this.eventTarget.removeEventListener("click", this.clickBubbled, false);
1909
+ this.eventTarget.addEventListener("click", this.clickBubbled, false);
1910
+ };
1911
+ clickBubbled = (event) => {
1912
+ if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
1913
+ const target = event.composedPath && event.composedPath()[0] || event.target;
1914
+ const link = findLinkFromClickTarget(target);
1915
+ if (link && doesNotTargetIFrame(link)) {
1916
+ const location2 = getLocationForLink(link);
1917
+ if (this.delegate.willFollowLinkToLocation(link, location2, event)) {
1918
+ event.preventDefault();
1919
+ this.delegate.followedLinkToLocation(link, location2);
1920
+ }
1921
+ }
1922
+ }
1923
+ };
1683
1924
  clickEventIsSignificant(event) {
1684
1925
  return !(event.target && event.target.isContentEditable || event.defaultPrevented || event.which > 1 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey);
1685
1926
  }
1686
- findLinkFromClickTarget(target) {
1687
- return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
1688
- }
1689
- getLocationForLink(link) {
1690
- return expandURL(link.getAttribute("href") || "");
1691
- }
1692
1927
  };
1693
- function doesNotTargetIFrame(anchor) {
1694
- if (anchor.hasAttribute("target")) {
1695
- for (const element of document.getElementsByName(anchor.target)) {
1696
- if (element instanceof HTMLIFrameElement)
1697
- return false;
1698
- }
1699
- return true;
1700
- } else {
1701
- return true;
1702
- }
1703
- }
1704
1928
  var FormLinkClickObserver = class {
1705
1929
  constructor(delegate, element) {
1706
1930
  this.delegate = delegate;
@@ -1712,8 +1936,16 @@
1712
1936
  stop() {
1713
1937
  this.linkInterceptor.stop();
1714
1938
  }
1939
+ // Link hover observer delegate
1940
+ canPrefetchRequestToLocation(link, location2) {
1941
+ return false;
1942
+ }
1943
+ prefetchAndCacheRequestToLocation(link, location2) {
1944
+ return;
1945
+ }
1946
+ // Link click observer delegate
1715
1947
  willFollowLinkToLocation(link, location2, originalEvent) {
1716
- return this.delegate.willSubmitFormLinkToLocation(link, location2, originalEvent) && link.hasAttribute("data-turbo-method");
1948
+ return this.delegate.willSubmitFormLinkToLocation(link, location2, originalEvent) && (link.hasAttribute("data-turbo-method") || link.hasAttribute("data-turbo-stream"));
1717
1949
  }
1718
1950
  followedLinkToLocation(link, location2) {
1719
1951
  const form = document.createElement("form");
@@ -1782,7 +2014,7 @@
1782
2014
  }
1783
2015
  replacePlaceholderWithPermanentElement(permanentElement) {
1784
2016
  const placeholder = this.getPlaceholderById(permanentElement.id);
1785
- placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
2017
+ placeholder?.replaceWith(permanentElement);
1786
2018
  }
1787
2019
  getPlaceholderById(id) {
1788
2020
  return this.placeholders.find((element) => element.content == id);
@@ -1798,8 +2030,8 @@
1798
2030
  return element;
1799
2031
  }
1800
2032
  var Renderer = class {
2033
+ #activeElement = null;
1801
2034
  constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1802
- this.activeElement = null;
1803
2035
  this.currentSnapshot = currentSnapshot;
1804
2036
  this.newSnapshot = newSnapshot;
1805
2037
  this.isPreview = isPreview;
@@ -1816,6 +2048,8 @@
1816
2048
  prepareToRender() {
1817
2049
  return;
1818
2050
  }
2051
+ render() {
2052
+ }
1819
2053
  finishRendering() {
1820
2054
  if (this.resolvingFunctions) {
1821
2055
  this.resolvingFunctions.resolve();
@@ -1827,21 +2061,22 @@
1827
2061
  }
1828
2062
  focusFirstAutofocusableElement() {
1829
2063
  const element = this.connectedSnapshot.firstAutofocusableElement;
1830
- if (elementIsFocusable(element)) {
2064
+ if (element) {
1831
2065
  element.focus();
1832
2066
  }
1833
2067
  }
2068
+ // Bardo delegate
1834
2069
  enteringBardo(currentPermanentElement) {
1835
- if (this.activeElement)
2070
+ if (this.#activeElement)
1836
2071
  return;
1837
2072
  if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
1838
- this.activeElement = this.currentSnapshot.activeElement;
2073
+ this.#activeElement = this.currentSnapshot.activeElement;
1839
2074
  }
1840
2075
  }
1841
2076
  leavingBardo(currentPermanentElement) {
1842
- if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
1843
- this.activeElement.focus();
1844
- this.activeElement = null;
2077
+ if (currentPermanentElement.contains(this.#activeElement) && this.#activeElement instanceof HTMLElement) {
2078
+ this.#activeElement.focus();
2079
+ this.#activeElement = null;
1845
2080
  }
1846
2081
  }
1847
2082
  get connectedSnapshot() {
@@ -1856,18 +2091,17 @@
1856
2091
  get permanentElementMap() {
1857
2092
  return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
1858
2093
  }
2094
+ get renderMethod() {
2095
+ return "replace";
2096
+ }
1859
2097
  };
1860
- function elementIsFocusable(element) {
1861
- return element && typeof element.focus == "function";
1862
- }
1863
2098
  var FrameRenderer = class extends Renderer {
1864
2099
  static renderElement(currentElement, newElement) {
1865
- var _a;
1866
2100
  const destinationRange = document.createRange();
1867
2101
  destinationRange.selectNodeContents(currentElement);
1868
2102
  destinationRange.deleteContents();
1869
2103
  const frameElement = newElement;
1870
- const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
2104
+ const sourceRange = frameElement.ownerDocument?.createRange();
1871
2105
  if (sourceRange) {
1872
2106
  sourceRange.selectNodeContents(frameElement);
1873
2107
  currentElement.appendChild(sourceRange.extractContents());
@@ -1881,14 +2115,14 @@
1881
2115
  return true;
1882
2116
  }
1883
2117
  async render() {
1884
- await nextAnimationFrame();
2118
+ await nextRepaint();
1885
2119
  this.preservingPermanentElements(() => {
1886
2120
  this.loadFrameElement();
1887
2121
  });
1888
2122
  this.scrollFrameIntoView();
1889
- await nextAnimationFrame();
2123
+ await nextRepaint();
1890
2124
  this.focusFirstAutofocusableElement();
1891
- await nextAnimationFrame();
2125
+ await nextRepaint();
1892
2126
  this.activateScriptElements();
1893
2127
  }
1894
2128
  loadFrameElement() {
@@ -1932,6 +2166,8 @@
1932
2166
  }
1933
2167
  }
1934
2168
  var ProgressBar = class _ProgressBar {
2169
+ static animationDuration = 300;
2170
+ /*ms*/
1935
2171
  static get defaultCSS() {
1936
2172
  return unindent`
1937
2173
  .turbo-progress-bar {
@@ -1949,13 +2185,10 @@
1949
2185
  }
1950
2186
  `;
1951
2187
  }
2188
+ hiding = false;
2189
+ value = 0;
2190
+ visible = false;
1952
2191
  constructor() {
1953
- this.hiding = false;
1954
- this.value = 0;
1955
- this.visible = false;
1956
- this.trickle = () => {
1957
- this.setValue(this.value + Math.random() / 100);
1958
- };
1959
2192
  this.stylesheetElement = this.createStylesheetElement();
1960
2193
  this.progressElement = this.createProgressElement();
1961
2194
  this.installStylesheetElement();
@@ -1983,6 +2216,7 @@
1983
2216
  this.value = value;
1984
2217
  this.refresh();
1985
2218
  }
2219
+ // Private
1986
2220
  installStylesheetElement() {
1987
2221
  document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
1988
2222
  }
@@ -2010,6 +2244,9 @@
2010
2244
  window.clearInterval(this.trickleInterval);
2011
2245
  delete this.trickleInterval;
2012
2246
  }
2247
+ trickle = () => {
2248
+ this.setValue(this.value + Math.random() / 100);
2249
+ };
2013
2250
  refresh() {
2014
2251
  requestAnimationFrame(() => {
2015
2252
  this.progressElement.style.width = `${10 + this.value * 90}%`;
@@ -2033,20 +2270,22 @@
2033
2270
  return getMetaContent("csp-nonce");
2034
2271
  }
2035
2272
  };
2036
- ProgressBar.animationDuration = 300;
2037
2273
  var HeadSnapshot = class extends Snapshot {
2038
- constructor() {
2039
- super(...arguments);
2040
- this.detailsByOuterHTML = this.children.filter((element) => !elementIsNoscript(element)).map((element) => elementWithoutNonce(element)).reduce((result, element) => {
2041
- const { outerHTML } = element;
2042
- const details = outerHTML in result ? result[outerHTML] : {
2043
- type: elementType(element),
2044
- tracked: elementIsTracked(element),
2045
- elements: []
2046
- };
2047
- return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
2048
- }, {});
2049
- }
2274
+ detailsByOuterHTML = this.children.filter((element) => !elementIsNoscript(element)).map((element) => elementWithoutNonce(element)).reduce((result, element) => {
2275
+ const { outerHTML } = element;
2276
+ const details = outerHTML in result ? result[outerHTML] : {
2277
+ type: elementType(element),
2278
+ tracked: elementIsTracked(element),
2279
+ elements: []
2280
+ };
2281
+ return {
2282
+ ...result,
2283
+ [outerHTML]: {
2284
+ ...details,
2285
+ elements: [...details.elements, element]
2286
+ }
2287
+ };
2288
+ }, {});
2050
2289
  get trackedElementSignature() {
2051
2290
  return Object.keys(this.detailsByOuterHTML).filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked).join("");
2052
2291
  }
@@ -2077,9 +2316,11 @@
2077
2316
  }
2078
2317
  findMetaElementByName(name) {
2079
2318
  return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
2080
- const { elements: [element] } = this.detailsByOuterHTML[outerHTML];
2319
+ const {
2320
+ elements: [element]
2321
+ } = this.detailsByOuterHTML[outerHTML];
2081
2322
  return elementIsMetaElementWithName(element, name) ? element : result;
2082
- }, void 0);
2323
+ }, void 0 | void 0);
2083
2324
  }
2084
2325
  };
2085
2326
  function elementType(element) {
@@ -2121,11 +2362,12 @@
2121
2362
  static fromElement(element) {
2122
2363
  return this.fromDocument(element.ownerDocument);
2123
2364
  }
2124
- static fromDocument({ head, body }) {
2125
- return new this(body, new HeadSnapshot(head));
2365
+ static fromDocument({ documentElement, body, head }) {
2366
+ return new this(documentElement, body, new HeadSnapshot(head));
2126
2367
  }
2127
- constructor(element, headSnapshot) {
2128
- super(element);
2368
+ constructor(documentElement, body, headSnapshot) {
2369
+ super(body);
2370
+ this.documentElement = documentElement;
2129
2371
  this.headSnapshot = headSnapshot;
2130
2372
  }
2131
2373
  clone() {
@@ -2142,14 +2384,16 @@
2142
2384
  for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
2143
2385
  clonedPasswordInput.value = "";
2144
2386
  }
2145
- return new _PageSnapshot(clonedElement, this.headSnapshot);
2387
+ return new _PageSnapshot(this.documentElement, clonedElement, this.headSnapshot);
2388
+ }
2389
+ get lang() {
2390
+ return this.documentElement.getAttribute("lang");
2146
2391
  }
2147
2392
  get headElement() {
2148
2393
  return this.headSnapshot.element;
2149
2394
  }
2150
2395
  get rootLocation() {
2151
- var _a;
2152
- const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
2396
+ const root = this.getSetting("root") ?? "/";
2153
2397
  return expandURL(root);
2154
2398
  }
2155
2399
  get cacheControlValue() {
@@ -2164,25 +2408,38 @@
2164
2408
  get isVisitable() {
2165
2409
  return this.getSetting("visit-control") != "reload";
2166
2410
  }
2411
+ get prefersViewTransitions() {
2412
+ return this.headSnapshot.getMetaValue("view-transition") === "same-origin";
2413
+ }
2414
+ get shouldMorphPage() {
2415
+ return this.getSetting("refresh-method") === "morph";
2416
+ }
2417
+ get shouldPreserveScrollPosition() {
2418
+ return this.getSetting("refresh-scroll") === "preserve";
2419
+ }
2420
+ // Private
2167
2421
  getSetting(name) {
2168
2422
  return this.headSnapshot.getMetaValue(`turbo-${name}`);
2169
2423
  }
2170
2424
  };
2171
- var TimingMetric;
2172
- (function(TimingMetric2) {
2173
- TimingMetric2["visitStart"] = "visitStart";
2174
- TimingMetric2["requestStart"] = "requestStart";
2175
- TimingMetric2["requestEnd"] = "requestEnd";
2176
- TimingMetric2["visitEnd"] = "visitEnd";
2177
- })(TimingMetric || (TimingMetric = {}));
2178
- var VisitState;
2179
- (function(VisitState2) {
2180
- VisitState2["initialized"] = "initialized";
2181
- VisitState2["started"] = "started";
2182
- VisitState2["canceled"] = "canceled";
2183
- VisitState2["failed"] = "failed";
2184
- VisitState2["completed"] = "completed";
2185
- })(VisitState || (VisitState = {}));
2425
+ var ViewTransitioner = class {
2426
+ #viewTransitionStarted = false;
2427
+ #lastOperation = Promise.resolve();
2428
+ renderChange(useViewTransition, render) {
2429
+ if (useViewTransition && this.viewTransitionsAvailable && !this.#viewTransitionStarted) {
2430
+ this.#viewTransitionStarted = true;
2431
+ this.#lastOperation = this.#lastOperation.then(async () => {
2432
+ await document.startViewTransition(render).finished;
2433
+ });
2434
+ } else {
2435
+ this.#lastOperation = this.#lastOperation.then(render);
2436
+ }
2437
+ return this.#lastOperation;
2438
+ }
2439
+ get viewTransitionsAvailable() {
2440
+ return document.startViewTransition;
2441
+ }
2442
+ };
2186
2443
  var defaultOptions = {
2187
2444
  action: "advance",
2188
2445
  historyChanged: false,
@@ -2193,27 +2450,62 @@
2193
2450
  shouldCacheSnapshot: true,
2194
2451
  acceptsStreamResponse: false
2195
2452
  };
2196
- var SystemStatusCode;
2197
- (function(SystemStatusCode2) {
2198
- SystemStatusCode2[SystemStatusCode2["networkFailure"] = 0] = "networkFailure";
2199
- SystemStatusCode2[SystemStatusCode2["timeoutFailure"] = -1] = "timeoutFailure";
2200
- SystemStatusCode2[SystemStatusCode2["contentTypeMismatch"] = -2] = "contentTypeMismatch";
2201
- })(SystemStatusCode || (SystemStatusCode = {}));
2453
+ var TimingMetric = {
2454
+ visitStart: "visitStart",
2455
+ requestStart: "requestStart",
2456
+ requestEnd: "requestEnd",
2457
+ visitEnd: "visitEnd"
2458
+ };
2459
+ var VisitState = {
2460
+ initialized: "initialized",
2461
+ started: "started",
2462
+ canceled: "canceled",
2463
+ failed: "failed",
2464
+ completed: "completed"
2465
+ };
2466
+ var SystemStatusCode = {
2467
+ networkFailure: 0,
2468
+ timeoutFailure: -1,
2469
+ contentTypeMismatch: -2
2470
+ };
2471
+ var Direction = {
2472
+ advance: "forward",
2473
+ restore: "back",
2474
+ replace: "none"
2475
+ };
2202
2476
  var Visit = class {
2477
+ identifier = uuid();
2478
+ // Required by turbo-ios
2479
+ timingMetrics = {};
2480
+ followedRedirect = false;
2481
+ historyChanged = false;
2482
+ scrolled = false;
2483
+ shouldCacheSnapshot = true;
2484
+ acceptsStreamResponse = false;
2485
+ snapshotCached = false;
2486
+ state = VisitState.initialized;
2487
+ viewTransitioner = new ViewTransitioner();
2203
2488
  constructor(delegate, location2, restorationIdentifier, options = {}) {
2204
- this.identifier = uuid();
2205
- this.timingMetrics = {};
2206
- this.followedRedirect = false;
2207
- this.historyChanged = false;
2208
- this.scrolled = false;
2209
- this.shouldCacheSnapshot = true;
2210
- this.acceptsStreamResponse = false;
2211
- this.snapshotCached = false;
2212
- this.state = VisitState.initialized;
2213
2489
  this.delegate = delegate;
2214
2490
  this.location = location2;
2215
2491
  this.restorationIdentifier = restorationIdentifier || uuid();
2216
- const { action, historyChanged, referrer, snapshot, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse } = Object.assign(Object.assign({}, defaultOptions), options);
2492
+ const {
2493
+ action,
2494
+ historyChanged,
2495
+ referrer,
2496
+ snapshot,
2497
+ snapshotHTML,
2498
+ response,
2499
+ visitCachedSnapshot,
2500
+ willRender,
2501
+ updateHistory,
2502
+ shouldCacheSnapshot,
2503
+ acceptsStreamResponse,
2504
+ direction
2505
+ } = {
2506
+ ...defaultOptions,
2507
+ ...options
2508
+ };
2217
2509
  this.action = action;
2218
2510
  this.historyChanged = historyChanged;
2219
2511
  this.referrer = referrer;
@@ -2221,12 +2513,14 @@
2221
2513
  this.snapshotHTML = snapshotHTML;
2222
2514
  this.response = response;
2223
2515
  this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
2516
+ this.isPageRefresh = this.view.isPageRefresh(this);
2224
2517
  this.visitCachedSnapshot = visitCachedSnapshot;
2225
2518
  this.willRender = willRender;
2226
2519
  this.updateHistory = updateHistory;
2227
2520
  this.scrolled = !willRender;
2228
2521
  this.shouldCacheSnapshot = shouldCacheSnapshot;
2229
2522
  this.acceptsStreamResponse = acceptsStreamResponse;
2523
+ this.direction = direction || Direction[action];
2230
2524
  }
2231
2525
  get adapter() {
2232
2526
  return this.delegate.adapter;
@@ -2263,10 +2557,10 @@
2263
2557
  complete() {
2264
2558
  if (this.state == VisitState.started) {
2265
2559
  this.recordTimingMetric(TimingMetric.visitEnd);
2560
+ this.adapter.visitCompleted(this);
2266
2561
  this.state = VisitState.completed;
2267
2562
  this.followRedirect();
2268
2563
  if (!this.followedRedirect) {
2269
- this.adapter.visitCompleted(this);
2270
2564
  this.delegate.visitCompleted(this);
2271
2565
  }
2272
2566
  }
@@ -2275,12 +2569,12 @@
2275
2569
  if (this.state == VisitState.started) {
2276
2570
  this.state = VisitState.failed;
2277
2571
  this.adapter.visitFailed(this);
2572
+ this.delegate.visitCompleted(this);
2278
2573
  }
2279
2574
  }
2280
2575
  changeHistory() {
2281
- var _a;
2282
2576
  if (!this.historyChanged && this.updateHistory) {
2283
- const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
2577
+ const actionForHistory = this.location.href === this.referrer?.href ? "replace" : this.action;
2284
2578
  const method = getHistoryMethodForAction(actionForHistory);
2285
2579
  this.history.update(method, this.location, this.restorationIdentifier);
2286
2580
  this.historyChanged = true;
@@ -2329,8 +2623,8 @@
2329
2623
  if (this.view.renderPromise)
2330
2624
  await this.view.renderPromise;
2331
2625
  if (isSuccessful(statusCode) && responseHTML != null) {
2332
- await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
2333
- this.performScroll();
2626
+ const snapshot = PageSnapshot.fromHTMLString(responseHTML);
2627
+ await this.renderPageSnapshot(snapshot, false);
2334
2628
  this.adapter.visitRendered(this);
2335
2629
  this.complete();
2336
2630
  } else {
@@ -2363,13 +2657,12 @@
2363
2657
  const isPreview = this.shouldIssueRequest();
2364
2658
  this.render(async () => {
2365
2659
  this.cacheSnapshot();
2366
- if (this.isSamePage) {
2660
+ if (this.isSamePage || this.isPageRefresh) {
2367
2661
  this.adapter.visitRendered(this);
2368
2662
  } else {
2369
2663
  if (this.view.renderPromise)
2370
2664
  await this.view.renderPromise;
2371
- await this.view.renderPage(snapshot, isPreview, this.willRender, this);
2372
- this.performScroll();
2665
+ await this.renderPageSnapshot(snapshot, isPreview);
2373
2666
  this.adapter.visitRendered(this);
2374
2667
  if (!isPreview) {
2375
2668
  this.complete();
@@ -2379,8 +2672,7 @@
2379
2672
  }
2380
2673
  }
2381
2674
  followRedirect() {
2382
- var _a;
2383
- if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
2675
+ if (this.redirectedToLocation && !this.followedRedirect && this.response?.redirected) {
2384
2676
  this.adapter.visitProposedToLocation(this.redirectedToLocation, {
2385
2677
  action: "replace",
2386
2678
  response: this.response,
@@ -2400,6 +2692,7 @@
2400
2692
  });
2401
2693
  }
2402
2694
  }
2695
+ // Fetch request delegate
2403
2696
  prepareRequest(request) {
2404
2697
  if (this.acceptsStreamResponse) {
2405
2698
  request.acceptResponseType(StreamMessage.contentType);
@@ -2444,8 +2737,9 @@
2444
2737
  requestFinished() {
2445
2738
  this.finishRequest();
2446
2739
  }
2740
+ // Scrolling
2447
2741
  performScroll() {
2448
- if (!this.scrolled && !this.view.forceReloaded) {
2742
+ if (!this.scrolled && !this.view.forceReloaded && !this.view.shouldPreserveScrollPosition(this)) {
2449
2743
  if (this.action == "restore") {
2450
2744
  this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
2451
2745
  } else {
@@ -2471,12 +2765,14 @@
2471
2765
  return true;
2472
2766
  }
2473
2767
  }
2768
+ // Instrumentation
2474
2769
  recordTimingMetric(metric) {
2475
2770
  this.timingMetrics[metric] = (/* @__PURE__ */ new Date()).getTime();
2476
2771
  }
2477
2772
  getTimingMetrics() {
2478
- return Object.assign({}, this.timingMetrics);
2773
+ return { ...this.timingMetrics };
2479
2774
  }
2775
+ // Private
2480
2776
  getHistoryMethodForAction(action) {
2481
2777
  switch (action) {
2482
2778
  case "replace":
@@ -2506,12 +2802,16 @@
2506
2802
  }
2507
2803
  async render(callback) {
2508
2804
  this.cancelRender();
2509
- await new Promise((resolve) => {
2510
- this.frame = requestAnimationFrame(() => resolve());
2511
- });
2805
+ this.frame = await nextRepaint();
2512
2806
  await callback();
2513
2807
  delete this.frame;
2514
2808
  }
2809
+ async renderPageSnapshot(snapshot, isPreview) {
2810
+ await this.viewTransitioner.renderChange(this.view.shouldTransitionTo(snapshot), async () => {
2811
+ await this.view.renderPage(snapshot, isPreview, this.willRender, this);
2812
+ this.performScroll();
2813
+ });
2814
+ }
2515
2815
  cancelRender() {
2516
2816
  if (this.frame) {
2517
2817
  cancelAnimationFrame(this.frame);
@@ -2523,15 +2823,16 @@
2523
2823
  return statusCode >= 200 && statusCode < 300;
2524
2824
  }
2525
2825
  var BrowserAdapter = class {
2826
+ progressBar = new ProgressBar();
2526
2827
  constructor(session2) {
2527
- this.progressBar = new ProgressBar();
2528
- this.showProgressBar = () => {
2529
- this.progressBar.show();
2530
- };
2531
2828
  this.session = session2;
2532
2829
  }
2533
2830
  visitProposedToLocation(location2, options) {
2534
- this.navigator.startVisit(location2, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
2831
+ if (locationIsVisitable(location2, this.navigator.rootLocation)) {
2832
+ this.navigator.startVisit(location2, options?.restorationIdentifier || uuid(), options);
2833
+ } else {
2834
+ window.location.href = location2.toString();
2835
+ }
2535
2836
  }
2536
2837
  visitStarted(visit2) {
2537
2838
  this.location = visit2.location;
@@ -2566,18 +2867,21 @@
2566
2867
  }
2567
2868
  }
2568
2869
  visitRequestFinished(_visit) {
2569
- this.progressBar.setValue(1);
2570
- this.hideVisitProgressBar();
2571
2870
  }
2572
2871
  visitCompleted(_visit) {
2872
+ this.progressBar.setValue(1);
2873
+ this.hideVisitProgressBar();
2573
2874
  }
2574
2875
  pageInvalidated(reason) {
2575
2876
  this.reload(reason);
2576
2877
  }
2577
2878
  visitFailed(_visit) {
2879
+ this.progressBar.setValue(1);
2880
+ this.hideVisitProgressBar();
2578
2881
  }
2579
2882
  visitRendered(_visit) {
2580
2883
  }
2884
+ // Form Submission Delegate
2581
2885
  formSubmissionStarted(_formSubmission) {
2582
2886
  this.progressBar.setValue(0);
2583
2887
  this.showFormProgressBarAfterDelay();
@@ -2586,6 +2890,7 @@
2586
2890
  this.progressBar.setValue(1);
2587
2891
  this.hideFormProgressBar();
2588
2892
  }
2893
+ // Private
2589
2894
  showVisitProgressBarAfterDelay() {
2590
2895
  this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
2591
2896
  }
@@ -2608,26 +2913,21 @@
2608
2913
  delete this.formProgressBarTimeout;
2609
2914
  }
2610
2915
  }
2916
+ showProgressBar = () => {
2917
+ this.progressBar.show();
2918
+ };
2611
2919
  reload(reason) {
2612
- var _a;
2613
2920
  dispatch("turbo:reload", { detail: reason });
2614
- window.location.href = ((_a = this.location) === null || _a === void 0 ? void 0 : _a.toString()) || window.location.href;
2921
+ window.location.href = this.location?.toString() || window.location.href;
2615
2922
  }
2616
2923
  get navigator() {
2617
2924
  return this.session.navigator;
2618
2925
  }
2619
2926
  };
2620
2927
  var CacheObserver = class {
2621
- constructor() {
2622
- this.selector = "[data-turbo-temporary]";
2623
- this.deprecatedSelector = "[data-turbo-cache=false]";
2624
- this.started = false;
2625
- this.removeTemporaryElements = (_event) => {
2626
- for (const element of this.temporaryElements) {
2627
- element.remove();
2628
- }
2629
- };
2630
- }
2928
+ selector = "[data-turbo-temporary]";
2929
+ deprecatedSelector = "[data-turbo-cache=false]";
2930
+ started = false;
2631
2931
  start() {
2632
2932
  if (!this.started) {
2633
2933
  this.started = true;
@@ -2640,13 +2940,20 @@
2640
2940
  removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
2641
2941
  }
2642
2942
  }
2943
+ removeTemporaryElements = (_event) => {
2944
+ for (const element of this.temporaryElements) {
2945
+ element.remove();
2946
+ }
2947
+ };
2643
2948
  get temporaryElements() {
2644
2949
  return [...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation];
2645
2950
  }
2646
2951
  get temporaryElementsWithDeprecation() {
2647
2952
  const elements = document.querySelectorAll(this.deprecatedSelector);
2648
2953
  if (elements.length) {
2649
- console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`);
2954
+ console.warn(
2955
+ `The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`
2956
+ );
2650
2957
  }
2651
2958
  return [...elements];
2652
2959
  }
@@ -2666,42 +2973,43 @@
2666
2973
  this.linkInterceptor.stop();
2667
2974
  this.formSubmitObserver.stop();
2668
2975
  }
2976
+ // Link interceptor delegate
2669
2977
  shouldInterceptLinkClick(element, _location, _event) {
2670
- return this.shouldRedirect(element);
2978
+ return this.#shouldRedirect(element);
2671
2979
  }
2672
2980
  linkClickIntercepted(element, url, event) {
2673
- const frame = this.findFrameElement(element);
2981
+ const frame = this.#findFrameElement(element);
2674
2982
  if (frame) {
2675
2983
  frame.delegate.linkClickIntercepted(element, url, event);
2676
2984
  }
2677
2985
  }
2986
+ // Form submit observer delegate
2678
2987
  willSubmitForm(element, submitter) {
2679
- return element.closest("turbo-frame") == null && this.shouldSubmit(element, submitter) && this.shouldRedirect(element, submitter);
2988
+ return element.closest("turbo-frame") == null && this.#shouldSubmit(element, submitter) && this.#shouldRedirect(element, submitter);
2680
2989
  }
2681
2990
  formSubmitted(element, submitter) {
2682
- const frame = this.findFrameElement(element, submitter);
2991
+ const frame = this.#findFrameElement(element, submitter);
2683
2992
  if (frame) {
2684
2993
  frame.delegate.formSubmitted(element, submitter);
2685
2994
  }
2686
2995
  }
2687
- shouldSubmit(form, submitter) {
2688
- var _a;
2689
- const action = getAction(form, submitter);
2996
+ #shouldSubmit(form, submitter) {
2997
+ const action = getAction$1(form, submitter);
2690
2998
  const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
2691
- const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
2692
- return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
2999
+ const rootLocation = expandURL(meta?.content ?? "/");
3000
+ return this.#shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
2693
3001
  }
2694
- shouldRedirect(element, submitter) {
3002
+ #shouldRedirect(element, submitter) {
2695
3003
  const isNavigatable = element instanceof HTMLFormElement ? this.session.submissionIsNavigatable(element, submitter) : this.session.elementIsNavigatable(element);
2696
3004
  if (isNavigatable) {
2697
- const frame = this.findFrameElement(element, submitter);
3005
+ const frame = this.#findFrameElement(element, submitter);
2698
3006
  return frame ? frame != element.closest("turbo-frame") : false;
2699
3007
  } else {
2700
3008
  return false;
2701
3009
  }
2702
3010
  }
2703
- findFrameElement(element, submitter) {
2704
- const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
3011
+ #findFrameElement(element, submitter) {
3012
+ const id = submitter?.getAttribute("data-turbo-frame") || element.getAttribute("data-turbo-frame");
2705
3013
  if (id && id != "_top") {
2706
3014
  const frame = this.element.querySelector(`#${id}:not([disabled])`);
2707
3015
  if (frame instanceof FrameElement) {
@@ -2711,32 +3019,20 @@
2711
3019
  }
2712
3020
  };
2713
3021
  var History = class {
3022
+ location;
3023
+ restorationIdentifier = uuid();
3024
+ restorationData = {};
3025
+ started = false;
3026
+ pageLoaded = false;
3027
+ currentIndex = 0;
2714
3028
  constructor(delegate) {
2715
- this.restorationIdentifier = uuid();
2716
- this.restorationData = {};
2717
- this.started = false;
2718
- this.pageLoaded = false;
2719
- this.onPopState = (event) => {
2720
- if (this.shouldHandlePopState()) {
2721
- const { turbo } = event.state || {};
2722
- if (turbo) {
2723
- this.location = new URL(window.location.href);
2724
- const { restorationIdentifier } = turbo;
2725
- this.restorationIdentifier = restorationIdentifier;
2726
- this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location, restorationIdentifier);
2727
- }
2728
- }
2729
- };
2730
- this.onPageLoad = async (_event) => {
2731
- await nextMicrotask();
2732
- this.pageLoaded = true;
2733
- };
2734
3029
  this.delegate = delegate;
2735
3030
  }
2736
3031
  start() {
2737
3032
  if (!this.started) {
2738
3033
  addEventListener("popstate", this.onPopState, false);
2739
3034
  addEventListener("load", this.onPageLoad, false);
3035
+ this.currentIndex = history.state?.turbo?.restorationIndex || 0;
2740
3036
  this.started = true;
2741
3037
  this.replace(new URL(window.location.href));
2742
3038
  }
@@ -2755,23 +3051,29 @@
2755
3051
  this.update(history.replaceState, location2, restorationIdentifier);
2756
3052
  }
2757
3053
  update(method, location2, restorationIdentifier = uuid()) {
2758
- const state = { turbo: { restorationIdentifier } };
3054
+ if (method === history.pushState)
3055
+ ++this.currentIndex;
3056
+ const state = { turbo: { restorationIdentifier, restorationIndex: this.currentIndex } };
2759
3057
  method.call(history, state, "", location2.href);
2760
3058
  this.location = location2;
2761
3059
  this.restorationIdentifier = restorationIdentifier;
2762
3060
  }
3061
+ // Restoration data
2763
3062
  getRestorationDataForIdentifier(restorationIdentifier) {
2764
3063
  return this.restorationData[restorationIdentifier] || {};
2765
3064
  }
2766
3065
  updateRestorationData(additionalData) {
2767
3066
  const { restorationIdentifier } = this;
2768
3067
  const restorationData = this.restorationData[restorationIdentifier];
2769
- this.restorationData[restorationIdentifier] = Object.assign(Object.assign({}, restorationData), additionalData);
3068
+ this.restorationData[restorationIdentifier] = {
3069
+ ...restorationData,
3070
+ ...additionalData
3071
+ };
2770
3072
  }
3073
+ // Scroll restoration
2771
3074
  assumeControlOfScrollRestoration() {
2772
- var _a;
2773
3075
  if (!this.previousScrollRestoration) {
2774
- this.previousScrollRestoration = (_a = history.scrollRestoration) !== null && _a !== void 0 ? _a : "auto";
3076
+ this.previousScrollRestoration = history.scrollRestoration ?? "auto";
2775
3077
  history.scrollRestoration = "manual";
2776
3078
  }
2777
3079
  }
@@ -2781,6 +3083,25 @@
2781
3083
  delete this.previousScrollRestoration;
2782
3084
  }
2783
3085
  }
3086
+ // Event handlers
3087
+ onPopState = (event) => {
3088
+ if (this.shouldHandlePopState()) {
3089
+ const { turbo } = event.state || {};
3090
+ if (turbo) {
3091
+ this.location = new URL(window.location.href);
3092
+ const { restorationIdentifier, restorationIndex } = turbo;
3093
+ this.restorationIdentifier = restorationIdentifier;
3094
+ const direction = restorationIndex > this.currentIndex ? "forward" : "back";
3095
+ this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location, restorationIdentifier, direction);
3096
+ this.currentIndex = restorationIndex;
3097
+ }
3098
+ }
3099
+ };
3100
+ onPageLoad = async (_event) => {
3101
+ await nextMicrotask();
3102
+ this.pageLoaded = true;
3103
+ };
3104
+ // Private
2784
3105
  shouldHandlePopState() {
2785
3106
  return this.pageIsLoaded();
2786
3107
  }
@@ -2788,22 +3109,178 @@
2788
3109
  return this.pageLoaded || document.readyState == "complete";
2789
3110
  }
2790
3111
  };
3112
+ var LinkPrefetchObserver = class {
3113
+ started = false;
3114
+ #prefetchedLink = null;
3115
+ constructor(delegate, eventTarget) {
3116
+ this.delegate = delegate;
3117
+ this.eventTarget = eventTarget;
3118
+ }
3119
+ start() {
3120
+ if (this.started)
3121
+ return;
3122
+ if (this.eventTarget.readyState === "loading") {
3123
+ this.eventTarget.addEventListener("DOMContentLoaded", this.#enable, { once: true });
3124
+ } else {
3125
+ this.#enable();
3126
+ }
3127
+ }
3128
+ stop() {
3129
+ if (!this.started)
3130
+ return;
3131
+ this.eventTarget.removeEventListener("mouseenter", this.#tryToPrefetchRequest, {
3132
+ capture: true,
3133
+ passive: true
3134
+ });
3135
+ this.eventTarget.removeEventListener("mouseleave", this.#cancelRequestIfObsolete, {
3136
+ capture: true,
3137
+ passive: true
3138
+ });
3139
+ this.eventTarget.removeEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true);
3140
+ this.started = false;
3141
+ }
3142
+ #enable = () => {
3143
+ this.eventTarget.addEventListener("mouseenter", this.#tryToPrefetchRequest, {
3144
+ capture: true,
3145
+ passive: true
3146
+ });
3147
+ this.eventTarget.addEventListener("mouseleave", this.#cancelRequestIfObsolete, {
3148
+ capture: true,
3149
+ passive: true
3150
+ });
3151
+ this.eventTarget.addEventListener("turbo:before-fetch-request", this.#tryToUsePrefetchedRequest, true);
3152
+ this.started = true;
3153
+ };
3154
+ #tryToPrefetchRequest = (event) => {
3155
+ if (getMetaContent("turbo-prefetch") === "false")
3156
+ return;
3157
+ const target = event.target;
3158
+ const isLink = target.matches && target.matches("a[href]:not([target^=_]):not([download])");
3159
+ if (isLink && this.#isPrefetchable(target)) {
3160
+ const link = target;
3161
+ const location2 = getLocationForLink(link);
3162
+ if (this.delegate.canPrefetchRequestToLocation(link, location2)) {
3163
+ this.#prefetchedLink = link;
3164
+ const fetchRequest = new FetchRequest(
3165
+ this,
3166
+ FetchMethod.get,
3167
+ location2,
3168
+ new URLSearchParams(),
3169
+ target
3170
+ );
3171
+ prefetchCache.setLater(location2.toString(), fetchRequest, this.#cacheTtl);
3172
+ }
3173
+ }
3174
+ };
3175
+ #cancelRequestIfObsolete = (event) => {
3176
+ if (event.target === this.#prefetchedLink)
3177
+ this.#cancelPrefetchRequest();
3178
+ };
3179
+ #cancelPrefetchRequest = () => {
3180
+ prefetchCache.clear();
3181
+ this.#prefetchedLink = null;
3182
+ };
3183
+ #tryToUsePrefetchedRequest = (event) => {
3184
+ if (event.target.tagName !== "FORM" && event.detail.fetchOptions.method === "get") {
3185
+ const cached = prefetchCache.get(event.detail.url.toString());
3186
+ if (cached) {
3187
+ event.detail.fetchRequest = cached;
3188
+ }
3189
+ prefetchCache.clear();
3190
+ }
3191
+ };
3192
+ prepareRequest(request) {
3193
+ const link = request.target;
3194
+ request.headers["X-Sec-Purpose"] = "prefetch";
3195
+ const turboFrame = link.closest("turbo-frame");
3196
+ const turboFrameTarget = link.getAttribute("data-turbo-frame") || turboFrame?.getAttribute("target") || turboFrame?.id;
3197
+ if (turboFrameTarget && turboFrameTarget !== "_top") {
3198
+ request.headers["Turbo-Frame"] = turboFrameTarget;
3199
+ }
3200
+ }
3201
+ // Fetch request interface
3202
+ requestSucceededWithResponse() {
3203
+ }
3204
+ requestStarted(fetchRequest) {
3205
+ }
3206
+ requestErrored(fetchRequest) {
3207
+ }
3208
+ requestFinished(fetchRequest) {
3209
+ }
3210
+ requestPreventedHandlingResponse(fetchRequest, fetchResponse) {
3211
+ }
3212
+ requestFailedWithResponse(fetchRequest, fetchResponse) {
3213
+ }
3214
+ get #cacheTtl() {
3215
+ return Number(getMetaContent("turbo-prefetch-cache-time")) || cacheTtl;
3216
+ }
3217
+ #isPrefetchable(link) {
3218
+ const href = link.getAttribute("href");
3219
+ if (!href)
3220
+ return false;
3221
+ if (unfetchableLink(link))
3222
+ return false;
3223
+ if (linkToTheSamePage(link))
3224
+ return false;
3225
+ if (linkOptsOut(link))
3226
+ return false;
3227
+ if (nonSafeLink(link))
3228
+ return false;
3229
+ if (eventPrevented(link))
3230
+ return false;
3231
+ return true;
3232
+ }
3233
+ };
3234
+ var unfetchableLink = (link) => {
3235
+ return link.origin !== document.location.origin || !["http:", "https:"].includes(link.protocol) || link.hasAttribute("target");
3236
+ };
3237
+ var linkToTheSamePage = (link) => {
3238
+ return link.pathname + link.search === document.location.pathname + document.location.search || link.href.startsWith("#");
3239
+ };
3240
+ var linkOptsOut = (link) => {
3241
+ if (link.getAttribute("data-turbo-prefetch") === "false")
3242
+ return true;
3243
+ if (link.getAttribute("data-turbo") === "false")
3244
+ return true;
3245
+ const turboPrefetchParent = findClosestRecursively(link, "[data-turbo-prefetch]");
3246
+ if (turboPrefetchParent && turboPrefetchParent.getAttribute("data-turbo-prefetch") === "false")
3247
+ return true;
3248
+ return false;
3249
+ };
3250
+ var nonSafeLink = (link) => {
3251
+ const turboMethod = link.getAttribute("data-turbo-method");
3252
+ if (turboMethod && turboMethod.toLowerCase() !== "get")
3253
+ return true;
3254
+ if (isUJS(link))
3255
+ return true;
3256
+ if (link.hasAttribute("data-turbo-confirm"))
3257
+ return true;
3258
+ if (link.hasAttribute("data-turbo-stream"))
3259
+ return true;
3260
+ return false;
3261
+ };
3262
+ var isUJS = (link) => {
3263
+ return link.hasAttribute("data-remote") || link.hasAttribute("data-behavior") || link.hasAttribute("data-confirm") || link.hasAttribute("data-method");
3264
+ };
3265
+ var eventPrevented = (link) => {
3266
+ const event = dispatch("turbo:before-prefetch", { target: link, cancelable: true });
3267
+ return event.defaultPrevented;
3268
+ };
2791
3269
  var Navigator = class {
2792
3270
  constructor(delegate) {
2793
3271
  this.delegate = delegate;
2794
3272
  }
2795
3273
  proposeVisit(location2, options = {}) {
2796
3274
  if (this.delegate.allowsVisitingLocationWithAction(location2, options.action)) {
2797
- if (locationIsVisitable(location2, this.view.snapshot.rootLocation)) {
2798
- this.delegate.visitProposedToLocation(location2, options);
2799
- } else {
2800
- window.location.href = location2.toString();
2801
- }
3275
+ this.delegate.visitProposedToLocation(location2, options);
2802
3276
  }
2803
3277
  }
2804
3278
  startVisit(locatable, restorationIdentifier, options = {}) {
2805
3279
  this.stop();
2806
- this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
3280
+ this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, {
3281
+ referrer: this.location,
3282
+ ...options
3283
+ });
2807
3284
  this.currentVisit.start();
2808
3285
  }
2809
3286
  submitForm(form, submitter) {
@@ -2827,9 +3304,13 @@
2827
3304
  get view() {
2828
3305
  return this.delegate.view;
2829
3306
  }
3307
+ get rootLocation() {
3308
+ return this.view.snapshot.rootLocation;
3309
+ }
2830
3310
  get history() {
2831
3311
  return this.delegate.history;
2832
3312
  }
3313
+ // Form submission delegate
2833
3314
  formSubmissionStarted(formSubmission) {
2834
3315
  if (typeof this.adapter.formSubmissionStarted === "function") {
2835
3316
  this.adapter.formSubmissionStarted(formSubmission);
@@ -2844,7 +3325,7 @@
2844
3325
  this.view.clearSnapshotCache();
2845
3326
  }
2846
3327
  const { statusCode, redirected } = fetchResponse;
2847
- const action = this.getActionForFormSubmission(formSubmission);
3328
+ const action = this.#getActionForFormSubmission(formSubmission, fetchResponse);
2848
3329
  const visitOptions = {
2849
3330
  action,
2850
3331
  shouldCacheSnapshot,
@@ -2863,7 +3344,9 @@
2863
3344
  } else {
2864
3345
  await this.view.renderPage(snapshot, false, true, this.currentVisit);
2865
3346
  }
2866
- this.view.scrollToTop();
3347
+ if (!snapshot.shouldPreserveScrollPosition) {
3348
+ this.view.scrollToTop();
3349
+ }
2867
3350
  this.view.clearSnapshotCache();
2868
3351
  }
2869
3352
  }
@@ -2875,6 +3358,7 @@
2875
3358
  this.adapter.formSubmissionFinished(formSubmission);
2876
3359
  }
2877
3360
  }
3361
+ // Visit delegate
2878
3362
  visitStarted(visit2) {
2879
3363
  this.delegate.visitStarted(visit2);
2880
3364
  }
@@ -2890,38 +3374,32 @@
2890
3374
  visitScrolledToSamePageLocation(oldURL, newURL) {
2891
3375
  this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
2892
3376
  }
3377
+ // Visits
2893
3378
  get location() {
2894
3379
  return this.history.location;
2895
3380
  }
2896
3381
  get restorationIdentifier() {
2897
3382
  return this.history.restorationIdentifier;
2898
3383
  }
2899
- getActionForFormSubmission({ submitter, formElement }) {
2900
- return getVisitAction(submitter, formElement) || "advance";
3384
+ #getActionForFormSubmission(formSubmission, fetchResponse) {
3385
+ const { submitter, formElement } = formSubmission;
3386
+ return getVisitAction(submitter, formElement) || this.#getDefaultAction(fetchResponse);
3387
+ }
3388
+ #getDefaultAction(fetchResponse) {
3389
+ const sameLocationRedirect = fetchResponse.redirected && fetchResponse.location.href === this.location?.href;
3390
+ return sameLocationRedirect ? "replace" : "advance";
2901
3391
  }
2902
3392
  };
2903
- var PageStage;
2904
- (function(PageStage2) {
2905
- PageStage2[PageStage2["initial"] = 0] = "initial";
2906
- PageStage2[PageStage2["loading"] = 1] = "loading";
2907
- PageStage2[PageStage2["interactive"] = 2] = "interactive";
2908
- PageStage2[PageStage2["complete"] = 3] = "complete";
2909
- })(PageStage || (PageStage = {}));
3393
+ var PageStage = {
3394
+ initial: 0,
3395
+ loading: 1,
3396
+ interactive: 2,
3397
+ complete: 3
3398
+ };
2910
3399
  var PageObserver = class {
3400
+ stage = PageStage.initial;
3401
+ started = false;
2911
3402
  constructor(delegate) {
2912
- this.stage = PageStage.initial;
2913
- this.started = false;
2914
- this.interpretReadyState = () => {
2915
- const { readyState } = this;
2916
- if (readyState == "interactive") {
2917
- this.pageIsInteractive();
2918
- } else if (readyState == "complete") {
2919
- this.pageIsComplete();
2920
- }
2921
- };
2922
- this.pageWillUnload = () => {
2923
- this.delegate.pageWillUnload();
2924
- };
2925
3403
  this.delegate = delegate;
2926
3404
  }
2927
3405
  start() {
@@ -2941,6 +3419,14 @@
2941
3419
  this.started = false;
2942
3420
  }
2943
3421
  }
3422
+ interpretReadyState = () => {
3423
+ const { readyState } = this;
3424
+ if (readyState == "interactive") {
3425
+ this.pageIsInteractive();
3426
+ } else if (readyState == "complete") {
3427
+ this.pageIsComplete();
3428
+ }
3429
+ };
2944
3430
  pageIsInteractive() {
2945
3431
  if (this.stage == PageStage.loading) {
2946
3432
  this.stage = PageStage.interactive;
@@ -2954,16 +3440,16 @@
2954
3440
  this.delegate.pageLoaded();
2955
3441
  }
2956
3442
  }
3443
+ pageWillUnload = () => {
3444
+ this.delegate.pageWillUnload();
3445
+ };
2957
3446
  get readyState() {
2958
3447
  return document.readyState;
2959
3448
  }
2960
3449
  };
2961
3450
  var ScrollObserver = class {
3451
+ started = false;
2962
3452
  constructor(delegate) {
2963
- this.started = false;
2964
- this.onScroll = () => {
2965
- this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
2966
- };
2967
3453
  this.delegate = delegate;
2968
3454
  }
2969
3455
  start() {
@@ -2979,14 +3465,25 @@
2979
3465
  this.started = false;
2980
3466
  }
2981
3467
  }
3468
+ onScroll = () => {
3469
+ this.updatePosition({ x: window.pageXOffset, y: window.pageYOffset });
3470
+ };
3471
+ // Private
2982
3472
  updatePosition(position) {
2983
3473
  this.delegate.scrollPositionChanged(position);
2984
3474
  }
2985
3475
  };
2986
3476
  var StreamMessageRenderer = class {
2987
3477
  render({ fragment }) {
2988
- Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
3478
+ Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => {
3479
+ withAutofocusFromFragment(fragment, () => {
3480
+ withPreservedFocus(() => {
3481
+ document.documentElement.appendChild(fragment);
3482
+ });
3483
+ });
3484
+ });
2989
3485
  }
3486
+ // Bardo delegate
2990
3487
  enteringBardo(currentPermanentElement, newPermanentElement) {
2991
3488
  newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
2992
3489
  }
@@ -3007,33 +3504,65 @@
3007
3504
  }
3008
3505
  return permanentElementMap;
3009
3506
  }
3507
+ async function withAutofocusFromFragment(fragment, callback) {
3508
+ const generatedID = `turbo-stream-autofocus-${uuid()}`;
3509
+ const turboStreams = fragment.querySelectorAll("turbo-stream");
3510
+ const elementWithAutofocus = firstAutofocusableElementInStreams(turboStreams);
3511
+ let willAutofocusId = null;
3512
+ if (elementWithAutofocus) {
3513
+ if (elementWithAutofocus.id) {
3514
+ willAutofocusId = elementWithAutofocus.id;
3515
+ } else {
3516
+ willAutofocusId = generatedID;
3517
+ }
3518
+ elementWithAutofocus.id = willAutofocusId;
3519
+ }
3520
+ callback();
3521
+ await nextRepaint();
3522
+ const hasNoActiveElement = document.activeElement == null || document.activeElement == document.body;
3523
+ if (hasNoActiveElement && willAutofocusId) {
3524
+ const elementToAutofocus = document.getElementById(willAutofocusId);
3525
+ if (elementIsFocusable(elementToAutofocus)) {
3526
+ elementToAutofocus.focus();
3527
+ }
3528
+ if (elementToAutofocus && elementToAutofocus.id == generatedID) {
3529
+ elementToAutofocus.removeAttribute("id");
3530
+ }
3531
+ }
3532
+ }
3533
+ async function withPreservedFocus(callback) {
3534
+ const [activeElementBeforeRender, activeElementAfterRender] = await around(callback, () => document.activeElement);
3535
+ const restoreFocusTo = activeElementBeforeRender && activeElementBeforeRender.id;
3536
+ if (restoreFocusTo) {
3537
+ const elementToFocus = document.getElementById(restoreFocusTo);
3538
+ if (elementIsFocusable(elementToFocus) && elementToFocus != activeElementAfterRender) {
3539
+ elementToFocus.focus();
3540
+ }
3541
+ }
3542
+ }
3543
+ function firstAutofocusableElementInStreams(nodeListOfStreamElements) {
3544
+ for (const streamElement of nodeListOfStreamElements) {
3545
+ const elementWithAutofocus = queryAutofocusableElement(streamElement.templateElement.content);
3546
+ if (elementWithAutofocus)
3547
+ return elementWithAutofocus;
3548
+ }
3549
+ return null;
3550
+ }
3010
3551
  var StreamObserver = class {
3552
+ sources = /* @__PURE__ */ new Set();
3553
+ #started = false;
3011
3554
  constructor(delegate) {
3012
- this.sources = /* @__PURE__ */ new Set();
3013
- this.started = false;
3014
- this.inspectFetchResponse = (event) => {
3015
- const response = fetchResponseFromEvent(event);
3016
- if (response && fetchResponseIsStream(response)) {
3017
- event.preventDefault();
3018
- this.receiveMessageResponse(response);
3019
- }
3020
- };
3021
- this.receiveMessageEvent = (event) => {
3022
- if (this.started && typeof event.data == "string") {
3023
- this.receiveMessageHTML(event.data);
3024
- }
3025
- };
3026
3555
  this.delegate = delegate;
3027
3556
  }
3028
3557
  start() {
3029
- if (!this.started) {
3030
- this.started = true;
3558
+ if (!this.#started) {
3559
+ this.#started = true;
3031
3560
  addEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
3032
3561
  }
3033
3562
  }
3034
3563
  stop() {
3035
- if (this.started) {
3036
- this.started = false;
3564
+ if (this.#started) {
3565
+ this.#started = false;
3037
3566
  removeEventListener("turbo:before-fetch-response", this.inspectFetchResponse, false);
3038
3567
  }
3039
3568
  }
@@ -3043,67 +3572,624 @@
3043
3572
  source.addEventListener("message", this.receiveMessageEvent, false);
3044
3573
  }
3045
3574
  }
3046
- disconnectStreamSource(source) {
3047
- if (this.streamSourceIsConnected(source)) {
3048
- this.sources.delete(source);
3049
- source.removeEventListener("message", this.receiveMessageEvent, false);
3575
+ disconnectStreamSource(source) {
3576
+ if (this.streamSourceIsConnected(source)) {
3577
+ this.sources.delete(source);
3578
+ source.removeEventListener("message", this.receiveMessageEvent, false);
3579
+ }
3580
+ }
3581
+ streamSourceIsConnected(source) {
3582
+ return this.sources.has(source);
3583
+ }
3584
+ inspectFetchResponse = (event) => {
3585
+ const response = fetchResponseFromEvent(event);
3586
+ if (response && fetchResponseIsStream(response)) {
3587
+ event.preventDefault();
3588
+ this.receiveMessageResponse(response);
3589
+ }
3590
+ };
3591
+ receiveMessageEvent = (event) => {
3592
+ if (this.#started && typeof event.data == "string") {
3593
+ this.receiveMessageHTML(event.data);
3594
+ }
3595
+ };
3596
+ async receiveMessageResponse(response) {
3597
+ const html = await response.responseHTML;
3598
+ if (html) {
3599
+ this.receiveMessageHTML(html);
3600
+ }
3601
+ }
3602
+ receiveMessageHTML(html) {
3603
+ this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
3604
+ }
3605
+ };
3606
+ function fetchResponseFromEvent(event) {
3607
+ const fetchResponse = event.detail?.fetchResponse;
3608
+ if (fetchResponse instanceof FetchResponse) {
3609
+ return fetchResponse;
3610
+ }
3611
+ }
3612
+ function fetchResponseIsStream(response) {
3613
+ const contentType = response.contentType ?? "";
3614
+ return contentType.startsWith(StreamMessage.contentType);
3615
+ }
3616
+ var ErrorRenderer = class extends Renderer {
3617
+ static renderElement(currentElement, newElement) {
3618
+ const { documentElement, body } = document;
3619
+ documentElement.replaceChild(newElement, body);
3620
+ }
3621
+ async render() {
3622
+ this.replaceHeadAndBody();
3623
+ this.activateScriptElements();
3624
+ }
3625
+ replaceHeadAndBody() {
3626
+ const { documentElement, head } = document;
3627
+ documentElement.replaceChild(this.newHead, head);
3628
+ this.renderElement(this.currentElement, this.newElement);
3629
+ }
3630
+ activateScriptElements() {
3631
+ for (const replaceableElement of this.scriptElements) {
3632
+ const parentNode = replaceableElement.parentNode;
3633
+ if (parentNode) {
3634
+ const element = activateScriptElement(replaceableElement);
3635
+ parentNode.replaceChild(element, replaceableElement);
3636
+ }
3637
+ }
3638
+ }
3639
+ get newHead() {
3640
+ return this.newSnapshot.headSnapshot.element;
3641
+ }
3642
+ get scriptElements() {
3643
+ return document.documentElement.querySelectorAll("script");
3644
+ }
3645
+ };
3646
+ var Idiomorph = /* @__PURE__ */ function() {
3647
+ let EMPTY_SET = /* @__PURE__ */ new Set();
3648
+ let defaults = {
3649
+ morphStyle: "outerHTML",
3650
+ callbacks: {
3651
+ beforeNodeAdded: noOp,
3652
+ afterNodeAdded: noOp,
3653
+ beforeNodeMorphed: noOp,
3654
+ afterNodeMorphed: noOp,
3655
+ beforeNodeRemoved: noOp,
3656
+ afterNodeRemoved: noOp,
3657
+ beforeAttributeUpdated: noOp
3658
+ },
3659
+ head: {
3660
+ style: "merge",
3661
+ shouldPreserve: function(elt) {
3662
+ return elt.getAttribute("im-preserve") === "true";
3663
+ },
3664
+ shouldReAppend: function(elt) {
3665
+ return elt.getAttribute("im-re-append") === "true";
3666
+ },
3667
+ shouldRemove: noOp,
3668
+ afterHeadMorphed: noOp
3669
+ }
3670
+ };
3671
+ function morph(oldNode, newContent, config = {}) {
3672
+ if (oldNode instanceof Document) {
3673
+ oldNode = oldNode.documentElement;
3674
+ }
3675
+ if (typeof newContent === "string") {
3676
+ newContent = parseContent(newContent);
3677
+ }
3678
+ let normalizedContent = normalizeContent(newContent);
3679
+ let ctx = createMorphContext(oldNode, normalizedContent, config);
3680
+ return morphNormalizedContent(oldNode, normalizedContent, ctx);
3681
+ }
3682
+ function morphNormalizedContent(oldNode, normalizedNewContent, ctx) {
3683
+ if (ctx.head.block) {
3684
+ let oldHead = oldNode.querySelector("head");
3685
+ let newHead = normalizedNewContent.querySelector("head");
3686
+ if (oldHead && newHead) {
3687
+ let promises = handleHeadElement(newHead, oldHead, ctx);
3688
+ Promise.all(promises).then(function() {
3689
+ morphNormalizedContent(oldNode, normalizedNewContent, Object.assign(ctx, {
3690
+ head: {
3691
+ block: false,
3692
+ ignore: true
3693
+ }
3694
+ }));
3695
+ });
3696
+ return;
3697
+ }
3698
+ }
3699
+ if (ctx.morphStyle === "innerHTML") {
3700
+ morphChildren(normalizedNewContent, oldNode, ctx);
3701
+ return oldNode.children;
3702
+ } else if (ctx.morphStyle === "outerHTML" || ctx.morphStyle == null) {
3703
+ let bestMatch = findBestNodeMatch(normalizedNewContent, oldNode, ctx);
3704
+ let previousSibling = bestMatch?.previousSibling;
3705
+ let nextSibling = bestMatch?.nextSibling;
3706
+ let morphedNode = morphOldNodeTo(oldNode, bestMatch, ctx);
3707
+ if (bestMatch) {
3708
+ return insertSiblings(previousSibling, morphedNode, nextSibling);
3709
+ } else {
3710
+ return [];
3711
+ }
3712
+ } else {
3713
+ throw "Do not understand how to morph style " + ctx.morphStyle;
3714
+ }
3715
+ }
3716
+ function ignoreValueOfActiveElement(possibleActiveElement, ctx) {
3717
+ return ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body;
3718
+ }
3719
+ function morphOldNodeTo(oldNode, newContent, ctx) {
3720
+ if (ctx.ignoreActive && oldNode === document.activeElement)
3721
+ ;
3722
+ else if (newContent == null) {
3723
+ if (ctx.callbacks.beforeNodeRemoved(oldNode) === false)
3724
+ return oldNode;
3725
+ oldNode.remove();
3726
+ ctx.callbacks.afterNodeRemoved(oldNode);
3727
+ return null;
3728
+ } else if (!isSoftMatch(oldNode, newContent)) {
3729
+ if (ctx.callbacks.beforeNodeRemoved(oldNode) === false)
3730
+ return oldNode;
3731
+ if (ctx.callbacks.beforeNodeAdded(newContent) === false)
3732
+ return oldNode;
3733
+ oldNode.parentElement.replaceChild(newContent, oldNode);
3734
+ ctx.callbacks.afterNodeAdded(newContent);
3735
+ ctx.callbacks.afterNodeRemoved(oldNode);
3736
+ return newContent;
3737
+ } else {
3738
+ if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false)
3739
+ return oldNode;
3740
+ if (oldNode instanceof HTMLHeadElement && ctx.head.ignore)
3741
+ ;
3742
+ else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") {
3743
+ handleHeadElement(newContent, oldNode, ctx);
3744
+ } else {
3745
+ syncNodeFrom(newContent, oldNode, ctx);
3746
+ if (!ignoreValueOfActiveElement(oldNode, ctx)) {
3747
+ morphChildren(newContent, oldNode, ctx);
3748
+ }
3749
+ }
3750
+ ctx.callbacks.afterNodeMorphed(oldNode, newContent);
3751
+ return oldNode;
3752
+ }
3753
+ }
3754
+ function morphChildren(newParent, oldParent, ctx) {
3755
+ let nextNewChild = newParent.firstChild;
3756
+ let insertionPoint = oldParent.firstChild;
3757
+ let newChild;
3758
+ while (nextNewChild) {
3759
+ newChild = nextNewChild;
3760
+ nextNewChild = newChild.nextSibling;
3761
+ if (insertionPoint == null) {
3762
+ if (ctx.callbacks.beforeNodeAdded(newChild) === false)
3763
+ return;
3764
+ oldParent.appendChild(newChild);
3765
+ ctx.callbacks.afterNodeAdded(newChild);
3766
+ removeIdsFromConsideration(ctx, newChild);
3767
+ continue;
3768
+ }
3769
+ if (isIdSetMatch(newChild, insertionPoint, ctx)) {
3770
+ morphOldNodeTo(insertionPoint, newChild, ctx);
3771
+ insertionPoint = insertionPoint.nextSibling;
3772
+ removeIdsFromConsideration(ctx, newChild);
3773
+ continue;
3774
+ }
3775
+ let idSetMatch = findIdSetMatch(newParent, oldParent, newChild, insertionPoint, ctx);
3776
+ if (idSetMatch) {
3777
+ insertionPoint = removeNodesBetween(insertionPoint, idSetMatch, ctx);
3778
+ morphOldNodeTo(idSetMatch, newChild, ctx);
3779
+ removeIdsFromConsideration(ctx, newChild);
3780
+ continue;
3781
+ }
3782
+ let softMatch = findSoftMatch(newParent, oldParent, newChild, insertionPoint, ctx);
3783
+ if (softMatch) {
3784
+ insertionPoint = removeNodesBetween(insertionPoint, softMatch, ctx);
3785
+ morphOldNodeTo(softMatch, newChild, ctx);
3786
+ removeIdsFromConsideration(ctx, newChild);
3787
+ continue;
3788
+ }
3789
+ if (ctx.callbacks.beforeNodeAdded(newChild) === false)
3790
+ return;
3791
+ oldParent.insertBefore(newChild, insertionPoint);
3792
+ ctx.callbacks.afterNodeAdded(newChild);
3793
+ removeIdsFromConsideration(ctx, newChild);
3794
+ }
3795
+ while (insertionPoint !== null) {
3796
+ let tempNode = insertionPoint;
3797
+ insertionPoint = insertionPoint.nextSibling;
3798
+ removeNode(tempNode, ctx);
3799
+ }
3800
+ }
3801
+ function ignoreAttribute(attr, to, updateType, ctx) {
3802
+ if (attr === "value" && ctx.ignoreActiveValue && to === document.activeElement) {
3803
+ return true;
3804
+ }
3805
+ return ctx.callbacks.beforeAttributeUpdated(attr, to, updateType) === false;
3806
+ }
3807
+ function syncNodeFrom(from, to, ctx) {
3808
+ let type = from.nodeType;
3809
+ if (type === 1) {
3810
+ const fromAttributes = from.attributes;
3811
+ const toAttributes = to.attributes;
3812
+ for (const fromAttribute of fromAttributes) {
3813
+ if (ignoreAttribute(fromAttribute.name, to, "update", ctx)) {
3814
+ continue;
3815
+ }
3816
+ if (to.getAttribute(fromAttribute.name) !== fromAttribute.value) {
3817
+ to.setAttribute(fromAttribute.name, fromAttribute.value);
3818
+ }
3819
+ }
3820
+ for (let i = toAttributes.length - 1; 0 <= i; i--) {
3821
+ const toAttribute = toAttributes[i];
3822
+ if (ignoreAttribute(toAttribute.name, to, "remove", ctx)) {
3823
+ continue;
3824
+ }
3825
+ if (!from.hasAttribute(toAttribute.name)) {
3826
+ to.removeAttribute(toAttribute.name);
3827
+ }
3828
+ }
3829
+ }
3830
+ if (type === 8 || type === 3) {
3831
+ if (to.nodeValue !== from.nodeValue) {
3832
+ to.nodeValue = from.nodeValue;
3833
+ }
3834
+ }
3835
+ if (!ignoreValueOfActiveElement(to, ctx)) {
3836
+ syncInputValue(from, to, ctx);
3837
+ }
3838
+ }
3839
+ function syncBooleanAttribute(from, to, attributeName, ctx) {
3840
+ if (from[attributeName] !== to[attributeName]) {
3841
+ let ignoreUpdate = ignoreAttribute(attributeName, to, "update", ctx);
3842
+ if (!ignoreUpdate) {
3843
+ to[attributeName] = from[attributeName];
3844
+ }
3845
+ if (from[attributeName]) {
3846
+ if (!ignoreUpdate) {
3847
+ to.setAttribute(attributeName, from[attributeName]);
3848
+ }
3849
+ } else {
3850
+ if (!ignoreAttribute(attributeName, to, "remove", ctx)) {
3851
+ to.removeAttribute(attributeName);
3852
+ }
3853
+ }
3854
+ }
3855
+ }
3856
+ function syncInputValue(from, to, ctx) {
3857
+ if (from instanceof HTMLInputElement && to instanceof HTMLInputElement && from.type !== "file") {
3858
+ let fromValue = from.value;
3859
+ let toValue = to.value;
3860
+ syncBooleanAttribute(from, to, "checked", ctx);
3861
+ syncBooleanAttribute(from, to, "disabled", ctx);
3862
+ if (!from.hasAttribute("value")) {
3863
+ if (!ignoreAttribute("value", to, "remove", ctx)) {
3864
+ to.value = "";
3865
+ to.removeAttribute("value");
3866
+ }
3867
+ } else if (fromValue !== toValue) {
3868
+ if (!ignoreAttribute("value", to, "update", ctx)) {
3869
+ to.setAttribute("value", fromValue);
3870
+ to.value = fromValue;
3871
+ }
3872
+ }
3873
+ } else if (from instanceof HTMLOptionElement) {
3874
+ syncBooleanAttribute(from, to, "selected", ctx);
3875
+ } else if (from instanceof HTMLTextAreaElement && to instanceof HTMLTextAreaElement) {
3876
+ let fromValue = from.value;
3877
+ let toValue = to.value;
3878
+ if (ignoreAttribute("value", to, "update", ctx)) {
3879
+ return;
3880
+ }
3881
+ if (fromValue !== toValue) {
3882
+ to.value = fromValue;
3883
+ }
3884
+ if (to.firstChild && to.firstChild.nodeValue !== fromValue) {
3885
+ to.firstChild.nodeValue = fromValue;
3886
+ }
3887
+ }
3888
+ }
3889
+ function handleHeadElement(newHeadTag, currentHead, ctx) {
3890
+ let added = [];
3891
+ let removed = [];
3892
+ let preserved = [];
3893
+ let nodesToAppend = [];
3894
+ let headMergeStyle = ctx.head.style;
3895
+ let srcToNewHeadNodes = /* @__PURE__ */ new Map();
3896
+ for (const newHeadChild of newHeadTag.children) {
3897
+ srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
3898
+ }
3899
+ for (const currentHeadElt of currentHead.children) {
3900
+ let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
3901
+ let isReAppended = ctx.head.shouldReAppend(currentHeadElt);
3902
+ let isPreserved = ctx.head.shouldPreserve(currentHeadElt);
3903
+ if (inNewContent || isPreserved) {
3904
+ if (isReAppended) {
3905
+ removed.push(currentHeadElt);
3906
+ } else {
3907
+ srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
3908
+ preserved.push(currentHeadElt);
3909
+ }
3910
+ } else {
3911
+ if (headMergeStyle === "append") {
3912
+ if (isReAppended) {
3913
+ removed.push(currentHeadElt);
3914
+ nodesToAppend.push(currentHeadElt);
3915
+ }
3916
+ } else {
3917
+ if (ctx.head.shouldRemove(currentHeadElt) !== false) {
3918
+ removed.push(currentHeadElt);
3919
+ }
3920
+ }
3921
+ }
3922
+ }
3923
+ nodesToAppend.push(...srcToNewHeadNodes.values());
3924
+ let promises = [];
3925
+ for (const newNode of nodesToAppend) {
3926
+ let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild;
3927
+ if (ctx.callbacks.beforeNodeAdded(newElt) !== false) {
3928
+ if (newElt.href || newElt.src) {
3929
+ let resolve = null;
3930
+ let promise = new Promise(function(_resolve) {
3931
+ resolve = _resolve;
3932
+ });
3933
+ newElt.addEventListener("load", function() {
3934
+ resolve();
3935
+ });
3936
+ promises.push(promise);
3937
+ }
3938
+ currentHead.appendChild(newElt);
3939
+ ctx.callbacks.afterNodeAdded(newElt);
3940
+ added.push(newElt);
3941
+ }
3942
+ }
3943
+ for (const removedElement of removed) {
3944
+ if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) {
3945
+ currentHead.removeChild(removedElement);
3946
+ ctx.callbacks.afterNodeRemoved(removedElement);
3947
+ }
3948
+ }
3949
+ ctx.head.afterHeadMorphed(currentHead, { added, kept: preserved, removed });
3950
+ return promises;
3951
+ }
3952
+ function noOp() {
3953
+ }
3954
+ function mergeDefaults(config) {
3955
+ let finalConfig = {};
3956
+ Object.assign(finalConfig, defaults);
3957
+ Object.assign(finalConfig, config);
3958
+ finalConfig.callbacks = {};
3959
+ Object.assign(finalConfig.callbacks, defaults.callbacks);
3960
+ Object.assign(finalConfig.callbacks, config.callbacks);
3961
+ finalConfig.head = {};
3962
+ Object.assign(finalConfig.head, defaults.head);
3963
+ Object.assign(finalConfig.head, config.head);
3964
+ return finalConfig;
3965
+ }
3966
+ function createMorphContext(oldNode, newContent, config) {
3967
+ config = mergeDefaults(config);
3968
+ return {
3969
+ target: oldNode,
3970
+ newContent,
3971
+ config,
3972
+ morphStyle: config.morphStyle,
3973
+ ignoreActive: config.ignoreActive,
3974
+ ignoreActiveValue: config.ignoreActiveValue,
3975
+ idMap: createIdMap(oldNode, newContent),
3976
+ deadIds: /* @__PURE__ */ new Set(),
3977
+ callbacks: config.callbacks,
3978
+ head: config.head
3979
+ };
3980
+ }
3981
+ function isIdSetMatch(node1, node2, ctx) {
3982
+ if (node1 == null || node2 == null) {
3983
+ return false;
3984
+ }
3985
+ if (node1.nodeType === node2.nodeType && node1.tagName === node2.tagName) {
3986
+ if (node1.id !== "" && node1.id === node2.id) {
3987
+ return true;
3988
+ } else {
3989
+ return getIdIntersectionCount(ctx, node1, node2) > 0;
3990
+ }
3991
+ }
3992
+ return false;
3993
+ }
3994
+ function isSoftMatch(node1, node2) {
3995
+ if (node1 == null || node2 == null) {
3996
+ return false;
3997
+ }
3998
+ return node1.nodeType === node2.nodeType && node1.tagName === node2.tagName;
3999
+ }
4000
+ function removeNodesBetween(startInclusive, endExclusive, ctx) {
4001
+ while (startInclusive !== endExclusive) {
4002
+ let tempNode = startInclusive;
4003
+ startInclusive = startInclusive.nextSibling;
4004
+ removeNode(tempNode, ctx);
4005
+ }
4006
+ removeIdsFromConsideration(ctx, endExclusive);
4007
+ return endExclusive.nextSibling;
4008
+ }
4009
+ function findIdSetMatch(newContent, oldParent, newChild, insertionPoint, ctx) {
4010
+ let newChildPotentialIdCount = getIdIntersectionCount(ctx, newChild, oldParent);
4011
+ let potentialMatch = null;
4012
+ if (newChildPotentialIdCount > 0) {
4013
+ let potentialMatch2 = insertionPoint;
4014
+ let otherMatchCount = 0;
4015
+ while (potentialMatch2 != null) {
4016
+ if (isIdSetMatch(newChild, potentialMatch2, ctx)) {
4017
+ return potentialMatch2;
4018
+ }
4019
+ otherMatchCount += getIdIntersectionCount(ctx, potentialMatch2, newContent);
4020
+ if (otherMatchCount > newChildPotentialIdCount) {
4021
+ return null;
4022
+ }
4023
+ potentialMatch2 = potentialMatch2.nextSibling;
4024
+ }
4025
+ }
4026
+ return potentialMatch;
4027
+ }
4028
+ function findSoftMatch(newContent, oldParent, newChild, insertionPoint, ctx) {
4029
+ let potentialSoftMatch = insertionPoint;
4030
+ let nextSibling = newChild.nextSibling;
4031
+ let siblingSoftMatchCount = 0;
4032
+ while (potentialSoftMatch != null) {
4033
+ if (getIdIntersectionCount(ctx, potentialSoftMatch, newContent) > 0) {
4034
+ return null;
4035
+ }
4036
+ if (isSoftMatch(newChild, potentialSoftMatch)) {
4037
+ return potentialSoftMatch;
4038
+ }
4039
+ if (isSoftMatch(nextSibling, potentialSoftMatch)) {
4040
+ siblingSoftMatchCount++;
4041
+ nextSibling = nextSibling.nextSibling;
4042
+ if (siblingSoftMatchCount >= 2) {
4043
+ return null;
4044
+ }
4045
+ }
4046
+ potentialSoftMatch = potentialSoftMatch.nextSibling;
4047
+ }
4048
+ return potentialSoftMatch;
4049
+ }
4050
+ function parseContent(newContent) {
4051
+ let parser = new DOMParser();
4052
+ let contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, "");
4053
+ if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) {
4054
+ let content = parser.parseFromString(newContent, "text/html");
4055
+ if (contentWithSvgsRemoved.match(/<\/html>/)) {
4056
+ content.generatedByIdiomorph = true;
4057
+ return content;
4058
+ } else {
4059
+ let htmlElement = content.firstChild;
4060
+ if (htmlElement) {
4061
+ htmlElement.generatedByIdiomorph = true;
4062
+ return htmlElement;
4063
+ } else {
4064
+ return null;
4065
+ }
4066
+ }
4067
+ } else {
4068
+ let responseDoc = parser.parseFromString("<body><template>" + newContent + "</template></body>", "text/html");
4069
+ let content = responseDoc.body.querySelector("template").content;
4070
+ content.generatedByIdiomorph = true;
4071
+ return content;
4072
+ }
4073
+ }
4074
+ function normalizeContent(newContent) {
4075
+ if (newContent == null) {
4076
+ const dummyParent = document.createElement("div");
4077
+ return dummyParent;
4078
+ } else if (newContent.generatedByIdiomorph) {
4079
+ return newContent;
4080
+ } else if (newContent instanceof Node) {
4081
+ const dummyParent = document.createElement("div");
4082
+ dummyParent.append(newContent);
4083
+ return dummyParent;
4084
+ } else {
4085
+ const dummyParent = document.createElement("div");
4086
+ for (const elt of [...newContent]) {
4087
+ dummyParent.append(elt);
4088
+ }
4089
+ return dummyParent;
4090
+ }
4091
+ }
4092
+ function insertSiblings(previousSibling, morphedNode, nextSibling) {
4093
+ let stack = [];
4094
+ let added = [];
4095
+ while (previousSibling != null) {
4096
+ stack.push(previousSibling);
4097
+ previousSibling = previousSibling.previousSibling;
3050
4098
  }
3051
- }
3052
- streamSourceIsConnected(source) {
3053
- return this.sources.has(source);
3054
- }
3055
- async receiveMessageResponse(response) {
3056
- const html = await response.responseHTML;
3057
- if (html) {
3058
- this.receiveMessageHTML(html);
4099
+ while (stack.length > 0) {
4100
+ let node = stack.pop();
4101
+ added.push(node);
4102
+ morphedNode.parentElement.insertBefore(node, morphedNode);
3059
4103
  }
4104
+ added.push(morphedNode);
4105
+ while (nextSibling != null) {
4106
+ stack.push(nextSibling);
4107
+ added.push(nextSibling);
4108
+ nextSibling = nextSibling.nextSibling;
4109
+ }
4110
+ while (stack.length > 0) {
4111
+ morphedNode.parentElement.insertBefore(stack.pop(), morphedNode.nextSibling);
4112
+ }
4113
+ return added;
3060
4114
  }
3061
- receiveMessageHTML(html) {
3062
- this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
3063
- }
3064
- };
3065
- function fetchResponseFromEvent(event) {
3066
- var _a;
3067
- const fetchResponse = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchResponse;
3068
- if (fetchResponse instanceof FetchResponse) {
3069
- return fetchResponse;
3070
- }
3071
- }
3072
- function fetchResponseIsStream(response) {
3073
- var _a;
3074
- const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
3075
- return contentType.startsWith(StreamMessage.contentType);
3076
- }
3077
- var ErrorRenderer = class extends Renderer {
3078
- static renderElement(currentElement, newElement) {
3079
- const { documentElement, body } = document;
3080
- documentElement.replaceChild(newElement, body);
3081
- }
3082
- async render() {
3083
- this.replaceHeadAndBody();
3084
- this.activateScriptElements();
4115
+ function findBestNodeMatch(newContent, oldNode, ctx) {
4116
+ let currentElement;
4117
+ currentElement = newContent.firstChild;
4118
+ let bestElement = currentElement;
4119
+ let score = 0;
4120
+ while (currentElement) {
4121
+ let newScore = scoreElement(currentElement, oldNode, ctx);
4122
+ if (newScore > score) {
4123
+ bestElement = currentElement;
4124
+ score = newScore;
4125
+ }
4126
+ currentElement = currentElement.nextSibling;
4127
+ }
4128
+ return bestElement;
3085
4129
  }
3086
- replaceHeadAndBody() {
3087
- const { documentElement, head } = document;
3088
- documentElement.replaceChild(this.newHead, head);
3089
- this.renderElement(this.currentElement, this.newElement);
4130
+ function scoreElement(node1, node2, ctx) {
4131
+ if (isSoftMatch(node1, node2)) {
4132
+ return 0.5 + getIdIntersectionCount(ctx, node1, node2);
4133
+ }
4134
+ return 0;
3090
4135
  }
3091
- activateScriptElements() {
3092
- for (const replaceableElement of this.scriptElements) {
3093
- const parentNode = replaceableElement.parentNode;
3094
- if (parentNode) {
3095
- const element = activateScriptElement(replaceableElement);
3096
- parentNode.replaceChild(element, replaceableElement);
4136
+ function removeNode(tempNode, ctx) {
4137
+ removeIdsFromConsideration(ctx, tempNode);
4138
+ if (ctx.callbacks.beforeNodeRemoved(tempNode) === false)
4139
+ return;
4140
+ tempNode.remove();
4141
+ ctx.callbacks.afterNodeRemoved(tempNode);
4142
+ }
4143
+ function isIdInConsideration(ctx, id) {
4144
+ return !ctx.deadIds.has(id);
4145
+ }
4146
+ function idIsWithinNode(ctx, id, targetNode) {
4147
+ let idSet = ctx.idMap.get(targetNode) || EMPTY_SET;
4148
+ return idSet.has(id);
4149
+ }
4150
+ function removeIdsFromConsideration(ctx, node) {
4151
+ let idSet = ctx.idMap.get(node) || EMPTY_SET;
4152
+ for (const id of idSet) {
4153
+ ctx.deadIds.add(id);
4154
+ }
4155
+ }
4156
+ function getIdIntersectionCount(ctx, node1, node2) {
4157
+ let sourceSet = ctx.idMap.get(node1) || EMPTY_SET;
4158
+ let matchCount = 0;
4159
+ for (const id of sourceSet) {
4160
+ if (isIdInConsideration(ctx, id) && idIsWithinNode(ctx, id, node2)) {
4161
+ ++matchCount;
4162
+ }
4163
+ }
4164
+ return matchCount;
4165
+ }
4166
+ function populateIdMapForNode(node, idMap) {
4167
+ let nodeParent = node.parentElement;
4168
+ let idElements = node.querySelectorAll("[id]");
4169
+ for (const elt of idElements) {
4170
+ let current = elt;
4171
+ while (current !== nodeParent && current != null) {
4172
+ let idSet = idMap.get(current);
4173
+ if (idSet == null) {
4174
+ idSet = /* @__PURE__ */ new Set();
4175
+ idMap.set(current, idSet);
4176
+ }
4177
+ idSet.add(elt.id);
4178
+ current = current.parentElement;
3097
4179
  }
3098
4180
  }
3099
4181
  }
3100
- get newHead() {
3101
- return this.newSnapshot.headSnapshot.element;
3102
- }
3103
- get scriptElements() {
3104
- return document.documentElement.querySelectorAll("script");
4182
+ function createIdMap(oldContent, newContent) {
4183
+ let idMap = /* @__PURE__ */ new Map();
4184
+ populateIdMapForNode(oldContent, idMap);
4185
+ populateIdMapForNode(newContent, idMap);
4186
+ return idMap;
3105
4187
  }
3106
- };
4188
+ return {
4189
+ morph,
4190
+ defaults
4191
+ };
4192
+ }();
3107
4193
  var PageRenderer = class extends Renderer {
3108
4194
  static renderElement(currentElement, newElement) {
3109
4195
  if (document.body && newElement instanceof HTMLBodyElement) {
@@ -3128,6 +4214,7 @@
3128
4214
  }
3129
4215
  }
3130
4216
  async prepareToRender() {
4217
+ this.#setLanguage();
3131
4218
  await this.mergeHead();
3132
4219
  }
3133
4220
  async render() {
@@ -3150,12 +4237,24 @@
3150
4237
  get newElement() {
3151
4238
  return this.newSnapshot.element;
3152
4239
  }
4240
+ #setLanguage() {
4241
+ const { documentElement } = this.currentSnapshot;
4242
+ const { lang } = this.newSnapshot;
4243
+ if (lang) {
4244
+ documentElement.setAttribute("lang", lang);
4245
+ } else {
4246
+ documentElement.removeAttribute("lang");
4247
+ }
4248
+ }
3153
4249
  async mergeHead() {
3154
4250
  const mergedHeadElements = this.mergeProvisionalElements();
3155
4251
  const newStylesheetElements = this.copyNewHeadStylesheetElements();
3156
4252
  this.copyNewHeadScriptElements();
3157
4253
  await mergedHeadElements;
3158
4254
  await newStylesheetElements;
4255
+ if (this.willRender) {
4256
+ this.removeUnusedDynamicStylesheetElements();
4257
+ }
3159
4258
  }
3160
4259
  async replaceBody() {
3161
4260
  await this.preservingPermanentElements(async () => {
@@ -3179,6 +4278,11 @@
3179
4278
  document.head.appendChild(activateScriptElement(element));
3180
4279
  }
3181
4280
  }
4281
+ removeUnusedDynamicStylesheetElements() {
4282
+ for (const element of this.unusedDynamicStylesheetElements) {
4283
+ document.head.removeChild(element);
4284
+ }
4285
+ }
3182
4286
  async mergeProvisionalElements() {
3183
4287
  const newHeadElements = [...this.newHeadProvisionalElements];
3184
4288
  for (const element of this.currentHeadProvisionalElements) {
@@ -3231,6 +4335,14 @@
3231
4335
  async assignNewBody() {
3232
4336
  await this.renderElement(this.currentElement, this.newElement);
3233
4337
  }
4338
+ get unusedDynamicStylesheetElements() {
4339
+ return this.oldHeadStylesheetElements.filter((element) => {
4340
+ return element.getAttribute("data-turbo-track") === "dynamic";
4341
+ });
4342
+ }
4343
+ get oldHeadStylesheetElements() {
4344
+ return this.currentHeadSnapshot.getStylesheetElementsNotInSnapshot(this.newHeadSnapshot);
4345
+ }
3234
4346
  get newHeadStylesheetElements() {
3235
4347
  return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
3236
4348
  }
@@ -3247,10 +4359,107 @@
3247
4359
  return this.newElement.querySelectorAll("script");
3248
4360
  }
3249
4361
  };
4362
+ var MorphRenderer = class extends PageRenderer {
4363
+ async render() {
4364
+ if (this.willRender)
4365
+ await this.#morphBody();
4366
+ }
4367
+ get renderMethod() {
4368
+ return "morph";
4369
+ }
4370
+ // Private
4371
+ async #morphBody() {
4372
+ this.#morphElements(this.currentElement, this.newElement);
4373
+ this.#reloadRemoteFrames();
4374
+ dispatch("turbo:morph", {
4375
+ detail: {
4376
+ currentElement: this.currentElement,
4377
+ newElement: this.newElement
4378
+ }
4379
+ });
4380
+ }
4381
+ #morphElements(currentElement, newElement, morphStyle = "outerHTML") {
4382
+ this.isMorphingTurboFrame = this.#isFrameReloadedWithMorph(currentElement);
4383
+ Idiomorph.morph(currentElement, newElement, {
4384
+ morphStyle,
4385
+ callbacks: {
4386
+ beforeNodeAdded: this.#shouldAddElement,
4387
+ beforeNodeMorphed: this.#shouldMorphElement,
4388
+ beforeAttributeUpdated: this.#shouldUpdateAttribute,
4389
+ beforeNodeRemoved: this.#shouldRemoveElement,
4390
+ afterNodeMorphed: this.#didMorphElement
4391
+ }
4392
+ });
4393
+ }
4394
+ #shouldAddElement = (node) => {
4395
+ return !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id));
4396
+ };
4397
+ #shouldMorphElement = (oldNode, newNode) => {
4398
+ if (oldNode instanceof HTMLElement) {
4399
+ if (!oldNode.hasAttribute("data-turbo-permanent") && (this.isMorphingTurboFrame || !this.#isFrameReloadedWithMorph(oldNode))) {
4400
+ const event = dispatch("turbo:before-morph-element", {
4401
+ cancelable: true,
4402
+ target: oldNode,
4403
+ detail: {
4404
+ newElement: newNode
4405
+ }
4406
+ });
4407
+ return !event.defaultPrevented;
4408
+ } else {
4409
+ return false;
4410
+ }
4411
+ }
4412
+ };
4413
+ #shouldUpdateAttribute = (attributeName, target, mutationType) => {
4414
+ const event = dispatch("turbo:before-morph-attribute", { cancelable: true, target, detail: { attributeName, mutationType } });
4415
+ return !event.defaultPrevented;
4416
+ };
4417
+ #didMorphElement = (oldNode, newNode) => {
4418
+ if (newNode instanceof HTMLElement) {
4419
+ dispatch("turbo:morph-element", {
4420
+ target: oldNode,
4421
+ detail: {
4422
+ newElement: newNode
4423
+ }
4424
+ });
4425
+ }
4426
+ };
4427
+ #shouldRemoveElement = (node) => {
4428
+ return this.#shouldMorphElement(node);
4429
+ };
4430
+ #reloadRemoteFrames() {
4431
+ this.#remoteFrames().forEach((frame) => {
4432
+ if (this.#isFrameReloadedWithMorph(frame)) {
4433
+ this.#renderFrameWithMorph(frame);
4434
+ frame.reload();
4435
+ }
4436
+ });
4437
+ }
4438
+ #renderFrameWithMorph(frame) {
4439
+ frame.addEventListener("turbo:before-frame-render", (event) => {
4440
+ event.detail.render = this.#morphFrameUpdate;
4441
+ }, { once: true });
4442
+ }
4443
+ #morphFrameUpdate = (currentElement, newElement) => {
4444
+ dispatch("turbo:before-frame-morph", {
4445
+ target: currentElement,
4446
+ detail: { currentElement, newElement }
4447
+ });
4448
+ this.#morphElements(currentElement, newElement.children, "innerHTML");
4449
+ };
4450
+ #isFrameReloadedWithMorph(element) {
4451
+ return element.src && element.refresh === "morph";
4452
+ }
4453
+ #remoteFrames() {
4454
+ return Array.from(document.querySelectorAll("turbo-frame[src]")).filter((frame) => {
4455
+ return !frame.closest("[data-turbo-permanent]");
4456
+ });
4457
+ }
4458
+ };
3250
4459
  var SnapshotCache = class {
4460
+ keys = [];
4461
+ snapshots = {};
3251
4462
  constructor(size) {
3252
- this.keys = [];
3253
- this.snapshots = {};
3254
4463
  this.size = size;
3255
4464
  }
3256
4465
  has(location2) {
@@ -3271,6 +4480,7 @@
3271
4480
  clear() {
3272
4481
  this.snapshots = {};
3273
4482
  }
4483
+ // Private
3274
4484
  read(location2) {
3275
4485
  return this.snapshots[toCacheKey(location2)];
3276
4486
  }
@@ -3292,23 +4502,25 @@
3292
4502
  }
3293
4503
  };
3294
4504
  var PageView = class extends View {
3295
- constructor() {
3296
- super(...arguments);
3297
- this.snapshotCache = new SnapshotCache(10);
3298
- this.lastRenderedLocation = new URL(location.href);
3299
- this.forceReloaded = false;
4505
+ snapshotCache = new SnapshotCache(10);
4506
+ lastRenderedLocation = new URL(location.href);
4507
+ forceReloaded = false;
4508
+ shouldTransitionTo(newSnapshot) {
4509
+ return this.snapshot.prefersViewTransitions && newSnapshot.prefersViewTransitions;
3300
4510
  }
3301
4511
  renderPage(snapshot, isPreview = false, willRender = true, visit2) {
3302
- const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
4512
+ const shouldMorphPage = this.isPageRefresh(visit2) && this.snapshot.shouldMorphPage;
4513
+ const rendererClass = shouldMorphPage ? MorphRenderer : PageRenderer;
4514
+ const renderer = new rendererClass(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
3303
4515
  if (!renderer.shouldRender) {
3304
4516
  this.forceReloaded = true;
3305
4517
  } else {
3306
- visit2 === null || visit2 === void 0 ? void 0 : visit2.changeHistory();
4518
+ visit2?.changeHistory();
3307
4519
  }
3308
4520
  return this.render(renderer);
3309
4521
  }
3310
4522
  renderError(snapshot, visit2) {
3311
- visit2 === null || visit2 === void 0 ? void 0 : visit2.changeHistory();
4523
+ visit2?.changeHistory();
3312
4524
  const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
3313
4525
  return this.render(renderer);
3314
4526
  }
@@ -3328,30 +4540,37 @@
3328
4540
  getCachedSnapshotForLocation(location2) {
3329
4541
  return this.snapshotCache.get(location2);
3330
4542
  }
4543
+ isPageRefresh(visit2) {
4544
+ return !visit2 || this.lastRenderedLocation.pathname === visit2.location.pathname && visit2.action === "replace";
4545
+ }
4546
+ shouldPreserveScrollPosition(visit2) {
4547
+ return this.isPageRefresh(visit2) && this.snapshot.shouldPreserveScrollPosition;
4548
+ }
3331
4549
  get snapshot() {
3332
4550
  return PageSnapshot.fromElement(this.element);
3333
4551
  }
3334
4552
  };
3335
4553
  var Preloader = class {
3336
- constructor(delegate) {
3337
- this.selector = "a[data-turbo-preload]";
4554
+ selector = "a[data-turbo-preload]";
4555
+ constructor(delegate, snapshotCache) {
3338
4556
  this.delegate = delegate;
3339
- }
3340
- get snapshotCache() {
3341
- return this.delegate.navigator.view.snapshotCache;
4557
+ this.snapshotCache = snapshotCache;
3342
4558
  }
3343
4559
  start() {
3344
4560
  if (document.readyState === "loading") {
3345
- return document.addEventListener("DOMContentLoaded", () => {
3346
- this.preloadOnLoadLinksForView(document.body);
3347
- });
4561
+ document.addEventListener("DOMContentLoaded", this.#preloadAll);
3348
4562
  } else {
3349
4563
  this.preloadOnLoadLinksForView(document.body);
3350
4564
  }
3351
4565
  }
4566
+ stop() {
4567
+ document.removeEventListener("DOMContentLoaded", this.#preloadAll);
4568
+ }
3352
4569
  preloadOnLoadLinksForView(element) {
3353
4570
  for (const link of element.querySelectorAll(this.selector)) {
3354
- this.preloadURL(link);
4571
+ if (this.delegate.shouldPreloadLink(link)) {
4572
+ this.preloadURL(link);
4573
+ }
3355
4574
  }
3356
4575
  }
3357
4576
  async preloadURL(link) {
@@ -3359,41 +4578,88 @@
3359
4578
  if (this.snapshotCache.has(location2)) {
3360
4579
  return;
3361
4580
  }
4581
+ const fetchRequest = new FetchRequest(this, FetchMethod.get, location2, new URLSearchParams(), link);
4582
+ await fetchRequest.perform();
4583
+ }
4584
+ // Fetch request delegate
4585
+ prepareRequest(fetchRequest) {
4586
+ fetchRequest.headers["X-Sec-Purpose"] = "prefetch";
4587
+ }
4588
+ async requestSucceededWithResponse(fetchRequest, fetchResponse) {
3362
4589
  try {
3363
- const response = await fetch(location2.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
3364
- const responseText = await response.text();
3365
- const snapshot = PageSnapshot.fromHTMLString(responseText);
3366
- this.snapshotCache.put(location2, snapshot);
4590
+ const responseHTML = await fetchResponse.responseHTML;
4591
+ const snapshot = PageSnapshot.fromHTMLString(responseHTML);
4592
+ this.snapshotCache.put(fetchRequest.url, snapshot);
3367
4593
  } catch (_) {
3368
4594
  }
3369
4595
  }
4596
+ requestStarted(fetchRequest) {
4597
+ }
4598
+ requestErrored(fetchRequest) {
4599
+ }
4600
+ requestFinished(fetchRequest) {
4601
+ }
4602
+ requestPreventedHandlingResponse(fetchRequest, fetchResponse) {
4603
+ }
4604
+ requestFailedWithResponse(fetchRequest, fetchResponse) {
4605
+ }
4606
+ #preloadAll = () => {
4607
+ this.preloadOnLoadLinksForView(document.body);
4608
+ };
4609
+ };
4610
+ var Cache = class {
4611
+ constructor(session2) {
4612
+ this.session = session2;
4613
+ }
4614
+ clear() {
4615
+ this.session.clearCache();
4616
+ }
4617
+ resetCacheControl() {
4618
+ this.#setCacheControl("");
4619
+ }
4620
+ exemptPageFromCache() {
4621
+ this.#setCacheControl("no-cache");
4622
+ }
4623
+ exemptPageFromPreview() {
4624
+ this.#setCacheControl("no-preview");
4625
+ }
4626
+ #setCacheControl(value) {
4627
+ setMetaContent("turbo-cache-control", value);
4628
+ }
3370
4629
  };
3371
4630
  var Session = class {
3372
- constructor() {
3373
- this.navigator = new Navigator(this);
3374
- this.history = new History(this);
3375
- this.preloader = new Preloader(this);
3376
- this.view = new PageView(this, document.documentElement);
3377
- this.adapter = new BrowserAdapter(this);
3378
- this.pageObserver = new PageObserver(this);
3379
- this.cacheObserver = new CacheObserver();
3380
- this.linkClickObserver = new LinkClickObserver(this, window);
3381
- this.formSubmitObserver = new FormSubmitObserver(this, document);
3382
- this.scrollObserver = new ScrollObserver(this);
3383
- this.streamObserver = new StreamObserver(this);
3384
- this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
3385
- this.frameRedirector = new FrameRedirector(this, document.documentElement);
3386
- this.streamMessageRenderer = new StreamMessageRenderer();
3387
- this.drive = true;
3388
- this.enabled = true;
3389
- this.progressBarDelay = 500;
3390
- this.started = false;
3391
- this.formMode = "on";
4631
+ navigator = new Navigator(this);
4632
+ history = new History(this);
4633
+ view = new PageView(this, document.documentElement);
4634
+ adapter = new BrowserAdapter(this);
4635
+ pageObserver = new PageObserver(this);
4636
+ cacheObserver = new CacheObserver();
4637
+ linkPrefetchObserver = new LinkPrefetchObserver(this, document);
4638
+ linkClickObserver = new LinkClickObserver(this, window);
4639
+ formSubmitObserver = new FormSubmitObserver(this, document);
4640
+ scrollObserver = new ScrollObserver(this);
4641
+ streamObserver = new StreamObserver(this);
4642
+ formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
4643
+ frameRedirector = new FrameRedirector(this, document.documentElement);
4644
+ streamMessageRenderer = new StreamMessageRenderer();
4645
+ cache = new Cache(this);
4646
+ drive = true;
4647
+ enabled = true;
4648
+ progressBarDelay = 500;
4649
+ started = false;
4650
+ formMode = "on";
4651
+ #pageRefreshDebouncePeriod = 150;
4652
+ constructor(recentRequests2) {
4653
+ this.recentRequests = recentRequests2;
4654
+ this.preloader = new Preloader(this, this.view.snapshotCache);
4655
+ this.debouncedRefresh = this.refresh;
4656
+ this.pageRefreshDebouncePeriod = this.pageRefreshDebouncePeriod;
3392
4657
  }
3393
4658
  start() {
3394
4659
  if (!this.started) {
3395
4660
  this.pageObserver.start();
3396
4661
  this.cacheObserver.start();
4662
+ this.linkPrefetchObserver.start();
3397
4663
  this.formLinkClickObserver.start();
3398
4664
  this.linkClickObserver.start();
3399
4665
  this.formSubmitObserver.start();
@@ -3413,6 +4679,7 @@
3413
4679
  if (this.started) {
3414
4680
  this.pageObserver.stop();
3415
4681
  this.cacheObserver.stop();
4682
+ this.linkPrefetchObserver.stop();
3416
4683
  this.formLinkClickObserver.stop();
3417
4684
  this.linkClickObserver.stop();
3418
4685
  this.formSubmitObserver.stop();
@@ -3420,6 +4687,7 @@
3420
4687
  this.streamObserver.stop();
3421
4688
  this.frameRedirector.stop();
3422
4689
  this.history.stop();
4690
+ this.preloader.stop();
3423
4691
  this.started = false;
3424
4692
  }
3425
4693
  }
@@ -3429,12 +4697,19 @@
3429
4697
  visit(location2, options = {}) {
3430
4698
  const frameElement = options.frame ? document.getElementById(options.frame) : null;
3431
4699
  if (frameElement instanceof FrameElement) {
4700
+ const action = options.action || getVisitAction(frameElement);
4701
+ frameElement.delegate.proposeVisitIfNavigatedWithAction(frameElement, action);
3432
4702
  frameElement.src = location2.toString();
3433
- frameElement.loaded;
3434
4703
  } else {
3435
4704
  this.navigator.proposeVisit(expandURL(location2), options);
3436
4705
  }
3437
4706
  }
4707
+ refresh(url, requestId) {
4708
+ const isRecentRequest = requestId && this.recentRequests.has(requestId);
4709
+ if (!isRecentRequest) {
4710
+ this.visit(url, { action: "replace", shouldCacheSnapshot: false });
4711
+ }
4712
+ }
3438
4713
  connectStreamSource(source) {
3439
4714
  this.streamObserver.connectStreamSource(source);
3440
4715
  }
@@ -3459,11 +4734,33 @@
3459
4734
  get restorationIdentifier() {
3460
4735
  return this.history.restorationIdentifier;
3461
4736
  }
3462
- historyPoppedToLocationWithRestorationIdentifier(location2, restorationIdentifier) {
4737
+ get pageRefreshDebouncePeriod() {
4738
+ return this.#pageRefreshDebouncePeriod;
4739
+ }
4740
+ set pageRefreshDebouncePeriod(value) {
4741
+ this.refresh = debounce(this.debouncedRefresh.bind(this), value);
4742
+ this.#pageRefreshDebouncePeriod = value;
4743
+ }
4744
+ // Preloader delegate
4745
+ shouldPreloadLink(element) {
4746
+ const isUnsafe = element.hasAttribute("data-turbo-method");
4747
+ const isStream = element.hasAttribute("data-turbo-stream");
4748
+ const frameTarget = element.getAttribute("data-turbo-frame");
4749
+ const frame = frameTarget == "_top" ? null : document.getElementById(frameTarget) || findClosestRecursively(element, "turbo-frame:not([disabled])");
4750
+ if (isUnsafe || isStream || frame instanceof FrameElement) {
4751
+ return false;
4752
+ } else {
4753
+ const location2 = new URL(element.href);
4754
+ return this.elementIsNavigatable(element) && locationIsVisitable(location2, this.snapshot.rootLocation);
4755
+ }
4756
+ }
4757
+ // History delegate
4758
+ historyPoppedToLocationWithRestorationIdentifierAndDirection(location2, restorationIdentifier, direction) {
3463
4759
  if (this.enabled) {
3464
4760
  this.navigator.startVisit(location2, restorationIdentifier, {
3465
4761
  action: "restore",
3466
- historyChanged: true
4762
+ historyChanged: true,
4763
+ direction
3467
4764
  });
3468
4765
  } else {
3469
4766
  this.adapter.pageInvalidated({
@@ -3471,14 +4768,21 @@
3471
4768
  });
3472
4769
  }
3473
4770
  }
4771
+ // Scroll observer delegate
3474
4772
  scrollPositionChanged(position) {
3475
4773
  this.history.updateRestorationData({ scrollPosition: position });
3476
4774
  }
4775
+ // Form click observer delegate
3477
4776
  willSubmitFormLinkToLocation(link, location2) {
3478
4777
  return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation);
3479
4778
  }
3480
4779
  submittedFormLinkToLocation() {
3481
4780
  }
4781
+ // Link hover observer delegate
4782
+ canPrefetchRequestToLocation(link, location2) {
4783
+ return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation);
4784
+ }
4785
+ // Link click observer delegate
3482
4786
  willFollowLinkToLocation(link, location2, event) {
3483
4787
  return this.elementIsNavigatable(link) && locationIsVisitable(location2, this.snapshot.rootLocation) && this.applicationAllowsFollowingLinkToLocation(link, location2, event);
3484
4788
  }
@@ -3487,6 +4791,7 @@
3487
4791
  const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
3488
4792
  this.visit(location2.href, { action, acceptsStreamResponse });
3489
4793
  }
4794
+ // Navigator delegate
3490
4795
  allowsVisitingLocationWithAction(location2, action) {
3491
4796
  return this.locationWithActionIsSamePage(location2, action) || this.applicationAllowsVisitingLocation(location2);
3492
4797
  }
@@ -3494,9 +4799,11 @@
3494
4799
  extendURLWithDeprecatedProperties(location2);
3495
4800
  this.adapter.visitProposedToLocation(location2, options);
3496
4801
  }
4802
+ // Visit delegate
3497
4803
  visitStarted(visit2) {
3498
4804
  if (!visit2.acceptsStreamResponse) {
3499
4805
  markAsBusy(document.documentElement);
4806
+ this.view.markVisitDirection(visit2.direction);
3500
4807
  }
3501
4808
  extendURLWithDeprecatedProperties(visit2.location);
3502
4809
  if (!visit2.silent) {
@@ -3504,6 +4811,7 @@
3504
4811
  }
3505
4812
  }
3506
4813
  visitCompleted(visit2) {
4814
+ this.view.unmarkVisitDirection();
3507
4815
  clearBusyState(document.documentElement);
3508
4816
  this.notifyApplicationAfterPageLoad(visit2.getTimingMetrics());
3509
4817
  }
@@ -3513,13 +4821,15 @@
3513
4821
  visitScrolledToSamePageLocation(oldURL, newURL) {
3514
4822
  this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
3515
4823
  }
4824
+ // Form submit observer delegate
3516
4825
  willSubmitForm(form, submitter) {
3517
- const action = getAction(form, submitter);
4826
+ const action = getAction$1(form, submitter);
3518
4827
  return this.submissionIsNavigatable(form, submitter) && locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
3519
4828
  }
3520
4829
  formSubmitted(form, submitter) {
3521
4830
  this.navigator.submitForm(form, submitter);
3522
4831
  }
4832
+ // Page observer delegate
3523
4833
  pageBecameInteractive() {
3524
4834
  this.view.lastRenderedLocation = this.location;
3525
4835
  this.notifyApplicationAfterPageLoad();
@@ -3530,26 +4840,30 @@
3530
4840
  pageWillUnload() {
3531
4841
  this.history.relinquishControlOfScrollRestoration();
3532
4842
  }
4843
+ // Stream observer delegate
3533
4844
  receivedMessageFromStream(message) {
3534
4845
  this.renderStreamMessage(message);
3535
4846
  }
4847
+ // Page view delegate
3536
4848
  viewWillCacheSnapshot() {
3537
- var _a;
3538
- if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
4849
+ if (!this.navigator.currentVisit?.silent) {
3539
4850
  this.notifyApplicationBeforeCachingSnapshot();
3540
4851
  }
3541
4852
  }
3542
4853
  allowsImmediateRender({ element }, options) {
3543
4854
  const event = this.notifyApplicationBeforeRender(element, options);
3544
- const { defaultPrevented, detail: { render } } = event;
4855
+ const {
4856
+ defaultPrevented,
4857
+ detail: { render }
4858
+ } = event;
3545
4859
  if (this.view.renderer && render) {
3546
4860
  this.view.renderer.renderElement = render;
3547
4861
  }
3548
4862
  return !defaultPrevented;
3549
4863
  }
3550
- viewRenderedSnapshot(_snapshot, _isPreview) {
4864
+ viewRenderedSnapshot(_snapshot, _isPreview, renderMethod) {
3551
4865
  this.view.lastRenderedLocation = this.history.location;
3552
- this.notifyApplicationAfterRender();
4866
+ this.notifyApplicationAfterRender(renderMethod);
3553
4867
  }
3554
4868
  preloadOnLoadLinksForView(element) {
3555
4869
  this.preloader.preloadOnLoadLinksForView(element);
@@ -3557,12 +4871,14 @@
3557
4871
  viewInvalidated(reason) {
3558
4872
  this.adapter.pageInvalidated(reason);
3559
4873
  }
4874
+ // Frame element
3560
4875
  frameLoaded(frame) {
3561
4876
  this.notifyApplicationAfterFrameLoad(frame);
3562
4877
  }
3563
4878
  frameRendered(fetchResponse, frame) {
3564
4879
  this.notifyApplicationAfterFrameRender(fetchResponse, frame);
3565
4880
  }
4881
+ // Application events
3566
4882
  applicationAllowsFollowingLinkToLocation(link, location2, ev) {
3567
4883
  const event = this.notifyApplicationAfterClickingLinkToLocation(link, location2, ev);
3568
4884
  return !event.defaultPrevented;
@@ -3592,12 +4908,12 @@
3592
4908
  }
3593
4909
  notifyApplicationBeforeRender(newBody, options) {
3594
4910
  return dispatch("turbo:before-render", {
3595
- detail: Object.assign({ newBody }, options),
4911
+ detail: { newBody, ...options },
3596
4912
  cancelable: true
3597
4913
  });
3598
4914
  }
3599
- notifyApplicationAfterRender() {
3600
- return dispatch("turbo:render");
4915
+ notifyApplicationAfterRender(renderMethod) {
4916
+ return dispatch("turbo:render", { detail: { renderMethod } });
3601
4917
  }
3602
4918
  notifyApplicationAfterPageLoad(timing = {}) {
3603
4919
  return dispatch("turbo:load", {
@@ -3605,10 +4921,12 @@
3605
4921
  });
3606
4922
  }
3607
4923
  notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
3608
- dispatchEvent(new HashChangeEvent("hashchange", {
3609
- oldURL: oldURL.toString(),
3610
- newURL: newURL.toString()
3611
- }));
4924
+ dispatchEvent(
4925
+ new HashChangeEvent("hashchange", {
4926
+ oldURL: oldURL.toString(),
4927
+ newURL: newURL.toString()
4928
+ })
4929
+ );
3612
4930
  }
3613
4931
  notifyApplicationAfterFrameLoad(frame) {
3614
4932
  return dispatch("turbo:frame-load", { target: frame });
@@ -3620,6 +4938,7 @@
3620
4938
  cancelable: true
3621
4939
  });
3622
4940
  }
4941
+ // Helpers
3623
4942
  submissionIsNavigatable(form, submitter) {
3624
4943
  if (this.formMode == "off") {
3625
4944
  return false;
@@ -3649,6 +4968,7 @@
3649
4968
  }
3650
4969
  }
3651
4970
  }
4971
+ // Private
3652
4972
  getActionForLink(link) {
3653
4973
  return getVisitAction(link) || "advance";
3654
4974
  }
@@ -3666,63 +4986,8 @@
3666
4986
  }
3667
4987
  }
3668
4988
  };
3669
- var Cache = class {
3670
- constructor(session2) {
3671
- this.session = session2;
3672
- }
3673
- clear() {
3674
- this.session.clearCache();
3675
- }
3676
- resetCacheControl() {
3677
- this.setCacheControl("");
3678
- }
3679
- exemptPageFromCache() {
3680
- this.setCacheControl("no-cache");
3681
- }
3682
- exemptPageFromPreview() {
3683
- this.setCacheControl("no-preview");
3684
- }
3685
- setCacheControl(value) {
3686
- setMetaContent("turbo-cache-control", value);
3687
- }
3688
- };
3689
- var StreamActions = {
3690
- after() {
3691
- this.targetElements.forEach((e) => {
3692
- var _a;
3693
- return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling);
3694
- });
3695
- },
3696
- append() {
3697
- this.removeDuplicateTargetChildren();
3698
- this.targetElements.forEach((e) => e.append(this.templateContent));
3699
- },
3700
- before() {
3701
- this.targetElements.forEach((e) => {
3702
- var _a;
3703
- return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e);
3704
- });
3705
- },
3706
- prepend() {
3707
- this.removeDuplicateTargetChildren();
3708
- this.targetElements.forEach((e) => e.prepend(this.templateContent));
3709
- },
3710
- remove() {
3711
- this.targetElements.forEach((e) => e.remove());
3712
- },
3713
- replace() {
3714
- this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
3715
- },
3716
- update() {
3717
- this.targetElements.forEach((targetElement) => {
3718
- targetElement.innerHTML = "";
3719
- targetElement.append(this.templateContent);
3720
- });
3721
- }
3722
- };
3723
- var session = new Session();
3724
- var cache = new Cache(session);
3725
- var { navigator: navigator$1 } = session;
4989
+ var session = new Session(recentRequests);
4990
+ var { cache, navigator: navigator$1 } = session;
3726
4991
  function start() {
3727
4992
  session.start();
3728
4993
  }
@@ -3742,7 +5007,9 @@
3742
5007
  session.renderStreamMessage(message);
3743
5008
  }
3744
5009
  function clearCache() {
3745
- console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
5010
+ console.warn(
5011
+ "Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`"
5012
+ );
3746
5013
  session.clearCache();
3747
5014
  }
3748
5015
  function setProgressBarDelay(delay) {
@@ -3762,6 +5029,7 @@
3762
5029
  PageRenderer,
3763
5030
  PageSnapshot,
3764
5031
  FrameRenderer,
5032
+ fetch: fetchWithTurboHeaders,
3765
5033
  start,
3766
5034
  registerAdapter,
3767
5035
  visit,
@@ -3771,29 +5039,20 @@
3771
5039
  clearCache,
3772
5040
  setProgressBarDelay,
3773
5041
  setConfirmMethod,
3774
- setFormMode,
3775
- StreamActions
5042
+ setFormMode
3776
5043
  });
3777
5044
  var TurboFrameMissingError = class extends Error {
3778
5045
  };
3779
5046
  var FrameController = class {
5047
+ fetchResponseLoaded = (_fetchResponse) => Promise.resolve();
5048
+ #currentFetchRequest = null;
5049
+ #resolveVisitPromise = () => {
5050
+ };
5051
+ #connected = false;
5052
+ #hasBeenLoaded = false;
5053
+ #ignoredAttributes = /* @__PURE__ */ new Set();
5054
+ action = null;
3780
5055
  constructor(element) {
3781
- this.fetchResponseLoaded = (_fetchResponse) => {
3782
- };
3783
- this.currentFetchRequest = null;
3784
- this.resolveVisitPromise = () => {
3785
- };
3786
- this.connected = false;
3787
- this.hasBeenLoaded = false;
3788
- this.ignoredAttributes = /* @__PURE__ */ new Set();
3789
- this.action = null;
3790
- this.visitCachedSnapshot = ({ element: element2 }) => {
3791
- const frame = element2.querySelector("#" + this.element.id);
3792
- if (frame && this.previousFrameElement) {
3793
- frame.replaceChildren(...this.previousFrameElement.children);
3794
- }
3795
- delete this.previousFrameElement;
3796
- };
3797
5056
  this.element = element;
3798
5057
  this.view = new FrameView(this, this.element);
3799
5058
  this.appearanceObserver = new AppearanceObserver(this, this.element);
@@ -3802,13 +5061,14 @@
3802
5061
  this.restorationIdentifier = uuid();
3803
5062
  this.formSubmitObserver = new FormSubmitObserver(this, this.element);
3804
5063
  }
5064
+ // Frame delegate
3805
5065
  connect() {
3806
- if (!this.connected) {
3807
- this.connected = true;
5066
+ if (!this.#connected) {
5067
+ this.#connected = true;
3808
5068
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
3809
5069
  this.appearanceObserver.start();
3810
5070
  } else {
3811
- this.loadSourceURL();
5071
+ this.#loadSourceURL();
3812
5072
  }
3813
5073
  this.formLinkClickObserver.start();
3814
5074
  this.linkInterceptor.start();
@@ -3816,8 +5076,8 @@
3816
5076
  }
3817
5077
  }
3818
5078
  disconnect() {
3819
- if (this.connected) {
3820
- this.connected = false;
5079
+ if (this.#connected) {
5080
+ this.#connected = false;
3821
5081
  this.appearanceObserver.stop();
3822
5082
  this.formLinkClickObserver.stop();
3823
5083
  this.linkInterceptor.stop();
@@ -3826,47 +5086,40 @@
3826
5086
  }
3827
5087
  disabledChanged() {
3828
5088
  if (this.loadingStyle == FrameLoadingStyle.eager) {
3829
- this.loadSourceURL();
5089
+ this.#loadSourceURL();
3830
5090
  }
3831
5091
  }
3832
5092
  sourceURLChanged() {
3833
- if (this.isIgnoringChangesTo("src"))
5093
+ if (this.#isIgnoringChangesTo("src"))
3834
5094
  return;
3835
5095
  if (this.element.isConnected) {
3836
5096
  this.complete = false;
3837
5097
  }
3838
- if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
3839
- this.loadSourceURL();
5098
+ if (this.loadingStyle == FrameLoadingStyle.eager || this.#hasBeenLoaded) {
5099
+ this.#loadSourceURL();
3840
5100
  }
3841
5101
  }
3842
5102
  sourceURLReloaded() {
3843
5103
  const { src } = this.element;
3844
- this.ignoringChangesToAttribute("complete", () => {
3845
- this.element.removeAttribute("complete");
3846
- });
5104
+ this.element.removeAttribute("complete");
3847
5105
  this.element.src = null;
3848
5106
  this.element.src = src;
3849
5107
  return this.element.loaded;
3850
5108
  }
3851
- completeChanged() {
3852
- if (this.isIgnoringChangesTo("complete"))
3853
- return;
3854
- this.loadSourceURL();
3855
- }
3856
5109
  loadingStyleChanged() {
3857
5110
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
3858
5111
  this.appearanceObserver.start();
3859
5112
  } else {
3860
5113
  this.appearanceObserver.stop();
3861
- this.loadSourceURL();
5114
+ this.#loadSourceURL();
3862
5115
  }
3863
5116
  }
3864
- async loadSourceURL() {
5117
+ async #loadSourceURL() {
3865
5118
  if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
3866
- this.element.loaded = this.visit(expandURL(this.sourceURL));
5119
+ this.element.loaded = this.#visit(expandURL(this.sourceURL));
3867
5120
  this.appearanceObserver.stop();
3868
5121
  await this.element.loaded;
3869
- this.hasBeenLoaded = true;
5122
+ this.#hasBeenLoaded = true;
3870
5123
  }
3871
5124
  }
3872
5125
  async loadResponse(fetchResponse) {
@@ -3879,36 +5132,39 @@
3879
5132
  const document2 = parseHTMLDocument(html);
3880
5133
  const pageSnapshot = PageSnapshot.fromDocument(document2);
3881
5134
  if (pageSnapshot.isVisitable) {
3882
- await this.loadFrameResponse(fetchResponse, document2);
5135
+ await this.#loadFrameResponse(fetchResponse, document2);
3883
5136
  } else {
3884
- await this.handleUnvisitableFrameResponse(fetchResponse);
5137
+ await this.#handleUnvisitableFrameResponse(fetchResponse);
3885
5138
  }
3886
5139
  }
3887
5140
  } finally {
3888
- this.fetchResponseLoaded = () => {
3889
- };
5141
+ this.fetchResponseLoaded = () => Promise.resolve();
3890
5142
  }
3891
5143
  }
5144
+ // Appearance observer delegate
3892
5145
  elementAppearedInViewport(element) {
3893
- this.proposeVisitIfNavigatedWithAction(element, element);
3894
- this.loadSourceURL();
5146
+ this.proposeVisitIfNavigatedWithAction(element, getVisitAction(element));
5147
+ this.#loadSourceURL();
3895
5148
  }
5149
+ // Form link click observer delegate
3896
5150
  willSubmitFormLinkToLocation(link) {
3897
- return this.shouldInterceptNavigation(link);
5151
+ return this.#shouldInterceptNavigation(link);
3898
5152
  }
3899
5153
  submittedFormLinkToLocation(link, _location, form) {
3900
- const frame = this.findFrameElement(link);
5154
+ const frame = this.#findFrameElement(link);
3901
5155
  if (frame)
3902
5156
  form.setAttribute("data-turbo-frame", frame.id);
3903
5157
  }
5158
+ // Link interceptor delegate
3904
5159
  shouldInterceptLinkClick(element, _location, _event) {
3905
- return this.shouldInterceptNavigation(element);
5160
+ return this.#shouldInterceptNavigation(element);
3906
5161
  }
3907
5162
  linkClickIntercepted(element, location2) {
3908
- this.navigateFrame(element, location2);
5163
+ this.#navigateFrame(element, location2);
3909
5164
  }
5165
+ // Form submit observer delegate
3910
5166
  willSubmitForm(element, submitter) {
3911
- return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
5167
+ return element.closest("turbo-frame") == this.element && this.#shouldInterceptNavigation(element, submitter);
3912
5168
  }
3913
5169
  formSubmitted(element, submitter) {
3914
5170
  if (this.formSubmission) {
@@ -3919,10 +5175,10 @@
3919
5175
  this.prepareRequest(fetchRequest);
3920
5176
  this.formSubmission.start();
3921
5177
  }
5178
+ // Fetch request delegate
3922
5179
  prepareRequest(request) {
3923
- var _a;
3924
5180
  request.headers["Turbo-Frame"] = this.id;
3925
- if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
5181
+ if (this.currentNavigationElement?.hasAttribute("data-turbo-stream")) {
3926
5182
  request.acceptResponseType(StreamMessage.contentType);
3927
5183
  }
3928
5184
  }
@@ -3930,29 +5186,30 @@
3930
5186
  markAsBusy(this.element);
3931
5187
  }
3932
5188
  requestPreventedHandlingResponse(_request, _response) {
3933
- this.resolveVisitPromise();
5189
+ this.#resolveVisitPromise();
3934
5190
  }
3935
5191
  async requestSucceededWithResponse(request, response) {
3936
5192
  await this.loadResponse(response);
3937
- this.resolveVisitPromise();
5193
+ this.#resolveVisitPromise();
3938
5194
  }
3939
5195
  async requestFailedWithResponse(request, response) {
3940
5196
  await this.loadResponse(response);
3941
- this.resolveVisitPromise();
5197
+ this.#resolveVisitPromise();
3942
5198
  }
3943
5199
  requestErrored(request, error2) {
3944
5200
  console.error(error2);
3945
- this.resolveVisitPromise();
5201
+ this.#resolveVisitPromise();
3946
5202
  }
3947
5203
  requestFinished(_request) {
3948
5204
  clearBusyState(this.element);
3949
5205
  }
5206
+ // Form submission delegate
3950
5207
  formSubmissionStarted({ formElement }) {
3951
- markAsBusy(formElement, this.findFrameElement(formElement));
5208
+ markAsBusy(formElement, this.#findFrameElement(formElement));
3952
5209
  }
3953
5210
  formSubmissionSucceededWithResponse(formSubmission, response) {
3954
- const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
3955
- frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
5211
+ const frame = this.#findFrameElement(formSubmission.formElement, formSubmission.submitter);
5212
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(formSubmission.submitter, formSubmission.formElement, frame));
3956
5213
  frame.delegate.loadResponse(response);
3957
5214
  if (!formSubmission.isSafe) {
3958
5215
  session.clearCache();
@@ -3966,31 +5223,44 @@
3966
5223
  console.error(error2);
3967
5224
  }
3968
5225
  formSubmissionFinished({ formElement }) {
3969
- clearBusyState(formElement, this.findFrameElement(formElement));
5226
+ clearBusyState(formElement, this.#findFrameElement(formElement));
3970
5227
  }
5228
+ // View delegate
3971
5229
  allowsImmediateRender({ element: newFrame }, options) {
3972
5230
  const event = dispatch("turbo:before-frame-render", {
3973
5231
  target: this.element,
3974
- detail: Object.assign({ newFrame }, options),
5232
+ detail: { newFrame, ...options },
3975
5233
  cancelable: true
3976
5234
  });
3977
- const { defaultPrevented, detail: { render } } = event;
5235
+ const {
5236
+ defaultPrevented,
5237
+ detail: { render }
5238
+ } = event;
3978
5239
  if (this.view.renderer && render) {
3979
5240
  this.view.renderer.renderElement = render;
3980
5241
  }
3981
5242
  return !defaultPrevented;
3982
5243
  }
3983
- viewRenderedSnapshot(_snapshot, _isPreview) {
5244
+ viewRenderedSnapshot(_snapshot, _isPreview, _renderMethod) {
3984
5245
  }
3985
5246
  preloadOnLoadLinksForView(element) {
3986
5247
  session.preloadOnLoadLinksForView(element);
3987
5248
  }
3988
5249
  viewInvalidated() {
3989
5250
  }
5251
+ // Frame renderer delegate
3990
5252
  willRenderFrame(currentElement, _newElement) {
3991
5253
  this.previousFrameElement = currentElement.cloneNode(true);
3992
5254
  }
3993
- async loadFrameResponse(fetchResponse, document2) {
5255
+ visitCachedSnapshot = ({ element }) => {
5256
+ const frame = element.querySelector("#" + this.element.id);
5257
+ if (frame && this.previousFrameElement) {
5258
+ frame.replaceChildren(...this.previousFrameElement.children);
5259
+ }
5260
+ delete this.previousFrameElement;
5261
+ };
5262
+ // Private
5263
+ async #loadFrameResponse(fetchResponse, document2) {
3994
5264
  const newFrameElement = await this.extractForeignFrameElement(document2.body);
3995
5265
  if (newFrameElement) {
3996
5266
  const snapshot = new Snapshot(newFrameElement);
@@ -4002,42 +5272,41 @@
4002
5272
  this.complete = true;
4003
5273
  session.frameRendered(fetchResponse, this.element);
4004
5274
  session.frameLoaded(this.element);
4005
- this.fetchResponseLoaded(fetchResponse);
4006
- } else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
4007
- this.handleFrameMissingFromResponse(fetchResponse);
5275
+ await this.fetchResponseLoaded(fetchResponse);
5276
+ } else if (this.#willHandleFrameMissingFromResponse(fetchResponse)) {
5277
+ this.#handleFrameMissingFromResponse(fetchResponse);
4008
5278
  }
4009
5279
  }
4010
- async visit(url) {
4011
- var _a;
5280
+ async #visit(url) {
4012
5281
  const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
4013
- (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
4014
- this.currentFetchRequest = request;
5282
+ this.#currentFetchRequest?.cancel();
5283
+ this.#currentFetchRequest = request;
4015
5284
  return new Promise((resolve) => {
4016
- this.resolveVisitPromise = () => {
4017
- this.resolveVisitPromise = () => {
5285
+ this.#resolveVisitPromise = () => {
5286
+ this.#resolveVisitPromise = () => {
4018
5287
  };
4019
- this.currentFetchRequest = null;
5288
+ this.#currentFetchRequest = null;
4020
5289
  resolve();
4021
5290
  };
4022
5291
  request.perform();
4023
5292
  });
4024
5293
  }
4025
- navigateFrame(element, url, submitter) {
4026
- const frame = this.findFrameElement(element, submitter);
4027
- frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
4028
- this.withCurrentNavigationElement(element, () => {
5294
+ #navigateFrame(element, url, submitter) {
5295
+ const frame = this.#findFrameElement(element, submitter);
5296
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(submitter, element, frame));
5297
+ this.#withCurrentNavigationElement(element, () => {
4029
5298
  frame.src = url;
4030
5299
  });
4031
5300
  }
4032
- proposeVisitIfNavigatedWithAction(frame, element, submitter) {
4033
- this.action = getVisitAction(submitter, element, frame);
5301
+ proposeVisitIfNavigatedWithAction(frame, action = null) {
5302
+ this.action = action;
4034
5303
  if (this.action) {
4035
5304
  const pageSnapshot = PageSnapshot.fromElement(frame).clone();
4036
5305
  const { visitCachedSnapshot } = frame.delegate;
4037
- frame.delegate.fetchResponseLoaded = (fetchResponse) => {
5306
+ frame.delegate.fetchResponseLoaded = async (fetchResponse) => {
4038
5307
  if (frame.src) {
4039
5308
  const { statusCode, redirected } = fetchResponse;
4040
- const responseHTML = frame.ownerDocument.documentElement.outerHTML;
5309
+ const responseHTML = await fetchResponse.responseHTML;
4041
5310
  const response = { statusCode, redirected, responseHTML };
4042
5311
  const options = {
4043
5312
  response,
@@ -4060,16 +5329,18 @@
4060
5329
  session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
4061
5330
  }
4062
5331
  }
4063
- async handleUnvisitableFrameResponse(fetchResponse) {
4064
- console.warn(`The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`);
4065
- await this.visitResponse(fetchResponse.response);
5332
+ async #handleUnvisitableFrameResponse(fetchResponse) {
5333
+ console.warn(
5334
+ `The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`
5335
+ );
5336
+ await this.#visitResponse(fetchResponse.response);
4066
5337
  }
4067
- willHandleFrameMissingFromResponse(fetchResponse) {
5338
+ #willHandleFrameMissingFromResponse(fetchResponse) {
4068
5339
  this.element.setAttribute("complete", "");
4069
5340
  const response = fetchResponse.response;
4070
- const visit2 = async (url, options = {}) => {
5341
+ const visit2 = async (url, options) => {
4071
5342
  if (url instanceof Response) {
4072
- this.visitResponse(url);
5343
+ this.#visitResponse(url);
4073
5344
  } else {
4074
5345
  session.visit(url, options);
4075
5346
  }
@@ -4081,24 +5352,23 @@
4081
5352
  });
4082
5353
  return !event.defaultPrevented;
4083
5354
  }
4084
- handleFrameMissingFromResponse(fetchResponse) {
5355
+ #handleFrameMissingFromResponse(fetchResponse) {
4085
5356
  this.view.missing();
4086
- this.throwFrameMissingError(fetchResponse);
5357
+ this.#throwFrameMissingError(fetchResponse);
4087
5358
  }
4088
- throwFrameMissingError(fetchResponse) {
5359
+ #throwFrameMissingError(fetchResponse) {
4089
5360
  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.`;
4090
5361
  throw new TurboFrameMissingError(message);
4091
5362
  }
4092
- async visitResponse(response) {
5363
+ async #visitResponse(response) {
4093
5364
  const wrapped = new FetchResponse(response);
4094
5365
  const responseHTML = await wrapped.responseHTML;
4095
5366
  const { location: location2, redirected, statusCode } = wrapped;
4096
5367
  return session.visit(location2, { response: { redirected, statusCode, responseHTML } });
4097
5368
  }
4098
- findFrameElement(element, submitter) {
4099
- var _a;
5369
+ #findFrameElement(element, submitter) {
4100
5370
  const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
4101
- return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
5371
+ return getFrameElementById(id) ?? this.element;
4102
5372
  }
4103
5373
  async extractForeignFrameElement(container) {
4104
5374
  let element;
@@ -4119,13 +5389,13 @@
4119
5389
  }
4120
5390
  return null;
4121
5391
  }
4122
- formActionIsVisitable(form, submitter) {
4123
- const action = getAction(form, submitter);
5392
+ #formActionIsVisitable(form, submitter) {
5393
+ const action = getAction$1(form, submitter);
4124
5394
  return locationIsVisitable(expandURL(action), this.rootLocation);
4125
5395
  }
4126
- shouldInterceptNavigation(element, submitter) {
5396
+ #shouldInterceptNavigation(element, submitter) {
4127
5397
  const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
4128
- if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
5398
+ if (element instanceof HTMLFormElement && !this.#formActionIsVisitable(element, submitter)) {
4129
5399
  return false;
4130
5400
  }
4131
5401
  if (!this.enabled || id == "_top") {
@@ -4145,6 +5415,7 @@
4145
5415
  }
4146
5416
  return true;
4147
5417
  }
5418
+ // Computed properties
4148
5419
  get id() {
4149
5420
  return this.element.id;
4150
5421
  }
@@ -4157,46 +5428,43 @@
4157
5428
  }
4158
5429
  }
4159
5430
  set sourceURL(sourceURL) {
4160
- this.ignoringChangesToAttribute("src", () => {
4161
- this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
5431
+ this.#ignoringChangesToAttribute("src", () => {
5432
+ this.element.src = sourceURL ?? null;
4162
5433
  });
4163
5434
  }
4164
5435
  get loadingStyle() {
4165
5436
  return this.element.loading;
4166
5437
  }
4167
5438
  get isLoading() {
4168
- return this.formSubmission !== void 0 || this.resolveVisitPromise() !== void 0;
5439
+ return this.formSubmission !== void 0 || this.#resolveVisitPromise() !== void 0;
4169
5440
  }
4170
5441
  get complete() {
4171
5442
  return this.element.hasAttribute("complete");
4172
5443
  }
4173
5444
  set complete(value) {
4174
- this.ignoringChangesToAttribute("complete", () => {
4175
- if (value) {
4176
- this.element.setAttribute("complete", "");
4177
- } else {
4178
- this.element.removeAttribute("complete");
4179
- }
4180
- });
5445
+ if (value) {
5446
+ this.element.setAttribute("complete", "");
5447
+ } else {
5448
+ this.element.removeAttribute("complete");
5449
+ }
4181
5450
  }
4182
5451
  get isActive() {
4183
- return this.element.isActive && this.connected;
5452
+ return this.element.isActive && this.#connected;
4184
5453
  }
4185
5454
  get rootLocation() {
4186
- var _a;
4187
5455
  const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
4188
- const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
5456
+ const root = meta?.content ?? "/";
4189
5457
  return expandURL(root);
4190
5458
  }
4191
- isIgnoringChangesTo(attributeName) {
4192
- return this.ignoredAttributes.has(attributeName);
5459
+ #isIgnoringChangesTo(attributeName) {
5460
+ return this.#ignoredAttributes.has(attributeName);
4193
5461
  }
4194
- ignoringChangesToAttribute(attributeName, callback) {
4195
- this.ignoredAttributes.add(attributeName);
5462
+ #ignoringChangesToAttribute(attributeName, callback) {
5463
+ this.#ignoredAttributes.add(attributeName);
4196
5464
  callback();
4197
- this.ignoredAttributes.delete(attributeName);
5465
+ this.#ignoredAttributes.delete(attributeName);
4198
5466
  }
4199
- withCurrentNavigationElement(element, callback) {
5467
+ #withCurrentNavigationElement(element, callback) {
4200
5468
  this.currentNavigationElement = element;
4201
5469
  callback();
4202
5470
  delete this.currentNavigationElement;
@@ -4226,6 +5494,37 @@
4226
5494
  }
4227
5495
  }
4228
5496
  }
5497
+ var StreamActions = {
5498
+ after() {
5499
+ this.targetElements.forEach((e) => e.parentElement?.insertBefore(this.templateContent, e.nextSibling));
5500
+ },
5501
+ append() {
5502
+ this.removeDuplicateTargetChildren();
5503
+ this.targetElements.forEach((e) => e.append(this.templateContent));
5504
+ },
5505
+ before() {
5506
+ this.targetElements.forEach((e) => e.parentElement?.insertBefore(this.templateContent, e));
5507
+ },
5508
+ prepend() {
5509
+ this.removeDuplicateTargetChildren();
5510
+ this.targetElements.forEach((e) => e.prepend(this.templateContent));
5511
+ },
5512
+ remove() {
5513
+ this.targetElements.forEach((e) => e.remove());
5514
+ },
5515
+ replace() {
5516
+ this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
5517
+ },
5518
+ update() {
5519
+ this.targetElements.forEach((targetElement) => {
5520
+ targetElement.innerHTML = "";
5521
+ targetElement.append(this.templateContent);
5522
+ });
5523
+ },
5524
+ refresh() {
5525
+ session.refresh(this.baseURI, this.requestId);
5526
+ }
5527
+ };
4229
5528
  var StreamElement = class _StreamElement extends HTMLElement {
4230
5529
  static async renderElement(newElement) {
4231
5530
  await newElement.performAction();
@@ -4240,11 +5539,10 @@
4240
5539
  }
4241
5540
  }
4242
5541
  async render() {
4243
- var _a;
4244
- return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : this.renderPromise = (async () => {
5542
+ return this.renderPromise ??= (async () => {
4245
5543
  const event = this.beforeRenderEvent;
4246
5544
  if (this.dispatchEvent(event)) {
4247
- await nextAnimationFrame();
5545
+ await nextRepaint();
4248
5546
  await event.detail.render(this);
4249
5547
  }
4250
5548
  })();
@@ -4252,40 +5550,57 @@
4252
5550
  disconnect() {
4253
5551
  try {
4254
5552
  this.remove();
4255
- } catch (_a) {
5553
+ } catch {
4256
5554
  }
4257
5555
  }
5556
+ /**
5557
+ * Removes duplicate children (by ID)
5558
+ */
4258
5559
  removeDuplicateTargetChildren() {
4259
5560
  this.duplicateChildren.forEach((c) => c.remove());
4260
5561
  }
5562
+ /**
5563
+ * Gets the list of duplicate children (i.e. those with the same ID)
5564
+ */
4261
5565
  get duplicateChildren() {
4262
- var _a;
4263
5566
  const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
4264
- const newChildrenIds = [...((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || []].filter((c) => !!c.id).map((c) => c.id);
5567
+ const newChildrenIds = [...this.templateContent?.children || []].filter((c) => !!c.id).map((c) => c.id);
4265
5568
  return existingChildren.filter((c) => newChildrenIds.includes(c.id));
4266
5569
  }
5570
+ /**
5571
+ * Gets the action function to be performed.
5572
+ */
4267
5573
  get performAction() {
4268
5574
  if (this.action) {
4269
5575
  const actionFunction = StreamActions[this.action];
4270
5576
  if (actionFunction) {
4271
5577
  return actionFunction;
4272
5578
  }
4273
- this.raise("unknown action");
5579
+ this.#raise("unknown action");
4274
5580
  }
4275
- this.raise("action attribute is missing");
5581
+ this.#raise("action attribute is missing");
4276
5582
  }
5583
+ /**
5584
+ * Gets the target elements which the template will be rendered to.
5585
+ */
4277
5586
  get targetElements() {
4278
5587
  if (this.target) {
4279
5588
  return this.targetElementsById;
4280
5589
  } else if (this.targets) {
4281
5590
  return this.targetElementsByQuery;
4282
5591
  } else {
4283
- this.raise("target or targets attribute is missing");
5592
+ this.#raise("target or targets attribute is missing");
4284
5593
  }
4285
5594
  }
5595
+ /**
5596
+ * Gets the contents of the main `<template>`.
5597
+ */
4286
5598
  get templateContent() {
4287
5599
  return this.templateElement.content.cloneNode(true);
4288
5600
  }
5601
+ /**
5602
+ * Gets the main `<template>` used for rendering
5603
+ */
4289
5604
  get templateElement() {
4290
5605
  if (this.firstElementChild === null) {
4291
5606
  const template = this.ownerDocument.createElement("template");
@@ -4294,23 +5609,38 @@
4294
5609
  } else if (this.firstElementChild instanceof HTMLTemplateElement) {
4295
5610
  return this.firstElementChild;
4296
5611
  }
4297
- this.raise("first child element must be a <template> element");
5612
+ this.#raise("first child element must be a <template> element");
4298
5613
  }
5614
+ /**
5615
+ * Gets the current action.
5616
+ */
4299
5617
  get action() {
4300
5618
  return this.getAttribute("action");
4301
5619
  }
5620
+ /**
5621
+ * Gets the current target (an element ID) to which the result will
5622
+ * be rendered.
5623
+ */
4302
5624
  get target() {
4303
5625
  return this.getAttribute("target");
4304
5626
  }
5627
+ /**
5628
+ * Gets the current "targets" selector (a CSS selector)
5629
+ */
4305
5630
  get targets() {
4306
5631
  return this.getAttribute("targets");
4307
5632
  }
4308
- raise(message) {
5633
+ /**
5634
+ * Reads the request-id attribute
5635
+ */
5636
+ get requestId() {
5637
+ return this.getAttribute("request-id");
5638
+ }
5639
+ #raise(message) {
4309
5640
  throw new Error(`${this.description}: ${message}`);
4310
5641
  }
4311
5642
  get description() {
4312
- var _a, _b;
4313
- return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
5643
+ return (this.outerHTML.match(/<[^>]+>/) ?? [])[0] ?? "<turbo-stream>";
4314
5644
  }
4315
5645
  get beforeRenderEvent() {
4316
5646
  return new CustomEvent("turbo:before-stream-render", {
@@ -4320,8 +5650,7 @@
4320
5650
  });
4321
5651
  }
4322
5652
  get targetElementsById() {
4323
- var _a;
4324
- const element = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
5653
+ const element = this.ownerDocument?.getElementById(this.target);
4325
5654
  if (element !== null) {
4326
5655
  return [element];
4327
5656
  } else {
@@ -4329,8 +5658,7 @@
4329
5658
  }
4330
5659
  }
4331
5660
  get targetElementsByQuery() {
4332
- var _a;
4333
- const elements = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.querySelectorAll(this.targets);
5661
+ const elements = this.ownerDocument?.querySelectorAll(this.targets);
4334
5662
  if (elements.length !== 0) {
4335
5663
  return Array.prototype.slice.call(elements);
4336
5664
  } else {
@@ -4339,16 +5667,14 @@
4339
5667
  }
4340
5668
  };
4341
5669
  var StreamSourceElement = class extends HTMLElement {
4342
- constructor() {
4343
- super(...arguments);
4344
- this.streamSource = null;
4345
- }
5670
+ streamSource = null;
4346
5671
  connectedCallback() {
4347
5672
  this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
4348
5673
  connectStreamSource(this.streamSource);
4349
5674
  }
4350
5675
  disconnectedCallback() {
4351
5676
  if (this.streamSource) {
5677
+ this.streamSource.close();
4352
5678
  disconnectStreamSource(this.streamSource);
4353
5679
  }
4354
5680
  }
@@ -4375,7 +5701,8 @@
4375
5701
  element = element.parentElement;
4376
5702
  while (element) {
4377
5703
  if (element == document.body) {
4378
- return console.warn(unindent`
5704
+ return console.warn(
5705
+ unindent`
4379
5706
  You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
4380
5707
 
4381
5708
  Load your application’s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.
@@ -4384,12 +5711,14 @@
4384
5711
 
4385
5712
  ——
4386
5713
  Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
4387
- `, element.outerHTML);
5714
+ `,
5715
+ element.outerHTML
5716
+ );
4388
5717
  }
4389
5718
  element = element.parentElement;
4390
5719
  }
4391
5720
  })();
4392
- window.Turbo = Turbo;
5721
+ window.Turbo = { ...Turbo, StreamActions };
4393
5722
  start();
4394
5723
 
4395
5724
  // node_modules/@hotwired/turbo-rails/app/javascript/turbo/cable.js
@@ -4493,7 +5822,9 @@
4493
5822
  }
4494
5823
  function determineFormMethod(submitter) {
4495
5824
  if (submitter instanceof HTMLButtonElement || submitter instanceof HTMLInputElement) {
4496
- if (submitter.hasAttribute("formmethod")) {
5825
+ if (submitter.name === "_method") {
5826
+ return submitter.value;
5827
+ } else if (submitter.hasAttribute("formmethod")) {
4497
5828
  return submitter.formMethod;
4498
5829
  } else {
4499
5830
  return null;
@@ -4507,6 +5838,7 @@
4507
5838
  }
4508
5839
 
4509
5840
  // node_modules/@hotwired/turbo-rails/app/javascript/turbo/index.js
5841
+ window.Turbo = turbo_es2017_esm_exports;
4510
5842
  addEventListener("turbo:before-fetch-request", encodeMethodIntoRequestBody);
4511
5843
 
4512
5844
  // node_modules/@hotwired/stimulus/dist/stimulus.js
@@ -5093,13 +6425,13 @@
5093
6425
  }
5094
6426
  };
5095
6427
  function add(map, key, value) {
5096
- fetch2(map, key).add(value);
6428
+ fetch(map, key).add(value);
5097
6429
  }
5098
6430
  function del(map, key, value) {
5099
- fetch2(map, key).delete(value);
6431
+ fetch(map, key).delete(value);
5100
6432
  prune(map, key);
5101
6433
  }
5102
- function fetch2(map, key) {
6434
+ function fetch(map, key) {
5103
6435
  let values = map.get(key);
5104
6436
  if (!values) {
5105
6437
  values = /* @__PURE__ */ new Set();
@@ -7119,4 +8451,12 @@
7119
8451
  application.register("emails", emails_controller_default);
7120
8452
  application.register("keys", keys_controller_default);
7121
8453
  })();
8454
+ /*! Bundled license information:
8455
+
8456
+ @hotwired/turbo/dist/turbo.es2017-esm.js:
8457
+ (*!
8458
+ Turbo 8.0.4
8459
+ Copyright © 2024 37signals LLC
8460
+ *)
8461
+ */
7122
8462
  //# sourceMappingURL=application.js.map