@hotwired/turbo 7.2.0-beta.2 → 7.2.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/turbo.es2017-esm.js +137 -62
- package/dist/turbo.es2017-umd.js +147 -69
- package/dist/types/core/drive/form_submission.d.ts +1 -3
- package/dist/types/core/drive/navigator.d.ts +1 -1
- package/dist/types/core/drive/visit.d.ts +2 -1
- package/dist/types/core/frames/frame_controller.d.ts +9 -3
- package/dist/types/core/index.d.ts +3 -5
- package/dist/types/core/session.d.ts +6 -10
- package/dist/types/core/snapshot.d.ts +3 -1
- package/dist/types/core/streams/stream_actions.d.ts +4 -2
- package/dist/types/core/streams/stream_message_renderer.d.ts +7 -0
- package/dist/types/elements/index.d.ts +1 -0
- package/dist/types/elements/stream_element.d.ts +5 -1
- package/dist/types/http/fetch_request.d.ts +7 -3
- package/dist/types/http/index.d.ts +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/tests/functional/drive_custom_body_tests.d.ts +1 -0
- package/dist/types/tests/helpers/page.d.ts +8 -2
- package/dist/types/util.d.ts +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
package/dist/turbo.es2017-esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Turbo 7.2.0-
|
|
2
|
+
Turbo 7.2.0-rc.1
|
|
3
3
|
Copyright © 2022 Basecamp, LLC
|
|
4
4
|
*/
|
|
5
5
|
(function () {
|
|
@@ -404,6 +404,9 @@ function getAttribute(attributeName, ...elements) {
|
|
|
404
404
|
}
|
|
405
405
|
return null;
|
|
406
406
|
}
|
|
407
|
+
function hasAttribute(attributeName, ...elements) {
|
|
408
|
+
return elements.some((element) => element && element.hasAttribute(attributeName));
|
|
409
|
+
}
|
|
407
410
|
function markAsBusy(...elements) {
|
|
408
411
|
for (const element of elements) {
|
|
409
412
|
if (element.localName == "turbo-frame") {
|
|
@@ -520,7 +523,9 @@ class FetchRequest {
|
|
|
520
523
|
}
|
|
521
524
|
catch (error) {
|
|
522
525
|
if (error.name !== "AbortError") {
|
|
523
|
-
this.
|
|
526
|
+
if (this.willDelegateErrorHandling(error)) {
|
|
527
|
+
this.delegate.requestErrored(this, error);
|
|
528
|
+
}
|
|
524
529
|
throw error;
|
|
525
530
|
}
|
|
526
531
|
}
|
|
@@ -586,6 +591,14 @@ class FetchRequest {
|
|
|
586
591
|
if (event.defaultPrevented)
|
|
587
592
|
await requestInterception;
|
|
588
593
|
}
|
|
594
|
+
willDelegateErrorHandling(error) {
|
|
595
|
+
const event = dispatch("turbo:fetch-request-error", {
|
|
596
|
+
target: this.target,
|
|
597
|
+
cancelable: true,
|
|
598
|
+
detail: { request: this, error: error },
|
|
599
|
+
});
|
|
600
|
+
return !event.defaultPrevented;
|
|
601
|
+
}
|
|
589
602
|
}
|
|
590
603
|
|
|
591
604
|
class AppearanceObserver {
|
|
@@ -679,7 +692,7 @@ class FormSubmission {
|
|
|
679
692
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
680
693
|
this.mustRedirect = mustRedirect;
|
|
681
694
|
}
|
|
682
|
-
static confirmMethod(message, _element) {
|
|
695
|
+
static confirmMethod(message, _element, _submitter) {
|
|
683
696
|
return Promise.resolve(confirm(message));
|
|
684
697
|
}
|
|
685
698
|
get method() {
|
|
@@ -717,17 +730,11 @@ class FormSubmission {
|
|
|
717
730
|
return entries.concat(typeof value == "string" ? [[name, value]] : []);
|
|
718
731
|
}, []);
|
|
719
732
|
}
|
|
720
|
-
get confirmationMessage() {
|
|
721
|
-
var _a;
|
|
722
|
-
return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-confirm")) || this.formElement.getAttribute("data-turbo-confirm");
|
|
723
|
-
}
|
|
724
|
-
get needsConfirmation() {
|
|
725
|
-
return this.confirmationMessage !== null;
|
|
726
|
-
}
|
|
727
733
|
async start() {
|
|
728
734
|
const { initialized, requesting } = FormSubmissionState;
|
|
729
|
-
|
|
730
|
-
|
|
735
|
+
const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
|
|
736
|
+
if (typeof confirmationMessage === "string") {
|
|
737
|
+
const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
|
|
731
738
|
if (!answer) {
|
|
732
739
|
return;
|
|
733
740
|
}
|
|
@@ -789,10 +796,6 @@ class FormSubmission {
|
|
|
789
796
|
}
|
|
790
797
|
requestErrored(request, error) {
|
|
791
798
|
this.result = { success: false, error };
|
|
792
|
-
dispatch("turbo:fetch-request-error", {
|
|
793
|
-
target: this.formElement,
|
|
794
|
-
detail: { request, error },
|
|
795
|
-
});
|
|
796
799
|
this.delegate.formSubmissionErrored(this, error);
|
|
797
800
|
}
|
|
798
801
|
requestFinished(_request) {
|
|
@@ -809,7 +812,7 @@ class FormSubmission {
|
|
|
809
812
|
return !request.isIdempotent && this.mustRedirect;
|
|
810
813
|
}
|
|
811
814
|
requestAcceptsTurboStreamResponse(request) {
|
|
812
|
-
return !request.isIdempotent ||
|
|
815
|
+
return !request.isIdempotent || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
|
|
813
816
|
}
|
|
814
817
|
}
|
|
815
818
|
function buildFormData(formElement, submitter) {
|
|
@@ -875,10 +878,10 @@ class Snapshot {
|
|
|
875
878
|
return null;
|
|
876
879
|
}
|
|
877
880
|
get permanentElements() {
|
|
878
|
-
return
|
|
881
|
+
return queryPermanentElementsAll(this.element);
|
|
879
882
|
}
|
|
880
883
|
getPermanentElementById(id) {
|
|
881
|
-
return this.element
|
|
884
|
+
return getPermanentElementById(this.element, id);
|
|
882
885
|
}
|
|
883
886
|
getPermanentElementMapForSnapshot(snapshot) {
|
|
884
887
|
const permanentElementMap = {};
|
|
@@ -892,6 +895,12 @@ class Snapshot {
|
|
|
892
895
|
return permanentElementMap;
|
|
893
896
|
}
|
|
894
897
|
}
|
|
898
|
+
function getPermanentElementById(node, id) {
|
|
899
|
+
return node.querySelector(`#${id}[data-turbo-permanent]`);
|
|
900
|
+
}
|
|
901
|
+
function queryPermanentElementsAll(node) {
|
|
902
|
+
return node.querySelectorAll("[id][data-turbo-permanent]");
|
|
903
|
+
}
|
|
895
904
|
|
|
896
905
|
class FormSubmitObserver {
|
|
897
906
|
constructor(delegate, eventTarget) {
|
|
@@ -1133,6 +1142,9 @@ class FormLinkClickObserver {
|
|
|
1133
1142
|
const turboFrame = link.getAttribute("data-turbo-frame");
|
|
1134
1143
|
if (turboFrame)
|
|
1135
1144
|
form.setAttribute("data-turbo-frame", turboFrame);
|
|
1145
|
+
const turboAction = link.getAttribute("data-turbo-action");
|
|
1146
|
+
if (turboAction)
|
|
1147
|
+
form.setAttribute("data-turbo-action", turboAction);
|
|
1136
1148
|
const turboConfirm = link.getAttribute("data-turbo-confirm");
|
|
1137
1149
|
if (turboConfirm)
|
|
1138
1150
|
form.setAttribute("data-turbo-confirm", turboConfirm);
|
|
@@ -1141,8 +1153,8 @@ class FormLinkClickObserver {
|
|
|
1141
1153
|
form.setAttribute("data-turbo-stream", "");
|
|
1142
1154
|
this.delegate.submittedFormLinkToLocation(link, location, form);
|
|
1143
1155
|
document.body.appendChild(form);
|
|
1144
|
-
form.
|
|
1145
|
-
form.
|
|
1156
|
+
form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
|
|
1157
|
+
requestAnimationFrame(() => form.requestSubmit());
|
|
1146
1158
|
}
|
|
1147
1159
|
}
|
|
1148
1160
|
|
|
@@ -1513,19 +1525,19 @@ function elementIsTracked(element) {
|
|
|
1513
1525
|
return element.getAttribute("data-turbo-track") == "reload";
|
|
1514
1526
|
}
|
|
1515
1527
|
function elementIsScript(element) {
|
|
1516
|
-
const tagName = element.
|
|
1528
|
+
const tagName = element.localName;
|
|
1517
1529
|
return tagName == "script";
|
|
1518
1530
|
}
|
|
1519
1531
|
function elementIsNoscript(element) {
|
|
1520
|
-
const tagName = element.
|
|
1532
|
+
const tagName = element.localName;
|
|
1521
1533
|
return tagName == "noscript";
|
|
1522
1534
|
}
|
|
1523
1535
|
function elementIsStylesheet(element) {
|
|
1524
|
-
const tagName = element.
|
|
1536
|
+
const tagName = element.localName;
|
|
1525
1537
|
return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
|
|
1526
1538
|
}
|
|
1527
1539
|
function elementIsMetaElementWithName(element, name) {
|
|
1528
|
-
const tagName = element.
|
|
1540
|
+
const tagName = element.localName;
|
|
1529
1541
|
return tagName == "meta" && element.getAttribute("name") == name;
|
|
1530
1542
|
}
|
|
1531
1543
|
function elementWithoutNonce(element) {
|
|
@@ -1550,7 +1562,20 @@ class PageSnapshot extends Snapshot {
|
|
|
1550
1562
|
return new this(body, new HeadSnapshot(head));
|
|
1551
1563
|
}
|
|
1552
1564
|
clone() {
|
|
1553
|
-
|
|
1565
|
+
const clonedElement = this.element.cloneNode(true);
|
|
1566
|
+
const selectElements = this.element.querySelectorAll("select");
|
|
1567
|
+
const clonedSelectElements = clonedElement.querySelectorAll("select");
|
|
1568
|
+
for (const [index, source] of selectElements.entries()) {
|
|
1569
|
+
const clone = clonedSelectElements[index];
|
|
1570
|
+
for (const option of clone.selectedOptions)
|
|
1571
|
+
option.selected = false;
|
|
1572
|
+
for (const option of source.selectedOptions)
|
|
1573
|
+
clone.options[option.index].selected = true;
|
|
1574
|
+
}
|
|
1575
|
+
for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
|
|
1576
|
+
clonedPasswordInput.value = "";
|
|
1577
|
+
}
|
|
1578
|
+
return new PageSnapshot(clonedElement, this.headSnapshot);
|
|
1554
1579
|
}
|
|
1555
1580
|
get headElement() {
|
|
1556
1581
|
return this.headSnapshot.element;
|
|
@@ -1600,6 +1625,7 @@ const defaultOptions = {
|
|
|
1600
1625
|
updateHistory: true,
|
|
1601
1626
|
shouldCacheSnapshot: true,
|
|
1602
1627
|
acceptsStreamResponse: false,
|
|
1628
|
+
initiator: document.documentElement,
|
|
1603
1629
|
};
|
|
1604
1630
|
var SystemStatusCode;
|
|
1605
1631
|
(function (SystemStatusCode) {
|
|
@@ -1609,7 +1635,6 @@ var SystemStatusCode;
|
|
|
1609
1635
|
})(SystemStatusCode || (SystemStatusCode = {}));
|
|
1610
1636
|
class Visit {
|
|
1611
1637
|
constructor(delegate, location, restorationIdentifier, options = {}) {
|
|
1612
|
-
this.identifier = uuid();
|
|
1613
1638
|
this.timingMetrics = {};
|
|
1614
1639
|
this.followedRedirect = false;
|
|
1615
1640
|
this.historyChanged = false;
|
|
@@ -1622,7 +1647,7 @@ class Visit {
|
|
|
1622
1647
|
this.location = location;
|
|
1623
1648
|
this.restorationIdentifier = restorationIdentifier || uuid();
|
|
1624
1649
|
this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
|
|
1625
|
-
const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1650
|
+
const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, initiator, } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1626
1651
|
this.action = action;
|
|
1627
1652
|
this.historyChanged = historyChanged;
|
|
1628
1653
|
this.referrer = referrer;
|
|
@@ -1635,6 +1660,7 @@ class Visit {
|
|
|
1635
1660
|
this.scrolled = !willRender;
|
|
1636
1661
|
this.shouldCacheSnapshot = shouldCacheSnapshot;
|
|
1637
1662
|
this.acceptsStreamResponse = acceptsStreamResponse;
|
|
1663
|
+
this.initiator = initiator;
|
|
1638
1664
|
}
|
|
1639
1665
|
get adapter() {
|
|
1640
1666
|
return this.delegate.adapter;
|
|
@@ -1702,7 +1728,7 @@ class Visit {
|
|
|
1702
1728
|
this.simulateRequest();
|
|
1703
1729
|
}
|
|
1704
1730
|
else if (this.shouldIssueRequest() && !this.request) {
|
|
1705
|
-
this.request = new FetchRequest(this, FetchMethod.get, this.location);
|
|
1731
|
+
this.request = new FetchRequest(this, FetchMethod.get, this.location, undefined, this.initiator);
|
|
1706
1732
|
this.request.perform();
|
|
1707
1733
|
}
|
|
1708
1734
|
}
|
|
@@ -1798,7 +1824,6 @@ class Visit {
|
|
|
1798
1824
|
if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
|
|
1799
1825
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1800
1826
|
action: "replace",
|
|
1801
|
-
willRender: false,
|
|
1802
1827
|
response: this.response,
|
|
1803
1828
|
});
|
|
1804
1829
|
this.followedRedirect = true;
|
|
@@ -2208,7 +2233,7 @@ class Navigator {
|
|
|
2208
2233
|
this.delegate = delegate;
|
|
2209
2234
|
}
|
|
2210
2235
|
proposeVisit(location, options = {}) {
|
|
2211
|
-
if (this.delegate.
|
|
2236
|
+
if (this.delegate.allowsVisitingLocation(location, options)) {
|
|
2212
2237
|
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
|
|
2213
2238
|
return this.delegate.visitProposedToLocation(location, options);
|
|
2214
2239
|
}
|
|
@@ -2416,6 +2441,30 @@ class ScrollObserver {
|
|
|
2416
2441
|
}
|
|
2417
2442
|
}
|
|
2418
2443
|
|
|
2444
|
+
class StreamMessageRenderer {
|
|
2445
|
+
render({ fragment }) {
|
|
2446
|
+
Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
|
|
2447
|
+
}
|
|
2448
|
+
enteringBardo(currentPermanentElement, newPermanentElement) {
|
|
2449
|
+
newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
|
|
2450
|
+
}
|
|
2451
|
+
leavingBardo() { }
|
|
2452
|
+
}
|
|
2453
|
+
function getPermanentElementMapForFragment(fragment) {
|
|
2454
|
+
const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
|
|
2455
|
+
const permanentElementMap = {};
|
|
2456
|
+
for (const permanentElementInDocument of permanentElementsInDocument) {
|
|
2457
|
+
const { id } = permanentElementInDocument;
|
|
2458
|
+
for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
|
|
2459
|
+
const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
|
|
2460
|
+
if (elementInStream) {
|
|
2461
|
+
permanentElementMap[id] = [permanentElementInDocument, elementInStream];
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
return permanentElementMap;
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2419
2468
|
class StreamObserver {
|
|
2420
2469
|
constructor(delegate) {
|
|
2421
2470
|
this.sources = new Set();
|
|
@@ -2776,6 +2825,7 @@ class Session {
|
|
|
2776
2825
|
this.streamObserver = new StreamObserver(this);
|
|
2777
2826
|
this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
|
|
2778
2827
|
this.frameRedirector = new FrameRedirector(this, document.documentElement);
|
|
2828
|
+
this.streamMessageRenderer = new StreamMessageRenderer();
|
|
2779
2829
|
this.drive = true;
|
|
2780
2830
|
this.enabled = true;
|
|
2781
2831
|
this.progressBarDelay = 500;
|
|
@@ -2819,7 +2869,7 @@ class Session {
|
|
|
2819
2869
|
this.adapter = adapter;
|
|
2820
2870
|
}
|
|
2821
2871
|
visit(location, options = {}) {
|
|
2822
|
-
const frameElement = document.getElementById(options.frame
|
|
2872
|
+
const frameElement = options.frame ? document.getElementById(options.frame) : null;
|
|
2823
2873
|
if (frameElement instanceof FrameElement) {
|
|
2824
2874
|
frameElement.src = location.toString();
|
|
2825
2875
|
return frameElement.loaded;
|
|
@@ -2835,7 +2885,7 @@ class Session {
|
|
|
2835
2885
|
this.streamObserver.disconnectStreamSource(source);
|
|
2836
2886
|
}
|
|
2837
2887
|
renderStreamMessage(message) {
|
|
2838
|
-
|
|
2888
|
+
this.streamMessageRenderer.render(StreamMessage.wrap(message));
|
|
2839
2889
|
}
|
|
2840
2890
|
clearCache() {
|
|
2841
2891
|
this.view.clearSnapshotCache();
|
|
@@ -2880,22 +2930,27 @@ class Session {
|
|
|
2880
2930
|
followedLinkToLocation(link, location) {
|
|
2881
2931
|
const action = this.getActionForLink(link);
|
|
2882
2932
|
const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
|
|
2883
|
-
this.visit(location.href, { action, acceptsStreamResponse });
|
|
2933
|
+
this.visit(location.href, { action, acceptsStreamResponse, initiator: link });
|
|
2884
2934
|
}
|
|
2885
|
-
|
|
2886
|
-
return this.locationWithActionIsSamePage(location, action) ||
|
|
2935
|
+
allowsVisitingLocation(location, options = {}) {
|
|
2936
|
+
return (this.locationWithActionIsSamePage(location, options.action) ||
|
|
2937
|
+
this.applicationAllowsVisitingLocation(location, options));
|
|
2887
2938
|
}
|
|
2888
2939
|
visitProposedToLocation(location, options) {
|
|
2889
2940
|
extendURLWithDeprecatedProperties(location);
|
|
2890
2941
|
return this.adapter.visitProposedToLocation(location, options);
|
|
2891
2942
|
}
|
|
2892
2943
|
visitStarted(visit) {
|
|
2944
|
+
if (!visit.acceptsStreamResponse) {
|
|
2945
|
+
markAsBusy(document.documentElement);
|
|
2946
|
+
}
|
|
2893
2947
|
extendURLWithDeprecatedProperties(visit.location);
|
|
2894
2948
|
if (!visit.silent) {
|
|
2895
|
-
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
|
|
2949
|
+
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action, visit.initiator);
|
|
2896
2950
|
}
|
|
2897
2951
|
}
|
|
2898
2952
|
visitCompleted(visit) {
|
|
2953
|
+
clearBusyState(document.documentElement);
|
|
2899
2954
|
this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
|
|
2900
2955
|
}
|
|
2901
2956
|
locationWithActionIsSamePage(location, action) {
|
|
@@ -2955,16 +3010,12 @@ class Session {
|
|
|
2955
3010
|
frameRendered(fetchResponse, frame) {
|
|
2956
3011
|
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
|
2957
3012
|
}
|
|
2958
|
-
frameMissing(frame, fetchResponse) {
|
|
2959
|
-
console.warn(`Completing full-page visit as matching frame for #${frame.id} was missing from the response`);
|
|
2960
|
-
return this.visit(fetchResponse.location);
|
|
2961
|
-
}
|
|
2962
3013
|
applicationAllowsFollowingLinkToLocation(link, location, ev) {
|
|
2963
3014
|
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
|
|
2964
3015
|
return !event.defaultPrevented;
|
|
2965
3016
|
}
|
|
2966
|
-
applicationAllowsVisitingLocation(location) {
|
|
2967
|
-
const event = this.notifyApplicationBeforeVisitingLocation(location);
|
|
3017
|
+
applicationAllowsVisitingLocation(location, options = {}) {
|
|
3018
|
+
const event = this.notifyApplicationBeforeVisitingLocation(location, options.initiator);
|
|
2968
3019
|
return !event.defaultPrevented;
|
|
2969
3020
|
}
|
|
2970
3021
|
notifyApplicationAfterClickingLinkToLocation(link, location, event) {
|
|
@@ -2974,15 +3025,18 @@ class Session {
|
|
|
2974
3025
|
cancelable: true,
|
|
2975
3026
|
});
|
|
2976
3027
|
}
|
|
2977
|
-
notifyApplicationBeforeVisitingLocation(location) {
|
|
3028
|
+
notifyApplicationBeforeVisitingLocation(location, element) {
|
|
2978
3029
|
return dispatch("turbo:before-visit", {
|
|
3030
|
+
target: element,
|
|
2979
3031
|
detail: { url: location.href },
|
|
2980
3032
|
cancelable: true,
|
|
2981
3033
|
});
|
|
2982
3034
|
}
|
|
2983
|
-
notifyApplicationAfterVisitingLocation(location, action) {
|
|
2984
|
-
|
|
2985
|
-
|
|
3035
|
+
notifyApplicationAfterVisitingLocation(location, action, element) {
|
|
3036
|
+
return dispatch("turbo:visit", {
|
|
3037
|
+
target: element,
|
|
3038
|
+
detail: { url: location.href, action },
|
|
3039
|
+
});
|
|
2986
3040
|
}
|
|
2987
3041
|
notifyApplicationBeforeCachingSnapshot() {
|
|
2988
3042
|
return dispatch("turbo:before-cache");
|
|
@@ -2997,7 +3051,6 @@ class Session {
|
|
|
2997
3051
|
return dispatch("turbo:render");
|
|
2998
3052
|
}
|
|
2999
3053
|
notifyApplicationAfterPageLoad(timing = {}) {
|
|
3000
|
-
clearBusyState(document.documentElement);
|
|
3001
3054
|
return dispatch("turbo:load", {
|
|
3002
3055
|
detail: { url: this.location.href, timing },
|
|
3003
3056
|
});
|
|
@@ -3279,8 +3332,9 @@ class FrameController {
|
|
|
3279
3332
|
session.frameLoaded(this.element);
|
|
3280
3333
|
this.fetchResponseLoaded(fetchResponse);
|
|
3281
3334
|
}
|
|
3282
|
-
else if (this.
|
|
3283
|
-
|
|
3335
|
+
else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
|
|
3336
|
+
console.warn(`A matching frame for #${this.element.id} was missing from the response, transforming into full-page Visit.`);
|
|
3337
|
+
this.visitResponse(fetchResponse.response);
|
|
3284
3338
|
}
|
|
3285
3339
|
}
|
|
3286
3340
|
}
|
|
@@ -3338,16 +3392,13 @@ class FrameController {
|
|
|
3338
3392
|
await this.loadResponse(response);
|
|
3339
3393
|
this.resolveVisitPromise();
|
|
3340
3394
|
}
|
|
3341
|
-
requestFailedWithResponse(request, response) {
|
|
3395
|
+
async requestFailedWithResponse(request, response) {
|
|
3342
3396
|
console.error(response);
|
|
3397
|
+
await this.loadResponse(response);
|
|
3343
3398
|
this.resolveVisitPromise();
|
|
3344
3399
|
}
|
|
3345
3400
|
requestErrored(request, error) {
|
|
3346
3401
|
console.error(error);
|
|
3347
|
-
dispatch("turbo:fetch-request-error", {
|
|
3348
|
-
target: this.element,
|
|
3349
|
-
detail: { request, error },
|
|
3350
|
-
});
|
|
3351
3402
|
this.resolveVisitPromise();
|
|
3352
3403
|
}
|
|
3353
3404
|
requestFinished(_request) {
|
|
@@ -3441,15 +3492,30 @@ class FrameController {
|
|
|
3441
3492
|
session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
|
|
3442
3493
|
}
|
|
3443
3494
|
}
|
|
3444
|
-
|
|
3495
|
+
willHandleFrameMissingFromResponse(fetchResponse) {
|
|
3445
3496
|
this.element.setAttribute("complete", "");
|
|
3497
|
+
const response = fetchResponse.response;
|
|
3498
|
+
const visit = async (url, options = {}) => {
|
|
3499
|
+
if (url instanceof Response) {
|
|
3500
|
+
this.visitResponse(url);
|
|
3501
|
+
}
|
|
3502
|
+
else {
|
|
3503
|
+
session.visit(url, options);
|
|
3504
|
+
}
|
|
3505
|
+
};
|
|
3446
3506
|
const event = dispatch("turbo:frame-missing", {
|
|
3447
3507
|
target: this.element,
|
|
3448
|
-
detail: {
|
|
3508
|
+
detail: { response, visit },
|
|
3449
3509
|
cancelable: true,
|
|
3450
3510
|
});
|
|
3451
3511
|
return !event.defaultPrevented;
|
|
3452
3512
|
}
|
|
3513
|
+
async visitResponse(response) {
|
|
3514
|
+
const wrapped = new FetchResponse(response);
|
|
3515
|
+
const responseHTML = await wrapped.responseHTML;
|
|
3516
|
+
const { location, redirected, statusCode } = wrapped;
|
|
3517
|
+
return session.visit(location, { response: { redirected, statusCode, responseHTML } });
|
|
3518
|
+
}
|
|
3453
3519
|
findFrameElement(element, submitter) {
|
|
3454
3520
|
var _a;
|
|
3455
3521
|
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
@@ -3585,6 +3651,9 @@ function activateElement(element, currentURL) {
|
|
|
3585
3651
|
}
|
|
3586
3652
|
|
|
3587
3653
|
class StreamElement extends HTMLElement {
|
|
3654
|
+
static async renderElement(newElement) {
|
|
3655
|
+
await newElement.performAction();
|
|
3656
|
+
}
|
|
3588
3657
|
async connectedCallback() {
|
|
3589
3658
|
try {
|
|
3590
3659
|
await this.render();
|
|
@@ -3599,9 +3668,10 @@ class StreamElement extends HTMLElement {
|
|
|
3599
3668
|
async render() {
|
|
3600
3669
|
var _a;
|
|
3601
3670
|
return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
|
|
3602
|
-
|
|
3671
|
+
const event = this.beforeRenderEvent;
|
|
3672
|
+
if (this.dispatchEvent(event)) {
|
|
3603
3673
|
await nextAnimationFrame();
|
|
3604
|
-
|
|
3674
|
+
await event.detail.render(this);
|
|
3605
3675
|
}
|
|
3606
3676
|
})()));
|
|
3607
3677
|
}
|
|
@@ -3645,7 +3715,12 @@ class StreamElement extends HTMLElement {
|
|
|
3645
3715
|
return this.templateElement.content.cloneNode(true);
|
|
3646
3716
|
}
|
|
3647
3717
|
get templateElement() {
|
|
3648
|
-
if (this.firstElementChild
|
|
3718
|
+
if (this.firstElementChild === null) {
|
|
3719
|
+
const template = this.ownerDocument.createElement("template");
|
|
3720
|
+
this.appendChild(template);
|
|
3721
|
+
return template;
|
|
3722
|
+
}
|
|
3723
|
+
else if (this.firstElementChild instanceof HTMLTemplateElement) {
|
|
3649
3724
|
return this.firstElementChild;
|
|
3650
3725
|
}
|
|
3651
3726
|
this.raise("first child element must be a <template> element");
|
|
@@ -3670,7 +3745,7 @@ class StreamElement extends HTMLElement {
|
|
|
3670
3745
|
return new CustomEvent("turbo:before-stream-render", {
|
|
3671
3746
|
bubbles: true,
|
|
3672
3747
|
cancelable: true,
|
|
3673
|
-
detail: { newStream: this },
|
|
3748
|
+
detail: { newStream: this, render: StreamElement.renderElement },
|
|
3674
3749
|
});
|
|
3675
3750
|
}
|
|
3676
3751
|
get targetElementsById() {
|
|
@@ -3752,4 +3827,4 @@ if (customElements.get("turbo-stream-source") === undefined) {
|
|
|
3752
3827
|
window.Turbo = Turbo;
|
|
3753
3828
|
start();
|
|
3754
3829
|
|
|
3755
|
-
export { FrameRenderer, PageRenderer, PageSnapshot, StreamActions, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
|
|
3830
|
+
export { FrameElement, FrameLoadingStyle, FrameRenderer, PageRenderer, PageSnapshot, StreamActions, StreamElement, StreamSourceElement, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
|