@hotwired/turbo 7.1.0 → 7.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/turbo.es2017-esm.js +712 -364
  2. package/dist/turbo.es2017-umd.js +717 -365
  3. package/dist/types/core/bardo.d.ts +7 -2
  4. package/dist/types/core/cache.d.ts +10 -0
  5. package/dist/types/core/drive/error_renderer.d.ts +1 -0
  6. package/dist/types/core/drive/form_submission.d.ts +12 -3
  7. package/dist/types/core/drive/history.d.ts +1 -1
  8. package/dist/types/core/drive/navigator.d.ts +1 -0
  9. package/dist/types/core/drive/page_renderer.d.ts +3 -0
  10. package/dist/types/core/drive/page_view.d.ts +8 -5
  11. package/dist/types/core/drive/preloader.d.ts +14 -0
  12. package/dist/types/core/drive/progress_bar.d.ts +1 -0
  13. package/dist/types/core/drive/visit.d.ts +3 -3
  14. package/dist/types/core/frames/frame_controller.d.ts +26 -16
  15. package/dist/types/core/frames/frame_redirector.d.ts +1 -1
  16. package/dist/types/core/frames/frame_renderer.d.ts +8 -1
  17. package/dist/types/core/frames/frame_view.d.ts +2 -1
  18. package/dist/types/core/frames/link_interceptor.d.ts +1 -1
  19. package/dist/types/core/index.d.ts +11 -2
  20. package/dist/types/core/native/adapter.d.ts +2 -1
  21. package/dist/types/core/native/browser_adapter.d.ts +17 -8
  22. package/dist/types/core/renderer.d.ts +12 -4
  23. package/dist/types/core/session.d.ts +68 -16
  24. package/dist/types/core/snapshot.d.ts +2 -1
  25. package/dist/types/core/view.d.ts +12 -6
  26. package/dist/types/elements/frame_element.d.ts +5 -1
  27. package/dist/types/elements/stream_element.d.ts +1 -0
  28. package/dist/types/elements/stream_source_element.d.ts +7 -0
  29. package/dist/types/http/fetch_request.d.ts +8 -0
  30. package/dist/types/observers/cache_observer.d.ts +1 -1
  31. package/dist/types/observers/form_link_interceptor.d.ts +14 -0
  32. package/dist/types/observers/link_click_observer.d.ts +2 -2
  33. package/dist/types/polyfills/submit-event.d.ts +1 -7
  34. package/dist/types/tests/functional/async_script_tests.d.ts +1 -6
  35. package/dist/types/tests/functional/autofocus_tests.d.ts +1 -9
  36. package/dist/types/tests/functional/cache_observer_tests.d.ts +1 -5
  37. package/dist/types/tests/functional/drive_disabled_tests.d.ts +1 -9
  38. package/dist/types/tests/functional/drive_tests.d.ts +1 -8
  39. package/dist/types/tests/functional/form_submission_tests.d.ts +1 -84
  40. package/dist/types/tests/functional/frame_navigation_tests.d.ts +1 -7
  41. package/dist/types/tests/functional/frame_tests.d.ts +1 -52
  42. package/dist/types/tests/functional/import_tests.d.ts +1 -4
  43. package/dist/types/tests/functional/loading_tests.d.ts +1 -13
  44. package/dist/types/tests/functional/navigation_tests.d.ts +1 -38
  45. package/dist/types/tests/functional/pausable_rendering_tests.d.ts +1 -6
  46. package/dist/types/tests/functional/pausable_requests_tests.d.ts +1 -6
  47. package/dist/types/tests/functional/preloader_tests.d.ts +1 -0
  48. package/dist/types/tests/functional/rendering_tests.d.ts +1 -35
  49. package/dist/types/tests/functional/scroll_restoration_tests.d.ts +1 -6
  50. package/dist/types/tests/functional/stream_tests.d.ts +1 -6
  51. package/dist/types/tests/functional/visit_tests.d.ts +1 -15
  52. package/dist/types/tests/helpers/page.d.ts +44 -0
  53. package/dist/types/tests/unit/deprecated_adapter_support_test.d.ts +10 -10
  54. package/dist/types/util.d.ts +6 -3
  55. package/package.json +22 -8
  56. package/CHANGELOG.md +0 -3
  57. package/dist/types/tests/functional/index.d.ts +0 -17
  58. package/dist/types/tests/helpers/functional_test_case.d.ts +0 -44
  59. package/dist/types/tests/helpers/remote_channel.d.ts +0 -10
  60. package/dist/types/tests/helpers/turbo_drive_test_case.d.ts +0 -21
@@ -1,26 +1,26 @@
1
1
  /*
2
- Turbo 7.1.0
3
- Copyright © 2021 Basecamp, LLC
2
+ Turbo 7.2.0-beta.1
3
+ Copyright © 2022 Basecamp, LLC
4
4
  */
5
5
  (function (global, factory) {
6
6
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
7
7
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
8
8
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Turbo = {}));
9
- }(this, (function (exports) { 'use strict';
9
+ })(this, (function (exports) { 'use strict';
10
10
 
11
11
  (function () {
12
- if (window.Reflect === undefined || window.customElements === undefined ||
12
+ if (window.Reflect === undefined ||
13
+ window.customElements === undefined ||
13
14
  window.customElements.polyfillWrapFlushCallback) {
14
15
  return;
15
16
  }
16
17
  const BuiltInHTMLElement = HTMLElement;
17
18
  const wrapperForTheName = {
18
- 'HTMLElement': function HTMLElement() {
19
+ HTMLElement: function HTMLElement() {
19
20
  return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
20
- }
21
+ },
21
22
  };
22
- window.HTMLElement =
23
- wrapperForTheName['HTMLElement'];
23
+ window.HTMLElement = wrapperForTheName["HTMLElement"];
24
24
  HTMLElement.prototype = BuiltInHTMLElement.prototype;
25
25
  HTMLElement.prototype.constructor = HTMLElement;
26
26
  Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
@@ -78,7 +78,7 @@ Copyright © 2021 Basecamp, LLC
78
78
  }
79
79
  })(HTMLFormElement.prototype);
80
80
 
81
- const submittersByForm = new WeakMap;
81
+ const submittersByForm = new WeakMap();
82
82
  function findSubmitterFromClickTarget(target) {
83
83
  const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
84
84
  const candidate = element ? element.closest("input, button") : null;
@@ -109,7 +109,7 @@ Copyright © 2021 Basecamp, LLC
109
109
  if (this.type == "submit" && this.target instanceof HTMLFormElement) {
110
110
  return submittersByForm.get(this.target);
111
111
  }
112
- }
112
+ },
113
113
  });
114
114
  })();
115
115
 
@@ -125,7 +125,7 @@ Copyright © 2021 Basecamp, LLC
125
125
  this.delegate = new FrameElement.delegateConstructor(this);
126
126
  }
127
127
  static get observedAttributes() {
128
- return ["disabled", "loading", "src"];
128
+ return ["disabled", "complete", "loading", "src"];
129
129
  }
130
130
  connectedCallback() {
131
131
  this.delegate.connect();
@@ -135,6 +135,7 @@ Copyright © 2021 Basecamp, LLC
135
135
  }
136
136
  reload() {
137
137
  const { src } = this;
138
+ this.removeAttribute("complete");
138
139
  this.src = null;
139
140
  this.src = src;
140
141
  }
@@ -142,6 +143,9 @@ Copyright © 2021 Basecamp, LLC
142
143
  if (name == "loading") {
143
144
  this.delegate.loadingStyleChanged();
144
145
  }
146
+ else if (name == "complete") {
147
+ this.delegate.completeChanged();
148
+ }
145
149
  else if (name == "src") {
146
150
  this.delegate.sourceURLChanged();
147
151
  }
@@ -206,8 +210,10 @@ Copyright © 2021 Basecamp, LLC
206
210
  }
207
211
  function frameLoadingStyleFromString(style) {
208
212
  switch (style.toLowerCase()) {
209
- case "lazy": return FrameLoadingStyle.lazy;
210
- default: return FrameLoadingStyle.eager;
213
+ case "lazy":
214
+ return FrameLoadingStyle.lazy;
215
+ default:
216
+ return FrameLoadingStyle.eager;
211
217
  }
212
218
  }
213
219
 
@@ -219,7 +225,7 @@ Copyright © 2021 Basecamp, LLC
219
225
  if (url.hash) {
220
226
  return url.hash.slice(1);
221
227
  }
222
- else if (anchorMatch = url.href.match(/#(.*)$/)) {
228
+ else if ((anchorMatch = url.href.match(/#(.*)$/))) {
223
229
  return anchorMatch[1];
224
230
  }
225
231
  }
@@ -231,7 +237,7 @@ Copyright © 2021 Basecamp, LLC
231
237
  return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
232
238
  }
233
239
  function isHTML(url) {
234
- return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml))$/);
240
+ return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
235
241
  }
236
242
  function isPrefixedBy(baseURL, url) {
237
243
  const prefix = getPrefix(url);
@@ -242,9 +248,7 @@ Copyright © 2021 Basecamp, LLC
242
248
  }
243
249
  function getRequestURL(url) {
244
250
  const anchor = getAnchor(url);
245
- return anchor != null
246
- ? url.href.slice(0, -(anchor.length + 1))
247
- : url.href;
251
+ return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
248
252
  }
249
253
  function toCacheKey(url) {
250
254
  return getRequestURL(url);
@@ -313,7 +317,11 @@ Copyright © 2021 Basecamp, LLC
313
317
  }
314
318
 
315
319
  function dispatch(eventName, { target, cancelable, detail } = {}) {
316
- const event = new CustomEvent(eventName, { cancelable, bubbles: true, detail });
320
+ const event = new CustomEvent(eventName, {
321
+ cancelable,
322
+ bubbles: true,
323
+ detail,
324
+ });
317
325
  if (target && target.isConnected) {
318
326
  target.dispatchEvent(event);
319
327
  }
@@ -323,10 +331,10 @@ Copyright © 2021 Basecamp, LLC
323
331
  return event;
324
332
  }
325
333
  function nextAnimationFrame() {
326
- return new Promise(resolve => requestAnimationFrame(() => resolve()));
334
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
327
335
  }
328
336
  function nextEventLoopTick() {
329
- return new Promise(resolve => setTimeout(() => resolve(), 0));
337
+ return new Promise((resolve) => setTimeout(() => resolve(), 0));
330
338
  }
331
339
  function nextMicrotask() {
332
340
  return Promise.resolve();
@@ -338,7 +346,7 @@ Copyright © 2021 Basecamp, LLC
338
346
  const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
339
347
  const match = lines[0].match(/^\s+/);
340
348
  const indent = match ? match[0].length : 0;
341
- return lines.map(line => line.slice(indent)).join("\n");
349
+ return lines.map((line) => line.slice(indent)).join("\n");
342
350
  }
343
351
  function interpolate(strings, values) {
344
352
  return strings.reduce((result, string, i) => {
@@ -347,7 +355,8 @@ Copyright © 2021 Basecamp, LLC
347
355
  }, "");
348
356
  }
349
357
  function uuid() {
350
- return Array.apply(null, { length: 36 }).map((_, i) => {
358
+ return Array.from({ length: 36 })
359
+ .map((_, i) => {
351
360
  if (i == 8 || i == 13 || i == 18 || i == 23) {
352
361
  return "-";
353
362
  }
@@ -360,10 +369,11 @@ Copyright © 2021 Basecamp, LLC
360
369
  else {
361
370
  return Math.floor(Math.random() * 15).toString(16);
362
371
  }
363
- }).join("");
372
+ })
373
+ .join("");
364
374
  }
365
375
  function getAttribute(attributeName, ...elements) {
366
- for (const value of elements.map(element => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
376
+ for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
367
377
  if (typeof value == "string")
368
378
  return value;
369
379
  }
@@ -385,6 +395,23 @@ Copyright © 2021 Basecamp, LLC
385
395
  element.removeAttribute("aria-busy");
386
396
  }
387
397
  }
398
+ function getMetaElement(name) {
399
+ return document.querySelector(`meta[name="${name}"]`);
400
+ }
401
+ function getMetaContent(name) {
402
+ const element = getMetaElement(name);
403
+ return element && element.content;
404
+ }
405
+ function setMetaContent(name, content) {
406
+ let element = getMetaElement(name);
407
+ if (!element) {
408
+ element = document.createElement("meta");
409
+ element.setAttribute("name", name);
410
+ document.head.appendChild(element);
411
+ }
412
+ element.setAttribute("content", content);
413
+ return element;
414
+ }
388
415
 
