@hotwired/turbo 7.1.0-rc.2 → 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 +730 -391
  2. package/dist/turbo.es2017-umd.js +735 -392
  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 +13 -4
  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 -79
  40. package/dist/types/tests/functional/frame_navigation_tests.d.ts +1 -7
  41. package/dist/types/tests/functional/frame_tests.d.ts +1 -51
  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 -37
  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 -13
  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-rc.1
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,27 +423,27 @@ 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;
413
- if (this.isIdempotent) {
414
- this.url = mergeFormDataEntries(location, [...body.entries()]);
415
- }
416
- else {
417
- this.body = body;
418
- this.url = location;
419
- }
445
+ this.body = body;
446
+ this.url = location;
420
447
  this.target = target;
421
448
  }
422
449
  get location() {
@@ -442,7 +469,7 @@ Copyright © 2021 Basecamp, LLC
442
469
  return await this.receive(response);
443
470
  }
444
471
  catch (error) {
445
- if (error.name !== 'AbortError') {
472
+ if (error.name !== "AbortError") {
446
473
  this.delegate.requestErrored(this, error);
447
474
  throw error;
448
475
  }
@@ -453,7 +480,11 @@ Copyright © 2021 Basecamp, LLC
453
480
  }
454
481
  async receive(response) {
455
482
  const fetchResponse = new FetchResponse(response);
456
- 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
+ });
457
488
  if (event.defaultPrevented) {
458
489
  this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
459
490
  }
@@ -472,14 +503,14 @@ Copyright © 2021 Basecamp, LLC
472
503
  credentials: "same-origin",
473
504
  headers: this.headers,
474
505
  redirect: "follow",
475
- body: this.body,
506
+ body: this.isIdempotent ? null : this.body,
476
507
  signal: this.abortSignal,
477
- 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,
478
509
  };
479
510
  }
480
511
  get defaultHeaders() {
481
512
  return {
482
- "Accept": "text/html, application/xhtml+xml"
513
+ Accept: "text/html, application/xhtml+xml",
483
514
  };
484
515
  }
485
516
  get isIdempotent() {
@@ -489,40 +520,25 @@ Copyright © 2021 Basecamp, LLC
489
520
  return this.abortController.signal;
490
521
  }
491
522
  async allowRequestToBeIntercepted(fetchOptions) {
492
- const requestInterception = new Promise(resolve => this.resolveRequestPromise = resolve);
523
+ const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
493
524
  const event = dispatch("turbo:before-fetch-request", {
494
525
  cancelable: true,
495
526
  detail: {
496
527
  fetchOptions,
497
- url: this.url.href,
498
- resume: this.resolveRequestPromise
528
+ url: this.url,
529
+ resume: this.resolveRequestPromise,
499
530
  },
500
- target: this.target
531
+ target: this.target,
501
532
  });
502
533
  if (event.defaultPrevented)
503
534
  await requestInterception;
504
535
  }
505
536
  }
506
- function mergeFormDataEntries(url, entries) {
507
- const currentSearchParams = new URLSearchParams(url.search);
508
- for (const [name, value] of entries) {
509
- if (value instanceof File)
510
- continue;
511
- if (currentSearchParams.has(name)) {
512
- currentSearchParams.delete(name);
513
- url.searchParams.set(name, value);
514
- }
515
- else {
516
- url.searchParams.append(name, value);
517
- }
518
- }
519
- return url;
520
- }
521
537
 
522
538
  class AppearanceObserver {
523
539
  constructor(delegate, element) {
524
540
  this.started = false;
525
- this.intersect = entries => {
541
+ this.intersect = (entries) => {
526
542
  const lastEntry = entries.slice(-1)[0];
527
543
  if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
528
544
  this.delegate.elementAppearedInViewport(this.element);
@@ -599,9 +615,12 @@ Copyright © 2021 Basecamp, LLC
599
615
  })(FormEnctype || (FormEnctype = {}));
600
616
  function formEnctypeFromString(encoding) {
601
617
  switch (encoding.toLowerCase()) {
602
- case FormEnctype.multipart: return FormEnctype.multipart;
603
- case FormEnctype.plain: return FormEnctype.plain;
604
- 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;
605
624
  }
606
625
  }
607
626
  class FormSubmission {
@@ -611,11 +630,15 @@ Copyright © 2021 Basecamp, LLC
611
630
  this.formElement = formElement;
612
631
  this.submitter = submitter;
613
632
  this.formData = buildFormData(formElement, submitter);
633
+ this.location = expandURL(this.action);
634
+ if (this.method == FetchMethod.get) {
635
+ mergeFormDataEntries(this.location, [...this.body.entries()]);
636
+ }
614
637
  this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
615
638
  this.mustRedirect = mustRedirect;
616
639
  }
617
- static confirmMethod(message, element) {
618
- return confirm(message);
640
+ static confirmMethod(message, _element) {
641
+ return Promise.resolve(confirm(message));
619
642
  }
620
643
  get method() {
621
644
  var _a;
@@ -624,11 +647,13 @@ Copyright © 2021 Basecamp, LLC
624
647
  }
625
648
  get action() {
626
649
  var _a;
627
- const formElementAction = typeof this.formElement.action === 'string' ? this.formElement.action : null;
628
- return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
629
- }
630
- get location() {
631
- return expandURL(this.action);
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
+ }
632
657
  }
633
658
  get body() {
634
659
  if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
@@ -651,7 +676,8 @@ Copyright © 2021 Basecamp, LLC
651
676
  }, []);
652
677
  }
653
678
  get confirmationMessage() {
654
- 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");
655
681
  }
656
682
  get needsConfirmation() {
657
683
  return this.confirmationMessage !== null;
@@ -659,7 +685,7 @@ Copyright © 2021 Basecamp, LLC
659
685
  async start() {
660
686
  const { initialized, requesting } = FormSubmissionState;
661
687
  if (this.needsConfirmation) {
662
- const answer = FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
688
+ const answer = await FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
663
689
  if (!answer) {
664
690
  return;
665
691
  }
@@ -683,14 +709,19 @@ Copyright © 2021 Basecamp, LLC
683
709
  if (token) {
684
710
  headers["X-CSRF-Token"] = token;
685
711
  }
712
+ }
713
+ if (this.requestAcceptsTurboStreamResponse(request)) {
686
714
  headers["Accept"] = [StreamMessage.contentType, headers["Accept"]].join(", ");
687
715
  }
688
716
  }
689
- requestStarted(request) {
717
+ requestStarted(_request) {
690
718
  var _a;
691
719
  this.state = FormSubmissionState.waiting;
692
720
  (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
693
- dispatch("turbo:submit-start", { target: this.formElement, detail: { formSubmission: this } });
721
+ dispatch("turbo:submit-start", {
722
+ target: this.formElement,
723
+ detail: { formSubmission: this },
724
+ });
694
725
  this.delegate.formSubmissionStarted(this);
695
726
  }
696
727
  requestPreventedHandlingResponse(request, response) {
@@ -718,16 +749,22 @@ Copyright © 2021 Basecamp, LLC
718
749
  this.result = { success: false, error };
719
750
  this.delegate.formSubmissionErrored(this, error);
720
751
  }
721
- requestFinished(request) {
752
+ requestFinished(_request) {
722
753
  var _a;
723
754
  this.state = FormSubmissionState.stopped;
724
755
  (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
725
- 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
+ });
726
760
  this.delegate.formSubmissionFinished(this);
727
761
  }
728
762
  requestMustRedirect(request) {
729
763
  return !request.isIdempotent && this.mustRedirect;
730
764
  }
765
+ requestAcceptsTurboStreamResponse(request) {
766
+ return !request.isIdempotent || this.formElement.hasAttribute("data-turbo-stream");
767
+ }
731
768
  }
732
769
  function buildFormData(formElement, submitter) {
733
770
  const formData = new FormData(formElement);
@@ -748,18 +785,27 @@ Copyright © 2021 Basecamp, LLC
748
785
  }
749
786
  }
750
787
  }
751
- function getMetaContent(name) {
752
- const element = document.querySelector(`meta[name="${name}"]`);
753
- return element && element.content;
754
- }
755
788
  function responseSucceededWithoutRedirect(response) {
756
789
  return response.statusCode == 200 && !response.redirected;
757
790
  }
791
+ function mergeFormDataEntries(url, entries) {
792
+ const searchParams = new URLSearchParams();
793
+ for (const [name, value] of entries) {
794
+ if (value instanceof File)
795
+ continue;
796
+ searchParams.append(name, value);
797
+ }
798
+ url.search = searchParams.toString();
799
+ return url;
800
+ }
758
801
 
