@hotwired/turbo 7.2.3 → 7.2.5
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 +107 -59
- package/dist/turbo.es2017-umd.js +107 -59
- package/dist/types/core/bardo.d.ts +1 -1
- package/dist/types/core/drive/form_submission.d.ts +2 -2
- package/dist/types/core/drive/navigator.d.ts +1 -2
- package/dist/types/core/drive/page_renderer.d.ts +4 -2
- package/dist/types/core/drive/visit.d.ts +2 -2
- package/dist/types/core/frames/frame_controller.d.ts +5 -7
- package/dist/types/core/renderer.d.ts +2 -2
- package/dist/types/core/types.d.ts +0 -1
- package/dist/types/http/fetch_request.d.ts +1 -1
- package/dist/types/observers/appearance_observer.d.ts +6 -6
- package/dist/types/observers/link_click_observer.d.ts +1 -1
- package/dist/types/tests/functional/frame_tests.d.ts +7 -0
- package/dist/types/tests/helpers/intern_test_case.d.ts +0 -1
- package/dist/types/tests/helpers/page.d.ts +7 -0
- package/dist/types/tests/{functional → integration}/ujs_tests.d.ts +0 -0
- package/dist/types/util.d.ts +2 -0
- package/package.json +3 -4
package/dist/turbo.es2017-esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Turbo 7.2.
|
|
3
|
-
Copyright ©
|
|
2
|
+
Turbo 7.2.5
|
|
3
|
+
Copyright © 2023 37signals LLC
|
|
4
4
|
*/
|
|
5
5
|
(function () {
|
|
6
6
|
if (window.Reflect === undefined ||
|
|
@@ -307,10 +307,6 @@ class FetchResponse {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
function isAction(action) {
|
|
311
|
-
return action == "advance" || action == "replace" || action == "restore";
|
|
312
|
-
}
|
|
313
|
-
|
|
314
310
|
function activateScriptElement(element) {
|
|
315
311
|
if (element.getAttribute("data-turbo-eval") == "false") {
|
|
316
312
|
return element;
|
|
@@ -341,6 +337,7 @@ function dispatch(eventName, { target, cancelable, detail } = {}) {
|
|
|
341
337
|
const event = new CustomEvent(eventName, {
|
|
342
338
|
cancelable,
|
|
343
339
|
bubbles: true,
|
|
340
|
+
composed: true,
|
|
344
341
|
detail,
|
|
345
342
|
});
|
|
346
343
|
if (target && target.isConnected) {
|
|
@@ -440,6 +437,9 @@ function getHistoryMethodForAction(action) {
|
|
|
440
437
|
return history.pushState;
|
|
441
438
|
}
|
|
442
439
|
}
|
|
440
|
+
function isAction(action) {
|
|
441
|
+
return action == "advance" || action == "replace" || action == "restore";
|
|
442
|
+
}
|
|
443
443
|
function getVisitAction(...elements) {
|
|
444
444
|
const action = getAttribute("data-turbo-action", ...elements);
|
|
445
445
|
return isAction(action) ? action : null;
|
|
@@ -461,6 +461,13 @@ function setMetaContent(name, content) {
|
|
|
461
461
|
element.setAttribute("content", content);
|
|
462
462
|
return element;
|
|
463
463
|
}
|
|
464
|
+
function findClosestRecursively(element, selector) {
|
|
465
|
+
var _a;
|
|
466
|
+
if (element instanceof Element) {
|
|
467
|
+
return (element.closest(selector) ||
|
|
468
|
+
findClosestRecursively(element.assignedSlot || ((_a = element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host), selector));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
464
471
|
|
|
465
472
|
var FetchMethod;
|
|
466
473
|
(function (FetchMethod) {
|
|
@@ -508,9 +515,8 @@ class FetchRequest {
|
|
|
508
515
|
this.abortController.abort();
|
|
509
516
|
}
|
|
510
517
|
async perform() {
|
|
511
|
-
var _a, _b;
|
|
512
518
|
const { fetchOptions } = this;
|
|
513
|
-
|
|
519
|
+
this.delegate.prepareRequest(this);
|
|
514
520
|
await this.allowRequestToBeIntercepted(fetchOptions);
|
|
515
521
|
try {
|
|
516
522
|
this.delegate.requestStarted(this);
|
|
@@ -748,11 +754,11 @@ class FormSubmission {
|
|
|
748
754
|
return true;
|
|
749
755
|
}
|
|
750
756
|
}
|
|
751
|
-
|
|
757
|
+
prepareRequest(request) {
|
|
752
758
|
if (!request.isIdempotent) {
|
|
753
759
|
const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
|
|
754
760
|
if (token) {
|
|
755
|
-
headers["X-CSRF-Token"] = token;
|
|
761
|
+
request.headers["X-CSRF-Token"] = token;
|
|
756
762
|
}
|
|
757
763
|
}
|
|
758
764
|
if (this.requestAcceptsTurboStreamResponse(request)) {
|
|
@@ -940,12 +946,17 @@ function submissionDoesNotDismissDialog(form, submitter) {
|
|
|
940
946
|
return method != "dialog";
|
|
941
947
|
}
|
|
942
948
|
function submissionDoesNotTargetIFrame(form, submitter) {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
949
|
+
if ((submitter === null || submitter === void 0 ? void 0 : submitter.hasAttribute("formtarget")) || form.hasAttribute("target")) {
|
|
950
|
+
const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
|
|
951
|
+
for (const element of document.getElementsByName(target)) {
|
|
952
|
+
if (element instanceof HTMLIFrameElement)
|
|
953
|
+
return false;
|
|
954
|
+
}
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
return true;
|
|
947
959
|
}
|
|
948
|
-
return true;
|
|
949
960
|
}
|
|
950
961
|
|
|
951
962
|
class View {
|
|
@@ -1138,20 +1149,23 @@ class LinkClickObserver {
|
|
|
1138
1149
|
event.shiftKey);
|
|
1139
1150
|
}
|
|
1140
1151
|
findLinkFromClickTarget(target) {
|
|
1141
|
-
|
|
1142
|
-
return target.closest("a[href]:not([target^=_]):not([download])");
|
|
1143
|
-
}
|
|
1152
|
+
return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
|
|
1144
1153
|
}
|
|
1145
1154
|
getLocationForLink(link) {
|
|
1146
1155
|
return expandURL(link.getAttribute("href") || "");
|
|
1147
1156
|
}
|
|
1148
1157
|
}
|
|
1149
1158
|
function doesNotTargetIFrame(anchor) {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1159
|
+
if (anchor.hasAttribute("target")) {
|
|
1160
|
+
for (const element of document.getElementsByName(anchor.target)) {
|
|
1161
|
+
if (element instanceof HTMLIFrameElement)
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
return true;
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
return true;
|
|
1153
1168
|
}
|
|
1154
|
-
return true;
|
|
1155
1169
|
}
|
|
1156
1170
|
|
|
1157
1171
|
class FormLinkClickObserver {
|
|
@@ -1170,10 +1184,14 @@ class FormLinkClickObserver {
|
|
|
1170
1184
|
link.hasAttribute("data-turbo-method"));
|
|
1171
1185
|
}
|
|
1172
1186
|
followedLinkToLocation(link, location) {
|
|
1173
|
-
const action = location.href;
|
|
1174
1187
|
const form = document.createElement("form");
|
|
1188
|
+
const type = "hidden";
|
|
1189
|
+
for (const [name, value] of location.searchParams) {
|
|
1190
|
+
form.append(Object.assign(document.createElement("input"), { type, name, value }));
|
|
1191
|
+
}
|
|
1192
|
+
const action = Object.assign(location, { search: "" });
|
|
1175
1193
|
form.setAttribute("data-turbo", "true");
|
|
1176
|
-
form.setAttribute("action", action);
|
|
1194
|
+
form.setAttribute("action", action.href);
|
|
1177
1195
|
form.setAttribute("hidden", "");
|
|
1178
1196
|
const method = link.getAttribute("data-turbo-method");
|
|
1179
1197
|
if (method)
|
|
@@ -1181,7 +1199,7 @@ class FormLinkClickObserver {
|
|
|
1181
1199
|
const turboFrame = link.getAttribute("data-turbo-frame");
|
|
1182
1200
|
if (turboFrame)
|
|
1183
1201
|
form.setAttribute("data-turbo-frame", turboFrame);
|
|
1184
|
-
const turboAction = link
|
|
1202
|
+
const turboAction = getVisitAction(link);
|
|
1185
1203
|
if (turboAction)
|
|
1186
1204
|
form.setAttribute("data-turbo-action", turboAction);
|
|
1187
1205
|
const turboConfirm = link.getAttribute("data-turbo-confirm");
|
|
@@ -1202,10 +1220,10 @@ class Bardo {
|
|
|
1202
1220
|
this.delegate = delegate;
|
|
1203
1221
|
this.permanentElementMap = permanentElementMap;
|
|
1204
1222
|
}
|
|
1205
|
-
static preservingPermanentElements(delegate, permanentElementMap, callback) {
|
|
1223
|
+
static async preservingPermanentElements(delegate, permanentElementMap, callback) {
|
|
1206
1224
|
const bardo = new this(delegate, permanentElementMap);
|
|
1207
1225
|
bardo.enter();
|
|
1208
|
-
callback();
|
|
1226
|
+
await callback();
|
|
1209
1227
|
bardo.leave();
|
|
1210
1228
|
}
|
|
1211
1229
|
enter() {
|
|
@@ -1274,8 +1292,8 @@ class Renderer {
|
|
|
1274
1292
|
delete this.resolvingFunctions;
|
|
1275
1293
|
}
|
|
1276
1294
|
}
|
|
1277
|
-
preservingPermanentElements(callback) {
|
|
1278
|
-
Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
|
|
1295
|
+
async preservingPermanentElements(callback) {
|
|
1296
|
+
await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
|
|
1279
1297
|
}
|
|
1280
1298
|
focusFirstAutofocusableElement() {
|
|
1281
1299
|
const element = this.connectedSnapshot.firstAutofocusableElement;
|
|
@@ -1860,6 +1878,8 @@ class Visit {
|
|
|
1860
1878
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1861
1879
|
action: "replace",
|
|
1862
1880
|
response: this.response,
|
|
1881
|
+
shouldCacheSnapshot: false,
|
|
1882
|
+
willRender: false,
|
|
1863
1883
|
});
|
|
1864
1884
|
this.followedRedirect = true;
|
|
1865
1885
|
}
|
|
@@ -1869,11 +1889,12 @@ class Visit {
|
|
|
1869
1889
|
this.render(async () => {
|
|
1870
1890
|
this.cacheSnapshot();
|
|
1871
1891
|
this.performScroll();
|
|
1892
|
+
this.changeHistory();
|
|
1872
1893
|
this.adapter.visitRendered(this);
|
|
1873
1894
|
});
|
|
1874
1895
|
}
|
|
1875
1896
|
}
|
|
1876
|
-
|
|
1897
|
+
prepareRequest(request) {
|
|
1877
1898
|
if (this.acceptsStreamResponse) {
|
|
1878
1899
|
request.acceptResponseType(StreamMessage.contentType);
|
|
1879
1900
|
}
|
|
@@ -2277,7 +2298,6 @@ class Navigator {
|
|
|
2277
2298
|
}
|
|
2278
2299
|
}
|
|
2279
2300
|
startVisit(locatable, restorationIdentifier, options = {}) {
|
|
2280
|
-
this.lastVisit = this.currentVisit;
|
|
2281
2301
|
this.stop();
|
|
2282
2302
|
this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
|
|
2283
2303
|
this.currentVisit.start();
|
|
@@ -2359,13 +2379,11 @@ class Navigator {
|
|
|
2359
2379
|
this.delegate.visitCompleted(visit);
|
|
2360
2380
|
}
|
|
2361
2381
|
locationWithActionIsSamePage(location, action) {
|
|
2362
|
-
var _a;
|
|
2363
2382
|
const anchor = getAnchor(location);
|
|
2364
|
-
const
|
|
2365
|
-
const currentAnchor = getAnchor(lastLocation);
|
|
2383
|
+
const currentAnchor = getAnchor(this.view.lastRenderedLocation);
|
|
2366
2384
|
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
|
|
2367
2385
|
return (action !== "replace" &&
|
|
2368
|
-
getRequestURL(location) === getRequestURL(
|
|
2386
|
+
getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
|
|
2369
2387
|
(isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
|
|
2370
2388
|
}
|
|
2371
2389
|
visitScrolledToSamePageLocation(oldURL, newURL) {
|
|
@@ -2377,10 +2395,8 @@ class Navigator {
|
|
|
2377
2395
|
get restorationIdentifier() {
|
|
2378
2396
|
return this.history.restorationIdentifier;
|
|
2379
2397
|
}
|
|
2380
|
-
getActionForFormSubmission(
|
|
2381
|
-
|
|
2382
|
-
const action = getAttribute("data-turbo-action", submitter, formElement);
|
|
2383
|
-
return isAction(action) ? action : "advance";
|
|
2398
|
+
getActionForFormSubmission({ submitter, formElement }) {
|
|
2399
|
+
return getVisitAction(submitter, formElement) || "advance";
|
|
2384
2400
|
}
|
|
2385
2401
|
}
|
|
2386
2402
|
|
|
@@ -2622,7 +2638,7 @@ class PageRenderer extends Renderer {
|
|
|
2622
2638
|
}
|
|
2623
2639
|
async render() {
|
|
2624
2640
|
if (this.willRender) {
|
|
2625
|
-
this.replaceBody();
|
|
2641
|
+
await this.replaceBody();
|
|
2626
2642
|
}
|
|
2627
2643
|
}
|
|
2628
2644
|
finishRendering() {
|
|
@@ -2641,16 +2657,16 @@ class PageRenderer extends Renderer {
|
|
|
2641
2657
|
return this.newSnapshot.element;
|
|
2642
2658
|
}
|
|
2643
2659
|
async mergeHead() {
|
|
2660
|
+
const mergedHeadElements = this.mergeProvisionalElements();
|
|
2644
2661
|
const newStylesheetElements = this.copyNewHeadStylesheetElements();
|
|
2645
2662
|
this.copyNewHeadScriptElements();
|
|
2646
|
-
|
|
2647
|
-
this.copyNewHeadProvisionalElements();
|
|
2663
|
+
await mergedHeadElements;
|
|
2648
2664
|
await newStylesheetElements;
|
|
2649
2665
|
}
|
|
2650
|
-
replaceBody() {
|
|
2651
|
-
this.preservingPermanentElements(() => {
|
|
2666
|
+
async replaceBody() {
|
|
2667
|
+
await this.preservingPermanentElements(async () => {
|
|
2652
2668
|
this.activateNewBody();
|
|
2653
|
-
this.assignNewBody();
|
|
2669
|
+
await this.assignNewBody();
|
|
2654
2670
|
});
|
|
2655
2671
|
}
|
|
2656
2672
|
get trackedElementsAreIdentical() {
|
|
@@ -2669,6 +2685,35 @@ class PageRenderer extends Renderer {
|
|
|
2669
2685
|
document.head.appendChild(activateScriptElement(element));
|
|
2670
2686
|
}
|
|
2671
2687
|
}
|
|
2688
|
+
async mergeProvisionalElements() {
|
|
2689
|
+
const newHeadElements = [...this.newHeadProvisionalElements];
|
|
2690
|
+
for (const element of this.currentHeadProvisionalElements) {
|
|
2691
|
+
if (!this.isCurrentElementInElementList(element, newHeadElements)) {
|
|
2692
|
+
document.head.removeChild(element);
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
for (const element of newHeadElements) {
|
|
2696
|
+
document.head.appendChild(element);
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
isCurrentElementInElementList(element, elementList) {
|
|
2700
|
+
for (const [index, newElement] of elementList.entries()) {
|
|
2701
|
+
if (element.tagName == "TITLE") {
|
|
2702
|
+
if (newElement.tagName != "TITLE") {
|
|
2703
|
+
continue;
|
|
2704
|
+
}
|
|
2705
|
+
if (element.innerHTML == newElement.innerHTML) {
|
|
2706
|
+
elementList.splice(index, 1);
|
|
2707
|
+
return true;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
if (newElement.isEqualNode(element)) {
|
|
2711
|
+
elementList.splice(index, 1);
|
|
2712
|
+
return true;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
return false;
|
|
2716
|
+
}
|
|
2672
2717
|
removeCurrentHeadProvisionalElements() {
|
|
2673
2718
|
for (const element of this.currentHeadProvisionalElements) {
|
|
2674
2719
|
document.head.removeChild(element);
|
|
@@ -2689,8 +2734,8 @@ class PageRenderer extends Renderer {
|
|
|
2689
2734
|
inertScriptElement.replaceWith(activatedScriptElement);
|
|
2690
2735
|
}
|
|
2691
2736
|
}
|
|
2692
|
-
assignNewBody() {
|
|
2693
|
-
this.renderElement(this.currentElement, this.newElement);
|
|
2737
|
+
async assignNewBody() {
|
|
2738
|
+
await this.renderElement(this.currentElement, this.newElement);
|
|
2694
2739
|
}
|
|
2695
2740
|
get newHeadStylesheetElements() {
|
|
2696
2741
|
return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
|
|
@@ -3107,8 +3152,8 @@ class Session {
|
|
|
3107
3152
|
}
|
|
3108
3153
|
}
|
|
3109
3154
|
elementIsNavigatable(element) {
|
|
3110
|
-
const container = element
|
|
3111
|
-
const withinFrame = element
|
|
3155
|
+
const container = findClosestRecursively(element, "[data-turbo]");
|
|
3156
|
+
const withinFrame = findClosestRecursively(element, "turbo-frame");
|
|
3112
3157
|
if (this.drive || withinFrame) {
|
|
3113
3158
|
if (container) {
|
|
3114
3159
|
return container.getAttribute("data-turbo") != "false";
|
|
@@ -3127,8 +3172,7 @@ class Session {
|
|
|
3127
3172
|
}
|
|
3128
3173
|
}
|
|
3129
3174
|
getActionForLink(link) {
|
|
3130
|
-
|
|
3131
|
-
return isAction(action) ? action : "advance";
|
|
3175
|
+
return getVisitAction(link) || "advance";
|
|
3132
3176
|
}
|
|
3133
3177
|
get snapshot() {
|
|
3134
3178
|
return this.view.snapshot;
|
|
@@ -3188,7 +3232,10 @@ const StreamActions = {
|
|
|
3188
3232
|
this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
|
|
3189
3233
|
},
|
|
3190
3234
|
update() {
|
|
3191
|
-
this.targetElements.forEach((
|
|
3235
|
+
this.targetElements.forEach((targetElement) => {
|
|
3236
|
+
targetElement.innerHTML = "";
|
|
3237
|
+
targetElement.append(this.templateContent);
|
|
3238
|
+
});
|
|
3192
3239
|
},
|
|
3193
3240
|
};
|
|
3194
3241
|
|
|
@@ -3376,7 +3423,8 @@ class FrameController {
|
|
|
3376
3423
|
this.fetchResponseLoaded = () => { };
|
|
3377
3424
|
}
|
|
3378
3425
|
}
|
|
3379
|
-
elementAppearedInViewport(
|
|
3426
|
+
elementAppearedInViewport(element) {
|
|
3427
|
+
this.proposeVisitIfNavigatedWithAction(element, element);
|
|
3380
3428
|
this.loadSourceURL();
|
|
3381
3429
|
}
|
|
3382
3430
|
willSubmitFormLinkToLocation(link) {
|
|
@@ -3402,12 +3450,12 @@ class FrameController {
|
|
|
3402
3450
|
}
|
|
3403
3451
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
|
3404
3452
|
const { fetchRequest } = this.formSubmission;
|
|
3405
|
-
this.
|
|
3453
|
+
this.prepareRequest(fetchRequest);
|
|
3406
3454
|
this.formSubmission.start();
|
|
3407
3455
|
}
|
|
3408
|
-
|
|
3456
|
+
prepareRequest(request) {
|
|
3409
3457
|
var _a;
|
|
3410
|
-
headers["Turbo-Frame"] = this.id;
|
|
3458
|
+
request.headers["Turbo-Frame"] = this.id;
|
|
3411
3459
|
if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
|
|
3412
3460
|
request.acceptResponseType(StreamMessage.contentType);
|
|
3413
3461
|
}
|
|
@@ -3487,7 +3535,6 @@ class FrameController {
|
|
|
3487
3535
|
}
|
|
3488
3536
|
navigateFrame(element, url, submitter) {
|
|
3489
3537
|
const frame = this.findFrameElement(element, submitter);
|
|
3490
|
-
this.pageSnapshot = PageSnapshot.fromElement(frame).clone();
|
|
3491
3538
|
frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
|
|
3492
3539
|
this.withCurrentNavigationElement(element, () => {
|
|
3493
3540
|
frame.src = url;
|
|
@@ -3495,7 +3542,8 @@ class FrameController {
|
|
|
3495
3542
|
}
|
|
3496
3543
|
proposeVisitIfNavigatedWithAction(frame, element, submitter) {
|
|
3497
3544
|
this.action = getVisitAction(submitter, element, frame);
|
|
3498
|
-
if (
|
|
3545
|
+
if (this.action) {
|
|
3546
|
+
const pageSnapshot = PageSnapshot.fromElement(frame).clone();
|
|
3499
3547
|
const { visitCachedSnapshot } = frame.delegate;
|
|
3500
3548
|
frame.delegate.fetchResponseLoaded = (fetchResponse) => {
|
|
3501
3549
|
if (frame.src) {
|
|
@@ -3508,7 +3556,7 @@ class FrameController {
|
|
|
3508
3556
|
willRender: false,
|
|
3509
3557
|
updateHistory: false,
|
|
3510
3558
|
restorationIdentifier: this.restorationIdentifier,
|
|
3511
|
-
snapshot:
|
|
3559
|
+
snapshot: pageSnapshot,
|
|
3512
3560
|
};
|
|
3513
3561
|
if (this.action)
|
|
3514
3562
|
options.action = this.action;
|
package/dist/turbo.es2017-umd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Turbo 7.2.
|
|
3
|
-
Copyright ©
|
|
2
|
+
Turbo 7.2.5
|
|
3
|
+
Copyright © 2023 37signals LLC
|
|
4
4
|
*/
|
|
5
5
|
(function (global, factory) {
|
|
6
6
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
@@ -313,10 +313,6 @@ Copyright © 2022 37signals LLC
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
function isAction(action) {
|
|
317
|
-
return action == "advance" || action == "replace" || action == "restore";
|
|
318
|
-
}
|
|
319
|
-
|
|
320
316
|
function activateScriptElement(element) {
|
|
321
317
|
if (element.getAttribute("data-turbo-eval") == "false") {
|
|
322
318
|
return element;
|
|
@@ -347,6 +343,7 @@ Copyright © 2022 37signals LLC
|
|
|
347
343
|
const event = new CustomEvent(eventName, {
|
|
348
344
|
cancelable,
|
|
349
345
|
bubbles: true,
|
|
346
|
+
composed: true,
|
|
350
347
|
detail,
|
|
351
348
|
});
|
|
352
349
|
if (target && target.isConnected) {
|
|
@@ -446,6 +443,9 @@ Copyright © 2022 37signals LLC
|
|
|
446
443
|
return history.pushState;
|
|
447
444
|
}
|
|
448
445
|
}
|
|
446
|
+
function isAction(action) {
|
|
447
|
+
return action == "advance" || action == "replace" || action == "restore";
|
|
448
|
+
}
|
|
449
449
|
function getVisitAction(...elements) {
|
|
450
450
|
const action = getAttribute("data-turbo-action", ...elements);
|
|
451
451
|
return isAction(action) ? action : null;
|
|
@@ -467,6 +467,13 @@ Copyright © 2022 37signals LLC
|
|
|
467
467
|
element.setAttribute("content", content);
|
|
468
468
|
return element;
|
|
469
469
|
}
|
|
470
|
+
function findClosestRecursively(element, selector) {
|
|
471
|
+
var _a;
|
|
472
|
+
if (element instanceof Element) {
|
|
473
|
+
return (element.closest(selector) ||
|
|
474
|
+
findClosestRecursively(element.assignedSlot || ((_a = element.getRootNode()) === null || _a === void 0 ? void 0 : _a.host), selector));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
470
477
|
|
|
471
478
|
var FetchMethod;
|
|
472
479
|
(function (FetchMethod) {
|
|
@@ -514,9 +521,8 @@ Copyright © 2022 37signals LLC
|
|
|
514
521
|
this.abortController.abort();
|
|
515
522
|
}
|
|
516
523
|
async perform() {
|
|
517
|
-
var _a, _b;
|
|
518
524
|
const { fetchOptions } = this;
|
|
519
|
-
|
|
525
|
+
this.delegate.prepareRequest(this);
|
|
520
526
|
await this.allowRequestToBeIntercepted(fetchOptions);
|
|
521
527
|
try {
|
|
522
528
|
this.delegate.requestStarted(this);
|
|
@@ -754,11 +760,11 @@ Copyright © 2022 37signals LLC
|
|
|
754
760
|
return true;
|
|
755
761
|
}
|
|
756
762
|
}
|
|
757
|
-
|
|
763
|
+
prepareRequest(request) {
|
|
758
764
|
if (!request.isIdempotent) {
|
|
759
765
|
const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
|
|
760
766
|
if (token) {
|
|
761
|
-
headers["X-CSRF-Token"] = token;
|
|
767
|
+
request.headers["X-CSRF-Token"] = token;
|
|
762
768
|
}
|
|
763
769
|
}
|
|
764
770
|
if (this.requestAcceptsTurboStreamResponse(request)) {
|
|
@@ -946,12 +952,17 @@ Copyright © 2022 37signals LLC
|
|
|
946
952
|
return method != "dialog";
|
|
947
953
|
}
|
|
948
954
|
function submissionDoesNotTargetIFrame(form, submitter) {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
955
|
+
if ((submitter === null || submitter === void 0 ? void 0 : submitter.hasAttribute("formtarget")) || form.hasAttribute("target")) {
|
|
956
|
+
const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
|
|
957
|
+
for (const element of document.getElementsByName(target)) {
|
|
958
|
+
if (element instanceof HTMLIFrameElement)
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
return true;
|
|
953
965
|
}
|
|
954
|
-
return true;
|
|
955
966
|
}
|
|
956
967
|
|
|
957
968
|
class View {
|
|
@@ -1144,20 +1155,23 @@ Copyright © 2022 37signals LLC
|
|
|
1144
1155
|
event.shiftKey);
|
|
1145
1156
|
}
|
|
1146
1157
|
findLinkFromClickTarget(target) {
|
|
1147
|
-
|
|
1148
|
-
return target.closest("a[href]:not([target^=_]):not([download])");
|
|
1149
|
-
}
|
|
1158
|
+
return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
|
|
1150
1159
|
}
|
|
1151
1160
|
getLocationForLink(link) {
|
|
1152
1161
|
return expandURL(link.getAttribute("href") || "");
|
|
1153
1162
|
}
|
|
1154
1163
|
}
|
|
1155
1164
|
function doesNotTargetIFrame(anchor) {
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1165
|
+
if (anchor.hasAttribute("target")) {
|
|
1166
|
+
for (const element of document.getElementsByName(anchor.target)) {
|
|
1167
|
+
if (element instanceof HTMLIFrameElement)
|
|
1168
|
+
return false;
|
|
1169
|
+
}
|
|
1170
|
+
return true;
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
return true;
|
|
1159
1174
|
}
|
|
1160
|
-
return true;
|
|
1161
1175
|
}
|
|
1162
1176
|
|
|
1163
1177
|
class FormLinkClickObserver {
|
|
@@ -1176,10 +1190,14 @@ Copyright © 2022 37signals LLC
|
|
|
1176
1190
|
link.hasAttribute("data-turbo-method"));
|
|
1177
1191
|
}
|
|
1178
1192
|
followedLinkToLocation(link, location) {
|
|
1179
|
-
const action = location.href;
|
|
1180
1193
|
const form = document.createElement("form");
|
|
1194
|
+
const type = "hidden";
|
|
1195
|
+
for (const [name, value] of location.searchParams) {
|
|
1196
|
+
form.append(Object.assign(document.createElement("input"), { type, name, value }));
|
|
1197
|
+
}
|
|
1198
|
+
const action = Object.assign(location, { search: "" });
|
|
1181
1199
|
form.setAttribute("data-turbo", "true");
|
|
1182
|
-
form.setAttribute("action", action);
|
|
1200
|
+
form.setAttribute("action", action.href);
|
|
1183
1201
|
form.setAttribute("hidden", "");
|
|
1184
1202
|
const method = link.getAttribute("data-turbo-method");
|
|
1185
1203
|
if (method)
|
|
@@ -1187,7 +1205,7 @@ Copyright © 2022 37signals LLC
|
|
|
1187
1205
|
const turboFrame = link.getAttribute("data-turbo-frame");
|
|
1188
1206
|
if (turboFrame)
|
|
1189
1207
|
form.setAttribute("data-turbo-frame", turboFrame);
|
|
1190
|
-
const turboAction = link
|
|
1208
|
+
const turboAction = getVisitAction(link);
|
|
1191
1209
|
if (turboAction)
|
|
1192
1210
|
form.setAttribute("data-turbo-action", turboAction);
|
|
1193
1211
|
const turboConfirm = link.getAttribute("data-turbo-confirm");
|
|
@@ -1208,10 +1226,10 @@ Copyright © 2022 37signals LLC
|
|
|
1208
1226
|
this.delegate = delegate;
|
|
1209
1227
|
this.permanentElementMap = permanentElementMap;
|
|
1210
1228
|
}
|
|
1211
|
-
static preservingPermanentElements(delegate, permanentElementMap, callback) {
|
|
1229
|
+
static async preservingPermanentElements(delegate, permanentElementMap, callback) {
|
|
1212
1230
|
const bardo = new this(delegate, permanentElementMap);
|
|
1213
1231
|
bardo.enter();
|
|
1214
|
-
callback();
|
|
1232
|
+
await callback();
|
|
1215
1233
|
bardo.leave();
|
|
1216
1234
|
}
|
|
1217
1235
|
enter() {
|
|
@@ -1280,8 +1298,8 @@ Copyright © 2022 37signals LLC
|
|
|
1280
1298
|
delete this.resolvingFunctions;
|
|
1281
1299
|
}
|
|
1282
1300
|
}
|
|
1283
|
-
preservingPermanentElements(callback) {
|
|
1284
|
-
Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
|
|
1301
|
+
async preservingPermanentElements(callback) {
|
|
1302
|
+
await Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
|
|
1285
1303
|
}
|
|
1286
1304
|
focusFirstAutofocusableElement() {
|
|
1287
1305
|
const element = this.connectedSnapshot.firstAutofocusableElement;
|
|
@@ -1866,6 +1884,8 @@ Copyright © 2022 37signals LLC
|
|
|
1866
1884
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1867
1885
|
action: "replace",
|
|
1868
1886
|
response: this.response,
|
|
1887
|
+
shouldCacheSnapshot: false,
|
|
1888
|
+
willRender: false,
|
|
1869
1889
|
});
|
|
1870
1890
|
this.followedRedirect = true;
|
|
1871
1891
|
}
|
|
@@ -1875,11 +1895,12 @@ Copyright © 2022 37signals LLC
|
|
|
1875
1895
|
this.render(async () => {
|
|
1876
1896
|
this.cacheSnapshot();
|
|
1877
1897
|
this.performScroll();
|
|
1898
|
+
this.changeHistory();
|
|
1878
1899
|
this.adapter.visitRendered(this);
|
|
1879
1900
|
});
|
|
1880
1901
|
}
|
|
1881
1902
|
}
|
|
1882
|
-
|
|
1903
|
+
prepareRequest(request) {
|
|
1883
1904
|
if (this.acceptsStreamResponse) {
|
|
1884
1905
|
request.acceptResponseType(StreamMessage.contentType);
|
|
1885
1906
|
}
|
|
@@ -2283,7 +2304,6 @@ Copyright © 2022 37signals LLC
|
|
|
2283
2304
|
}
|
|
2284
2305
|
}
|
|
2285
2306
|
startVisit(locatable, restorationIdentifier, options = {}) {
|
|
2286
|
-
this.lastVisit = this.currentVisit;
|
|
2287
2307
|
this.stop();
|
|
2288
2308
|
this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
|
|
2289
2309
|
this.currentVisit.start();
|
|
@@ -2365,13 +2385,11 @@ Copyright © 2022 37signals LLC
|
|
|
2365
2385
|
this.delegate.visitCompleted(visit);
|
|
2366
2386
|
}
|
|
2367
2387
|
locationWithActionIsSamePage(location, action) {
|
|
2368
|
-
var _a;
|
|
2369
2388
|
const anchor = getAnchor(location);
|
|
2370
|
-
const
|
|
2371
|
-
const currentAnchor = getAnchor(lastLocation);
|
|
2389
|
+
const currentAnchor = getAnchor(this.view.lastRenderedLocation);
|
|
2372
2390
|
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
|
|
2373
2391
|
return (action !== "replace" &&
|
|
2374
|
-
getRequestURL(location) === getRequestURL(
|
|
2392
|
+
getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) &&
|
|
2375
2393
|
(isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
|
|
2376
2394
|
}
|
|
2377
2395
|
visitScrolledToSamePageLocation(oldURL, newURL) {
|
|
@@ -2383,10 +2401,8 @@ Copyright © 2022 37signals LLC
|
|
|
2383
2401
|
get restorationIdentifier() {
|
|
2384
2402
|
return this.history.restorationIdentifier;
|
|
2385
2403
|
}
|
|
2386
|
-
getActionForFormSubmission(
|
|
2387
|
-
|
|
2388
|
-
const action = getAttribute("data-turbo-action", submitter, formElement);
|
|
2389
|
-
return isAction(action) ? action : "advance";
|
|
2404
|
+
getActionForFormSubmission({ submitter, formElement }) {
|
|
2405
|
+
return getVisitAction(submitter, formElement) || "advance";
|
|
2390
2406
|
}
|
|
2391
2407
|
}
|
|
2392
2408
|
|
|
@@ -2628,7 +2644,7 @@ Copyright © 2022 37signals LLC
|
|
|
2628
2644
|
}
|
|
2629
2645
|
async render() {
|
|
2630
2646
|
if (this.willRender) {
|
|
2631
|
-
this.replaceBody();
|
|
2647
|
+
await this.replaceBody();
|
|
2632
2648
|
}
|
|
2633
2649
|
}
|
|
2634
2650
|
finishRendering() {
|
|
@@ -2647,16 +2663,16 @@ Copyright © 2022 37signals LLC
|
|
|
2647
2663
|
return this.newSnapshot.element;
|
|
2648
2664
|
}
|
|
2649
2665
|
async mergeHead() {
|
|
2666
|
+
const mergedHeadElements = this.mergeProvisionalElements();
|
|
2650
2667
|
const newStylesheetElements = this.copyNewHeadStylesheetElements();
|
|
2651
2668
|
this.copyNewHeadScriptElements();
|
|
2652
|
-
|
|
2653
|
-
this.copyNewHeadProvisionalElements();
|
|
2669
|
+
await mergedHeadElements;
|
|
2654
2670
|
await newStylesheetElements;
|
|
2655
2671
|
}
|
|
2656
|
-
replaceBody() {
|
|
2657
|
-
this.preservingPermanentElements(() => {
|
|
2672
|
+
async replaceBody() {
|
|
2673
|
+
await this.preservingPermanentElements(async () => {
|
|
2658
2674
|
this.activateNewBody();
|
|
2659
|
-
this.assignNewBody();
|
|
2675
|
+
await this.assignNewBody();
|
|
2660
2676
|
});
|
|
2661
2677
|
}
|
|
2662
2678
|
get trackedElementsAreIdentical() {
|
|
@@ -2675,6 +2691,35 @@ Copyright © 2022 37signals LLC
|
|
|
2675
2691
|
document.head.appendChild(activateScriptElement(element));
|
|
2676
2692
|
}
|
|
2677
2693
|
}
|
|
2694
|
+
async mergeProvisionalElements() {
|
|
2695
|
+
const newHeadElements = [...this.newHeadProvisionalElements];
|
|
2696
|
+
for (const element of this.currentHeadProvisionalElements) {
|
|
2697
|
+
if (!this.isCurrentElementInElementList(element, newHeadElements)) {
|
|
2698
|
+
document.head.removeChild(element);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
for (const element of newHeadElements) {
|
|
2702
|
+
document.head.appendChild(element);
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
isCurrentElementInElementList(element, elementList) {
|
|
2706
|
+
for (const [index, newElement] of elementList.entries()) {
|
|
2707
|
+
if (element.tagName == "TITLE") {
|
|
2708
|
+
if (newElement.tagName != "TITLE") {
|
|
2709
|
+
continue;
|
|
2710
|
+
}
|
|
2711
|
+
if (element.innerHTML == newElement.innerHTML) {
|
|
2712
|
+
elementList.splice(index, 1);
|
|
2713
|
+
return true;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
if (newElement.isEqualNode(element)) {
|
|
2717
|
+
elementList.splice(index, 1);
|
|
2718
|
+
return true;
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
return false;
|
|
2722
|
+
}
|
|
2678
2723
|
removeCurrentHeadProvisionalElements() {
|
|
2679
2724
|
for (const element of this.currentHeadProvisionalElements) {
|
|
2680
2725
|
document.head.removeChild(element);
|
|
@@ -2695,8 +2740,8 @@ Copyright © 2022 37signals LLC
|
|
|
2695
2740
|
inertScriptElement.replaceWith(activatedScriptElement);
|
|
2696
2741
|
}
|
|
2697
2742
|
}
|
|
2698
|
-
assignNewBody() {
|
|
2699
|
-
this.renderElement(this.currentElement, this.newElement);
|
|
2743
|
+
async assignNewBody() {
|
|
2744
|
+
await this.renderElement(this.currentElement, this.newElement);
|
|
2700
2745
|
}
|
|
2701
2746
|
get newHeadStylesheetElements() {
|
|
2702
2747
|
return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
|
|
@@ -3113,8 +3158,8 @@ Copyright © 2022 37signals LLC
|
|
|
3113
3158
|
}
|
|
3114
3159
|
}
|
|
3115
3160
|
elementIsNavigatable(element) {
|
|
3116
|
-
const container = element
|
|
3117
|
-
const withinFrame = element
|
|
3161
|
+
const container = findClosestRecursively(element, "[data-turbo]");
|
|
3162
|
+
const withinFrame = findClosestRecursively(element, "turbo-frame");
|
|
3118
3163
|
if (this.drive || withinFrame) {
|
|
3119
3164
|
if (container) {
|
|
3120
3165
|
return container.getAttribute("data-turbo") != "false";
|
|
@@ -3133,8 +3178,7 @@ Copyright © 2022 37signals LLC
|
|
|
3133
3178
|
}
|
|
3134
3179
|
}
|
|
3135
3180
|
getActionForLink(link) {
|
|
3136
|
-
|
|
3137
|
-
return isAction(action) ? action : "advance";
|
|
3181
|
+
return getVisitAction(link) || "advance";
|
|
3138
3182
|
}
|
|
3139
3183
|
get snapshot() {
|
|
3140
3184
|
return this.view.snapshot;
|
|
@@ -3194,7 +3238,10 @@ Copyright © 2022 37signals LLC
|
|
|
3194
3238
|
this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
|
|
3195
3239
|
},
|
|
3196
3240
|
update() {
|
|
3197
|
-
this.targetElements.forEach((
|
|
3241
|
+
this.targetElements.forEach((targetElement) => {
|
|
3242
|
+
targetElement.innerHTML = "";
|
|
3243
|
+
targetElement.append(this.templateContent);
|
|
3244
|
+
});
|
|
3198
3245
|
},
|
|
3199
3246
|
};
|
|
3200
3247
|
|
|
@@ -3382,7 +3429,8 @@ Copyright © 2022 37signals LLC
|
|
|
3382
3429
|
this.fetchResponseLoaded = () => { };
|
|
3383
3430
|
}
|
|
3384
3431
|
}
|
|
3385
|
-
elementAppearedInViewport(
|
|
3432
|
+
elementAppearedInViewport(element) {
|
|
3433
|
+
this.proposeVisitIfNavigatedWithAction(element, element);
|
|
3386
3434
|
this.loadSourceURL();
|
|
3387
3435
|
}
|
|
3388
3436
|
willSubmitFormLinkToLocation(link) {
|
|
@@ -3408,12 +3456,12 @@ Copyright © 2022 37signals LLC
|
|
|
3408
3456
|
}
|
|
3409
3457
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
|
3410
3458
|
const { fetchRequest } = this.formSubmission;
|
|
3411
|
-
this.
|
|
3459
|
+
this.prepareRequest(fetchRequest);
|
|
3412
3460
|
this.formSubmission.start();
|
|
3413
3461
|
}
|
|
3414
|
-
|
|
3462
|
+
prepareRequest(request) {
|
|
3415
3463
|
var _a;
|
|
3416
|
-
headers["Turbo-Frame"] = this.id;
|
|
3464
|
+
request.headers["Turbo-Frame"] = this.id;
|
|
3417
3465
|
if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
|
|
3418
3466
|
request.acceptResponseType(StreamMessage.contentType);
|
|
3419
3467
|
}
|
|
@@ -3493,7 +3541,6 @@ Copyright © 2022 37signals LLC
|
|
|
3493
3541
|
}
|
|
3494
3542
|
navigateFrame(element, url, submitter) {
|
|
3495
3543
|
const frame = this.findFrameElement(element, submitter);
|
|
3496
|
-
this.pageSnapshot = PageSnapshot.fromElement(frame).clone();
|
|
3497
3544
|
frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
|
|
3498
3545
|
this.withCurrentNavigationElement(element, () => {
|
|
3499
3546
|
frame.src = url;
|
|
@@ -3501,7 +3548,8 @@ Copyright © 2022 37signals LLC
|
|
|
3501
3548
|
}
|
|
3502
3549
|
proposeVisitIfNavigatedWithAction(frame, element, submitter) {
|
|
3503
3550
|
this.action = getVisitAction(submitter, element, frame);
|
|
3504
|
-
if (
|
|
3551
|
+
if (this.action) {
|
|
3552
|
+
const pageSnapshot = PageSnapshot.fromElement(frame).clone();
|
|
3505
3553
|
const { visitCachedSnapshot } = frame.delegate;
|
|
3506
3554
|
frame.delegate.fetchResponseLoaded = (fetchResponse) => {
|
|
3507
3555
|
if (frame.src) {
|
|
@@ -3514,7 +3562,7 @@ Copyright © 2022 37signals LLC
|
|
|
3514
3562
|
willRender: false,
|
|
3515
3563
|
updateHistory: false,
|
|
3516
3564
|
restorationIdentifier: this.restorationIdentifier,
|
|
3517
|
-
snapshot:
|
|
3565
|
+
snapshot: pageSnapshot,
|
|
3518
3566
|
};
|
|
3519
3567
|
if (this.action)
|
|
3520
3568
|
options.action = this.action;
|
|
@@ -6,7 +6,7 @@ export interface BardoDelegate {
|
|
|
6
6
|
export declare class Bardo {
|
|
7
7
|
readonly permanentElementMap: PermanentElementMap;
|
|
8
8
|
readonly delegate: BardoDelegate;
|
|
9
|
-
static preservingPermanentElements(delegate: BardoDelegate, permanentElementMap: PermanentElementMap, callback: () => void): void
|
|
9
|
+
static preservingPermanentElements(delegate: BardoDelegate, permanentElementMap: PermanentElementMap, callback: () => void): Promise<void>;
|
|
10
10
|
constructor(delegate: BardoDelegate, permanentElementMap: PermanentElementMap);
|
|
11
11
|
enter(): void;
|
|
12
12
|
leave(): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FetchRequest, FetchMethod
|
|
1
|
+
import { FetchRequest, FetchMethod } from "../../http/fetch_request";
|
|
2
2
|
import { FetchResponse } from "../../http/fetch_response";
|
|
3
3
|
export interface FormSubmissionDelegate {
|
|
4
4
|
formSubmissionStarted(formSubmission: FormSubmission): void;
|
|
@@ -55,7 +55,7 @@ export declare class FormSubmission {
|
|
|
55
55
|
get stringFormData(): [string, string][];
|
|
56
56
|
start(): Promise<void | FetchResponse>;
|
|
57
57
|
stop(): true | undefined;
|
|
58
|
-
|
|
58
|
+
prepareRequest(request: FetchRequest): void;
|
|
59
59
|
requestStarted(_request: FetchRequest): void;
|
|
60
60
|
requestPreventedHandlingResponse(request: FetchRequest, response: FetchResponse): void;
|
|
61
61
|
requestSucceededWithResponse(request: FetchRequest, response: FetchResponse): void;
|
|
@@ -12,7 +12,6 @@ export declare class Navigator {
|
|
|
12
12
|
readonly delegate: NavigatorDelegate;
|
|
13
13
|
formSubmission?: FormSubmission;
|
|
14
14
|
currentVisit?: Visit;
|
|
15
|
-
lastVisit?: Visit;
|
|
16
15
|
constructor(delegate: NavigatorDelegate);
|
|
17
16
|
proposeVisit(location: URL, options?: Partial<VisitOptions>): void;
|
|
18
17
|
startVisit(locatable: Locatable, restorationIdentifier: string, options?: Partial<VisitOptions>): void;
|
|
@@ -32,5 +31,5 @@ export declare class Navigator {
|
|
|
32
31
|
visitScrolledToSamePageLocation(oldURL: URL, newURL: URL): void;
|
|
33
32
|
get location(): URL;
|
|
34
33
|
get restorationIdentifier(): string;
|
|
35
|
-
getActionForFormSubmission(
|
|
34
|
+
getActionForFormSubmission({ submitter, formElement }: FormSubmission): Action;
|
|
36
35
|
}
|
|
@@ -12,15 +12,17 @@ export declare class PageRenderer extends Renderer<HTMLBodyElement, PageSnapshot
|
|
|
12
12
|
get newHeadSnapshot(): import("./head_snapshot").HeadSnapshot;
|
|
13
13
|
get newElement(): HTMLBodyElement;
|
|
14
14
|
mergeHead(): Promise<void>;
|
|
15
|
-
replaceBody(): void
|
|
15
|
+
replaceBody(): Promise<void>;
|
|
16
16
|
get trackedElementsAreIdentical(): boolean;
|
|
17
17
|
copyNewHeadStylesheetElements(): Promise<void>;
|
|
18
18
|
copyNewHeadScriptElements(): void;
|
|
19
|
+
mergeProvisionalElements(): Promise<void>;
|
|
20
|
+
isCurrentElementInElementList(element: Element, elementList: Element[]): boolean;
|
|
19
21
|
removeCurrentHeadProvisionalElements(): void;
|
|
20
22
|
copyNewHeadProvisionalElements(): void;
|
|
21
23
|
activateNewBody(): void;
|
|
22
24
|
activateNewBodyScriptElements(): void;
|
|
23
|
-
assignNewBody(): void
|
|
25
|
+
assignNewBody(): Promise<void>;
|
|
24
26
|
get newHeadStylesheetElements(): HTMLLinkElement[];
|
|
25
27
|
get newHeadScriptElements(): HTMLScriptElement[];
|
|
26
28
|
get currentHeadProvisionalElements(): Element[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Adapter } from "../native/adapter";
|
|
2
|
-
import { FetchRequest, FetchRequestDelegate
|
|
2
|
+
import { FetchRequest, FetchRequestDelegate } from "../../http/fetch_request";
|
|
3
3
|
import { FetchResponse } from "../../http/fetch_response";
|
|
4
4
|
import { History } from "./history";
|
|
5
5
|
import { Snapshot } from "../snapshot";
|
|
@@ -104,7 +104,7 @@ export declare class Visit implements FetchRequestDelegate {
|
|
|
104
104
|
loadCachedSnapshot(): void;
|
|
105
105
|
followRedirect(): void;
|
|
106
106
|
goToSamePageAnchor(): void;
|
|
107
|
-
|
|
107
|
+
prepareRequest(request: FetchRequest): void;
|
|
108
108
|
requestStarted(): void;
|
|
109
109
|
requestPreventedHandlingResponse(_request: FetchRequest, _response: FetchResponse): void;
|
|
110
110
|
requestSucceededWithResponse(request: FetchRequest, response: FetchResponse): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FrameElement, FrameElementDelegate, FrameLoadingStyle } from "../../elements/frame_element";
|
|
2
|
-
import { FetchRequest, FetchRequestDelegate
|
|
2
|
+
import { FetchRequest, FetchRequestDelegate } from "../../http/fetch_request";
|
|
3
3
|
import { FetchResponse } from "../../http/fetch_response";
|
|
4
4
|
import { AppearanceObserver, AppearanceObserverDelegate } from "../../observers/appearance_observer";
|
|
5
5
|
import { FormSubmission, FormSubmissionDelegate } from "../drive/form_submission";
|
|
@@ -11,16 +11,15 @@ 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
|
-
import { PageSnapshot } from "../drive/page_snapshot";
|
|
15
14
|
declare type VisitFallback = (location: Response | Locatable, options: Partial<VisitOptions>) => Promise<void>;
|
|
16
15
|
export declare type TurboFrameMissingEvent = CustomEvent<{
|
|
17
16
|
response: Response;
|
|
18
17
|
visit: VisitFallback;
|
|
19
18
|
}>;
|
|
20
|
-
export declare class FrameController implements AppearanceObserverDelegate
|
|
19
|
+
export declare class FrameController implements AppearanceObserverDelegate<FrameElement>, FetchRequestDelegate, FormSubmitObserverDelegate, FormSubmissionDelegate, FrameElementDelegate, FormLinkClickObserverDelegate, LinkInterceptorDelegate, ViewDelegate<FrameElement, Snapshot<FrameElement>> {
|
|
21
20
|
readonly element: FrameElement;
|
|
22
21
|
readonly view: FrameView;
|
|
23
|
-
readonly appearanceObserver: AppearanceObserver
|
|
22
|
+
readonly appearanceObserver: AppearanceObserver<FrameElement>;
|
|
24
23
|
readonly formLinkClickObserver: FormLinkClickObserver;
|
|
25
24
|
readonly linkInterceptor: LinkInterceptor;
|
|
26
25
|
readonly formSubmitObserver: FormSubmitObserver;
|
|
@@ -35,7 +34,6 @@ export declare class FrameController implements AppearanceObserverDelegate, Fetc
|
|
|
35
34
|
readonly restorationIdentifier: string;
|
|
36
35
|
private previousFrameElement?;
|
|
37
36
|
private currentNavigationElement?;
|
|
38
|
-
pageSnapshot?: PageSnapshot;
|
|
39
37
|
constructor(element: FrameElement);
|
|
40
38
|
connect(): void;
|
|
41
39
|
disconnect(): void;
|
|
@@ -46,14 +44,14 @@ export declare class FrameController implements AppearanceObserverDelegate, Fetc
|
|
|
46
44
|
loadingStyleChanged(): void;
|
|
47
45
|
private loadSourceURL;
|
|
48
46
|
loadResponse(fetchResponse: FetchResponse): Promise<void>;
|
|
49
|
-
elementAppearedInViewport(
|
|
47
|
+
elementAppearedInViewport(element: FrameElement): void;
|
|
50
48
|
willSubmitFormLinkToLocation(link: Element): boolean;
|
|
51
49
|
submittedFormLinkToLocation(link: Element, _location: URL, form: HTMLFormElement): void;
|
|
52
50
|
shouldInterceptLinkClick(element: Element, _location: string, _event: MouseEvent): boolean;
|
|
53
51
|
linkClickIntercepted(element: Element, location: string): void;
|
|
54
52
|
willSubmitForm(element: HTMLFormElement, submitter?: HTMLElement): boolean;
|
|
55
53
|
formSubmitted(element: HTMLFormElement, submitter?: HTMLElement): void;
|
|
56
|
-
|
|
54
|
+
prepareRequest(request: FetchRequest): void;
|
|
57
55
|
requestStarted(_request: FetchRequest): void;
|
|
58
56
|
requestPreventedHandlingResponse(_request: FetchRequest, _response: FetchResponse): void;
|
|
59
57
|
requestSucceededWithResponse(request: FetchRequest, response: FetchResponse): Promise<void>;
|
|
@@ -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 declare type Render<E> = (
|
|
4
|
+
export declare 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;
|
|
@@ -17,7 +17,7 @@ export declare abstract class Renderer<E extends Element, S extends Snapshot<E>
|
|
|
17
17
|
prepareToRender(): void;
|
|
18
18
|
abstract render(): Promise<void>;
|
|
19
19
|
finishRendering(): void;
|
|
20
|
-
preservingPermanentElements(callback: () => void): void
|
|
20
|
+
preservingPermanentElements(callback: () => void): Promise<void>;
|
|
21
21
|
focusFirstAutofocusableElement(): void;
|
|
22
22
|
enteringBardo(currentPermanentElement: Element): void;
|
|
23
23
|
leavingBardo(currentPermanentElement: Element): void;
|
|
@@ -14,7 +14,7 @@ export declare type TurboFetchRequestErrorEvent = CustomEvent<{
|
|
|
14
14
|
}>;
|
|
15
15
|
export interface FetchRequestDelegate {
|
|
16
16
|
referrer?: URL;
|
|
17
|
-
|
|
17
|
+
prepareRequest(request: FetchRequest): void;
|
|
18
18
|
requestStarted(request: FetchRequest): void;
|
|
19
19
|
requestPreventedHandlingResponse(request: FetchRequest, response: FetchResponse): void;
|
|
20
20
|
requestSucceededWithResponse(request: FetchRequest, response: FetchResponse): void;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export interface AppearanceObserverDelegate {
|
|
2
|
-
elementAppearedInViewport(element:
|
|
1
|
+
export interface AppearanceObserverDelegate<T extends Element> {
|
|
2
|
+
elementAppearedInViewport(element: T): void;
|
|
3
3
|
}
|
|
4
|
-
export declare class AppearanceObserver {
|
|
5
|
-
readonly delegate: AppearanceObserverDelegate
|
|
6
|
-
readonly element:
|
|
4
|
+
export declare class AppearanceObserver<T extends Element> {
|
|
5
|
+
readonly delegate: AppearanceObserverDelegate<T>;
|
|
6
|
+
readonly element: T;
|
|
7
7
|
readonly intersectionObserver: IntersectionObserver;
|
|
8
8
|
started: boolean;
|
|
9
|
-
constructor(delegate: AppearanceObserverDelegate
|
|
9
|
+
constructor(delegate: AppearanceObserverDelegate<T>, element: T);
|
|
10
10
|
start(): void;
|
|
11
11
|
stop(): void;
|
|
12
12
|
intersect: IntersectionObserverCallback;
|
|
@@ -12,6 +12,6 @@ export declare class LinkClickObserver {
|
|
|
12
12
|
clickCaptured: () => void;
|
|
13
13
|
clickBubbled: (event: Event) => void;
|
|
14
14
|
clickEventIsSignificant(event: MouseEvent): boolean;
|
|
15
|
-
findLinkFromClickTarget(target: EventTarget | null): HTMLAnchorElement |
|
|
15
|
+
findLinkFromClickTarget(target: EventTarget | null): HTMLAnchorElement | undefined;
|
|
16
16
|
getLocationForLink(link: Element): URL;
|
|
17
17
|
}
|
|
@@ -6,6 +6,8 @@ declare type EventLog = [EventType, EventDetail, Target];
|
|
|
6
6
|
declare type MutationAttributeName = string;
|
|
7
7
|
declare type MutationAttributeValue = string | null;
|
|
8
8
|
declare type MutationLog = [MutationAttributeName, Target, MutationAttributeValue];
|
|
9
|
+
declare type BodyHTML = string;
|
|
10
|
+
declare type BodyMutationLog = [BodyHTML];
|
|
9
11
|
export declare function attributeForSelector(page: Page, selector: string, attributeName: string): Promise<string | null>;
|
|
10
12
|
declare type CancellableEvent = "turbo:click" | "turbo:before-visit";
|
|
11
13
|
export declare function cancelNextEvent(page: Page, eventName: CancellableEvent): Promise<void>;
|
|
@@ -23,13 +25,17 @@ export declare function nextBody(_page: Page, timeout?: number): Promise<void>;
|
|
|
23
25
|
export declare function nextEventNamed(page: Page, eventName: string): Promise<any>;
|
|
24
26
|
export declare function nextEventOnTarget(page: Page, elementId: string, eventName: string): Promise<any>;
|
|
25
27
|
export declare function listenForEventOnTarget(page: Page, elementId: string, eventName: string): Promise<void>;
|
|
28
|
+
export declare function nextBodyMutation(page: Page): Promise<string | null>;
|
|
29
|
+
export declare function noNextBodyMutation(page: Page): Promise<boolean>;
|
|
26
30
|
export declare function nextAttributeMutationNamed(page: Page, elementId: string, attributeName: string): Promise<string | null>;
|
|
27
31
|
export declare function noNextAttributeMutationNamed(page: Page, elementId: string, attributeName: string): Promise<boolean>;
|
|
28
32
|
export declare function noNextEventNamed(page: Page, eventName: string): Promise<boolean>;
|
|
29
33
|
export declare function noNextEventOnTarget(page: Page, elementId: string, eventName: string): Promise<boolean>;
|
|
30
34
|
export declare function outerHTMLForSelector(page: Page, selector: string): Promise<string>;
|
|
31
35
|
export declare function pathname(url: string): string;
|
|
36
|
+
export declare function pathnameForIFrame(page: Page, name: string): Promise<string>;
|
|
32
37
|
export declare function propertyForSelector(page: Page, selector: string, propertyName: string): Promise<any>;
|
|
38
|
+
export declare function readBodyMutationLogs(page: Page, length?: number): Promise<BodyMutationLog[]>;
|
|
33
39
|
export declare function readEventLogs(page: Page, length?: number): Promise<EventLog[]>;
|
|
34
40
|
export declare function readMutationLogs(page: Page, length?: number): Promise<MutationLog[]>;
|
|
35
41
|
export declare function search(url: string): string;
|
|
@@ -47,6 +53,7 @@ export declare function strictElementEquals(left: Locator, right: Locator): Prom
|
|
|
47
53
|
export declare function textContent(page: Page, html: string): Promise<string | null>;
|
|
48
54
|
export declare function visitAction(page: Page): Promise<string>;
|
|
49
55
|
export declare function waitForPathname(page: Page, pathname: string): Promise<void>;
|
|
56
|
+
export declare function waitUntilText(page: Page, text: string, state?: "visible" | "attached"): Promise<import("playwright-core").ElementHandle<HTMLElement | SVGElement>>;
|
|
50
57
|
export declare function waitUntilSelector(page: Page, selector: string, state?: "visible" | "attached"): Promise<import("playwright-core").ElementHandle<HTMLElement | SVGElement>>;
|
|
51
58
|
export declare function waitUntilNoSelector(page: Page, selector: string, state?: "hidden" | "detached"): Promise<import("playwright-core").ElementHandle<HTMLElement | SVGElement> | null>;
|
|
52
59
|
export declare function willChangeBody(page: Page, callback: () => Promise<void>): Promise<boolean>;
|
|
File without changes
|
package/dist/types/util.d.ts
CHANGED
|
@@ -19,7 +19,9 @@ export declare function markAsBusy(...elements: Element[]): void;
|
|
|
19
19
|
export declare function clearBusyState(...elements: Element[]): void;
|
|
20
20
|
export declare function waitForLoad(element: HTMLLinkElement, timeoutInMilliseconds?: number): Promise<void>;
|
|
21
21
|
export declare function getHistoryMethodForAction(action: Action): (data: any, unused: string, url?: string | URL | null | undefined) => void;
|
|
22
|
+
export declare function isAction(action: any): action is Action;
|
|
22
23
|
export declare function getVisitAction(...elements: (Element | undefined)[]): Action | null;
|
|
23
24
|
export declare function getMetaElement(name: string): HTMLMetaElement | null;
|
|
24
25
|
export declare function getMetaContent(name: string): string | null;
|
|
25
26
|
export declare function setMetaContent(name: string, content: string): HTMLMetaElement;
|
|
27
|
+
export declare function findClosestRecursively<E extends Element>(element: Element | null, selector: string): E | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotwired/turbo",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.5",
|
|
4
4
|
"description": "The speed of a single-page web application without having to write any JavaScript",
|
|
5
5
|
"module": "dist/turbo.es2017-esm.js",
|
|
6
6
|
"main": "dist/turbo.es2017-umd.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@playwright/test": "^1.
|
|
38
|
+
"@playwright/test": "^1.28.0",
|
|
39
39
|
"@rollup/plugin-node-resolve": "13.1.3",
|
|
40
40
|
"@rollup/plugin-typescript": "^8.5.0",
|
|
41
41
|
"@types/multer": "^1.4.5",
|
|
@@ -64,8 +64,7 @@
|
|
|
64
64
|
"test:browser": "playwright test",
|
|
65
65
|
"test:unit": "NODE_OPTIONS=--inspect node src/tests/runner.js",
|
|
66
66
|
"test:unit:win": "SET NODE_OPTIONS=--inspect & node src/tests/runner.js",
|
|
67
|
-
"
|
|
68
|
-
"release": "npm publish && git commit -am \"$npm_package_name v$npm_package_version\" && git push",
|
|
67
|
+
"release": "yarn build && npm publish",
|
|
69
68
|
"lint": "eslint . --ext .ts"
|
|
70
69
|
},
|
|
71
70
|
"engines": {
|