389
416
  var FetchMethod;
390
417
  (function (FetchMethod) {
@@ -396,17 +423,22 @@ Copyright © 2021 Basecamp, LLC
396
423
  })(FetchMethod || (FetchMethod = {}));
397
424
  function fetchMethodFromString(method) {
398
425
  switch (method.toLowerCase()) {
399
- case "get": return FetchMethod.get;
400
- case "post": return FetchMethod.post;
401
- case "put": return FetchMethod.put;
402
- case "patch": return FetchMethod.patch;
403
- case "delete": return FetchMethod.delete;
426
+ case "get":
427
+ return FetchMethod.get;
428
+ case "post":
429
+ return FetchMethod.post;
430
+ case "put":
431
+ return FetchMethod.put;
432
+ case "patch":
433
+ return FetchMethod.patch;
434
+ case "delete":
435
+ return FetchMethod.delete;
404
436
  }
405
437
  }
406
438
  class FetchRequest {
407
- constructor(delegate, method, location, body = new URLSearchParams, target = null) {
408
- this.abortController = new AbortController;
409
- this.resolveRequestPromise = (value) => { };
439
+ constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
440
+ this.abortController = new AbortController();
441
+ this.resolveRequestPromise = (_value) => { };
410
442
  this.delegate = delegate;
411
443
  this.method = method;
412
444
  this.headers = this.defaultHeaders;
@@ -437,7 +469,7 @@ Copyright © 2021 Basecamp, LLC
437
469
  return await this.receive(response);
438
470
  }
439
471
  catch (error) {
440
- if (error.name !== 'AbortError') {
472
+ if (error.name !== "AbortError") {
441
473
  this.delegate.requestErrored(this, error);
442
474
  throw error;
443
475
  }
@@ -448,7 +480,11 @@ Copyright © 2021 Basecamp, LLC
448
480
  }
449
481
  async receive(response) {
450
482
  const fetchResponse = new FetchResponse(response);
451
- const event = dispatch("turbo:before-fetch-response", { cancelable: true, detail: { fetchResponse }, target: this.target });
483
+ const event = dispatch("turbo:before-fetch-response", {
484
+ cancelable: true,
485
+ detail: { fetchResponse },
486
+ target: this.target,
487
+ });
452
488
  if (event.defaultPrevented) {
453
489
  this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
454
490
  }
@@ -469,12 +505,12 @@ Copyright © 2021 Basecamp, LLC
469
505
  redirect: "follow",
470
506
  body: this.isIdempotent ? null : this.body,
471
507
  signal: this.abortSignal,
472
- referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
508
+ referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
473
509
  };
474
510
  }
475
511
  get defaultHeaders() {
476
512
  return {
477
- "Accept": "text/html, application/xhtml+xml"
513
+ Accept: "text/html, application/xhtml+xml",
478
514
  };
479
515
  }
480
516
  get isIdempotent() {
@@ -484,15 +520,15 @@ Copyright © 2021 Basecamp, LLC
484
520
  return this.abortController.signal;
485
521
  }
486
522
  async allowRequestToBeIntercepted(fetchOptions) {
487
- const requestInterception = new Promise(resolve => this.resolveRequestPromise = resolve);
523
+ const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
488
524
  const event = dispatch("turbo:before-fetch-request", {
489
525
  cancelable: true,
490
526
  detail: {
491
527
  fetchOptions,
492
528
  url: this.url,
493
- resume: this.resolveRequestPromise
529
+ resume: this.resolveRequestPromise,
494
530
  },
495
- target: this.target
531
+ target: this.target,
496
532
  });
497
533
  if (event.defaultPrevented)
498
534
  await requestInterception;
@@ -502,7 +538,7 @@ Copyright © 2021 Basecamp, LLC
502
538
  class AppearanceObserver {
503
539
  constructor(delegate, element) {
504
540
  this.started = false;
505
- this.intersect = entries => {
541
+ this.intersect = (entries) => {
506
542
  const lastEntry = entries.slice(-1)[0];
507
543
  if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
508
544
  this.delegate.elementAppearedInViewport(this.element);
@@ -579,9 +615,12 @@ Copyright © 2021 Basecamp, LLC
579
615
  })(FormEnctype || (FormEnctype = {}));
580
616
  function formEnctypeFromString(encoding) {
581
617
  switch (encoding.toLowerCase()) {
582
- case FormEnctype.multipart: return FormEnctype.multipart;
583
- case FormEnctype.plain: return FormEnctype.plain;
584
- default: return FormEnctype.urlEncoded;
618
+ case FormEnctype.multipart:
619
+ return FormEnctype.multipart;
620
+ case FormEnctype.plain:
621
+ return FormEnctype.plain;
622
+ default:
623
+ return FormEnctype.urlEncoded;
585
624
  }
586
625
  }
587
626
  class FormSubmission {
@@ -598,8 +637,8 @@ Copyright © 2021 Basecamp, LLC
598
637
  this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
599
638
  this.mustRedirect = mustRedirect;
600
639
  }
601
- static confirmMethod(message, element) {
602
- return confirm(message);
640
+ static confirmMethod(message, _element) {
641
+ return Promise.resolve(confirm(message));
603
642
  }
604
643
  get method() {
605
644
  var _a;
@@ -608,8 +647,13 @@ Copyright © 2021 Basecamp, LLC
608
647
  }
609
648
  get action() {
610
649
  var _a;
611
- const formElementAction = typeof this.formElement.action === 'string' ? this.formElement.action : null;
612
- return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
650
+ const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
651
+ if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
652
+ return this.submitter.getAttribute("formaction") || "";
653
+ }
654
+ else {
655
+ return this.formElement.getAttribute("action") || formElementAction || "";
656
+ }
613
657
  }
614
658
  get body() {
615
659
  if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
@@ -632,7 +676,8 @@ Copyright © 2021 Basecamp, LLC
632
676
  }, []);
633
677
  }
634
678
  get confirmationMessage() {
635
- return this.formElement.getAttribute("data-turbo-confirm");
679
+ var _a;
680
+ return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-confirm")) || this.formElement.getAttribute("data-turbo-confirm");
636
681
  }
637
682
  get needsConfirmation() {
638
683
  return this.confirmationMessage !== null;
@@ -640,7 +685,7 @@ Copyright © 2021 Basecamp, LLC
640
685
  async start() {
641
686
  const { initialized, requesting } = FormSubmissionState;
642
687
  if (this.needsConfirmation) {
643
- const answer = FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
688
+ const answer = await FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
644
689
  if (!answer) {
645
690
  return;
646
691
  }
@@ -664,14 +709,19 @@ Copyright © 2021 Basecamp, LLC
664
709
  if (token) {
665
710
  headers["X-CSRF-Token"] = token;
666
711
  }
712
+ }
713
+ if (this.requestAcceptsTurboStreamResponse(request)) {
667
714
  headers["Accept"] = [StreamMessage.contentType, headers["Accept"]].join(", ");
668
715
  }
669
716
  }
670
- requestStarted(request) {
717
+ requestStarted(_request) {
671
718
  var _a;
672
719
  this.state = FormSubmissionState.waiting;
673
720
  (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
674
- dispatch("turbo:submit-start", { target: this.formElement, detail: { formSubmission: this } });
721
+ dispatch("turbo:submit-start", {
722
+ target: this.formElement,
723
+ detail: { formSubmission: this },
724
+ });
675
725
  this.delegate.formSubmissionStarted(this);
676
726
  }
677
727
  requestPreventedHandlingResponse(request, response) {
@@ -699,16 +749,22 @@ Copyright © 2021 Basecamp, LLC
699
749
  this.result = { success: false, error };
700
750
  this.delegate.formSubmissionErrored(this, error);
701
751
  }
702
- requestFinished(request) {
752
+ requestFinished(_request) {
703
753
  var _a;
704
754
  this.state = FormSubmissionState.stopped;
705
755
  (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
706
- dispatch("turbo:submit-end", { target: this.formElement, detail: Object.assign({ formSubmission: this }, this.result) });
756
+ dispatch("turbo:submit-end", {
757
+ target: this.formElement,
758
+ detail: Object.assign({ formSubmission: this }, this.result),
759
+ });
707
760
  this.delegate.formSubmissionFinished(this);
708
761
  }
709
762
  requestMustRedirect(request) {
710
763
  return !request.isIdempotent && this.mustRedirect;
711
764
  }
765
+ requestAcceptsTurboStreamResponse(request) {
766
+ return !request.isIdempotent || this.formElement.hasAttribute("data-turbo-stream");
767
+ }
712
768
  }
713
769
  function buildFormData(formElement, submitter) {
714
770
  const formData = new FormData(formElement);
@@ -729,15 +785,11 @@ Copyright © 2021 Basecamp, LLC
729
785
  }
730
786
  }
731
787
  }
732
- function getMetaContent(name) {
733
- const element = document.querySelector(`meta[name="${name}"]`);
734
- return element && element.content;
735
- }
736
788
  function responseSucceededWithoutRedirect(response) {
737
789
  return response.statusCode == 200 && !response.redirected;
738
790
  }
739
791
  function mergeFormDataEntries(url, entries) {
740
- const searchParams = new URLSearchParams;
792
+ const searchParams = new URLSearchParams();
741
793
  for (const [name, value] of entries) {
742
794
  if (value instanceof File)
743
795
  continue;
@@ -751,6 +803,9 @@ Copyright © 2021 Basecamp, LLC
751
803
  constructor(element) {
752
804
  this.element = element;
753
805
  }
806
+ get activeElement() {
807
+ return this.element.ownerDocument.activeElement;
808
+ }
754
809
  get children() {
755
810
  return [...this.element.children];
756
811
  }
@@ -789,7 +844,9 @@ Copyright © 2021 Basecamp, LLC
789
844
  constructor(delegate, element) {
790
845
  this.submitBubbled = ((event) => {
791
846
  const form = event.target;
792
- if (!event.defaultPrevented && form instanceof HTMLFormElement && form.closest("turbo-frame, html") == this.element) {
847
+ if (!event.defaultPrevented &&
848
+ form instanceof HTMLFormElement &&
849
+ form.closest("turbo-frame, html") == this.element) {
793
850
  const submitter = event.submitter || undefined;
794
851
  const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
795
852
  if (method != "dialog" && this.delegate.shouldInterceptFormSubmission(form, submitter)) {
@@ -812,8 +869,8 @@ Copyright © 2021 Basecamp, LLC
812
869
 
813
870
  class View {
814
871
  constructor(delegate, element) {
815
- this.resolveRenderPromise = (value) => { };
816
- this.resolveInterceptionPromise = (value) => { };
872
+ this.resolveRenderPromise = (_value) => { };
873
+ this.resolveInterceptionPromise = (_value) => { };
817
874
  this.delegate = delegate;
818
875
  this.element = element;
819
876
  }
@@ -858,15 +915,17 @@ Copyright © 2021 Basecamp, LLC
858
915
  const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
859
916
  if (shouldRender) {
860
917
  try {
861
- this.renderPromise = new Promise(resolve => this.resolveRenderPromise = resolve);
918
+ this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
862
919
  this.renderer = renderer;
863
920
  this.prepareToRenderSnapshot(renderer);
864
- const renderInterception = new Promise(resolve => this.resolveInterceptionPromise = resolve);
865
- const immediateRender = this.delegate.allowsImmediateRender(snapshot, this.resolveInterceptionPromise);
921
+ const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
922
+ const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
923
+ const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
866
924
  if (!immediateRender)
867
925
  await renderInterception;
868
926
  await this.renderSnapshot(renderer);
869
927
  this.delegate.viewRenderedSnapshot(snapshot, isPreview);
928
+ this.delegate.preloadOnLoadLinksForView(this.element);
870
929
  this.finishRenderingSnapshot(renderer);
871
930
  }
872
931
  finally {
@@ -876,11 +935,11 @@ Copyright © 2021 Basecamp, LLC
876
935
  }
877
936
  }
878
937
  else {
879
- this.invalidate();
938
+ this.invalidate(renderer.reloadReason);
880
939
  }
881
940
  }
882
- invalidate() {
883
- this.delegate.viewInvalidated();
941
+ invalidate(reason) {
942
+ this.delegate.viewInvalidated(reason);
884
943
  }
885
944
  prepareToRenderSnapshot(renderer) {
886
945
  this.markAsPreview(renderer.isPreview);
@@ -931,9 +990,9 @@ Copyright © 2021 Basecamp, LLC
931
990
  }
932
991
  delete this.clickEvent;
933
992
  });
934
- this.willVisit = () => {
993
+ this.willVisit = ((_event) => {
935
994
  delete this.clickEvent;
936
- };
995
+ });
937
996
  this.delegate = delegate;
938
997
  this.element = element;
939
998
  }
@@ -948,28 +1007,65 @@ Copyright © 2021 Basecamp, LLC
948
1007
  document.removeEventListener("turbo:before-visit", this.willVisit);
949
1008
  }
950
1009
  respondsToEventTarget(target) {
951
- const element = target instanceof Element
952
- ? target
953
- : target instanceof Node
954
- ? target.parentElement
955
- : null;
1010
+ const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
956
1011
  return element && element.closest("turbo-frame, html") == this.element;
957
1012
  }
958
1013
  }
959
1014
 
1015
+ class FormLinkInterceptor {
1016
+ constructor(delegate, element) {
1017
+ this.delegate = delegate;
1018
+ this.linkInterceptor = new LinkInterceptor(this, element);
1019
+ }
1020
+ start() {
1021
+ this.linkInterceptor.start();
1022
+ }
1023
+ stop() {
1024
+ this.linkInterceptor.stop();
1025
+ }
1026
+ shouldInterceptLinkClick(link) {
1027
+ return (this.delegate.shouldInterceptFormLinkClick(link) &&
1028
+ (link.hasAttribute("data-turbo-method") || link.hasAttribute("data-turbo-stream")));
1029
+ }
1030
+ linkClickIntercepted(link, action) {
1031
+ const form = document.createElement("form");
1032
+ form.setAttribute("data-turbo", "true");
1033
+ form.setAttribute("action", action);
1034
+ form.setAttribute("hidden", "");
1035
+ const method = link.getAttribute("data-turbo-method");
1036
+ if (method)
1037
+ form.setAttribute("method", method);
1038
+ const turboFrame = link.getAttribute("data-turbo-frame");
1039
+ if (turboFrame)
1040
+ form.setAttribute("data-turbo-frame", turboFrame);
1041
+ const turboConfirm = link.getAttribute("data-turbo-confirm");
1042
+ if (turboConfirm)
1043
+ form.setAttribute("data-turbo-confirm", turboConfirm);
1044
+ const turboStream = link.hasAttribute("data-turbo-stream");
1045
+ if (turboStream)
1046
+ form.setAttribute("data-turbo-stream", "");
1047
+ this.delegate.formLinkClickIntercepted(link, form);
1048
+ document.body.appendChild(form);
1049
+ form.requestSubmit();
1050
+ form.remove();
1051
+ }
1052
+ }
1053
+
960
1054
  class Bardo {
961
- constructor(permanentElementMap) {
1055
+ constructor(delegate, permanentElementMap) {
1056
+ this.delegate = delegate;
962
1057
  this.permanentElementMap = permanentElementMap;
963
1058
  }
964
- static preservingPermanentElements(permanentElementMap, callback) {
965
- const bardo = new this(permanentElementMap);
1059
+ static preservingPermanentElements(delegate, permanentElementMap, callback) {
1060
+ const bardo = new this(delegate, permanentElementMap);
966
1061
  bardo.enter();
967
1062
  callback();
968
1063
  bardo.leave();
969
1064
  }
970
1065
  enter() {
971
1066
  for (const id in this.permanentElementMap) {
972
- const [, newPermanentElement] = this.permanentElementMap[id];
1067
+ const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
1068
+ this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
973
1069
  this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
974
1070
  }
975
1071
  }
@@ -978,6 +1074,7 @@ Copyright © 2021 Basecamp, LLC
978
1074
  const [currentPermanentElement] = this.permanentElementMap[id];
979
1075
  this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
980
1076
  this.replacePlaceholderWithPermanentElement(currentPermanentElement);
1077
+ this.delegate.leavingBardo(currentPermanentElement);
981
1078
  }
982
1079
  }
983
1080
  replaceNewPermanentElementWithPlaceholder(permanentElement) {
@@ -993,7 +1090,7 @@ Copyright © 2021 Basecamp, LLC
993
1090
  placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
994
1091
  }
995
1092
  getPlaceholderById(id) {
996
- return this.placeholders.find(element => element.content == id);
1093
+ return this.placeholders.find((element) => element.content == id);
997
1094
  }
998
1095
  get placeholders() {
999
1096
  return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
@@ -1007,16 +1104,21 @@ Copyright © 2021 Basecamp, LLC
1007
1104
  }
1008
1105
 
1009
1106
  class Renderer {
1010
- constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
1107
+ constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1108
+ this.activeElement = null;
1011
1109
  this.currentSnapshot = currentSnapshot;
1012
1110
  this.newSnapshot = newSnapshot;
1013
1111
  this.isPreview = isPreview;
1014
1112
  this.willRender = willRender;
1015
- this.promise = new Promise((resolve, reject) => this.resolvingFunctions = { resolve, reject });
1113
+ this.renderElement = renderElement;
1114
+ this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
1016
1115
  }
1017
1116
  get shouldRender() {
1018
1117
  return true;
1019
1118
  }
1119
+ get reloadReason() {
1120
+ return;
1121
+ }
1020
1122
  prepareToRender() {
1021
1123
  return;
1022
1124
  }
@@ -1042,7 +1144,7 @@ Copyright © 2021 Basecamp, LLC
1042
1144
  }
1043
1145
  }
1044
1146
  preservingPermanentElements(callback) {
1045
- Bardo.preservingPermanentElements(this.permanentElementMap, callback);
1147
+ Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
1046
1148
  }
1047
1149
  focusFirstAutofocusableElement() {
1048
1150
  const element = this.connectedSnapshot.firstAutofocusableElement;
@@ -1050,6 +1152,19 @@ Copyright © 2021 Basecamp, LLC
1050
1152
  element.focus();
1051
1153
  }
1052
1154
  }
1155
+ enteringBardo(currentPermanentElement) {
1156
+ if (this.activeElement)
1157
+ return;
1158
+ if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
1159
+ this.activeElement = this.currentSnapshot.activeElement;
1160
+ }
1161
+ }
1162
+ leavingBardo(currentPermanentElement) {
1163
+ if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
1164
+ this.activeElement.focus();
1165
+ this.activeElement = null;
1166
+ }
1167
+ }
1053
1168
  get connectedSnapshot() {
1054
1169
  return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
1055
1170
  }
@@ -1063,8 +1178,7 @@ Copyright © 2021 Basecamp, LLC
1063
1178
  return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
1064
1179
  }
1065
1180
  get cspNonce() {
1066
- var _a;
1067
- return (_a = document.head.querySelector('meta[name="csp-nonce"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
1181
+ return getMetaContent("csp-nonce");
1068
1182
  }
1069
1183
  }
1070
1184
  function copyElementAttributes(destinationElement, sourceElement) {
@@ -1077,6 +1191,22 @@ Copyright © 2021 Basecamp, LLC
1077
1191
  }
1078
1192
 
1079
1193
  class FrameRenderer extends Renderer {
1194
+ constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1195
+ super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
1196
+ this.delegate = delegate;
1197
+ }
1198
+ static renderElement(currentElement, newElement) {
1199
+ var _a;
1200
+ const destinationRange = document.createRange();
1201
+ destinationRange.selectNodeContents(currentElement);
1202
+ destinationRange.deleteContents();
1203
+ const frameElement = newElement;
1204
+ const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1205
+ if (sourceRange) {
1206
+ sourceRange.selectNodeContents(frameElement);
1207
+ currentElement.appendChild(sourceRange.extractContents());
1208
+ }
1209
+ }
1080
1210
  get shouldRender() {
1081
1211
  return true;
1082
1212
  }
@@ -1092,23 +1222,16 @@ Copyright © 2021 Basecamp, LLC
1092
1222
  this.activateScriptElements();
1093
1223
  }
1094
1224
  loadFrameElement() {
1095
- var _a;
1096
- const destinationRange = document.createRange();
1097
- destinationRange.selectNodeContents(this.currentElement);
1098
- destinationRange.deleteContents();
1099
- const frameElement = this.newElement;
1100
- const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1101
- if (sourceRange) {
1102
- sourceRange.selectNodeContents(frameElement);
1103
- this.currentElement.appendChild(sourceRange.extractContents());
1104
- }
1225
+ this.delegate.frameExtracted(this.newElement.cloneNode(true));
1226
+ this.renderElement(this.currentElement, this.newElement);
1105
1227
  }
1106
1228
  scrollFrameIntoView() {
1107
1229
  if (this.currentElement.autoscroll || this.newElement.autoscroll) {
1108
1230
  const element = this.currentElement.firstElementChild;
1109
1231
  const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
1232
+ const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
1110
1233
  if (element) {
1111
- element.scrollIntoView({ block });
1234
+ element.scrollIntoView({ block, behavior });
1112
1235
  return true;
1113
1236
  }
1114
1237
  }
@@ -1132,6 +1255,14 @@ Copyright © 2021 Basecamp, LLC
1132
1255
  return defaultValue;
1133
1256
  }
1134
1257
  }
1258
+ function readScrollBehavior(value, defaultValue) {
1259
+ if (value == "auto" || value == "smooth") {
1260
+ return value;
1261
+ }
1262
+ else {
1263
+ return defaultValue;
1264
+ }
1265
+ }
1135
1266
 
1136
1267
  class ProgressBar {
1137
1268
  constructor() {
@@ -1155,7 +1286,7 @@ Copyright © 2021 Basecamp, LLC
1155
1286
  left: 0;
1156
1287
  height: 3px;
1157
1288
  background: #0076ff;
1158
- z-index: 9999;
1289
+ z-index: 2147483647;
1159
1290
  transition:
1160
1291
  width ${ProgressBar.animationDuration}ms ease-out,
1161
1292
  opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
@@ -1214,13 +1345,16 @@ Copyright © 2021 Basecamp, LLC
1214
1345
  }
1215
1346
  refresh() {
1216
1347
  requestAnimationFrame(() => {
1217
- this.progressElement.style.width = `${10 + (this.value * 90)}%`;
1348
+ this.progressElement.style.width = `${10 + this.value * 90}%`;
1218
1349
  });
1219
1350
  }
1220
1351
  createStylesheetElement() {
1221
1352
  const element = document.createElement("style");
1222
1353
  element.type = "text/css";
1223
1354
  element.textContent = ProgressBar.defaultCSS;
1355
+ if (this.cspNonce) {
1356
+ element.nonce = this.cspNonce;
1357
+ }
1224
1358
  return element;
1225
1359
  }
1226
1360
  createProgressElement() {
@@ -1228,6 +1362,9 @@ Copyright © 2021 Basecamp, LLC
1228
1362
  element.className = "turbo-progress-bar";
1229
1363
  return element;
1230
1364
  }
1365
+ get cspNonce() {
1366
+ return getMetaContent("csp-nonce");
1367
+ }
1231
1368
  }
1232
1369
  ProgressBar.animationDuration = 300;
1233
1370
 
@@ -1244,14 +1381,14 @@ Copyright © 2021 Basecamp, LLC
1244
1381
  : {
1245
1382
  type: elementType(element),
1246
1383
  tracked: elementIsTracked(element),
1247
- elements: []
1384
+ elements: [],
1248
1385
  };
1249
1386
  return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
1250
1387
  }, {});
