@hotwired/turbo 7.1.0-rc.3 → 7.2.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/turbo.es2017-esm.js +1027 -613
  2. package/dist/turbo.es2017-umd.js +1032 -614
  3. package/dist/types/core/bardo.d.ts +7 -2
  4. package/dist/types/core/cache.d.ts +10 -0
  5. package/dist/types/core/drive/error_renderer.d.ts +2 -1
  6. package/dist/types/core/drive/form_submission.d.ts +13 -4
  7. package/dist/types/core/drive/head_snapshot.d.ts +3 -3
  8. package/dist/types/core/drive/history.d.ts +1 -1
  9. package/dist/types/core/drive/navigator.d.ts +4 -3
  10. package/dist/types/core/drive/page_renderer.d.ts +8 -5
  11. package/dist/types/core/drive/page_view.d.ts +8 -5
  12. package/dist/types/core/drive/preloader.d.ts +14 -0
  13. package/dist/types/core/drive/progress_bar.d.ts +1 -0
  14. package/dist/types/core/drive/visit.d.ts +15 -4
  15. package/dist/types/core/frames/frame_controller.d.ts +43 -23
  16. package/dist/types/core/frames/frame_redirector.d.ts +12 -10
  17. package/dist/types/core/frames/frame_renderer.d.ts +8 -1
  18. package/dist/types/core/frames/frame_view.d.ts +2 -1
  19. package/dist/types/core/index.d.ts +14 -4
  20. package/dist/types/core/native/adapter.d.ts +3 -2
  21. package/dist/types/core/native/browser_adapter.d.ts +18 -9
  22. package/dist/types/core/renderer.d.ts +11 -5
  23. package/dist/types/core/session.d.ts +78 -19
  24. package/dist/types/core/snapshot.d.ts +2 -1
  25. package/dist/types/core/streams/stream_message.d.ts +2 -6
  26. package/dist/types/core/types.d.ts +4 -0
  27. package/dist/types/core/view.d.ts +13 -7
  28. package/dist/types/elements/frame_element.d.ts +10 -6
  29. package/dist/types/elements/stream_element.d.ts +3 -0
  30. package/dist/types/elements/stream_source_element.d.ts +7 -0
  31. package/dist/types/http/fetch_request.d.ts +9 -0
  32. package/dist/types/observers/cache_observer.d.ts +1 -1
  33. package/dist/types/observers/form_link_click_observer.d.ts +14 -0
  34. package/dist/types/observers/form_submit_observer.d.ts +2 -1
  35. package/dist/types/observers/link_click_observer.d.ts +5 -4
  36. package/dist/types/polyfills/submit-event.d.ts +1 -7
  37. package/dist/types/tests/functional/async_script_tests.d.ts +1 -6
  38. package/dist/types/tests/functional/autofocus_tests.d.ts +1 -9
  39. package/dist/types/tests/functional/cache_observer_tests.d.ts +1 -5
  40. package/dist/types/tests/functional/drive_disabled_tests.d.ts +1 -9
  41. package/dist/types/tests/functional/drive_tests.d.ts +1 -8
  42. package/dist/types/tests/functional/form_mode_tests.d.ts +1 -0
  43. package/dist/types/tests/functional/form_submission_tests.d.ts +1 -84
  44. package/dist/types/tests/functional/frame_navigation_tests.d.ts +1 -7
  45. package/dist/types/tests/functional/frame_tests.d.ts +1 -51
  46. package/dist/types/tests/functional/import_tests.d.ts +1 -4
  47. package/dist/types/tests/functional/loading_tests.d.ts +1 -13
  48. package/dist/types/tests/functional/navigation_tests.d.ts +1 -37
  49. package/dist/types/tests/functional/pausable_rendering_tests.d.ts +1 -6
  50. package/dist/types/tests/functional/pausable_requests_tests.d.ts +1 -6
  51. package/dist/types/tests/functional/preloader_tests.d.ts +1 -0
  52. package/dist/types/tests/functional/rendering_tests.d.ts +1 -35
  53. package/dist/types/tests/functional/scroll_restoration_tests.d.ts +1 -6
  54. package/dist/types/tests/functional/stream_tests.d.ts +1 -6
  55. package/dist/types/tests/functional/visit_tests.d.ts +1 -14
  56. package/dist/types/tests/helpers/page.d.ts +44 -0
  57. package/dist/types/tests/unit/deprecated_adapter_support_test.d.ts +10 -10
  58. package/dist/types/util.d.ts +12 -3
  59. package/package.json +22 -8
  60. package/CHANGELOG.md +0 -3
  61. package/dist/types/core/frames/form_interceptor.d.ts +0 -12
  62. package/dist/types/core/frames/link_interceptor.d.ts +0 -16
  63. package/dist/types/tests/functional/index.d.ts +0 -17
  64. package/dist/types/tests/helpers/functional_test_case.d.ts +0 -44
  65. package/dist/types/tests/helpers/remote_channel.d.ts +0 -10
  66. package/dist/types/tests/helpers/turbo_drive_test_case.d.ts +0 -21
@@ -1,26 +1,26 @@
1
1
  /*
2
- Turbo 7.1.0-rc.3
3
- Copyright © 2021 Basecamp, LLC
2
+ Turbo 7.2.0-beta.2
3
+ Copyright © 2022 Basecamp, LLC
4
4
  */
5
5
  (function (global, factory) {
6
6
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
7
7
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
8
8
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Turbo = {}));
9
- }(this, (function (exports) { 'use strict';
9
+ })(this, (function (exports) { 'use strict';
10
10
 
11
11
  (function () {
12
- if (window.Reflect === undefined || window.customElements === undefined ||
12
+ if (window.Reflect === undefined ||
13
+ window.customElements === undefined ||
13
14
  window.customElements.polyfillWrapFlushCallback) {
14
15
  return;
15
16
  }
16
17
  const BuiltInHTMLElement = HTMLElement;
17
18
  const wrapperForTheName = {
18
- 'HTMLElement': function HTMLElement() {
19
+ HTMLElement: function HTMLElement() {
19
20
  return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
20
- }
21
+ },
21
22
  };
22
- window.HTMLElement =
23
- wrapperForTheName['HTMLElement'];
23
+ window.HTMLElement = wrapperForTheName["HTMLElement"];
24
24
  HTMLElement.prototype = BuiltInHTMLElement.prototype;
25
25
  HTMLElement.prototype.constructor = HTMLElement;
26
26
  Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
@@ -78,7 +78,7 @@ Copyright © 2021 Basecamp, LLC
78
78
  }
79
79
  })(HTMLFormElement.prototype);
80
80
 
81
- const submittersByForm = new WeakMap;
81
+ const submittersByForm = new WeakMap();
82
82
  function findSubmitterFromClickTarget(target) {
83
83
  const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
84
84
  const candidate = element ? element.closest("input, button") : null;
@@ -109,7 +109,7 @@ Copyright © 2021 Basecamp, LLC
109
109
  if (this.type == "submit" && this.target instanceof HTMLFormElement) {
110
110
  return submittersByForm.get(this.target);
111
111
  }
112
- }
112
+ },
113
113
  });
114
114
  })();
115
115
 
@@ -125,7 +125,7 @@ Copyright © 2021 Basecamp, LLC
125
125
  this.delegate = new FrameElement.delegateConstructor(this);
126
126
  }
127
127
  static get observedAttributes() {
128
- return ["disabled", "loading", "src"];
128
+ return ["disabled", "complete", "loading", "src"];
129
129
  }
130
130
  connectedCallback() {
131
131
  this.delegate.connect();
@@ -135,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 FrameLoadingStyle.lazy;
216
+ default:
217
+ return 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,10 +400,11 @@ 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
  }
@@ -385,6 +426,48 @@ Copyright © 2021 Basecamp, LLC
385
426
  element.removeAttribute("aria-busy");
386
427
  }
387
428
  }
429
+ function waitForLoad(element, timeoutInMilliseconds = 2000) {
430
+ return new Promise((resolve) => {
431
+ const onComplete = () => {
432
+ element.removeEventListener("error", onComplete);
433
+ element.removeEventListener("load", onComplete);
434
+ resolve();
435
+ };
436
+ element.addEventListener("load", onComplete, { once: true });
437
+ element.addEventListener("error", onComplete, { once: true });
438
+ setTimeout(resolve, timeoutInMilliseconds);
439
+ });
440
+ }
441
+ function getHistoryMethodForAction(action) {
442
+ switch (action) {
443
+ case "replace":
444
+ return history.replaceState;
445
+ case "advance":
446
+ case "restore":
447
+ return history.pushState;
448
+ }
449
+ }
450
+ function getVisitAction(...elements) {
451
+ const action = getAttribute("data-turbo-action", ...elements);
452
+ return isAction(action) ? action : null;
453
+ }
454
+ function getMetaElement(name) {
455
+ return document.querySelector(`meta[name="${name}"]`);
456
+ }
457
+ function getMetaContent(name) {
458
+ const element = getMetaElement(name);
459
+ return element && element.content;
460
+ }
461
+ function setMetaContent(name, content) {
462
+ let element = getMetaElement(name);
463
+ if (!element) {
464
+ element = document.createElement("meta");
465
+ element.setAttribute("name", name);
466
+ document.head.appendChild(element);
467
+ }
468
+ element.setAttribute("content", content);
469
+ return element;
470
+ }
388
471
 
389
472
  var FetchMethod;
390
473
  (function (FetchMethod) {
@@ -396,24 +479,27 @@ Copyright © 2021 Basecamp, LLC
396
479
  })(FetchMethod || (FetchMethod = {}));
397
480
  function fetchMethodFromString(method) {
398
481
  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;
482
+ case "get":
483
+ return FetchMethod.get;
484
+ case "post":
485
+ return FetchMethod.post;
486
+ case "put":
487
+ return FetchMethod.put;
488
+ case "patch":
489
+ return FetchMethod.patch;
490
+ case "delete":
491
+ return FetchMethod.delete;
404
492
  }
405
493
  }