759
802
  class Snapshot {
760
803
  constructor(element) {
761
804
  this.element = element;
762
805
  }
806
+ get activeElement() {
807
+ return this.element.ownerDocument.activeElement;
808
+ }
763
809
  get children() {
764
810
  return [...this.element.children];
765
811
  }
@@ -798,7 +844,9 @@ Copyright © 2021 Basecamp, LLC
798
844
  constructor(delegate, element) {
799
845
  this.submitBubbled = ((event) => {
800
846
  const form = event.target;
801
- if (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) {
802
850
  const submitter = event.submitter || undefined;
803
851
  const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
804
852
  if (method != "dialog" && this.delegate.shouldInterceptFormSubmission(form, submitter)) {
@@ -821,8 +869,8 @@ Copyright © 2021 Basecamp, LLC
821
869
 
822
870
  class View {
823
871
  constructor(delegate, element) {
824
- this.resolveRenderPromise = (value) => { };
825
- this.resolveInterceptionPromise = (value) => { };
872
+ this.resolveRenderPromise = (_value) => { };
873
+ this.resolveInterceptionPromise = (_value) => { };
826
874
  this.delegate = delegate;
827
875
  this.element = element;
828
876
  }
@@ -867,15 +915,17 @@ Copyright © 2021 Basecamp, LLC
867
915
  const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
868
916
  if (shouldRender) {
869
917
  try {
870
- this.renderPromise = new Promise(resolve => this.resolveRenderPromise = resolve);
918
+ this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
871
919
  this.renderer = renderer;
872
920
  this.prepareToRenderSnapshot(renderer);
873
- const renderInterception = new Promise(resolve => this.resolveInterceptionPromise = resolve);
874
- 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);
875
924
  if (!immediateRender)
876
925
  await renderInterception;
877
926
  await this.renderSnapshot(renderer);
878
927
  this.delegate.viewRenderedSnapshot(snapshot, isPreview);
928
+ this.delegate.preloadOnLoadLinksForView(this.element);
879
929
  this.finishRenderingSnapshot(renderer);
880
930
  }
881
931
  finally {
@@ -885,11 +935,11 @@ Copyright © 2021 Basecamp, LLC
885
935
  }
886
936
  }
887
937
  else {
888
- this.invalidate();
938
+ this.invalidate(renderer.reloadReason);
889
939
  }
890
940
  }
891
- invalidate() {
892
- this.delegate.viewInvalidated();
941
+ invalidate(reason) {
942
+ this.delegate.viewInvalidated(reason);
893
943
  }
894
944
  prepareToRenderSnapshot(renderer) {
895
945
  this.markAsPreview(renderer.isPreview);
@@ -940,9 +990,9 @@ Copyright © 2021 Basecamp, LLC
940
990
  }
941
991
  delete this.clickEvent;
942
992
  });
943
- this.willVisit = () => {
993
+ this.willVisit = ((_event) => {
944
994
  delete this.clickEvent;
945
- };
995
+ });
946
996
  this.delegate = delegate;
947
997
  this.element = element;
948
998
  }
@@ -957,28 +1007,65 @@ Copyright © 2021 Basecamp, LLC
957
1007
  document.removeEventListener("turbo:before-visit", this.willVisit);
958
1008
  }
959
1009
  respondsToEventTarget(target) {
960
- const element = target instanceof Element
961
- ? target
962
- : target instanceof Node
963
- ? target.parentElement
964
- : null;
1010
+ const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
965
1011
  return element && element.closest("turbo-frame, html") == this.element;
966
1012
  }
967
1013
  }
968
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
+
969
1054
  class Bardo {
970
- constructor(permanentElementMap) {
1055
+ constructor(delegate, permanentElementMap) {
1056
+ this.delegate = delegate;
971
1057
  this.permanentElementMap = permanentElementMap;
972
1058
  }
973
- static preservingPermanentElements(permanentElementMap, callback) {
974
- const bardo = new this(permanentElementMap);
1059
+ static preservingPermanentElements(delegate, permanentElementMap, callback) {
1060
+ const bardo = new this(delegate, permanentElementMap);
975
1061
  bardo.enter();
976
1062
  callback();
977
1063
  bardo.leave();
978
1064
  }
979
1065
  enter() {
980
1066
  for (const id in this.permanentElementMap) {
981
- const [, newPermanentElement] = this.permanentElementMap[id];
1067
+ const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
1068
+ this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
982
1069
  this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
983
1070
  }
984
1071
  }
@@ -987,6 +1074,7 @@ Copyright © 2021 Basecamp, LLC
987
1074
  const [currentPermanentElement] = this.permanentElementMap[id];
988
1075
  this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
989
1076
  this.replacePlaceholderWithPermanentElement(currentPermanentElement);
1077
+ this.delegate.leavingBardo(currentPermanentElement);
990
1078
  }
991
1079
  }
992
1080
  replaceNewPermanentElementWithPlaceholder(permanentElement) {
@@ -1002,7 +1090,7 @@ Copyright © 2021 Basecamp, LLC
1002
1090
  placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
1003
1091
  }
1004
1092
  getPlaceholderById(id) {
1005
- return this.placeholders.find(element => element.content == id);
1093
+ return this.placeholders.find((element) => element.content == id);
1006
1094
  }
1007
1095
  get placeholders() {
1008
1096
  return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
@@ -1016,16 +1104,21 @@ Copyright © 2021 Basecamp, LLC
1016
1104
  }
1017
1105
 
1018
1106
  class Renderer {
1019
- constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
1107
+ constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1108
+ this.activeElement = null;
1020
1109
  this.currentSnapshot = currentSnapshot;
1021
1110
  this.newSnapshot = newSnapshot;
1022
1111
  this.isPreview = isPreview;
1023
1112
  this.willRender = willRender;
1024
- 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 }));
1025
1115
  }
1026
1116
  get shouldRender() {
1027
1117
  return true;
1028
1118
  }
1119
+ get reloadReason() {
1120
+ return;
1121
+ }
1029
1122
  prepareToRender() {
1030
1123
  return;
1031
1124
  }
@@ -1051,7 +1144,7 @@ Copyright © 2021 Basecamp, LLC
1051
1144
  }
1052
1145
  }
1053
1146
  preservingPermanentElements(callback) {
1054
- Bardo.preservingPermanentElements(this.permanentElementMap, callback);
1147
+ Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
1055
1148
  }
1056
1149
  focusFirstAutofocusableElement() {
1057
1150
  const element = this.connectedSnapshot.firstAutofocusableElement;
@@ -1059,6 +1152,19 @@ Copyright © 2021 Basecamp, LLC
1059
1152
  element.focus();
1060
1153
  }
1061
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
+ }
1062
1168
  get connectedSnapshot() {
1063
1169
  return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
1064
1170
  }
@@ -1072,8 +1178,7 @@ Copyright © 2021 Basecamp, LLC
1072
1178
  return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
1073
1179
  }
1074
1180
  get cspNonce() {
1075
- var _a;
1076
- return (_a = document.head.querySelector('meta[name="csp-nonce"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
1181
+ return getMetaContent("csp-nonce");
1077
1182
  }
1078
1183
  }
1079
1184
  function copyElementAttributes(destinationElement, sourceElement) {
@@ -1086,6 +1191,22 @@ Copyright © 2021 Basecamp, LLC
1086
1191
  }
1087
1192
 
1088
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
+ }
1089
1210
  get shouldRender() {
1090
1211
  return true;
1091
1212
  }
@@ -1101,23 +1222,16 @@ Copyright © 2021 Basecamp, LLC
1101
1222
  this.activateScriptElements();
1102
1223
  }
1103
1224
  loadFrameElement() {
1104
- var _a;
1105
- const destinationRange = document.createRange();
1106
- destinationRange.selectNodeContents(this.currentElement);
1107
- destinationRange.deleteContents();
1108
- const frameElement = this.newElement;
1109
- const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1110
- if (sourceRange) {
1111
- sourceRange.selectNodeContents(frameElement);
1112
- this.currentElement.appendChild(sourceRange.extractContents());
1113
- }
1225
+ this.delegate.frameExtracted(this.newElement.cloneNode(true));
1226
+ this.renderElement(this.currentElement, this.newElement);
1114
1227
  }
1115
1228
  scrollFrameIntoView() {
1116
1229
  if (this.currentElement.autoscroll || this.newElement.autoscroll) {
1117
1230
  const element = this.currentElement.firstElementChild;
1118
1231
  const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
1232
+ const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
1119
1233
  if (element) {
1120
- element.scrollIntoView({ block });
1234
+ element.scrollIntoView({ block, behavior });
1121
1235
  return true;
1122
1236
  }
1123
1237
  }
@@ -1141,6 +1255,14 @@ Copyright © 2021 Basecamp, LLC
1141
1255
  return defaultValue;
1142
1256
  }
1143
1257
  }
1258
+ function readScrollBehavior(value, defaultValue) {
1259
+ if (value == "auto" || value == "smooth") {
1260
+ return value;
1261
+ }
1262
+ else {
1263
+ return defaultValue;
1264
+ }
1265
+ }
1144
1266
 
