@hotwired/turbo 7.1.0 → 7.2.0-rc.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 (73) hide show
  1. package/README.md +4 -0
  2. package/dist/turbo.es2017-esm.js +1139 -630
  3. package/dist/turbo.es2017-umd.js +1153 -637
  4. package/dist/types/core/bardo.d.ts +7 -2
  5. package/dist/types/core/cache.d.ts +10 -0
  6. package/dist/types/core/drive/error_renderer.d.ts +2 -1
  7. package/dist/types/core/drive/form_submission.d.ts +12 -5
  8. package/dist/types/core/drive/head_snapshot.d.ts +3 -3
  9. package/dist/types/core/drive/history.d.ts +1 -1
  10. package/dist/types/core/drive/navigator.d.ts +5 -4
  11. package/dist/types/core/drive/page_renderer.d.ts +11 -5
  12. package/dist/types/core/drive/page_view.d.ts +8 -5
  13. package/dist/types/core/drive/preloader.d.ts +14 -0
  14. package/dist/types/core/drive/progress_bar.d.ts +1 -0
  15. package/dist/types/core/drive/visit.d.ts +17 -5
  16. package/dist/types/core/frames/frame_controller.d.ts +50 -24
  17. package/dist/types/core/frames/frame_redirector.d.ts +12 -10
  18. package/dist/types/core/frames/frame_renderer.d.ts +8 -1
  19. package/dist/types/core/frames/frame_view.d.ts +2 -1
  20. package/dist/types/core/index.d.ts +12 -4
  21. package/dist/types/core/native/adapter.d.ts +3 -2
  22. package/dist/types/core/native/browser_adapter.d.ts +18 -9
  23. package/dist/types/core/renderer.d.ts +11 -5
  24. package/dist/types/core/session.d.ts +81 -21
  25. package/dist/types/core/snapshot.d.ts +5 -2
  26. package/dist/types/core/streams/stream_actions.d.ts +4 -2
  27. package/dist/types/core/streams/stream_message.d.ts +2 -6
  28. package/dist/types/core/streams/stream_message_renderer.d.ts +7 -0
  29. package/dist/types/core/types.d.ts +4 -0
  30. package/dist/types/core/view.d.ts +13 -7
  31. package/dist/types/elements/frame_element.d.ts +10 -6
  32. package/dist/types/elements/index.d.ts +1 -0
  33. package/dist/types/elements/stream_element.d.ts +8 -1
  34. package/dist/types/elements/stream_source_element.d.ts +7 -0
  35. package/dist/types/http/fetch_request.d.ts +11 -3
  36. package/dist/types/http/index.d.ts +1 -0
  37. package/dist/types/index.d.ts +2 -0
  38. package/dist/types/observers/cache_observer.d.ts +1 -1
  39. package/dist/types/observers/form_link_click_observer.d.ts +14 -0
  40. package/dist/types/observers/form_submit_observer.d.ts +2 -1
  41. package/dist/types/observers/link_click_observer.d.ts +5 -4
  42. package/dist/types/polyfills/submit-event.d.ts +1 -7
  43. package/dist/types/tests/functional/async_script_tests.d.ts +1 -6
  44. package/dist/types/tests/functional/autofocus_tests.d.ts +1 -9
  45. package/dist/types/tests/functional/cache_observer_tests.d.ts +1 -5
  46. package/dist/types/tests/functional/drive_custom_body_tests.d.ts +1 -0
  47. package/dist/types/tests/functional/drive_disabled_tests.d.ts +1 -9
  48. package/dist/types/tests/functional/drive_tests.d.ts +1 -8
  49. package/dist/types/tests/functional/form_mode_tests.d.ts +1 -0
  50. package/dist/types/tests/functional/form_submission_tests.d.ts +1 -84
  51. package/dist/types/tests/functional/frame_navigation_tests.d.ts +1 -7
  52. package/dist/types/tests/functional/frame_tests.d.ts +1 -52
  53. package/dist/types/tests/functional/import_tests.d.ts +1 -4
  54. package/dist/types/tests/functional/loading_tests.d.ts +1 -13
  55. package/dist/types/tests/functional/navigation_tests.d.ts +1 -38
  56. package/dist/types/tests/functional/pausable_rendering_tests.d.ts +1 -6
  57. package/dist/types/tests/functional/pausable_requests_tests.d.ts +1 -6
  58. package/dist/types/tests/functional/preloader_tests.d.ts +1 -0
  59. package/dist/types/tests/functional/rendering_tests.d.ts +1 -35
  60. package/dist/types/tests/functional/scroll_restoration_tests.d.ts +1 -6
  61. package/dist/types/tests/functional/stream_tests.d.ts +1 -6
  62. package/dist/types/tests/functional/visit_tests.d.ts +1 -15
  63. package/dist/types/tests/helpers/page.d.ts +50 -0
  64. package/dist/types/tests/unit/deprecated_adapter_support_test.d.ts +10 -10
  65. package/dist/types/util.d.ts +14 -3
  66. package/package.json +23 -9
  67. package/CHANGELOG.md +0 -3
  68. package/dist/types/core/frames/form_interceptor.d.ts +0 -12
  69. package/dist/types/core/frames/link_interceptor.d.ts +0 -16
  70. package/dist/types/tests/functional/index.d.ts +0 -17
  71. package/dist/types/tests/helpers/functional_test_case.d.ts +0 -44
  72. package/dist/types/tests/helpers/remote_channel.d.ts +0 -10
  73. package/dist/types/tests/helpers/turbo_drive_test_case.d.ts +0 -21
