@hotwired/turbo 7.2.5 → 7.3.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.
- package/dist/turbo.es2017-esm.js +135 -75
- package/dist/turbo.es2017-umd.js +135 -75
- package/dist/types/core/drive/form_submission.d.ts +8 -4
- package/dist/types/core/drive/head_snapshot.d.ts +3 -3
- package/dist/types/core/drive/history.d.ts +3 -3
- package/dist/types/core/drive/navigator.d.ts +1 -1
- package/dist/types/core/drive/page_view.d.ts +2 -2
- package/dist/types/core/drive/visit.d.ts +3 -3
- package/dist/types/core/errors.d.ts +2 -0
- package/dist/types/core/frames/frame_controller.d.ts +6 -2
- package/dist/types/core/frames/frame_view.d.ts +2 -2
- package/dist/types/core/index.d.ts +5 -4
- package/dist/types/core/native/browser_adapter.d.ts +1 -1
- package/dist/types/core/renderer.d.ts +1 -1
- package/dist/types/core/session.d.ts +12 -12
- package/dist/types/core/snapshot.d.ts +1 -1
- package/dist/types/core/streams/stream_actions.d.ts +2 -2
- package/dist/types/core/types.d.ts +3 -3
- package/dist/types/core/url.d.ts +1 -1
- package/dist/types/core/view.d.ts +1 -1
- package/dist/types/elements/frame_element.d.ts +1 -1
- package/dist/types/elements/stream_element.d.ts +2 -2
- package/dist/types/http/fetch_request.d.ts +7 -7
- package/dist/types/http/index.d.ts +1 -1
- package/dist/types/observers/cache_observer.d.ts +5 -1
- package/dist/types/observers/form_link_click_observer.d.ts +1 -1
- package/dist/types/polyfills/custom-elements-native-shim.d.ts +1 -0
- package/dist/types/tests/helpers/dom_test_case.d.ts +1 -2
- package/dist/types/tests/helpers/page.d.ts +10 -10
- package/dist/types/tests/unit/deprecated_adapter_support_tests.d.ts +1 -0
- package/dist/types/tests/unit/export_tests.d.ts +1 -5
- package/dist/types/tests/unit/stream_element_tests.d.ts +0 -10
- package/dist/types/util.d.ts +1 -1
- package/package.json +16 -10
- package/dist/types/tests/helpers/intern_test_case.d.ts +0 -19
- package/dist/types/tests/unit/deprecated_adapter_support_test.d.ts +0 -24
- package/dist/types/tests/unit/index.d.ts +0 -3
package/dist/turbo.es2017-umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Turbo 7.
|
|
2
|
+
Turbo 7.3.0
|
|
3
3
|
Copyright © 2023 37signals LLC
|
|
4
4
|
*/
|
|
5
5
|
(function (global, factory) {
|
|
@@ -93,16 +93,13 @@ Copyright © 2023 37signals LLC
|
|
|
93
93
|
(function () {
|
|
94
94
|
if ("submitter" in Event.prototype)
|
|
95
95
|
return;
|
|
96
|
-
let prototype;
|
|
96
|
+
let prototype = window.Event.prototype;
|
|
97
97
|
if ("SubmitEvent" in window && /Apple Computer/.test(navigator.vendor)) {
|
|
98
98
|
prototype = window.SubmitEvent.prototype;
|
|
99
99
|
}
|
|
100
100
|
else if ("SubmitEvent" in window) {
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
|
-
else {
|
|
104
|
-
prototype = window.Event.prototype;
|
|
105
|
-
}
|
|
106
103
|
addEventListener("click", clickCaptured, true);
|
|
107
104
|
Object.defineProperty(prototype, "submitter", {
|
|
108
105
|
get() {
|
|
@@ -119,14 +116,14 @@ Copyright © 2023 37signals LLC
|
|
|
119
116
|
FrameLoadingStyle["lazy"] = "lazy";
|
|
120
117
|
})(exports.FrameLoadingStyle || (exports.FrameLoadingStyle = {}));
|
|
121
118
|
class FrameElement extends HTMLElement {
|
|
119
|
+
static get observedAttributes() {
|
|
120
|
+
return ["disabled", "complete", "loading", "src"];
|
|
121
|
+
}
|
|
122
122
|
constructor() {
|
|
123
123
|
super();
|
|
124
124
|
this.loaded = Promise.resolve();
|
|
125
125
|
this.delegate = new FrameElement.delegateConstructor(this);
|
|
126
126
|
}
|
|
127
|
-
static get observedAttributes() {
|
|
128
|
-
return ["disabled", "complete", "loading", "src"];
|
|
129
|
-
}
|
|
130
127
|
connectedCallback() {
|
|
131
128
|
this.delegate.connect();
|
|
132
129
|
}
|
|
@@ -566,7 +563,7 @@ Copyright © 2023 37signals LLC
|
|
|
566
563
|
credentials: "same-origin",
|
|
567
564
|
headers: this.headers,
|
|
568
565
|
redirect: "follow",
|
|
569
|
-
body: this.
|
|
566
|
+
body: this.isSafe ? null : this.body,
|
|
570
567
|
signal: this.abortSignal,
|
|
571
568
|
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
|
|
572
569
|
};
|
|
@@ -576,8 +573,8 @@ Copyright © 2023 37signals LLC
|
|
|
576
573
|
Accept: "text/html, application/xhtml+xml",
|
|
577
574
|
};
|
|
578
575
|
}
|
|
579
|
-
get
|
|
580
|
-
return this.method
|
|
576
|
+
get isSafe() {
|
|
577
|
+
return this.method === FetchMethod.get;
|
|
581
578
|
}
|
|
582
579
|
get abortSignal() {
|
|
583
580
|
return this.abortController.signal;
|
|
@@ -637,9 +634,6 @@ Copyright © 2023 37signals LLC
|
|
|
637
634
|
}
|
|
638
635
|
|
|
639
636
|
class StreamMessage {
|
|
640
|
-
constructor(fragment) {
|
|
641
|
-
this.fragment = importStreamElements(fragment);
|
|
642
|
-
}
|
|
643
637
|
static wrap(message) {
|
|
644
638
|
if (typeof message == "string") {
|
|
645
639
|
return new this(createDocumentFragment(message));
|
|
@@ -648,6 +642,9 @@ Copyright © 2023 37signals LLC
|
|
|
648
642
|
return message;
|
|
649
643
|
}
|
|
650
644
|
}
|
|
645
|
+
constructor(fragment) {
|
|
646
|
+
this.fragment = importStreamElements(fragment);
|
|
647
|
+
}
|
|
651
648
|
}
|
|
652
649
|
StreamMessage.contentType = "text/vnd.turbo-stream.html";
|
|
653
650
|
function importStreamElements(fragment) {
|
|
@@ -687,6 +684,9 @@ Copyright © 2023 37signals LLC
|
|
|
687
684
|
}
|
|
688
685
|
}
|
|
689
686
|
class FormSubmission {
|
|
687
|
+
static confirmMethod(message, _element, _submitter) {
|
|
688
|
+
return Promise.resolve(confirm(message));
|
|
689
|
+
}
|
|
690
690
|
constructor(delegate, formElement, submitter, mustRedirect = false) {
|
|
691
691
|
this.state = FormSubmissionState.initialized;
|
|
692
692
|
this.delegate = delegate;
|
|
@@ -700,9 +700,6 @@ Copyright © 2023 37signals LLC
|
|
|
700
700
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
701
701
|
this.mustRedirect = mustRedirect;
|
|
702
702
|
}
|
|
703
|
-
static confirmMethod(message, _element, _submitter) {
|
|
704
|
-
return Promise.resolve(confirm(message));
|
|
705
|
-
}
|
|
706
703
|
get method() {
|
|
707
704
|
var _a;
|
|
708
705
|
const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
|
|
@@ -730,8 +727,8 @@ Copyright © 2023 37signals LLC
|
|
|
730
727
|
var _a;
|
|
731
728
|
return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
|
|
732
729
|
}
|
|
733
|
-
get
|
|
734
|
-
return this.fetchRequest.
|
|
730
|
+
get isSafe() {
|
|
731
|
+
return this.fetchRequest.isSafe;
|
|
735
732
|
}
|
|
736
733
|
get stringFormData() {
|
|
737
734
|
return [...this.formData].reduce((entries, [name, value]) => {
|
|
@@ -761,7 +758,7 @@ Copyright © 2023 37signals LLC
|
|
|
761
758
|
}
|
|
762
759
|
}
|
|
763
760
|
prepareRequest(request) {
|
|
764
|
-
if (!request.
|
|
761
|
+
if (!request.isSafe) {
|
|
765
762
|
const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
|
|
766
763
|
if (token) {
|
|
767
764
|
request.headers["X-CSRF-Token"] = token;
|
|
@@ -775,6 +772,7 @@ Copyright © 2023 37signals LLC
|
|
|
775
772
|
var _a;
|
|
776
773
|
this.state = FormSubmissionState.waiting;
|
|
777
774
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
|
|
775
|
+
this.setSubmitsWith();
|
|
778
776
|
dispatch("turbo:submit-start", {
|
|
779
777
|
target: this.formElement,
|
|
780
778
|
detail: { formSubmission: this },
|
|
@@ -810,17 +808,46 @@ Copyright © 2023 37signals LLC
|
|
|
810
808
|
var _a;
|
|
811
809
|
this.state = FormSubmissionState.stopped;
|
|
812
810
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
|
|
811
|
+
this.resetSubmitterText();
|
|
813
812
|
dispatch("turbo:submit-end", {
|
|
814
813
|
target: this.formElement,
|
|
815
814
|
detail: Object.assign({ formSubmission: this }, this.result),
|
|
816
815
|
});
|
|
817
816
|
this.delegate.formSubmissionFinished(this);
|
|
818
817
|
}
|
|
818
|
+
setSubmitsWith() {
|
|
819
|
+
if (!this.submitter || !this.submitsWith)
|
|
820
|
+
return;
|
|
821
|
+
if (this.submitter.matches("button")) {
|
|
822
|
+
this.originalSubmitText = this.submitter.innerHTML;
|
|
823
|
+
this.submitter.innerHTML = this.submitsWith;
|
|
824
|
+
}
|
|
825
|
+
else if (this.submitter.matches("input")) {
|
|
826
|
+
const input = this.submitter;
|
|
827
|
+
this.originalSubmitText = input.value;
|
|
828
|
+
input.value = this.submitsWith;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
resetSubmitterText() {
|
|
832
|
+
if (!this.submitter || !this.originalSubmitText)
|
|
833
|
+
return;
|
|
834
|
+
if (this.submitter.matches("button")) {
|
|
835
|
+
this.submitter.innerHTML = this.originalSubmitText;
|
|
836
|
+
}
|
|
837
|
+
else if (this.submitter.matches("input")) {
|
|
838
|
+
const input = this.submitter;
|
|
839
|
+
input.value = this.originalSubmitText;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
819
842
|
requestMustRedirect(request) {
|
|
820
|
-
return !request.
|
|
843
|
+
return !request.isSafe && this.mustRedirect;
|
|
821
844
|
}
|
|
822
845
|
requestAcceptsTurboStreamResponse(request) {
|
|
823
|
-
return !request.
|
|
846
|
+
return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
|
|
847
|
+
}
|
|
848
|
+
get submitsWith() {
|
|
849
|
+
var _a;
|
|
850
|
+
return (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-submits-with");
|
|
824
851
|
}
|
|
825
852
|
}
|
|
826
853
|
function buildFormData(formElement, submitter) {
|
|
@@ -1060,8 +1087,8 @@ Copyright © 2023 37signals LLC
|
|
|
1060
1087
|
}
|
|
1061
1088
|
|
|
1062
1089
|
class FrameView extends View {
|
|
1063
|
-
|
|
1064
|
-
this.element.innerHTML = ""
|
|
1090
|
+
missing() {
|
|
1091
|
+
this.element.innerHTML = `<strong class="turbo-frame-error">Content missing</strong>`;
|
|
1065
1092
|
}
|
|
1066
1093
|
get snapshot() {
|
|
1067
1094
|
return new Snapshot(this.element);
|
|
@@ -1222,16 +1249,16 @@ Copyright © 2023 37signals LLC
|
|
|
1222
1249
|
}
|
|
1223
1250
|
|
|
1224
1251
|
class Bardo {
|
|
1225
|
-
constructor(delegate, permanentElementMap) {
|
|
1226
|
-
this.delegate = delegate;
|
|
1227
|
-
this.permanentElementMap = permanentElementMap;
|
|
1228
|
-
}
|
|
1229
1252
|
static async preservingPermanentElements(delegate, permanentElementMap, callback) {
|
|
1230
1253
|
const bardo = new this(delegate, permanentElementMap);
|
|
1231
1254
|
bardo.enter();
|
|
1232
1255
|
await callback();
|
|
1233
1256
|
bardo.leave();
|
|
1234
1257
|
}
|
|
1258
|
+
constructor(delegate, permanentElementMap) {
|
|
1259
|
+
this.delegate = delegate;
|
|
1260
|
+
this.permanentElementMap = permanentElementMap;
|
|
1261
|
+
}
|
|
1235
1262
|
enter() {
|
|
1236
1263
|
for (const id in this.permanentElementMap) {
|
|
1237
1264
|
const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
|
|
@@ -1338,10 +1365,6 @@ Copyright © 2023 37signals LLC
|
|
|
1338
1365
|
}
|
|
1339
1366
|
|
|
1340
1367
|
class FrameRenderer extends Renderer {
|
|
1341
|
-
constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
|
1342
|
-
super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
|
|
1343
|
-
this.delegate = delegate;
|
|
1344
|
-
}
|
|
1345
1368
|
static renderElement(currentElement, newElement) {
|
|
1346
1369
|
var _a;
|
|
1347
1370
|
const destinationRange = document.createRange();
|
|
@@ -1354,6 +1377,10 @@ Copyright © 2023 37signals LLC
|
|
|
1354
1377
|
currentElement.appendChild(sourceRange.extractContents());
|
|
1355
1378
|
}
|
|
1356
1379
|
}
|
|
1380
|
+
constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
|
1381
|
+
super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
|
|
1382
|
+
this.delegate = delegate;
|
|
1383
|
+
}
|
|
1357
1384
|
get shouldRender() {
|
|
1358
1385
|
return true;
|
|
1359
1386
|
}
|
|
@@ -1412,18 +1439,6 @@ Copyright © 2023 37signals LLC
|
|
|
1412
1439
|
}
|
|
1413
1440
|
|
|
1414
1441
|
class ProgressBar {
|
|
1415
|
-
constructor() {
|
|
1416
|
-
this.hiding = false;
|
|
1417
|
-
this.value = 0;
|
|
1418
|
-
this.visible = false;
|
|
1419
|
-
this.trickle = () => {
|
|
1420
|
-
this.setValue(this.value + Math.random() / 100);
|
|
1421
|
-
};
|
|
1422
|
-
this.stylesheetElement = this.createStylesheetElement();
|
|
1423
|
-
this.progressElement = this.createProgressElement();
|
|
1424
|
-
this.installStylesheetElement();
|
|
1425
|
-
this.setValue(0);
|
|
1426
|
-
}
|
|
1427
1442
|
static get defaultCSS() {
|
|
1428
1443
|
return unindent `
|
|
1429
1444
|
.turbo-progress-bar {
|
|
@@ -1441,6 +1456,18 @@ Copyright © 2023 37signals LLC
|
|
|
1441
1456
|
}
|
|
1442
1457
|
`;
|
|
1443
1458
|
}
|
|
1459
|
+
constructor() {
|
|
1460
|
+
this.hiding = false;
|
|
1461
|
+
this.value = 0;
|
|
1462
|
+
this.visible = false;
|
|
1463
|
+
this.trickle = () => {
|
|
1464
|
+
this.setValue(this.value + Math.random() / 100);
|
|
1465
|
+
};
|
|
1466
|
+
this.stylesheetElement = this.createStylesheetElement();
|
|
1467
|
+
this.progressElement = this.createProgressElement();
|
|
1468
|
+
this.installStylesheetElement();
|
|
1469
|
+
this.setValue(0);
|
|
1470
|
+
}
|
|
1444
1471
|
show() {
|
|
1445
1472
|
if (!this.visible) {
|
|
1446
1473
|
this.visible = true;
|
|
@@ -1611,10 +1638,6 @@ Copyright © 2023 37signals LLC
|
|
|
1611
1638
|
}
|
|
1612
1639
|
|
|
1613
1640
|
class PageSnapshot extends Snapshot {
|
|
1614
|
-
constructor(element, headSnapshot) {
|
|
1615
|
-
super(element);
|
|
1616
|
-
this.headSnapshot = headSnapshot;
|
|
1617
|
-
}
|
|
1618
1641
|
static fromHTMLString(html = "") {
|
|
1619
1642
|
return this.fromDocument(parseHTMLDocument(html));
|
|
1620
1643
|
}
|
|
@@ -1624,6 +1647,10 @@ Copyright © 2023 37signals LLC
|
|
|
1624
1647
|
static fromDocument({ head, body }) {
|
|
1625
1648
|
return new this(body, new HeadSnapshot(head));
|
|
1626
1649
|
}
|
|
1650
|
+
constructor(element, headSnapshot) {
|
|
1651
|
+
super(element);
|
|
1652
|
+
this.headSnapshot = headSnapshot;
|
|
1653
|
+
}
|
|
1627
1654
|
clone() {
|
|
1628
1655
|
const clonedElement = this.element.cloneNode(true);
|
|
1629
1656
|
const selectElements = this.element.querySelectorAll("select");
|
|
@@ -2123,10 +2150,11 @@ Copyright © 2023 37signals LLC
|
|
|
2123
2150
|
|
|
2124
2151
|
class CacheObserver {
|
|
2125
2152
|
constructor() {
|
|
2153
|
+
this.selector = "[data-turbo-temporary]";
|
|
2154
|
+
this.deprecatedSelector = "[data-turbo-cache=false]";
|
|
2126
2155
|
this.started = false;
|
|
2127
|
-
this.
|
|
2128
|
-
const
|
|
2129
|
-
for (const element of staleElements) {
|
|
2156
|
+
this.removeTemporaryElements = ((_event) => {
|
|
2157
|
+
for (const element of this.temporaryElements) {
|
|
2130
2158
|
element.remove();
|
|
2131
2159
|
}
|
|
2132
2160
|
});
|
|
@@ -2134,15 +2162,25 @@ Copyright © 2023 37signals LLC
|
|
|
2134
2162
|
start() {
|
|
2135
2163
|
if (!this.started) {
|
|
2136
2164
|
this.started = true;
|
|
2137
|
-
addEventListener("turbo:before-cache", this.
|
|
2165
|
+
addEventListener("turbo:before-cache", this.removeTemporaryElements, false);
|
|
2138
2166
|
}
|
|
2139
2167
|
}
|
|
2140
2168
|
stop() {
|
|
2141
2169
|
if (this.started) {
|
|
2142
2170
|
this.started = false;
|
|
2143
|
-
removeEventListener("turbo:before-cache", this.
|
|
2171
|
+
removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
|
|
2144
2172
|
}
|
|
2145
2173
|
}
|
|
2174
|
+
get temporaryElements() {
|
|
2175
|
+
return [...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation];
|
|
2176
|
+
}
|
|
2177
|
+
get temporaryElementsWithDeprecation() {
|
|
2178
|
+
const elements = document.querySelectorAll(this.deprecatedSelector);
|
|
2179
|
+
if (elements.length) {
|
|
2180
|
+
console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`);
|
|
2181
|
+
}
|
|
2182
|
+
return [...elements];
|
|
2183
|
+
}
|
|
2146
2184
|
}
|
|
2147
2185
|
|
|
2148
2186
|
class FrameRedirector {
|
|
@@ -2341,7 +2379,7 @@ Copyright © 2023 37signals LLC
|
|
|
2341
2379
|
if (formSubmission == this.formSubmission) {
|
|
2342
2380
|
const responseHTML = await fetchResponse.responseHTML;
|
|
2343
2381
|
if (responseHTML) {
|
|
2344
|
-
const shouldCacheSnapshot = formSubmission.
|
|
2382
|
+
const shouldCacheSnapshot = formSubmission.isSafe;
|
|
2345
2383
|
if (!shouldCacheSnapshot) {
|
|
2346
2384
|
this.view.clearSnapshotCache();
|
|
2347
2385
|
}
|
|
@@ -3301,6 +3339,9 @@ Copyright © 2023 37signals LLC
|
|
|
3301
3339
|
StreamActions: StreamActions
|
|
3302
3340
|
});
|
|
3303
3341
|
|
|
3342
|
+
class TurboFrameMissingError extends Error {
|
|
3343
|
+
}
|
|
3344
|
+
|
|
3304
3345
|
class FrameController {
|
|
3305
3346
|
constructor(element) {
|
|
3306
3347
|
this.fetchResponseLoaded = (_fetchResponse) => { };
|
|
@@ -3401,30 +3442,16 @@ Copyright © 2023 37signals LLC
|
|
|
3401
3442
|
try {
|
|
3402
3443
|
const html = await fetchResponse.responseHTML;
|
|
3403
3444
|
if (html) {
|
|
3404
|
-
const
|
|
3405
|
-
const
|
|
3406
|
-
if (
|
|
3407
|
-
|
|
3408
|
-
const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
|
|
3409
|
-
if (this.view.renderPromise)
|
|
3410
|
-
await this.view.renderPromise;
|
|
3411
|
-
this.changeHistory();
|
|
3412
|
-
await this.view.render(renderer);
|
|
3413
|
-
this.complete = true;
|
|
3414
|
-
session.frameRendered(fetchResponse, this.element);
|
|
3415
|
-
session.frameLoaded(this.element);
|
|
3416
|
-
this.fetchResponseLoaded(fetchResponse);
|
|
3445
|
+
const document = parseHTMLDocument(html);
|
|
3446
|
+
const pageSnapshot = PageSnapshot.fromDocument(document);
|
|
3447
|
+
if (pageSnapshot.isVisitable) {
|
|
3448
|
+
await this.loadFrameResponse(fetchResponse, document);
|
|
3417
3449
|
}
|
|
3418
|
-
else
|
|
3419
|
-
|
|
3420
|
-
this.visitResponse(fetchResponse.response);
|
|
3450
|
+
else {
|
|
3451
|
+
await this.handleUnvisitableFrameResponse(fetchResponse);
|
|
3421
3452
|
}
|
|
3422
3453
|
}
|
|
3423
3454
|
}
|
|
3424
|
-
catch (error) {
|
|
3425
|
-
console.error(error);
|
|
3426
|
-
this.view.invalidate();
|
|
3427
|
-
}
|
|
3428
3455
|
finally {
|
|
3429
3456
|
this.fetchResponseLoaded = () => { };
|
|
3430
3457
|
}
|
|
@@ -3477,7 +3504,6 @@ Copyright © 2023 37signals LLC
|
|
|
3477
3504
|
this.resolveVisitPromise();
|
|
3478
3505
|
}
|
|
3479
3506
|
async requestFailedWithResponse(request, response) {
|
|
3480
|
-
console.error(response);
|
|
3481
3507
|
await this.loadResponse(response);
|
|
3482
3508
|
this.resolveVisitPromise();
|
|
3483
3509
|
}
|
|
@@ -3495,9 +3521,13 @@ Copyright © 2023 37signals LLC
|
|
|
3495
3521
|
const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
|
|
3496
3522
|
frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
|
|
3497
3523
|
frame.delegate.loadResponse(response);
|
|
3524
|
+
if (!formSubmission.isSafe) {
|
|
3525
|
+
session.clearCache();
|
|
3526
|
+
}
|
|
3498
3527
|
}
|
|
3499
3528
|
formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
|
|
3500
3529
|
this.element.delegate.loadResponse(fetchResponse);
|
|
3530
|
+
session.clearCache();
|
|
3501
3531
|
}
|
|
3502
3532
|
formSubmissionErrored(formSubmission, error) {
|
|
3503
3533
|
console.error(error);
|
|
@@ -3525,6 +3555,24 @@ Copyright © 2023 37signals LLC
|
|
|
3525
3555
|
willRenderFrame(currentElement, _newElement) {
|
|
3526
3556
|
this.previousFrameElement = currentElement.cloneNode(true);
|
|
3527
3557
|
}
|
|
3558
|
+
async loadFrameResponse(fetchResponse, document) {
|
|
3559
|
+
const newFrameElement = await this.extractForeignFrameElement(document.body);
|
|
3560
|
+
if (newFrameElement) {
|
|
3561
|
+
const snapshot = new Snapshot(newFrameElement);
|
|
3562
|
+
const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
|
|
3563
|
+
if (this.view.renderPromise)
|
|
3564
|
+
await this.view.renderPromise;
|
|
3565
|
+
this.changeHistory();
|
|
3566
|
+
await this.view.render(renderer);
|
|
3567
|
+
this.complete = true;
|
|
3568
|
+
session.frameRendered(fetchResponse, this.element);
|
|
3569
|
+
session.frameLoaded(this.element);
|
|
3570
|
+
this.fetchResponseLoaded(fetchResponse);
|
|
3571
|
+
}
|
|
3572
|
+
else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
|
|
3573
|
+
this.handleFrameMissingFromResponse(fetchResponse);
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3528
3576
|
async visit(url) {
|
|
3529
3577
|
var _a;
|
|
3530
3578
|
const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
|
|
@@ -3577,6 +3625,10 @@ Copyright © 2023 37signals LLC
|
|
|
3577
3625
|
session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
|
|
3578
3626
|
}
|
|
3579
3627
|
}
|
|
3628
|
+
async handleUnvisitableFrameResponse(fetchResponse) {
|
|
3629
|
+
console.warn(`The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`);
|
|
3630
|
+
await this.visitResponse(fetchResponse.response);
|
|
3631
|
+
}
|
|
3580
3632
|
willHandleFrameMissingFromResponse(fetchResponse) {
|
|
3581
3633
|
this.element.setAttribute("complete", "");
|
|
3582
3634
|
const response = fetchResponse.response;
|
|
@@ -3595,6 +3647,14 @@ Copyright © 2023 37signals LLC
|
|
|
3595
3647
|
});
|
|
3596
3648
|
return !event.defaultPrevented;
|
|
3597
3649
|
}
|
|
3650
|
+
handleFrameMissingFromResponse(fetchResponse) {
|
|
3651
|
+
this.view.missing();
|
|
3652
|
+
this.throwFrameMissingError(fetchResponse);
|
|
3653
|
+
}
|
|
3654
|
+
throwFrameMissingError(fetchResponse) {
|
|
3655
|
+
const message = `The response (${fetchResponse.statusCode}) did not contain the expected <turbo-frame id="${this.element.id}"> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;
|
|
3656
|
+
throw new TurboFrameMissingError(message);
|
|
3657
|
+
}
|
|
3598
3658
|
async visitResponse(response) {
|
|
3599
3659
|
const wrapped = new FetchResponse(response);
|
|
3600
3660
|
const responseHTML = await wrapped.responseHTML;
|
|
@@ -7,7 +7,7 @@ export interface FormSubmissionDelegate {
|
|
|
7
7
|
formSubmissionErrored(formSubmission: FormSubmission, error: Error): void;
|
|
8
8
|
formSubmissionFinished(formSubmission: FormSubmission): void;
|
|
9
9
|
}
|
|
10
|
-
export
|
|
10
|
+
export type FormSubmissionResult = {
|
|
11
11
|
success: boolean;
|
|
12
12
|
fetchResponse: FetchResponse;
|
|
13
13
|
} | {
|
|
@@ -27,10 +27,10 @@ declare enum FormEnctype {
|
|
|
27
27
|
multipart = "multipart/form-data",
|
|
28
28
|
plain = "text/plain"
|
|
29
29
|
}
|
|
30
|
-
export
|
|
30
|
+
export type TurboSubmitStartEvent = CustomEvent<{
|
|
31
31
|
formSubmission: FormSubmission;
|
|
32
32
|
}>;
|
|
33
|
-
export
|
|
33
|
+
export type TurboSubmitEndEvent = CustomEvent<{
|
|
34
34
|
formSubmission: FormSubmission;
|
|
35
35
|
} & {
|
|
36
36
|
[K in keyof FormSubmissionResult]?: FormSubmissionResult[K];
|
|
@@ -45,13 +45,14 @@ export declare class FormSubmission {
|
|
|
45
45
|
readonly mustRedirect: boolean;
|
|
46
46
|
state: FormSubmissionState;
|
|
47
47
|
result?: FormSubmissionResult;
|
|
48
|
+
originalSubmitText?: string;
|
|
48
49
|
static confirmMethod(message: string, _element: HTMLFormElement, _submitter: HTMLElement | undefined): Promise<boolean>;
|
|
49
50
|
constructor(delegate: FormSubmissionDelegate, formElement: HTMLFormElement, submitter?: HTMLElement, mustRedirect?: boolean);
|
|
50
51
|
get method(): FetchMethod;
|
|
51
52
|
get action(): string;
|
|
52
53
|
get body(): FormData;
|
|
53
54
|
get enctype(): FormEnctype;
|
|
54
|
-
get
|
|
55
|
+
get isSafe(): boolean;
|
|
55
56
|
get stringFormData(): [string, string][];
|
|
56
57
|
start(): Promise<void | FetchResponse>;
|
|
57
58
|
stop(): true | undefined;
|
|
@@ -62,7 +63,10 @@ export declare class FormSubmission {
|
|
|
62
63
|
requestFailedWithResponse(request: FetchRequest, response: FetchResponse): void;
|
|
63
64
|
requestErrored(request: FetchRequest, error: Error): void;
|
|
64
65
|
requestFinished(_request: FetchRequest): void;
|
|
66
|
+
setSubmitsWith(): void;
|
|
67
|
+
resetSubmitterText(): void;
|
|
65
68
|
requestMustRedirect(request: FetchRequest): boolean;
|
|
66
69
|
requestAcceptsTurboStreamResponse(request: FetchRequest): boolean;
|
|
70
|
+
get submitsWith(): string | null | undefined;
|
|
67
71
|
}
|
|
68
72
|
export {};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Snapshot } from "../snapshot";
|
|
2
|
-
|
|
2
|
+
type ElementDetailMap = {
|
|
3
3
|
[outerHTML: string]: ElementDetails;
|
|
4
4
|
};
|
|
5
|
-
|
|
5
|
+
type ElementDetails = {
|
|
6
6
|
type?: ElementType;
|
|
7
7
|
tracked: boolean;
|
|
8
8
|
elements: Element[];
|
|
9
9
|
};
|
|
10
|
-
|
|
10
|
+
type ElementType = "script" | "stylesheet";
|
|
11
11
|
export declare class HeadSnapshot extends Snapshot<HTMLHeadElement> {
|
|
12
12
|
readonly detailsByOuterHTML: ElementDetailMap;
|
|
13
13
|
get trackedElementSignature(): string;
|
|
@@ -2,11 +2,11 @@ import { Position } from "../types";
|
|
|
2
2
|
export interface HistoryDelegate {
|
|
3
3
|
historyPoppedToLocationWithRestorationIdentifier(location: URL, restorationIdentifier: string): void;
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
export
|
|
5
|
+
type HistoryMethod = (this: typeof history, state: any, title: string, url?: string | null | undefined) => void;
|
|
6
|
+
export type RestorationData = {
|
|
7
7
|
scrollPosition?: Position;
|
|
8
8
|
};
|
|
9
|
-
export
|
|
9
|
+
export type RestorationDataMap = {
|
|
10
10
|
[restorationIdentifier: string]: RestorationData;
|
|
11
11
|
};
|
|
12
12
|
export declare class History {
|
|
@@ -3,7 +3,7 @@ import { FetchResponse } from "../../http/fetch_response";
|
|
|
3
3
|
import { FormSubmission } from "./form_submission";
|
|
4
4
|
import { Locatable } from "../url";
|
|
5
5
|
import { Visit, VisitDelegate, VisitOptions } from "./visit";
|
|
6
|
-
export
|
|
6
|
+
export type NavigatorDelegate = VisitDelegate & {
|
|
7
7
|
allowsVisitingLocationWithAction(location: URL, action?: Action): boolean;
|
|
8
8
|
visitProposedToLocation(location: URL, options: Partial<VisitOptions>): void;
|
|
9
9
|
notifyApplicationAfterVisitingSamePageLocation(oldURL: URL, newURL: URL): void;
|
|
@@ -4,11 +4,11 @@ import { PageRenderer } from "./page_renderer";
|
|
|
4
4
|
import { PageSnapshot } from "./page_snapshot";
|
|
5
5
|
import { SnapshotCache } from "./snapshot_cache";
|
|
6
6
|
import { Visit } from "./visit";
|
|
7
|
-
export
|
|
7
|
+
export type PageViewRenderOptions = ViewRenderOptions<HTMLBodyElement>;
|
|
8
8
|
export interface PageViewDelegate extends ViewDelegate<HTMLBodyElement, PageSnapshot> {
|
|
9
9
|
viewWillCacheSnapshot(): void;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
type PageViewRenderer = PageRenderer | ErrorRenderer;
|
|
12
12
|
export declare class PageView extends View<HTMLBodyElement, PageSnapshot, PageViewRenderer, PageViewDelegate> {
|
|
13
13
|
readonly snapshotCache: SnapshotCache;
|
|
14
14
|
lastRenderedLocation: URL;
|
|
@@ -21,7 +21,7 @@ export declare enum TimingMetric {
|
|
|
21
21
|
requestEnd = "requestEnd",
|
|
22
22
|
visitEnd = "visitEnd"
|
|
23
23
|
}
|
|
24
|
-
export
|
|
24
|
+
export type TimingMetrics = Partial<{
|
|
25
25
|
[metric in TimingMetric]: any;
|
|
26
26
|
}>;
|
|
27
27
|
export declare enum VisitState {
|
|
@@ -31,7 +31,7 @@ export declare enum VisitState {
|
|
|
31
31
|
failed = "failed",
|
|
32
32
|
completed = "completed"
|
|
33
33
|
}
|
|
34
|
-
export
|
|
34
|
+
export type VisitOptions = {
|
|
35
35
|
action: Action;
|
|
36
36
|
historyChanged: boolean;
|
|
37
37
|
referrer?: URL;
|
|
@@ -46,7 +46,7 @@ export declare type VisitOptions = {
|
|
|
46
46
|
frame?: string;
|
|
47
47
|
acceptsStreamResponse: boolean;
|
|
48
48
|
};
|
|
49
|
-
export
|
|
49
|
+
export type VisitResponse = {
|
|
50
50
|
statusCode: number;
|
|
51
51
|
redirected: boolean;
|
|
52
52
|
responseHTML?: string;
|
|
@@ -11,8 +11,8 @@ import { FrameView } from "./frame_view";
|
|
|
11
11
|
import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor";
|
|
12
12
|
import { FormLinkClickObserver, FormLinkClickObserverDelegate } from "../../observers/form_link_click_observer";
|
|
13
13
|
import { VisitOptions } from "../drive/visit";
|
|
14
|
-
|
|
15
|
-
export
|
|
14
|
+
type VisitFallback = (location: Response | Locatable, options: Partial<VisitOptions>) => Promise<void>;
|
|
15
|
+
export type TurboFrameMissingEvent = CustomEvent<{
|
|
16
16
|
response: Response;
|
|
17
17
|
visit: VisitFallback;
|
|
18
18
|
}>;
|
|
@@ -69,11 +69,15 @@ export declare class FrameController implements AppearanceObserverDelegate<Frame
|
|
|
69
69
|
viewInvalidated(): void;
|
|
70
70
|
willRenderFrame(currentElement: FrameElement, _newElement: FrameElement): void;
|
|
71
71
|
visitCachedSnapshot: ({ element }: Snapshot) => void;
|
|
72
|
+
private loadFrameResponse;
|
|
72
73
|
private visit;
|
|
73
74
|
private navigateFrame;
|
|
74
75
|
proposeVisitIfNavigatedWithAction(frame: FrameElement, element: Element, submitter?: HTMLElement): void;
|
|
75
76
|
changeHistory(): void;
|
|
77
|
+
private handleUnvisitableFrameResponse;
|
|
76
78
|
private willHandleFrameMissingFromResponse;
|
|
79
|
+
private handleFrameMissingFromResponse;
|
|
80
|
+
private throwFrameMissingError;
|
|
77
81
|
private visitResponse;
|
|
78
82
|
private findFrameElement;
|
|
79
83
|
extractForeignFrameElement(container: ParentNode): Promise<FrameElement | null>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FrameElement } from "../../elements";
|
|
2
2
|
import { Snapshot } from "../snapshot";
|
|
3
3
|
import { View, ViewRenderOptions } from "../view";
|
|
4
|
-
export
|
|
4
|
+
export type FrameViewRenderOptions = ViewRenderOptions<FrameElement>;
|
|
5
5
|
export declare class FrameView extends View<FrameElement> {
|
|
6
|
-
|
|
6
|
+
missing(): void;
|
|
7
7
|
get snapshot(): Snapshot<FrameElement>;
|
|
8
8
|
}
|
|
@@ -12,10 +12,11 @@ declare const session: Session;
|
|
|
12
12
|
declare const cache: Cache;
|
|
13
13
|
declare const navigator: import("./drive/navigator").Navigator;
|
|
14
14
|
export { navigator, session, cache, PageRenderer, PageSnapshot, FrameRenderer };
|
|
15
|
-
export { TurboBeforeCacheEvent, TurboBeforeRenderEvent, TurboBeforeVisitEvent, TurboClickEvent, TurboBeforeFrameRenderEvent, TurboFrameLoadEvent, TurboFrameRenderEvent, TurboLoadEvent, TurboRenderEvent, TurboVisitEvent, } from "./session";
|
|
16
|
-
export { TurboSubmitStartEvent, TurboSubmitEndEvent } from "./drive/form_submission";
|
|
17
|
-
export { TurboFrameMissingEvent } from "./frames/frame_controller";
|
|
18
|
-
export { StreamActions
|
|
15
|
+
export type { TurboBeforeCacheEvent, TurboBeforeRenderEvent, TurboBeforeVisitEvent, TurboClickEvent, TurboBeforeFrameRenderEvent, TurboFrameLoadEvent, TurboFrameRenderEvent, TurboLoadEvent, TurboRenderEvent, TurboVisitEvent, } from "./session";
|
|
16
|
+
export type { TurboSubmitStartEvent, TurboSubmitEndEvent } from "./drive/form_submission";
|
|
17
|
+
export type { TurboFrameMissingEvent } from "./frames/frame_controller";
|
|
18
|
+
export { StreamActions } from "./streams/stream_actions";
|
|
19
|
+
export type { TurboStreamAction, TurboStreamActions } from "./streams/stream_actions";
|
|
19
20
|
export declare function start(): void;
|
|
20
21
|
export declare function registerAdapter(adapter: Adapter): void;
|
|
21
22
|
export declare function visit(location: Locatable, options?: Partial<VisitOptions>): void;
|
|
@@ -3,7 +3,7 @@ import { ProgressBar } from "../drive/progress_bar";
|
|
|
3
3
|
import { Visit, VisitOptions } from "../drive/visit";
|
|
4
4
|
import { FormSubmission } from "../drive/form_submission";
|
|
5
5
|
import { Session } from "../session";
|
|
6
|
-
export
|
|
6
|
+
export type ReloadReason = StructuredReason | undefined;
|
|
7
7
|
interface StructuredReason {
|
|
8
8
|
reason: string;
|
|
9
9
|
context?: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BardoDelegate } from "./bardo";
|
|
2
2
|
import { Snapshot } from "./snapshot";
|
|
3
3
|
import { ReloadReason } from "./native/browser_adapter";
|
|
4
|
-
export
|
|
4
|
+
export type Render<E> = (currentElement: E, newElement: E) => void;
|
|
5
5
|
export declare abstract class Renderer<E extends Element, S extends Snapshot<E> = Snapshot<E>> implements BardoDelegate {
|
|
6
6
|
readonly currentSnapshot: S;
|
|
7
7
|
readonly newSnapshot: S;
|