1145
1267
  class ProgressBar {
1146
1268
  constructor() {
@@ -1164,7 +1286,7 @@ Copyright © 2021 Basecamp, LLC
1164
1286
  left: 0;
1165
1287
  height: 3px;
1166
1288
  background: #0076ff;
1167
- z-index: 9999;
1289
+ z-index: 2147483647;
1168
1290
  transition:
1169
1291
  width ${ProgressBar.animationDuration}ms ease-out,
1170
1292
  opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
@@ -1223,13 +1345,16 @@ Copyright © 2021 Basecamp, LLC
1223
1345
  }
1224
1346
  refresh() {
1225
1347
  requestAnimationFrame(() => {
1226
- this.progressElement.style.width = `${10 + (this.value * 90)}%`;
1348
+ this.progressElement.style.width = `${10 + this.value * 90}%`;
1227
1349
  });
1228
1350
  }
1229
1351
  createStylesheetElement() {
1230
1352
  const element = document.createElement("style");
1231
1353
  element.type = "text/css";
1232
1354
  element.textContent = ProgressBar.defaultCSS;
1355
+ if (this.cspNonce) {
1356
+ element.nonce = this.cspNonce;
1357
+ }
1233
1358
  return element;
1234
1359
  }
1235
1360
  createProgressElement() {
@@ -1237,6 +1362,9 @@ Copyright © 2021 Basecamp, LLC
1237
1362
  element.className = "turbo-progress-bar";
1238
1363
  return element;
1239
1364
  }
1365
+ get cspNonce() {
1366
+ return getMetaContent("csp-nonce");
1367
+ }
1240
1368
  }
1241
1369
  ProgressBar.animationDuration = 300;
1242
1370
 
@@ -1253,14 +1381,14 @@ Copyright © 2021 Basecamp, LLC
1253
1381
  : {
1254
1382
  type: elementType(element),
1255
1383
  tracked: elementIsTracked(element),
1256
- elements: []
1384
+ elements: [],
1257
1385
  };
1258
1386
  return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
1259
1387
  }, {});
1260
1388
  }
1261
1389
  get trackedElementSignature() {
1262
1390
  return Object.keys(this.detailsByOuterHTML)
1263
- .filter(outerHTML => this.detailsByOuterHTML[outerHTML].tracked)
1391
+ .filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
1264
1392
  .join("");
1265
1393
  }
1266
1394
  getScriptElementsNotInSnapshot(snapshot) {
@@ -1271,8 +1399,8 @@ Copyright © 2021 Basecamp, LLC
1271
1399
  }
1272
1400
  getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
1273
1401
  return Object.keys(this.detailsByOuterHTML)
1274
- .filter(outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))
1275
- .map(outerHTML => this.detailsByOuterHTML[outerHTML])
1402
+ .filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
1403
+ .map((outerHTML) => this.detailsByOuterHTML[outerHTML])
1276
1404
  .filter(({ type }) => type == matchedType)
1277
1405
  .map(({ elements: [element] }) => element);
1278
1406
  }
@@ -1292,13 +1420,11 @@ Copyright © 2021 Basecamp, LLC
1292
1420
  }
1293
1421
  getMetaValue(name) {
1294
1422
  const element = this.findMetaElementByName(name);
1295
- return element
1296
- ? element.getAttribute("content")
1297
- : null;
1423
+ return element ? element.getAttribute("content") : null;
1298
1424
  }
1299
1425
  findMetaElementByName(name) {
1300
1426
  return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
1301
- const { elements: [element] } = this.detailsByOuterHTML[outerHTML];
1427
+ const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
1302
1428
  return elementIsMetaElementWithName(element, name) ? element : result;
1303
1429
  }, undefined);
1304
1430
  }
@@ -1465,9 +1591,11 @@ Copyright © 2021 Basecamp, LLC
1465
1591
  if (this.state == VisitState.started) {
1466
1592
  this.recordTimingMetric(TimingMetric.visitEnd);
1467
1593
  this.state = VisitState.completed;
1468
- this.adapter.visitCompleted(this);
1469
- this.delegate.visitCompleted(this);
1470
1594
  this.followRedirect();
1595
+ if (!this.followedRedirect) {
1596
+ this.adapter.visitCompleted(this);
1597
+ this.delegate.visitCompleted(this);
1598
+ }
1471
1599
  }
1472
1600
  }
1473
1601
  fail() {
@@ -1529,12 +1657,12 @@ Copyright © 2021 Basecamp, LLC
1529
1657
  if (this.view.renderPromise)
1530
1658
  await this.view.renderPromise;
1531
1659
  if (isSuccessful(statusCode) && responseHTML != null) {
1532
- await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender);
1660
+ await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
1533
1661
  this.adapter.visitRendered(this);
1534
1662
  this.complete();
1535
1663
  }
1536
1664
  else {
1537
- await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML));
1665
+ await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
1538
1666
  this.adapter.visitRendered(this);
1539
1667
  this.fail();
1540
1668
  }
