masks 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/masks/application.css +1 -1
  3. data/app/assets/builds/masks/application.js +2153 -726
  4. data/app/assets/builds/masks/application.js.map +4 -4
  5. data/app/assets/javascripts/controllers/application.js +1 -1
  6. data/app/assets/javascripts/controllers/index.js +9 -0
  7. data/app/assets/javascripts/controllers/table_controller.js +15 -0
  8. data/app/assets/stylesheets/application.css +12 -4
  9. data/app/controllers/concerns/masks/controller.rb +1 -1
  10. data/app/controllers/masks/manage/actors_controller.rb +72 -1
  11. data/app/controllers/masks/manage/base_controller.rb +10 -2
  12. data/app/controllers/masks/manage/clients_controller.rb +84 -0
  13. data/app/controllers/masks/manage/dashboard_controller.rb +15 -0
  14. data/app/controllers/masks/manage/devices_controller.rb +19 -0
  15. data/app/controllers/masks/openid/authorizations_controller.rb +45 -0
  16. data/app/controllers/masks/openid/discoveries_controller.rb +55 -0
  17. data/app/controllers/masks/openid/tokens_controller.rb +45 -0
  18. data/app/controllers/masks/openid/userinfo_controller.rb +28 -0
  19. data/app/controllers/masks/sessions_controller.rb +1 -1
  20. data/app/models/concerns/masks/access.rb +2 -2
  21. data/app/models/masks/access/actor_password.rb +2 -1
  22. data/app/models/masks/access/actor_signup.rb +1 -2
  23. data/app/models/masks/credentials/access_token.rb +60 -0
  24. data/app/models/masks/credentials/key.rb +1 -1
  25. data/app/models/masks/credentials/return_to.rb +27 -0
  26. data/app/models/masks/mask.rb +12 -1
  27. data/app/models/masks/openid/authorization.rb +116 -0
  28. data/app/models/masks/openid/token.rb +56 -0
  29. data/app/models/masks/rails/actor.rb +23 -1
  30. data/app/models/masks/rails/openid/access_token.rb +55 -0
  31. data/app/models/masks/rails/openid/authorization.rb +45 -0
  32. data/app/models/masks/rails/openid/client.rb +186 -0
  33. data/app/models/masks/rails/openid/id_token.rb +43 -0
  34. data/app/models/masks/sessions/access.rb +2 -1
  35. data/app/resources/masks/session_resource.rb +1 -1
  36. data/app/views/layouts/masks/manage.html.erb +22 -5
  37. data/app/views/masks/actor_mailer/recover_credentials.html.erb +2 -3
  38. data/app/views/masks/actor_mailer/verify_email.html.erb +2 -3
  39. data/app/views/masks/actors/current.html.erb +7 -14
  40. data/app/views/masks/application/_header.html.erb +3 -4
  41. data/app/views/masks/backup_codes/new.html.erb +34 -20
  42. data/app/views/masks/emails/new.html.erb +14 -8
  43. data/app/views/masks/keys/new.html.erb +7 -7
  44. data/app/views/masks/manage/actors/index.html.erb +101 -37
  45. data/app/views/masks/manage/{actor → actors}/show.html.erb +63 -17
  46. data/app/views/masks/manage/clients/index.html.erb +102 -0
  47. data/app/views/masks/manage/clients/show.html.erb +156 -0
  48. data/app/views/masks/manage/dashboard/index.html.erb +10 -0
  49. data/app/views/masks/manage/devices/index.html.erb +47 -0
  50. data/app/views/masks/one_time_code/new.html.erb +41 -24
  51. data/app/views/masks/openid/authorizations/error.html.erb +23 -0
  52. data/app/views/masks/openid/authorizations/new.html.erb +46 -0
  53. data/app/views/masks/passwords/edit.html.erb +20 -7
  54. data/app/views/masks/recoveries/new.html.erb +2 -4
  55. data/app/views/masks/recoveries/password.html.erb +2 -3
  56. data/app/views/masks/sessions/new.html.erb +22 -23
  57. data/config/initializers/inflections.rb +5 -0
  58. data/config/locales/en.yml +23 -2
  59. data/config/routes.rb +40 -3
  60. data/db/migrate/20240329182422_support_openid.rb +64 -0
  61. data/lib/generators/masks/install/templates/masks.json +4 -1
  62. data/lib/masks/configuration.rb +22 -9
  63. data/lib/masks/version.rb +1 -1
  64. data/lib/masks.rb +1 -0
  65. data/lib/tasks/masks_tasks.rake +3 -2
  66. data/masks.json +47 -6
  67. metadata +59 -11
  68. data/app/assets/builds/application.css +0 -4764
  69. data/app/assets/builds/application.js +0 -8236
  70. data/app/assets/builds/application.js.map +0 -7
  71. data/app/controllers/masks/manage/actor_controller.rb +0 -35