1251
1388
  }
1252
1389
  get trackedElementSignature() {
1253
1390
  return Object.keys(this.detailsByOuterHTML)
1254
- .filter(outerHTML => this.detailsByOuterHTML[outerHTML].tracked)
1391
+ .filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
1255
1392
  .join("");
1256
1393
  }
1257
1394
  getScriptElementsNotInSnapshot(snapshot) {
@@ -1262,8 +1399,8 @@ Copyright © 2021 Basecamp, LLC
1262
1399
  }
1263
1400
  getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
1264
1401
  return Object.keys(this.detailsByOuterHTML)
1265
- .filter(outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))
1266
- .map(outerHTML => this.detailsByOuterHTML[outerHTML])
1402
+ .filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
1403
+ .map((outerHTML) => this.detailsByOuterHTML[outerHTML])
1267
1404
  .filter(({ type }) => type == matchedType)
1268
1405
  .map(({ elements: [element] }) => element);
1269
1406
  }
@@ -1283,13 +1420,11 @@ Copyright © 2021 Basecamp, LLC
1283
1420
  }
1284
1421
  getMetaValue(name) {
1285
1422
  const element = this.findMetaElementByName(name);
1286
- return element
1287
- ? element.getAttribute("content")
1288
- : null;
1423
+ return element ? element.getAttribute("content") : null;
1289
1424
  }