@@ -1569,7 +1697,7 @@ Copyright © 2021 Basecamp, LLC
1569
1697
  else {
1570
1698
  if (this.view.renderPromise)
1571
1699
  await this.view.renderPromise;
1572
- await this.view.renderPage(snapshot, isPreview, this.willRender);
1700
+ await this.view.renderPage(snapshot, isPreview, this.willRender, this);
1573
1701
  this.adapter.visitRendered(this);
1574
1702
  if (!isPreview) {
1575
1703
  this.complete();
@@ -1582,8 +1710,9 @@ Copyright © 2021 Basecamp, LLC
1582
1710
  var _a;
1583
1711
  if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
1584
1712
  this.adapter.visitProposedToLocation(this.redirectedToLocation, {
1585
- action: 'replace',
1586
- response: this.response
1713
+ action: "replace",
1714
+ willRender: false,
1715
+ response: this.response,
1587
1716
  });
1588
1717
  this.followedRedirect = true;
1589
1718
  }
@@ -1599,13 +1728,15 @@ Copyright © 2021 Basecamp, LLC
1599
1728
  requestStarted() {
1600
1729
  this.startRequest();
1601
1730
  }
1602
- requestPreventedHandlingResponse(request, response) {
1603
- }
1731
+ requestPreventedHandlingResponse(_request, _response) { }
1604
1732
  async requestSucceededWithResponse(request, response) {
1605
1733
  const responseHTML = await response.responseHTML;
1606
1734
  const { redirected, statusCode } = response;
1607
1735
  if (responseHTML == undefined) {
1608
- this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
1736
+ this.recordResponse({
1737
+ statusCode: SystemStatusCode.contentTypeMismatch,
1738
+ redirected,
1739
+ });
1609
1740
  }
1610
1741
  else {
1611
1742
  this.redirectedToLocation = response.redirected ? response.location : undefined;
@@ -1616,14 +1747,20 @@ Copyright © 2021 Basecamp, LLC
1616
1747
  const responseHTML = await response.responseHTML;
1617
1748
  const { redirected, statusCode } = response;
1618
1749
  if (responseHTML == undefined) {
1619
- this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
1750
+ this.recordResponse({
1751
+ statusCode: SystemStatusCode.contentTypeMismatch,
1752
+ redirected,
1753
+ });
1620
1754
  }
1621
1755
  else {
1622
1756
  this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
1623
1757
  }
1624
1758
  }
1625
- requestErrored(request, error) {
1626
- this.recordResponse({ statusCode: SystemStatusCode.networkFailure, redirected: false });
1759
+ requestErrored(_request, _error) {
1760
+ this.recordResponse({
1761
+ statusCode: SystemStatusCode.networkFailure,
1762
+ redirected: false,
1763
+ });
1627
1764
  }
1628
1765
  requestFinished() {
1629
1766
  this.finishRequest();
@@ -1664,9 +1801,11 @@ Copyright © 2021 Basecamp, LLC
1664
1801
  }
1665
1802
  getHistoryMethodForAction(action) {
1666
1803
  switch (action) {
1667
- case "replace": return history.replaceState;
1804
+ case "replace":
1805
+ return history.replaceState;
1668
1806
  case "advance":
1669
- case "restore": return history.pushState;
1807
+ case "restore":
1808
+ return history.pushState;
1670
1809
  }
1671
1810
  }
1672
1811
  hasPreloadedResponse() {
@@ -1685,18 +1824,20 @@ Copyright © 2021 Basecamp, LLC
1685
1824
  }
1686
1825
  cacheSnapshot() {
1687
1826
  if (!this.snapshotCached) {
1688
- this.view.cacheSnapshot().then(snapshot => snapshot && this.visitCachedSnapshot(snapshot));
1827
+ this.view.cacheSnapshot().then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
1689
1828
  this.snapshotCached = true;
1690
1829
  }
1691
1830
  }
1692
1831
  async render(callback) {
1693
1832
  this.cancelRender();
1694
- await new Promise(resolve => {
1833
+ await new Promise((resolve) => {
1695
1834
  this.frame = requestAnimationFrame(() => resolve());
1696
1835
  });
1697
1836
  await callback();
1698
1837
  delete this.frame;
1699
- this.performScroll();
1838
+ if (!this.view.forceReloaded) {
1839
+ this.performScroll();
1840
+ }
1700
1841
  }
1701
1842
  cancelRender() {
1702
1843
  if (this.frame) {
@@ -1711,7 +1852,7 @@ Copyright © 2021 Basecamp, LLC
1711
1852
 
1712
1853
  class BrowserAdapter {
1713
1854
  constructor(session) {
1714
- this.progressBar = new ProgressBar;
1855
+ this.progressBar = new ProgressBar();
1715
1856
  this.showProgressBar = () => {
1716
1857
  this.progressBar.show();
1717
1858
  };
@@ -1721,10 +1862,10 @@ Copyright © 2021 Basecamp, LLC
1721
1862
  this.navigator.startVisit(location, uuid(), options);
1722
1863
  }
1723
1864
  visitStarted(visit) {
1865
+ this.location = visit.location;
1866
+ visit.loadCachedSnapshot();
1724
1867
  visit.issueRequest();
1725
- visit.changeHistory();
1726
1868
  visit.goToSamePageAnchor();
1727
- visit.loadCachedSnapshot();
1728
1869
  }
1729
1870
  visitRequestStarted(visit) {
1730
1871
  this.progressBar.setValue(0);
@@ -1743,29 +1884,31 @@ Copyright © 2021 Basecamp, LLC
1743
1884
  case SystemStatusCode.networkFailure:
1744
1885
  case SystemStatusCode.timeoutFailure:
1745
1886
  case SystemStatusCode.contentTypeMismatch:
1746
- return this.reload();
1887
+ return this.reload({
1888
+ reason: "request_failed",
1889
+ context: {
1890
+ statusCode,
1891
+ },
1892
+ });
1747
1893
  default:
1748
1894
  return visit.loadResponse();
1749
1895
  }
1750
1896
  }
1751
- visitRequestFinished(visit) {
1897
+ visitRequestFinished(_visit) {
1752
1898
  this.progressBar.setValue(1);
1753
1899
  this.hideVisitProgressBar();
1754
1900
  }
1755
- visitCompleted(visit) {
1756
- }
1757
- pageInvalidated() {
1758
- this.reload();
1901
+ visitCompleted(_visit) { }
1902
+ pageInvalidated(reason) {
1903
+ this.reload(reason);
1759
1904
  }
1760
- visitFailed(visit) {
1761
- }
1762
- visitRendered(visit) {
1763
- }
1764
- formSubmissionStarted(formSubmission) {
1905
+ visitFailed(_visit) { }
1906
+ visitRendered(_visit) { }
1907
+ formSubmissionStarted(_formSubmission) {
1765
1908
  this.progressBar.setValue(0);
1766
1909
  this.showFormProgressBarAfterDelay();
1767
1910
  }
1768
- formSubmissionFinished(formSubmission) {
1911
+ formSubmissionFinished(_formSubmission) {
1769
1912
  this.progressBar.setValue(1);
1770
1913
  this.hideFormProgressBar();
1771
1914
  }
@@ -1791,8 +1934,11 @@ Copyright © 2021 Basecamp, LLC
1791
1934
  delete this.formProgressBarTimeout;
1792
1935
  }
1793
1936
  }
1794
- reload() {
1795
- 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();
1796
1942
  }
1797
1943
  get navigator() {
1798
1944
  return this.session.navigator;
@@ -1802,6 +1948,12 @@ Copyright © 2021 Basecamp, LLC
1802
1948
  class CacheObserver {
1803
1949
  constructor() {
1804
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
+ });
1805
1957
  }
1806
1958
  start() {
1807
1959
  if (!this.started) {
@@ -1815,12 +1967,6 @@ Copyright © 2021 Basecamp, LLC
1815
1967
  removeEventListener("turbo:before-cache", this.removeStaleElements, false);
1816
1968
  }
1817
1969
  }
1818
- removeStaleElements() {
1819
- const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
1820
- for (const element of staleElements) {
1821
- element.remove();
1822
- }
1823
- }
1824
1970
  }
1825
1971
 
1826
1972
  class FormSubmitObserver {
@@ -1834,12 +1980,12 @@ Copyright © 2021 Basecamp, LLC
1834
1980
  if (!event.defaultPrevented) {
1835
1981
  const form = event.target instanceof HTMLFormElement ? event.target : undefined;
1836
1982
  const submitter = event.submitter || undefined;
1837
- if (form) {
1838
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
1839
- if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
1840
- event.preventDefault();
1841
- this.delegate.formSubmitted(form, submitter);
1842
- }
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);
1843
1989
  }
1844
1990
  }
1845
1991
  });
@@ -1858,6 +2004,18 @@ Copyright © 2021 Basecamp, LLC
1858
2004
  }
1859
2005
  }
1860
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
+ }
1861
2019
 
1862
2020
  class FrameRedirector {
1863
2021
  constructor(element) {
@@ -1873,7 +2031,7 @@ Copyright © 2021 Basecamp, LLC
1873
2031
  this.linkInterceptor.stop();
1874
2032
  this.formInterceptor.stop();
1875
2033
  }
1876
- shouldInterceptLinkClick(element, url) {
2034
+ shouldInterceptLinkClick(element, _url) {
1877
2035
  return this.shouldRedirect(element);
1878
2036
  }
1879
2037
  linkClickIntercepted(element, url) {
@@ -1888,7 +2046,6 @@ Copyright © 2021 Basecamp, LLC
1888
2046
  formSubmissionIntercepted(element, submitter) {
1889
2047
  const frame = this.findFrameElement(element, submitter);
1890
2048
  if (frame) {
1891
- frame.removeAttribute("reloadable");
1892
2049
  frame.delegate.formSubmissionIntercepted(element, submitter);
1893
2050
  }
1894
2051
  }
@@ -1931,7 +2088,7 @@ Copyright © 2021 Basecamp, LLC
1931
2088
  }
1932
2089
  }
1933
2090
  };
1934
- this.onPageLoad = async (event) => {
2091
+ this.onPageLoad = async (_event) => {
1935
2092
  await nextMicrotask();
1936
2093
  this.pageLoaded = true;
1937
2094
  };
@@ -2004,9 +2161,9 @@ Copyright © 2021 Basecamp, LLC
2004
2161
  if (this.clickEventIsSignificant(event)) {
2005
2162
  const target = (event.composedPath && event.composedPath()[0]) || event.target;
2006
2163
  const link = this.findLinkFromClickTarget(target);
2007
- if (link) {
2164
+ if (link && doesNotTargetIFrame(link)) {
2008
2165
  const location = this.getLocationForLink(link);
2009
- if (this.delegate.willFollowLinkToLocation(link, location)) {
2166
+ if (this.delegate.willFollowLinkToLocation(link, location, event)) {
2010
2167
  event.preventDefault();
2011
2168
  this.delegate.followedLinkToLocation(link, location);
2012
2169
  }
@@ -2028,13 +2185,13 @@ Copyright © 2021 Basecamp, LLC
2028
2185
  }
2029
2186
  }
2030
2187
  clickEventIsSignificant(event) {
2031
- return !((event.target && event.target.isContentEditable)
2032
- || event.defaultPrevented
2033
- || event.which > 1
2034
- || event.altKey
2035
- || event.ctrlKey
2036
- || event.metaKey
2037
- || 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);
2038
2195
  }
2039
2196
  findLinkFromClickTarget(target) {
2040
2197
  if (target instanceof Element) {
@@ -2045,6 +2202,13 @@ Copyright © 2021 Basecamp, LLC
2045
2202
  return expandURL(link.getAttribute("href") || "");
2046
2203
  }
2047
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
+ }
2048
2212
 
2049
2213
  function isAction(action) {
2050
2214
  return action == "advance" || action == "replace" || action == "restore";
@@ -2065,6 +2229,7 @@ Copyright © 2021 Basecamp, LLC
2065
2229
  }
2066
2230
  }
2067
2231
  startVisit(locatable, restorationIdentifier, options = {}) {
2232
+ this.lastVisit = this.currentVisit;
2068
2233
  this.stop();
2069
2234
  this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
2070
2235
  this.currentVisit.start();
@@ -2094,7 +2259,7 @@ Copyright © 2021 Basecamp, LLC
2094
2259
  return this.delegate.history;
2095
2260
  }
2096
2261
  formSubmissionStarted(formSubmission) {
2097
- if (typeof this.adapter.formSubmissionStarted === 'function') {
2262
+ if (typeof this.adapter.formSubmissionStarted === "function") {
2098
2263
  this.adapter.formSubmissionStarted(formSubmission);
2099
2264
  }
2100
2265
  }
@@ -2107,7 +2272,10 @@ Copyright © 2021 Basecamp, LLC
2107
2272
  }
2108
2273
  const { statusCode, redirected } = fetchResponse;
2109
2274
  const action = this.getActionForFormSubmission(formSubmission);
2110
- const visitOptions = { action, response: { statusCode, responseHTML, redirected } };
2275
+ const visitOptions = {
2276
+ action,
2277
+ response: { statusCode, responseHTML, redirected },
2278
+ };
2111
2279
  this.proposeVisit(fetchResponse.location, visitOptions);
2112
2280
  }
2113
2281
  }
@@ -2117,10 +2285,10 @@ Copyright © 2021 Basecamp, LLC
2117
2285
  if (responseHTML) {
2118
2286
  const snapshot = PageSnapshot.fromHTMLString(responseHTML);
2119
2287
  if (fetchResponse.serverError) {
2120
- await this.view.renderError(snapshot);
2288
+ await this.view.renderError(snapshot, this.currentVisit);
2121
2289
  }
2122
2290
  else {
2123
- await this.view.renderPage(snapshot);
2291
+ await this.view.renderPage(snapshot, false, true, this.currentVisit);
2124
2292
  }
2125
2293
  this.view.scrollToTop();
2126
2294
  this.view.clearSnapshotCache();
@@ -2130,7 +2298,7 @@ Copyright © 2021 Basecamp, LLC
2130
2298
  console.error(error);
2131
2299
  }
2132
2300
  formSubmissionFinished(formSubmission) {
2133
- if (typeof this.adapter.formSubmissionFinished === 'function') {
2301
+ if (typeof this.adapter.formSubmissionFinished === "function") {
2134
2302
  this.adapter.formSubmissionFinished(formSubmission);
2135
2303
  }
2136
2304
  }
@@ -2141,12 +2309,14 @@ Copyright © 2021 Basecamp, LLC
2141
2309
  this.delegate.visitCompleted(visit);
2142
2310
  }
2143
2311
  locationWithActionIsSamePage(location, action) {
2312
+ var _a;
2144
2313
  const anchor = getAnchor(location);
2145
- const currentAnchor = getAnchor(this.view.lastRenderedLocation);
2146
- const isRestorationToTop = action === 'restore' && typeof anchor === 'undefined';
2147
- return action !== "replace" &&
2148
- getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
2149
- (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)));
2150
2320
  }