@@ -1,26 +1,26 @@
1
1
  /*
2
- Turbo 7.1.0
3
- Copyright © 2021 Basecamp, LLC
2
+ Turbo 7.2.0-rc.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,15 +109,15 @@ 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
 
116
- var FrameLoadingStyle;
116
+ exports.FrameLoadingStyle = void 0;
117
117
  (function (FrameLoadingStyle) {
118
118
  FrameLoadingStyle["eager"] = "eager";
119
119
  FrameLoadingStyle["lazy"] = "lazy";
120
- })(FrameLoadingStyle || (FrameLoadingStyle = {}));
120
+ })(exports.FrameLoadingStyle || (exports.FrameLoadingStyle = {}));
121
121
  class FrameElement extends HTMLElement {
122
122
  constructor() {
123
123
  super();
@@ -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,13 +135,18 @@ 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;
141
+ return this.loaded;
140
142
  }
141
143
  attributeChangedCallback(name) {
142
144
  if (name == "loading") {
143
145
  this.delegate.loadingStyleChanged();
144
146
  }
147
+ else if (name == "complete") {
148
+ this.delegate.completeChanged();
149
+ }
145
150
  else if (name == "src") {
146
151
  this.delegate.sourceURLChanged();
147
152
  }
@@ -206,8 +211,10 @@ Copyright © 2021 Basecamp, LLC
206
211
  }
207
212
  function frameLoadingStyleFromString(style) {
208
213
  switch (style.toLowerCase()) {
209
- case "lazy": return FrameLoadingStyle.lazy;
210
- default: return FrameLoadingStyle.eager;
214
+ case "lazy":
215
+ return exports.FrameLoadingStyle.lazy;
216
+ default:
217
+ return exports.FrameLoadingStyle.eager;
211
218
  }
212
219
  }
213
220
 
@@ -219,7 +226,7 @@ Copyright © 2021 Basecamp, LLC
219
226
  if (url.hash) {
220
227
  return url.hash.slice(1);
221
228
  }
222
- else if (anchorMatch = url.href.match(/#(.*)$/)) {
229
+ else if ((anchorMatch = url.href.match(/#(.*)$/))) {
223
230
  return anchorMatch[1];
224
231
  }
225
232
  }
@@ -231,7 +238,7 @@ Copyright © 2021 Basecamp, LLC
231
238
  return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
232
239
  }
233
240
  function isHTML(url) {
234
- return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml))$/);
241
+ return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
235
242
  }
236
243
  function isPrefixedBy(baseURL, url) {
237
244
  const prefix = getPrefix(url);
@@ -242,9 +249,7 @@ Copyright © 2021 Basecamp, LLC
242
249
  }
243
250
  function getRequestURL(url) {
244
251
  const anchor = getAnchor(url);
245
- return anchor != null
246
- ? url.href.slice(0, -(anchor.length + 1))
247
- : url.href;
252
+ return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
248
253
  }
249
254
  function toCacheKey(url) {
250
255
  return getRequestURL(url);
@@ -312,8 +317,42 @@ Copyright © 2021 Basecamp, LLC
312
317
  }
313
318
  }
314
319
 
320
+ function isAction(action) {
321
+ return action == "advance" || action == "replace" || action == "restore";
322
+ }
323
+
324
+ function activateScriptElement(element) {
325
+ if (element.getAttribute("data-turbo-eval") == "false") {
326
+ return element;
327
+ }
328
+ else {
329
+ const createdScriptElement = document.createElement("script");
330
+ const cspNonce = getMetaContent("csp-nonce");
331
+ if (cspNonce) {
332
+ createdScriptElement.nonce = cspNonce;
333
+ }
334
+ createdScriptElement.textContent = element.textContent;
335
+ createdScriptElement.async = false;
336
+ copyElementAttributes(createdScriptElement, element);
337
+ return createdScriptElement;
338
+ }
339
+ }
340
+ function copyElementAttributes(destinationElement, sourceElement) {
341
+ for (const { name, value } of sourceElement.attributes) {
342
+ destinationElement.setAttribute(name, value);
343
+ }
344
+ }
345
+ function createDocumentFragment(html) {
346
+ const template = document.createElement("template");
347
+ template.innerHTML = html;
348
+ return template.content;
349
+ }
315
350
  function dispatch(eventName, { target, cancelable, detail } = {}) {
316
- const event = new CustomEvent(eventName, { cancelable, bubbles: true, detail });
351
+ const event = new CustomEvent(eventName, {
352
+ cancelable,
353
+ bubbles: true,
354
+ detail,
355
+ });
317
356
  if (target && target.isConnected) {
318
357
  target.dispatchEvent(event);
319
358
  }
@@ -323,10 +362,10 @@ Copyright © 2021 Basecamp, LLC
323
362
  return event;
324
363
  }
325
364
  function nextAnimationFrame() {
326
- return new Promise(resolve => requestAnimationFrame(() => resolve()));
365
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
327
366
  }
328
367
  function nextEventLoopTick() {
329
- return new Promise(resolve => setTimeout(() => resolve(), 0));
368
+ return new Promise((resolve) => setTimeout(() => resolve(), 0));
330
369
  }
331
370
  function nextMicrotask() {
332
371
  return Promise.resolve();
@@ -338,7 +377,7 @@ Copyright © 2021 Basecamp, LLC
338
377
  const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
339
378
  const match = lines[0].match(/^\s+/);
340
379
  const indent = match ? match[0].length : 0;
341
- return lines.map(line => line.slice(indent)).join("\n");
380
+ return lines.map((line) => line.slice(indent)).join("\n");
342
381
  }
343
382
  function interpolate(strings, values) {
344
383
  return strings.reduce((result, string, i) => {
@@ -347,7 +386,8 @@ Copyright © 2021 Basecamp, LLC
347
386
  }, "");
348
387
  }
349
388
  function uuid() {
350
- return Array.apply(null, { length: 36 }).map((_, i) => {
389
+ return Array.from({ length: 36 })
390
+ .map((_, i) => {
351
391
  if (i == 8 || i == 13 || i == 18 || i == 23) {
352
392
  return "-";
353
393
  }
@@ -360,15 +400,19 @@ Copyright © 2021 Basecamp, LLC
360
400
  else {
361
401
  return Math.floor(Math.random() * 15).toString(16);
362
402
  }
363
- }).join("");
403
+ })
404
+ .join("");
364
405
  }
365
406
  function getAttribute(attributeName, ...elements) {
366
- for (const value of elements.map(element => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
407
+ for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
367
408
  if (typeof value == "string")
368
409
  return value;
369
410
  }
370
411
  return null;
371
412
  }
413
+ function hasAttribute(attributeName, ...elements) {
414
+ return elements.some((element) => element && element.hasAttribute(attributeName));
415
+ }
372
416
  function markAsBusy(...elements) {
373
417
  for (const element of elements) {
374
418
  if (element.localName == "turbo-frame") {
@@ -385,6 +429,51 @@ Copyright © 2021 Basecamp, LLC
385
429
  element.removeAttribute("aria-busy");
386
430
  }
387
431
  }
432
+ function waitForLoad(element, timeoutInMilliseconds = 2000) {
433
+ return new Promise((resolve) => {
434
+ const onComplete = () => {
435
+ element.removeEventListener("error", onComplete);
436
+ element.removeEventListener("load", onComplete);
437
+ resolve();
438
+ };
439
+ element.addEventListener("load", onComplete, { once: true });
440
+ element.addEventListener("error", onComplete, { once: true });
441
+ setTimeout(resolve, timeoutInMilliseconds);
442
+ });
443
+ }
444
+ function getHistoryMethodForAction(action) {
445
+ switch (action) {
446
+ case "replace":
447
+ return history.replaceState;
448
+ case "advance":
449
+ case "restore":
450
+ return history.pushState;
451
+ }
452
+ }
453
+ function getVisitAction(...elements) {
454
+ const action = getAttribute("data-turbo-action", ...elements);
455
+ return isAction(action) ? action : null;
456
+ }
457
+ function getBodyElementId() {
458
+ return getMetaContent("turbo-body");
459
+ }
460
+ function getMetaElement(name) {
461
+ return document.querySelector(`meta[name="${name}"]`);
462
+ }
463
+ function getMetaContent(name) {
464
+ const element = getMetaElement(name);
465
+ return element && element.content;
466
+ }
467
+ function setMetaContent(name, content) {
468
+ let element = getMetaElement(name);
469
+ if (!element) {
470
+ element = document.createElement("meta");
471
+ element.setAttribute("name", name);
472
+ document.head.appendChild(element);
473
+ }
474
+ element.setAttribute("content", content);
475
+ return element;
476
+ }
388
477
 
389
478
  var FetchMethod;
390
479
  (function (FetchMethod) {
@@ -396,17 +485,22 @@ Copyright © 2021 Basecamp, LLC
396
485
  })(FetchMethod || (FetchMethod = {}));
397
486
  function fetchMethodFromString(method) {
398
487
  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;
488
+ case "get":
489
+ return FetchMethod.get;
490
+ case "post":
491
+ return FetchMethod.post;
492
+ case "put":
493
+ return FetchMethod.put;
494
+ case "patch":
495
+ return FetchMethod.patch;
496
+ case "delete":
497
+ return FetchMethod.delete;
404
498
  }
405
499
  }
406
500
  class FetchRequest {
407
- constructor(delegate, method, location, body = new URLSearchParams, target = null) {
408
- this.abortController = new AbortController;
409
- this.resolveRequestPromise = (value) => { };
501
+ constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
502
+ this.abortController = new AbortController();
503
+ this.resolveRequestPromise = (_value) => { };
410
504
  this.delegate = delegate;
411
505
  this.method = method;
412
506
  this.headers = this.defaultHeaders;
@@ -437,7 +531,7 @@ Copyright © 2021 Basecamp, LLC
437
531
  return await this.receive(response);
438
532
  }
439
533
  catch (error) {
440
- if (error.name !== 'AbortError') {
534
+ if (error.name !== "AbortError") {
441
535
  this.delegate.requestErrored(this, error);
442
536
  throw error;
443
537
  }
@@ -448,7 +542,11 @@ Copyright © 2021 Basecamp, LLC
448
542
  }
449
543
  async receive(response) {
450
544
  const fetchResponse = new FetchResponse(response);
451
- const event = dispatch("turbo:before-fetch-response", { cancelable: true, detail: { fetchResponse }, target: this.target });
545
+ const event = dispatch("turbo:before-fetch-response", {
546
+ cancelable: true,
547
+ detail: { fetchResponse },
548
+ target: this.target,
549
+ });
452
550
  if (event.defaultPrevented) {
453
551
  this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
454
552
  }
@@ -469,12 +567,12 @@ Copyright © 2021 Basecamp, LLC
469
567
  redirect: "follow",
470
568
  body: this.isIdempotent ? null : this.body,
471
569
  signal: this.abortSignal,
472
- referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
570
+ referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
473
571
  };
474
572
  }
475
573
  get defaultHeaders() {
476
574
  return {
477
- "Accept": "text/html, application/xhtml+xml"
575
+ Accept: "text/html, application/xhtml+xml",
478
576
  };
479
577
  }
480
578
  get isIdempotent() {
@@ -483,16 +581,19 @@ Copyright © 2021 Basecamp, LLC
483
581
  get abortSignal() {
484
582
  return this.abortController.signal;
485
583
  }
584
+ acceptResponseType(mimeType) {
585
+ this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
586
+ }
486
587
  async allowRequestToBeIntercepted(fetchOptions) {
487
- const requestInterception = new Promise(resolve => this.resolveRequestPromise = resolve);
588
+ const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
488
589
  const event = dispatch("turbo:before-fetch-request", {
489
590
  cancelable: true,
490
591
  detail: {
491
592
  fetchOptions,
492
593
  url: this.url,
493
- resume: this.resolveRequestPromise
594
+ resume: this.resolveRequestPromise,
494
595
  },
495
- target: this.target
596
+ target: this.target,
496
597
  });
497
598
  if (event.defaultPrevented)
498
599
  await requestInterception;
@@ -502,7 +603,7 @@ Copyright © 2021 Basecamp, LLC
502
603
  class AppearanceObserver {
503
604
  constructor(delegate, element) {
504
605
  this.started = false;
505
- this.intersect = entries => {
606
+ this.intersect = (entries) => {
506
607
  const lastEntry = entries.slice(-1)[0];
507
608
  if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
508
609
  this.delegate.elementAppearedInViewport(this.element);
@@ -527,40 +628,29 @@ Copyright © 2021 Basecamp, LLC
527
628
  }
528
629
 
529
630
  class StreamMessage {
530
- constructor(html) {
531
- this.templateElement = document.createElement("template");
532
- this.templateElement.innerHTML = html;
631
+ constructor(fragment) {
632
+ this.fragment = importStreamElements(fragment);
533
633
  }
534
634
  static wrap(message) {
535
635
  if (typeof message == "string") {
536
- return new this(message);
636
+ return new this(createDocumentFragment(message));
537
637
  }
538
638
  else {
539
639
  return message;
540
640
  }
541
641
  }
542
- get fragment() {
543
- const fragment = document.createDocumentFragment();
544
- for (const element of this.foreignElements) {
545
- fragment.appendChild(document.importNode(element, true));
642
+ }
643
+ StreamMessage.contentType = "text/vnd.turbo-stream.html";
644
+ function importStreamElements(fragment) {
645
+ for (const element of fragment.querySelectorAll("turbo-stream")) {
646
+ const streamElement = document.importNode(element, true);
647
+ for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
648
+ inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
546
649
  }
547
- return fragment;
548
- }
549
- get foreignElements() {
550
- return this.templateChildren.reduce((streamElements, child) => {
551
- if (child.tagName.toLowerCase() == "turbo-stream") {
552
- return [...streamElements, child];
553
- }
554
- else {
555
- return streamElements;
556
- }
557
- }, []);
558
- }
559
- get templateChildren() {
560
- return Array.from(this.templateElement.content.children);
650
+ element.replaceWith(streamElement);
561
651
  }
652
+ return fragment;
562
653
  }
563
- StreamMessage.contentType = "text/vnd.turbo-stream.html";
564
654
 
565
655
  var FormSubmissionState;
566
656
  (function (FormSubmissionState) {
@@ -579,9 +669,12 @@ Copyright © 2021 Basecamp, LLC
579
669
  })(FormEnctype || (FormEnctype = {}));
580
670
  function formEnctypeFromString(encoding) {
581
671
  switch (encoding.toLowerCase()) {
582
- case FormEnctype.multipart: return FormEnctype.multipart;
583
- case FormEnctype.plain: return FormEnctype.plain;
584
- default: return FormEnctype.urlEncoded;
672
+ case FormEnctype.multipart:
673
+ return FormEnctype.multipart;
674
+ case FormEnctype.plain:
675
+ return FormEnctype.plain;
676
+ default:
677
+ return FormEnctype.urlEncoded;
585
678
  }
586
679
  }
587
680
  class FormSubmission {
@@ -598,8 +691,8 @@ Copyright © 2021 Basecamp, LLC
598
691
  this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
599
692
  this.mustRedirect = mustRedirect;
600
693
  }
601
- static confirmMethod(message, element) {
602
- return confirm(message);
694
+ static confirmMethod(message, _element, _submitter) {
695
+ return Promise.resolve(confirm(message));
603
696
  }
604
697
  get method() {
605
698
  var _a;
@@ -608,8 +701,13 @@ Copyright © 2021 Basecamp, LLC
608
701
  }
609
702
  get action() {
610
703
  var _a;
611
- const formElementAction = typeof this.formElement.action === 'string' ? this.formElement.action : null;
612
- return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
704
+ const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
705
+ if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
706
+ return this.submitter.getAttribute("formaction") || "";
707
+ }
708
+ else {
709
+ return this.formElement.getAttribute("action") || formElementAction || "";
710
+ }
613
711
  }
614
712
  get body() {
615
713
  if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
@@ -631,16 +729,11 @@ Copyright © 2021 Basecamp, LLC
631
729
  return entries.concat(typeof value == "string" ? [[name, value]] : []);
632
730
  }, []);
633
731
  }
634
- get confirmationMessage() {
635
- return this.formElement.getAttribute("data-turbo-confirm");
636
- }
637
- get needsConfirmation() {
638
- return this.confirmationMessage !== null;
639
- }
640
732
  async start() {
641
733
  const { initialized, requesting } = FormSubmissionState;
642
- if (this.needsConfirmation) {
643
- const answer = FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
734
+ const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
735
+ if (typeof confirmationMessage === "string") {
736
+ const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
644
737
  if (!answer) {
645
738
  return;
646
739
  }
@@ -664,14 +757,19 @@ Copyright © 2021 Basecamp, LLC
664
757
  if (token) {
665
758
  headers["X-CSRF-Token"] = token;
666
759
  }
667
- headers["Accept"] = [StreamMessage.contentType, headers["Accept"]].join(", ");
760
+ }
761
+ if (this.requestAcceptsTurboStreamResponse(request)) {
762
+ request.acceptResponseType(StreamMessage.contentType);
668
763
  }
669
764
  }
670
- requestStarted(request) {
765
+ requestStarted(_request) {
671
766
  var _a;
672
767
  this.state = FormSubmissionState.waiting;
673
768
  (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
674
- dispatch("turbo:submit-start", { target: this.formElement, detail: { formSubmission: this } });
769
+ dispatch("turbo:submit-start", {
770
+ target: this.formElement,
771
+ detail: { formSubmission: this },
772
+ });
675
773
  this.delegate.formSubmissionStarted(this);
676
774
  }
677
775
  requestPreventedHandlingResponse(request, response) {
@@ -697,25 +795,35 @@ Copyright © 2021 Basecamp, LLC
697
795
  }
698
796
  requestErrored(request, error) {
699
797
  this.result = { success: false, error };
798
+ dispatch("turbo:fetch-request-error", {
799
+ target: this.formElement,
800
+ detail: { request, error },
801
+ });
700
802
  this.delegate.formSubmissionErrored(this, error);
701
803
  }
702
- requestFinished(request) {
804
+ requestFinished(_request) {
703
805
  var _a;
704
806
  this.state = FormSubmissionState.stopped;
705
807
  (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
706
- dispatch("turbo:submit-end", { target: this.formElement, detail: Object.assign({ formSubmission: this }, this.result) });
808
+ dispatch("turbo:submit-end", {
809
+ target: this.formElement,
810
+ detail: Object.assign({ formSubmission: this }, this.result),
811
+ });
707
812
  this.delegate.formSubmissionFinished(this);
708
813
  }
709
814
  requestMustRedirect(request) {
710
815
  return !request.isIdempotent && this.mustRedirect;
711
816
  }
817
+ requestAcceptsTurboStreamResponse(request) {
818
+ return !request.isIdempotent || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
819
+ }
712
820
  }
713
821
  function buildFormData(formElement, submitter) {
714
822
  const formData = new FormData(formElement);
715
823
  const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
716
824
  const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
717
- if (name && value != null && formData.get(name) != value) {
718
- formData.append(name, value);
825
+ if (name) {
826
+ formData.append(name, value || "");
719
827
  }
720
828
  return formData;
721
829
  }
@@ -729,15 +837,11 @@ Copyright © 2021 Basecamp, LLC
729
837
  }
730
838
  }
731
839
  }
732
- function getMetaContent(name) {
733
- const element = document.querySelector(`meta[name="${name}"]`);
734
- return element && element.content;
735
- }
736
840
  function responseSucceededWithoutRedirect(response) {
737
841
  return response.statusCode == 200 && !response.redirected;
738
842
  }
739
843
  function mergeFormDataEntries(url, entries) {
740
- const searchParams = new URLSearchParams;
844
+ const searchParams = new URLSearchParams();
741
845
  for (const [name, value] of entries) {
742
846
  if (value instanceof File)
743
847
  continue;
@@ -751,6 +855,9 @@ Copyright © 2021 Basecamp, LLC
751
855
  constructor(element) {
752
856
  this.element = element;
753
857
  }
858
+ get activeElement() {
859
+ return this.element.ownerDocument.activeElement;
860
+ }
754
861
  get children() {
755
862
  return [...this.element.children];
756
863
  }
@@ -764,13 +871,20 @@ Copyright © 2021 Basecamp, LLC
764
871
  return this.element.isConnected;
765
872
  }
766
873
  get firstAutofocusableElement() {
767
- return this.element.querySelector("[autofocus]");
874
+ const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
875
+ for (const element of this.element.querySelectorAll("[autofocus]")) {
876
+ if (element.closest(inertDisabledOrHidden) == null)
877
+ return element;
878
+ else
879
+ continue;
880
+ }
881
+ return null;
768
882
  }
769
883
  get permanentElements() {
770
- return [...this.element.querySelectorAll("[id][data-turbo-permanent]")];
884
+ return queryPermanentElementsAll(this.element);
771
885
  }
772
886
  getPermanentElementById(id) {
773
- return this.element.querySelector(`#${id}[data-turbo-permanent]`);
887
+ return getPermanentElementById(this.element, id);
774
888
  }
775
889
  getPermanentElementMapForSnapshot(snapshot) {
776
890
  const permanentElementMap = {};
@@ -784,36 +898,66 @@ Copyright © 2021 Basecamp, LLC
784
898
  return permanentElementMap;
785
899
  }
786
900
  }
901
+ function getPermanentElementById(node, id) {
902
+ return node.querySelector(`#${id}[data-turbo-permanent]`);
903
+ }
904
+ function queryPermanentElementsAll(node) {
905
+ return node.querySelectorAll("[id][data-turbo-permanent]");
906
+ }
787
907
 
788
- class FormInterceptor {
789
- constructor(delegate, element) {
908
+ class FormSubmitObserver {
909
+ constructor(delegate, eventTarget) {
910
+ this.started = false;
911
+ this.submitCaptured = () => {
912
+ this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
913
+ this.eventTarget.addEventListener("submit", this.submitBubbled, false);
914
+ };
790
915
  this.submitBubbled = ((event) => {
791
- const form = event.target;
792
- if (!event.defaultPrevented && form instanceof HTMLFormElement && form.closest("turbo-frame, html") == this.element) {
916
+ if (!event.defaultPrevented) {
917
+ const form = event.target instanceof HTMLFormElement ? event.target : undefined;
793
918
  const submitter = event.submitter || undefined;
794
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
795
- if (method != "dialog" && this.delegate.shouldInterceptFormSubmission(form, submitter)) {
919
+ if (form &&
920
+ submissionDoesNotDismissDialog(form, submitter) &&
921
+ submissionDoesNotTargetIFrame(form, submitter) &&
922
+ this.delegate.willSubmitForm(form, submitter)) {
796
923
  event.preventDefault();
797
- event.stopImmediatePropagation();
798
- this.delegate.formSubmissionIntercepted(form, submitter);
924
+ this.delegate.formSubmitted(form, submitter);
799
925
  }
800
926
  }
801
927
  });
802
928
  this.delegate = delegate;
803
- this.element = element;
929
+ this.eventTarget = eventTarget;
804
930
  }
805
931
  start() {
806
- this.element.addEventListener("submit", this.submitBubbled);
932
+ if (!this.started) {
933
+ this.eventTarget.addEventListener("submit", this.submitCaptured, true);
934
+ this.started = true;
935
+ }
807
936
  }
808
937
  stop() {
809
- this.element.removeEventListener("submit", this.submitBubbled);
938
+ if (this.started) {
939
+ this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
940
+ this.started = false;
941
+ }
942
+ }
943
+ }
944
+ function submissionDoesNotDismissDialog(form, submitter) {
945
+ const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
946
+ return method != "dialog";
947
+ }
948
+ function submissionDoesNotTargetIFrame(form, submitter) {
949
+ const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
950
+ for (const element of document.getElementsByName(target)) {
951
+ if (element instanceof HTMLIFrameElement)
952
+ return false;
810
953
  }
954
+ return true;
811
955
  }
812
956
 
813
957
  class View {
814
958
  constructor(delegate, element) {
815
- this.resolveRenderPromise = (value) => { };
816
- this.resolveInterceptionPromise = (value) => { };
959
+ this.resolveRenderPromise = (_value) => { };
960
+ this.resolveInterceptionPromise = (_value) => { };
817
961
  this.delegate = delegate;
818
962
  this.element = element;
819
963
  }
@@ -858,15 +1002,17 @@ Copyright © 2021 Basecamp, LLC
858
1002
  const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
859
1003
  if (shouldRender) {
860
1004
  try {
861
- this.renderPromise = new Promise(resolve => this.resolveRenderPromise = resolve);
1005
+ this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
862
1006
  this.renderer = renderer;
863
- this.prepareToRenderSnapshot(renderer);
864
- const renderInterception = new Promise(resolve => this.resolveInterceptionPromise = resolve);
865
- const immediateRender = this.delegate.allowsImmediateRender(snapshot, this.resolveInterceptionPromise);
1007
+ await this.prepareToRenderSnapshot(renderer);
1008
+ const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
1009
+ const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
1010
+ const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
866
1011
  if (!immediateRender)
867
1012
  await renderInterception;
868
1013
  await this.renderSnapshot(renderer);
869
1014
  this.delegate.viewRenderedSnapshot(snapshot, isPreview);
1015
+ this.delegate.preloadOnLoadLinksForView(this.element);
870
1016
  this.finishRenderingSnapshot(renderer);
871
1017
  }
872
1018
  finally {
@@ -876,15 +1022,15 @@ Copyright © 2021 Basecamp, LLC
876
1022
  }
877
1023
  }
878
1024
  else {
879
- this.invalidate();
1025
+ this.invalidate(renderer.reloadReason);
880
1026
  }
881
1027
  }
882
- invalidate() {
883
- this.delegate.viewInvalidated();
1028
+ invalidate(reason) {
1029
+ this.delegate.viewInvalidated(reason);
884
1030
  }
885
- prepareToRenderSnapshot(renderer) {
1031
+ async prepareToRenderSnapshot(renderer) {
886
1032
  this.markAsPreview(renderer.isPreview);
887
- renderer.prepareToRender();
1033
+ await renderer.prepareToRender();
888
1034
  }
889
1035
  markAsPreview(isPreview) {
890
1036
  if (isPreview) {
@@ -911,65 +1057,125 @@ Copyright © 2021 Basecamp, LLC
911
1057
  }
912
1058
  }
913
1059
 
914
- class LinkInterceptor {
915
- constructor(delegate, element) {
916
- this.clickBubbled = (event) => {
917
- if (this.respondsToEventTarget(event.target)) {
918
- this.clickEvent = event;
919
- }
920
- else {
921
- delete this.clickEvent;
922
- }
1060
+ class LinkClickObserver {
1061
+ constructor(delegate, eventTarget) {
1062
+ this.started = false;
1063
+ this.clickCaptured = () => {
1064
+ this.eventTarget.removeEventListener("click", this.clickBubbled, false);
1065
+ this.eventTarget.addEventListener("click", this.clickBubbled, false);
923
1066
  };
924
- this.linkClicked = ((event) => {
925
- if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
926
- if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url)) {
927
- this.clickEvent.preventDefault();
928
- event.preventDefault();
929
- this.delegate.linkClickIntercepted(event.target, event.detail.url);
1067
+ this.clickBubbled = (event) => {
1068
+ if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
1069
+ const target = (event.composedPath && event.composedPath()[0]) || event.target;
1070
+ const link = this.findLinkFromClickTarget(target);
1071
+ if (link && doesNotTargetIFrame(link)) {
1072
+ const location = this.getLocationForLink(link);
1073
+ if (this.delegate.willFollowLinkToLocation(link, location, event)) {
1074
+ event.preventDefault();
1075
+ this.delegate.followedLinkToLocation(link, location);
1076
+ }
930
1077
  }
931
1078
  }
932
- delete this.clickEvent;
933
- });
934
- this.willVisit = () => {
935
- delete this.clickEvent;
936
1079
  };
937
1080
  this.delegate = delegate;
938
- this.element = element;
1081
+ this.eventTarget = eventTarget;
1082
+ }
1083
+ start() {
1084
+ if (!this.started) {
1085
+ this.eventTarget.addEventListener("click", this.clickCaptured, true);
1086
+ this.started = true;
1087
+ }
1088
+ }
1089
+ stop() {
1090
+ if (this.started) {
1091
+ this.eventTarget.removeEventListener("click", this.clickCaptured, true);
1092
+ this.started = false;
1093
+ }
1094
+ }
1095
+ clickEventIsSignificant(event) {
1096
+ return !((event.target && event.target.isContentEditable) ||
1097
+ event.defaultPrevented ||
1098
+ event.which > 1 ||
1099
+ event.altKey ||
1100
+ event.ctrlKey ||
1101
+ event.metaKey ||
1102
+ event.shiftKey);
1103
+ }
1104
+ findLinkFromClickTarget(target) {
1105
+ if (target instanceof Element) {
1106
+ return target.closest("a[href]:not([target^=_]):not([download])");
1107
+ }
1108
+ }
1109
+ getLocationForLink(link) {
1110
+ return expandURL(link.getAttribute("href") || "");
1111
+ }
1112
+ }
1113
+ function doesNotTargetIFrame(anchor) {
1114
+ for (const element of document.getElementsByName(anchor.target)) {
1115
+ if (element instanceof HTMLIFrameElement)
1116
+ return false;
1117
+ }
1118
+ return true;
1119
+ }
1120
+
1121
+ class FormLinkClickObserver {
1122
+ constructor(delegate, element) {
1123
+ this.delegate = delegate;
1124
+ this.linkClickObserver = new LinkClickObserver(this, element);
939
1125
  }
940
1126
  start() {
941
- this.element.addEventListener("click", this.clickBubbled);
942
- document.addEventListener("turbo:click", this.linkClicked);
943
- document.addEventListener("turbo:before-visit", this.willVisit);
1127
+ this.linkClickObserver.start();
944
1128
  }
945
1129
  stop() {
946
- this.element.removeEventListener("click", this.clickBubbled);
947
- document.removeEventListener("turbo:click", this.linkClicked);
948
- document.removeEventListener("turbo:before-visit", this.willVisit);
1130
+ this.linkClickObserver.stop();
1131
+ }
1132
+ willFollowLinkToLocation(link, location, originalEvent) {
1133
+ return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
1134
+ link.hasAttribute("data-turbo-method"));
949
1135
  }
950
- respondsToEventTarget(target) {
951
- const element = target instanceof Element
952
- ? target
953
- : target instanceof Node
954
- ? target.parentElement
955
- : null;
956
- return element && element.closest("turbo-frame, html") == this.element;
1136
+ followedLinkToLocation(link, location) {
1137
+ const action = location.href;
1138
+ const form = document.createElement("form");
1139
+ form.setAttribute("data-turbo", "true");
1140
+ form.setAttribute("action", action);
1141
+ form.setAttribute("hidden", "");
1142
+ const method = link.getAttribute("data-turbo-method");
1143
+ if (method)
1144
+ form.setAttribute("method", method);
1145
+ const turboFrame = link.getAttribute("data-turbo-frame");
1146
+ if (turboFrame)
1147
+ form.setAttribute("data-turbo-frame", turboFrame);
1148
+ const turboAction = link.getAttribute("data-turbo-action");
1149
+ if (turboAction)
1150
+ form.setAttribute("data-turbo-action", turboAction);
1151
+ const turboConfirm = link.getAttribute("data-turbo-confirm");
1152
+ if (turboConfirm)
1153
+ form.setAttribute("data-turbo-confirm", turboConfirm);
1154
+ const turboStream = link.hasAttribute("data-turbo-stream");
1155
+ if (turboStream)
1156
+ form.setAttribute("data-turbo-stream", "");
1157
+ this.delegate.submittedFormLinkToLocation(link, location, form);
1158
+ document.body.appendChild(form);
1159
+ form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
1160
+ requestAnimationFrame(() => form.requestSubmit());
957
1161
  }
958
1162
  }
959
1163
 
960
1164
  class Bardo {
961
- constructor(permanentElementMap) {
1165
+ constructor(delegate, permanentElementMap) {
1166
+ this.delegate = delegate;
962
1167
  this.permanentElementMap = permanentElementMap;
963
1168
  }
964
- static preservingPermanentElements(permanentElementMap, callback) {
965
- const bardo = new this(permanentElementMap);
1169
+ static preservingPermanentElements(delegate, permanentElementMap, callback) {
1170
+ const bardo = new this(delegate, permanentElementMap);
966
1171
  bardo.enter();
967
1172
  callback();
968
1173
  bardo.leave();
969
1174
  }
970
1175
  enter() {
971
1176
  for (const id in this.permanentElementMap) {
972
- const [, newPermanentElement] = this.permanentElementMap[id];
1177
+ const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
1178
+ this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
973
1179
  this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
974
1180
  }
975
1181
  }
@@ -978,6 +1184,7 @@ Copyright © 2021 Basecamp, LLC
978
1184
  const [currentPermanentElement] = this.permanentElementMap[id];
979
1185
  this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
980
1186
  this.replacePlaceholderWithPermanentElement(currentPermanentElement);
1187
+ this.delegate.leavingBardo(currentPermanentElement);
981
1188
  }
982
1189
  }
983
1190
  replaceNewPermanentElementWithPlaceholder(permanentElement) {
@@ -993,7 +1200,7 @@ Copyright © 2021 Basecamp, LLC
993
1200
  placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
994
1201
  }
995
1202
  getPlaceholderById(id) {
996
- return this.placeholders.find(element => element.content == id);
1203
+ return this.placeholders.find((element) => element.content == id);
997
1204
  }
998
1205
  get placeholders() {
999
1206
  return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
@@ -1007,16 +1214,21 @@ Copyright © 2021 Basecamp, LLC
1007
1214
  }
1008
1215
 
1009
1216
  class Renderer {
1010
- constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
1217
+ constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1218
+ this.activeElement = null;
1011
1219
  this.currentSnapshot = currentSnapshot;
1012
1220
  this.newSnapshot = newSnapshot;
1013
1221
  this.isPreview = isPreview;
1014
1222
  this.willRender = willRender;
1015
- this.promise = new Promise((resolve, reject) => this.resolvingFunctions = { resolve, reject });
1223
+ this.renderElement = renderElement;
1224
+ this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
1016
1225
  }
1017
1226
  get shouldRender() {
1018
1227
  return true;
1019
1228
  }
1229
+ get reloadReason() {
1230
+ return;
1231
+ }
1020
1232
  prepareToRender() {
1021
1233
  return;
1022
1234
  }
@@ -1026,23 +1238,8 @@ Copyright © 2021 Basecamp, LLC
1026
1238
  delete this.resolvingFunctions;
1027
1239
  }
1028
1240
  }
1029
- createScriptElement(element) {
1030
- if (element.getAttribute("data-turbo-eval") == "false") {
1031
- return element;
1032
- }
1033
- else {
1034
- const createdScriptElement = document.createElement("script");
1035
- if (this.cspNonce) {
1036
- createdScriptElement.nonce = this.cspNonce;
1037
- }
1038
- createdScriptElement.textContent = element.textContent;
1039
- createdScriptElement.async = false;
1040
- copyElementAttributes(createdScriptElement, element);
1041
- return createdScriptElement;
1042
- }
1043
- }
1044
1241
  preservingPermanentElements(callback) {
1045
- Bardo.preservingPermanentElements(this.permanentElementMap, callback);
1242
+ Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
1046
1243
  }
1047
1244
  focusFirstAutofocusableElement() {
1048
1245
  const element = this.connectedSnapshot.firstAutofocusableElement;
@@ -1050,6 +1247,19 @@ Copyright © 2021 Basecamp, LLC
1050
1247
  element.focus();
1051
1248
  }
1052
1249
  }
1250
+ enteringBardo(currentPermanentElement) {
1251
+ if (this.activeElement)
1252
+ return;
1253
+ if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
1254
+ this.activeElement = this.currentSnapshot.activeElement;
1255
+ }
1256
+ }
1257
+ leavingBardo(currentPermanentElement) {
1258
+ if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
1259
+ this.activeElement.focus();
1260
+ this.activeElement = null;
1261
+ }
1262
+ }
1053
1263
  get connectedSnapshot() {
1054
1264
  return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
1055
1265
  }
@@ -1062,21 +1272,28 @@ Copyright © 2021 Basecamp, LLC
1062
1272
  get permanentElementMap() {
1063
1273
  return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
1064
1274
  }
1065
- get cspNonce() {
1066
- var _a;
1067
- return (_a = document.head.querySelector('meta[name="csp-nonce"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
1068
- }
1069
- }
1070
- function copyElementAttributes(destinationElement, sourceElement) {
1071
- for (const { name, value } of [...sourceElement.attributes]) {
1072
- destinationElement.setAttribute(name, value);
1073
- }
1074
1275
  }
1075
1276
  function elementIsFocusable(element) {
1076
1277
  return element && typeof element.focus == "function";
1077
1278
  }
1078
1279
 
1079
1280
  class FrameRenderer extends Renderer {
1281
+ constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1282
+ super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
1283
+ this.delegate = delegate;
1284
+ }
1285
+ static renderElement(currentElement, newElement) {
1286
+ var _a;
1287
+ const destinationRange = document.createRange();
1288
+ destinationRange.selectNodeContents(currentElement);
1289
+ destinationRange.deleteContents();
1290
+ const frameElement = newElement;
1291
+ const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1292
+ if (sourceRange) {
1293
+ sourceRange.selectNodeContents(frameElement);
1294
+ currentElement.appendChild(sourceRange.extractContents());
1295
+ }
1296
+ }
1080
1297
  get shouldRender() {
1081
1298
  return true;
1082
1299
  }
@@ -1092,23 +1309,16 @@ Copyright © 2021 Basecamp, LLC
1092
1309
  this.activateScriptElements();
1093
1310
  }
1094
1311
  loadFrameElement() {
1095
- var _a;
1096
- const destinationRange = document.createRange();
1097
- destinationRange.selectNodeContents(this.currentElement);
1098
- destinationRange.deleteContents();
1099
- const frameElement = this.newElement;
1100
- const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1101
- if (sourceRange) {
1102
- sourceRange.selectNodeContents(frameElement);
1103
- this.currentElement.appendChild(sourceRange.extractContents());
1104
- }
1312
+ this.delegate.willRenderFrame(this.currentElement, this.newElement);
1313
+ this.renderElement(this.currentElement, this.newElement);
1105
1314
  }
1106
1315
  scrollFrameIntoView() {
1107
1316
  if (this.currentElement.autoscroll || this.newElement.autoscroll) {
1108
1317
  const element = this.currentElement.firstElementChild;
1109
1318
  const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
1319
+ const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
1110
1320
  if (element) {
1111
- element.scrollIntoView({ block });
1321
+ element.scrollIntoView({ block, behavior });
1112
1322
  return true;
1113
1323
  }
1114
1324
  }
@@ -1116,7 +1326,7 @@ Copyright © 2021 Basecamp, LLC
1116
1326
  }
1117
1327
  activateScriptElements() {
1118
1328
  for (const inertScriptElement of this.newScriptElements) {
1119
- const activatedScriptElement = this.createScriptElement(inertScriptElement);
1329
+ const activatedScriptElement = activateScriptElement(inertScriptElement);
1120
1330
  inertScriptElement.replaceWith(activatedScriptElement);
1121
1331
  }
1122
1332
  }
@@ -1132,6 +1342,14 @@ Copyright © 2021 Basecamp, LLC
1132
1342
  return defaultValue;
1133
1343
  }
1134
1344
  }
1345
+ function readScrollBehavior(value, defaultValue) {
1346
+ if (value == "auto" || value == "smooth") {
1347
+ return value;
1348
+ }
1349
+ else {
1350
+ return defaultValue;
1351
+ }
1352
+ }
1135
1353
 
1136
1354
  class ProgressBar {
1137
1355
  constructor() {
@@ -1155,7 +1373,7 @@ Copyright © 2021 Basecamp, LLC
1155
1373
  left: 0;
1156
1374
  height: 3px;
1157
1375
  background: #0076ff;
1158
- z-index: 9999;
1376
+ z-index: 2147483647;
1159
1377
  transition:
1160
1378
  width ${ProgressBar.animationDuration}ms ease-out,
1161
1379
  opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
@@ -1214,13 +1432,16 @@ Copyright © 2021 Basecamp, LLC
1214
1432
  }
1215
1433
  refresh() {
1216
1434
  requestAnimationFrame(() => {
1217
- this.progressElement.style.width = `${10 + (this.value * 90)}%`;
1435
+ this.progressElement.style.width = `${10 + this.value * 90}%`;
1218
1436
  });
1219
1437
  }
1220
1438
  createStylesheetElement() {
1221
1439
  const element = document.createElement("style");
1222
1440
  element.type = "text/css";
1223
1441
  element.textContent = ProgressBar.defaultCSS;
1442
+ if (this.cspNonce) {
1443
+ element.nonce = this.cspNonce;
1444
+ }
1224
1445
  return element;
1225
1446
  }
1226
1447
  createProgressElement() {
@@ -1228,6 +1449,9 @@ Copyright © 2021 Basecamp, LLC
1228
1449
  element.className = "turbo-progress-bar";
1229
1450
  return element;
1230
1451
  }
1452
+ get cspNonce() {
1453
+ return getMetaContent("csp-nonce");
1454
+ }
1231
1455
  }
1232
1456
  ProgressBar.animationDuration = 300;
1233
1457
 
@@ -1244,14 +1468,14 @@ Copyright © 2021 Basecamp, LLC
1244
1468
  : {
1245
1469
  type: elementType(element),
1246
1470
  tracked: elementIsTracked(element),
1247
- elements: []
1471
+ elements: [],
1248
1472
  };
1249
1473
  return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
1250
1474
  }, {});
1251
1475
  }
1252
1476
  get trackedElementSignature() {
1253
1477
  return Object.keys(this.detailsByOuterHTML)
1254
- .filter(outerHTML => this.detailsByOuterHTML[outerHTML].tracked)
1478
+ .filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
1255
1479
  .join("");
1256
1480
  }
1257
1481
  getScriptElementsNotInSnapshot(snapshot) {
@@ -1262,8 +1486,8 @@ Copyright © 2021 Basecamp, LLC
1262
1486
  }
1263
1487
  getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
1264
1488
  return Object.keys(this.detailsByOuterHTML)
1265
- .filter(outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))
1266
- .map(outerHTML => this.detailsByOuterHTML[outerHTML])
1489
+ .filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
1490
+ .map((outerHTML) => this.detailsByOuterHTML[outerHTML])
1267
1491
  .filter(({ type }) => type == matchedType)
1268
1492
  .map(({ elements: [element] }) => element);
1269
1493
  }
@@ -1283,13 +1507,11 @@ Copyright © 2021 Basecamp, LLC
1283
1507
  }
1284
1508
  getMetaValue(name) {
1285
1509
  const element = this.findMetaElementByName(name);
1286
- return element
1287
- ? element.getAttribute("content")
1288
- : null;
1510
+ return element ? element.getAttribute("content") : null;
1289
1511
  }
1290
1512
  findMetaElementByName(name) {
1291
1513
  return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
1292
- const { elements: [element] } = this.detailsByOuterHTML[outerHTML];
1514
+ const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
1293
1515
  return elementIsMetaElementWithName(element, name) ? element : result;
1294
1516
  }, undefined);
1295
1517
  }
@@ -1306,19 +1528,19 @@ Copyright © 2021 Basecamp, LLC
1306
1528
  return element.getAttribute("data-turbo-track") == "reload";
1307
1529
  }
1308
1530
  function elementIsScript(element) {
1309
- const tagName = element.tagName.toLowerCase();
1531
+ const tagName = element.localName;
1310
1532
  return tagName == "script";
1311
1533
  }
1312
1534
  function elementIsNoscript(element) {
1313
- const tagName = element.tagName.toLowerCase();
1535
+ const tagName = element.localName;
1314
1536
  return tagName == "noscript";
1315
1537
  }
1316
1538
  function elementIsStylesheet(element) {
1317
- const tagName = element.tagName.toLowerCase();
1539
+ const tagName = element.localName;
1318
1540
  return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
1319
1541
  }
1320
1542
  function elementIsMetaElementWithName(element, name) {
1321
- const tagName = element.tagName.toLowerCase();
1543
+ const tagName = element.localName;
1322
1544
  return tagName == "meta" && element.getAttribute("name") == name;
1323
1545
  }
1324
1546
  function elementWithoutNonce(element) {
@@ -1343,7 +1565,20 @@ Copyright © 2021 Basecamp, LLC
1343
1565
  return new this(body, new HeadSnapshot(head));
1344
1566
  }
1345
1567
  clone() {
1346
- return new PageSnapshot(this.element.cloneNode(true), this.headSnapshot);
1568
+ const clonedElement = this.element.cloneNode(true);
1569
+ const selectElements = this.element.querySelectorAll("select");
1570
+ const clonedSelectElements = clonedElement.querySelectorAll("select");
1571
+ for (const [index, source] of selectElements.entries()) {
1572
+ const clone = clonedSelectElements[index];
1573
+ for (const option of clone.selectedOptions)
1574
+ option.selected = false;
1575
+ for (const option of source.selectedOptions)
1576
+ clone.options[option.index].selected = true;
1577
+ }
1578
+ for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
1579
+ clonedPasswordInput.value = "";
1580
+ }
1581
+ return new PageSnapshot(clonedElement, this.headSnapshot);
1347
1582
  }
1348
1583
  get headElement() {
1349
1584
  return this.headSnapshot.element;
@@ -1390,6 +1625,10 @@ Copyright © 2021 Basecamp, LLC
1390
1625
  historyChanged: false,
1391
1626
  visitCachedSnapshot: () => { },
1392
1627
  willRender: true,
1628
+ updateHistory: true,
1629
+ shouldCacheSnapshot: true,
1630
+ acceptsStreamResponse: false,
1631
+ initiator: document.documentElement,
1393
1632
  };
1394
1633
  var SystemStatusCode;
1395
1634
  (function (SystemStatusCode) {
@@ -1399,17 +1638,19 @@ Copyright © 2021 Basecamp, LLC
1399
1638
  })(SystemStatusCode || (SystemStatusCode = {}));
1400
1639
  class Visit {
1401
1640
  constructor(delegate, location, restorationIdentifier, options = {}) {
1402
- this.identifier = uuid();
1403
1641
  this.timingMetrics = {};
1404
1642
  this.followedRedirect = false;
1405
1643
  this.historyChanged = false;
1406
1644
  this.scrolled = false;
1645
+ this.shouldCacheSnapshot = true;
1646
+ this.acceptsStreamResponse = false;
1407
1647
  this.snapshotCached = false;
1408
1648
  this.state = VisitState.initialized;
1409
1649
  this.delegate = delegate;
1410
1650
  this.location = location;
1411
1651
  this.restorationIdentifier = restorationIdentifier || uuid();
1412
- const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender } = Object.assign(Object.assign({}, defaultOptions), options);
1652
+ this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
1653
+ const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, initiator, } = Object.assign(Object.assign({}, defaultOptions), options);
1413
1654
  this.action = action;
1414
1655
  this.historyChanged = historyChanged;
1415
1656
  this.referrer = referrer;
@@ -1418,7 +1659,11 @@ Copyright © 2021 Basecamp, LLC
1418
1659
  this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
1419
1660
  this.visitCachedSnapshot = visitCachedSnapshot;
1420
1661
  this.willRender = willRender;
1662
+ this.updateHistory = updateHistory;
1421
1663
  this.scrolled = !willRender;
1664
+ this.shouldCacheSnapshot = shouldCacheSnapshot;
1665
+ this.acceptsStreamResponse = acceptsStreamResponse;
1666
+ this.initiator = initiator;
1422
1667
  }
1423
1668
  get adapter() {
1424
1669
  return this.delegate.adapter;
@@ -1450,28 +1695,33 @@ Copyright © 2021 Basecamp, LLC
1450
1695
  }
1451
1696
  this.cancelRender();
1452
1697
  this.state = VisitState.canceled;
1698
+ this.resolvingFunctions.reject();
1453
1699
  }
1454
1700
  }
1455
1701
  complete() {
1456
1702
  if (this.state == VisitState.started) {
1457
1703
  this.recordTimingMetric(TimingMetric.visitEnd);
1458
1704
  this.state = VisitState.completed;
1459
- this.adapter.visitCompleted(this);
1460
- this.delegate.visitCompleted(this);
1461
1705
  this.followRedirect();
1706
+ if (!this.followedRedirect) {
1707
+ this.adapter.visitCompleted(this);
1708
+ this.delegate.visitCompleted(this);
1709
+ }
1710
+ this.resolvingFunctions.resolve();
1462
1711
  }
1463
1712
  }
1464
1713
  fail() {
1465
1714
  if (this.state == VisitState.started) {
1466
1715
  this.state = VisitState.failed;
1467
1716
  this.adapter.visitFailed(this);
1717
+ this.resolvingFunctions.reject();
1468
1718
  }
1469
1719
  }
1470
1720
  changeHistory() {
1471
1721
  var _a;
1472
- if (!this.historyChanged) {
1722
+ if (!this.historyChanged && this.updateHistory) {
1473
1723
  const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
1474
- const method = this.getHistoryMethodForAction(actionForHistory);
1724
+ const method = getHistoryMethodForAction(actionForHistory);
1475
1725
  this.history.update(method, this.location, this.restorationIdentifier);
1476
1726
  this.historyChanged = true;
1477
1727
  }
@@ -1481,7 +1731,7 @@ Copyright © 2021 Basecamp, LLC
1481
1731
  this.simulateRequest();
1482
1732
  }
1483
1733
  else if (this.shouldIssueRequest() && !this.request) {
1484
- this.request = new FetchRequest(this, FetchMethod.get, this.location);
1734
+ this.request = new FetchRequest(this, FetchMethod.get, this.location, undefined, this.initiator);
1485
1735
  this.request.perform();
1486
1736
  }
1487
1737
  }
@@ -1516,16 +1766,18 @@ Copyright © 2021 Basecamp, LLC
1516
1766
  if (this.response) {
1517
1767
  const { statusCode, responseHTML } = this.response;
1518
1768
  this.render(async () => {
1519
- this.cacheSnapshot();
1769
+ if (this.shouldCacheSnapshot)
1770
+ this.cacheSnapshot();
1520
1771
  if (this.view.renderPromise)
1521
1772
  await this.view.renderPromise;
1522
1773
  if (isSuccessful(statusCode) && responseHTML != null) {
1523
- await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender);
1774
+ await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
1775
+ this.performScroll();
1524
1776
  this.adapter.visitRendered(this);
1525
1777
  this.complete();
1526
1778
  }
1527
1779
  else {
1528
- await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML));
1780
+ await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
1529
1781
  this.adapter.visitRendered(this);
1530
1782
  this.fail();
1531
1783
  }
@@ -1560,7 +1812,8 @@ Copyright © 2021 Basecamp, LLC
1560
1812
  else {
1561
1813
  if (this.view.renderPromise)
1562
1814
  await this.view.renderPromise;
1563
- await this.view.renderPage(snapshot, isPreview, this.willRender);
1815
+ await this.view.renderPage(snapshot, isPreview, this.willRender, this);
1816
+ this.performScroll();
1564
1817
  this.adapter.visitRendered(this);
1565
1818
  if (!isPreview) {
1566
1819
  this.complete();
@@ -1573,8 +1826,8 @@ Copyright © 2021 Basecamp, LLC
1573
1826
  var _a;
1574
1827
  if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
1575
1828
  this.adapter.visitProposedToLocation(this.redirectedToLocation, {
1576
- action: 'replace',
1577
- response: this.response
1829
+ action: "replace",
1830
+ response: this.response,
1578
1831
  });
1579
1832
  this.followedRedirect = true;
1580
1833
  }
@@ -1583,20 +1836,28 @@ Copyright © 2021 Basecamp, LLC
1583
1836
  if (this.isSamePage) {
1584
1837
  this.render(async () => {
1585
1838
  this.cacheSnapshot();
1839
+ this.performScroll();
1586
1840
  this.adapter.visitRendered(this);
1587
1841
  });
1588
1842
  }
1589
1843
  }
1844
+ prepareHeadersForRequest(headers, request) {
1845
+ if (this.acceptsStreamResponse) {
1846
+ request.acceptResponseType(StreamMessage.contentType);
1847
+ }
1848
+ }
1590
1849
  requestStarted() {
1591
1850
  this.startRequest();
1592
1851
  }
1593
- requestPreventedHandlingResponse(request, response) {
1594
- }
1852
+ requestPreventedHandlingResponse(_request, _response) { }
1595
1853
  async requestSucceededWithResponse(request, response) {
1596
1854
  const responseHTML = await response.responseHTML;
1597
1855
  const { redirected, statusCode } = response;
1598
1856
  if (responseHTML == undefined) {
1599
- this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
1857
+ this.recordResponse({
1858
+ statusCode: SystemStatusCode.contentTypeMismatch,
1859
+ redirected,
1860
+ });
1600
1861
  }
1601
1862
  else {
1602
1863
  this.redirectedToLocation = response.redirected ? response.location : undefined;
@@ -1607,20 +1868,26 @@ Copyright © 2021 Basecamp, LLC
1607
1868
  const responseHTML = await response.responseHTML;
1608
1869
  const { redirected, statusCode } = response;
1609
1870
  if (responseHTML == undefined) {
1610
- this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
1871
+ this.recordResponse({
1872
+ statusCode: SystemStatusCode.contentTypeMismatch,
1873
+ redirected,
1874
+ });
1611
1875
  }
1612
1876
  else {
1613
1877
  this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
1614
1878
  }
1615
1879
  }
1616
- requestErrored(request, error) {
1617
- this.recordResponse({ statusCode: SystemStatusCode.networkFailure, redirected: false });
1880
+ requestErrored(_request, _error) {
1881
+ this.recordResponse({
1882
+ statusCode: SystemStatusCode.networkFailure,
1883
+ redirected: false,
1884
+ });
1618
1885
  }
1619
1886
  requestFinished() {
1620
1887
  this.finishRequest();
1621
1888
  }
1622
1889
  performScroll() {
1623
- if (!this.scrolled) {
1890
+ if (!this.scrolled && !this.view.forceReloaded) {
1624
1891
  if (this.action == "restore") {
1625
1892
  this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
1626
1893
  }
@@ -1655,9 +1922,11 @@ Copyright © 2021 Basecamp, LLC
1655
1922
  }
1656
1923
  getHistoryMethodForAction(action) {
1657
1924
  switch (action) {
1658
- case "replace": return history.replaceState;
1925
+ case "replace":
1926
+ return history.replaceState;
1659
1927
  case "advance":
1660
- case "restore": return history.pushState;
1928
+ case "restore":
1929
+ return history.pushState;
1661
1930
  }
1662
1931
  }
1663
1932
  hasPreloadedResponse() {
@@ -1676,18 +1945,17 @@ Copyright © 2021 Basecamp, LLC
1676
1945
  }
1677
1946
  cacheSnapshot() {
1678
1947
  if (!this.snapshotCached) {
1679
- this.view.cacheSnapshot().then(snapshot => snapshot && this.visitCachedSnapshot(snapshot));
1948
+ this.view.cacheSnapshot().then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
1680
1949
  this.snapshotCached = true;
1681
1950
  }
1682
1951
  }
1683
1952
  async render(callback) {
1684
1953
  this.cancelRender();
1685
- await new Promise(resolve => {
1954
+ await new Promise((resolve) => {
1686
1955
  this.frame = requestAnimationFrame(() => resolve());
1687
1956
  });
1688
1957
  await callback();
1689
1958
  delete this.frame;
1690
- this.performScroll();
1691
1959
  }
1692
1960
  cancelRender() {
1693
1961
  if (this.frame) {
@@ -1702,19 +1970,19 @@ Copyright © 2021 Basecamp, LLC
1702
1970
 
1703
1971
  class BrowserAdapter {
1704
1972
  constructor(session) {
1705
- this.progressBar = new ProgressBar;
1973
+ this.progressBar = new ProgressBar();
1706
1974
  this.showProgressBar = () => {
1707
1975
  this.progressBar.show();
1708
1976
  };
1709
1977
  this.session = session;
1710
1978
  }
1711
1979
  visitProposedToLocation(location, options) {
1712
- this.navigator.startVisit(location, uuid(), options);
1980
+ return this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
1713
1981
  }
1714
1982
  visitStarted(visit) {
1983
+ this.location = visit.location;
1715
1984
  visit.loadCachedSnapshot();
1716
1985
  visit.issueRequest();
1717
- visit.changeHistory();
1718
1986
  visit.goToSamePageAnchor();
1719
1987
  }
1720
1988
  visitRequestStarted(visit) {
@@ -1734,29 +2002,31 @@ Copyright © 2021 Basecamp, LLC
1734
2002
  case SystemStatusCode.networkFailure:
1735
2003
  case SystemStatusCode.timeoutFailure:
1736
2004
  case SystemStatusCode.contentTypeMismatch:
1737
- return this.reload();
2005
+ return this.reload({
2006
+ reason: "request_failed",
2007
+ context: {
2008
+ statusCode,
2009
+ },
2010
+ });
1738
2011
  default:
1739
2012
  return visit.loadResponse();
1740
2013
  }
1741
2014
  }
1742
- visitRequestFinished(visit) {
2015
+ visitRequestFinished(_visit) {
1743
2016
  this.progressBar.setValue(1);
1744
2017
  this.hideVisitProgressBar();
1745
2018
  }
1746
- visitCompleted(visit) {
1747
- }
1748
- pageInvalidated() {
1749
- this.reload();
1750
- }
1751
- visitFailed(visit) {
2019
+ visitCompleted(_visit) { }
2020
+ pageInvalidated(reason) {
2021
+ this.reload(reason);
1752
2022
  }
1753
- visitRendered(visit) {
1754
- }
1755
- formSubmissionStarted(formSubmission) {
2023
+ visitFailed(_visit) { }
2024
+ visitRendered(_visit) { }
2025
+ formSubmissionStarted(_formSubmission) {
1756
2026
  this.progressBar.setValue(0);
1757
2027
  this.showFormProgressBarAfterDelay();
1758
2028
  }
1759
- formSubmissionFinished(formSubmission) {
2029
+ formSubmissionFinished(_formSubmission) {
1760
2030
  this.progressBar.setValue(1);
1761
2031
  this.hideFormProgressBar();
1762
2032
  }
@@ -1782,8 +2052,11 @@ Copyright © 2021 Basecamp, LLC
1782
2052
  delete this.formProgressBarTimeout;
1783
2053
  }
1784
2054
  }
1785
- reload() {
1786
- window.location.reload();
2055
+ reload(reason) {
2056
+ dispatch("turbo:reload", { detail: reason });
2057
+ if (!this.location)
2058
+ return;
2059
+ window.location.href = this.location.toString();
1787
2060
  }
1788
2061
  get navigator() {
1789
2062
  return this.session.navigator;
@@ -1793,94 +2066,60 @@ Copyright © 2021 Basecamp, LLC
1793
2066
  class CacheObserver {
1794
2067
  constructor() {
1795
2068
  this.started = false;
1796
- }
1797
- start() {
1798
- if (!this.started) {
1799
- this.started = true;
1800
- addEventListener("turbo:before-cache", this.removeStaleElements, false);
1801
- }
1802
- }
1803
- stop() {
1804
- if (this.started) {
1805
- this.started = false;
1806
- removeEventListener("turbo:before-cache", this.removeStaleElements, false);
1807
- }
1808
- }
1809
- removeStaleElements() {
1810
- const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
1811
- for (const element of staleElements) {
1812
- element.remove();
1813
- }
1814
- }
1815
- }
1816
-
1817
- class FormSubmitObserver {
1818
- constructor(delegate) {
1819
- this.started = false;
1820
- this.submitCaptured = () => {
1821
- removeEventListener("submit", this.submitBubbled, false);
1822
- addEventListener("submit", this.submitBubbled, false);
1823
- };
1824
- this.submitBubbled = ((event) => {
1825
- if (!event.defaultPrevented) {
1826
- const form = event.target instanceof HTMLFormElement ? event.target : undefined;
1827
- const submitter = event.submitter || undefined;
1828
- if (form) {
1829
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
1830
- if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
1831
- event.preventDefault();
1832
- this.delegate.formSubmitted(form, submitter);
1833
- }
1834
- }
2069
+ this.removeStaleElements = ((_event) => {
2070
+ const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
2071
+ for (const element of staleElements) {
2072
+ element.remove();
1835
2073
  }
1836
2074
  });
1837
- this.delegate = delegate;
1838
2075
  }
1839
2076
  start() {
1840
2077
  if (!this.started) {
1841
- addEventListener("submit", this.submitCaptured, true);
1842
2078
  this.started = true;
2079
+ addEventListener("turbo:before-cache", this.removeStaleElements, false);
1843
2080
  }
1844
2081
  }
1845
2082
  stop() {
1846
2083
  if (this.started) {
1847
- removeEventListener("submit", this.submitCaptured, true);
1848
2084
  this.started = false;
2085
+ removeEventListener("turbo:before-cache", this.removeStaleElements, false);
1849
2086
  }
1850
2087
  }
1851
2088
  }
1852
2089
 
1853
2090
  class FrameRedirector {
1854
- constructor(element) {
2091
+ constructor(session, element) {
2092
+ this.session = session;
1855
2093
  this.element = element;
1856
- this.linkInterceptor = new LinkInterceptor(this, element);
1857
- this.formInterceptor = new FormInterceptor(this, element);
2094
+ this.linkClickObserver = new LinkClickObserver(this, element);
2095
+ this.formSubmitObserver = new FormSubmitObserver(this, element);
1858
2096
  }
1859
2097
  start() {
1860
- this.linkInterceptor.start();
1861
- this.formInterceptor.start();
2098
+ this.linkClickObserver.start();
2099
+ this.formSubmitObserver.start();
1862
2100
  }
1863
2101
  stop() {
1864
- this.linkInterceptor.stop();
1865
- this.formInterceptor.stop();
2102
+ this.linkClickObserver.stop();
2103
+ this.formSubmitObserver.stop();
1866
2104
  }
1867
- shouldInterceptLinkClick(element, url) {
2105
+ willFollowLinkToLocation(element) {
1868
2106
  return this.shouldRedirect(element);
1869
2107
  }
1870
- linkClickIntercepted(element, url) {
2108
+ followedLinkToLocation(element, url) {
1871
2109
  const frame = this.findFrameElement(element);
1872
2110
  if (frame) {
1873
- frame.delegate.linkClickIntercepted(element, url);
2111
+ frame.delegate.followedLinkToLocation(element, url);
1874
2112
  }
1875
2113
  }
1876
- shouldInterceptFormSubmission(element, submitter) {
1877
- return this.shouldSubmit(element, submitter);
2114
+ willSubmitForm(element, submitter) {
2115
+ return (element.closest("turbo-frame") == null &&
2116
+ this.shouldSubmit(element, submitter) &&
2117
+ this.shouldRedirect(element, submitter));
1878
2118
  }
1879
- formSubmissionIntercepted(element, submitter) {
2119
+ formSubmitted(element, submitter) {
1880
2120
  const frame = this.findFrameElement(element, submitter);
1881
2121
  if (frame) {
1882
- frame.removeAttribute("reloadable");
1883
- frame.delegate.formSubmissionIntercepted(element, submitter);
2122
+ frame.delegate.formSubmitted(element, submitter);
1884
2123
  }
1885
2124
  }
1886
2125
  shouldSubmit(form, submitter) {
@@ -1891,8 +2130,16 @@ Copyright © 2021 Basecamp, LLC
1891
2130
  return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
1892
2131
  }
1893
2132
  shouldRedirect(element, submitter) {
1894
- const frame = this.findFrameElement(element, submitter);
1895
- return frame ? frame != element.closest("turbo-frame") : false;
2133
+ const isNavigatable = element instanceof HTMLFormElement
2134
+ ? this.session.submissionIsNavigatable(element, submitter)
2135
+ : this.session.elementIsNavigatable(element);
2136
+ if (isNavigatable) {
2137
+ const frame = this.findFrameElement(element, submitter);
2138
+ return frame ? frame != element.closest("turbo-frame") : false;
2139
+ }
2140
+ else {
2141
+ return false;
2142
+ }
1896
2143
  }
1897
2144
  findFrameElement(element, submitter) {
1898
2145
  const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
@@ -1922,7 +2169,7 @@ Copyright © 2021 Basecamp, LLC
1922
2169
  }
1923
2170
  }
1924
2171
  };
1925
- this.onPageLoad = async (event) => {
2172
+ this.onPageLoad = async (_event) => {
1926
2173
  await nextMicrotask();
1927
2174
  this.pageLoaded = true;
1928
2175
  };
@@ -1984,81 +2231,30 @@ Copyright © 2021 Basecamp, LLC
1984
2231
  }
1985
2232
  }
1986
2233
 
1987
- class LinkClickObserver {
1988
- constructor(delegate) {
1989
- this.started = false;
1990
- this.clickCaptured = () => {
1991
- removeEventListener("click", this.clickBubbled, false);
1992
- addEventListener("click", this.clickBubbled, false);
1993
- };
1994
- this.clickBubbled = (event) => {
1995
- if (this.clickEventIsSignificant(event)) {
1996
- const target = (event.composedPath && event.composedPath()[0]) || event.target;
1997
- const link = this.findLinkFromClickTarget(target);
1998
- if (link) {
1999
- const location = this.getLocationForLink(link);
2000
- if (this.delegate.willFollowLinkToLocation(link, location)) {
2001
- event.preventDefault();
2002
- this.delegate.followedLinkToLocation(link, location);
2003
- }
2004
- }
2005
- }
2006
- };
2007
- this.delegate = delegate;
2008
- }
2009
- start() {
2010
- if (!this.started) {
2011
- addEventListener("click", this.clickCaptured, true);
2012
- this.started = true;
2013
- }
2014
- }
2015
- stop() {
2016
- if (this.started) {
2017
- removeEventListener("click", this.clickCaptured, true);
2018
- this.started = false;
2019
- }
2020
- }
2021
- clickEventIsSignificant(event) {
2022
- return !((event.target && event.target.isContentEditable)
2023
- || event.defaultPrevented
2024
- || event.which > 1
2025
- || event.altKey
2026
- || event.ctrlKey
2027
- || event.metaKey
2028
- || event.shiftKey);
2029
- }
2030
- findLinkFromClickTarget(target) {
2031
- if (target instanceof Element) {
2032
- return target.closest("a[href]:not([target^=_]):not([download])");
2033
- }
2034
- }
2035
- getLocationForLink(link) {
2036
- return expandURL(link.getAttribute("href") || "");
2037
- }
2038
- }
2039
-
2040
- function isAction(action) {
2041
- return action == "advance" || action == "replace" || action == "restore";
2042
- }
2043
-
2044
- class Navigator {
2234
+ class Navigator {
2045
2235
  constructor(delegate) {
2046
2236
  this.delegate = delegate;
2047
2237
  }
2048
2238
  proposeVisit(location, options = {}) {
2049
- if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
2239
+ if (this.delegate.allowsVisitingLocation(location, options)) {
2050
2240
  if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
2051
- this.delegate.visitProposedToLocation(location, options);
2241
+ return this.delegate.visitProposedToLocation(location, options);
2052
2242
  }
2053
2243
  else {
2054
2244
  window.location.href = location.toString();
2245
+ return Promise.resolve();
2055
2246
  }
2056
2247
  }
2248
+ else {
2249
+ return Promise.reject();
2250
+ }
2057
2251
  }
2058
2252
  startVisit(locatable, restorationIdentifier, options = {}) {
2253
+ this.lastVisit = this.currentVisit;
2059
2254
  this.stop();
2060
2255
  this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
2061
2256
  this.currentVisit.start();
2257
+ return this.currentVisit.promise;
2062
2258
  }
2063
2259
  submitForm(form, submitter) {
2064
2260
  this.stop();
@@ -2085,7 +2281,7 @@ Copyright © 2021 Basecamp, LLC
2085
2281
  return this.delegate.history;
2086
2282
  }
2087
2283
  formSubmissionStarted(formSubmission) {
2088
- if (typeof this.adapter.formSubmissionStarted === 'function') {
2284
+ if (typeof this.adapter.formSubmissionStarted === "function") {
2089
2285
  this.adapter.formSubmissionStarted(formSubmission);
2090
2286
  }
2091
2287
  }
@@ -2093,12 +2289,17 @@ Copyright © 2021 Basecamp, LLC
2093
2289
  if (formSubmission == this.formSubmission) {
2094
2290
  const responseHTML = await fetchResponse.responseHTML;
2095
2291
  if (responseHTML) {
2096
- if (formSubmission.method != FetchMethod.get) {
2292
+ const shouldCacheSnapshot = formSubmission.method == FetchMethod.get;
2293
+ if (!shouldCacheSnapshot) {
2097
2294
  this.view.clearSnapshotCache();
2098
2295
  }
2099
2296
  const { statusCode, redirected } = fetchResponse;
2100
2297
  const action = this.getActionForFormSubmission(formSubmission);
2101
- const visitOptions = { action, response: { statusCode, responseHTML, redirected } };
2298
+ const visitOptions = {
2299
+ action,
2300
+ shouldCacheSnapshot,
2301
+ response: { statusCode, responseHTML, redirected },
2302
+ };
2102
2303
  this.proposeVisit(fetchResponse.location, visitOptions);
2103
2304
  }
2104
2305
  }
@@ -2108,10 +2309,10 @@ Copyright © 2021 Basecamp, LLC
2108
2309
  if (responseHTML) {
2109
2310
  const snapshot = PageSnapshot.fromHTMLString(responseHTML);
2110
2311
  if (fetchResponse.serverError) {
2111
- await this.view.renderError(snapshot);
2312
+ await this.view.renderError(snapshot, this.currentVisit);
2112
2313
  }
2113
2314
  else {
2114
- await this.view.renderPage(snapshot);
2315
+ await this.view.renderPage(snapshot, false, true, this.currentVisit);
2115
2316
  }
2116
2317
  this.view.scrollToTop();
2117
2318
  this.view.clearSnapshotCache();
@@ -2121,7 +2322,7 @@ Copyright © 2021 Basecamp, LLC
2121
2322
  console.error(error);
2122
2323
  }
2123
2324
  formSubmissionFinished(formSubmission) {
2124
- if (typeof this.adapter.formSubmissionFinished === 'function') {
2325
+ if (typeof this.adapter.formSubmissionFinished === "function") {
2125
2326
  this.adapter.formSubmissionFinished(formSubmission);
2126
2327
  }
2127
2328
  }
@@ -2132,12 +2333,14 @@ Copyright © 2021 Basecamp, LLC
2132
2333
  this.delegate.visitCompleted(visit);
2133
2334
  }
2134
2335
  locationWithActionIsSamePage(location, action) {
2336
+ var _a;
2135
2337
  const anchor = getAnchor(location);
2136
- const currentAnchor = getAnchor(this.view.lastRenderedLocation);
2137
- const isRestorationToTop = action === 'restore' && typeof anchor === 'undefined';
2138
- return action !== "replace" &&
2139
- getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
2140
- (isRestorationToTop || (anchor != null && anchor !== currentAnchor));
2338
+ const lastLocation = ((_a = this.lastVisit) === null || _a === void 0 ? void 0 : _a.location) || this.view.lastRenderedLocation;
2339
+ const currentAnchor = getAnchor(lastLocation);
2340
+ const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
2341
+ return (action !== "replace" &&
2342
+ getRequestURL(location) === getRequestURL(lastLocation) &&
2343
+ (isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
2141
2344
  }
2142
2345
  visitScrolledToSamePageLocation(oldURL, newURL) {
2143
2346
  this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
@@ -2241,9 +2444,33 @@ Copyright © 2021 Basecamp, LLC
2241
2444
  }
2242
2445
  }
2243
2446
 
2447
+ class StreamMessageRenderer {
2448
+ render({ fragment }) {
2449
+ Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
2450
+ }
2451
+ enteringBardo(currentPermanentElement, newPermanentElement) {
2452
+ newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
2453
+ }
2454
+ leavingBardo() { }
2455
+ }
2456
+ function getPermanentElementMapForFragment(fragment) {
2457
+ const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
2458
+ const permanentElementMap = {};
2459
+ for (const permanentElementInDocument of permanentElementsInDocument) {
2460
+ const { id } = permanentElementInDocument;
2461
+ for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
2462
+ const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
2463
+ if (elementInStream) {
2464
+ permanentElementMap[id] = [permanentElementInDocument, elementInStream];
2465
+ }
2466
+ }
2467
+ }
2468
+ return permanentElementMap;
2469
+ }
2470
+
2244
2471
  class StreamObserver {
2245
2472
  constructor(delegate) {
2246
- this.sources = new Set;
2473
+ this.sources = new Set();
2247
2474
  this.started = false;
2248
2475
  this.inspectFetchResponse = ((event) => {
2249
2476
  const response = fetchResponseFromEvent(event);
@@ -2293,7 +2520,7 @@ Copyright © 2021 Basecamp, LLC
2293
2520
  }
2294
2521
  }
2295
2522
  receiveMessageHTML(html) {
2296
- this.delegate.receivedMessageFromStream(new StreamMessage(html));
2523
+ this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
2297
2524
  }
2298
2525
  }
2299
2526
  function fetchResponseFromEvent(event) {
@@ -2310,20 +2537,24 @@ Copyright © 2021 Basecamp, LLC
2310
2537
  }
2311
2538
 
2312
2539
  class ErrorRenderer extends Renderer {
2540
+ static renderElement(currentElement, newElement) {
2541
+ const { documentElement, body } = document;
2542
+ documentElement.replaceChild(newElement, body);
2543
+ }
2313
2544
  async render() {
2314
2545
  this.replaceHeadAndBody();
2315
2546
  this.activateScriptElements();
2316
2547
  }
2317
2548
  replaceHeadAndBody() {
2318
- const { documentElement, head, body } = document;
2549
+ const { documentElement, head } = document;
2319
2550
  documentElement.replaceChild(this.newHead, head);
2320
- documentElement.replaceChild(this.newElement, body);
2551
+ this.renderElement(this.currentElement, this.newElement);
2321
2552
  }
2322
2553
  activateScriptElements() {
2323
2554
  for (const replaceableElement of this.scriptElements) {
2324
2555
  const parentNode = replaceableElement.parentNode;
2325
2556
  if (parentNode) {
2326
- const element = this.createScriptElement(replaceableElement);
2557
+ const element = activateScriptElement(replaceableElement);
2327
2558
  parentNode.replaceChild(element, replaceableElement);
2328
2559
  }
2329
2560
  }
@@ -2332,16 +2563,44 @@ Copyright © 2021 Basecamp, LLC
2332
2563
  return this.newSnapshot.headSnapshot.element;
2333
2564
  }
2334
2565
  get scriptElements() {
2335
- return [...document.documentElement.querySelectorAll("script")];
2566
+ return document.documentElement.querySelectorAll("script");
2336
2567
  }
2337
2568
  }
2338
2569
 
2339
2570
  class PageRenderer extends Renderer {
2571
+ static async renderElement(currentElement, newElement) {
2572
+ await nextEventLoopTick();
2573
+ if (document.body && newElement instanceof HTMLBodyElement) {
2574
+ const currentBody = PageRenderer.getBodyElement(currentElement);
2575
+ const newBody = PageRenderer.getBodyElement(newElement);
2576
+ currentBody.replaceWith(newBody);
2577
+ }
2578
+ else {
2579
+ document.documentElement.appendChild(newElement);
2580
+ }
2581
+ }
2340
2582
  get shouldRender() {
2341
- return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
2583
+ return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical && this.bodyElementMatches;
2342
2584
  }
2343
- prepareToRender() {
2344
- this.mergeHead();
2585
+ get reloadReason() {
2586
+ if (!this.newSnapshot.isVisitable) {
2587
+ return {
2588
+ reason: "turbo_visit_control_is_reload",
2589
+ };
2590
+ }
2591
+ if (!this.trackedElementsAreIdentical) {
2592
+ return {
2593
+ reason: "tracked_element_mismatch",
2594
+ };
2595
+ }
2596
+ if (!this.bodyElementMatches) {
2597
+ return {
2598
+ reason: "body_element_mismatch",
2599
+ };
2600
+ }
2601
+ }
2602
+ async prepareToRender() {
2603
+ await this.mergeHead();
2345
2604
  }
2346
2605
  async render() {
2347
2606
  if (this.willRender) {
@@ -2363,11 +2622,12 @@ Copyright © 2021 Basecamp, LLC
2363
2622
  get newElement() {
2364
2623
  return this.newSnapshot.element;
2365
2624
  }
2366
- mergeHead() {
2367
- this.copyNewHeadStylesheetElements();
2625
+ async mergeHead() {
2626
+ const newStylesheetElements = this.copyNewHeadStylesheetElements();
2368
2627
  this.copyNewHeadScriptElements();
2369
2628
  this.removeCurrentHeadProvisionalElements();
2370
2629
  this.copyNewHeadProvisionalElements();
2630
+ await newStylesheetElements;
2371
2631
  }
2372
2632
  replaceBody() {
2373
2633
  this.preservingPermanentElements(() => {
@@ -2378,14 +2638,27 @@ Copyright © 2021 Basecamp, LLC
2378
2638
  get trackedElementsAreIdentical() {
2379
2639
  return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
2380
2640
  }
2381
- copyNewHeadStylesheetElements() {
2641
+ get bodyElementMatches() {
2642
+ return PageRenderer.getBodyElement(this.newElement) !== null;
2643
+ }
2644
+ static get bodySelector() {
2645
+ const bodyId = getBodyElementId();
2646
+ return bodyId ? `#${bodyId}` : "body";
2647
+ }
2648
+ static getBodyElement(element) {
2649
+ return element.querySelector(this.bodySelector) || element;
2650
+ }
2651
+ async copyNewHeadStylesheetElements() {
2652
+ const loadingElements = [];
2382
2653
  for (const element of this.newHeadStylesheetElements) {
2654
+ loadingElements.push(waitForLoad(element));
2383
2655
  document.head.appendChild(element);
2384
2656
  }
2657
+ await Promise.all(loadingElements);
2385
2658
  }
2386
2659
  copyNewHeadScriptElements() {
2387
2660
  for (const element of this.newHeadScriptElements) {
2388
- document.head.appendChild(this.createScriptElement(element));
2661
+ document.head.appendChild(activateScriptElement(element));
2389
2662
  }
2390
2663
  }
2391
2664
  removeCurrentHeadProvisionalElements() {
@@ -2404,17 +2677,12 @@ Copyright © 2021 Basecamp, LLC
2404
2677
  }
2405
2678
  activateNewBodyScriptElements() {
2406
2679
  for (const inertScriptElement of this.newBodyScriptElements) {
2407
- const activatedScriptElement = this.createScriptElement(inertScriptElement);
2680
+ const activatedScriptElement = activateScriptElement(inertScriptElement);
2408
2681
  inertScriptElement.replaceWith(activatedScriptElement);
2409
2682
  }
2410
2683
  }
2411
2684
  assignNewBody() {
2412
- if (document.body && this.newElement instanceof HTMLBodyElement) {
2413
- document.body.replaceWith(this.newElement);
2414
- }
2415
- else {
2416
- document.documentElement.appendChild(this.newElement);
2417
- }
2685
+ this.renderElement(this.currentElement, this.newElement);
2418
2686
  }
2419
2687
  get newHeadStylesheetElements() {
2420
2688
  return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
@@ -2483,13 +2751,21 @@ Copyright © 2021 Basecamp, LLC
2483
2751
  super(...arguments);
2484
2752
  this.snapshotCache = new SnapshotCache(10);
2485
2753
  this.lastRenderedLocation = new URL(location.href);
2754
+ this.forceReloaded = false;
2486
2755
  }
2487
- renderPage(snapshot, isPreview = false, willRender = true) {
2488
- const renderer = new PageRenderer(this.snapshot, snapshot, isPreview, willRender);
2756
+ renderPage(snapshot, isPreview = false, willRender = true, visit) {
2757
+ const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
2758
+ if (!renderer.shouldRender) {
2759
+ this.forceReloaded = true;
2760
+ }
2761
+ else {
2762
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
2763
+ }
2489
2764
  return this.render(renderer);
2490
2765
  }
2491
- renderError(snapshot) {
2492
- const renderer = new ErrorRenderer(this.snapshot, snapshot, false);
2766
+ renderError(snapshot, visit) {
2767
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
2768
+ const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
2493
2769
  return this.render(renderer);
2494
2770
  }
2495
2771
  clearSnapshotCache() {
@@ -2516,34 +2792,79 @@ Copyright © 2021 Basecamp, LLC
2516
2792
  }
2517
2793
  }
2518
2794
 
2795
+ class Preloader {
2796
+ constructor(delegate) {
2797
+ this.selector = "a[data-turbo-preload]";
2798
+ this.delegate = delegate;
2799
+ }
2800
+ get snapshotCache() {
2801
+ return this.delegate.navigator.view.snapshotCache;
2802
+ }
2803
+ start() {
2804
+ if (document.readyState === "loading") {
2805
+ return document.addEventListener("DOMContentLoaded", () => {
2806
+ this.preloadOnLoadLinksForView(document.body);
2807
+ });
2808
+ }
2809
+ else {
2810
+ this.preloadOnLoadLinksForView(document.body);
2811
+ }
2812
+ }
2813
+ preloadOnLoadLinksForView(element) {
2814
+ for (const link of element.querySelectorAll(this.selector)) {
2815
+ this.preloadURL(link);
2816
+ }
2817
+ }
2818
+ async preloadURL(link) {
2819
+ const location = new URL(link.href);
2820
+ if (this.snapshotCache.has(location)) {
2821
+ return;
2822
+ }
2823
+ try {
2824
+ const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
2825
+ const responseText = await response.text();
2826
+ const snapshot = PageSnapshot.fromHTMLString(responseText);
2827
+ this.snapshotCache.put(location, snapshot);
2828
+ }
2829
+ catch (_) {
2830
+ }
2831
+ }
2832
+ }
2833
+
2519
2834
  class Session {
2520
2835
  constructor() {
2521
2836
  this.navigator = new Navigator(this);
2522
2837
  this.history = new History(this);
2838
+ this.preloader = new Preloader(this);
2523
2839
  this.view = new PageView(this, document.documentElement);
2524
2840
  this.adapter = new BrowserAdapter(this);
2525
2841
  this.pageObserver = new PageObserver(this);
2526
2842
  this.cacheObserver = new CacheObserver();
2527
- this.linkClickObserver = new LinkClickObserver(this);
2528
- this.formSubmitObserver = new FormSubmitObserver(this);
2843
+ this.linkClickObserver = new LinkClickObserver(this, window);
2844
+ this.formSubmitObserver = new FormSubmitObserver(this, document);
2529
2845
  this.scrollObserver = new ScrollObserver(this);
2530
2846
  this.streamObserver = new StreamObserver(this);
2531
- this.frameRedirector = new FrameRedirector(document.documentElement);
2847
+ this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
2848
+ this.frameRedirector = new FrameRedirector(this, document.documentElement);
2849
+ this.streamMessageRenderer = new StreamMessageRenderer();
2532
2850
  this.drive = true;
2533
2851
  this.enabled = true;
2534
2852
  this.progressBarDelay = 500;
2535
2853
  this.started = false;
2854
+ this.formMode = "on";
2536
2855
  }
2537
2856
  start() {
2538
2857
  if (!this.started) {
2539
2858
  this.pageObserver.start();
2540
2859
  this.cacheObserver.start();
2860
+ this.formLinkClickObserver.start();
2541
2861
  this.linkClickObserver.start();
2542
2862
  this.formSubmitObserver.start();
2543
2863
  this.scrollObserver.start();
2544
2864
  this.streamObserver.start();
2545
2865
  this.frameRedirector.start();
2546
2866
  this.history.start();
2867
+ this.preloader.start();
2547
2868
  this.started = true;
2548
2869
  this.enabled = true;
2549
2870
  }
@@ -2555,6 +2876,7 @@ Copyright © 2021 Basecamp, LLC
2555
2876
  if (this.started) {
2556
2877
  this.pageObserver.stop();
2557
2878
  this.cacheObserver.stop();
2879
+ this.formLinkClickObserver.stop();
2558
2880
  this.linkClickObserver.stop();
2559
2881
  this.formSubmitObserver.stop();
2560
2882
  this.scrollObserver.stop();
@@ -2568,7 +2890,14 @@ Copyright © 2021 Basecamp, LLC
2568
2890
  this.adapter = adapter;
2569
2891
  }
2570
2892
  visit(location, options = {}) {
2571
- this.navigator.proposeVisit(expandURL(location), options);
2893
+ const frameElement = options.frame ? document.getElementById(options.frame) : null;
2894
+ if (frameElement instanceof FrameElement) {
2895
+ frameElement.src = location.toString();
2896
+ return frameElement.loaded;
2897
+ }
2898
+ else {
2899
+ return this.navigator.proposeVisit(expandURL(location), options);
2900
+ }
2572
2901
  }
2573
2902
  connectStreamSource(source) {
2574
2903
  this.streamObserver.connectStreamSource(source);
@@ -2577,7 +2906,7 @@ Copyright © 2021 Basecamp, LLC
2577
2906
  this.streamObserver.disconnectStreamSource(source);
2578
2907
  }
2579
2908
  renderStreamMessage(message) {
2580
- document.documentElement.appendChild(StreamMessage.wrap(message).fragment);
2909
+ this.streamMessageRenderer.render(StreamMessage.wrap(message));
2581
2910
  }
2582
2911
  clearCache() {
2583
2912
  this.view.clearSnapshotCache();
@@ -2585,6 +2914,9 @@ Copyright © 2021 Basecamp, LLC
2585
2914
  setProgressBarDelay(delay) {
2586
2915
  this.progressBarDelay = delay;
2587
2916
  }
2917
+ setFormMode(mode) {
2918
+ this.formMode = mode;
2919
+ }
2588
2920
  get location() {
2589
2921
  return this.history.location;
2590
2922
  }
@@ -2593,63 +2925,53 @@ Copyright © 2021 Basecamp, LLC
2593
2925
  }
2594
2926
  historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
2595
2927
  if (this.enabled) {
2596
- this.navigator.startVisit(location, restorationIdentifier, { action: "restore", historyChanged: true });
2928
+ this.navigator.startVisit(location, restorationIdentifier, {
2929
+ action: "restore",
2930
+ historyChanged: true,
2931
+ });
2597
2932
  }
2598
2933
  else {
2599
- this.adapter.pageInvalidated();
2934
+ this.adapter.pageInvalidated({
2935
+ reason: "turbo_disabled",
2936
+ });
2600
2937
  }
2601
2938
  }
2602
2939
  scrollPositionChanged(position) {
2603
2940
  this.history.updateRestorationData({ scrollPosition: position });
2604
2941
  }
2605
- willFollowLinkToLocation(link, location) {
2606
- return this.elementDriveEnabled(link)
2607
- && locationIsVisitable(location, this.snapshot.rootLocation)
2608
- && this.applicationAllowsFollowingLinkToLocation(link, location);
2942
+ willSubmitFormLinkToLocation(link, location) {
2943
+ return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
2944
+ }
2945
+ submittedFormLinkToLocation() { }
2946
+ willFollowLinkToLocation(link, location, event) {
2947
+ return (this.elementIsNavigatable(link) &&
2948
+ locationIsVisitable(location, this.snapshot.rootLocation) &&
2949
+ this.applicationAllowsFollowingLinkToLocation(link, location, event));
2609
2950
  }
2610
2951
  followedLinkToLocation(link, location) {
2611
2952
  const action = this.getActionForLink(link);
2612
- this.convertLinkWithMethodClickToFormSubmission(link) || this.visit(location.href, { action });
2613
- }
2614
- convertLinkWithMethodClickToFormSubmission(link) {
2615
- const linkMethod = link.getAttribute("data-turbo-method");
2616
- if (linkMethod) {
2617
- const form = document.createElement("form");
2618
- form.method = linkMethod;
2619
- form.action = link.getAttribute("href") || "undefined";
2620
- form.hidden = true;
2621
- if (link.hasAttribute("data-turbo-confirm")) {
2622
- form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
2623
- }
2624
- const frame = this.getTargetFrameForLink(link);
2625
- if (frame) {
2626
- form.setAttribute("data-turbo-frame", frame);
2627
- form.addEventListener("turbo:submit-start", () => form.remove());
2628
- }
2629
- else {
2630
- form.addEventListener("submit", () => form.remove());
2631
- }
2632
- document.body.appendChild(form);
2633
- return dispatch("submit", { cancelable: true, target: form });
2634
- }
2635
- else {
2636
- return false;
2637
- }
2953
+ const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
2954
+ this.visit(location.href, { action, acceptsStreamResponse, initiator: link });
2638
2955
  }
2639
- allowsVisitingLocationWithAction(location, action) {
2640
- return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
2956
+ allowsVisitingLocation(location, options = {}) {
2957
+ return (this.locationWithActionIsSamePage(location, options.action) ||
2958
+ this.applicationAllowsVisitingLocation(location, options));
2641
2959
  }
2642
2960
  visitProposedToLocation(location, options) {
2643
2961
  extendURLWithDeprecatedProperties(location);
2644
- this.adapter.visitProposedToLocation(location, options);
2962
+ return this.adapter.visitProposedToLocation(location, options);
2645
2963
  }
2646
2964
  visitStarted(visit) {
2965
+ if (!visit.acceptsStreamResponse) {
2966
+ markAsBusy(document.documentElement);
2967
+ }
2647
2968
  extendURLWithDeprecatedProperties(visit.location);
2648
2969
  if (!visit.silent) {
2649
- this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
2970
+ this.notifyApplicationAfterVisitingLocation(visit.location, visit.action, visit.initiator);
2650
2971
  }
2651
2972
  }
2652
2973
  visitCompleted(visit) {
2974
+ clearBusyState(document.documentElement);
2653
2975
  this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
2654
2976
  }
2655
2977
  locationWithActionIsSamePage(location, action) {
@@ -2660,9 +2982,8 @@ Copyright © 2021 Basecamp, LLC
2660
2982
  }
2661
2983
  willSubmitForm(form, submitter) {
2662
2984
  const action = getAction(form, submitter);
2663
- return this.elementDriveEnabled(form)
2664
- && (!submitter || this.elementDriveEnabled(submitter))
2665
- && locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
2985
+ return (this.submissionIsNavigatable(form, submitter) &&
2986
+ locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
2666
2987
  }
2667
2988
  formSubmitted(form, submitter) {
2668
2989
  this.navigator.submitForm(form, submitter);
@@ -2686,16 +3007,23 @@ Copyright © 2021 Basecamp, LLC
2686
3007
  this.notifyApplicationBeforeCachingSnapshot();
2687
3008
  }
2688
3009
  }
2689
- allowsImmediateRender({ element }, resume) {
2690
- const event = this.notifyApplicationBeforeRender(element, resume);
2691
- return !event.defaultPrevented;
3010
+ allowsImmediateRender({ element }, options) {
3011
+ const event = this.notifyApplicationBeforeRender(element, options);
3012
+ const { defaultPrevented, detail: { render }, } = event;
3013
+ if (this.view.renderer && render) {
3014
+ this.view.renderer.renderElement = render;
3015
+ }
3016
+ return !defaultPrevented;
2692
3017
  }
2693
- viewRenderedSnapshot(snapshot, isPreview) {
3018
+ viewRenderedSnapshot(_snapshot, _isPreview) {
2694
3019
  this.view.lastRenderedLocation = this.history.location;
2695
3020
  this.notifyApplicationAfterRender();
2696
3021
  }
2697
- viewInvalidated() {
2698
- this.adapter.pageInvalidated();
3022
+ preloadOnLoadLinksForView(element) {
3023
+ this.preloader.preloadOnLoadLinksForView(element);
3024
+ }
3025
+ viewInvalidated(reason) {
3026
+ this.adapter.pageInvalidated(reason);
2699
3027
  }
2700
3028
  frameLoaded(frame) {
2701
3029
  this.notifyApplicationAfterFrameLoad(frame);
@@ -2703,49 +3031,85 @@ Copyright © 2021 Basecamp, LLC
2703
3031
  frameRendered(fetchResponse, frame) {
2704
3032
  this.notifyApplicationAfterFrameRender(fetchResponse, frame);
2705
3033
  }
2706
- applicationAllowsFollowingLinkToLocation(link, location) {
2707
- const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
3034
+ applicationAllowsFollowingLinkToLocation(link, location, ev) {
3035
+ const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
2708
3036
  return !event.defaultPrevented;
2709
3037
  }
2710
- applicationAllowsVisitingLocation(location) {
2711
- const event = this.notifyApplicationBeforeVisitingLocation(location);
3038
+ applicationAllowsVisitingLocation(location, options = {}) {
3039
+ const event = this.notifyApplicationBeforeVisitingLocation(location, options.initiator);
2712
3040
  return !event.defaultPrevented;
2713
3041
  }
2714
- notifyApplicationAfterClickingLinkToLocation(link, location) {
2715
- return dispatch("turbo:click", { target: link, detail: { url: location.href }, cancelable: true });
3042
+ notifyApplicationAfterClickingLinkToLocation(link, location, event) {
3043
+ return dispatch("turbo:click", {
3044
+ target: link,
3045
+ detail: { url: location.href, originalEvent: event },
3046
+ cancelable: true,
3047
+ });
2716
3048
  }
2717
- notifyApplicationBeforeVisitingLocation(location) {
2718
- return dispatch("turbo:before-visit", { detail: { url: location.href }, cancelable: true });
3049
+ notifyApplicationBeforeVisitingLocation(location, element) {
3050
+ return dispatch("turbo:before-visit", {
3051
+ target: element,
3052
+ detail: { url: location.href },
3053
+ cancelable: true,
3054
+ });
2719
3055
  }
2720
- notifyApplicationAfterVisitingLocation(location, action) {
2721
- markAsBusy(document.documentElement);
2722
- return dispatch("turbo:visit", { detail: { url: location.href, action } });
3056
+ notifyApplicationAfterVisitingLocation(location, action, element) {
3057
+ return dispatch("turbo:visit", {
3058
+ target: element,
3059
+ detail: { url: location.href, action },
3060
+ });
2723
3061
  }
2724
3062
  notifyApplicationBeforeCachingSnapshot() {
2725
3063
  return dispatch("turbo:before-cache");
2726
3064
  }
2727
- notifyApplicationBeforeRender(newBody, resume) {
2728
- return dispatch("turbo:before-render", { detail: { newBody, resume }, cancelable: true });
3065
+ notifyApplicationBeforeRender(newBody, options) {
3066
+ return dispatch("turbo:before-render", {
3067
+ detail: Object.assign({ newBody }, options),
3068
+ cancelable: true,
3069
+ });
2729
3070
  }
2730
3071
  notifyApplicationAfterRender() {
2731
3072
  return dispatch("turbo:render");
2732
3073
  }
2733
3074
  notifyApplicationAfterPageLoad(timing = {}) {
2734
- clearBusyState(document.documentElement);
2735
- return dispatch("turbo:load", { detail: { url: this.location.href, timing } });
3075
+ return dispatch("turbo:load", {
3076
+ detail: { url: this.location.href, timing },
3077
+ });
2736
3078
  }
2737
3079
  notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
2738
- dispatchEvent(new HashChangeEvent("hashchange", { oldURL: oldURL.toString(), newURL: newURL.toString() }));
3080
+ dispatchEvent(new HashChangeEvent("hashchange", {
3081
+ oldURL: oldURL.toString(),
3082
+ newURL: newURL.toString(),
3083
+ }));
2739
3084
  }
2740
3085
  notifyApplicationAfterFrameLoad(frame) {
2741
3086
  return dispatch("turbo:frame-load", { target: frame });
2742
3087
  }
2743
3088
  notifyApplicationAfterFrameRender(fetchResponse, frame) {
2744
- return dispatch("turbo:frame-render", { detail: { fetchResponse }, target: frame, cancelable: true });
3089
+ return dispatch("turbo:frame-render", {
3090
+ detail: { fetchResponse },
3091
+ target: frame,
3092
+ cancelable: true,
3093
+ });
2745
3094
  }
2746
- elementDriveEnabled(element) {
2747
- const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
2748
- if (this.drive) {
3095
+ submissionIsNavigatable(form, submitter) {
3096
+ if (this.formMode == "off") {
3097
+ return false;
3098
+ }
3099
+ else {
3100
+ const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
3101
+ if (this.formMode == "optin") {
3102
+ return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
3103
+ }
3104
+ else {
3105
+ return submitterIsNavigatable && this.elementIsNavigatable(form);
3106
+ }
3107
+ }
3108
+ }
3109
+ elementIsNavigatable(element) {
3110
+ const container = element.closest("[data-turbo]");
3111
+ const withinFrame = element.closest("turbo-frame");
3112
+ if (this.drive || withinFrame) {
2749
3113
  if (container) {
2750
3114
  return container.getAttribute("data-turbo") != "false";
2751
3115
  }
@@ -2766,18 +3130,6 @@ Copyright © 2021 Basecamp, LLC
2766
3130
  const action = link.getAttribute("data-turbo-action");
2767
3131
  return isAction(action) ? action : "advance";
2768
3132
  }
2769
- getTargetFrameForLink(link) {
2770
- const frame = link.getAttribute("data-turbo-frame");
2771
- if (frame) {
2772
- return frame;
2773
- }
2774
- else {
2775
- const container = link.closest("turbo-frame");
2776
- if (container) {
2777
- return container.id;
2778
- }
2779
- }
2780
- }
2781
3133
  get snapshot() {
2782
3134
  return this.view.snapshot;
2783
3135
  }
@@ -2789,11 +3141,59 @@ Copyright © 2021 Basecamp, LLC
2789
3141
  absoluteURL: {
2790
3142
  get() {
2791
3143
  return this.toString();
2792
- }
3144
+ },
3145
+ },
3146
+ };
3147
+
3148
+ class Cache {
3149
+ constructor(session) {
3150
+ this.session = session;
3151
+ }
3152
+ clear() {
3153
+ this.session.clearCache();
3154
+ }
3155
+ resetCacheControl() {
3156
+ this.setCacheControl("");
2793
3157
  }
3158
+ exemptPageFromCache() {
3159
+ this.setCacheControl("no-cache");
3160
+ }
3161
+ exemptPageFromPreview() {
3162
+ this.setCacheControl("no-preview");
3163
+ }
3164
+ setCacheControl(value) {
3165
+ setMetaContent("turbo-cache-control", value);
3166
+ }
3167
+ }
3168
+
3169
+ const StreamActions = {
3170
+ after() {
3171
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
3172
+ },
3173
+ append() {
3174
+ this.removeDuplicateTargetChildren();
3175
+ this.targetElements.forEach((e) => e.append(this.templateContent));
3176
+ },
3177
+ before() {
3178
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
3179
+ },
3180
+ prepend() {
3181
+ this.removeDuplicateTargetChildren();
3182
+ this.targetElements.forEach((e) => e.prepend(this.templateContent));
3183
+ },
3184
+ remove() {
3185
+ this.targetElements.forEach((e) => e.remove());
3186
+ },
3187
+ replace() {
3188
+ this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
3189
+ },
3190
+ update() {
3191
+ this.targetElements.forEach((e) => e.replaceChildren(this.templateContent));
3192
+ },
2794
3193
  };
2795
3194
 
2796
- const session = new Session;
3195
+ const session = new Session();
3196
+ const cache = new Cache(session);
2797
3197
  const { navigator: navigator$1 } = session;
2798
3198
  function start() {
2799
3199
  session.start();
@@ -2802,7 +3202,7 @@ Copyright © 2021 Basecamp, LLC
2802
3202
  session.registerAdapter(adapter);
2803
3203
  }
2804
3204
  function visit(location, options) {
2805
- session.visit(location, options);
3205
+ return session.visit(location, options);
2806
3206
  }
2807
3207
  function connectStreamSource(source) {
2808
3208
  session.connectStreamSource(source);
@@ -2814,6 +3214,7 @@ Copyright © 2021 Basecamp, LLC
2814
3214
  session.renderStreamMessage(message);
2815
3215
  }
2816
3216
  function clearCache() {
3217
+ console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
2817
3218
  session.clearCache();
2818
3219
  }
2819
3220
  function setProgressBarDelay(delay) {
@@ -2822,13 +3223,18 @@ Copyright © 2021 Basecamp, LLC
2822
3223
  function setConfirmMethod(confirmMethod) {
2823
3224
  FormSubmission.confirmMethod = confirmMethod;
2824
3225
  }
3226
+ function setFormMode(mode) {
3227
+ session.setFormMode(mode);
3228
+ }
2825
3229
 
2826
3230
  var Turbo = /*#__PURE__*/Object.freeze({
2827
3231
  __proto__: null,
2828
3232
  navigator: navigator$1,
2829
3233
  session: session,
3234
+ cache: cache,
2830
3235
  PageRenderer: PageRenderer,
2831
3236
  PageSnapshot: PageSnapshot,
3237
+ FrameRenderer: FrameRenderer,
2832
3238
  start: start,
2833
3239
  registerAdapter: registerAdapter,
2834
3240
  visit: visit,
@@ -2837,55 +3243,80 @@ Copyright © 2021 Basecamp, LLC
2837
3243
  renderStreamMessage: renderStreamMessage,
2838
3244
  clearCache: clearCache,
2839
3245
  setProgressBarDelay: setProgressBarDelay,
2840
- setConfirmMethod: setConfirmMethod
3246
+ setConfirmMethod: setConfirmMethod,
3247
+ setFormMode: setFormMode,
3248
+ StreamActions: StreamActions
2841
3249
  });