1290
1425
  findMetaElementByName(name) {
1291
1426
  return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
1292
- const { elements: [element] } = this.detailsByOuterHTML[outerHTML];
1427
+ const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
1293
1428
  return elementIsMetaElementWithName(element, name) ? element : result;
1294
1429
  }, undefined);
1295
1430
  }
@@ -1456,9 +1591,11 @@ Copyright © 2021 Basecamp, LLC
1456
1591
  if (this.state == VisitState.started) {
1457
1592
  this.recordTimingMetric(TimingMetric.visitEnd);
1458
1593
  this.state = VisitState.completed;
1459
- this.adapter.visitCompleted(this);
1460
- this.delegate.visitCompleted(this);
1461
1594
  this.followRedirect();
1595
+ if (!this.followedRedirect) {
1596
+ this.adapter.visitCompleted(this);
1597
+ this.delegate.visitCompleted(this);
1598
+ }
1462
1599
  }
1463
1600
  }
1464
1601
  fail() {
@@ -1520,12 +1657,12 @@ Copyright © 2021 Basecamp, LLC
1520
1657
  if (this.view.renderPromise)
1521
1658
  await this.view.renderPromise;
1522
1659
  if (isSuccessful(statusCode) && responseHTML != null) {
1523
- await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender);
1660
+ await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
1524
1661
  this.adapter.visitRendered(this);
1525
1662
  this.complete();
1526
1663
  }
1527
1664
  else {
1528
- await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML));
1665
+ await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
1529
1666
  this.adapter.visitRendered(this);
1530
1667
  this.fail();
1531
1668
  }