2151
2321
  visitScrolledToSamePageLocation(oldURL, newURL) {
2152
2322
  this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
@@ -2252,7 +2422,7 @@ Copyright © 2021 Basecamp, LLC
2252
2422
 
2253
2423
  class StreamObserver {
2254
2424
  constructor(delegate) {
2255
- this.sources = new Set;
2425
+ this.sources = new Set();
2256
2426
  this.started = false;
2257
2427
  this.inspectFetchResponse = ((event) => {
2258
2428
  const response = fetchResponseFromEvent(event);
@@ -2319,14 +2489,18 @@ Copyright © 2021 Basecamp, LLC
2319
2489
  }
2320
2490
 
2321
2491
  class ErrorRenderer extends Renderer {
2492
+ static renderElement(currentElement, newElement) {
2493
+ const { documentElement, body } = document;
2494
+ documentElement.replaceChild(newElement, body);
2495
+ }
2322
2496
  async render() {
2323
2497
  this.replaceHeadAndBody();
2324
2498
  this.activateScriptElements();
2325
2499
  }
2326
2500
  replaceHeadAndBody() {
2327
- const { documentElement, head, body } = document;
2501
+ const { documentElement, head } = document;
2328
2502
  documentElement.replaceChild(this.newHead, head);
2329
- documentElement.replaceChild(this.newElement, body);
2503
+ this.renderElement(this.currentElement, this.newElement);
2330
2504
  }
2331
2505
  activateScriptElements() {
2332
2506
  for (const replaceableElement of this.scriptElements) {
@@ -2346,9 +2520,29 @@ Copyright © 2021 Basecamp, LLC
2346
2520
  }
2347
2521
 
2348
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
+ }
2349
2531
  get shouldRender() {
2350
2532
  return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
2351
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
+ }
2352
2546
  prepareToRender() {
2353
2547
  this.mergeHead();
2354
2548
  }
@@ -2418,12 +2612,7 @@ Copyright © 2021 Basecamp, LLC
2418
2612
  }
2419
2613
  }
2420
2614
  assignNewBody() {
2421
- if (document.body && this.newElement instanceof HTMLBodyElement) {
2422
- document.body.replaceWith(this.newElement);
2423
- }
2424
- else {
2425
- document.documentElement.appendChild(this.newElement);
2426
- }
2615
+ this.renderElement(this.currentElement, this.newElement);
2427
2616
  }
2428
2617
  get newHeadStylesheetElements() {
2429
2618
  return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
@@ -2492,13 +2681,21 @@ Copyright © 2021 Basecamp, LLC
2492
2681
  super(...arguments);
2493
2682
  this.snapshotCache = new SnapshotCache(10);
2494
2683
  this.lastRenderedLocation = new URL(location.href);
2684
+ this.forceReloaded = false;
2495
2685
  }
2496
- renderPage(snapshot, isPreview = false, willRender = true) {
2497
- 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
+ }
2498
2694
  return this.render(renderer);
2499
2695
  }
2500
- renderError(snapshot) {
2501
- 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);
2502
2699
  return this.render(renderer);
2503
2700
  }
2504
2701
  clearSnapshotCache() {
@@ -2525,10 +2722,50 @@ Copyright © 2021 Basecamp, LLC
2525
2722
  }
2526
2723
  }
2527
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
+
2528
2764
  class Session {
2529
2765
  constructor() {
2530
2766
  this.navigator = new Navigator(this);
2531
2767
  this.history = new History(this);
2768
+ this.preloader = new Preloader(this);
2532
2769
  this.view = new PageView(this, document.documentElement);
2533
2770
  this.adapter = new BrowserAdapter(this);
2534
2771
  this.pageObserver = new PageObserver(this);
@@ -2537,22 +2774,26 @@ Copyright © 2021 Basecamp, LLC
2537
2774
  this.formSubmitObserver = new FormSubmitObserver(this);
2538
2775
  this.scrollObserver = new ScrollObserver(this);
2539
2776
  this.streamObserver = new StreamObserver(this);
2777
+ this.formLinkInterceptor = new FormLinkInterceptor(this, document.documentElement);
2540
2778
  this.frameRedirector = new FrameRedirector(document.documentElement);
2541
2779
  this.drive = true;
2542
2780
  this.enabled = true;
2543
2781
  this.progressBarDelay = 500;
2544
2782
  this.started = false;
2783
+ this.formMode = "on";
2545
2784
  }
2546
2785
  start() {
2547
2786
  if (!this.started) {
2548
2787
  this.pageObserver.start();
2549
2788
  this.cacheObserver.start();
2789
+ this.formLinkInterceptor.start();
2550
2790
  this.linkClickObserver.start();
2551
2791
  this.formSubmitObserver.start();
2552
2792
  this.scrollObserver.start();
2553
2793
  this.streamObserver.start();
2554
2794
  this.frameRedirector.start();
2555
2795
  this.history.start();
2796
+ this.preloader.start();
2556
2797
  this.started = true;
2557
2798
  this.enabled = true;
2558
2799
  }
@@ -2564,6 +2805,7 @@ Copyright © 2021 Basecamp, LLC
2564
2805
  if (this.started) {
2565
2806
  this.pageObserver.stop();
2566
2807
  this.cacheObserver.stop();
2808
+ this.formLinkInterceptor.stop();
2567
2809
  this.linkClickObserver.stop();
2568
2810
  this.formSubmitObserver.stop();
2569
2811
  this.scrollObserver.stop();
@@ -2594,6 +2836,9 @@ Copyright © 2021 Basecamp, LLC
2594
2836
  setProgressBarDelay(delay) {
2595
2837
  this.progressBarDelay = delay;
2596
2838
  }
2839
+ setFormMode(mode) {
2840
+ this.formMode = mode;
2841
+ }
2597
2842
  get location() {
2598
2843
  return this.history.location;
2599
2844
  }
@@ -2602,48 +2847,32 @@ Copyright © 2021 Basecamp, LLC
2602
2847
  }
2603
2848
  historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
2604
2849
  if (this.enabled) {
2605
- this.navigator.startVisit(location, restorationIdentifier, { action: "restore", historyChanged: true });
2850
+ this.navigator.startVisit(location, restorationIdentifier, {
2851
+ action: "restore",
2852
+ historyChanged: true,
2853
+ });
2606
2854
  }
2607
2855
  else {
2608
- this.adapter.pageInvalidated();
2856
+ this.adapter.pageInvalidated({
2857
+ reason: "turbo_disabled",
2858
+ });
2609
2859
  }
2610
2860
  }
2611
2861
  scrollPositionChanged(position) {
2612
2862
  this.history.updateRestorationData({ scrollPosition: position });
2613
2863
  }
2614
- willFollowLinkToLocation(link, location) {
2615
- return this.elementDriveEnabled(link)
2616
- && locationIsVisitable(location, this.snapshot.rootLocation)
2617
- && 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));
2618
2872
  }
