@hotwired/turbo 7.1.0 → 7.2.0

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