406
494
  class FetchRequest {
407
- constructor(delegate, method, location, body = new URLSearchParams, target = null) {
408
- this.abortController = new AbortController;
409
- this.resolveRequestPromise = (value) => { };
495
+ constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
496
+ this.abortController = new AbortController();
497
+ this.resolveRequestPromise = (_value) => { };
410
498
  this.delegate = delegate;
411
499
  this.method = method;
412
500
  this.headers = this.defaultHeaders;
413
501
  this.body = body;
414
- this.url = this.isIdempotent ?
415
- mergeFormDataEntries(new URL(location.href), this.entries) :
416
- location;
502
+ this.url = location;
417
503
  this.target = target;
418
504
  }
419
505
  get location() {
@@ -439,7 +525,7 @@ Copyright © 2021 Basecamp, LLC
439
525
  return await this.receive(response);
440
526
  }
441
527
  catch (error) {
442
- if (error.name !== 'AbortError') {
528
+ if (error.name !== "AbortError") {
443
529
  this.delegate.requestErrored(this, error);
444
530
  throw error;
445
531
  }
@@ -450,7 +536,11 @@ Copyright © 2021 Basecamp, LLC
450
536
  }
451
537
  async receive(response) {
452
538
  const fetchResponse = new FetchResponse(response);
453
- const event = dispatch("turbo:before-fetch-response", { cancelable: true, detail: { fetchResponse }, target: this.target });
539
+ const event = dispatch("turbo:before-fetch-response", {
540
+ cancelable: true,
541
+ detail: { fetchResponse },
542
+ target: this.target,
543
+ });
454
544
  if (event.defaultPrevented) {
455
545
  this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
456
546
  }
@@ -471,12 +561,12 @@ Copyright © 2021 Basecamp, LLC
471
561
  redirect: "follow",
472
562
  body: this.isIdempotent ? null : this.body,
473
563
  signal: this.abortSignal,
474
- referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
564
+ referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
475
565
  };
476
566
  }
477
567
  get defaultHeaders() {
478
568
  return {
479
- "Accept": "text/html, application/xhtml+xml"
569
+ Accept: "text/html, application/xhtml+xml",
480
570
  };
481
571
  }
482
572
  get isIdempotent() {
@@ -485,36 +575,29 @@ Copyright © 2021 Basecamp, LLC
485
575
  get abortSignal() {
486
576
  return this.abortController.signal;
487
577
  }
578
+ acceptResponseType(mimeType) {
579
+ this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
580
+ }
488
581
  async allowRequestToBeIntercepted(fetchOptions) {
489
- const requestInterception = new Promise(resolve => this.resolveRequestPromise = resolve);
582
+ const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
490
583
  const event = dispatch("turbo:before-fetch-request", {
491
584
  cancelable: true,
492
585
  detail: {
493
586
  fetchOptions,
494
587
  url: this.url,
495
- resume: this.resolveRequestPromise
588
+ resume: this.resolveRequestPromise,
496
589
  },
497
- target: this.target
590
+ target: this.target,
498
591
  });
499
592
  if (event.defaultPrevented)
500
593
  await requestInterception;
501
594
  }
502
595
  }
503
- function mergeFormDataEntries(url, entries) {
504
- const searchParams = new URLSearchParams;
505
- for (const [name, value] of entries) {
506
- if (value instanceof File)
507
- continue;
508
- searchParams.append(name, value);
509
- }
510
- url.search = searchParams.toString();
511
- return url;
512
- }
513
596
 
514
597
  class AppearanceObserver {
515
598
  constructor(delegate, element) {
516
599
  this.started = false;
517
- this.intersect = entries => {
600
+ this.intersect = (entries) => {
518
601
  const lastEntry = entries.slice(-1)[0];
519
602
  if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
520
603
  this.delegate.elementAppearedInViewport(this.element);
@@ -539,40 +622,29 @@ Copyright © 2021 Basecamp, LLC
539
622
  }
540
623
 
541
624
  class StreamMessage {
542
- constructor(html) {
543
- this.templateElement = document.createElement("template");
544
- this.templateElement.innerHTML = html;
625
+ constructor(fragment) {
626
+ this.fragment = importStreamElements(fragment);
545
627
  }
546
628
  static wrap(message) {
547
629
  if (typeof message == "string") {
548
- return new this(message);
630
+ return new this(createDocumentFragment(message));
549
631
  }
550
632
  else {
551
633
  return message;
552
634
  }
553
635
  }
554
- get fragment() {
555
- const fragment = document.createDocumentFragment();
556
- for (const element of this.foreignElements) {
557
- fragment.appendChild(document.importNode(element, true));
636
+ }
637
+ StreamMessage.contentType = "text/vnd.turbo-stream.html";
638
+ function importStreamElements(fragment) {
639
+ for (const element of fragment.querySelectorAll("turbo-stream")) {
640
+ const streamElement = document.importNode(element, true);
641
+ for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
642
+ inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
558
643
  }
559
- return fragment;
560
- }
561
- get foreignElements() {
562
- return this.templateChildren.reduce((streamElements, child) => {
563
- if (child.tagName.toLowerCase() == "turbo-stream") {
564
- return [...streamElements, child];
565
- }
566
- else {
567
- return streamElements;
568
- }
569
- }, []);
570
- }
571
- get templateChildren() {
572
- return Array.from(this.templateElement.content.children);
644
+ element.replaceWith(streamElement);
573
645
  }
646
+ return fragment;
574
647
  }
575
- StreamMessage.contentType = "text/vnd.turbo-stream.html";
576
648
 
577
649
  var FormSubmissionState;
578
650
  (function (FormSubmissionState) {
@@ -591,9 +663,12 @@ Copyright © 2021 Basecamp, LLC
591
663
  })(FormEnctype || (FormEnctype = {}));
592
664
  function formEnctypeFromString(encoding) {
593
665
  switch (encoding.toLowerCase()) {
594
- case FormEnctype.multipart: return FormEnctype.multipart;
595
- case FormEnctype.plain: return FormEnctype.plain;
596
- default: return FormEnctype.urlEncoded;
666
+ case FormEnctype.multipart:
667
+ return FormEnctype.multipart;
668
+ case FormEnctype.plain:
669
+ return FormEnctype.plain;
670
+ default:
671
+ return FormEnctype.urlEncoded;
597
672
  }
598
673
  }
599
674
  class FormSubmission {
@@ -603,11 +678,15 @@ Copyright © 2021 Basecamp, LLC
603
678
  this.formElement = formElement;
604
679
  this.submitter = submitter;
605
680
  this.formData = buildFormData(formElement, submitter);
681
+ this.location = expandURL(this.action);
682
+ if (this.method == FetchMethod.get) {
683
+ mergeFormDataEntries(this.location, [...this.body.entries()]);
684
+ }
606
685
  this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
607
686
  this.mustRedirect = mustRedirect;
608
687
  }
609
- static confirmMethod(message, element) {
610
- return confirm(message);
688
+ static confirmMethod(message, _element) {
689
+ return Promise.resolve(confirm(message));
611
690
  }
612
691
  get method() {
613
692
  var _a;
@@ -616,11 +695,13 @@ Copyright © 2021 Basecamp, LLC
616
695
  }
617
696
  get action() {
618
697
  var _a;
619
- const formElementAction = typeof this.formElement.action === 'string' ? this.formElement.action : null;
620
- return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
621
- }
622
- get location() {
623
- return expandURL(this.action);
698
+ const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
699
+ if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
700
+ return this.submitter.getAttribute("formaction") || "";
701
+ }
702
+ else {
703
+ return this.formElement.getAttribute("action") || formElementAction || "";
704
+ }
624
705
  }
625
706
  get body() {
626
707
  if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
@@ -643,7 +724,8 @@ Copyright © 2021 Basecamp, LLC
643
724
  }, []);
644
725
  }
645
726
  get confirmationMessage() {
646
- return this.formElement.getAttribute("data-turbo-confirm");
727
+ var _a;
728
+ return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-confirm")) || this.formElement.getAttribute("data-turbo-confirm");
647
729
  }
648
730
  get needsConfirmation() {
649
731
  return this.confirmationMessage !== null;
@@ -651,7 +733,7 @@ Copyright © 2021 Basecamp, LLC
651
733
  async start() {
652
734
  const { initialized, requesting } = FormSubmissionState;
653
735
  if (this.needsConfirmation) {
654
- const answer = FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
736
+ const answer = await FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
655
737
  if (!answer) {
656
738
  return;
657
739
  }
@@ -675,14 +757,19 @@ Copyright © 2021 Basecamp, LLC
675
757
  if (token) {
676
758
  headers["X-CSRF-Token"] = token;
677
759
  }
678
- headers["Accept"] = [StreamMessage.contentType, headers["Accept"]].join(", ");
760
+ }
761
+ if (this.requestAcceptsTurboStreamResponse(request)) {
762
+ request.acceptResponseType(StreamMessage.contentType);
679
763
  }
680
764
  }
681
- requestStarted(request) {
765
+ requestStarted(_request) {
682
766
  var _a;
683
767
  this.state = FormSubmissionState.waiting;
684
768
  (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
685
- dispatch("turbo:submit-start", { target: this.formElement, detail: { formSubmission: this } });
769
+ dispatch("turbo:submit-start", {
770
+ target: this.formElement,
771
+ detail: { formSubmission: this },
772
+ });
686
773
  this.delegate.formSubmissionStarted(this);
687
774
  }
688
775
  requestPreventedHandlingResponse(request, response) {
@@ -708,25 +795,35 @@ Copyright © 2021 Basecamp, LLC
708
795
  }
709
796
  requestErrored(request, error) {
710
797
  this.result = { success: false, error };
798
+ dispatch("turbo:fetch-request-error", {
799
+ target: this.formElement,
800
+ detail: { request, error },
801
+ });
711
802
  this.delegate.formSubmissionErrored(this, error);
712
803
  }
713
- requestFinished(request) {
804
+ requestFinished(_request) {
714
805
  var _a;
715
806
  this.state = FormSubmissionState.stopped;
716
807
  (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
717
- 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
+ });
718
812
  this.delegate.formSubmissionFinished(this);
719
813
  }
720
814
  requestMustRedirect(request) {
721
815
  return !request.isIdempotent && this.mustRedirect;
722
816
  }
817
+ requestAcceptsTurboStreamResponse(request) {
818
+ return !request.isIdempotent || this.formElement.hasAttribute("data-turbo-stream");
819
+ }
723
820
  }
724
821
  function buildFormData(formElement, submitter) {
725
822
  const formData = new FormData(formElement);
726
823
  const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
727
824
  const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
728
- if (name && value != null && formData.get(name) != value) {
729
- formData.append(name, value);
825
+ if (name) {
826
+ formData.append(name, value || "");
730
827
  }
731
828
  return formData;
732
829
  }
@@ -740,18 +837,27 @@ Copyright © 2021 Basecamp, LLC
740
837
  }
741
838
  }
742
839
  }
743
- function getMetaContent(name) {
744
- const element = document.querySelector(`meta[name="${name}"]`);
745
- return element && element.content;
746
- }
747
840
  function responseSucceededWithoutRedirect(response) {
748
841
  return response.statusCode == 200 && !response.redirected;
749
842
  }
843
+ function mergeFormDataEntries(url, entries) {
844
+ const searchParams = new URLSearchParams();
845
+ for (const [name, value] of entries) {
846
+ if (value instanceof File)
847
+ continue;
848
+ searchParams.append(name, value);
849
+ }
850
+ url.search = searchParams.toString();
851
+ return url;
852
+ }
750
853
 
751
854
  class Snapshot {
752
855
  constructor(element) {
753
856
  this.element = element;
754
857
  }
858
+ get activeElement() {
859
+ return this.element.ownerDocument.activeElement;
860
+ }
755
861
  get children() {
756
862
  return [...this.element.children];
757
863
  }
@@ -765,7 +871,14 @@ Copyright © 2021 Basecamp, LLC
765
871
  return this.element.isConnected;
766
872
  }
767
873
  get firstAutofocusableElement() {
768
- 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;
769
882
  }
770
883
  get permanentElements() {
771
884
  return [...this.element.querySelectorAll("[id][data-turbo-permanent]")];
@@ -786,35 +899,59 @@ Copyright © 2021 Basecamp, LLC
786
899
  }
787
900
  }
788
901
 
789
- class FormInterceptor {
790
- constructor(delegate, element) {
902
+ class FormSubmitObserver {
903
+ constructor(delegate, eventTarget) {
904
+ this.started = false;
905
+ this.submitCaptured = () => {
906
+ this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
907
+ this.eventTarget.addEventListener("submit", this.submitBubbled, false);
908
+ };
791
909
  this.submitBubbled = ((event) => {
792
- const form = event.target;
793
- if (!event.defaultPrevented && form instanceof HTMLFormElement && form.closest("turbo-frame, html") == this.element) {
910
+ if (!event.defaultPrevented) {
911
+ const form = event.target instanceof HTMLFormElement ? event.target : undefined;
794
912
  const submitter = event.submitter || undefined;
795
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
796
- if (method != "dialog" && this.delegate.shouldInterceptFormSubmission(form, submitter)) {
913
+ if (form &&
914
+ submissionDoesNotDismissDialog(form, submitter) &&
915
+ submissionDoesNotTargetIFrame(form, submitter) &&
916
+ this.delegate.willSubmitForm(form, submitter)) {
797
917
  event.preventDefault();
798
- event.stopImmediatePropagation();
799
- this.delegate.formSubmissionIntercepted(form, submitter);
918
+ this.delegate.formSubmitted(form, submitter);
800
919
  }
801
920
  }
802
921
  });
803
922
  this.delegate = delegate;
804
- this.element = element;
923
+ this.eventTarget = eventTarget;
805
924
  }
806
925
  start() {
807
- this.element.addEventListener("submit", this.submitBubbled);
926
+ if (!this.started) {
927
+ this.eventTarget.addEventListener("submit", this.submitCaptured, true);
928
+ this.started = true;
929
+ }
808
930
  }
809
931
  stop() {
810
- this.element.removeEventListener("submit", this.submitBubbled);
932
+ if (this.started) {
933
+ this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
934
+ this.started = false;
935
+ }
936
+ }
937
+ }
938
+ function submissionDoesNotDismissDialog(form, submitter) {
939
+ const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
940
+ return method != "dialog";
941
+ }
942
+ function submissionDoesNotTargetIFrame(form, submitter) {
943
+ const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
944
+ for (const element of document.getElementsByName(target)) {
945
+ if (element instanceof HTMLIFrameElement)
946
+ return false;
811
947
  }
948
+ return true;
812
949
  }
813
950
 
814
951
  class View {
815
952
  constructor(delegate, element) {
816
- this.resolveRenderPromise = (value) => { };
817
- this.resolveInterceptionPromise = (value) => { };
953
+ this.resolveRenderPromise = (_value) => { };
954
+ this.resolveInterceptionPromise = (_value) => { };
818
955
  this.delegate = delegate;
819
956
  this.element = element;
820
957
  }
@@ -859,15 +996,17 @@ Copyright © 2021 Basecamp, LLC
859
996
  const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
860
997
  if (shouldRender) {
861
998
  try {
862
- this.renderPromise = new Promise(resolve => this.resolveRenderPromise = resolve);
999
+ this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
863
1000
  this.renderer = renderer;
864
- this.prepareToRenderSnapshot(renderer);
865
- const renderInterception = new Promise(resolve => this.resolveInterceptionPromise = resolve);
866
- const immediateRender = this.delegate.allowsImmediateRender(snapshot, this.resolveInterceptionPromise);
1001
+ await this.prepareToRenderSnapshot(renderer);
1002
+ const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
1003
+ const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
1004
+ const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
867
1005
  if (!immediateRender)
868
1006
  await renderInterception;
869
1007
  await this.renderSnapshot(renderer);
870
1008
  this.delegate.viewRenderedSnapshot(snapshot, isPreview);
1009
+ this.delegate.preloadOnLoadLinksForView(this.element);
871
1010
  this.finishRenderingSnapshot(renderer);
872
1011
  }
873
1012
  finally {
@@ -877,15 +1016,15 @@ Copyright © 2021 Basecamp, LLC
877
1016
  }
878
1017
  }
879
1018
  else {
880
- this.invalidate();
1019
+ this.invalidate(renderer.reloadReason);
881
1020
  }
882
1021
  }
883
- invalidate() {
884
- this.delegate.viewInvalidated();
1022
+ invalidate(reason) {
1023
+ this.delegate.viewInvalidated(reason);
885
1024
  }
886
- prepareToRenderSnapshot(renderer) {
1025
+ async prepareToRenderSnapshot(renderer) {
887
1026
  this.markAsPreview(renderer.isPreview);
888
- renderer.prepareToRender();
1027
+ await renderer.prepareToRender();
889
1028
  }
890
1029
  markAsPreview(isPreview) {
891
1030
  if (isPreview) {
@@ -912,65 +1051,122 @@ Copyright © 2021 Basecamp, LLC
912
1051
  }
913
1052
  }
914
1053
 
915
- class LinkInterceptor {
916
- constructor(delegate, element) {
917
- this.clickBubbled = (event) => {
918
- if (this.respondsToEventTarget(event.target)) {
919
- this.clickEvent = event;
920
- }
921
- else {
922
- delete this.clickEvent;
923
- }
1054
+ class LinkClickObserver {
1055
+ constructor(delegate, eventTarget) {
1056
+ this.started = false;
1057
+ this.clickCaptured = () => {
1058
+ this.eventTarget.removeEventListener("click", this.clickBubbled, false);
1059
+ this.eventTarget.addEventListener("click", this.clickBubbled, false);
924
1060
  };
925
- this.linkClicked = ((event) => {
926
- if (this.clickEvent && this.respondsToEventTarget(event.target) && event.target instanceof Element) {
927
- if (this.delegate.shouldInterceptLinkClick(event.target, event.detail.url)) {
928
- this.clickEvent.preventDefault();
929
- event.preventDefault();
930
- this.delegate.linkClickIntercepted(event.target, event.detail.url);
1061
+ this.clickBubbled = (event) => {
1062
+ if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
1063
+ const target = (event.composedPath && event.composedPath()[0]) || event.target;
1064
+ const link = this.findLinkFromClickTarget(target);
1065
+ if (link && doesNotTargetIFrame(link)) {
1066
+ const location = this.getLocationForLink(link);
1067
+ if (this.delegate.willFollowLinkToLocation(link, location, event)) {
1068
+ event.preventDefault();
1069
+ this.delegate.followedLinkToLocation(link, location);
1070
+ }
931
1071
  }
932
1072
  }
933
- delete this.clickEvent;
934
- });
935
- this.willVisit = () => {
936
- delete this.clickEvent;
937
1073
  };
938
1074
  this.delegate = delegate;
939
- this.element = element;
1075
+ this.eventTarget = eventTarget;
1076
+ }
1077
+ start() {
1078
+ if (!this.started) {
1079
+ this.eventTarget.addEventListener("click", this.clickCaptured, true);
1080
+ this.started = true;
1081
+ }
1082
+ }
1083
+ stop() {
1084
+ if (this.started) {
1085
+ this.eventTarget.removeEventListener("click", this.clickCaptured, true);
1086
+ this.started = false;
1087
+ }
1088
+ }
1089
+ clickEventIsSignificant(event) {
1090
+ return !((event.target && event.target.isContentEditable) ||
1091
+ event.defaultPrevented ||
1092
+ event.which > 1 ||
1093
+ event.altKey ||
1094
+ event.ctrlKey ||
1095
+ event.metaKey ||
1096
+ event.shiftKey);
1097
+ }
1098
+ findLinkFromClickTarget(target) {
1099
+ if (target instanceof Element) {
1100
+ return target.closest("a[href]:not([target^=_]):not([download])");
1101
+ }
1102
+ }
1103
+ getLocationForLink(link) {
1104
+ return expandURL(link.getAttribute("href") || "");
1105
+ }
1106
+ }
1107
+ function doesNotTargetIFrame(anchor) {
1108
+ for (const element of document.getElementsByName(anchor.target)) {
1109
+ if (element instanceof HTMLIFrameElement)
1110
+ return false;
1111
+ }
1112
+ return true;
1113
+ }
1114
+
1115
+ class FormLinkClickObserver {
1116
+ constructor(delegate, element) {
1117
+ this.delegate = delegate;
1118
+ this.linkClickObserver = new LinkClickObserver(this, element);
940
1119
  }
941
1120
  start() {
942
- this.element.addEventListener("click", this.clickBubbled);
943
- document.addEventListener("turbo:click", this.linkClicked);
944
- document.addEventListener("turbo:before-visit", this.willVisit);
1121
+ this.linkClickObserver.start();
945
1122
  }
946
1123
  stop() {
947
- this.element.removeEventListener("click", this.clickBubbled);
948
- document.removeEventListener("turbo:click", this.linkClicked);
949
- document.removeEventListener("turbo:before-visit", this.willVisit);
1124
+ this.linkClickObserver.stop();
1125
+ }
1126
+ willFollowLinkToLocation(link, location, originalEvent) {
1127
+ return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
1128
+ link.hasAttribute("data-turbo-method"));
950
1129
  }
951
- respondsToEventTarget(target) {
952
- const element = target instanceof Element
953
- ? target
954
- : target instanceof Node
955
- ? target.parentElement
956
- : null;
957
- return element && element.closest("turbo-frame, html") == this.element;
1130
+ followedLinkToLocation(link, location) {
1131
+ const action = location.href;
1132
+ const form = document.createElement("form");
1133
+ form.setAttribute("data-turbo", "true");
1134
+ form.setAttribute("action", action);
1135
+ form.setAttribute("hidden", "");
1136
+ const method = link.getAttribute("data-turbo-method");
1137
+ if (method)
1138
+ form.setAttribute("method", method);
1139
+ const turboFrame = link.getAttribute("data-turbo-frame");
1140
+ if (turboFrame)
1141
+ form.setAttribute("data-turbo-frame", turboFrame);
1142
+ const turboConfirm = link.getAttribute("data-turbo-confirm");
1143
+ if (turboConfirm)
1144
+ form.setAttribute("data-turbo-confirm", turboConfirm);
1145
+ const turboStream = link.hasAttribute("data-turbo-stream");
1146
+ if (turboStream)
1147
+ form.setAttribute("data-turbo-stream", "");
1148
+ this.delegate.submittedFormLinkToLocation(link, location, form);
1149
+ document.body.appendChild(form);
1150
+ form.requestSubmit();
1151
+ form.remove();
958
1152
  }
959
1153
  }
960
1154
 
961
1155
  class Bardo {
962
- constructor(permanentElementMap) {
1156
+ constructor(delegate, permanentElementMap) {
1157
+ this.delegate = delegate;
963
1158
  this.permanentElementMap = permanentElementMap;
964
1159
  }
965
- static preservingPermanentElements(permanentElementMap, callback) {
966
- const bardo = new this(permanentElementMap);
1160
+ static preservingPermanentElements(delegate, permanentElementMap, callback) {
1161
+ const bardo = new this(delegate, permanentElementMap);
967
1162
  bardo.enter();
968
1163
  callback();
969
1164
  bardo.leave();
970
1165
  }
971
1166
  enter() {
972
1167
  for (const id in this.permanentElementMap) {
973
- const [, newPermanentElement] = this.permanentElementMap[id];
1168
+ const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
1169
+ this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
974
1170
  this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
975
1171
  }
976
1172
  }
@@ -979,6 +1175,7 @@ Copyright © 2021 Basecamp, LLC
979
1175
  const [currentPermanentElement] = this.permanentElementMap[id];
980
1176
  this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
981
1177
  this.replacePlaceholderWithPermanentElement(currentPermanentElement);
1178
+ this.delegate.leavingBardo(currentPermanentElement);
982
1179
  }
983
1180
  }
984
1181
  replaceNewPermanentElementWithPlaceholder(permanentElement) {
@@ -994,7 +1191,7 @@ Copyright © 2021 Basecamp, LLC
994
1191
  placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
995
1192
  }
996
1193
  getPlaceholderById(id) {
997
- return this.placeholders.find(element => element.content == id);
1194
+ return this.placeholders.find((element) => element.content == id);
998
1195
  }
999
1196
  get placeholders() {
1000
1197
  return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
@@ -1008,16 +1205,21 @@ Copyright © 2021 Basecamp, LLC
1008
1205
  }
1009
1206
 
1010
1207
  class Renderer {
1011
- constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
1208
+ constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1209
+ this.activeElement = null;
1012
1210
  this.currentSnapshot = currentSnapshot;
1013
1211
  this.newSnapshot = newSnapshot;
1014
1212
  this.isPreview = isPreview;
1015
1213
  this.willRender = willRender;
1016
- this.promise = new Promise((resolve, reject) => this.resolvingFunctions = { resolve, reject });
1214
+ this.renderElement = renderElement;
1215
+ this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
1017
1216
  }
1018
1217
  get shouldRender() {
1019
1218
  return true;
1020
1219
  }
1220
+ get reloadReason() {
1221
+ return;
1222
+ }
1021
1223
  prepareToRender() {
1022
1224
  return;
1023
1225
  }
@@ -1027,23 +1229,8 @@ Copyright © 2021 Basecamp, LLC
1027
1229
  delete this.resolvingFunctions;
1028
1230
  }
1029
1231
  }
1030
- createScriptElement(element) {
1031
- if (element.getAttribute("data-turbo-eval") == "false") {
1032
- return element;
1033
- }
1034
- else {
1035
- const createdScriptElement = document.createElement("script");
1036
- if (this.cspNonce) {
1037
- createdScriptElement.nonce = this.cspNonce;
1038
- }
1039
- createdScriptElement.textContent = element.textContent;
1040
- createdScriptElement.async = false;
1041
- copyElementAttributes(createdScriptElement, element);
1042
- return createdScriptElement;
1043
- }
1044
- }
1045
1232
  preservingPermanentElements(callback) {
1046
- Bardo.preservingPermanentElements(this.permanentElementMap, callback);
1233
+ Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
1047
1234
  }
1048
1235
  focusFirstAutofocusableElement() {
1049
1236
  const element = this.connectedSnapshot.firstAutofocusableElement;
@@ -1051,6 +1238,19 @@ Copyright © 2021 Basecamp, LLC
1051
1238
  element.focus();
1052
1239
  }
1053
1240
  }
1241
+ enteringBardo(currentPermanentElement) {
1242
+ if (this.activeElement)
1243
+ return;
1244
+ if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
1245
+ this.activeElement = this.currentSnapshot.activeElement;
1246
+ }
1247
+ }
1248
+ leavingBardo(currentPermanentElement) {
1249
+ if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
1250
+ this.activeElement.focus();
1251
+ this.activeElement = null;
1252
+ }
1253
+ }
1054
1254
  get connectedSnapshot() {
1055
1255
  return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
1056
1256
  }
@@ -1063,21 +1263,28 @@ Copyright © 2021 Basecamp, LLC
1063
1263
  get permanentElementMap() {
1064
1264
  return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
1065
1265
  }
1066
- get cspNonce() {
1067
- var _a;
1068
- return (_a = document.head.querySelector('meta[name="csp-nonce"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
1069
- }
1070
- }
1071
- function copyElementAttributes(destinationElement, sourceElement) {
1072
- for (const { name, value } of [...sourceElement.attributes]) {
1073
- destinationElement.setAttribute(name, value);
1074
- }
1075
1266
  }
1076
1267
  function elementIsFocusable(element) {
1077
1268
  return element && typeof element.focus == "function";
1078
1269
  }
1079
1270
 
1080
1271
  class FrameRenderer extends Renderer {
1272
+ constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
1273
+ super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
1274
+ this.delegate = delegate;
1275
+ }
1276
+ static renderElement(currentElement, newElement) {
1277
+ var _a;
1278
+ const destinationRange = document.createRange();
1279
+ destinationRange.selectNodeContents(currentElement);
1280
+ destinationRange.deleteContents();
1281
+ const frameElement = newElement;
1282
+ const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1283
+ if (sourceRange) {
1284
+ sourceRange.selectNodeContents(frameElement);
1285
+ currentElement.appendChild(sourceRange.extractContents());
1286
+ }
1287
+ }
1081
1288
  get shouldRender() {
1082
1289
  return true;
1083
1290
  }
@@ -1093,23 +1300,16 @@ Copyright © 2021 Basecamp, LLC
1093
1300
  this.activateScriptElements();
1094
1301
  }
1095
1302
  loadFrameElement() {
1096
- var _a;
1097
- const destinationRange = document.createRange();
1098
- destinationRange.selectNodeContents(this.currentElement);
1099
- destinationRange.deleteContents();
1100
- const frameElement = this.newElement;
1101
- const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
1102
- if (sourceRange) {
1103
- sourceRange.selectNodeContents(frameElement);
1104
- this.currentElement.appendChild(sourceRange.extractContents());
1105
- }
1303
+ this.delegate.willRenderFrame(this.currentElement, this.newElement);
1304
+ this.renderElement(this.currentElement, this.newElement);
1106
1305
  }
1107
1306
  scrollFrameIntoView() {
1108
1307
  if (this.currentElement.autoscroll || this.newElement.autoscroll) {
1109
1308
  const element = this.currentElement.firstElementChild;
1110
1309
  const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
1310
+ const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
1111
1311
  if (element) {
1112
- element.scrollIntoView({ block });
1312
+ element.scrollIntoView({ block, behavior });
1113
1313
  return true;
1114
1314
  }
1115
1315
  }
@@ -1117,7 +1317,7 @@ Copyright © 2021 Basecamp, LLC
1117
1317
  }
1118
1318
  activateScriptElements() {
1119
1319
  for (const inertScriptElement of this.newScriptElements) {
1120
- const activatedScriptElement = this.createScriptElement(inertScriptElement);
1320
+ const activatedScriptElement = activateScriptElement(inertScriptElement);
1121
1321
  inertScriptElement.replaceWith(activatedScriptElement);
1122
1322
  }
1123
1323
  }
@@ -1133,6 +1333,14 @@ Copyright © 2021 Basecamp, LLC
1133
1333
  return defaultValue;
1134
1334
  }
1135
1335
  }
1336
+ function readScrollBehavior(value, defaultValue) {
1337
+ if (value == "auto" || value == "smooth") {
1338
+ return value;
1339
+ }
1340
+ else {
1341
+ return defaultValue;
1342
+ }
1343
+ }
1136
1344
 
1137
1345
  class ProgressBar {
1138
1346
  constructor() {
@@ -1156,7 +1364,7 @@ Copyright © 2021 Basecamp, LLC
1156
1364
  left: 0;
1157
1365
  height: 3px;
1158
1366
  background: #0076ff;
1159
- z-index: 9999;
1367
+ z-index: 2147483647;
1160
1368
  transition:
1161
1369
  width ${ProgressBar.animationDuration}ms ease-out,
1162
1370
  opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
@@ -1215,13 +1423,16 @@ Copyright © 2021 Basecamp, LLC
1215
1423
  }
1216
1424
  refresh() {
1217
1425
  requestAnimationFrame(() => {
1218
- this.progressElement.style.width = `${10 + (this.value * 90)}%`;
1426
+ this.progressElement.style.width = `${10 + this.value * 90}%`;
1219
1427
  });
1220
1428
  }
1221
1429
  createStylesheetElement() {
1222
1430
  const element = document.createElement("style");
1223
1431
  element.type = "text/css";
1224
1432
  element.textContent = ProgressBar.defaultCSS;
1433
+ if (this.cspNonce) {
1434
+ element.nonce = this.cspNonce;
1435
+ }
1225
1436
  return element;
1226
1437
  }
1227
1438
  createProgressElement() {
@@ -1229,6 +1440,9 @@ Copyright © 2021 Basecamp, LLC
1229
1440
  element.className = "turbo-progress-bar";
1230
1441
  return element;
1231
1442
  }
1443
+ get cspNonce() {
1444
+ return getMetaContent("csp-nonce");
1445
+ }
1232
1446
  }
1233
1447
  ProgressBar.animationDuration = 300;
1234
1448
 
@@ -1245,14 +1459,14 @@ Copyright © 2021 Basecamp, LLC
1245
1459
  : {
1246
1460
  type: elementType(element),
1247
1461
  tracked: elementIsTracked(element),
1248
- elements: []
1462
+ elements: [],
1249
1463
  };
1250
1464
  return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
1251
1465
  }, {});
1252
1466
  }
1253
1467
  get trackedElementSignature() {
1254
1468
  return Object.keys(this.detailsByOuterHTML)
1255
- .filter(outerHTML => this.detailsByOuterHTML[outerHTML].tracked)
1469
+ .filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
1256
1470
  .join("");
1257
1471
  }
1258
1472
  getScriptElementsNotInSnapshot(snapshot) {
@@ -1263,8 +1477,8 @@ Copyright © 2021 Basecamp, LLC
1263
1477
  }
1264
1478
  getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
1265
1479
  return Object.keys(this.detailsByOuterHTML)
1266
- .filter(outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))
1267
- .map(outerHTML => this.detailsByOuterHTML[outerHTML])
1480
+ .filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
1481
+ .map((outerHTML) => this.detailsByOuterHTML[outerHTML])
1268
1482
  .filter(({ type }) => type == matchedType)
1269
1483
  .map(({ elements: [element] }) => element);
1270
1484
  }
@@ -1284,13 +1498,11 @@ Copyright © 2021 Basecamp, LLC
1284
1498
  }
1285
1499
  getMetaValue(name) {
1286
1500
  const element = this.findMetaElementByName(name);
1287
- return element
1288
- ? element.getAttribute("content")
1289
- : null;
1501
+ return element ? element.getAttribute("content") : null;
1290
1502
  }
1291
1503
  findMetaElementByName(name) {
1292
1504
  return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
1293
- const { elements: [element] } = this.detailsByOuterHTML[outerHTML];
1505
+ const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
1294
1506
  return elementIsMetaElementWithName(element, name) ? element : result;
1295
1507
  }, undefined);
1296
1508
  }