@@ -1560,7 +1697,7 @@ Copyright © 2021 Basecamp, LLC
1560
1697
  else {
1561
1698
  if (this.view.renderPromise)
1562
1699
  await this.view.renderPromise;
1563
- await this.view.renderPage(snapshot, isPreview, this.willRender);
1700
+ await this.view.renderPage(snapshot, isPreview, this.willRender, this);
1564
1701
  this.adapter.visitRendered(this);
1565
1702
  if (!isPreview) {
1566
1703
  this.complete();
@@ -1573,8 +1710,9 @@ Copyright © 2021 Basecamp, LLC
1573
1710
  var _a;
1574
1711
  if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
1575
1712
  this.adapter.visitProposedToLocation(this.redirectedToLocation, {
1576
- action: 'replace',
1577
- response: this.response
1713
+ action: "replace",
1714
+ willRender: false,
1715
+ response: this.response,
1578
1716
  });
1579
1717
  this.followedRedirect = true;
1580
1718
  }
@@ -1590,13 +1728,15 @@ Copyright © 2021 Basecamp, LLC
1590
1728
  requestStarted() {
1591
1729
  this.startRequest();
1592
1730
  }
1593
- requestPreventedHandlingResponse(request, response) {
1594
- }
1731
+ requestPreventedHandlingResponse(_request, _response) { }
1595
1732
  async requestSucceededWithResponse(request, response) {
1596
1733
  const responseHTML = await response.responseHTML;
1597
1734
  const { redirected, statusCode } = response;
1598
1735
  if (responseHTML == undefined) {
1599
- this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
1736
+ this.recordResponse({
1737
+ statusCode: SystemStatusCode.contentTypeMismatch,
1738
+ redirected,
1739
+ });
1600
1740
  }
1601
1741
  else {
1602
1742
  this.redirectedToLocation = response.redirected ? response.location : undefined;
@@ -1607,14 +1747,20 @@ Copyright © 2021 Basecamp, LLC
1607
1747
  const responseHTML = await response.responseHTML;
1608
1748
  const { redirected, statusCode } = response;
1609
1749
  if (responseHTML == undefined) {
1610
- this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
1750
+ this.recordResponse({
1751
+ statusCode: SystemStatusCode.contentTypeMismatch,
1752
+ redirected,
1753
+ });
1611
1754
  }
1612
1755
  else {
1613
1756
  this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
1614
1757
  }
1615
1758
  }
1616
- requestErrored(request, error) {
1617
- this.recordResponse({ statusCode: SystemStatusCode.networkFailure, redirected: false });
1759
+ requestErrored(_request, _error) {
1760
+ this.recordResponse({
1761
+ statusCode: SystemStatusCode.networkFailure,
1762
+ redirected: false,
1763
+ });
1618
1764
  }
1619
1765
  requestFinished() {
1620
1766
  this.finishRequest();
@@ -1655,9 +1801,11 @@ Copyright © 2021 Basecamp, LLC
1655
1801
  }
1656
1802
  getHistoryMethodForAction(action) {
1657
1803
  switch (action) {
1658
- case "replace": return history.replaceState;
1804
+ case "replace":
1805
+ return history.replaceState;
1659
1806
  case "advance":
1660
- case "restore": return history.pushState;
1807
+ case "restore":
1808
+ return history.pushState;
1661
1809
  }
1662
1810
  }
1663
1811
  hasPreloadedResponse() {
@@ -1676,18 +1824,20 @@ Copyright © 2021 Basecamp, LLC
1676
1824
  }
1677
1825
  cacheSnapshot() {
1678
1826
  if (!this.snapshotCached) {
1679
- this.view.cacheSnapshot().then(snapshot => snapshot && this.visitCachedSnapshot(snapshot));
1827
+ this.view.cacheSnapshot().then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
1680
1828
  this.snapshotCached = true;
1681
1829
  }
1682
1830
  }
1683
1831
  async render(callback) {
1684
1832
  this.cancelRender();
1685
- await new Promise(resolve => {
1833
+ await new Promise((resolve) => {
1686
1834
  this.frame = requestAnimationFrame(() => resolve());
1687
1835
  });
1688
1836
  await callback();
1689
1837
  delete this.frame;
1690
- this.performScroll();
1838
+ if (!this.view.forceReloaded) {
1839
+ this.performScroll();
1840
+ }
1691
1841
  }
1692
1842
  cancelRender() {
1693
1843
  if (this.frame) {
@@ -1702,7 +1852,7 @@ Copyright © 2021 Basecamp, LLC
1702
1852
 
1703
1853
  class BrowserAdapter {
1704
1854
  constructor(session) {
1705
- this.progressBar = new ProgressBar;
1855
+ this.progressBar = new ProgressBar();
1706
1856
  this.showProgressBar = () => {
1707
1857
  this.progressBar.show();
1708
1858
  };
@@ -1712,9 +1862,9 @@ Copyright © 2021 Basecamp, LLC
1712
1862
  this.navigator.startVisit(location, uuid(), options);
1713
1863
  }
1714
1864
  visitStarted(visit) {
1865
+ this.location = visit.location;
1715
1866
  visit.loadCachedSnapshot();
1716
1867
  visit.issueRequest();
1717
- visit.changeHistory();
1718
1868
  visit.goToSamePageAnchor();
1719
1869
  }
1720
1870
  visitRequestStarted(visit) {
@@ -1734,29 +1884,31 @@ Copyright © 2021 Basecamp, LLC
1734
1884
  case SystemStatusCode.networkFailure:
1735
1885
  case SystemStatusCode.timeoutFailure:
1736
1886
  case SystemStatusCode.contentTypeMismatch:
1737
- return this.reload();
1887
+ return this.reload({
1888
+ reason: "request_failed",
1889
+ context: {
1890
+ statusCode,
1891
+ },
1892
+ });
1738
1893
  default:
1739
1894
  return visit.loadResponse();
1740
1895
  }
1741
1896
  }
1742
- visitRequestFinished(visit) {
1897
+ visitRequestFinished(_visit) {
1743
1898
  this.progressBar.setValue(1);
1744
1899
  this.hideVisitProgressBar();
1745
1900
  }
1746
- visitCompleted(visit) {
1747
- }
1748
- pageInvalidated() {
1749
- this.reload();
1750
- }
1751
- visitFailed(visit) {
1901
+ visitCompleted(_visit) { }
1902
+ pageInvalidated(reason) {
1903
+ this.reload(reason);
1752
1904
  }
1753
- visitRendered(visit) {
1754
- }
1755
- formSubmissionStarted(formSubmission) {
1905
+ visitFailed(_visit) { }
1906
+ visitRendered(_visit) { }
1907
+ formSubmissionStarted(_formSubmission) {
1756
1908
  this.progressBar.setValue(0);
1757
1909
  this.showFormProgressBarAfterDelay();
1758
1910
  }
1759
- formSubmissionFinished(formSubmission) {
1911
+ formSubmissionFinished(_formSubmission) {
1760
1912
  this.progressBar.setValue(1);
1761
1913
  this.hideFormProgressBar();
1762
1914
  }
@@ -1782,8 +1934,11 @@ Copyright © 2021 Basecamp, LLC
1782
1934
  delete this.formProgressBarTimeout;
1783
1935
  }
1784
1936
  }
1785
- reload() {
1786
- window.location.reload();
1937
+ reload(reason) {
1938
+ dispatch("turbo:reload", { detail: reason });
1939
+ if (!this.location)
1940
+ return;
1941
+ window.location.href = this.location.toString();
1787
1942
  }
1788
1943
  get navigator() {
1789
1944
  return this.session.navigator;
@@ -1793,6 +1948,12 @@ Copyright © 2021 Basecamp, LLC
1793
1948
  class CacheObserver {
1794
1949
  constructor() {
1795
1950
  this.started = false;
1951
+ this.removeStaleElements = ((_event) => {
1952
+ const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
1953
+ for (const element of staleElements) {
1954
+ element.remove();
1955
+ }
1956
+ });
1796
1957
  }
1797
1958
  start() {
1798
1959
  if (!this.started) {
@@ -1806,12 +1967,6 @@ Copyright © 2021 Basecamp, LLC
1806
1967
  removeEventListener("turbo:before-cache", this.removeStaleElements, false);
1807
1968
  }
1808
1969
  }
1809
- removeStaleElements() {
1810
- const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
1811
- for (const element of staleElements) {
1812
- element.remove();
1813
- }
1814
- }
1815
1970
  }
1816
1971
 
1817
1972
  class FormSubmitObserver {
@@ -1825,12 +1980,12 @@ Copyright © 2021 Basecamp, LLC
1825
1980
  if (!event.defaultPrevented) {
1826
1981
  const form = event.target instanceof HTMLFormElement ? event.target : undefined;
1827
1982
  const submitter = event.submitter || undefined;
1828
- if (form) {
1829
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
1830
- if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
1831
- event.preventDefault();
1832
- this.delegate.formSubmitted(form, submitter);
1833
- }
1983
+ if (form &&
1984
+ submissionDoesNotDismissDialog(form, submitter) &&
1985
+ submissionDoesNotTargetIFrame(form, submitter) &&
1986
+ this.delegate.willSubmitForm(form, submitter)) {
1987
+ event.preventDefault();
1988
+ this.delegate.formSubmitted(form, submitter);
1834
1989
  }
1835
1990
  }
1836
1991
  });
@@ -1849,6 +2004,18 @@ Copyright © 2021 Basecamp, LLC
1849
2004
  }
1850
2005
  }
1851
2006
  }
2007
+ function submissionDoesNotDismissDialog(form, submitter) {
2008
+ const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
2009
+ return method != "dialog";
2010
+ }
2011
+ function submissionDoesNotTargetIFrame(form, submitter) {
2012
+ const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
2013
+ for (const element of document.getElementsByName(target)) {
2014
+ if (element instanceof HTMLIFrameElement)
2015
+ return false;
2016
+ }
2017
+ return true;
2018
+ }
1852
2019
 
1853
2020
  class FrameRedirector {
1854
2021
  constructor(element) {
@@ -1864,7 +2031,7 @@ Copyright © 2021 Basecamp, LLC
1864
2031
  this.linkInterceptor.stop();
1865
2032
  this.formInterceptor.stop();
1866
2033
  }
1867
- shouldInterceptLinkClick(element, url) {
2034
+ shouldInterceptLinkClick(element, _url) {
1868
2035
  return this.shouldRedirect(element);
1869
2036
  }
1870
2037
  linkClickIntercepted(element, url) {
@@ -1879,7 +2046,6 @@ Copyright © 2021 Basecamp, LLC
1879
2046
  formSubmissionIntercepted(element, submitter) {
1880
2047
  const frame = this.findFrameElement(element, submitter);
1881
2048
  if (frame) {
1882
- frame.removeAttribute("reloadable");
1883
2049
  frame.delegate.formSubmissionIntercepted(element, submitter);
1884
2050
  }
1885
2051
  }
@@ -1922,7 +2088,7 @@ Copyright © 2021 Basecamp, LLC
1922
2088
  }
1923
2089
  }
1924
2090
  };
1925
- this.onPageLoad = async (event) => {
2091
+ this.onPageLoad = async (_event) => {
1926
2092
  await nextMicrotask();
1927
2093
  this.pageLoaded = true;
1928
2094
  };
@@ -1995,9 +2161,9 @@ Copyright © 2021 Basecamp, LLC
1995
2161
  if (this.clickEventIsSignificant(event)) {
1996
2162
  const target = (event.composedPath && event.composedPath()[0]) || event.target;
1997
2163
  const link = this.findLinkFromClickTarget(target);
1998
- if (link) {
2164
+ if (link && doesNotTargetIFrame(link)) {
1999
2165
  const location = this.getLocationForLink(link);
2000
- if (this.delegate.willFollowLinkToLocation(link, location)) {
2166
+ if (this.delegate.willFollowLinkToLocation(link, location, event)) {
2001
2167
  event.preventDefault();
2002
2168
  this.delegate.followedLinkToLocation(link, location);
2003
2169
  }
@@ -2019,13 +2185,13 @@ Copyright © 2021 Basecamp, LLC
2019
2185
  }
2020
2186
  }
2021
2187
  clickEventIsSignificant(event) {
2022
- return !((event.target && event.target.isContentEditable)
2023
- || event.defaultPrevented
2024
- || event.which > 1
2025
- || event.altKey
2026
- || event.ctrlKey
2027
- || event.metaKey
2028
- || event.shiftKey);
2188
+ return !((event.target && event.target.isContentEditable) ||
2189
+ event.defaultPrevented ||
2190
+ event.which > 1 ||
2191
+ event.altKey ||
2192
+ event.ctrlKey ||
2193
+ event.metaKey ||
2194
+ event.shiftKey);
2029
2195
  }
2030
2196
  findLinkFromClickTarget(target) {
2031
2197
  if (target instanceof Element) {
@@ -2036,6 +2202,13 @@ Copyright © 2021 Basecamp, LLC
2036
2202
  return expandURL(link.getAttribute("href") || "");
2037
2203
  }
2038
2204
  }
2205
+ function doesNotTargetIFrame(anchor) {
2206
+ for (const element of document.getElementsByName(anchor.target)) {
2207
+ if (element instanceof HTMLIFrameElement)
2208
+ return false;
2209
+ }
2210
+ return true;
2211
+ }
2039
2212
 
2040
2213
  function isAction(action) {
2041
2214
  return action == "advance" || action == "replace" || action == "restore";
@@ -2056,6 +2229,7 @@ Copyright © 2021 Basecamp, LLC
2056
2229
  }
2057
2230
  }
2058
2231
  startVisit(locatable, restorationIdentifier, options = {}) {
2232
+ this.lastVisit = this.currentVisit;
2059
2233
  this.stop();
2060
2234
  this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
2061
2235
  this.currentVisit.start();
@@ -2085,7 +2259,7 @@ Copyright © 2021 Basecamp, LLC
2085
2259
  return this.delegate.history;
2086
2260
  }
2087
2261
  formSubmissionStarted(formSubmission) {
2088
- if (typeof this.adapter.formSubmissionStarted === 'function') {
2262
+ if (typeof this.adapter.formSubmissionStarted === "function") {
2089
2263
  this.adapter.formSubmissionStarted(formSubmission);
2090
2264
  }
2091
2265
  }
@@ -2098,7 +2272,10 @@ Copyright © 2021 Basecamp, LLC
2098
2272
  }
2099
2273
  const { statusCode, redirected } = fetchResponse;
2100
2274
  const action = this.getActionForFormSubmission(formSubmission);
2101
- const visitOptions = { action, response: { statusCode, responseHTML, redirected } };
2275
+ const visitOptions = {
2276
+ action,
2277
+ response: { statusCode, responseHTML, redirected },
2278
+ };
2102
2279
  this.proposeVisit(fetchResponse.location, visitOptions);
2103
2280
  }
2104
2281
  }
@@ -2108,10 +2285,10 @@ Copyright © 2021 Basecamp, LLC
2108
2285
  if (responseHTML) {
2109
2286
  const snapshot = PageSnapshot.fromHTMLString(responseHTML);
2110
2287
  if (fetchResponse.serverError) {
2111
- await this.view.renderError(snapshot);
2288
+ await this.view.renderError(snapshot, this.currentVisit);
2112
2289
  }
2113
2290
  else {
2114
- await this.view.renderPage(snapshot);
2291
+ await this.view.renderPage(snapshot, false, true, this.currentVisit);
2115
2292
  }
2116
2293
  this.view.scrollToTop();
2117
2294
  this.view.clearSnapshotCache();
@@ -2121,7 +2298,7 @@ Copyright © 2021 Basecamp, LLC
2121
2298
  console.error(error);
2122
2299
  }
2123
2300
  formSubmissionFinished(formSubmission) {
2124
- if (typeof this.adapter.formSubmissionFinished === 'function') {
2301
+ if (typeof this.adapter.formSubmissionFinished === "function") {
2125
2302
  this.adapter.formSubmissionFinished(formSubmission);
2126
2303
  }
2127
2304
  }
@@ -2132,12 +2309,14 @@ Copyright © 2021 Basecamp, LLC
2132
2309
  this.delegate.visitCompleted(visit);
2133
2310
  }
2134
2311
  locationWithActionIsSamePage(location, action) {
2312
+ var _a;
2135
2313
  const anchor = getAnchor(location);
2136
- const currentAnchor = getAnchor(this.view.lastRenderedLocation);
2137
- const isRestorationToTop = action === 'restore' && typeof anchor === 'undefined';
2138
- return action !== "replace" &&
2139
- getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
2140
- (isRestorationToTop || (anchor != null && anchor !== currentAnchor));
2314
+ const lastLocation = ((_a = this.lastVisit) === null || _a === void 0 ? void 0 : _a.location) || this.view.lastRenderedLocation;
2315
+ const currentAnchor = getAnchor(lastLocation);
2316
+ const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
2317
+ return (action !== "replace" &&
2318
+ getRequestURL(location) === getRequestURL(lastLocation) &&
2319
+ (isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
2141
2320
  }
2142
2321
  visitScrolledToSamePageLocation(oldURL, newURL) {
2143
2322
  this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
@@ -2243,7 +2422,7 @@ Copyright © 2021 Basecamp, LLC
2243
2422
 
2244
2423
  class StreamObserver {
2245
2424
  constructor(delegate) {
2246
- this.sources = new Set;
2425
+ this.sources = new Set();
2247
2426
  this.started = false;
2248
2427
  this.inspectFetchResponse = ((event) => {
2249
2428
  const response = fetchResponseFromEvent(event);
@@ -2310,14 +2489,18 @@ Copyright © 2021 Basecamp, LLC
2310
2489
  }
2311
2490
 
2312
2491
  class ErrorRenderer extends Renderer {
2492
+ static renderElement(currentElement, newElement) {
2493
+ const { documentElement, body } = document;
2494
+ documentElement.replaceChild(newElement, body);
2495
+ }
2313
2496
  async render() {
2314
2497
  this.replaceHeadAndBody();
2315
2498
  this.activateScriptElements();
2316
2499
  }
2317
2500
  replaceHeadAndBody() {
2318
- const { documentElement, head, body } = document;
2501
+ const { documentElement, head } = document;
2319
2502
  documentElement.replaceChild(this.newHead, head);
2320
- documentElement.replaceChild(this.newElement, body);
2503
+ this.renderElement(this.currentElement, this.newElement);
2321
2504
  }
2322
2505
  activateScriptElements() {
2323
2506
  for (const replaceableElement of this.scriptElements) {
@@ -2337,9 +2520,29 @@ Copyright © 2021 Basecamp, LLC
2337
2520
  }
2338
2521
 
2339
2522
  class PageRenderer extends Renderer {
2523
+ static renderElement(currentElement, newElement) {
2524
+ if (document.body && newElement instanceof HTMLBodyElement) {
2525
+ document.body.replaceWith(newElement);
2526
+ }
2527
+ else {
2528
+ document.documentElement.appendChild(newElement);
2529
+ }
2530
+ }
2340
2531
  get shouldRender() {
2341
2532
  return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
2342
2533
  }
2534
+ get reloadReason() {
2535
+ if (!this.newSnapshot.isVisitable) {
2536
+ return {
2537
+ reason: "turbo_visit_control_is_reload",
2538
+ };
2539
+ }
2540
+ if (!this.trackedElementsAreIdentical) {
2541
+ return {
2542
+ reason: "tracked_element_mismatch",
2543
+ };
2544
+ }
2545
+ }
2343
2546
  prepareToRender() {
2344
2547
  this.mergeHead();
2345
2548
  }
@@ -2409,12 +2612,7 @@ Copyright © 2021 Basecamp, LLC
2409
2612
  }
2410
2613
  }
2411
2614
  assignNewBody() {
2412
- if (document.body && this.newElement instanceof HTMLBodyElement) {
2413
- document.body.replaceWith(this.newElement);
2414
- }
2415
- else {
2416
- document.documentElement.appendChild(this.newElement);
2417
- }
2615
+ this.renderElement(this.currentElement, this.newElement);
2418
2616
  }
2419
2617
  get newHeadStylesheetElements() {
2420
2618
  return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
@@ -2483,13 +2681,21 @@ Copyright © 2021 Basecamp, LLC
2483
2681
  super(...arguments);
2484
2682
  this.snapshotCache = new SnapshotCache(10);
2485
2683
  this.lastRenderedLocation = new URL(location.href);
2684
+ this.forceReloaded = false;
2486
2685
  }
2487
- renderPage(snapshot, isPreview = false, willRender = true) {
2488
- const renderer = new PageRenderer(this.snapshot, snapshot, isPreview, willRender);
2686
+ renderPage(snapshot, isPreview = false, willRender = true, visit) {
2687
+ const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
2688
+ if (!renderer.shouldRender) {
2689
+ this.forceReloaded = true;
2690
+ }
2691
+ else {
2692
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
2693
+ }
2489
2694
  return this.render(renderer);
2490
2695
  }
2491
- renderError(snapshot) {
2492
- const renderer = new ErrorRenderer(this.snapshot, snapshot, false);
2696
+ renderError(snapshot, visit) {
2697
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
2698
+ const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
2493
2699
  return this.render(renderer);
2494
2700
  }
2495
2701
  clearSnapshotCache() {
@@ -2516,10 +2722,50 @@ Copyright © 2021 Basecamp, LLC
2516
2722
  }
2517
2723
  }
2518
2724
 
2725
+ class Preloader {
2726
+ constructor(delegate) {
2727
+ this.selector = "a[data-turbo-preload]";
2728
+ this.delegate = delegate;
2729
+ }
2730
+ get snapshotCache() {
2731
+ return this.delegate.navigator.view.snapshotCache;
2732
+ }
2733
+ start() {
2734
+ if (document.readyState === "loading") {
2735
+ return document.addEventListener("DOMContentLoaded", () => {
2736
+ this.preloadOnLoadLinksForView(document.body);
2737
+ });
2738
+ }
2739
+ else {
2740
+ this.preloadOnLoadLinksForView(document.body);
2741
+ }
2742
+ }
2743
+ preloadOnLoadLinksForView(element) {
2744
+ for (const link of element.querySelectorAll(this.selector)) {
2745
+ this.preloadURL(link);
2746
+ }
2747
+ }
2748
+ async preloadURL(link) {
2749
+ const location = new URL(link.href);
2750
+ if (this.snapshotCache.has(location)) {
2751
+ return;
2752
+ }
2753
+ try {
2754
+ const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
2755
+ const responseText = await response.text();
2756
+ const snapshot = PageSnapshot.fromHTMLString(responseText);
2757
+ this.snapshotCache.put(location, snapshot);
2758
+ }
2759
+ catch (_) {
2760
+ }
2761
+ }
2762
+ }
2763
+
2519
2764
  class Session {
2520
2765
  constructor() {
2521
2766
  this.navigator = new Navigator(this);
2522
2767
  this.history = new History(this);
2768
+ this.preloader = new Preloader(this);
2523
2769
  this.view = new PageView(this, document.documentElement);
2524
2770
  this.adapter = new BrowserAdapter(this);
2525
2771
  this.pageObserver = new PageObserver(this);
@@ -2528,22 +2774,26 @@ Copyright © 2021 Basecamp, LLC
2528
2774
  this.formSubmitObserver = new FormSubmitObserver(this);
2529
2775
  this.scrollObserver = new ScrollObserver(this);
2530
2776
  this.streamObserver = new StreamObserver(this);
2777
+ this.formLinkInterceptor = new FormLinkInterceptor(this, document.documentElement);
2531
2778
  this.frameRedirector = new FrameRedirector(document.documentElement);
2532
2779
  this.drive = true;
2533
2780
  this.enabled = true;
2534
2781
  this.progressBarDelay = 500;
2535
2782
  this.started = false;
2783
+ this.formMode = "on";
2536
2784
  }
2537
2785
  start() {
2538
2786
  if (!this.started) {
2539
2787
  this.pageObserver.start();
2540
2788
  this.cacheObserver.start();
2789
+ this.formLinkInterceptor.start();
2541
2790
  this.linkClickObserver.start();
2542
2791
  this.formSubmitObserver.start();
2543
2792
  this.scrollObserver.start();
2544
2793
  this.streamObserver.start();
2545
2794
  this.frameRedirector.start();
2546
2795
  this.history.start();
2796
+ this.preloader.start();
2547
2797
  this.started = true;
2548
2798
  this.enabled = true;
2549
2799
  }
@@ -2555,6 +2805,7 @@ Copyright © 2021 Basecamp, LLC
2555
2805
  if (this.started) {
2556
2806
  this.pageObserver.stop();
2557
2807
  this.cacheObserver.stop();
2808
+ this.formLinkInterceptor.stop();
2558
2809
  this.linkClickObserver.stop();
2559
2810
  this.formSubmitObserver.stop();
2560
2811
  this.scrollObserver.stop();
@@ -2585,6 +2836,9 @@ Copyright © 2021 Basecamp, LLC
2585
2836
  setProgressBarDelay(delay) {
2586
2837
  this.progressBarDelay = delay;
2587
2838
  }
2839
+ setFormMode(mode) {
2840
+ this.formMode = mode;
2841
+ }
2588
2842
  get location() {
2589
2843
  return this.history.location;
2590
2844
  }
@@ -2593,48 +2847,32 @@ Copyright © 2021 Basecamp, LLC
2593
2847
  }
2594
2848
  historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
2595
2849
  if (this.enabled) {
2596
- this.navigator.startVisit(location, restorationIdentifier, { action: "restore", historyChanged: true });
2850
+ this.navigator.startVisit(location, restorationIdentifier, {
2851
+ action: "restore",
2852
+ historyChanged: true,
2853
+ });
2597
2854
  }
2598
2855
  else {
2599
- this.adapter.pageInvalidated();
2856
+ this.adapter.pageInvalidated({
2857
+ reason: "turbo_disabled",
2858
+ });
2600
2859
  }
2601
2860
  }
2602
2861
  scrollPositionChanged(position) {
2603
2862
  this.history.updateRestorationData({ scrollPosition: position });
2604
2863
  }
2605
- willFollowLinkToLocation(link, location) {
2606
- return this.elementDriveEnabled(link)
2607
- && locationIsVisitable(location, this.snapshot.rootLocation)
2608
- && this.applicationAllowsFollowingLinkToLocation(link, location);
2864
+ shouldInterceptFormLinkClick(_link) {
2865
+ return true;
2866
+ }
2867
+ formLinkClickIntercepted(_link, _form) { }
2868
+ willFollowLinkToLocation(link, location, event) {
2869
+ return (this.elementDriveEnabled(link) &&
2870
+ locationIsVisitable(location, this.snapshot.rootLocation) &&
2871
+ this.applicationAllowsFollowingLinkToLocation(link, location, event));
2609
2872
  }
2610
2873
  followedLinkToLocation(link, location) {
2611
2874
  const action = this.getActionForLink(link);
2612
- this.convertLinkWithMethodClickToFormSubmission(link) || this.visit(location.href, { action });
2613
- }
2614
- convertLinkWithMethodClickToFormSubmission(link) {
2615
- const linkMethod = link.getAttribute("data-turbo-method");
2616
- if (linkMethod) {
2617
- const form = document.createElement("form");
2618
- form.method = linkMethod;
2619
- form.action = link.getAttribute("href") || "undefined";
2620
- form.hidden = true;
2621
- if (link.hasAttribute("data-turbo-confirm")) {
2622
- form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
2623
- }
2624
- const frame = this.getTargetFrameForLink(link);
2625
- if (frame) {
2626
- form.setAttribute("data-turbo-frame", frame);
2627
- form.addEventListener("turbo:submit-start", () => form.remove());
2628
- }
2629
- else {
2630
- form.addEventListener("submit", () => form.remove());
2631
- }
2632
- document.body.appendChild(form);
2633
- return dispatch("submit", { cancelable: true, target: form });
2634
- }
2635
- else {
2636
- return false;
2637
- }
2875
+ this.visit(location.href, { action });
2638
2876
  }
2639
2877
  allowsVisitingLocationWithAction(location, action) {
2640
2878
  return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
@@ -2660,9 +2898,9 @@ Copyright © 2021 Basecamp, LLC
2660
2898
  }
2661
2899
  willSubmitForm(form, submitter) {
2662
2900
  const action = getAction(form, submitter);
2663
- return this.elementDriveEnabled(form)
2664
- && (!submitter || this.elementDriveEnabled(submitter))
2665
- && locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
2901
+ return (this.elementDriveEnabled(form) &&
2902
+ (!submitter || this.formElementDriveEnabled(submitter)) &&
2903
+ locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
2666
2904
  }
2667
2905
  formSubmitted(form, submitter) {
2668
2906
  this.navigator.submitForm(form, submitter);
@@ -2686,16 +2924,23 @@ Copyright © 2021 Basecamp, LLC
2686
2924
  this.notifyApplicationBeforeCachingSnapshot();
2687
2925
  }
2688
2926
  }
2689
- allowsImmediateRender({ element }, resume) {
2690
- const event = this.notifyApplicationBeforeRender(element, resume);
2691
- return !event.defaultPrevented;
2927
+ allowsImmediateRender({ element }, options) {
2928
+ const event = this.notifyApplicationBeforeRender(element, options);
2929
+ const { defaultPrevented, detail: { render }, } = event;
2930
+ if (this.view.renderer && render) {
2931
+ this.view.renderer.renderElement = render;
2932
+ }
2933
+ return !defaultPrevented;
2692
2934
  }
2693
- viewRenderedSnapshot(snapshot, isPreview) {
2935
+ viewRenderedSnapshot(_snapshot, _isPreview) {
2694
2936
  this.view.lastRenderedLocation = this.history.location;
2695
2937
  this.notifyApplicationAfterRender();
2696
2938
  }
2697
- viewInvalidated() {
2698
- this.adapter.pageInvalidated();
2939
+ preloadOnLoadLinksForView(element) {
2940
+ this.preloader.preloadOnLoadLinksForView(element);
2941
+ }
2942
+ viewInvalidated(reason) {
2943
+ this.adapter.pageInvalidated(reason);
2699
2944
  }
2700
2945
  frameLoaded(frame) {
2701
2946
  this.notifyApplicationAfterFrameLoad(frame);
@@ -2703,19 +2948,26 @@ Copyright © 2021 Basecamp, LLC
2703
2948
  frameRendered(fetchResponse, frame) {
2704
2949
  this.notifyApplicationAfterFrameRender(fetchResponse, frame);
2705
2950
  }
2706
- applicationAllowsFollowingLinkToLocation(link, location) {
2707
- const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
2951
+ applicationAllowsFollowingLinkToLocation(link, location, ev) {
2952
+ const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
2708
2953
  return !event.defaultPrevented;
2709
2954
  }
2710
2955
  applicationAllowsVisitingLocation(location) {
2711
2956
  const event = this.notifyApplicationBeforeVisitingLocation(location);
2712
2957
  return !event.defaultPrevented;
2713
2958
  }
2714
- notifyApplicationAfterClickingLinkToLocation(link, location) {
2715
- return dispatch("turbo:click", { target: link, detail: { url: location.href }, cancelable: true });
2959
+ notifyApplicationAfterClickingLinkToLocation(link, location, event) {
2960
+ return dispatch("turbo:click", {
2961
+ target: link,
2962
+ detail: { url: location.href, originalEvent: event },
2963
+ cancelable: true,
2964
+ });
2716
2965
  }
2717
2966
  notifyApplicationBeforeVisitingLocation(location) {
2718
- return dispatch("turbo:before-visit", { detail: { url: location.href }, cancelable: true });
2967
+ return dispatch("turbo:before-visit", {
2968
+ detail: { url: location.href },
2969
+ cancelable: true,
2970
+ });
2719
2971
  }
2720
2972
  notifyApplicationAfterVisitingLocation(location, action) {
2721
2973
  markAsBusy(document.documentElement);
@@ -2724,24 +2976,46 @@ Copyright © 2021 Basecamp, LLC
2724
2976
  notifyApplicationBeforeCachingSnapshot() {
2725
2977
  return dispatch("turbo:before-cache");
2726
2978
  }
2727
- notifyApplicationBeforeRender(newBody, resume) {
2728
- return dispatch("turbo:before-render", { detail: { newBody, resume }, cancelable: true });
2979
+ notifyApplicationBeforeRender(newBody, options) {
2980
+ return dispatch("turbo:before-render", {
2981
+ detail: Object.assign({ newBody }, options),
2982
+ cancelable: true,
2983
+ });
2729
2984
  }
2730
2985
  notifyApplicationAfterRender() {
2731
2986
  return dispatch("turbo:render");
2732
2987
  }
2733
2988
  notifyApplicationAfterPageLoad(timing = {}) {
2734
2989
  clearBusyState(document.documentElement);
2735
- return dispatch("turbo:load", { detail: { url: this.location.href, timing } });
2990
+ return dispatch("turbo:load", {
2991
+ detail: { url: this.location.href, timing },
2992
+ });
2736
2993
  }
2737
2994
  notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
2738
- dispatchEvent(new HashChangeEvent("hashchange", { oldURL: oldURL.toString(), newURL: newURL.toString() }));
2995
+ dispatchEvent(new HashChangeEvent("hashchange", {
2996
+ oldURL: oldURL.toString(),
2997
+ newURL: newURL.toString(),
2998
+ }));
2739
2999
  }
2740
3000
  notifyApplicationAfterFrameLoad(frame) {
2741
3001
  return dispatch("turbo:frame-load", { target: frame });
2742
3002
  }
2743
3003
  notifyApplicationAfterFrameRender(fetchResponse, frame) {
2744
- return dispatch("turbo:frame-render", { detail: { fetchResponse }, target: frame, cancelable: true });
3004
+ return dispatch("turbo:frame-render", {
3005
+ detail: { fetchResponse },
3006
+ target: frame,
3007
+ cancelable: true,
3008
+ });
3009
+ }
3010
+ formElementDriveEnabled(element) {
3011
+ if (this.formMode == "off") {
3012
+ return false;
3013
+ }
3014
+ if (this.formMode == "optin") {
3015
+ const form = element === null || element === void 0 ? void 0 : element.closest("form[data-turbo]");
3016
+ return (form === null || form === void 0 ? void 0 : form.getAttribute("data-turbo")) == "true";
3017
+ }
3018
+ return this.elementDriveEnabled(element);
2745
3019
  }
2746
3020
  elementDriveEnabled(element) {
2747
3021
  const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
@@ -2766,18 +3040,6 @@ Copyright © 2021 Basecamp, LLC
2766
3040
  const action = link.getAttribute("data-turbo-action");
2767
3041
  return isAction(action) ? action : "advance";
2768
3042
  }
2769
- getTargetFrameForLink(link) {
2770
- const frame = link.getAttribute("data-turbo-frame");
2771
- if (frame) {
2772
- return frame;
2773
- }
2774
- else {
2775
- const container = link.closest("turbo-frame");
2776
- if (container) {
2777
- return container.id;
2778
- }
2779
- }
2780
- }
2781
3043
  get snapshot() {
2782
3044
  return this.view.snapshot;
2783
3045
  }
@@ -2789,11 +3051,59 @@ Copyright © 2021 Basecamp, LLC
2789
3051
  absoluteURL: {
2790
3052
  get() {
2791
3053
  return this.toString();
2792
- }
3054
+ },
3055
+ },
3056
+ };
3057
+
3058
+ class Cache {
3059
+ constructor(session) {
3060
+ this.session = session;
2793
3061
  }
3062
+ clear() {
3063
+ this.session.clearCache();
3064
+ }
3065
+ resetCacheControl() {
3066
+ this.setCacheControl("");
3067
+ }
3068
+ exemptPageFromCache() {
3069
+ this.setCacheControl("no-cache");
3070
+ }
3071
+ exemptPageFromPreview() {
3072
+ this.setCacheControl("no-preview");
3073
+ }
3074
+ setCacheControl(value) {
3075
+ setMetaContent("turbo-cache-control", value);
3076
+ }
3077
+ }
3078
+
3079
+ const StreamActions = {
3080
+ after() {
3081
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
3082
+ },
3083
+ append() {
3084
+ this.removeDuplicateTargetChildren();
3085
+ this.targetElements.forEach((e) => e.append(this.templateContent));
3086
+ },
3087
+ before() {
3088
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
3089
+ },
3090
+ prepend() {
3091
+ this.removeDuplicateTargetChildren();
3092
+ this.targetElements.forEach((e) => e.prepend(this.templateContent));
3093
+ },
3094
+ remove() {
3095
+ this.targetElements.forEach((e) => e.remove());
3096
+ },
3097
+ replace() {
3098
+ this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
3099
+ },
3100
+ update() {
3101
+ this.targetElements.forEach((e) => e.replaceChildren(this.templateContent));
3102
+ },
2794
3103
  };
2795
3104
 
2796
- const session = new Session;
3105
+ const session = new Session();
3106
+ const cache = new Cache(session);
2797
3107
  const { navigator: navigator$1 } = session;
2798
3108
  function start() {
2799
3109
  session.start();
@@ -2814,6 +3124,7 @@ Copyright © 2021 Basecamp, LLC
2814
3124
  session.renderStreamMessage(message);
2815
3125
  }
2816
3126
  function clearCache() {
3127
+ 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.`");
2817
3128
  session.clearCache();
2818
3129
  }
2819
3130
  function setProgressBarDelay(delay) {
@@ -2822,13 +3133,18 @@ Copyright © 2021 Basecamp, LLC
2822
3133
  function setConfirmMethod(confirmMethod) {
2823
3134
  FormSubmission.confirmMethod = confirmMethod;
2824
3135
  }
3136
+ function setFormMode(mode) {
3137
+ session.setFormMode(mode);
3138
+ }
2825
3139
 
2826
3140
  var Turbo = /*#__PURE__*/Object.freeze({
2827
3141
  __proto__: null,
2828
3142
  navigator: navigator$1,
2829
3143
  session: session,
3144
+ cache: cache,
2830
3145
  PageRenderer: PageRenderer,
2831
3146
  PageSnapshot: PageSnapshot,
3147
+ FrameRenderer: FrameRenderer,
2832
3148
  start: start,
2833
3149
  registerAdapter: registerAdapter,
2834
3150
  visit: visit,
@@ -2837,39 +3153,52 @@ Copyright © 2021 Basecamp, LLC
2837
3153
  renderStreamMessage: renderStreamMessage,
2838
3154
  clearCache: clearCache,
2839
3155
  setProgressBarDelay: setProgressBarDelay,
2840
- setConfirmMethod: setConfirmMethod
3156
+ setConfirmMethod: setConfirmMethod,
3157
+ setFormMode: setFormMode,
3158
+ StreamActions: StreamActions
2841
3159
  });
2842
3160
 
2843
3161
  class FrameController {
2844
3162
  constructor(element) {
2845
- this.fetchResponseLoaded = (fetchResponse) => { };
3163
+ this.fetchResponseLoaded = (_fetchResponse) => { };
2846
3164
  this.currentFetchRequest = null;
2847
3165
  this.resolveVisitPromise = () => { };
2848
3166
  this.connected = false;
2849
3167
  this.hasBeenLoaded = false;
2850
- this.settingSourceURL = false;
3168
+ this.ignoredAttributes = new Set();
3169
+ this.visitCachedSnapshot = ({ element }) => {
3170
+ const frame = element.querySelector("#" + this.element.id);
3171
+ if (frame && this.previousFrameElement) {
3172
+ frame.replaceChildren(...this.previousFrameElement.children);
3173
+ }
3174
+ delete this.previousFrameElement;
3175
+ };
2851
3176
  this.element = element;
2852
3177
  this.view = new FrameView(this, this.element);
2853
3178
  this.appearanceObserver = new AppearanceObserver(this, this.element);
3179
+ this.formLinkInterceptor = new FormLinkInterceptor(this, this.element);
2854
3180
  this.linkInterceptor = new LinkInterceptor(this, this.element);
2855
3181
  this.formInterceptor = new FormInterceptor(this, this.element);
2856
3182
  }
2857
3183
  connect() {
2858
3184
  if (!this.connected) {
2859
3185
  this.connected = true;
2860
- this.reloadable = false;
2861
3186
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
2862
3187
  this.appearanceObserver.start();
2863
3188
  }
3189
+ else {
3190
+ this.loadSourceURL();
3191
+ }
3192
+ this.formLinkInterceptor.start();
2864
3193
  this.linkInterceptor.start();
2865
3194
  this.formInterceptor.start();
2866
- this.sourceURLChanged();
2867
3195
  }
2868
3196
  }
2869
3197
  disconnect() {
2870
3198
  if (this.connected) {
2871
3199
  this.connected = false;
2872
3200
  this.appearanceObserver.stop();
3201
+ this.formLinkInterceptor.stop();
2873
3202
  this.linkInterceptor.stop();
2874
3203
  this.formInterceptor.stop();
2875
3204
  }
@@ -2880,10 +3209,20 @@ Copyright © 2021 Basecamp, LLC
2880
3209
  }
2881
3210
  }
2882
3211
  sourceURLChanged() {
3212
+ if (this.isIgnoringChangesTo("src"))
3213
+ return;
3214
+ if (this.element.isConnected) {
3215
+ this.complete = false;
3216
+ }
2883
3217
  if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
2884
3218
  this.loadSourceURL();
2885
3219
  }
2886
3220
  }
3221
+ completeChanged() {
3222
+ if (this.isIgnoringChangesTo("complete"))
3223
+ return;
3224
+ this.loadSourceURL();
3225
+ }
2887
3226
  loadingStyleChanged() {
2888
3227
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
2889
3228
  this.appearanceObserver.start();
@@ -2894,21 +3233,11 @@ Copyright © 2021 Basecamp, LLC
2894
3233
  }
2895
3234
  }
2896
3235
  async loadSourceURL() {
2897
- if (!this.settingSourceURL && this.enabled && this.isActive && (this.reloadable || this.sourceURL != this.currentURL)) {
2898
- const previousURL = this.currentURL;
2899
- this.currentURL = this.sourceURL;
2900
- if (this.sourceURL) {
2901
- try {
2902
- this.element.loaded = this.visit(expandURL(this.sourceURL));
2903
- this.appearanceObserver.stop();
2904
- await this.element.loaded;
2905
- this.hasBeenLoaded = true;
2906
- }
2907
- catch (error) {
2908
- this.currentURL = previousURL;
2909
- throw error;
2910
- }
2911
- }
3236
+ if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
3237
+ this.element.loaded = this.visit(expandURL(this.sourceURL));
3238
+ this.appearanceObserver.stop();
3239
+ await this.element.loaded;
3240
+ this.hasBeenLoaded = true;
2912
3241
  }
2913
3242
  }
2914
3243
  async loadResponse(fetchResponse) {
@@ -2920,10 +3249,11 @@ Copyright © 2021 Basecamp, LLC
2920
3249
  if (html) {
2921
3250
  const { body } = parseHTMLDocument(html);
2922
3251
  const snapshot = new Snapshot(await this.extractForeignFrameElement(body));
2923
- const renderer = new FrameRenderer(this.view.snapshot, snapshot, false, false);
3252
+ const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
2924
3253
  if (this.view.renderPromise)
2925
3254
  await this.view.renderPromise;
2926
3255
  await this.view.render(renderer);
3256
+ this.complete = true;
2927
3257
  session.frameRendered(fetchResponse, this.element);
2928
3258
  session.frameLoaded(this.element);
2929
3259
  this.fetchResponseLoaded(fetchResponse);
@@ -2937,19 +3267,21 @@ Copyright © 2021 Basecamp, LLC
2937
3267
  this.fetchResponseLoaded = () => { };
2938
3268
  }
2939
3269
  }
2940
- elementAppearedInViewport(element) {
3270
+ elementAppearedInViewport(_element) {
2941
3271
  this.loadSourceURL();
2942
3272
  }
2943
- shouldInterceptLinkClick(element, url) {
2944
- if (element.hasAttribute("data-turbo-method")) {
2945
- return false;
2946
- }
2947
- else {
2948
- return this.shouldInterceptNavigation(element);
2949
- }
3273
+ shouldInterceptFormLinkClick(link) {
3274
+ return this.shouldInterceptNavigation(link);
3275
+ }
3276
+ formLinkClickIntercepted(link, form) {
3277
+ const frame = this.findFrameElement(link);
3278
+ if (frame)
3279
+ form.setAttribute("data-turbo-frame", frame.id);
3280
+ }
3281
+ shouldInterceptLinkClick(element, _url) {
3282
+ return this.shouldInterceptNavigation(element);
2950
3283
  }
2951
3284
  linkClickIntercepted(element, url) {
2952
- this.reloadable = true;
2953
3285
  this.navigateFrame(element, url);
2954
3286
  }
2955
3287
  shouldInterceptFormSubmission(element, submitter) {
@@ -2959,19 +3291,18 @@ Copyright © 2021 Basecamp, LLC
2959
3291
  if (this.formSubmission) {
2960
3292
  this.formSubmission.stop();
2961
3293
  }
2962
- this.reloadable = false;
2963
3294
  this.formSubmission = new FormSubmission(this, element, submitter);
2964
3295
  const { fetchRequest } = this.formSubmission;
2965
3296
  this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
2966
3297
  this.formSubmission.start();
2967
3298
  }
2968
- prepareHeadersForRequest(headers, request) {
3299
+ prepareHeadersForRequest(headers, _request) {
2969
3300
  headers["Turbo-Frame"] = this.id;
2970
3301
  }
2971
- requestStarted(request) {
3302
+ requestStarted(_request) {
2972
3303
  markAsBusy(this.element);
2973
3304
  }
2974
- requestPreventedHandlingResponse(request, response) {
3305
+ requestPreventedHandlingResponse(_request, _response) {
2975
3306
  this.resolveVisitPromise();
2976
3307
  }
2977
3308
  async requestSucceededWithResponse(request, response) {
@@ -2986,7 +3317,7 @@ Copyright © 2021 Basecamp, LLC
2986
3317
  console.error(error);
2987
3318
  this.resolveVisitPromise();
2988
3319
  }
2989
- requestFinished(request) {
3320
+ requestFinished(_request) {
2990
3321
  clearBusyState(this.element);
2991
3322
  }
2992
3323
  formSubmissionStarted({ formElement }) {
@@ -3006,19 +3337,32 @@ Copyright © 2021 Basecamp, LLC
3006
3337
  formSubmissionFinished({ formElement }) {
3007
3338
  clearBusyState(formElement, this.findFrameElement(formElement));
3008
3339
  }
3009
- allowsImmediateRender(snapshot, resume) {
3010
- return true;
3340
+ allowsImmediateRender({ element: newFrame }, options) {
3341
+ const event = dispatch("turbo:before-frame-render", {
3342
+ target: this.element,
3343
+ detail: Object.assign({ newFrame }, options),
3344
+ cancelable: true,
3345
+ });
3346
+ const { defaultPrevented, detail: { render }, } = event;
3347
+ if (this.view.renderer && render) {
3348
+ this.view.renderer.renderElement = render;
3349
+ }
3350
+ return !defaultPrevented;
3011
3351
  }
3012
- viewRenderedSnapshot(snapshot, isPreview) {
3352
+ viewRenderedSnapshot(_snapshot, _isPreview) { }
3353
+ preloadOnLoadLinksForView(element) {
3354
+ session.preloadOnLoadLinksForView(element);
3013
3355
  }
3014
- viewInvalidated() {
3356
+ viewInvalidated() { }
3357
+ frameExtracted(element) {
3358
+ this.previousFrameElement = element;
3015
3359
  }
3016
3360
  async visit(url) {
3017
3361
  var _a;
3018
- const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams, this.element);
3362
+ const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
3019
3363
  (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
3020
3364
  this.currentFetchRequest = request;
3021
- return new Promise(resolve => {
3365
+ return new Promise((resolve) => {
3022
3366
  this.resolveVisitPromise = () => {
3023
3367
  this.resolveVisitPromise = () => { };
3024
3368
  this.currentFetchRequest = null;
@@ -3030,19 +3374,23 @@ Copyright © 2021 Basecamp, LLC
3030
3374
  navigateFrame(element, url, submitter) {
3031
3375
  const frame = this.findFrameElement(element, submitter);
3032
3376
  this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
3033
- frame.setAttribute("reloadable", "");
3034
3377
  frame.src = url;
3035
3378
  }
3036
3379
  proposeVisitIfNavigatedWithAction(frame, element, submitter) {
3037
3380
  const action = getAttribute("data-turbo-action", submitter, element, frame);
3038
3381
  if (isAction(action)) {
3039
- const { visitCachedSnapshot } = new SnapshotSubstitution(frame);
3382
+ const { visitCachedSnapshot } = frame.delegate;
3040
3383
  frame.delegate.fetchResponseLoaded = (fetchResponse) => {
3041
3384
  if (frame.src) {
3042
3385
  const { statusCode, redirected } = fetchResponse;
3043
3386
  const responseHTML = frame.ownerDocument.documentElement.outerHTML;
3044
3387
  const response = { statusCode, redirected, responseHTML };
3045
- session.visit(frame.src, { action, response, visitCachedSnapshot, willRender: false });
3388
+ session.visit(frame.src, {
3389
+ action,
3390
+ response,
3391
+ visitCachedSnapshot,
3392
+ willRender: false,
3393
+ });
3046
3394
  }
3047
3395
  };
3048
3396
  }
@@ -3056,10 +3404,12 @@ Copyright © 2021 Basecamp, LLC
3056
3404
  let element;
3057
3405
  const id = CSS.escape(this.id);
3058
3406
  try {
3059
- if (element = activateElement(container.querySelector(`turbo-frame#${id}`), this.currentURL)) {
3407
+ element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
3408
+ if (element) {
3060
3409
  return element;
3061
3410
  }
3062
- if (element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.currentURL)) {
3411
+ element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
3412
+ if (element) {
3063
3413
  await element.loaded;
3064
3414
  return await this.extractForeignFrameElement(element);
3065
3415
  }
@@ -3107,24 +3457,10 @@ Copyright © 2021 Basecamp, LLC
3107
3457
  return this.element.src;
3108
3458
  }
3109
3459
  }
3110
- get reloadable() {
3111
- const frame = this.findFrameElement(this.element);
3112
- return frame.hasAttribute("reloadable");
3113
- }
3114
- set reloadable(value) {
3115
- const frame = this.findFrameElement(this.element);
3116
- if (value) {
3117
- frame.setAttribute("reloadable", "");
3118
- }
3119
- else {
3120
- frame.removeAttribute("reloadable");
3121
- }
3122
- }
3123
3460
  set sourceURL(sourceURL) {
3124
- this.settingSourceURL = true;
3125
- this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3126
- this.currentURL = this.element.src;
3127
- this.settingSourceURL = false;
3461
+ this.ignoringChangesToAttribute("src", () => {
3462
+ this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3463
+ });
3128
3464
  }
3129
3465
  get loadingStyle() {
3130
3466
  return this.element.loading;
@@ -3132,6 +3468,19 @@ Copyright © 2021 Basecamp, LLC
3132
3468
  get isLoading() {
3133
3469
  return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
3134
3470
  }
3471
+ get complete() {
3472
+ return this.element.hasAttribute("complete");
3473
+ }
3474
+ set complete(value) {
3475
+ this.ignoringChangesToAttribute("complete", () => {
3476
+ if (value) {
3477
+ this.element.setAttribute("complete", "");
3478
+ }
3479
+ else {
3480
+ this.element.removeAttribute("complete");
3481
+ }
3482
+ });
3483
+ }
3135
3484
  get isActive() {
3136
3485
  return this.element.isActive && this.connected;
3137
3486
  }
@@ -3141,16 +3490,13 @@ Copyright © 2021 Basecamp, LLC
3141
3490
  const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
3142
3491
  return expandURL(root);
3143
3492
  }
3144
- }
3145
- class SnapshotSubstitution {
3146
- constructor(element) {
3147
- this.visitCachedSnapshot = ({ element }) => {
3148
- var _a;
3149
- const { id, clone } = this;
3150
- (_a = element.querySelector("#" + id)) === null || _a === void 0 ? void 0 : _a.replaceWith(clone);
3151
- };
3152
- this.clone = element.cloneNode(true);
3153
- this.id = element.id;
3493
+ isIgnoringChangesTo(attributeName) {
3494
+ return this.ignoredAttributes.has(attributeName);
3495
+ }
3496
+ ignoringChangesToAttribute(attributeName, callback) {
3497
+ this.ignoredAttributes.add(attributeName);
3498
+ callback();
3499
+ this.ignoredAttributes.delete(attributeName);
3154
3500
  }
3155
3501
  }
3156
3502
  function getFrameElementById(id) {
@@ -3178,35 +3524,6 @@ Copyright © 2021 Basecamp, LLC
3178
3524
  }
3179
3525
  }
3180
3526
 
3181
- const StreamActions = {
3182
- after() {
3183
- this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
3184
- },
3185
- append() {
3186
- this.removeDuplicateTargetChildren();
3187
- this.targetElements.forEach(e => e.append(this.templateContent));
3188
- },
3189
- before() {
3190
- this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
3191
- },
3192
- prepend() {
3193
- this.removeDuplicateTargetChildren();
3194
- this.targetElements.forEach(e => e.prepend(this.templateContent));
3195
- },
3196
- remove() {
3197
- this.targetElements.forEach(e => e.remove());
3198
- },
3199
- replace() {
3200
- this.targetElements.forEach(e => e.replaceWith(this.templateContent));
3201
- },
3202
- update() {
3203
- this.targetElements.forEach(e => {
3204
- e.innerHTML = "";
3205
- e.append(this.templateContent);
3206
- });
3207
- }
3208
- };
3209
-
3210
3527
  class StreamElement extends HTMLElement {
3211
3528
  async connectedCallback() {
3212
3529
  try {
@@ -3221,12 +3538,12 @@ Copyright © 2021 Basecamp, LLC
3221
3538
  }
3222
3539
  async render() {
3223
3540
  var _a;
3224
- return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
3541
+ return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
3225
3542
  if (this.dispatchEvent(this.beforeRenderEvent)) {
3226
3543
  await nextAnimationFrame();
3227
3544
  this.performAction();
3228
3545
  }
3229
- })());
3546
+ })()));
3230
3547
  }
3231
3548
  disconnect() {
3232
3549
  try {
@@ -3235,13 +3552,13 @@ Copyright © 2021 Basecamp, LLC
3235
3552
  catch (_a) { }
3236
3553
  }
3237
3554
  removeDuplicateTargetChildren() {
3238
- this.duplicateChildren.forEach(c => c.remove());
3555
+ this.duplicateChildren.forEach((c) => c.remove());
3239
3556
  }
3240
3557
  get duplicateChildren() {
3241
3558
  var _a;
3242
- const existingChildren = this.targetElements.flatMap(e => [...e.children]).filter(c => !!c.id);
3243
- const newChildrenIds = [...(_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children].filter(c => !!c.id).map(c => c.id);
3244
- return existingChildren.filter(c => newChildrenIds.includes(c.id));
3559
+ const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
3560
+ const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
3561
+ return existingChildren.filter((c) => newChildrenIds.includes(c.id));
3245
3562
  }
3246
3563
  get performAction() {
3247
3564
  if (this.action) {
@@ -3290,7 +3607,10 @@ Copyright © 2021 Basecamp, LLC
3290
3607
  return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
3291
3608
  }
3292
3609
  get beforeRenderEvent() {
3293
- return new CustomEvent("turbo:before-stream-render", { bubbles: true, cancelable: true });
3610
+ return new CustomEvent("turbo:before-stream-render", {
3611
+ bubbles: true,
3612
+ cancelable: true,
3613
+ });
3294
3614
  }
3295
3615
  get targetElementsById() {
3296
3616
  var _a;
@@ -3314,9 +3634,35 @@ Copyright © 2021 Basecamp, LLC
3314
3634
  }
3315
3635
  }
3316
3636
 
3637
+ class StreamSourceElement extends HTMLElement {
3638
+ constructor() {
3639
+ super(...arguments);
3640
+ this.streamSource = null;
3641
+ }
3642
+ connectedCallback() {
3643
+ this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
3644
+ connectStreamSource(this.streamSource);
3645
+ }
3646
+ disconnectedCallback() {
3647
+ if (this.streamSource) {
3648
+ disconnectStreamSource(this.streamSource);
3649
+ }
3650
+ }
3651
+ get src() {
3652
+ return this.getAttribute("src") || "";
3653
+ }
3654
+ }
3655
+
3317
3656
  FrameElement.delegateConstructor = FrameController;
3318
- customElements.define("turbo-frame", FrameElement);
3319
- customElements.define("turbo-stream", StreamElement);
3657
+ if (customElements.get("turbo-frame") === undefined) {
3658
+ customElements.define("turbo-frame", FrameElement);
3659
+ }
3660
+ if (customElements.get("turbo-stream") === undefined) {
3661
+ customElements.define("turbo-stream", StreamElement);
3662
+ }
3663
+ if (customElements.get("turbo-stream-source") === undefined) {
3664
+ customElements.define("turbo-stream-source", StreamSourceElement);
3665
+ }
3320
3666
 
3321
3667
  (() => {
3322
3668
  let element = document.currentScript;
@@ -3324,7 +3670,8 @@ Copyright © 2021 Basecamp, LLC
3324
3670
  return;
3325
3671
  if (element.hasAttribute("data-turbo-suppress-warning"))
3326
3672
  return;
3327
- while (element = element.parentElement) {
3673
+ element = element.parentElement;
3674
+ while (element) {
3328
3675
  if (element == document.body) {
3329
3676
  return console.warn(unindent `
3330
3677
  You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
@@ -3337,14 +3684,18 @@ Copyright © 2021 Basecamp, LLC
3337
3684
  Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
3338
3685
  `, element.outerHTML);
3339
3686
  }
3687
+ element = element.parentElement;
3340
3688
  }
3341
3689
  })();
3342
3690
 
3343
3691
  window.Turbo = Turbo;
3344
3692
  start();
3345
3693
 
3694
+ exports.FrameRenderer = FrameRenderer;
3346
3695
  exports.PageRenderer = PageRenderer;
3347
3696
  exports.PageSnapshot = PageSnapshot;
3697
+ exports.StreamActions = StreamActions;
3698
+ exports.cache = cache;
3348
3699
  exports.clearCache = clearCache;
3349
3700
  exports.connectStreamSource = connectStreamSource;
3350
3701
  exports.disconnectStreamSource = disconnectStreamSource;
@@ -3353,10 +3704,11 @@ Copyright © 2021 Basecamp, LLC
3353
3704
  exports.renderStreamMessage = renderStreamMessage;
3354
3705
  exports.session = session;
3355
3706
  exports.setConfirmMethod = setConfirmMethod;
3707
+ exports.setFormMode = setFormMode;
3356
3708
  exports.setProgressBarDelay = setProgressBarDelay;
3357
3709
  exports.start = start;
3358
3710
  exports.visit = visit;
3359
3711
 
3360
3712
  Object.defineProperty(exports, '__esModule', { value: true });
3361
3713
 
3362
- })));
3714
+ }));