2619
2873
  followedLinkToLocation(link, location) {
2620
2874
  const action = this.getActionForLink(link);
2621
- this.convertLinkWithMethodClickToFormSubmission(link) || this.visit(location.href, { action });
2622
- }
2623
- convertLinkWithMethodClickToFormSubmission(link) {
2624
- const linkMethod = link.getAttribute("data-turbo-method");
2625
- if (linkMethod) {
2626
- const form = document.createElement("form");
2627
- form.method = linkMethod;
2628
- form.action = link.getAttribute("href") || "undefined";
2629
- form.hidden = true;
2630
- if (link.hasAttribute("data-turbo-confirm")) {
2631
- form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
2632
- }
2633
- const frame = this.getTargetFrameForLink(link);
2634
- if (frame) {
2635
- form.setAttribute("data-turbo-frame", frame);
2636
- form.addEventListener("turbo:submit-start", () => form.remove());
2637
- }
2638
- else {
2639
- form.addEventListener("submit", () => form.remove());
2640
- }
2641
- document.body.appendChild(form);
2642
- return dispatch("submit", { cancelable: true, target: form });
2643
- }
2644
- else {
2645
- return false;
2646
- }
2875
+ this.visit(location.href, { action });
2647
2876
  }
2648
2877
  allowsVisitingLocationWithAction(location, action) {
2649
2878
  return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
@@ -2669,9 +2898,9 @@ Copyright © 2021 Basecamp, LLC
2669
2898
  }
2670
2899
  willSubmitForm(form, submitter) {
2671
2900
  const action = getAction(form, submitter);
2672
- return this.elementDriveEnabled(form)
2673
- && (!submitter || this.elementDriveEnabled(submitter))
2674
- && locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
2901
+ return (this.elementDriveEnabled(form) &&
2902
+ (!submitter || this.formElementDriveEnabled(submitter)) &&
2903
+ locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
2675
2904
  }
2676
2905
  formSubmitted(form, submitter) {
2677
2906
  this.navigator.submitForm(form, submitter);
@@ -2695,16 +2924,23 @@ Copyright © 2021 Basecamp, LLC
2695
2924
  this.notifyApplicationBeforeCachingSnapshot();
2696
2925
  }
2697
2926
  }
2698
- allowsImmediateRender({ element }, resume) {
2699
- const event = this.notifyApplicationBeforeRender(element, resume);
2700
- 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;
2701
2934
  }
2702
- viewRenderedSnapshot(snapshot, isPreview) {
2935
+ viewRenderedSnapshot(_snapshot, _isPreview) {
2703
2936
  this.view.lastRenderedLocation = this.history.location;
2704
2937
  this.notifyApplicationAfterRender();
2705
2938
  }
2706
- viewInvalidated() {
2707
- this.adapter.pageInvalidated();
2939
+ preloadOnLoadLinksForView(element) {
2940
+ this.preloader.preloadOnLoadLinksForView(element);
2941
+ }
2942
+ viewInvalidated(reason) {
2943
+ this.adapter.pageInvalidated(reason);
2708
2944
  }
2709
2945
  frameLoaded(frame) {
2710
2946
  this.notifyApplicationAfterFrameLoad(frame);
@@ -2712,19 +2948,26 @@ Copyright © 2021 Basecamp, LLC
2712
2948
  frameRendered(fetchResponse, frame) {
2713
2949
  this.notifyApplicationAfterFrameRender(fetchResponse, frame);
2714
2950
  }
2715
- applicationAllowsFollowingLinkToLocation(link, location) {
2716
- const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
2951
+ applicationAllowsFollowingLinkToLocation(link, location, ev) {
2952
+ const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
2717
2953
  return !event.defaultPrevented;
2718
2954
  }
2719
2955
  applicationAllowsVisitingLocation(location) {
2720
2956
  const event = this.notifyApplicationBeforeVisitingLocation(location);
2721
2957
  return !event.defaultPrevented;
2722
2958
  }
2723
- notifyApplicationAfterClickingLinkToLocation(link, location) {
2724
- 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
+ });
2725
2965
  }
2726
2966
  notifyApplicationBeforeVisitingLocation(location) {
2727
- 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
+ });
2728
2971
  }
2729
2972
  notifyApplicationAfterVisitingLocation(location, action) {
2730
2973
  markAsBusy(document.documentElement);
@@ -2733,24 +2976,46 @@ Copyright © 2021 Basecamp, LLC
2733
2976
  notifyApplicationBeforeCachingSnapshot() {
2734
2977
  return dispatch("turbo:before-cache");
2735
2978
  }
2736
- notifyApplicationBeforeRender(newBody, resume) {
2737
- 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
+ });
2738
2984
  }
2739
2985
  notifyApplicationAfterRender() {
2740
2986
  return dispatch("turbo:render");
2741
2987
  }
2742
2988
  notifyApplicationAfterPageLoad(timing = {}) {
2743
2989
  clearBusyState(document.documentElement);
2744
- return dispatch("turbo:load", { detail: { url: this.location.href, timing } });
2990
+ return dispatch("turbo:load", {
2991
+ detail: { url: this.location.href, timing },
2992
+ });
2745
2993
  }
2746
2994
  notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
2747
- dispatchEvent(new HashChangeEvent("hashchange", { oldURL: oldURL.toString(), newURL: newURL.toString() }));
2995
+ dispatchEvent(new HashChangeEvent("hashchange", {
2996
+ oldURL: oldURL.toString(),
2997
+ newURL: newURL.toString(),
2998
+ }));
2748
2999
  }
2749
3000
  notifyApplicationAfterFrameLoad(frame) {
2750
3001
  return dispatch("turbo:frame-load", { target: frame });
2751
3002
  }
2752
3003
  notifyApplicationAfterFrameRender(fetchResponse, frame) {
2753
- 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);
2754
3019
  }
2755
3020
  elementDriveEnabled(element) {
2756
3021
  const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
@@ -2775,18 +3040,6 @@ Copyright © 2021 Basecamp, LLC
2775
3040
  const action = link.getAttribute("data-turbo-action");
2776
3041
  return isAction(action) ? action : "advance";
2777
3042
  }
2778
- getTargetFrameForLink(link) {
2779
- const frame = link.getAttribute("data-turbo-frame");
2780
- if (frame) {
2781
- return frame;
2782
- }
2783
- else {
2784
- const container = link.closest("turbo-frame");
2785
- if (container) {
2786
- return container.id;
2787
- }
2788
- }
2789
- }
2790
3043
  get snapshot() {
2791
3044
  return this.view.snapshot;
2792
3045
  }
@@ -2798,11 +3051,59 @@ Copyright © 2021 Basecamp, LLC
2798
3051
  absoluteURL: {
2799
3052
  get() {
2800
3053
  return this.toString();
2801
- }
3054
+ },
3055
+ },
3056
+ };
3057
+
3058
+ class Cache {
3059
+ constructor(session) {
3060
+ this.session = session;
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);
2802
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
+ },
2803
3103
  };
2804
3104
 
2805
- const session = new Session;
3105
+ const session = new Session();
3106
+ const cache = new Cache(session);
2806
3107
  const { navigator: navigator$1 } = session;
2807
3108
  function start() {
2808
3109
  session.start();
@@ -2823,6 +3124,7 @@ Copyright © 2021 Basecamp, LLC
2823
3124
  session.renderStreamMessage(message);
2824
3125
  }