@@ -1391,6 +1603,9 @@ Copyright © 2021 Basecamp, LLC
1391
1603
  historyChanged: false,
1392
1604
  visitCachedSnapshot: () => { },
1393
1605
  willRender: true,
1606
+ updateHistory: true,
1607
+ shouldCacheSnapshot: true,
1608
+ acceptsStreamResponse: false,
1394
1609
  };
1395
1610
  var SystemStatusCode;
1396
1611
  (function (SystemStatusCode) {
@@ -1405,12 +1620,15 @@ Copyright © 2021 Basecamp, LLC
1405
1620
  this.followedRedirect = false;
1406
1621
  this.historyChanged = false;
1407
1622
  this.scrolled = false;
1623
+ this.shouldCacheSnapshot = true;
1624
+ this.acceptsStreamResponse = false;
1408
1625
  this.snapshotCached = false;
1409
1626
  this.state = VisitState.initialized;
1410
1627
  this.delegate = delegate;
1411
1628
  this.location = location;
1412
1629
  this.restorationIdentifier = restorationIdentifier || uuid();
1413
- const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender } = Object.assign(Object.assign({}, defaultOptions), options);
1630
+ this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
1631
+ const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, defaultOptions), options);
1414
1632
  this.action = action;
1415
1633
  this.historyChanged = historyChanged;
1416
1634
  this.referrer = referrer;
@@ -1419,7 +1637,10 @@ Copyright © 2021 Basecamp, LLC
1419
1637
  this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
1420
1638
  this.visitCachedSnapshot = visitCachedSnapshot;
1421
1639
  this.willRender = willRender;