2842
3250
 
2843
3251
  class FrameController {
2844
3252
  constructor(element) {
2845
- this.fetchResponseLoaded = (fetchResponse) => { };
3253
+ this.fetchResponseLoaded = (_fetchResponse) => { };
2846
3254
  this.currentFetchRequest = null;
2847
3255
  this.resolveVisitPromise = () => { };
2848
3256
  this.connected = false;
2849
3257
  this.hasBeenLoaded = false;
2850
- this.settingSourceURL = false;
3258
+ this.ignoredAttributes = new Set();
3259
+ this.action = null;
3260
+ this.visitCachedSnapshot = ({ element }) => {
3261
+ const frame = element.querySelector("#" + this.element.id);
3262
+ if (frame && this.previousFrameElement) {
3263
+ frame.replaceChildren(...this.previousFrameElement.children);
3264
+ }
3265
+ delete this.previousFrameElement;
3266
+ };
2851
3267
  this.element = element;
2852
3268
  this.view = new FrameView(this, this.element);
2853
3269
  this.appearanceObserver = new AppearanceObserver(this, this.element);
2854
- this.linkInterceptor = new LinkInterceptor(this, this.element);
2855
- this.formInterceptor = new FormInterceptor(this, this.element);
3270
+ this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
3271
+ this.linkClickObserver = new LinkClickObserver(this, this.element);
3272
+ this.restorationIdentifier = uuid();
3273
+ this.formSubmitObserver = new FormSubmitObserver(this, this.element);
2856
3274
  }
2857
3275
  connect() {
2858
3276
  if (!this.connected) {
2859
3277
  this.connected = true;
2860
- this.reloadable = false;
2861
- if (this.loadingStyle == FrameLoadingStyle.lazy) {
3278
+ if (this.loadingStyle == exports.FrameLoadingStyle.lazy) {
2862
3279
  this.appearanceObserver.start();
2863
3280
  }
2864
- this.linkInterceptor.start();
2865
- this.formInterceptor.start();
2866
- this.sourceURLChanged();
3281
+ else {
3282
+ this.loadSourceURL();
3283
+ }
3284
+ this.formLinkClickObserver.start();
3285
+ this.linkClickObserver.start();
3286
+ this.formSubmitObserver.start();
2867
3287
  }
2868
3288
  }
2869
3289
  disconnect() {
2870
3290
  if (this.connected) {
2871
3291
  this.connected = false;
2872
3292
  this.appearanceObserver.stop();
2873
- this.linkInterceptor.stop();
2874
- this.formInterceptor.stop();
3293
+ this.formLinkClickObserver.stop();
3294
+ this.linkClickObserver.stop();
3295
+ this.formSubmitObserver.stop();
2875
3296
  }
2876
3297
  }
2877
3298
  disabledChanged() {
2878
- if (this.loadingStyle == FrameLoadingStyle.eager) {
3299
+ if (this.loadingStyle == exports.FrameLoadingStyle.eager) {
2879
3300
  this.loadSourceURL();
2880
3301
  }
2881
3302
  }
2882
3303
  sourceURLChanged() {
2883
- if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
3304
+ if (this.isIgnoringChangesTo("src"))
3305
+ return;
3306
+ if (this.element.isConnected) {
3307
+ this.complete = false;
3308
+ }
3309
+ if (this.loadingStyle == exports.FrameLoadingStyle.eager || this.hasBeenLoaded) {
2884
3310
  this.loadSourceURL();
2885
3311
  }
2886
3312
  }
3313
+ completeChanged() {
3314
+ if (this.isIgnoringChangesTo("complete"))
3315
+ return;
3316
+ this.loadSourceURL();
3317
+ }
2887
3318
  loadingStyleChanged() {
2888
- if (this.loadingStyle == FrameLoadingStyle.lazy) {
3319
+ if (this.loadingStyle == exports.FrameLoadingStyle.lazy) {
2889
3320
  this.appearanceObserver.start();
2890
3321
  }
2891
3322
  else {
@@ -2894,21 +3325,11 @@ Copyright © 2021 Basecamp, LLC
2894
3325
  }
2895
3326
  }
2896
3327
  async loadSourceURL() {
2897
- if (!this.settingSourceURL && this.enabled && this.isActive && (this.reloadable || this.sourceURL != this.currentURL)) {
2898
- const previousURL = this.currentURL;
2899
- this.currentURL = this.sourceURL;
2900
- if (this.sourceURL) {
2901
- try {
2902
- this.element.loaded = this.visit(expandURL(this.sourceURL));
2903
- this.appearanceObserver.stop();
2904
- await this.element.loaded;
2905
- this.hasBeenLoaded = true;
2906
- }
2907
- catch (error) {
2908
- this.currentURL = previousURL;
2909
- throw error;
2910
- }
2911
- }
3328
+ if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
3329
+ this.element.loaded = this.visit(expandURL(this.sourceURL));
3330
+ this.appearanceObserver.stop();
3331
+ await this.element.loaded;
3332
+ this.hasBeenLoaded = true;
2912
3333
  }
2913
3334
  }
2914
3335
  async loadResponse(fetchResponse) {
@@ -2919,14 +3340,23 @@ Copyright © 2021 Basecamp, LLC
2919
3340
  const html = await fetchResponse.responseHTML;
2920
3341
  if (html) {
2921
3342
  const { body } = parseHTMLDocument(html);
2922
- const snapshot = new Snapshot(await this.extractForeignFrameElement(body));
2923
- const renderer = new FrameRenderer(this.view.snapshot, snapshot, false, false);
2924
- if (this.view.renderPromise)
2925
- await this.view.renderPromise;
2926
- await this.view.render(renderer);
2927
- session.frameRendered(fetchResponse, this.element);
2928
- session.frameLoaded(this.element);
2929
- this.fetchResponseLoaded(fetchResponse);
3343
+ const newFrameElement = await this.extractForeignFrameElement(body);
3344
+ if (newFrameElement) {
3345
+ const snapshot = new Snapshot(newFrameElement);
3346
+ const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
3347
+ if (this.view.renderPromise)
3348
+ await this.view.renderPromise;
3349
+ this.changeHistory();
3350
+ await this.view.render(renderer);
3351
+ this.complete = true;
3352
+ session.frameRendered(fetchResponse, this.element);
3353
+ session.frameLoaded(this.element);
3354
+ this.fetchResponseLoaded(fetchResponse);
3355
+ }
3356
+ else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
3357
+ console.warn(`A matching frame for #${this.element.id} was missing from the response, transforming into full-page Visit.`);
3358
+ this.visitResponse(fetchResponse.response);
3359
+ }
2930
3360
  }
2931
3361
  }
2932
3362
  catch (error) {
@@ -2937,56 +3367,66 @@ Copyright © 2021 Basecamp, LLC
2937
3367
  this.fetchResponseLoaded = () => { };
2938
3368
  }
2939
3369
  }
2940
- elementAppearedInViewport(element) {
3370
+ elementAppearedInViewport(_element) {
2941
3371
  this.loadSourceURL();
2942
3372
  }
2943
- shouldInterceptLinkClick(element, url) {
2944
- if (element.hasAttribute("data-turbo-method")) {
2945
- return false;
2946
- }
2947
- else {
2948
- return this.shouldInterceptNavigation(element);
2949
- }
3373
+ willSubmitFormLinkToLocation(link) {
3374
+ return link.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(link);
3375
+ }
3376
+ submittedFormLinkToLocation(link, _location, form) {
3377
+ const frame = this.findFrameElement(link);
3378
+ if (frame)
3379
+ form.setAttribute("data-turbo-frame", frame.id);
2950
3380
  }
2951
- linkClickIntercepted(element, url) {
2952
- this.reloadable = true;
2953
- this.navigateFrame(element, url);
3381
+ willFollowLinkToLocation(element) {
3382
+ return this.shouldInterceptNavigation(element);
2954
3383
  }
2955
- shouldInterceptFormSubmission(element, submitter) {
2956
- return this.shouldInterceptNavigation(element, submitter);
3384
+ followedLinkToLocation(element, location) {
3385
+ this.navigateFrame(element, location.href);
2957
3386
  }
2958
- formSubmissionIntercepted(element, submitter) {
3387
+ willSubmitForm(element, submitter) {
3388
+ return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
3389
+ }
3390
+ formSubmitted(element, submitter) {
2959
3391
  if (this.formSubmission) {
2960
3392
  this.formSubmission.stop();
2961
3393
  }
2962
- this.reloadable = false;
2963
3394
  this.formSubmission = new FormSubmission(this, element, submitter);
2964
3395
  const { fetchRequest } = this.formSubmission;
2965
3396
  this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
2966
3397
  this.formSubmission.start();
2967
3398
  }
2968
3399
  prepareHeadersForRequest(headers, request) {
3400
+ var _a;
2969
3401
  headers["Turbo-Frame"] = this.id;
3402
+ if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
3403
+ request.acceptResponseType(StreamMessage.contentType);
3404
+ }
2970
3405
  }
2971
- requestStarted(request) {
3406
+ requestStarted(_request) {
2972
3407
  markAsBusy(this.element);
2973
3408
  }
2974
- requestPreventedHandlingResponse(request, response) {
3409
+ requestPreventedHandlingResponse(_request, _response) {
2975
3410
  this.resolveVisitPromise();
2976
3411
  }
2977
3412
  async requestSucceededWithResponse(request, response) {
2978
3413
  await this.loadResponse(response);
2979
3414
  this.resolveVisitPromise();
2980
3415
  }
2981
- requestFailedWithResponse(request, response) {
3416
+ async requestFailedWithResponse(request, response) {
2982
3417
  console.error(response);
3418
+ await this.loadResponse(response);
2983
3419
  this.resolveVisitPromise();
2984
3420
  }
2985
3421
  requestErrored(request, error) {
2986
3422
  console.error(error);
3423
+ dispatch("turbo:fetch-request-error", {
3424
+ target: this.element,
3425
+ detail: { request, error },
3426
+ });
2987
3427
  this.resolveVisitPromise();
2988
3428
  }
2989
- requestFinished(request) {
3429
+ requestFinished(_request) {
2990
3430
  clearBusyState(this.element);
2991
3431
  }
2992
3432
  formSubmissionStarted({ formElement }) {
@@ -3006,19 +3446,32 @@ Copyright © 2021 Basecamp, LLC
3006
3446
  formSubmissionFinished({ formElement }) {
3007
3447
  clearBusyState(formElement, this.findFrameElement(formElement));
3008
3448
  }
3009
- allowsImmediateRender(snapshot, resume) {
3010
- return true;
3449
+ allowsImmediateRender({ element: newFrame }, options) {
3450
+ const event = dispatch("turbo:before-frame-render", {
3451
+ target: this.element,
3452
+ detail: Object.assign({ newFrame }, options),
3453
+ cancelable: true,
3454
+ });
3455
+ const { defaultPrevented, detail: { render }, } = event;
3456
+ if (this.view.renderer && render) {
3457
+ this.view.renderer.renderElement = render;
3458
+ }
3459
+ return !defaultPrevented;
3011
3460
  }
3012
- viewRenderedSnapshot(snapshot, isPreview) {
3461
+ viewRenderedSnapshot(_snapshot, _isPreview) { }
3462
+ preloadOnLoadLinksForView(element) {
3463
+ session.preloadOnLoadLinksForView(element);
3013
3464
  }
3014
- viewInvalidated() {
3465
+ viewInvalidated() { }
3466
+ willRenderFrame(currentElement, _newElement) {
3467
+ this.previousFrameElement = currentElement.cloneNode(true);
3015
3468
  }
3016
3469
  async visit(url) {
3017
3470
  var _a;
3018
- const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams, this.element);
3471
+ const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
3019
3472
  (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
3020
3473
  this.currentFetchRequest = request;
3021
- return new Promise(resolve => {
3474
+ return new Promise((resolve) => {
3022
3475
  this.resolveVisitPromise = () => {
3023
3476
  this.resolveVisitPromise = () => { };
3024
3477
  this.currentFetchRequest = null;
@@ -3030,23 +3483,64 @@ Copyright © 2021 Basecamp, LLC
3030
3483
  navigateFrame(element, url, submitter) {
3031
3484
  const frame = this.findFrameElement(element, submitter);
3032
3485
  this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
3033
- frame.setAttribute("reloadable", "");
3034
- frame.src = url;
3486
+ this.withCurrentNavigationElement(element, () => {
3487
+ frame.src = url;
3488
+ });
3035
3489
  }
3036
3490
  proposeVisitIfNavigatedWithAction(frame, element, submitter) {
3037
- const action = getAttribute("data-turbo-action", submitter, element, frame);
3038
- if (isAction(action)) {
3039
- const { visitCachedSnapshot } = new SnapshotSubstitution(frame);
3491
+ this.action = getVisitAction(submitter, element, frame);
3492
+ this.frame = frame;
3493
+ if (isAction(this.action)) {
3494
+ const { visitCachedSnapshot } = frame.delegate;
3040
3495
  frame.delegate.fetchResponseLoaded = (fetchResponse) => {
3041
3496
  if (frame.src) {
3042
3497
  const { statusCode, redirected } = fetchResponse;
3043
3498
  const responseHTML = frame.ownerDocument.documentElement.outerHTML;
3044
3499
  const response = { statusCode, redirected, responseHTML };
3045
- session.visit(frame.src, { action, response, visitCachedSnapshot, willRender: false });
3500
+ const options = {
3501
+ response,
3502
+ visitCachedSnapshot,
3503
+ willRender: false,
3504
+ updateHistory: false,
3505
+ restorationIdentifier: this.restorationIdentifier,
3506
+ };
3507
+ if (this.action)
3508
+ options.action = this.action;
3509
+ session.visit(frame.src, options);
3046
3510
  }
3047
3511
  };
3048
3512
  }
3049
3513
  }
3514
+ changeHistory() {
3515
+ if (this.action && this.frame) {
3516
+ const method = getHistoryMethodForAction(this.action);
3517
+ session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
3518
+ }
3519
+ }
3520
+ willHandleFrameMissingFromResponse(fetchResponse) {
3521
+ this.element.setAttribute("complete", "");
3522
+ const response = fetchResponse.response;
3523
+ const visit = async (url, options = {}) => {
3524
+ if (url instanceof Response) {
3525
+ this.visitResponse(url);
3526
+ }
3527
+ else {
3528
+ session.visit(url, options);
3529
+ }
3530
+ };
3531
+ const event = dispatch("turbo:frame-missing", {
3532
+ target: this.element,
3533
+ detail: { response, visit },
3534
+ cancelable: true,
3535
+ });
3536
+ return !event.defaultPrevented;
3537
+ }
3538
+ async visitResponse(response) {
3539
+ const wrapped = new FetchResponse(response);
3540
+ const responseHTML = await wrapped.responseHTML;
3541
+ const { location, redirected, statusCode } = wrapped;
3542
+ return session.visit(location, { response: { redirected, statusCode, responseHTML } });
3543
+ }
3050
3544
  findFrameElement(element, submitter) {
3051
3545
  var _a;
3052
3546
  const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
@@ -3056,19 +3550,21 @@ Copyright © 2021 Basecamp, LLC
3056
3550
  let element;
3057
3551
  const id = CSS.escape(this.id);
3058
3552
  try {
3059
- if (element = activateElement(container.querySelector(`turbo-frame#${id}`), this.currentURL)) {
3553
+ element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
3554
+ if (element) {
3060
3555
  return element;
3061
3556
  }
3062
- if (element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.currentURL)) {
3557
+ element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
3558
+ if (element) {
3063
3559
  await element.loaded;
3064
3560
  return await this.extractForeignFrameElement(element);
3065
3561
  }
3066
- console.error(`Response has no matching <turbo-frame id="${id}"> element`);
3067
3562
  }
3068
3563
  catch (error) {
3069
3564
  console.error(error);
3565
+ return new FrameElement();
3070
3566
  }
3071
- return new FrameElement();
3567
+ return null;
3072
3568
  }
3073
3569
  formActionIsVisitable(form, submitter) {
3074
3570
  const action = getAction(form, submitter);
@@ -3088,10 +3584,10 @@ Copyright © 2021 Basecamp, LLC
3088
3584
  return !frameElement.disabled;
3089
3585
  }
3090
3586
  }
3091
- if (!session.elementDriveEnabled(element)) {
3587
+ if (!session.elementIsNavigatable(element)) {
3092
3588
  return false;
3093
3589
  }
3094
- if (submitter && !session.elementDriveEnabled(submitter)) {
3590
+ if (submitter && !session.elementIsNavigatable(submitter)) {
3095
3591
  return false;
3096
3592
  }
3097
3593
  return true;
@@ -3107,24 +3603,10 @@ Copyright © 2021 Basecamp, LLC
3107
3603
  return this.element.src;
3108
3604
  }
3109
3605
  }
3110
- get reloadable() {
3111
- const frame = this.findFrameElement(this.element);
3112
- return frame.hasAttribute("reloadable");
3113
- }
3114
- set reloadable(value) {
3115
- const frame = this.findFrameElement(this.element);
3116
- if (value) {
3117
- frame.setAttribute("reloadable", "");
3118
- }
3119
- else {
3120
- frame.removeAttribute("reloadable");
3121
- }
3122
- }
3123
3606
  set sourceURL(sourceURL) {
3124
- this.settingSourceURL = true;
3125
- this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3126
- this.currentURL = this.element.src;
3127
- this.settingSourceURL = false;
3607
+ this.ignoringChangesToAttribute("src", () => {
3608
+ this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3609
+ });
3128
3610
  }
3129
3611
  get loadingStyle() {
3130
3612
  return this.element.loading;
@@ -3132,6 +3614,19 @@ Copyright © 2021 Basecamp, LLC
3132
3614
  get isLoading() {
3133
3615
  return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
3134
3616
  }
3617
+ get complete() {
3618
+ return this.element.hasAttribute("complete");
3619
+ }
3620
+ set complete(value) {
3621
+ this.ignoringChangesToAttribute("complete", () => {
3622
+ if (value) {
3623
+ this.element.setAttribute("complete", "");
3624
+ }
3625
+ else {
3626
+ this.element.removeAttribute("complete");
3627
+ }
3628
+ });
3629
+ }
3135
3630
  get isActive() {
3136
3631
  return this.element.isActive && this.connected;
3137
3632
  }
@@ -3141,16 +3636,18 @@ Copyright © 2021 Basecamp, LLC
3141
3636
  const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
3142
3637
  return expandURL(root);
3143
3638
  }
3144
- }
3145
- class SnapshotSubstitution {
3146
- constructor(element) {
3147
- this.visitCachedSnapshot = ({ element }) => {
3148
- var _a;
3149
- const { id, clone } = this;
3150
- (_a = element.querySelector("#" + id)) === null || _a === void 0 ? void 0 : _a.replaceWith(clone);
3151
- };
3152
- this.clone = element.cloneNode(true);
3153
- this.id = element.id;
3639
+ isIgnoringChangesTo(attributeName) {
3640
+ return this.ignoredAttributes.has(attributeName);
3641
+ }
3642
+ ignoringChangesToAttribute(attributeName, callback) {
3643
+ this.ignoredAttributes.add(attributeName);
3644
+ callback();
3645
+ this.ignoredAttributes.delete(attributeName);
3646
+ }
3647
+ withCurrentNavigationElement(element, callback) {
3648
+ this.currentNavigationElement = element;
3649
+ callback();
3650
+ delete this.currentNavigationElement;
3154
3651
  }
3155
3652
  }
3156
3653
  function getFrameElementById(id) {
@@ -3178,36 +3675,10 @@ Copyright © 2021 Basecamp, LLC
3178
3675
  }
3179
3676
  }
3180
3677
 
3181
- const StreamActions = {
3182
- after() {
3183
- this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
3184
- },
3185
- append() {
3186
- this.removeDuplicateTargetChildren();
3187
- this.targetElements.forEach(e => e.append(this.templateContent));
3188
- },
3189
- before() {
3190
- this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
3191
- },
3192
- prepend() {
3193
- this.removeDuplicateTargetChildren();
3194
- this.targetElements.forEach(e => e.prepend(this.templateContent));
3195
- },
3196
- remove() {
3197
- this.targetElements.forEach(e => e.remove());
3198
- },
3199
- replace() {
3200
- this.targetElements.forEach(e => e.replaceWith(this.templateContent));
3201
- },
3202
- update() {
3203
- this.targetElements.forEach(e => {
3204
- e.innerHTML = "";
3205
- e.append(this.templateContent);
3206
- });
3207
- }
3208
- };
3209
-
3210
3678
  class StreamElement extends HTMLElement {
3679
+ static async renderElement(newElement) {
3680
+ await newElement.performAction();
3681
+ }
3211
3682
  async connectedCallback() {
3212
3683
  try {
3213
3684
  await this.render();
@@ -3221,12 +3692,13 @@ Copyright © 2021 Basecamp, LLC
3221
3692
  }
3222
3693
  async render() {
3223
3694
  var _a;
3224
- return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
3225
- if (this.dispatchEvent(this.beforeRenderEvent)) {
3695
+ return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
3696
+ const event = this.beforeRenderEvent;
3697
+ if (this.dispatchEvent(event)) {
3226
3698
  await nextAnimationFrame();
3227
- this.performAction();
3699
+ await event.detail.render(this);
3228
3700
  }
3229
- })());
3701
+ })()));
3230
3702
  }
3231
3703
  disconnect() {
3232
3704
  try {
@@ -3235,13 +3707,13 @@ Copyright © 2021 Basecamp, LLC
3235
3707
  catch (_a) { }
3236
3708
  }
3237
3709
  removeDuplicateTargetChildren() {
3238
- this.duplicateChildren.forEach(c => c.remove());
3710
+ this.duplicateChildren.forEach((c) => c.remove());
3239
3711
  }
3240
3712
  get duplicateChildren() {
3241
3713
  var _a;
3242
- const existingChildren = this.targetElements.flatMap(e => [...e.children]).filter(c => !!c.id);
3243
- const newChildrenIds = [...(_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children].filter(c => !!c.id).map(c => c.id);
3244
- return existingChildren.filter(c => newChildrenIds.includes(c.id));
3714
+ const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
3715
+ const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
3716
+ return existingChildren.filter((c) => newChildrenIds.includes(c.id));
3245
3717
  }
3246
3718
  get performAction() {
3247
3719
  if (this.action) {
@@ -3268,7 +3740,12 @@ Copyright © 2021 Basecamp, LLC
3268
3740
  return this.templateElement.content.cloneNode(true);
3269
3741
  }
3270
3742
  get templateElement() {
3271
- if (this.firstElementChild instanceof HTMLTemplateElement) {
3743
+ if (this.firstElementChild === null) {
3744
+ const template = this.ownerDocument.createElement("template");
3745
+ this.appendChild(template);
3746
+ return template;
3747
+ }
3748
+ else if (this.firstElementChild instanceof HTMLTemplateElement) {
3272
3749
  return this.firstElementChild;
3273
3750
  }
3274
3751
  this.raise("first child element must be a <template> element");
@@ -3290,7 +3767,11 @@ Copyright © 2021 Basecamp, LLC
3290
3767
  return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
3291
3768
  }
3292
3769
  get beforeRenderEvent() {
3293
- return new CustomEvent("turbo:before-stream-render", { bubbles: true, cancelable: true });
3770
+ return new CustomEvent("turbo:before-stream-render", {
3771
+ bubbles: true,
3772
+ cancelable: true,
3773
+ detail: { newStream: this, render: StreamElement.renderElement },
3774
+ });
3294
3775
  }
3295
3776
  get targetElementsById() {
3296
3777
  var _a;
@@ -3314,9 +3795,35 @@ Copyright © 2021 Basecamp, LLC
3314
3795
  }
3315
3796
  }
3316
3797
 
3798
+ class StreamSourceElement extends HTMLElement {
3799
+ constructor() {
3800
+ super(...arguments);
3801
+ this.streamSource = null;
3802
+ }
3803
+ connectedCallback() {
3804
+ this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
3805
+ connectStreamSource(this.streamSource);
3806
+ }
3807
+ disconnectedCallback() {
3808
+ if (this.streamSource) {
3809
+ disconnectStreamSource(this.streamSource);
3810
+ }
3811
+ }
3812
+ get src() {
3813
+ return this.getAttribute("src") || "";
3814
+ }
3815
+ }
3816
+
3317
3817
  FrameElement.delegateConstructor = FrameController;
3318
- customElements.define("turbo-frame", FrameElement);
3319
- customElements.define("turbo-stream", StreamElement);
3818
+ if (customElements.get("turbo-frame") === undefined) {
3819
+ customElements.define("turbo-frame", FrameElement);
3820
+ }
3821
+ if (customElements.get("turbo-stream") === undefined) {
3822
+ customElements.define("turbo-stream", StreamElement);
3823
+ }
3824
+ if (customElements.get("turbo-stream-source") === undefined) {
3825
+ customElements.define("turbo-stream-source", StreamSourceElement);
3826
+ }
3320
3827
 
3321
3828
  (() => {
3322
3829
  let element = document.currentScript;
@@ -3324,7 +3831,8 @@ Copyright © 2021 Basecamp, LLC
3324
3831
  return;
3325
3832
  if (element.hasAttribute("data-turbo-suppress-warning"))
3326
3833
  return;
3327
- while (element = element.parentElement) {
3834
+ element = element.parentElement;
3835
+ while (element) {
3328
3836
  if (element == document.body) {
3329
3837
  return console.warn(unindent `
3330
3838
  You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
@@ -3337,14 +3845,21 @@ Copyright © 2021 Basecamp, LLC
3337
3845
  Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
3338
3846
  `, element.outerHTML);
3339
3847
  }
3848
+ element = element.parentElement;
3340
3849
  }
3341
3850
  })();
3342
3851
 
3343
3852
  window.Turbo = Turbo;
3344
3853
  start();
3345
3854
 
3855
+ exports.FrameElement = FrameElement;
3856
+ exports.FrameRenderer = FrameRenderer;
3346
3857
  exports.PageRenderer = PageRenderer;
3347
3858
  exports.PageSnapshot = PageSnapshot;
3859
+ exports.StreamActions = StreamActions;
3860
+ exports.StreamElement = StreamElement;
3861
+ exports.StreamSourceElement = StreamSourceElement;
3862
+ exports.cache = cache;
3348
3863
  exports.clearCache = clearCache;
3349
3864
  exports.connectStreamSource = connectStreamSource;
3350
3865
  exports.disconnectStreamSource = disconnectStreamSource;
@@ -3353,10 +3868,11 @@ Copyright © 2021 Basecamp, LLC
3353
3868
  exports.renderStreamMessage = renderStreamMessage;
3354
3869
  exports.session = session;
3355
3870
  exports.setConfirmMethod = setConfirmMethod;
3871
+ exports.setFormMode = setFormMode;
3356
3872
  exports.setProgressBarDelay = setProgressBarDelay;
3357
3873
  exports.start = start;
3358
3874
  exports.visit = visit;
3359
3875
 
3360
3876
  Object.defineProperty(exports, '__esModule', { value: true });
3361
3877
 
3362
- })));
3878
+ }));