2825
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.`");
2826
3128
  session.clearCache();
2827
3129
  }
2828
3130
  function setProgressBarDelay(delay) {
@@ -2831,13 +3133,18 @@ Copyright © 2021 Basecamp, LLC
2831
3133
  function setConfirmMethod(confirmMethod) {
2832
3134
  FormSubmission.confirmMethod = confirmMethod;
2833
3135
  }
3136
+ function setFormMode(mode) {
3137
+ session.setFormMode(mode);
3138
+ }
2834
3139
 
2835
3140
  var Turbo = /*#__PURE__*/Object.freeze({
2836
3141
  __proto__: null,
2837
3142
  navigator: navigator$1,
2838
3143
  session: session,
3144
+ cache: cache,
2839
3145
  PageRenderer: PageRenderer,
2840
3146
  PageSnapshot: PageSnapshot,
3147
+ FrameRenderer: FrameRenderer,
2841
3148
  start: start,
2842
3149
  registerAdapter: registerAdapter,
2843
3150
  visit: visit,
@@ -2846,39 +3153,52 @@ Copyright © 2021 Basecamp, LLC
2846
3153
  renderStreamMessage: renderStreamMessage,
2847
3154
  clearCache: clearCache,
2848
3155
  setProgressBarDelay: setProgressBarDelay,
2849
- setConfirmMethod: setConfirmMethod
3156
+ setConfirmMethod: setConfirmMethod,
3157
+ setFormMode: setFormMode,
3158
+ StreamActions: StreamActions
2850
3159
  });
2851
3160
 
2852
3161
  class FrameController {
2853
3162
  constructor(element) {
2854
- this.fetchResponseLoaded = (fetchResponse) => { };
3163
+ this.fetchResponseLoaded = (_fetchResponse) => { };
2855
3164
  this.currentFetchRequest = null;
2856
3165
  this.resolveVisitPromise = () => { };
2857
3166
  this.connected = false;
2858
3167
  this.hasBeenLoaded = false;
2859
- 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
+ };
2860
3176
  this.element = element;
2861
3177
  this.view = new FrameView(this, this.element);
2862
3178
  this.appearanceObserver = new AppearanceObserver(this, this.element);
3179
+ this.formLinkInterceptor = new FormLinkInterceptor(this, this.element);
2863
3180
  this.linkInterceptor = new LinkInterceptor(this, this.element);
2864
3181
  this.formInterceptor = new FormInterceptor(this, this.element);
2865
3182
  }
2866
3183
  connect() {
2867
3184
  if (!this.connected) {
2868
3185
  this.connected = true;
2869
- this.reloadable = false;
2870
3186
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
2871
3187
  this.appearanceObserver.start();
2872
3188
  }
3189
+ else {
3190
+ this.loadSourceURL();
3191
+ }
3192
+ this.formLinkInterceptor.start();
2873
3193
  this.linkInterceptor.start();
2874
3194
  this.formInterceptor.start();
2875
- this.sourceURLChanged();
2876
3195
  }
2877
3196
  }
2878
3197
  disconnect() {
2879
3198
  if (this.connected) {
2880
3199
  this.connected = false;
2881
3200
  this.appearanceObserver.stop();
3201
+ this.formLinkInterceptor.stop();
2882
3202
  this.linkInterceptor.stop();
2883
3203
  this.formInterceptor.stop();
2884
3204
  }
@@ -2889,10 +3209,20 @@ Copyright © 2021 Basecamp, LLC
2889
3209
  }
2890
3210
  }
2891
3211
  sourceURLChanged() {
3212
+ if (this.isIgnoringChangesTo("src"))
3213
+ return;
3214
+ if (this.element.isConnected) {
3215
+ this.complete = false;
3216
+ }
2892
3217
  if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
2893
3218
  this.loadSourceURL();
2894
3219
  }
2895
3220
  }
3221
+ completeChanged() {
3222
+ if (this.isIgnoringChangesTo("complete"))
3223
+ return;
3224
+ this.loadSourceURL();
3225
+ }
2896
3226
  loadingStyleChanged() {
2897
3227
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
2898
3228
  this.appearanceObserver.start();
@@ -2903,21 +3233,11 @@ Copyright © 2021 Basecamp, LLC
2903
3233
  }
2904
3234
  }
2905
3235
  async loadSourceURL() {
2906
- if (!this.settingSourceURL && this.enabled && this.isActive && (this.reloadable || this.sourceURL != this.currentURL)) {
2907
- const previousURL = this.currentURL;
2908
- this.currentURL = this.sourceURL;
2909
- if (this.sourceURL) {
2910
- try {
2911
- this.element.loaded = this.visit(this.sourceURL);
2912
- this.appearanceObserver.stop();
2913
- await this.element.loaded;
2914
- this.hasBeenLoaded = true;
2915
- }
2916
- catch (error) {
2917
- this.currentURL = previousURL;
2918
- throw error;
2919
- }
2920
- }
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;
2921
3241
  }
2922
3242
  }
2923
3243
  async loadResponse(fetchResponse) {
@@ -2929,10 +3249,11 @@ Copyright © 2021 Basecamp, LLC
2929
3249
  if (html) {
2930
3250
  const { body } = parseHTMLDocument(html);
2931
3251
  const snapshot = new Snapshot(await this.extractForeignFrameElement(body));
2932
- const renderer = new FrameRenderer(this.view.snapshot, snapshot, false, false);
3252
+ const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
2933
3253
  if (this.view.renderPromise)
2934
3254
  await this.view.renderPromise;
2935
3255
  await this.view.render(renderer);
3256
+ this.complete = true;
2936
3257
  session.frameRendered(fetchResponse, this.element);
2937
3258
  session.frameLoaded(this.element);
2938
3259
  this.fetchResponseLoaded(fetchResponse);
@@ -2946,19 +3267,21 @@ Copyright © 2021 Basecamp, LLC
2946
3267
  this.fetchResponseLoaded = () => { };
2947
3268
  }
2948
3269
  }
2949
- elementAppearedInViewport(element) {
3270
+ elementAppearedInViewport(_element) {
2950
3271
  this.loadSourceURL();
2951
3272
  }
2952
- shouldInterceptLinkClick(element, url) {
2953
- if (element.hasAttribute("data-turbo-method")) {
2954
- return false;
2955
- }
2956
- else {
2957
- return this.shouldInterceptNavigation(element);
2958
- }
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);
2959
3283
  }
2960
3284
  linkClickIntercepted(element, url) {
2961
- this.reloadable = true;
2962
3285
  this.navigateFrame(element, url);
2963
3286
  }
2964
3287
  shouldInterceptFormSubmission(element, submitter) {
@@ -2968,19 +3291,18 @@ Copyright © 2021 Basecamp, LLC
2968
3291
  if (this.formSubmission) {
2969
3292
  this.formSubmission.stop();
2970
3293
  }
2971
- this.reloadable = false;
2972
3294
  this.formSubmission = new FormSubmission(this, element, submitter);
2973
3295
  const { fetchRequest } = this.formSubmission;
2974
3296
  this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
2975
3297
  this.formSubmission.start();
2976
3298
  }
2977
- prepareHeadersForRequest(headers, request) {
3299
+ prepareHeadersForRequest(headers, _request) {
2978
3300
  headers["Turbo-Frame"] = this.id;
2979
3301
  }
2980
- requestStarted(request) {
3302
+ requestStarted(_request) {
2981
3303
  markAsBusy(this.element);
2982
3304
  }
2983
- requestPreventedHandlingResponse(request, response) {
3305
+ requestPreventedHandlingResponse(_request, _response) {
2984
3306
  this.resolveVisitPromise();
2985
3307
  }
2986
3308
  async requestSucceededWithResponse(request, response) {
@@ -2995,7 +3317,7 @@ Copyright © 2021 Basecamp, LLC
2995
3317
  console.error(error);
2996
3318
  this.resolveVisitPromise();
2997
3319
  }
2998
- requestFinished(request) {
3320
+ requestFinished(_request) {
2999
3321
  clearBusyState(this.element);
3000
3322
  }
3001
3323
  formSubmissionStarted({ formElement }) {
@@ -3015,19 +3337,32 @@ Copyright © 2021 Basecamp, LLC
3015
3337
  formSubmissionFinished({ formElement }) {
3016
3338
  clearBusyState(formElement, this.findFrameElement(formElement));
3017
3339
  }
3018
- allowsImmediateRender(snapshot, resume) {
3019
- 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;
3020
3351
  }
3021
- viewRenderedSnapshot(snapshot, isPreview) {
3352
+ viewRenderedSnapshot(_snapshot, _isPreview) { }
3353
+ preloadOnLoadLinksForView(element) {
3354
+ session.preloadOnLoadLinksForView(element);
3022
3355
  }
3023
- viewInvalidated() {
3356
+ viewInvalidated() { }
3357
+ frameExtracted(element) {
3358
+ this.previousFrameElement = element;
3024
3359
  }
3025
3360
  async visit(url) {
3026
3361
  var _a;
3027
- const request = new FetchRequest(this, FetchMethod.get, expandURL(url), new URLSearchParams, this.element);
3362
+ const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
3028
3363
  (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
3029
3364
  this.currentFetchRequest = request;
3030
- return new Promise(resolve => {
3365
+ return new Promise((resolve) => {
3031
3366
  this.resolveVisitPromise = () => {
3032
3367
  this.resolveVisitPromise = () => { };
3033
3368
  this.currentFetchRequest = null;
@@ -3039,19 +3374,23 @@ Copyright © 2021 Basecamp, LLC
3039
3374
  navigateFrame(element, url, submitter) {
3040
3375
  const frame = this.findFrameElement(element, submitter);
3041
3376
  this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
3042
- frame.setAttribute("reloadable", "");
3043
3377
  frame.src = url;
3044
3378
  }
3045
3379
  proposeVisitIfNavigatedWithAction(frame, element, submitter) {
3046
3380
  const action = getAttribute("data-turbo-action", submitter, element, frame);
3047
3381
  if (isAction(action)) {
3048
- const { visitCachedSnapshot } = new SnapshotSubstitution(frame);
3382
+ const { visitCachedSnapshot } = frame.delegate;
3049
3383
  frame.delegate.fetchResponseLoaded = (fetchResponse) => {
3050
3384
  if (frame.src) {
3051
3385
  const { statusCode, redirected } = fetchResponse;
3052
3386
  const responseHTML = frame.ownerDocument.documentElement.outerHTML;
3053
3387
  const response = { statusCode, redirected, responseHTML };
3054
- session.visit(frame.src, { action, response, visitCachedSnapshot, willRender: false });
3388
+ session.visit(frame.src, {
3389
+ action,
3390
+ response,
3391
+ visitCachedSnapshot,
3392
+ willRender: false,
3393
+ });
3055
3394
  }
3056
3395
  };
3057
3396
  }
@@ -3065,10 +3404,12 @@ Copyright © 2021 Basecamp, LLC
3065
3404
  let element;
3066
3405
  const id = CSS.escape(this.id);
3067
3406
  try {
3068
- if (element = activateElement(container.querySelector(`turbo-frame#${id}`), this.currentURL)) {
3407
+ element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
3408
+ if (element) {
3069
3409
  return element;
3070
3410
  }