1640
+ this.updateHistory = updateHistory;
1422
1641
  this.scrolled = !willRender;
1642
+ this.shouldCacheSnapshot = shouldCacheSnapshot;
1643
+ this.acceptsStreamResponse = acceptsStreamResponse;
1423
1644
  }
1424
1645
  get adapter() {
1425
1646
  return this.delegate.adapter;
@@ -1451,28 +1672,33 @@ Copyright © 2021 Basecamp, LLC
1451
1672
  }
1452
1673
  this.cancelRender();
1453
1674
  this.state = VisitState.canceled;
1675
+ this.resolvingFunctions.reject();
1454
1676
  }
1455
1677
  }
1456
1678
  complete() {
1457
1679
  if (this.state == VisitState.started) {
1458
1680
  this.recordTimingMetric(TimingMetric.visitEnd);
1459
1681
  this.state = VisitState.completed;
1460
- this.adapter.visitCompleted(this);
1461
- this.delegate.visitCompleted(this);
1462
1682
  this.followRedirect();
1683
+ if (!this.followedRedirect) {
1684
+ this.adapter.visitCompleted(this);
1685
+ this.delegate.visitCompleted(this);
1686
+ }
1687
+ this.resolvingFunctions.resolve();
1463
1688
  }
1464
1689
  }
1465
1690
  fail() {
1466
1691
  if (this.state == VisitState.started) {
1467
1692
  this.state = VisitState.failed;
1468
1693
  this.adapter.visitFailed(this);
1694
+ this.resolvingFunctions.reject();
1469
1695
  }
1470
1696
  }
