@hotwired/turbo 7.2.0-beta.2 → 7.2.0-rc.1
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 +150 -56
- package/dist/turbo.es2017-umd.js +160 -63
- 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/page_renderer.d.ts +4 -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 +2 -4
- package/dist/types/core/session.d.ts +6 -5
- 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 +2 -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 +2 -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") {
|
|
@@ -445,6 +448,9 @@ function getVisitAction(...elements) {
|
|
|
445
448
|
const action = getAttribute("data-turbo-action", ...elements);
|
|
446
449
|
return isAction(action) ? action : null;
|
|
447
450
|
}
|
|
451
|
+
function getBodyElementId() {
|
|
452
|
+
return getMetaContent("turbo-body");
|
|
453
|
+
}
|
|
448
454
|
function getMetaElement(name) {
|
|
449
455
|
return document.querySelector(`meta[name="${name}"]`);
|
|
450
456
|
}
|
|
@@ -679,7 +685,7 @@ class FormSubmission {
|
|
|
679
685
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
680
686
|
this.mustRedirect = mustRedirect;
|
|
681
687
|
}
|
|
682
|
-
static confirmMethod(message, _element) {
|
|
688
|
+
static confirmMethod(message, _element, _submitter) {
|
|
683
689
|
return Promise.resolve(confirm(message));
|
|
684
690
|
}
|
|
685
691
|
get method() {
|
|
@@ -717,17 +723,11 @@ class FormSubmission {
|
|
|
717
723
|
return entries.concat(typeof value == "string" ? [[name, value]] : []);
|
|
718
724
|
}, []);
|
|
719
725
|
}
|
|
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
726
|
async start() {
|
|
728
727
|
const { initialized, requesting } = FormSubmissionState;
|
|
729
|
-
|
|
730
|
-
|
|
728
|
+
const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
|
|
729
|
+
if (typeof confirmationMessage === "string") {
|
|
730
|
+
const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
|
|
731
731
|
if (!answer) {
|
|
732
732
|
return;
|
|
733
733
|
}
|
|
@@ -809,7 +809,7 @@ class FormSubmission {
|
|
|
809
809
|
return !request.isIdempotent && this.mustRedirect;
|
|
810
810
|
}
|
|
811
811
|
requestAcceptsTurboStreamResponse(request) {
|
|
812
|
-
return !request.isIdempotent ||
|
|
812
|
+
return !request.isIdempotent || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
|
|
813
813
|
}
|
|
814
814
|
}
|
|
815
815
|
function buildFormData(formElement, submitter) {
|
|
@@ -875,10 +875,10 @@ class Snapshot {
|
|
|
875
875
|
return null;
|
|
876
876
|
}
|
|
877
877
|
get permanentElements() {
|
|
878
|
-
return
|
|
878
|
+
return queryPermanentElementsAll(this.element);
|
|
879
879
|
}
|
|
880
880
|
getPermanentElementById(id) {
|
|
881
|
-
return this.element
|
|
881
|
+
return getPermanentElementById(this.element, id);
|
|
882
882
|
}
|
|
883
883
|
getPermanentElementMapForSnapshot(snapshot) {
|
|
884
884
|
const permanentElementMap = {};
|
|
@@ -892,6 +892,12 @@ class Snapshot {
|
|
|
892
892
|
return permanentElementMap;
|
|
893
893
|
}
|
|
894
894
|
}
|
|
895
|
+
function getPermanentElementById(node, id) {
|
|
896
|
+
return node.querySelector(`#${id}[data-turbo-permanent]`);
|
|
897
|
+
}
|
|
898
|
+
function queryPermanentElementsAll(node) {
|
|
899
|
+
return node.querySelectorAll("[id][data-turbo-permanent]");
|
|
900
|
+
}
|
|
895
901
|
|
|
896
902
|
class FormSubmitObserver {
|
|
897
903
|
constructor(delegate, eventTarget) {
|
|
@@ -1133,6 +1139,9 @@ class FormLinkClickObserver {
|
|
|
1133
1139
|
const turboFrame = link.getAttribute("data-turbo-frame");
|
|
1134
1140
|
if (turboFrame)
|
|
1135
1141
|
form.setAttribute("data-turbo-frame", turboFrame);
|
|
1142
|
+
const turboAction = link.getAttribute("data-turbo-action");
|
|
1143
|
+
if (turboAction)
|
|
1144
|
+
form.setAttribute("data-turbo-action", turboAction);
|
|
1136
1145
|
const turboConfirm = link.getAttribute("data-turbo-confirm");
|
|
1137
1146
|
if (turboConfirm)
|
|
1138
1147
|
form.setAttribute("data-turbo-confirm", turboConfirm);
|
|
@@ -1141,8 +1150,8 @@ class FormLinkClickObserver {
|
|
|
1141
1150
|
form.setAttribute("data-turbo-stream", "");
|
|
1142
1151
|
this.delegate.submittedFormLinkToLocation(link, location, form);
|
|
1143
1152
|
document.body.appendChild(form);
|
|
1144
|
-
form.
|
|
1145
|
-
form.
|
|
1153
|
+
form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
|
|
1154
|
+
requestAnimationFrame(() => form.requestSubmit());
|
|
1146
1155
|
}
|
|
1147
1156
|
}
|
|
1148
1157
|
|
|
@@ -1513,19 +1522,19 @@ function elementIsTracked(element) {
|
|
|
1513
1522
|
return element.getAttribute("data-turbo-track") == "reload";
|
|
1514
1523
|
}
|
|
1515
1524
|
function elementIsScript(element) {
|
|
1516
|
-
const tagName = element.
|
|
1525
|
+
const tagName = element.localName;
|
|
1517
1526
|
return tagName == "script";
|
|
1518
1527
|
}
|
|
1519
1528
|
function elementIsNoscript(element) {
|
|
1520
|
-
const tagName = element.
|
|
1529
|
+
const tagName = element.localName;
|
|
1521
1530
|
return tagName == "noscript";
|
|
1522
1531
|
}
|
|
1523
1532
|
function elementIsStylesheet(element) {
|
|
1524
|
-
const tagName = element.
|
|
1533
|
+
const tagName = element.localName;
|
|
1525
1534
|
return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
|
|
1526
1535
|
}
|
|
1527
1536
|
function elementIsMetaElementWithName(element, name) {
|
|
1528
|
-
const tagName = element.
|
|
1537
|
+
const tagName = element.localName;
|
|
1529
1538
|
return tagName == "meta" && element.getAttribute("name") == name;
|
|
1530
1539
|
}
|
|
1531
1540
|
function elementWithoutNonce(element) {
|
|
@@ -1550,7 +1559,20 @@ class PageSnapshot extends Snapshot {
|
|
|
1550
1559
|
return new this(body, new HeadSnapshot(head));
|
|
1551
1560
|
}
|
|
1552
1561
|
clone() {
|
|
1553
|
-
|
|
1562
|
+
const clonedElement = this.element.cloneNode(true);
|
|
1563
|
+
const selectElements = this.element.querySelectorAll("select");
|
|
1564
|
+
const clonedSelectElements = clonedElement.querySelectorAll("select");
|
|
1565
|
+
for (const [index, source] of selectElements.entries()) {
|
|
1566
|
+
const clone = clonedSelectElements[index];
|
|
1567
|
+
for (const option of clone.selectedOptions)
|
|
1568
|
+
option.selected = false;
|
|
1569
|
+
for (const option of source.selectedOptions)
|
|
1570
|
+
clone.options[option.index].selected = true;
|
|
1571
|
+
}
|
|
1572
|
+
for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
|
|
1573
|
+
clonedPasswordInput.value = "";
|
|
1574
|
+
}
|
|
1575
|
+
return new PageSnapshot(clonedElement, this.headSnapshot);
|
|
1554
1576
|
}
|
|
1555
1577
|
get headElement() {
|
|
1556
1578
|
return this.headSnapshot.element;
|
|
@@ -1600,6 +1622,7 @@ const defaultOptions = {
|
|
|
1600
1622
|
updateHistory: true,
|
|
1601
1623
|
shouldCacheSnapshot: true,
|
|
1602
1624
|
acceptsStreamResponse: false,
|
|
1625
|
+
initiator: document.documentElement,
|
|
1603
1626
|
};
|
|
1604
1627
|
var SystemStatusCode;
|
|
1605
1628
|
(function (SystemStatusCode) {
|
|
@@ -1609,7 +1632,6 @@ var SystemStatusCode;
|
|
|
1609
1632
|
})(SystemStatusCode || (SystemStatusCode = {}));
|
|
1610
1633
|
class Visit {
|
|
1611
1634
|
constructor(delegate, location, restorationIdentifier, options = {}) {
|
|
1612
|
-
this.identifier = uuid();
|
|
1613
1635
|
this.timingMetrics = {};
|
|
1614
1636
|
this.followedRedirect = false;
|
|
1615
1637
|
this.historyChanged = false;
|
|
@@ -1622,7 +1644,7 @@ class Visit {
|
|
|
1622
1644
|
this.location = location;
|
|
1623
1645
|
this.restorationIdentifier = restorationIdentifier || uuid();
|
|
1624
1646
|
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);
|
|
1647
|
+
const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, initiator, } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1626
1648
|
this.action = action;
|
|
1627
1649
|
this.historyChanged = historyChanged;
|
|
1628
1650
|
this.referrer = referrer;
|
|
@@ -1635,6 +1657,7 @@ class Visit {
|
|
|
1635
1657
|
this.scrolled = !willRender;
|
|
1636
1658
|
this.shouldCacheSnapshot = shouldCacheSnapshot;
|
|
1637
1659
|
this.acceptsStreamResponse = acceptsStreamResponse;
|
|
1660
|
+
this.initiator = initiator;
|
|
1638
1661
|
}
|
|
1639
1662
|
get adapter() {
|
|
1640
1663
|
return this.delegate.adapter;
|
|
@@ -1702,7 +1725,7 @@ class Visit {
|
|
|
1702
1725
|
this.simulateRequest();
|
|
1703
1726
|
}
|
|
1704
1727
|
else if (this.shouldIssueRequest() && !this.request) {
|
|
1705
|
-
this.request = new FetchRequest(this, FetchMethod.get, this.location);
|
|
1728
|
+
this.request = new FetchRequest(this, FetchMethod.get, this.location, undefined, this.initiator);
|
|
1706
1729
|
this.request.perform();
|
|
1707
1730
|
}
|
|
1708
1731
|
}
|
|
@@ -1798,7 +1821,6 @@ class Visit {
|
|
|
1798
1821
|
if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
|
|
1799
1822
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1800
1823
|
action: "replace",
|
|
1801
|
-
willRender: false,
|
|
1802
1824
|
response: this.response,
|
|
1803
1825
|
});
|
|
1804
1826
|
this.followedRedirect = true;
|
|
@@ -2208,7 +2230,7 @@ class Navigator {
|
|
|
2208
2230
|
this.delegate = delegate;
|
|
2209
2231
|
}
|
|
2210
2232
|
proposeVisit(location, options = {}) {
|
|
2211
|
-
if (this.delegate.
|
|
2233
|
+
if (this.delegate.allowsVisitingLocation(location, options)) {
|
|
2212
2234
|
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
|
|
2213
2235
|
return this.delegate.visitProposedToLocation(location, options);
|
|
2214
2236
|
}
|
|
@@ -2416,6 +2438,30 @@ class ScrollObserver {
|
|
|
2416
2438
|
}
|
|
2417
2439
|
}
|
|
2418
2440
|
|
|
2441
|
+
class StreamMessageRenderer {
|
|
2442
|
+
render({ fragment }) {
|
|
2443
|
+
Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
|
|
2444
|
+
}
|
|
2445
|
+
enteringBardo(currentPermanentElement, newPermanentElement) {
|
|
2446
|
+
newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
|
|
2447
|
+
}
|
|
2448
|
+
leavingBardo() { }
|
|
2449
|
+
}
|
|
2450
|
+
function getPermanentElementMapForFragment(fragment) {
|
|
2451
|
+
const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
|
|
2452
|
+
const permanentElementMap = {};
|
|
2453
|
+
for (const permanentElementInDocument of permanentElementsInDocument) {
|
|
2454
|
+
const { id } = permanentElementInDocument;
|
|
2455
|
+
for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
|
|
2456
|
+
const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
|
|
2457
|
+
if (elementInStream) {
|
|
2458
|
+
permanentElementMap[id] = [permanentElementInDocument, elementInStream];
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
return permanentElementMap;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2419
2465
|
class StreamObserver {
|
|
2420
2466
|
constructor(delegate) {
|
|
2421
2467
|
this.sources = new Set();
|
|
@@ -2516,16 +2562,19 @@ class ErrorRenderer extends Renderer {
|
|
|
2516
2562
|
}
|
|
2517
2563
|
|
|
2518
2564
|
class PageRenderer extends Renderer {
|
|
2519
|
-
static renderElement(currentElement, newElement) {
|
|
2565
|
+
static async renderElement(currentElement, newElement) {
|
|
2566
|
+
await nextEventLoopTick();
|
|
2520
2567
|
if (document.body && newElement instanceof HTMLBodyElement) {
|
|
2521
|
-
|
|
2568
|
+
const currentBody = PageRenderer.getBodyElement(currentElement);
|
|
2569
|
+
const newBody = PageRenderer.getBodyElement(newElement);
|
|
2570
|
+
currentBody.replaceWith(newBody);
|
|
2522
2571
|
}
|
|
2523
2572
|
else {
|
|
2524
2573
|
document.documentElement.appendChild(newElement);
|
|
2525
2574
|
}
|
|
2526
2575
|
}
|
|
2527
2576
|
get shouldRender() {
|
|
2528
|
-
return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
|
|
2577
|
+
return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical && this.bodyElementMatches;
|
|
2529
2578
|
}
|
|
2530
2579
|
get reloadReason() {
|
|
2531
2580
|
if (!this.newSnapshot.isVisitable) {
|
|
@@ -2538,6 +2587,11 @@ class PageRenderer extends Renderer {
|
|
|
2538
2587
|
reason: "tracked_element_mismatch",
|
|
2539
2588
|
};
|
|
2540
2589
|
}
|
|
2590
|
+
if (!this.bodyElementMatches) {
|
|
2591
|
+
return {
|
|
2592
|
+
reason: "body_element_mismatch",
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2541
2595
|
}
|
|
2542
2596
|
async prepareToRender() {
|
|
2543
2597
|
await this.mergeHead();
|
|
@@ -2578,6 +2632,16 @@ class PageRenderer extends Renderer {
|
|
|
2578
2632
|
get trackedElementsAreIdentical() {
|
|
2579
2633
|
return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
|
|
2580
2634
|
}
|
|
2635
|
+
get bodyElementMatches() {
|
|
2636
|
+
return PageRenderer.getBodyElement(this.newElement) !== null;
|
|
2637
|
+
}
|
|
2638
|
+
static get bodySelector() {
|
|
2639
|
+
const bodyId = getBodyElementId();
|
|
2640
|
+
return bodyId ? `#${bodyId}` : "body";
|
|
2641
|
+
}
|
|
2642
|
+
static getBodyElement(element) {
|
|
2643
|
+
return element.querySelector(this.bodySelector) || element;
|
|
2644
|
+
}
|
|
2581
2645
|
async copyNewHeadStylesheetElements() {
|
|
2582
2646
|
const loadingElements = [];
|
|
2583
2647
|
for (const element of this.newHeadStylesheetElements) {
|
|
@@ -2776,6 +2840,7 @@ class Session {
|
|
|
2776
2840
|
this.streamObserver = new StreamObserver(this);
|
|
2777
2841
|
this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
|
|
2778
2842
|
this.frameRedirector = new FrameRedirector(this, document.documentElement);
|
|
2843
|
+
this.streamMessageRenderer = new StreamMessageRenderer();
|
|
2779
2844
|
this.drive = true;
|
|
2780
2845
|
this.enabled = true;
|
|
2781
2846
|
this.progressBarDelay = 500;
|
|
@@ -2819,7 +2884,7 @@ class Session {
|
|
|
2819
2884
|
this.adapter = adapter;
|
|
2820
2885
|
}
|
|
2821
2886
|
visit(location, options = {}) {
|
|
2822
|
-
const frameElement = document.getElementById(options.frame
|
|
2887
|
+
const frameElement = options.frame ? document.getElementById(options.frame) : null;
|
|
2823
2888
|
if (frameElement instanceof FrameElement) {
|
|
2824
2889
|
frameElement.src = location.toString();
|
|
2825
2890
|
return frameElement.loaded;
|
|
@@ -2835,7 +2900,7 @@ class Session {
|
|
|
2835
2900
|
this.streamObserver.disconnectStreamSource(source);
|
|
2836
2901
|
}
|
|
2837
2902
|
renderStreamMessage(message) {
|
|
2838
|
-
|
|
2903
|
+
this.streamMessageRenderer.render(StreamMessage.wrap(message));
|
|
2839
2904
|
}
|
|
2840
2905
|
clearCache() {
|
|
2841
2906
|
this.view.clearSnapshotCache();
|
|
@@ -2880,22 +2945,27 @@ class Session {
|
|
|
2880
2945
|
followedLinkToLocation(link, location) {
|
|
2881
2946
|
const action = this.getActionForLink(link);
|
|
2882
2947
|
const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
|
|
2883
|
-
this.visit(location.href, { action, acceptsStreamResponse });
|
|
2948
|
+
this.visit(location.href, { action, acceptsStreamResponse, initiator: link });
|
|
2884
2949
|
}
|
|
2885
|
-
|
|
2886
|
-
return this.locationWithActionIsSamePage(location, action) ||
|
|
2950
|
+
allowsVisitingLocation(location, options = {}) {
|
|
2951
|
+
return (this.locationWithActionIsSamePage(location, options.action) ||
|
|
2952
|
+
this.applicationAllowsVisitingLocation(location, options));
|
|
2887
2953
|
}
|
|
2888
2954
|
visitProposedToLocation(location, options) {
|
|
2889
2955
|
extendURLWithDeprecatedProperties(location);
|
|
2890
2956
|
return this.adapter.visitProposedToLocation(location, options);
|
|
2891
2957
|
}
|
|
2892
2958
|
visitStarted(visit) {
|
|
2959
|
+
if (!visit.acceptsStreamResponse) {
|
|
2960
|
+
markAsBusy(document.documentElement);
|
|
2961
|
+
}
|
|
2893
2962
|
extendURLWithDeprecatedProperties(visit.location);
|
|
2894
2963
|
if (!visit.silent) {
|
|
2895
|
-
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
|
|
2964
|
+
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action, visit.initiator);
|
|
2896
2965
|
}
|
|
2897
2966
|
}
|
|
2898
2967
|
visitCompleted(visit) {
|
|
2968
|
+
clearBusyState(document.documentElement);
|
|
2899
2969
|
this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
|
|
2900
2970
|
}
|
|
2901
2971
|
locationWithActionIsSamePage(location, action) {
|
|
@@ -2955,16 +3025,12 @@ class Session {
|
|
|
2955
3025
|
frameRendered(fetchResponse, frame) {
|
|
2956
3026
|
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
|
2957
3027
|
}
|
|
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
3028
|
applicationAllowsFollowingLinkToLocation(link, location, ev) {
|
|
2963
3029
|
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
|
|
2964
3030
|
return !event.defaultPrevented;
|
|
2965
3031
|
}
|
|
2966
|
-
applicationAllowsVisitingLocation(location) {
|
|
2967
|
-
const event = this.notifyApplicationBeforeVisitingLocation(location);
|
|
3032
|
+
applicationAllowsVisitingLocation(location, options = {}) {
|
|
3033
|
+
const event = this.notifyApplicationBeforeVisitingLocation(location, options.initiator);
|
|
2968
3034
|
return !event.defaultPrevented;
|
|
2969
3035
|
}
|
|
2970
3036
|
notifyApplicationAfterClickingLinkToLocation(link, location, event) {
|
|
@@ -2974,15 +3040,18 @@ class Session {
|
|
|
2974
3040
|
cancelable: true,
|
|
2975
3041
|
});
|
|
2976
3042
|
}
|
|
2977
|
-
notifyApplicationBeforeVisitingLocation(location) {
|
|
3043
|
+
notifyApplicationBeforeVisitingLocation(location, element) {
|
|
2978
3044
|
return dispatch("turbo:before-visit", {
|
|
3045
|
+
target: element,
|
|
2979
3046
|
detail: { url: location.href },
|
|
2980
3047
|
cancelable: true,
|
|
2981
3048
|
});
|
|
2982
3049
|
}
|
|
2983
|
-
notifyApplicationAfterVisitingLocation(location, action) {
|
|
2984
|
-
|
|
2985
|
-
|
|
3050
|
+
notifyApplicationAfterVisitingLocation(location, action, element) {
|
|
3051
|
+
return dispatch("turbo:visit", {
|
|
3052
|
+
target: element,
|
|
3053
|
+
detail: { url: location.href, action },
|
|
3054
|
+
});
|
|
2986
3055
|
}
|
|
2987
3056
|
notifyApplicationBeforeCachingSnapshot() {
|
|
2988
3057
|
return dispatch("turbo:before-cache");
|
|
@@ -2997,7 +3066,6 @@ class Session {
|
|
|
2997
3066
|
return dispatch("turbo:render");
|
|
2998
3067
|
}
|
|
2999
3068
|
notifyApplicationAfterPageLoad(timing = {}) {
|
|
3000
|
-
clearBusyState(document.documentElement);
|
|
3001
3069
|
return dispatch("turbo:load", {
|
|
3002
3070
|
detail: { url: this.location.href, timing },
|
|
3003
3071
|
});
|
|
@@ -3279,8 +3347,9 @@ class FrameController {
|
|
|
3279
3347
|
session.frameLoaded(this.element);
|
|
3280
3348
|
this.fetchResponseLoaded(fetchResponse);
|
|
3281
3349
|
}
|
|
3282
|
-
else if (this.
|
|
3283
|
-
|
|
3350
|
+
else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
|
|
3351
|
+
console.warn(`A matching frame for #${this.element.id} was missing from the response, transforming into full-page Visit.`);
|
|
3352
|
+
this.visitResponse(fetchResponse.response);
|
|
3284
3353
|
}
|
|
3285
3354
|
}
|
|
3286
3355
|
}
|
|
@@ -3338,8 +3407,9 @@ class FrameController {
|
|
|
3338
3407
|
await this.loadResponse(response);
|
|
3339
3408
|
this.resolveVisitPromise();
|
|
3340
3409
|
}
|
|
3341
|
-
requestFailedWithResponse(request, response) {
|
|
3410
|
+
async requestFailedWithResponse(request, response) {
|
|
3342
3411
|
console.error(response);
|
|
3412
|
+
await this.loadResponse(response);
|
|
3343
3413
|
this.resolveVisitPromise();
|
|
3344
3414
|
}
|
|
3345
3415
|
requestErrored(request, error) {
|
|
@@ -3441,15 +3511,30 @@ class FrameController {
|
|
|
3441
3511
|
session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
|
|
3442
3512
|
}
|
|
3443
3513
|
}
|
|
3444
|
-
|
|
3514
|
+
willHandleFrameMissingFromResponse(fetchResponse) {
|
|
3445
3515
|
this.element.setAttribute("complete", "");
|
|
3516
|
+
const response = fetchResponse.response;
|
|
3517
|
+
const visit = async (url, options = {}) => {
|
|
3518
|
+
if (url instanceof Response) {
|
|
3519
|
+
this.visitResponse(url);
|
|
3520
|
+
}
|
|
3521
|
+
else {
|
|
3522
|
+
session.visit(url, options);
|
|
3523
|
+
}
|
|
3524
|
+
};
|
|
3446
3525
|
const event = dispatch("turbo:frame-missing", {
|
|
3447
3526
|
target: this.element,
|
|
3448
|
-
detail: {
|
|
3527
|
+
detail: { response, visit },
|
|
3449
3528
|
cancelable: true,
|
|
3450
3529
|
});
|
|
3451
3530
|
return !event.defaultPrevented;
|
|
3452
3531
|
}
|
|
3532
|
+
async visitResponse(response) {
|
|
3533
|
+
const wrapped = new FetchResponse(response);
|
|
3534
|
+
const responseHTML = await wrapped.responseHTML;
|
|
3535
|
+
const { location, redirected, statusCode } = wrapped;
|
|
3536
|
+
return session.visit(location, { response: { redirected, statusCode, responseHTML } });
|
|
3537
|
+
}
|
|
3453
3538
|
findFrameElement(element, submitter) {
|
|
3454
3539
|
var _a;
|
|
3455
3540
|
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
@@ -3585,6 +3670,9 @@ function activateElement(element, currentURL) {
|
|
|
3585
3670
|
}
|
|
3586
3671
|
|
|
3587
3672
|
class StreamElement extends HTMLElement {
|
|
3673
|
+
static async renderElement(newElement) {
|
|
3674
|
+
await newElement.performAction();
|
|
3675
|
+
}
|
|
3588
3676
|
async connectedCallback() {
|
|
3589
3677
|
try {
|
|
3590
3678
|
await this.render();
|
|
@@ -3599,9 +3687,10 @@ class StreamElement extends HTMLElement {
|
|
|
3599
3687
|
async render() {
|
|
3600
3688
|
var _a;
|
|
3601
3689
|
return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
|
|
3602
|
-
|
|
3690
|
+
const event = this.beforeRenderEvent;
|
|
3691
|
+
if (this.dispatchEvent(event)) {
|
|
3603
3692
|
await nextAnimationFrame();
|
|
3604
|
-
|
|
3693
|
+
await event.detail.render(this);
|
|
3605
3694
|
}
|
|
3606
3695
|
})()));
|
|
3607
3696
|
}
|
|
@@ -3645,7 +3734,12 @@ class StreamElement extends HTMLElement {
|
|
|
3645
3734
|
return this.templateElement.content.cloneNode(true);
|
|
3646
3735
|
}
|
|
3647
3736
|
get templateElement() {
|
|
3648
|
-
if (this.firstElementChild
|
|
3737
|
+
if (this.firstElementChild === null) {
|
|
3738
|
+
const template = this.ownerDocument.createElement("template");
|
|
3739
|
+
this.appendChild(template);
|
|
3740
|
+
return template;
|
|
3741
|
+
}
|
|
3742
|
+
else if (this.firstElementChild instanceof HTMLTemplateElement) {
|
|
3649
3743
|
return this.firstElementChild;
|
|
3650
3744
|
}
|
|
3651
3745
|
this.raise("first child element must be a <template> element");
|
|
@@ -3670,7 +3764,7 @@ class StreamElement extends HTMLElement {
|
|
|
3670
3764
|
return new CustomEvent("turbo:before-stream-render", {
|
|
3671
3765
|
bubbles: true,
|
|
3672
3766
|
cancelable: true,
|
|
3673
|
-
detail: { newStream: this },
|
|
3767
|
+
detail: { newStream: this, render: StreamElement.renderElement },
|
|
3674
3768
|
});
|
|
3675
3769
|
}
|
|
3676
3770
|
get targetElementsById() {
|
|
@@ -3752,4 +3846,4 @@ if (customElements.get("turbo-stream-source") === undefined) {
|
|
|
3752
3846
|
window.Turbo = Turbo;
|
|
3753
3847
|
start();
|
|
3754
3848
|
|
|
3755
|
-
export { FrameRenderer, PageRenderer, PageSnapshot, StreamActions, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
|
|
3849
|
+
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 };
|