3071
- 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) {
3072
3413
  await element.loaded;
3073
3414
  return await this.extractForeignFrameElement(element);
3074
3415
  }
@@ -3116,24 +3457,10 @@ Copyright © 2021 Basecamp, LLC
3116
3457
  return this.element.src;
3117
3458
  }
3118
3459
  }
3119
- get reloadable() {
3120
- const frame = this.findFrameElement(this.element);
3121
- return frame.hasAttribute("reloadable");
3122
- }
3123
- set reloadable(value) {
3124
- const frame = this.findFrameElement(this.element);
3125
- if (value) {
3126
- frame.setAttribute("reloadable", "");
3127
- }
3128
- else {
3129
- frame.removeAttribute("reloadable");
3130
- }
3131
- }
3132
3460
  set sourceURL(sourceURL) {
3133
- this.settingSourceURL = true;
3134
- this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3135
- this.currentURL = this.element.src;
3136
- this.settingSourceURL = false;
3461
+ this.ignoringChangesToAttribute("src", () => {
3462
+ this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3463
+ });
3137
3464
  }
3138
3465
  get loadingStyle() {
3139
3466
  return this.element.loading;
@@ -3141,6 +3468,19 @@ Copyright © 2021 Basecamp, LLC
3141
3468
  get isLoading() {
3142
3469
  return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
3143
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
+ }
3144
3484
  get isActive() {
3145
3485
  return this.element.isActive && this.connected;
3146
3486
  }
@@ -3150,16 +3490,13 @@ Copyright © 2021 Basecamp, LLC
3150
3490
  const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
3151
3491
  return expandURL(root);
3152
3492
  }
3153
- }
3154
- class SnapshotSubstitution {
3155
- constructor(element) {
3156
- this.visitCachedSnapshot = ({ element }) => {
3157
- var _a;
3158
- const { id, clone } = this;
3159
- (_a = element.querySelector("#" + id)) === null || _a === void 0 ? void 0 : _a.replaceWith(clone);
3160
- };
3161
- this.clone = element.cloneNode(true);
3162
- 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);
3163
3500
  }
3164
3501
  }
3165
3502
  function getFrameElementById(id) {
@@ -3187,35 +3524,6 @@ Copyright © 2021 Basecamp, LLC
3187
3524
  }
3188
3525
  }
3189
3526
 
3190
- const StreamActions = {
3191
- after() {
3192
- this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
3193
- },
3194
- append() {
3195
- this.removeDuplicateTargetChildren();
3196
- this.targetElements.forEach(e => e.append(this.templateContent));
3197
- },
3198
- before() {
3199
- this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
3200
- },
3201
- prepend() {
3202
- this.removeDuplicateTargetChildren();
3203
- this.targetElements.forEach(e => e.prepend(this.templateContent));
3204
- },
3205
- remove() {
3206
- this.targetElements.forEach(e => e.remove());
3207
- },
3208
- replace() {
3209
- this.targetElements.forEach(e => e.replaceWith(this.templateContent));
3210
- },
3211
- update() {
3212
- this.targetElements.forEach(e => {
3213
- e.innerHTML = "";
3214
- e.append(this.templateContent);
3215
- });
3216
- }
3217
- };
3218
-
3219
3527
  class StreamElement extends HTMLElement {
3220
3528
  async connectedCallback() {
3221
3529
  try {
@@ -3230,12 +3538,12 @@ Copyright © 2021 Basecamp, LLC
3230
3538
  }
3231
3539
  async render() {
3232
3540
  var _a;
3233
- 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 () => {
3234
3542
  if (this.dispatchEvent(this.beforeRenderEvent)) {
3235
3543
  await nextAnimationFrame();
3236
3544
  this.performAction();
3237
3545
  }
3238
- })());
3546
+ })()));
3239
3547
  }
3240
3548
  disconnect() {
3241
3549
  try {
@@ -3244,13 +3552,13 @@ Copyright © 2021 Basecamp, LLC
3244
3552
  catch (_a) { }
3245
3553
  }
3246
3554
  removeDuplicateTargetChildren() {
3247
- this.duplicateChildren.forEach(c => c.remove());
3555
+ this.duplicateChildren.forEach((c) => c.remove());
3248
3556
  }
3249
3557
  get duplicateChildren() {
3250
3558
  var _a;
3251
- const existingChildren = this.targetElements.flatMap(e => [...e.children]).filter(c => !!c.id);
3252
- const newChildrenIds = [...(_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children].filter(c => !!c.id).map(c => c.id);
3253
- 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));
3254
3562
  }
3255
3563
  get performAction() {
3256
3564
  if (this.action) {
@@ -3299,7 +3607,10 @@ Copyright © 2021 Basecamp, LLC
3299
3607
  return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
3300
3608
  }
3301
3609
  get beforeRenderEvent() {
3302
- 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
+ });
3303
3614
  }
3304
3615
  get targetElementsById() {
3305
3616
  var _a;
@@ -3323,9 +3634,35 @@ Copyright © 2021 Basecamp, LLC
3323
3634
  }
3324
3635
  }
3325
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
+
3326
3656
  FrameElement.delegateConstructor = FrameController;
3327
- customElements.define("turbo-frame", FrameElement);
3328
- 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
+ }
3329
3666
 
3330
3667
  (() => {
3331
3668
  let element = document.currentScript;
@@ -3333,7 +3670,8 @@ Copyright © 2021 Basecamp, LLC
3333
3670
  return;
3334
3671
  if (element.hasAttribute("data-turbo-suppress-warning"))
3335
3672
  return;
3336
- while (element = element.parentElement) {
3673
+ element = element.parentElement;
3674
+ while (element) {
3337
3675
  if (element == document.body) {
3338
3676
  return console.warn(unindent `
3339
3677
  You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
@@ -3346,14 +3684,18 @@ Copyright © 2021 Basecamp, LLC
3346
3684
  Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
3347
3685
  `, element.outerHTML);
3348
3686
  }
3687
+ element = element.parentElement;
3349
3688
  }
3350
3689
  })();
3351
3690
 
3352
3691
  window.Turbo = Turbo;
3353
3692
  start();
3354
3693
 
3694
+ exports.FrameRenderer = FrameRenderer;
3355
3695
  exports.PageRenderer = PageRenderer;
3356
3696
  exports.PageSnapshot = PageSnapshot;
3697
+ exports.StreamActions = StreamActions;
3698
+ exports.cache = cache;
3357
3699
  exports.clearCache = clearCache;
3358
3700
  exports.connectStreamSource = connectStreamSource;
3359
3701
  exports.disconnectStreamSource = disconnectStreamSource;
@@ -3362,10 +3704,11 @@ Copyright © 2021 Basecamp, LLC
3362
3704
  exports.renderStreamMessage = renderStreamMessage;
3363
3705
  exports.session = session;
3364
3706
  exports.setConfirmMethod = setConfirmMethod;
3707
+ exports.setFormMode = setFormMode;
3365
3708
  exports.setProgressBarDelay = setProgressBarDelay;
3366
3709
  exports.start = start;
3367
3710
  exports.visit = visit;
3368
3711
 
3369
3712
  Object.defineProperty(exports, '__esModule', { value: true });
3370
3713
 
3371
- })));
3714
+ }));