@@ -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
  }
@@ -3052,6 +3581,18 @@
3052
3581
  streamSourceIsConnected(source) {
3053
3582
  return this.sources.has(source);
3054
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
+ };
3055
3596
  async receiveMessageResponse(response) {
3056
3597
  const html = await response.responseHTML;
3057
3598
  if (html) {
@@ -3063,15 +3604,13 @@
3063
3604
  }
3064
3605
  };
3065
3606
  function fetchResponseFromEvent(event) {
3066
- var _a;
3067
- const fetchResponse = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchResponse;
3607
+ const fetchResponse = event.detail?.fetchResponse;
3068
3608
  if (fetchResponse instanceof FetchResponse) {
3069
3609
  return fetchResponse;
3070
3610
  }
3071
3611
  }
3072
3612
  function fetchResponseIsStream(response) {
3073
- var _a;
3074
- const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
3613
+ const contentType = response.contentType ?? "";
3075
3614
  return contentType.startsWith(StreamMessage.contentType);
3076
3615
  }
3077
3616
  var ErrorRenderer = class extends Renderer {
@@ -3088,22 +3627,569 @@
3088
3627
  documentElement.replaceChild(this.newHead, head);
3089
3628
  this.renderElement(this.currentElement, this.newElement);
3090
3629
  }
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);
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;
4098
+ }
4099
+ while (stack.length > 0) {
4100
+ let node = stack.pop();
4101
+ added.push(node);
4102
+ morphedNode.parentElement.insertBefore(node, morphedNode);
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;
4114
+ }
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;
3097
4125
  }
4126
+ currentElement = currentElement.nextSibling;
3098
4127
  }
4128
+ return bestElement;
3099
4129
  }
3100
- get newHead() {
3101
- return this.newSnapshot.headSnapshot.element;
4130
+ function scoreElement(node1, node2, ctx) {
4131
+ if (isSoftMatch(node1, node2)) {
4132
+ return 0.5 + getIdIntersectionCount(ctx, node1, node2);
4133
+ }
4134
+ return 0;
3102
4135
  }
3103
- get scriptElements() {
3104
- return document.documentElement.querySelectorAll("script");
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;
4179
+ }
4180
+ }
3105
4181
  }