1471
1697
  changeHistory() {
1472
1698
  var _a;
1473
- if (!this.historyChanged) {
1699
+ if (!this.historyChanged && this.updateHistory) {
1474
1700
  const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
1475
- const method = this.getHistoryMethodForAction(actionForHistory);
1701
+ const method = getHistoryMethodForAction(actionForHistory);
1476
1702
  this.history.update(method, this.location, this.restorationIdentifier);
1477
1703
  this.historyChanged = true;
1478
1704
  }
@@ -1517,16 +1743,18 @@ Copyright © 2021 Basecamp, LLC
1517
1743
  if (this.response) {
1518
1744
  const { statusCode, responseHTML } = this.response;
1519
1745
  this.render(async () => {
1520
- this.cacheSnapshot();
1746
+ if (this.shouldCacheSnapshot)
1747
+ this.cacheSnapshot();
1521
1748
  if (this.view.renderPromise)
1522
1749
  await this.view.renderPromise;
1523
1750
  if (isSuccessful(statusCode) && responseHTML != null) {
1524
- await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender);
1751
+ await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
1752
+ this.performScroll();
1525
1753
  this.adapter.visitRendered(this);
1526
1754
  this.complete();
1527
1755
  }
1528
1756
  else {
1529
- await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML));
1757
+ await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
1530
1758
  this.adapter.visitRendered(this);
1531
1759
  this.fail();
1532
1760
  }
@@ -1561,7 +1789,8 @@ Copyright © 2021 Basecamp, LLC
1561
1789
  else {
1562
1790
  if (this.view.renderPromise)
1563
1791
  await this.view.renderPromise;
1564
- await this.view.renderPage(snapshot, isPreview, this.willRender);
1792
+ await this.view.renderPage(snapshot, isPreview, this.willRender, this);
1793
+ this.performScroll();
1565
1794
  this.adapter.visitRendered(this);
1566
1795
  if (!isPreview) {
1567
1796
  this.complete();
@@ -1574,8 +1803,9 @@ Copyright © 2021 Basecamp, LLC
1574
1803
  var _a;
1575
1804
  if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
1576
1805
  this.adapter.visitProposedToLocation(this.redirectedToLocation, {
1577
- action: 'replace',
1578
- response: this.response
1806
+ action: "replace",
1807
+ willRender: false,
1808
+ response: this.response,
1579
1809
  });
1580
1810
  this.followedRedirect = true;
1581
1811
  }
@@ -1584,20 +1814,28 @@ Copyright © 2021 Basecamp, LLC
1584
1814
  if (this.isSamePage) {
1585
1815
  this.render(async () => {
1586
1816
  this.cacheSnapshot();
1817
+ this.performScroll();
1587
1818
  this.adapter.visitRendered(this);
1588
1819
  });
1589
1820
  }
1590
1821
  }
1822
+ prepareHeadersForRequest(headers, request) {
1823
+ if (this.acceptsStreamResponse) {
1824
+ request.acceptResponseType(StreamMessage.contentType);
1825
+ }
1826
+ }
1591
1827
  requestStarted() {
1592
1828
  this.startRequest();
1593
1829
  }
1594
- requestPreventedHandlingResponse(request, response) {
1595
- }
1830
+ requestPreventedHandlingResponse(_request, _response) { }
1596
1831
  async requestSucceededWithResponse(request, response) {
1597
1832
  const responseHTML = await response.responseHTML;
1598
1833
  const { redirected, statusCode } = response;
1599
1834
  if (responseHTML == undefined) {
1600
- this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
1835
+ this.recordResponse({
1836
+ statusCode: SystemStatusCode.contentTypeMismatch,
1837
+ redirected,
1838
+ });
1601
1839
  }
1602
1840
  else {
1603
1841
  this.redirectedToLocation = response.redirected ? response.location : undefined;
@@ -1608,20 +1846,26 @@ Copyright © 2021 Basecamp, LLC
1608
1846
  const responseHTML = await response.responseHTML;
1609
1847
  const { redirected, statusCode } = response;
1610
1848
  if (responseHTML == undefined) {
1611
- this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
1849
+ this.recordResponse({
1850
+ statusCode: SystemStatusCode.contentTypeMismatch,
1851
+ redirected,
1852
+ });
1612
1853
  }
1613
1854
  else {
1614
1855
  this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
1615
1856
  }
1616
1857
  }
1617
- requestErrored(request, error) {
1618
- this.recordResponse({ statusCode: SystemStatusCode.networkFailure, redirected: false });
1858
+ requestErrored(_request, _error) {
1859
+ this.recordResponse({
1860
+ statusCode: SystemStatusCode.networkFailure,
1861
+ redirected: false,
1862
+ });
1619
1863
  }
1620
1864
  requestFinished() {
1621
1865
  this.finishRequest();
1622
1866
  }
1623
1867
  performScroll() {
1624
- if (!this.scrolled) {
1868
+ if (!this.scrolled && !this.view.forceReloaded) {
1625
1869
  if (this.action == "restore") {
1626
1870
  this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
1627
1871
  }
@@ -1656,9 +1900,11 @@ Copyright © 2021 Basecamp, LLC
1656
1900
  }
1657
1901
  getHistoryMethodForAction(action) {
1658
1902
  switch (action) {
1659
- case "replace": return history.replaceState;
1903
+ case "replace":
1904
+ return history.replaceState;
1660
1905
  case "advance":
1661
- case "restore": return history.pushState;
1906
+ case "restore":
1907
+ return history.pushState;
1662
1908
  }
1663
1909
  }
1664
1910
  hasPreloadedResponse() {
@@ -1677,18 +1923,17 @@ Copyright © 2021 Basecamp, LLC
1677
1923
  }
1678
1924
  cacheSnapshot() {
1679
1925
  if (!this.snapshotCached) {
1680
- this.view.cacheSnapshot().then(snapshot => snapshot && this.visitCachedSnapshot(snapshot));
1926
+ this.view.cacheSnapshot().then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
1681
1927
  this.snapshotCached = true;
1682
1928
  }
1683
1929
  }
1684
1930
  async render(callback) {
1685
1931
  this.cancelRender();
1686
- await new Promise(resolve => {
1932
+ await new Promise((resolve) => {
1687
1933
  this.frame = requestAnimationFrame(() => resolve());
1688
1934
  });
1689
1935
  await callback();
1690
1936
  delete this.frame;
1691
- this.performScroll();
1692
1937
  }
1693
1938
  cancelRender() {
1694
1939
  if (this.frame) {
@@ -1703,19 +1948,19 @@ Copyright © 2021 Basecamp, LLC
1703
1948
 
1704
1949
  class BrowserAdapter {
1705
1950
  constructor(session) {
1706
- this.progressBar = new ProgressBar;
1951
+ this.progressBar = new ProgressBar();
1707
1952
  this.showProgressBar = () => {
1708
1953
  this.progressBar.show();
1709
1954
  };
1710
1955
  this.session = session;
1711
1956
  }
1712
1957
  visitProposedToLocation(location, options) {
1713
- this.navigator.startVisit(location, uuid(), options);
1958
+ return this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
1714
1959
  }
1715
1960
  visitStarted(visit) {
1961
+ this.location = visit.location;
1716
1962
  visit.loadCachedSnapshot();
1717
1963
  visit.issueRequest();
1718
- visit.changeHistory();
1719
1964
  visit.goToSamePageAnchor();
1720
1965
  }
1721
1966
  visitRequestStarted(visit) {
@@ -1735,29 +1980,31 @@ Copyright © 2021 Basecamp, LLC
1735
1980
  case SystemStatusCode.networkFailure:
1736
1981
  case SystemStatusCode.timeoutFailure:
1737
1982
  case SystemStatusCode.contentTypeMismatch:
1738
- return this.reload();
1983
+ return this.reload({
1984
+ reason: "request_failed",
1985
+ context: {
1986
+ statusCode,
1987
+ },
1988
+ });
1739
1989
  default:
1740
1990
  return visit.loadResponse();
1741
1991
  }
1742
1992
  }
1743
- visitRequestFinished(visit) {
1993
+ visitRequestFinished(_visit) {
1744
1994
  this.progressBar.setValue(1);
1745
1995
  this.hideVisitProgressBar();
1746
1996
  }
1747
- visitCompleted(visit) {
1748
- }
1749
- pageInvalidated() {
1750
- this.reload();
1751
- }
1752
- visitFailed(visit) {
1997
+ visitCompleted(_visit) { }
1998
+ pageInvalidated(reason) {
1999
+ this.reload(reason);
1753
2000
  }
1754
- visitRendered(visit) {
1755
- }
1756
- formSubmissionStarted(formSubmission) {
2001
+ visitFailed(_visit) { }
2002
+ visitRendered(_visit) { }
2003
+ formSubmissionStarted(_formSubmission) {
1757
2004
  this.progressBar.setValue(0);
1758
2005
  this.showFormProgressBarAfterDelay();
1759
2006
  }
1760
- formSubmissionFinished(formSubmission) {
2007
+ formSubmissionFinished(_formSubmission) {
1761
2008
  this.progressBar.setValue(1);
1762
2009
  this.hideFormProgressBar();
1763
2010
  }
@@ -1783,8 +2030,11 @@ Copyright © 2021 Basecamp, LLC
1783
2030
  delete this.formProgressBarTimeout;
1784
2031
  }
1785
2032
  }
1786
- reload() {
1787
- window.location.reload();
2033
+ reload(reason) {
2034
+ dispatch("turbo:reload", { detail: reason });
2035
+ if (!this.location)
2036
+ return;
2037
+ window.location.href = this.location.toString();
1788
2038
  }
1789
2039
  get navigator() {
1790
2040
  return this.session.navigator;
@@ -1794,94 +2044,60 @@ Copyright © 2021 Basecamp, LLC
1794
2044
  class CacheObserver {
1795
2045
  constructor() {
1796
2046
  this.started = false;
1797
- }
1798
- start() {
1799
- if (!this.started) {
1800
- this.started = true;
1801
- addEventListener("turbo:before-cache", this.removeStaleElements, false);
1802
- }
1803
- }
1804
- stop() {
1805
- if (this.started) {
1806
- this.started = false;
1807
- removeEventListener("turbo:before-cache", this.removeStaleElements, false);
1808
- }
1809
- }
1810
- removeStaleElements() {
1811
- const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
1812
- for (const element of staleElements) {
1813
- element.remove();
1814
- }
1815
- }
1816
- }
1817
-
1818
- class FormSubmitObserver {
1819
- constructor(delegate) {
1820
- this.started = false;
1821
- this.submitCaptured = () => {
1822
- removeEventListener("submit", this.submitBubbled, false);
1823
- addEventListener("submit", this.submitBubbled, false);
1824
- };
1825
- this.submitBubbled = ((event) => {
1826
- if (!event.defaultPrevented) {
1827
- const form = event.target instanceof HTMLFormElement ? event.target : undefined;
1828
- const submitter = event.submitter || undefined;
1829
- if (form) {
1830
- const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
1831
- if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
1832
- event.preventDefault();
1833
- this.delegate.formSubmitted(form, submitter);
1834
- }
1835
- }
2047
+ this.removeStaleElements = ((_event) => {
2048
+ const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
2049
+ for (const element of staleElements) {
2050
+ element.remove();
1836
2051
  }
1837
2052
  });
1838
- this.delegate = delegate;
1839
2053
  }
1840
2054
  start() {
1841
2055
  if (!this.started) {
1842
- addEventListener("submit", this.submitCaptured, true);
1843
2056
  this.started = true;
2057
+ addEventListener("turbo:before-cache", this.removeStaleElements, false);
1844
2058
  }
1845
2059
  }
1846
2060
  stop() {
1847
2061
  if (this.started) {
1848
- removeEventListener("submit", this.submitCaptured, true);
1849
2062
  this.started = false;
2063
+ removeEventListener("turbo:before-cache", this.removeStaleElements, false);
1850
2064
  }
1851
2065
  }
1852
2066
  }
1853
2067
 
1854
2068
  class FrameRedirector {
1855
- constructor(element) {
2069
+ constructor(session, element) {
2070
+ this.session = session;
1856
2071
  this.element = element;
1857
- this.linkInterceptor = new LinkInterceptor(this, element);
1858
- this.formInterceptor = new FormInterceptor(this, element);
2072
+ this.linkClickObserver = new LinkClickObserver(this, element);
2073
+ this.formSubmitObserver = new FormSubmitObserver(this, element);
1859
2074
  }
1860
2075
  start() {
1861
- this.linkInterceptor.start();
1862
- this.formInterceptor.start();
2076
+ this.linkClickObserver.start();
2077
+ this.formSubmitObserver.start();
1863
2078
  }
1864
2079
  stop() {
1865
- this.linkInterceptor.stop();
1866
- this.formInterceptor.stop();
2080
+ this.linkClickObserver.stop();
2081
+ this.formSubmitObserver.stop();
1867
2082
  }
1868
- shouldInterceptLinkClick(element, url) {
2083
+ willFollowLinkToLocation(element) {
1869
2084
  return this.shouldRedirect(element);
1870
2085
  }
1871
- linkClickIntercepted(element, url) {
2086
+ followedLinkToLocation(element, url) {
1872
2087
  const frame = this.findFrameElement(element);
1873
2088
  if (frame) {
1874
- frame.delegate.linkClickIntercepted(element, url);
2089
+ frame.delegate.followedLinkToLocation(element, url);
1875
2090
  }
1876
2091
  }
1877
- shouldInterceptFormSubmission(element, submitter) {
1878
- return this.shouldSubmit(element, submitter);
2092
+ willSubmitForm(element, submitter) {
2093
+ return (element.closest("turbo-frame") == null &&
2094
+ this.shouldSubmit(element, submitter) &&
2095
+ this.shouldRedirect(element, submitter));
1879
2096
  }
1880
- formSubmissionIntercepted(element, submitter) {
2097
+ formSubmitted(element, submitter) {
1881
2098
  const frame = this.findFrameElement(element, submitter);
1882
2099
  if (frame) {
1883
- frame.removeAttribute("reloadable");
1884
- frame.delegate.formSubmissionIntercepted(element, submitter);
2100
+ frame.delegate.formSubmitted(element, submitter);
1885
2101
  }
1886
2102
  }
1887
2103
  shouldSubmit(form, submitter) {
@@ -1892,8 +2108,16 @@ Copyright © 2021 Basecamp, LLC
1892
2108
  return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
1893
2109
  }
1894
2110
  shouldRedirect(element, submitter) {
1895
- const frame = this.findFrameElement(element, submitter);
1896
- return frame ? frame != element.closest("turbo-frame") : false;
2111
+ const isNavigatable = element instanceof HTMLFormElement
2112
+ ? this.session.submissionIsNavigatable(element, submitter)
2113
+ : this.session.elementIsNavigatable(element);
2114
+ if (isNavigatable) {
2115
+ const frame = this.findFrameElement(element, submitter);
2116
+ return frame ? frame != element.closest("turbo-frame") : false;
2117
+ }
2118
+ else {
2119
+ return false;
2120
+ }
1897
2121
  }
1898
2122
  findFrameElement(element, submitter) {
1899
2123
  const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
@@ -1923,7 +2147,7 @@ Copyright © 2021 Basecamp, LLC
1923
2147
  }
1924
2148
  }
1925
2149
  };
1926
- this.onPageLoad = async (event) => {
2150
+ this.onPageLoad = async (_event) => {
1927
2151
  await nextMicrotask();
1928
2152
  this.pageLoaded = true;
1929
2153
  };
@@ -1985,81 +2209,30 @@ Copyright © 2021 Basecamp, LLC
1985
2209
  }
1986
2210
  }
1987
2211
 
1988
- class LinkClickObserver {
1989
- constructor(delegate) {
1990
- this.started = false;
1991
- this.clickCaptured = () => {
1992
- removeEventListener("click", this.clickBubbled, false);
1993
- addEventListener("click", this.clickBubbled, false);
1994
- };
1995
- this.clickBubbled = (event) => {
1996
- if (this.clickEventIsSignificant(event)) {
1997
- const target = (event.composedPath && event.composedPath()[0]) || event.target;
1998
- const link = this.findLinkFromClickTarget(target);
1999
- if (link) {
2000
- const location = this.getLocationForLink(link);
2001
- if (this.delegate.willFollowLinkToLocation(link, location)) {
2002
- event.preventDefault();
2003
- this.delegate.followedLinkToLocation(link, location);
2004
- }
2005
- }
2006
- }
2007
- };
2008
- this.delegate = delegate;
2009
- }
2010
- start() {
2011
- if (!this.started) {
2012
- addEventListener("click", this.clickCaptured, true);
2013
- this.started = true;
2014
- }
2015
- }
2016
- stop() {
2017
- if (this.started) {
2018
- removeEventListener("click", this.clickCaptured, true);
2019
- this.started = false;
2020
- }
2021
- }
2022
- clickEventIsSignificant(event) {
2023
- return !((event.target && event.target.isContentEditable)
2024
- || event.defaultPrevented
2025
- || event.which > 1
2026
- || event.altKey
2027
- || event.ctrlKey
2028
- || event.metaKey
2029
- || event.shiftKey);
2030
- }
2031
- findLinkFromClickTarget(target) {
2032
- if (target instanceof Element) {
2033
- return target.closest("a[href]:not([target^=_]):not([download])");
2034
- }
2035
- }
2036
- getLocationForLink(link) {
2037
- return expandURL(link.getAttribute("href") || "");
2038
- }
2039
- }
2040
-
2041
- function isAction(action) {
2042
- return action == "advance" || action == "replace" || action == "restore";
2043
- }
2044
-
2045
- class Navigator {
2212
+ class Navigator {
2046
2213
  constructor(delegate) {
2047
2214
  this.delegate = delegate;
2048
2215
  }
2049
2216
  proposeVisit(location, options = {}) {
2050
2217
  if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
2051
2218
  if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
2052
- this.delegate.visitProposedToLocation(location, options);
2219
+ return this.delegate.visitProposedToLocation(location, options);
2053
2220
  }
2054
2221
  else {
2055
2222
  window.location.href = location.toString();
2223
+ return Promise.resolve();
2056
2224
  }
2057
2225
  }
2226
+ else {
2227
+ return Promise.reject();
2228
+ }
2058
2229
  }
2059
2230
  startVisit(locatable, restorationIdentifier, options = {}) {
2231
+ this.lastVisit = this.currentVisit;
2060
2232
  this.stop();
2061
2233
  this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
2062
2234
  this.currentVisit.start();
2235
+ return this.currentVisit.promise;
2063
2236
  }
2064
2237
  submitForm(form, submitter) {
2065
2238
  this.stop();
@@ -2086,7 +2259,7 @@ Copyright © 2021 Basecamp, LLC
2086
2259
  return this.delegate.history;
2087
2260
  }
2088
2261
  formSubmissionStarted(formSubmission) {
2089
- if (typeof this.adapter.formSubmissionStarted === 'function') {
2262
+ if (typeof this.adapter.formSubmissionStarted === "function") {
2090
2263
  this.adapter.formSubmissionStarted(formSubmission);
2091
2264
  }
2092
2265
  }
@@ -2094,12 +2267,17 @@ Copyright © 2021 Basecamp, LLC
2094
2267
  if (formSubmission == this.formSubmission) {
2095
2268
  const responseHTML = await fetchResponse.responseHTML;
2096
2269
  if (responseHTML) {
2097
- if (formSubmission.method != FetchMethod.get) {
2270
+ const shouldCacheSnapshot = formSubmission.method == FetchMethod.get;
2271
+ if (!shouldCacheSnapshot) {
2098
2272
  this.view.clearSnapshotCache();
2099
2273
  }
2100
2274
  const { statusCode, redirected } = fetchResponse;
2101
2275
  const action = this.getActionForFormSubmission(formSubmission);
2102
- const visitOptions = { action, response: { statusCode, responseHTML, redirected } };
2276
+ const visitOptions = {
2277
+ action,
2278
+ shouldCacheSnapshot,
2279
+ response: { statusCode, responseHTML, redirected },
2280
+ };
2103
2281
  this.proposeVisit(fetchResponse.location, visitOptions);
2104
2282
  }
2105
2283
  }
@@ -2109,10 +2287,10 @@ Copyright © 2021 Basecamp, LLC
2109
2287
  if (responseHTML) {
2110
2288
  const snapshot = PageSnapshot.fromHTMLString(responseHTML);
2111
2289
  if (fetchResponse.serverError) {
2112
- await this.view.renderError(snapshot);
2290
+ await this.view.renderError(snapshot, this.currentVisit);
2113
2291
  }
2114
2292
  else {
2115
- await this.view.renderPage(snapshot);
2293
+ await this.view.renderPage(snapshot, false, true, this.currentVisit);
2116
2294
  }
2117
2295
  this.view.scrollToTop();
2118
2296
  this.view.clearSnapshotCache();
@@ -2122,7 +2300,7 @@ Copyright © 2021 Basecamp, LLC
2122
2300
  console.error(error);
2123
2301
  }
2124
2302
  formSubmissionFinished(formSubmission) {
2125
- if (typeof this.adapter.formSubmissionFinished === 'function') {
2303
+ if (typeof this.adapter.formSubmissionFinished === "function") {
2126
2304
  this.adapter.formSubmissionFinished(formSubmission);
2127
2305
  }
2128
2306
  }
@@ -2133,12 +2311,14 @@ Copyright © 2021 Basecamp, LLC
2133
2311
  this.delegate.visitCompleted(visit);
2134
2312
  }
2135
2313
  locationWithActionIsSamePage(location, action) {
2314
+ var _a;
2136
2315
  const anchor = getAnchor(location);
2137
- const currentAnchor = getAnchor(this.view.lastRenderedLocation);
2138
- const isRestorationToTop = action === 'restore' && typeof anchor === 'undefined';
2139
- return action !== "replace" &&
2140
- getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
2141
- (isRestorationToTop || (anchor != null && anchor !== currentAnchor));
2316
+ const lastLocation = ((_a = this.lastVisit) === null || _a === void 0 ? void 0 : _a.location) || this.view.lastRenderedLocation;
2317
+ const currentAnchor = getAnchor(lastLocation);
2318
+ const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
2319
+ return (action !== "replace" &&
2320
+ getRequestURL(location) === getRequestURL(lastLocation) &&
2321
+ (isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
2142
2322
  }
2143
2323
  visitScrolledToSamePageLocation(oldURL, newURL) {
2144
2324
  this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
@@ -2244,7 +2424,7 @@ Copyright © 2021 Basecamp, LLC
2244
2424
 
2245
2425
  class StreamObserver {
2246
2426
  constructor(delegate) {
2247
- this.sources = new Set;
2427
+ this.sources = new Set();
2248
2428
  this.started = false;
2249
2429
  this.inspectFetchResponse = ((event) => {
2250
2430
  const response = fetchResponseFromEvent(event);
@@ -2294,7 +2474,7 @@ Copyright © 2021 Basecamp, LLC
2294
2474
  }
2295
2475
  }
2296
2476
  receiveMessageHTML(html) {
2297
- this.delegate.receivedMessageFromStream(new StreamMessage(html));
2477
+ this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
2298
2478
  }
2299
2479
  }
2300
2480
  function fetchResponseFromEvent(event) {
@@ -2311,20 +2491,24 @@ Copyright © 2021 Basecamp, LLC
2311
2491
  }
2312
2492
 
2313
2493
  class ErrorRenderer extends Renderer {
2494
+ static renderElement(currentElement, newElement) {
2495
+ const { documentElement, body } = document;
2496
+ documentElement.replaceChild(newElement, body);
2497
+ }
2314
2498
  async render() {
2315
2499
  this.replaceHeadAndBody();
2316
2500
  this.activateScriptElements();
2317
2501
  }
2318
2502
  replaceHeadAndBody() {
2319
- const { documentElement, head, body } = document;
2503
+ const { documentElement, head } = document;
2320
2504
  documentElement.replaceChild(this.newHead, head);
2321
- documentElement.replaceChild(this.newElement, body);
2505
+ this.renderElement(this.currentElement, this.newElement);
2322
2506
  }
2323
2507
  activateScriptElements() {
2324
2508
  for (const replaceableElement of this.scriptElements) {
2325
2509
  const parentNode = replaceableElement.parentNode;
2326
2510
  if (parentNode) {
2327
- const element = this.createScriptElement(replaceableElement);
2511
+ const element = activateScriptElement(replaceableElement);
2328
2512
  parentNode.replaceChild(element, replaceableElement);
2329
2513
  }
2330
2514
  }
@@ -2333,16 +2517,36 @@ Copyright © 2021 Basecamp, LLC
2333
2517
  return this.newSnapshot.headSnapshot.element;
2334
2518
  }
2335
2519
  get scriptElements() {
2336
- return [...document.documentElement.querySelectorAll("script")];
2520
+ return document.documentElement.querySelectorAll("script");
2337
2521
  }
2338
2522
  }
2339
2523
 
2340
2524
  class PageRenderer extends Renderer {
2525
+ static renderElement(currentElement, newElement) {
2526
+ if (document.body && newElement instanceof HTMLBodyElement) {
2527
+ document.body.replaceWith(newElement);
2528
+ }
2529
+ else {
2530
+ document.documentElement.appendChild(newElement);
2531
+ }
2532
+ }
2341
2533
  get shouldRender() {
2342
2534
  return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
2343
2535
  }
2344
- prepareToRender() {
2345
- this.mergeHead();
2536
+ get reloadReason() {
2537
+ if (!this.newSnapshot.isVisitable) {
2538
+ return {
2539
+ reason: "turbo_visit_control_is_reload",
2540
+ };
2541
+ }
2542
+ if (!this.trackedElementsAreIdentical) {
2543
+ return {
2544
+ reason: "tracked_element_mismatch",
2545
+ };
2546
+ }
2547
+ }
2548
+ async prepareToRender() {
2549
+ await this.mergeHead();
2346
2550
  }
2347
2551
  async render() {
2348
2552
  if (this.willRender) {
@@ -2364,11 +2568,12 @@ Copyright © 2021 Basecamp, LLC
2364
2568
  get newElement() {
2365
2569
  return this.newSnapshot.element;
2366
2570
  }
2367
- mergeHead() {
2368
- this.copyNewHeadStylesheetElements();
2571
+ async mergeHead() {
2572
+ const newStylesheetElements = this.copyNewHeadStylesheetElements();
2369
2573
  this.copyNewHeadScriptElements();
2370
2574
  this.removeCurrentHeadProvisionalElements();
2371
2575
  this.copyNewHeadProvisionalElements();
2576
+ await newStylesheetElements;
2372
2577
  }
2373
2578
  replaceBody() {
2374
2579
  this.preservingPermanentElements(() => {
@@ -2379,14 +2584,17 @@ Copyright © 2021 Basecamp, LLC
2379
2584
  get trackedElementsAreIdentical() {
2380
2585
  return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
2381
2586
  }
2382
- copyNewHeadStylesheetElements() {
2587
+ async copyNewHeadStylesheetElements() {
2588
+ const loadingElements = [];
2383
2589
  for (const element of this.newHeadStylesheetElements) {
2590
+ loadingElements.push(waitForLoad(element));
2384
2591
  document.head.appendChild(element);
2385
2592
  }
2593
+ await Promise.all(loadingElements);
2386
2594
  }
2387
2595
  copyNewHeadScriptElements() {
2388
2596
  for (const element of this.newHeadScriptElements) {
2389
- document.head.appendChild(this.createScriptElement(element));
2597
+ document.head.appendChild(activateScriptElement(element));
2390
2598
  }
2391
2599
  }
2392
2600
  removeCurrentHeadProvisionalElements() {
@@ -2405,17 +2613,12 @@ Copyright © 2021 Basecamp, LLC
2405
2613
  }
2406
2614
  activateNewBodyScriptElements() {
2407
2615
  for (const inertScriptElement of this.newBodyScriptElements) {
2408
- const activatedScriptElement = this.createScriptElement(inertScriptElement);
2616
+ const activatedScriptElement = activateScriptElement(inertScriptElement);
2409
2617
  inertScriptElement.replaceWith(activatedScriptElement);
2410
2618
  }
2411
2619
  }
2412
2620
  assignNewBody() {
2413
- if (document.body && this.newElement instanceof HTMLBodyElement) {
2414
- document.body.replaceWith(this.newElement);
2415
- }
2416
- else {
2417
- document.documentElement.appendChild(this.newElement);
2418
- }
2621
+ this.renderElement(this.currentElement, this.newElement);
2419
2622
  }
2420
2623
  get newHeadStylesheetElements() {
2421
2624
  return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
@@ -2484,13 +2687,21 @@ Copyright © 2021 Basecamp, LLC
2484
2687
  super(...arguments);
2485
2688
  this.snapshotCache = new SnapshotCache(10);
2486
2689
  this.lastRenderedLocation = new URL(location.href);
2690
+ this.forceReloaded = false;
2487
2691
  }
2488
- renderPage(snapshot, isPreview = false, willRender = true) {
2489
- const renderer = new PageRenderer(this.snapshot, snapshot, isPreview, willRender);
2692
+ renderPage(snapshot, isPreview = false, willRender = true, visit) {
2693
+ const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
2694
+ if (!renderer.shouldRender) {
2695
+ this.forceReloaded = true;
2696
+ }
2697
+ else {
2698
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
2699
+ }
2490
2700
  return this.render(renderer);
2491
2701
  }
2492
- renderError(snapshot) {
2493
- const renderer = new ErrorRenderer(this.snapshot, snapshot, false);
2702
+ renderError(snapshot, visit) {
2703
+ visit === null || visit === void 0 ? void 0 : visit.changeHistory();
2704
+ const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
2494
2705
  return this.render(renderer);
2495
2706
  }
2496
2707
  clearSnapshotCache() {
@@ -2517,34 +2728,78 @@ Copyright © 2021 Basecamp, LLC
2517
2728
  }
2518
2729
  }
2519
2730
 
2731
+ class Preloader {
2732
+ constructor(delegate) {
2733
+ this.selector = "a[data-turbo-preload]";
2734
+ this.delegate = delegate;
2735
+ }
2736
+ get snapshotCache() {
2737
+ return this.delegate.navigator.view.snapshotCache;
2738
+ }
2739
+ start() {
2740
+ if (document.readyState === "loading") {
2741
+ return document.addEventListener("DOMContentLoaded", () => {
2742
+ this.preloadOnLoadLinksForView(document.body);
2743
+ });
2744
+ }
2745
+ else {
2746
+ this.preloadOnLoadLinksForView(document.body);
2747
+ }
2748
+ }
2749
+ preloadOnLoadLinksForView(element) {
2750
+ for (const link of element.querySelectorAll(this.selector)) {
2751
+ this.preloadURL(link);
2752
+ }
2753
+ }
2754
+ async preloadURL(link) {
2755
+ const location = new URL(link.href);
2756
+ if (this.snapshotCache.has(location)) {
2757
+ return;
2758
+ }
2759
+ try {
2760
+ const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
2761
+ const responseText = await response.text();
2762
+ const snapshot = PageSnapshot.fromHTMLString(responseText);
2763
+ this.snapshotCache.put(location, snapshot);
2764
+ }
2765
+ catch (_) {
2766
+ }
2767
+ }
2768
+ }
2769
+
2520
2770
  class Session {
2521
2771
  constructor() {
2522
2772
  this.navigator = new Navigator(this);
2523
2773
  this.history = new History(this);
2774
+ this.preloader = new Preloader(this);
2524
2775
  this.view = new PageView(this, document.documentElement);
2525
2776
  this.adapter = new BrowserAdapter(this);
2526
2777
  this.pageObserver = new PageObserver(this);
2527
2778
  this.cacheObserver = new CacheObserver();
2528
- this.linkClickObserver = new LinkClickObserver(this);
2529
- this.formSubmitObserver = new FormSubmitObserver(this);
2779
+ this.linkClickObserver = new LinkClickObserver(this, window);
2780
+ this.formSubmitObserver = new FormSubmitObserver(this, document);
2530
2781
  this.scrollObserver = new ScrollObserver(this);
2531
2782
  this.streamObserver = new StreamObserver(this);
2532
- this.frameRedirector = new FrameRedirector(document.documentElement);
2783
+ this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
2784
+ this.frameRedirector = new FrameRedirector(this, document.documentElement);
2533
2785
  this.drive = true;
2534
2786
  this.enabled = true;
2535
2787
  this.progressBarDelay = 500;
2536
2788
  this.started = false;
2789
+ this.formMode = "on";
2537
2790
  }
2538
2791
  start() {
2539
2792
  if (!this.started) {
2540
2793
  this.pageObserver.start();
2541
2794
  this.cacheObserver.start();
2795
+ this.formLinkClickObserver.start();
2542
2796
  this.linkClickObserver.start();
2543
2797
  this.formSubmitObserver.start();
2544
2798
  this.scrollObserver.start();
2545
2799
  this.streamObserver.start();
2546
2800
  this.frameRedirector.start();
2547
2801
  this.history.start();
2802
+ this.preloader.start();
2548
2803
  this.started = true;
2549
2804
  this.enabled = true;
2550
2805
  }
@@ -2556,6 +2811,7 @@ Copyright © 2021 Basecamp, LLC
2556
2811
  if (this.started) {
2557
2812
  this.pageObserver.stop();
2558
2813
  this.cacheObserver.stop();
2814
+ this.formLinkClickObserver.stop();
2559
2815
  this.linkClickObserver.stop();
2560
2816
  this.formSubmitObserver.stop();
2561
2817
  this.scrollObserver.stop();
@@ -2569,7 +2825,14 @@ Copyright © 2021 Basecamp, LLC
2569
2825
  this.adapter = adapter;
2570
2826
  }
2571
2827
  visit(location, options = {}) {
2572
- this.navigator.proposeVisit(expandURL(location), options);
2828
+ const frameElement = document.getElementById(options.frame || "");
2829
+ if (frameElement instanceof FrameElement) {
2830
+ frameElement.src = location.toString();
2831
+ return frameElement.loaded;
2832
+ }
2833
+ else {
2834
+ return this.navigator.proposeVisit(expandURL(location), options);
2835
+ }
2573
2836
  }
2574
2837
  connectStreamSource(source) {
2575
2838
  this.streamObserver.connectStreamSource(source);
@@ -2586,6 +2849,9 @@ Copyright © 2021 Basecamp, LLC
2586
2849
  setProgressBarDelay(delay) {
2587
2850
  this.progressBarDelay = delay;
2588
2851
  }
2852
+ setFormMode(mode) {
2853
+ this.formMode = mode;
2854
+ }
2589
2855
  get location() {
2590
2856
  return this.history.location;
2591
2857
  }
@@ -2594,55 +2860,40 @@ Copyright © 2021 Basecamp, LLC
2594
2860
  }
2595
2861
  historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
2596
2862
  if (this.enabled) {
2597
- this.navigator.startVisit(location, restorationIdentifier, { action: "restore", historyChanged: true });
2863
+ this.navigator.startVisit(location, restorationIdentifier, {
2864
+ action: "restore",
2865
+ historyChanged: true,
2866
+ });
2598
2867
  }
2599
2868
  else {
2600
- this.adapter.pageInvalidated();
2869
+ this.adapter.pageInvalidated({
2870
+ reason: "turbo_disabled",
2871
+ });
2601
2872
  }
2602
2873
  }
2603
2874
  scrollPositionChanged(position) {
2604
2875
  this.history.updateRestorationData({ scrollPosition: position });
2605
2876
  }
2606
- willFollowLinkToLocation(link, location) {
2607
- return this.elementDriveEnabled(link)
2608
- && locationIsVisitable(location, this.snapshot.rootLocation)
2609
- && this.applicationAllowsFollowingLinkToLocation(link, location);
2877
+ willSubmitFormLinkToLocation(link, location) {
2878
+ return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
2879
+ }
2880
+ submittedFormLinkToLocation() { }
2881
+ willFollowLinkToLocation(link, location, event) {
2882
+ return (this.elementIsNavigatable(link) &&
2883
+ locationIsVisitable(location, this.snapshot.rootLocation) &&
2884
+ this.applicationAllowsFollowingLinkToLocation(link, location, event));
2610
2885
  }
2611
2886
  followedLinkToLocation(link, location) {
2612
2887
  const action = this.getActionForLink(link);
2613
- this.convertLinkWithMethodClickToFormSubmission(link) || this.visit(location.href, { action });
2614
- }
2615
- convertLinkWithMethodClickToFormSubmission(link) {
2616
- const linkMethod = link.getAttribute("data-turbo-method");
2617
- if (linkMethod) {
2618
- const form = document.createElement("form");
2619
- form.method = linkMethod;
2620
- form.action = link.getAttribute("href") || "undefined";
2621
- form.hidden = true;
2622
- if (link.hasAttribute("data-turbo-confirm")) {
2623
- form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
2624
- }
2625
- const frame = this.getTargetFrameForLink(link);
2626
- if (frame) {
2627
- form.setAttribute("data-turbo-frame", frame);
2628
- form.addEventListener("turbo:submit-start", () => form.remove());
2629
- }
2630
- else {
2631
- form.addEventListener("submit", () => form.remove());
2632
- }
2633
- document.body.appendChild(form);
2634
- return dispatch("submit", { cancelable: true, target: form });
2635
- }
2636
- else {
2637
- return false;
2638
- }
2888
+ const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
2889
+ this.visit(location.href, { action, acceptsStreamResponse });
2639
2890
  }
2640
2891
  allowsVisitingLocationWithAction(location, action) {
2641
2892
  return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
2642
2893
  }
2643
2894
  visitProposedToLocation(location, options) {
2644
2895
  extendURLWithDeprecatedProperties(location);
2645
- this.adapter.visitProposedToLocation(location, options);
2896
+ return this.adapter.visitProposedToLocation(location, options);
2646
2897
  }
2647
2898
  visitStarted(visit) {
2648
2899
  extendURLWithDeprecatedProperties(visit.location);
@@ -2661,9 +2912,8 @@ Copyright © 2021 Basecamp, LLC
2661
2912
  }
2662
2913
  willSubmitForm(form, submitter) {
2663
2914
  const action = getAction(form, submitter);
2664
- return this.elementDriveEnabled(form)
2665
- && (!submitter || this.elementDriveEnabled(submitter))
2666
- && locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
2915
+ return (this.submissionIsNavigatable(form, submitter) &&
2916
+ locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
2667
2917
  }
2668
2918
  formSubmitted(form, submitter) {
2669
2919
  this.navigator.submitForm(form, submitter);
@@ -2687,16 +2937,23 @@ Copyright © 2021 Basecamp, LLC
2687
2937
  this.notifyApplicationBeforeCachingSnapshot();
2688
2938
  }
2689
2939
  }
2690
- allowsImmediateRender({ element }, resume) {
2691
- const event = this.notifyApplicationBeforeRender(element, resume);
2692
- return !event.defaultPrevented;
2940
+ allowsImmediateRender({ element }, options) {
2941
+ const event = this.notifyApplicationBeforeRender(element, options);
2942
+ const { defaultPrevented, detail: { render }, } = event;
2943
+ if (this.view.renderer && render) {
2944
+ this.view.renderer.renderElement = render;
2945
+ }
2946
+ return !defaultPrevented;
2693
2947
  }
2694
- viewRenderedSnapshot(snapshot, isPreview) {
2948
+ viewRenderedSnapshot(_snapshot, _isPreview) {
2695
2949
  this.view.lastRenderedLocation = this.history.location;
2696
2950
  this.notifyApplicationAfterRender();
2697
2951
  }
2698
- viewInvalidated() {
2699
- this.adapter.pageInvalidated();
2952
+ preloadOnLoadLinksForView(element) {
2953
+ this.preloader.preloadOnLoadLinksForView(element);
2954
+ }
2955
+ viewInvalidated(reason) {
2956
+ this.adapter.pageInvalidated(reason);
2700
2957
  }
2701
2958
  frameLoaded(frame) {
2702
2959
  this.notifyApplicationAfterFrameLoad(frame);
@@ -2704,19 +2961,30 @@ Copyright © 2021 Basecamp, LLC
2704
2961
  frameRendered(fetchResponse, frame) {
2705
2962
  this.notifyApplicationAfterFrameRender(fetchResponse, frame);
2706
2963
  }
2707
- applicationAllowsFollowingLinkToLocation(link, location) {
2708
- const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
2964
+ frameMissing(frame, fetchResponse) {
2965
+ console.warn(`Completing full-page visit as matching frame for #${frame.id} was missing from the response`);
2966
+ return this.visit(fetchResponse.location);
2967
+ }
2968
+ applicationAllowsFollowingLinkToLocation(link, location, ev) {
2969
+ const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
2709
2970
  return !event.defaultPrevented;
2710
2971
  }
2711
2972
  applicationAllowsVisitingLocation(location) {
2712
2973
  const event = this.notifyApplicationBeforeVisitingLocation(location);
2713
2974
  return !event.defaultPrevented;
2714
2975
  }
2715
- notifyApplicationAfterClickingLinkToLocation(link, location) {
2716
- return dispatch("turbo:click", { target: link, detail: { url: location.href }, cancelable: true });
2976
+ notifyApplicationAfterClickingLinkToLocation(link, location, event) {
2977
+ return dispatch("turbo:click", {
2978
+ target: link,
2979
+ detail: { url: location.href, originalEvent: event },
2980
+ cancelable: true,
2981
+ });
2717
2982
  }
2718
2983
  notifyApplicationBeforeVisitingLocation(location) {
2719
- return dispatch("turbo:before-visit", { detail: { url: location.href }, cancelable: true });
2984
+ return dispatch("turbo:before-visit", {
2985
+ detail: { url: location.href },
2986
+ cancelable: true,
2987
+ });
2720
2988
  }
2721
2989
  notifyApplicationAfterVisitingLocation(location, action) {
2722
2990
  markAsBusy(document.documentElement);
@@ -2725,28 +2993,55 @@ Copyright © 2021 Basecamp, LLC
2725
2993
  notifyApplicationBeforeCachingSnapshot() {
2726
2994
  return dispatch("turbo:before-cache");
2727
2995
  }
2728
- notifyApplicationBeforeRender(newBody, resume) {
2729
- return dispatch("turbo:before-render", { detail: { newBody, resume }, cancelable: true });
2996
+ notifyApplicationBeforeRender(newBody, options) {
2997
+ return dispatch("turbo:before-render", {
2998
+ detail: Object.assign({ newBody }, options),
2999
+ cancelable: true,
3000
+ });
2730
3001
  }
2731
3002
  notifyApplicationAfterRender() {
2732
3003
  return dispatch("turbo:render");
2733
3004
  }
2734
3005
  notifyApplicationAfterPageLoad(timing = {}) {
2735
3006
  clearBusyState(document.documentElement);
2736
- return dispatch("turbo:load", { detail: { url: this.location.href, timing } });
3007
+ return dispatch("turbo:load", {
3008
+ detail: { url: this.location.href, timing },
3009
+ });
2737
3010
  }
2738
3011
  notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
2739
- dispatchEvent(new HashChangeEvent("hashchange", { oldURL: oldURL.toString(), newURL: newURL.toString() }));
3012
+ dispatchEvent(new HashChangeEvent("hashchange", {
3013
+ oldURL: oldURL.toString(),
3014
+ newURL: newURL.toString(),
3015
+ }));
2740
3016
  }
2741
3017
  notifyApplicationAfterFrameLoad(frame) {
2742
3018
  return dispatch("turbo:frame-load", { target: frame });
2743
3019
  }
2744
3020
  notifyApplicationAfterFrameRender(fetchResponse, frame) {
2745
- return dispatch("turbo:frame-render", { detail: { fetchResponse }, target: frame, cancelable: true });
3021
+ return dispatch("turbo:frame-render", {
3022
+ detail: { fetchResponse },
3023
+ target: frame,
3024
+ cancelable: true,
3025
+ });
3026
+ }
3027
+ submissionIsNavigatable(form, submitter) {
3028
+ if (this.formMode == "off") {
3029
+ return false;
3030
+ }
3031
+ else {
3032
+ const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
3033
+ if (this.formMode == "optin") {
3034
+ return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
3035
+ }
3036
+ else {
3037
+ return submitterIsNavigatable && this.elementIsNavigatable(form);
3038
+ }
3039
+ }
2746
3040
  }
2747
- elementDriveEnabled(element) {
2748
- const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
2749
- if (this.drive) {
3041
+ elementIsNavigatable(element) {
3042
+ const container = element.closest("[data-turbo]");
3043
+ const withinFrame = element.closest("turbo-frame");
3044
+ if (this.drive || withinFrame) {
2750
3045
  if (container) {
2751
3046
  return container.getAttribute("data-turbo") != "false";
2752
3047
  }
@@ -2767,18 +3062,6 @@ Copyright © 2021 Basecamp, LLC
2767
3062
  const action = link.getAttribute("data-turbo-action");
2768
3063
  return isAction(action) ? action : "advance";
2769
3064
  }
2770
- getTargetFrameForLink(link) {
2771
- const frame = link.getAttribute("data-turbo-frame");
2772
- if (frame) {
2773
- return frame;
2774
- }
2775
- else {
2776
- const container = link.closest("turbo-frame");
2777
- if (container) {
2778
- return container.id;
2779
- }
2780
- }
2781
- }
2782
3065
  get snapshot() {
2783
3066
  return this.view.snapshot;
2784
3067
  }
@@ -2790,11 +3073,59 @@ Copyright © 2021 Basecamp, LLC
2790
3073
  absoluteURL: {
2791
3074
  get() {
2792
3075
  return this.toString();
2793
- }
3076
+ },
3077
+ },
3078
+ };
3079
+
3080
+ class Cache {
3081
+ constructor(session) {
3082
+ this.session = session;
3083
+ }
3084
+ clear() {
3085
+ this.session.clearCache();
2794
3086
  }
3087
+ resetCacheControl() {
3088
+ this.setCacheControl("");
3089
+ }
3090
+ exemptPageFromCache() {
3091
+ this.setCacheControl("no-cache");
3092
+ }
3093
+ exemptPageFromPreview() {
3094
+ this.setCacheControl("no-preview");
3095
+ }
3096
+ setCacheControl(value) {
3097
+ setMetaContent("turbo-cache-control", value);
3098
+ }
3099
+ }
3100
+
3101
+ const StreamActions = {
3102
+ after() {
3103
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
3104
+ },
3105
+ append() {
3106
+ this.removeDuplicateTargetChildren();
3107
+ this.targetElements.forEach((e) => e.append(this.templateContent));
3108
+ },
3109
+ before() {
3110
+ this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
3111
+ },
3112
+ prepend() {
3113
+ this.removeDuplicateTargetChildren();
3114
+ this.targetElements.forEach((e) => e.prepend(this.templateContent));
3115
+ },
3116
+ remove() {
3117
+ this.targetElements.forEach((e) => e.remove());
3118
+ },
3119
+ replace() {
3120
+ this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
3121
+ },
3122
+ update() {
3123
+ this.targetElements.forEach((e) => e.replaceChildren(this.templateContent));
3124
+ },
2795
3125
  };
2796
3126
 
2797
- const session = new Session;
3127
+ const session = new Session();
3128
+ const cache = new Cache(session);
2798
3129
  const { navigator: navigator$1 } = session;
2799
3130
  function start() {
2800
3131
  session.start();
@@ -2803,7 +3134,7 @@ Copyright © 2021 Basecamp, LLC
2803
3134
  session.registerAdapter(adapter);
2804
3135
  }
2805
3136
  function visit(location, options) {
2806
- session.visit(location, options);
3137
+ return session.visit(location, options);
2807
3138
  }
2808
3139
  function connectStreamSource(source) {
2809
3140
  session.connectStreamSource(source);
@@ -2815,6 +3146,7 @@ Copyright © 2021 Basecamp, LLC
2815
3146
  session.renderStreamMessage(message);
2816
3147
  }
2817
3148
  function clearCache() {
3149
+ 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.`");
2818
3150
  session.clearCache();
2819
3151
  }
2820
3152
  function setProgressBarDelay(delay) {
@@ -2823,13 +3155,18 @@ Copyright © 2021 Basecamp, LLC
2823
3155
  function setConfirmMethod(confirmMethod) {
2824
3156
  FormSubmission.confirmMethod = confirmMethod;
2825
3157
  }
3158
+ function setFormMode(mode) {
3159
+ session.setFormMode(mode);
3160
+ }
2826
3161
 
2827
3162
  var Turbo = /*#__PURE__*/Object.freeze({
2828
3163
  __proto__: null,
2829
3164
  navigator: navigator$1,
2830
3165
  session: session,
3166
+ cache: cache,
2831
3167
  PageRenderer: PageRenderer,
2832
3168
  PageSnapshot: PageSnapshot,
3169
+ FrameRenderer: FrameRenderer,
2833
3170
  start: start,
2834
3171
  registerAdapter: registerAdapter,
2835
3172
  visit: visit,
@@ -2838,41 +3175,56 @@ Copyright © 2021 Basecamp, LLC
2838
3175
  renderStreamMessage: renderStreamMessage,
2839
3176
  clearCache: clearCache,
2840
3177
  setProgressBarDelay: setProgressBarDelay,
2841
- setConfirmMethod: setConfirmMethod
3178
+ setConfirmMethod: setConfirmMethod,
3179
+ setFormMode: setFormMode,
3180
+ StreamActions: StreamActions
2842
3181
  });
2843
3182
 
2844
3183
  class FrameController {
2845
3184
  constructor(element) {
2846
- this.fetchResponseLoaded = (fetchResponse) => { };
3185
+ this.fetchResponseLoaded = (_fetchResponse) => { };
2847
3186
  this.currentFetchRequest = null;
2848
3187
  this.resolveVisitPromise = () => { };
2849
3188
  this.connected = false;
2850
3189
  this.hasBeenLoaded = false;
2851
- this.settingSourceURL = false;
3190
+ this.ignoredAttributes = new Set();
3191
+ this.action = null;
3192
+ this.visitCachedSnapshot = ({ element }) => {
3193
+ const frame = element.querySelector("#" + this.element.id);
3194
+ if (frame && this.previousFrameElement) {
3195
+ frame.replaceChildren(...this.previousFrameElement.children);
3196
+ }
3197
+ delete this.previousFrameElement;
3198
+ };
2852
3199
  this.element = element;
2853
3200
  this.view = new FrameView(this, this.element);
2854
3201
  this.appearanceObserver = new AppearanceObserver(this, this.element);
2855
- this.linkInterceptor = new LinkInterceptor(this, this.element);
2856
- this.formInterceptor = new FormInterceptor(this, this.element);
3202
+ this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
3203
+ this.linkClickObserver = new LinkClickObserver(this, this.element);
3204
+ this.restorationIdentifier = uuid();
3205
+ this.formSubmitObserver = new FormSubmitObserver(this, this.element);
2857
3206
  }
2858
3207
  connect() {
2859
3208
  if (!this.connected) {
2860
3209
  this.connected = true;
2861
- this.reloadable = false;
2862
3210
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
2863
3211
  this.appearanceObserver.start();
2864
3212
  }
2865
- this.linkInterceptor.start();
2866
- this.formInterceptor.start();
2867
- this.sourceURLChanged();
3213
+ else {
3214
+ this.loadSourceURL();
3215
+ }
3216
+ this.formLinkClickObserver.start();
3217
+ this.linkClickObserver.start();
3218
+ this.formSubmitObserver.start();
2868
3219
  }
2869
3220
  }
2870
3221
  disconnect() {
2871
3222
  if (this.connected) {
2872
3223
  this.connected = false;
2873
3224
  this.appearanceObserver.stop();
2874
- this.linkInterceptor.stop();
2875
- this.formInterceptor.stop();
3225
+ this.formLinkClickObserver.stop();
3226
+ this.linkClickObserver.stop();
3227
+ this.formSubmitObserver.stop();
2876
3228
  }
2877
3229
  }
2878
3230
  disabledChanged() {
@@ -2881,10 +3233,20 @@ Copyright © 2021 Basecamp, LLC
2881
3233
  }
2882
3234
  }
2883
3235
  sourceURLChanged() {
3236
+ if (this.isIgnoringChangesTo("src"))
3237
+ return;
3238
+ if (this.element.isConnected) {
3239
+ this.complete = false;
3240
+ }
2884
3241
  if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
2885
3242
  this.loadSourceURL();
2886
3243
  }
2887
3244
  }
3245
+ completeChanged() {
3246
+ if (this.isIgnoringChangesTo("complete"))
3247
+ return;
3248
+ this.loadSourceURL();
3249
+ }
2888
3250
  loadingStyleChanged() {
2889
3251
  if (this.loadingStyle == FrameLoadingStyle.lazy) {
2890
3252
  this.appearanceObserver.start();
@@ -2895,21 +3257,11 @@ Copyright © 2021 Basecamp, LLC
2895
3257
  }
2896
3258
  }
2897
3259
  async loadSourceURL() {
2898
- if (!this.settingSourceURL && this.enabled && this.isActive && (this.reloadable || this.sourceURL != this.currentURL)) {
2899
- const previousURL = this.currentURL;
2900
- this.currentURL = this.sourceURL;
2901
- if (this.sourceURL) {
2902
- try {
2903
- this.element.loaded = this.visit(expandURL(this.sourceURL));
2904
- this.appearanceObserver.stop();
2905
- await this.element.loaded;
2906
- this.hasBeenLoaded = true;
2907
- }
2908
- catch (error) {
2909
- this.currentURL = previousURL;
2910
- throw error;
2911
- }
2912
- }
3260
+ if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
3261
+ this.element.loaded = this.visit(expandURL(this.sourceURL));
3262
+ this.appearanceObserver.stop();
3263
+ await this.element.loaded;
3264
+ this.hasBeenLoaded = true;
2913
3265
  }
2914
3266
  }
2915
3267
  async loadResponse(fetchResponse) {
@@ -2920,14 +3272,22 @@ Copyright © 2021 Basecamp, LLC
2920
3272
  const html = await fetchResponse.responseHTML;
2921
3273
  if (html) {
2922
3274
  const { body } = parseHTMLDocument(html);
2923
- const snapshot = new Snapshot(await this.extractForeignFrameElement(body));
2924
- const renderer = new FrameRenderer(this.view.snapshot, snapshot, false, false);
2925
- if (this.view.renderPromise)
2926
- await this.view.renderPromise;
2927
- await this.view.render(renderer);
2928
- session.frameRendered(fetchResponse, this.element);
2929
- session.frameLoaded(this.element);
2930
- this.fetchResponseLoaded(fetchResponse);
3275
+ const newFrameElement = await this.extractForeignFrameElement(body);
3276
+ if (newFrameElement) {
3277
+ const snapshot = new Snapshot(newFrameElement);
3278
+ const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
3279
+ if (this.view.renderPromise)
3280
+ await this.view.renderPromise;
3281
+ this.changeHistory();
3282
+ await this.view.render(renderer);
3283
+ this.complete = true;
3284
+ session.frameRendered(fetchResponse, this.element);
3285
+ session.frameLoaded(this.element);
3286
+ this.fetchResponseLoaded(fetchResponse);
3287
+ }
3288
+ else if (this.sessionWillHandleMissingFrame(fetchResponse)) {
3289
+ await session.frameMissing(this.element, fetchResponse);
3290
+ }
2931
3291
  }
2932
3292
  }
2933
3293
  catch (error) {
@@ -2938,41 +3298,46 @@ Copyright © 2021 Basecamp, LLC
2938
3298
  this.fetchResponseLoaded = () => { };
2939
3299
  }
2940
3300
  }
2941
- elementAppearedInViewport(element) {
3301
+ elementAppearedInViewport(_element) {
2942
3302
  this.loadSourceURL();
2943
3303
  }
2944
- shouldInterceptLinkClick(element, url) {
2945
- if (element.hasAttribute("data-turbo-method")) {
2946
- return false;
2947
- }
2948
- else {
2949
- return this.shouldInterceptNavigation(element);
2950
- }
3304
+ willSubmitFormLinkToLocation(link) {
3305
+ return link.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(link);
3306
+ }
3307
+ submittedFormLinkToLocation(link, _location, form) {
3308
+ const frame = this.findFrameElement(link);
3309
+ if (frame)
3310
+ form.setAttribute("data-turbo-frame", frame.id);
3311
+ }
3312
+ willFollowLinkToLocation(element) {
3313
+ return this.shouldInterceptNavigation(element);
2951
3314
  }
2952
- linkClickIntercepted(element, url) {
2953
- this.reloadable = true;
2954
- this.navigateFrame(element, url);
3315
+ followedLinkToLocation(element, location) {
3316
+ this.navigateFrame(element, location.href);
2955
3317
  }
2956
- shouldInterceptFormSubmission(element, submitter) {
2957
- return this.shouldInterceptNavigation(element, submitter);
3318
+ willSubmitForm(element, submitter) {
3319
+ return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
2958
3320
  }
2959
- formSubmissionIntercepted(element, submitter) {
3321
+ formSubmitted(element, submitter) {
2960
3322
  if (this.formSubmission) {
2961
3323
  this.formSubmission.stop();
2962
3324
  }
2963
- this.reloadable = false;
2964
3325
  this.formSubmission = new FormSubmission(this, element, submitter);
2965
3326
  const { fetchRequest } = this.formSubmission;
2966
3327
  this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
2967
3328
  this.formSubmission.start();
2968
3329
  }
2969
3330
  prepareHeadersForRequest(headers, request) {
3331
+ var _a;
2970
3332
  headers["Turbo-Frame"] = this.id;
3333
+ if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
3334
+ request.acceptResponseType(StreamMessage.contentType);
3335
+ }
2971
3336
  }
2972
- requestStarted(request) {
3337
+ requestStarted(_request) {
2973
3338
  markAsBusy(this.element);
2974
3339
  }
2975
- requestPreventedHandlingResponse(request, response) {
3340
+ requestPreventedHandlingResponse(_request, _response) {
2976
3341
  this.resolveVisitPromise();
2977
3342
  }
2978
3343
  async requestSucceededWithResponse(request, response) {
@@ -2985,9 +3350,13 @@ Copyright © 2021 Basecamp, LLC
2985
3350
  }
2986
3351
  requestErrored(request, error) {
2987
3352
  console.error(error);
3353
+ dispatch("turbo:fetch-request-error", {
3354
+ target: this.element,
3355
+ detail: { request, error },
3356
+ });
2988
3357
  this.resolveVisitPromise();
2989
3358
  }
2990
- requestFinished(request) {
3359
+ requestFinished(_request) {
2991
3360
  clearBusyState(this.element);
2992
3361
  }
2993
3362
  formSubmissionStarted({ formElement }) {
@@ -3007,19 +3376,32 @@ Copyright © 2021 Basecamp, LLC
3007
3376
  formSubmissionFinished({ formElement }) {
3008
3377
  clearBusyState(formElement, this.findFrameElement(formElement));
3009
3378
  }
3010
- allowsImmediateRender(snapshot, resume) {
3011
- return true;
3379
+ allowsImmediateRender({ element: newFrame }, options) {
3380
+ const event = dispatch("turbo:before-frame-render", {
3381
+ target: this.element,
3382
+ detail: Object.assign({ newFrame }, options),
3383
+ cancelable: true,
3384
+ });
3385
+ const { defaultPrevented, detail: { render }, } = event;
3386
+ if (this.view.renderer && render) {
3387
+ this.view.renderer.renderElement = render;
3388
+ }
3389
+ return !defaultPrevented;
3012
3390
  }
3013
- viewRenderedSnapshot(snapshot, isPreview) {
3391
+ viewRenderedSnapshot(_snapshot, _isPreview) { }
3392
+ preloadOnLoadLinksForView(element) {
3393
+ session.preloadOnLoadLinksForView(element);
3014
3394
  }
3015
- viewInvalidated() {
3395
+ viewInvalidated() { }
3396
+ willRenderFrame(currentElement, _newElement) {
3397
+ this.previousFrameElement = currentElement.cloneNode(true);
3016
3398
  }
3017
3399
  async visit(url) {
3018
3400
  var _a;
3019
- const request = new FetchRequest(this, FetchMethod.get, url, url.searchParams, this.element);
3401
+ const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
3020
3402
  (_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
3021
3403
  this.currentFetchRequest = request;
3022
- return new Promise(resolve => {
3404
+ return new Promise((resolve) => {
3023
3405
  this.resolveVisitPromise = () => {
3024
3406
  this.resolveVisitPromise = () => { };
3025
3407
  this.currentFetchRequest = null;
@@ -3031,23 +3413,49 @@ Copyright © 2021 Basecamp, LLC
3031
3413
  navigateFrame(element, url, submitter) {
3032
3414
  const frame = this.findFrameElement(element, submitter);
3033
3415
  this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
3034
- frame.setAttribute("reloadable", "");
3035
- frame.src = url;
3416
+ this.withCurrentNavigationElement(element, () => {
3417
+ frame.src = url;
3418
+ });
3036
3419
  }
3037
3420
  proposeVisitIfNavigatedWithAction(frame, element, submitter) {
3038
- const action = getAttribute("data-turbo-action", submitter, element, frame);
3039
- if (isAction(action)) {
3040
- const { visitCachedSnapshot } = new SnapshotSubstitution(frame);
3421
+ this.action = getVisitAction(submitter, element, frame);
3422
+ this.frame = frame;
3423
+ if (isAction(this.action)) {
3424
+ const { visitCachedSnapshot } = frame.delegate;
3041
3425
  frame.delegate.fetchResponseLoaded = (fetchResponse) => {
3042
3426
  if (frame.src) {
3043
3427
  const { statusCode, redirected } = fetchResponse;
3044
3428
  const responseHTML = frame.ownerDocument.documentElement.outerHTML;
3045
3429
  const response = { statusCode, redirected, responseHTML };
3046
- session.visit(frame.src, { action, response, visitCachedSnapshot, willRender: false });
3430
+ const options = {
3431
+ response,
3432
+ visitCachedSnapshot,
3433
+ willRender: false,
3434
+ updateHistory: false,
3435
+ restorationIdentifier: this.restorationIdentifier,
3436
+ };
3437
+ if (this.action)
3438
+ options.action = this.action;
3439
+ session.visit(frame.src, options);
3047
3440
  }
3048
3441
  };
3049
3442
  }
3050
3443
  }
3444
+ changeHistory() {
3445
+ if (this.action && this.frame) {
3446
+ const method = getHistoryMethodForAction(this.action);
3447
+ session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
3448
+ }
3449
+ }
3450
+ sessionWillHandleMissingFrame(fetchResponse) {
3451
+ this.element.setAttribute("complete", "");
3452
+ const event = dispatch("turbo:frame-missing", {
3453
+ target: this.element,
3454
+ detail: { fetchResponse },
3455
+ cancelable: true,
3456
+ });
3457
+ return !event.defaultPrevented;
3458
+ }
3051
3459
  findFrameElement(element, submitter) {
3052
3460
  var _a;
3053
3461
  const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
@@ -3057,19 +3465,21 @@ Copyright © 2021 Basecamp, LLC
3057
3465
  let element;
3058
3466
  const id = CSS.escape(this.id);
3059
3467
  try {
3060
- if (element = activateElement(container.querySelector(`turbo-frame#${id}`), this.currentURL)) {
3468
+ element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
3469
+ if (element) {
3061
3470
  return element;
3062
3471
  }
3063
- if (element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.currentURL)) {
3472
+ element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
3473
+ if (element) {
3064
3474
  await element.loaded;
3065
3475
  return await this.extractForeignFrameElement(element);
3066
3476
  }
3067
- console.error(`Response has no matching <turbo-frame id="${id}"> element`);
3068
3477
  }
3069
3478
  catch (error) {
3070
3479
  console.error(error);
3480
+ return new FrameElement();
3071
3481
  }
3072
- return new FrameElement();
3482
+ return null;
3073
3483
  }
3074
3484
  formActionIsVisitable(form, submitter) {
3075
3485
  const action = getAction(form, submitter);
@@ -3089,10 +3499,10 @@ Copyright © 2021 Basecamp, LLC
3089
3499
  return !frameElement.disabled;
3090
3500
  }
3091
3501
  }
3092
- if (!session.elementDriveEnabled(element)) {
3502
+ if (!session.elementIsNavigatable(element)) {
3093
3503
  return false;
3094
3504
  }
3095
- if (submitter && !session.elementDriveEnabled(submitter)) {
3505
+ if (submitter && !session.elementIsNavigatable(submitter)) {
3096
3506
  return false;
3097
3507
  }
3098
3508
  return true;
@@ -3108,24 +3518,10 @@ Copyright © 2021 Basecamp, LLC
3108
3518
  return this.element.src;
3109
3519
  }
3110
3520
  }
3111
- get reloadable() {
3112
- const frame = this.findFrameElement(this.element);
3113
- return frame.hasAttribute("reloadable");
3114
- }
3115
- set reloadable(value) {
3116
- const frame = this.findFrameElement(this.element);
3117
- if (value) {
3118
- frame.setAttribute("reloadable", "");
3119
- }
3120
- else {
3121
- frame.removeAttribute("reloadable");
3122
- }
3123
- }
3124
3521
  set sourceURL(sourceURL) {
3125
- this.settingSourceURL = true;
3126
- this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3127
- this.currentURL = this.element.src;
3128
- this.settingSourceURL = false;
3522
+ this.ignoringChangesToAttribute("src", () => {
3523
+ this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
3524
+ });
3129
3525
  }
3130
3526
  get loadingStyle() {
3131
3527
  return this.element.loading;
@@ -3133,6 +3529,19 @@ Copyright © 2021 Basecamp, LLC
3133
3529
  get isLoading() {
3134
3530
  return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
3135
3531
  }
3532
+ get complete() {
3533
+ return this.element.hasAttribute("complete");
3534
+ }
3535
+ set complete(value) {
3536
+ this.ignoringChangesToAttribute("complete", () => {
3537
+ if (value) {
3538
+ this.element.setAttribute("complete", "");
3539
+ }
3540
+ else {
3541
+ this.element.removeAttribute("complete");
3542
+ }
3543
+ });
3544
+ }
3136
3545
  get isActive() {
3137
3546
  return this.element.isActive && this.connected;
3138
3547
  }
@@ -3142,16 +3551,18 @@ Copyright © 2021 Basecamp, LLC
3142
3551
  const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
3143
3552
  return expandURL(root);
3144
3553
  }
3145
- }
3146
- class SnapshotSubstitution {
3147
- constructor(element) {
3148
- this.visitCachedSnapshot = ({ element }) => {
3149
- var _a;
3150
- const { id, clone } = this;
3151
- (_a = element.querySelector("#" + id)) === null || _a === void 0 ? void 0 : _a.replaceWith(clone);
3152
- };
3153
- this.clone = element.cloneNode(true);
3154
- this.id = element.id;
3554
+ isIgnoringChangesTo(attributeName) {
3555
+ return this.ignoredAttributes.has(attributeName);
3556
+ }
3557
+ ignoringChangesToAttribute(attributeName, callback) {
3558
+ this.ignoredAttributes.add(attributeName);
3559
+ callback();
3560
+ this.ignoredAttributes.delete(attributeName);
3561
+ }
3562
+ withCurrentNavigationElement(element, callback) {
3563
+ this.currentNavigationElement = element;
3564
+ callback();
3565
+ delete this.currentNavigationElement;
3155
3566
  }
3156
3567
  }
3157
3568
  function getFrameElementById(id) {
@@ -3179,35 +3590,6 @@ Copyright © 2021 Basecamp, LLC
3179
3590
  }
3180
3591
  }
3181
3592
 
3182
- const StreamActions = {
3183
- after() {
3184
- this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
3185
- },
3186
- append() {
3187
- this.removeDuplicateTargetChildren();
3188
- this.targetElements.forEach(e => e.append(this.templateContent));
3189
- },
3190
- before() {
3191
- this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
3192
- },
3193
- prepend() {
3194
- this.removeDuplicateTargetChildren();
3195
- this.targetElements.forEach(e => e.prepend(this.templateContent));
3196
- },
3197
- remove() {
3198
- this.targetElements.forEach(e => e.remove());
3199
- },
3200
- replace() {
3201
- this.targetElements.forEach(e => e.replaceWith(this.templateContent));
3202
- },
3203
- update() {
3204
- this.targetElements.forEach(e => {
3205
- e.innerHTML = "";
3206
- e.append(this.templateContent);
3207
- });
3208
- }
3209
- };
3210
-
3211
3593
  class StreamElement extends HTMLElement {
3212
3594
  async connectedCallback() {
3213
3595
  try {
@@ -3222,12 +3604,12 @@ Copyright © 2021 Basecamp, LLC
3222
3604
  }
3223
3605
  async render() {
3224
3606
  var _a;
3225
- return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
3607
+ return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
3226
3608
  if (this.dispatchEvent(this.beforeRenderEvent)) {
3227
3609
  await nextAnimationFrame();
3228
3610
  this.performAction();
3229
3611
  }
3230
- })());
3612
+ })()));
3231
3613
  }
3232
3614
  disconnect() {
3233
3615
  try {
@@ -3236,13 +3618,13 @@ Copyright © 2021 Basecamp, LLC
3236
3618
  catch (_a) { }
3237
3619
  }
3238
3620
  removeDuplicateTargetChildren() {
3239
- this.duplicateChildren.forEach(c => c.remove());
3621
+ this.duplicateChildren.forEach((c) => c.remove());
3240
3622
  }
3241
3623
  get duplicateChildren() {
3242
3624
  var _a;
3243
- const existingChildren = this.targetElements.flatMap(e => [...e.children]).filter(c => !!c.id);
3244
- const newChildrenIds = [...(_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children].filter(c => !!c.id).map(c => c.id);
3245
- return existingChildren.filter(c => newChildrenIds.includes(c.id));
3625
+ const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
3626
+ const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
3627
+ return existingChildren.filter((c) => newChildrenIds.includes(c.id));
3246
3628
  }
3247
3629
  get performAction() {
3248
3630
  if (this.action) {
@@ -3291,7 +3673,11 @@ Copyright © 2021 Basecamp, LLC
3291
3673
  return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
3292
3674
  }
3293
3675
  get beforeRenderEvent() {
3294
- return new CustomEvent("turbo:before-stream-render", { bubbles: true, cancelable: true });
3676
+ return new CustomEvent("turbo:before-stream-render", {
3677
+ bubbles: true,
3678
+ cancelable: true,
3679
+ detail: { newStream: this },
3680
+ });
3295
3681
  }
3296
3682
  get targetElementsById() {
3297
3683
  var _a;
@@ -3315,9 +3701,35 @@ Copyright © 2021 Basecamp, LLC
3315
3701
  }
3316
3702
  }
3317
3703
 
3704
+ class StreamSourceElement extends HTMLElement {
3705
+ constructor() {
3706
+ super(...arguments);
3707
+ this.streamSource = null;
3708
+ }
3709
+ connectedCallback() {
3710
+ this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
3711
+ connectStreamSource(this.streamSource);
3712
+ }
3713
+ disconnectedCallback() {
3714
+ if (this.streamSource) {
3715
+ disconnectStreamSource(this.streamSource);
3716
+ }
3717
+ }
3718
+ get src() {
3719
+ return this.getAttribute("src") || "";
3720
+ }
3721
+ }
3722
+
3318
3723
  FrameElement.delegateConstructor = FrameController;
3319
- customElements.define("turbo-frame", FrameElement);
3320
- customElements.define("turbo-stream", StreamElement);
3724
+ if (customElements.get("turbo-frame") === undefined) {
3725
+ customElements.define("turbo-frame", FrameElement);
3726
+ }
3727
+ if (customElements.get("turbo-stream") === undefined) {
3728
+ customElements.define("turbo-stream", StreamElement);
3729
+ }
3730
+ if (customElements.get("turbo-stream-source") === undefined) {
3731
+ customElements.define("turbo-stream-source", StreamSourceElement);
3732
+ }
3321
3733
 
3322
3734
  (() => {
3323
3735
  let element = document.currentScript;
@@ -3325,7 +3737,8 @@ Copyright © 2021 Basecamp, LLC
3325
3737
  return;
3326
3738
  if (element.hasAttribute("data-turbo-suppress-warning"))
3327
3739
  return;
3328
- while (element = element.parentElement) {
3740
+ element = element.parentElement;
3741
+ while (element) {
3329
3742
  if (element == document.body) {
3330
3743
  return console.warn(unindent `
3331
3744
  You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
@@ -3338,14 +3751,18 @@ Copyright © 2021 Basecamp, LLC
3338
3751
  Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
3339
3752
  `, element.outerHTML);
3340
3753
  }
3754
+ element = element.parentElement;
3341
3755
  }
3342
3756
  })();
3343
3757
 
3344
3758
  window.Turbo = Turbo;
3345
3759
  start();
3346
3760
 
3761
+ exports.FrameRenderer = FrameRenderer;
3347
3762
  exports.PageRenderer = PageRenderer;
3348
3763
  exports.PageSnapshot = PageSnapshot;
3764
+ exports.StreamActions = StreamActions;
3765
+ exports.cache = cache;
3349
3766
  exports.clearCache = clearCache;
3350
3767
  exports.connectStreamSource = connectStreamSource;
3351
3768
  exports.disconnectStreamSource = disconnectStreamSource;
@@ -3354,10 +3771,11 @@ Copyright © 2021 Basecamp, LLC
3354
3771
  exports.renderStreamMessage = renderStreamMessage;
3355
3772
  exports.session = session;
3356
3773
  exports.setConfirmMethod = setConfirmMethod;
3774
+ exports.setFormMode = setFormMode;
3357
3775
  exports.setProgressBarDelay = setProgressBarDelay;
3358
3776
  exports.start = start;
3359
3777
  exports.visit = visit;
3360
3778
 
3361
3779
  Object.defineProperty(exports, '__esModule', { value: true });
3362
3780
 
3363
- })));
3781
+ }));