3106
- };
4182
+ function createIdMap(oldContent, newContent) {
4183
+ let idMap = /* @__PURE__ */ new Map();
4184
+ populateIdMapForNode(oldContent, idMap);
4185
+ populateIdMapForNode(newContent, idMap);
4186
+ return idMap;
4187
+ }
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) {
@@ -3754,7 +5021,7 @@
3754
5021
  function setFormMode(mode) {
3755
5022
  session.setFormMode(mode);
3756
5023
  }
3757
- var Turbo = /* @__PURE__ */ Object.freeze({
5024
+ var Turbo2 = /* @__PURE__ */ Object.freeze({
3758
5025
  __proto__: null,
3759
5026
  navigator: navigator$1,
3760
5027
  session,
@@ -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 = { ...Turbo2, 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();
@@ -6959,9 +8291,79 @@
6959
8291
 
6960
8292
  // app/assets/javascripts/controllers/application.js
6961
8293
  var application = Application.start();
6962
- application.debug = false;
8294
+ application.debug = true;
6963
8295
  window.Stimulus = application;
6964
8296
 
8297
+ // node_modules/@stimulus-components/password-visibility/dist/stimulus-password-visibility.mjs
8298
+ var _PasswordVisibility = class _PasswordVisibility2 extends Controller {
8299
+ connect() {
8300
+ this.hidden = this.inputTarget.type === "password", this.class = this.hasHiddenClass ? this.hiddenClass : "hidden";
8301
+ }
8302
+ toggle(e) {
8303
+ e.preventDefault(), this.inputTarget.type = this.hidden ? "text" : "password", this.hidden = !this.hidden, this.iconTargets.forEach((icon) => icon.classList.toggle(this.class));
8304
+ }
8305
+ };
8306
+ _PasswordVisibility.targets = ["input", "icon"], _PasswordVisibility.classes = ["hidden"];
8307
+ var PasswordVisibility = _PasswordVisibility;
8308
+
8309
+ // node_modules/@stimulus-components/reveal/dist/stimulus-reveal-controller.mjs
8310
+ var _Reveal = class _Reveal2 extends Controller {
8311
+ connect() {
8312
+ this.class = this.hasHiddenClass ? this.hiddenClass : "hidden";
8313
+ }
8314
+ toggle() {
8315
+ this.itemTargets.forEach((item) => {
8316
+ item.classList.toggle(this.class);
8317
+ });
8318
+ }
8319
+ show() {
8320
+ this.itemTargets.forEach((item) => {
8321
+ item.classList.remove(this.class);
8322
+ });
8323
+ }
8324
+ hide() {
8325
+ this.itemTargets.forEach((item) => {
8326
+ item.classList.add(this.class);
8327
+ });
8328
+ }
8329
+ };
8330
+ _Reveal.targets = ["item"], _Reveal.classes = ["hidden"];
8331
+ var Reveal = _Reveal;
8332
+
8333
+ // node_modules/@stimulus-components/dialog/dist/stimulus-dialog.mjs
8334
+ var _Dialog = class _Dialog2 extends Controller {
8335
+ initialize() {
8336
+ this.forceClose = this.forceClose.bind(this);
8337
+ }
8338
+ connect() {
8339
+ this.openValue && this.open(), document.addEventListener("turbo:before-render", this.forceClose);
8340
+ }
8341
+ disconnect() {
8342
+ document.removeEventListener("turbo:before-render", this.forceClose);
8343
+ }
8344
+ open() {
8345
+ this.dialogTarget.showModal();
8346
+ }
8347
+ close() {
8348
+ this.dialogTarget.setAttribute("closing", ""), Promise.all(this.dialogTarget.getAnimations().map((animation) => animation.finished)).then(() => {
8349
+ this.dialogTarget.removeAttribute("closing"), this.dialogTarget.close();
8350
+ });
8351
+ }
8352
+ backdropClose(event) {
8353
+ event.target === this.dialogTarget && this.close();
8354
+ }
8355
+ forceClose() {
8356
+ this.dialogTarget.close();
8357
+ }
8358
+ };
8359
+ _Dialog.targets = ["dialog"], _Dialog.values = {
8360
+ open: {
8361
+ type: Boolean,
8362
+ default: false
8363
+ }
8364
+ };
8365
+ var Dialog = _Dialog;
8366
+
6965
8367
  // app/assets/javascripts/controllers/session_controller.js
6966
8368
  var session_controller_default = class extends Controller {
6967
8369
  static get targets() {
@@ -7112,11 +8514,36 @@
7112
8514
  }
7113
8515
  };
7114
8516
 
8517
+ // app/assets/javascripts/controllers/table_controller.js
8518
+ var table_controller_default = class extends Controller {
8519
+ static get targets() {
8520
+ return ["url"];
8521
+ }
8522
+ get href() {
8523
+ return this.urlTarget.href;
8524
+ }
8525
+ click(e) {
8526
+ Turbo.visit(this.href);
8527
+ }
8528
+ };
8529
+
7115
8530
  // app/assets/javascripts/controllers/index.js
7116
8531
  application.register("session", session_controller_default);
7117
8532
  application.register("recover", recover_controller_default);
7118
8533
  application.register("recover-password", recover_password_controller_default);
7119
8534
  application.register("emails", emails_controller_default);
7120
8535
  application.register("keys", keys_controller_default);
8536
+ application.register("table", table_controller_default);
8537
+ application.register("password-visibility", PasswordVisibility);
8538
+ application.register("reveal", Reveal);
8539
+ application.register("dialog", Dialog);
7121
8540
  })();
8541
+ /*! Bundled license information:
8542
+
8543
+ @hotwired/turbo/dist/turbo.es2017-esm.js:
8544
+ (*!
8545
+ Turbo 8.0.4
8546
+ Copyright © 2024 37signals LLC
8547
+ *)
8548
+ */
7122
8549
  //# sourceMappingURL=application.js.map