@hotwired/turbo 7.0.1 → 7.1.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/CHANGELOG.md +3 -0
- package/dist/turbo.es2017-esm.js +248 -52
- package/dist/turbo.es2017-umd.js +248 -51
- package/dist/types/core/drive/form_submission.d.ts +3 -0
- package/dist/types/core/drive/navigator.d.ts +1 -0
- package/dist/types/core/drive/visit.d.ts +4 -0
- package/dist/types/core/frames/frame_controller.d.ts +7 -3
- package/dist/types/core/frames/frame_redirector.d.ts +1 -0
- package/dist/types/core/index.d.ts +1 -0
- package/dist/types/core/session.d.ts +2 -1
- package/dist/types/core/url.d.ts +2 -0
- package/dist/types/elements/frame_element.d.ts +1 -0
- package/dist/types/polyfills/index.d.ts +1 -0
- package/dist/types/tests/functional/drive_tests.d.ts +1 -0
- package/dist/types/tests/functional/form_submission_tests.d.ts +29 -3
- package/dist/types/tests/functional/frame_tests.d.ts +12 -1
- package/dist/types/tests/functional/navigation_tests.d.ts +4 -2
- package/dist/types/util.d.ts +3 -0
- package/package.json +1 -1
- package/dist/turbo.es2017-esm.js.map +0 -1
- package/dist/turbo.es2017-umd.js.map +0 -1
- package/dist/turbo.es5-umd.js +0 -3954
- package/dist/turbo.es5-umd.js.map +0 -1
package/dist/turbo.es2017-umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Turbo 7.0.
|
|
2
|
+
Turbo 7.0.1
|
|
3
3
|
Copyright © 2021 Basecamp, LLC
|
|
4
4
|
*/
|
|
5
5
|
(function (global, factory) {
|
|
@@ -26,6 +26,58 @@ Copyright © 2021 Basecamp, LLC
|
|
|
26
26
|
Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
|
|
27
27
|
})();
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* The MIT License (MIT)
|
|
31
|
+
*
|
|
32
|
+
* Copyright (c) 2019 Javan Makhmali
|
|
33
|
+
*
|
|
34
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
35
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
36
|
+
* in the Software without restriction, including without limitation the rights
|
|
37
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
38
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
39
|
+
* furnished to do so, subject to the following conditions:
|
|
40
|
+
*
|
|
41
|
+
* The above copyright notice and this permission notice shall be included in
|
|
42
|
+
* all copies or substantial portions of the Software.
|
|
43
|
+
*
|
|
44
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
45
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
46
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
47
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
48
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
49
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
50
|
+
* THE SOFTWARE.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
(function(prototype) {
|
|
54
|
+
if (typeof prototype.requestSubmit == "function") return
|
|
55
|
+
|
|
56
|
+
prototype.requestSubmit = function(submitter) {
|
|
57
|
+
if (submitter) {
|
|
58
|
+
validateSubmitter(submitter, this);
|
|
59
|
+
submitter.click();
|
|
60
|
+
} else {
|
|
61
|
+
submitter = document.createElement("input");
|
|
62
|
+
submitter.type = "submit";
|
|
63
|
+
submitter.hidden = true;
|
|
64
|
+
this.appendChild(submitter);
|
|
65
|
+
submitter.click();
|
|
66
|
+
this.removeChild(submitter);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function validateSubmitter(submitter, form) {
|
|
71
|
+
submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
|
|
72
|
+
submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
|
|
73
|
+
submitter.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function raise(errorConstructor, message, name) {
|
|
77
|
+
throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name)
|
|
78
|
+
}
|
|
79
|
+
})(HTMLFormElement.prototype);
|
|
80
|
+
|
|
29
81
|
const submittersByForm = new WeakMap;
|
|
30
82
|
function findSubmitterFromClickTarget(target) {
|
|
31
83
|
const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
@@ -171,6 +223,10 @@ Copyright © 2021 Basecamp, LLC
|
|
|
171
223
|
return anchorMatch[1];
|
|
172
224
|
}
|
|
173
225
|
}
|
|
226
|
+
function getAction(form, submitter) {
|
|
227
|
+
const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formaction")) || form.getAttribute("action") || form.action;
|
|
228
|
+
return expandURL(action);
|
|
229
|
+
}
|
|
174
230
|
function getExtension(url) {
|
|
175
231
|
return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
|
|
176
232
|
}
|
|
@@ -181,6 +237,9 @@ Copyright © 2021 Basecamp, LLC
|
|
|
181
237
|
const prefix = getPrefix(url);
|
|
182
238
|
return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
|
|
183
239
|
}
|
|
240
|
+
function locationIsVisitable(location, rootLocation) {
|
|
241
|
+
return isPrefixedBy(location, rootLocation) && isHTML(location);
|
|
242
|
+
}
|
|
184
243
|
function getRequestURL(url) {
|
|
185
244
|
const anchor = getAnchor(url);
|
|
186
245
|
return anchor != null
|
|
@@ -303,6 +362,29 @@ Copyright © 2021 Basecamp, LLC
|
|
|
303
362
|
}
|
|
304
363
|
}).join("");
|
|
305
364
|
}
|
|
365
|
+
function getAttribute(attributeName, ...elements) {
|
|
366
|
+
for (const value of elements.map(element => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
|
|
367
|
+
if (typeof value == "string")
|
|
368
|
+
return value;
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
function markAsBusy(...elements) {
|
|
373
|
+
for (const element of elements) {
|
|
374
|
+
if (element.localName == "turbo-frame") {
|
|
375
|
+
element.setAttribute("busy", "");
|
|
376
|
+
}
|
|
377
|
+
element.setAttribute("aria-busy", "true");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function clearBusyState(...elements) {
|
|
381
|
+
for (const element of elements) {
|
|
382
|
+
if (element.localName == "turbo-frame") {
|
|
383
|
+
element.removeAttribute("busy");
|
|
384
|
+
}
|
|
385
|
+
element.removeAttribute("aria-busy");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
306
388
|
|
|
307
389
|
var FetchMethod;
|
|
308
390
|
(function (FetchMethod) {
|
|
@@ -532,6 +614,9 @@ Copyright © 2021 Basecamp, LLC
|
|
|
532
614
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
533
615
|
this.mustRedirect = mustRedirect;
|
|
534
616
|
}
|
|
617
|
+
static confirmMethod(message, element) {
|
|
618
|
+
return confirm(message);
|
|
619
|
+
}
|
|
535
620
|
get method() {
|
|
536
621
|
var _a;
|
|
537
622
|
const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
|
|
@@ -565,8 +650,20 @@ Copyright © 2021 Basecamp, LLC
|
|
|
565
650
|
return entries.concat(typeof value == "string" ? [[name, value]] : []);
|
|
566
651
|
}, []);
|
|
567
652
|
}
|
|
653
|
+
get confirmationMessage() {
|
|
654
|
+
return this.formElement.getAttribute("data-turbo-confirm");
|
|
655
|
+
}
|
|
656
|
+
get needsConfirmation() {
|
|
657
|
+
return this.confirmationMessage !== null;
|
|
658
|
+
}
|
|
568
659
|
async start() {
|
|
569
660
|
const { initialized, requesting } = FormSubmissionState;
|
|
661
|
+
if (this.needsConfirmation) {
|
|
662
|
+
const answer = FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
|
|
663
|
+
if (!answer) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
570
667
|
if (this.state == initialized) {
|
|
571
668
|
this.state = requesting;
|
|
572
669
|
return this.fetchRequest.perform();
|
|
@@ -590,7 +687,9 @@ Copyright © 2021 Basecamp, LLC
|
|
|
590
687
|
}
|
|
591
688
|
}
|
|
592
689
|
requestStarted(request) {
|
|
690
|
+
var _a;
|
|
593
691
|
this.state = FormSubmissionState.waiting;
|
|
692
|
+
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
|
|
594
693
|
dispatch("turbo:submit-start", { target: this.formElement, detail: { formSubmission: this } });
|
|
595
694
|
this.delegate.formSubmissionStarted(this);
|
|
596
695
|
}
|
|
@@ -620,7 +719,9 @@ Copyright © 2021 Basecamp, LLC
|
|
|
620
719
|
this.delegate.formSubmissionErrored(this, error);
|
|
621
720
|
}
|
|
622
721
|
requestFinished(request) {
|
|
722
|
+
var _a;
|
|
623
723
|
this.state = FormSubmissionState.stopped;
|
|
724
|
+
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
|
|
624
725
|
dispatch("turbo:submit-end", { target: this.formElement, detail: Object.assign({ formSubmission: this }, this.result) });
|
|
625
726
|
this.delegate.formSubmissionFinished(this);
|
|
626
727
|
}
|
|
@@ -699,7 +800,8 @@ Copyright © 2021 Basecamp, LLC
|
|
|
699
800
|
const form = event.target;
|
|
700
801
|
if (form instanceof HTMLFormElement && form.closest("turbo-frame, html") == this.element) {
|
|
701
802
|
const submitter = event.submitter || undefined;
|
|
702
|
-
|
|
803
|
+
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
|
|
804
|
+
if (method != "dialog" && this.delegate.shouldInterceptFormSubmission(form, submitter)) {
|
|
703
805
|
event.preventDefault();
|
|
704
806
|
event.stopImmediatePropagation();
|
|
705
807
|
this.delegate.formSubmissionIntercepted(form, submitter);
|
|
@@ -1293,7 +1395,8 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1293
1395
|
})(VisitState || (VisitState = {}));
|
|
1294
1396
|
const defaultOptions = {
|
|
1295
1397
|
action: "advance",
|
|
1296
|
-
|
|
1398
|
+
delegate: {},
|
|
1399
|
+
historyChanged: false,
|
|
1297
1400
|
};
|
|
1298
1401
|
var SystemStatusCode;
|
|
1299
1402
|
(function (SystemStatusCode) {
|
|
@@ -1313,13 +1416,14 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1313
1416
|
this.delegate = delegate;
|
|
1314
1417
|
this.location = location;
|
|
1315
1418
|
this.restorationIdentifier = restorationIdentifier || uuid();
|
|
1316
|
-
const { action, historyChanged, referrer, snapshotHTML, response } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1419
|
+
const { action, historyChanged, referrer, snapshotHTML, response, delegate: optionalDelegate } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1317
1420
|
this.action = action;
|
|
1318
1421
|
this.historyChanged = historyChanged;
|
|
1319
1422
|
this.referrer = referrer;
|
|
1320
1423
|
this.snapshotHTML = snapshotHTML;
|
|
1321
1424
|
this.response = response;
|
|
1322
1425
|
this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
|
|
1426
|
+
this.optionalDelegate = optionalDelegate;
|
|
1323
1427
|
}
|
|
1324
1428
|
get adapter() {
|
|
1325
1429
|
return this.delegate.adapter;
|
|
@@ -1342,6 +1446,8 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1342
1446
|
this.state = VisitState.started;
|
|
1343
1447
|
this.adapter.visitStarted(this);
|
|
1344
1448
|
this.delegate.visitStarted(this);
|
|
1449
|
+
if (this.optionalDelegate.visitStarted)
|
|
1450
|
+
this.optionalDelegate.visitStarted(this);
|
|
1345
1451
|
}
|
|
1346
1452
|
}
|
|
1347
1453
|
cancel() {
|
|
@@ -1471,7 +1577,8 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1471
1577
|
}
|
|
1472
1578
|
}
|
|
1473
1579
|
followRedirect() {
|
|
1474
|
-
|
|
1580
|
+
var _a;
|
|
1581
|
+
if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
|
|
1475
1582
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1476
1583
|
action: 'replace',
|
|
1477
1584
|
response: this.response
|
|
@@ -1494,25 +1601,27 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1494
1601
|
}
|
|
1495
1602
|
async requestSucceededWithResponse(request, response) {
|
|
1496
1603
|
const responseHTML = await response.responseHTML;
|
|
1604
|
+
const { redirected, statusCode } = response;
|
|
1497
1605
|
if (responseHTML == undefined) {
|
|
1498
|
-
this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch });
|
|
1606
|
+
this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
|
|
1499
1607
|
}
|
|
1500
1608
|
else {
|
|
1501
1609
|
this.redirectedToLocation = response.redirected ? response.location : undefined;
|
|
1502
|
-
this.recordResponse({ statusCode:
|
|
1610
|
+
this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
|
|
1503
1611
|
}
|
|
1504
1612
|
}
|
|
1505
1613
|
async requestFailedWithResponse(request, response) {
|
|
1506
1614
|
const responseHTML = await response.responseHTML;
|
|
1615
|
+
const { redirected, statusCode } = response;
|
|
1507
1616
|
if (responseHTML == undefined) {
|
|
1508
|
-
this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch });
|
|
1617
|
+
this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
|
|
1509
1618
|
}
|
|
1510
1619
|
else {
|
|
1511
|
-
this.recordResponse({ statusCode:
|
|
1620
|
+
this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
|
|
1512
1621
|
}
|
|
1513
1622
|
}
|
|
1514
1623
|
requestErrored(request, error) {
|
|
1515
|
-
this.recordResponse({ statusCode: SystemStatusCode.networkFailure });
|
|
1624
|
+
this.recordResponse({ statusCode: SystemStatusCode.networkFailure, redirected: false });
|
|
1516
1625
|
}
|
|
1517
1626
|
requestFinished() {
|
|
1518
1627
|
this.finishRequest();
|
|
@@ -1576,6 +1685,8 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1576
1685
|
if (!this.snapshotCached) {
|
|
1577
1686
|
this.view.cacheSnapshot();
|
|
1578
1687
|
this.snapshotCached = true;
|
|
1688
|
+
if (this.optionalDelegate.visitCachedSnapshot)
|
|
1689
|
+
this.optionalDelegate.visitCachedSnapshot(this);
|
|
1579
1690
|
}
|
|
1580
1691
|
}
|
|
1581
1692
|
async render(callback) {
|
|
@@ -1724,7 +1835,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1724
1835
|
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
1725
1836
|
const submitter = event.submitter || undefined;
|
|
1726
1837
|
if (form) {
|
|
1727
|
-
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
|
|
1838
|
+
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
1728
1839
|
if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
|
|
1729
1840
|
event.preventDefault();
|
|
1730
1841
|
this.delegate.formSubmitted(form, submitter);
|
|
@@ -1768,12 +1879,11 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1768
1879
|
linkClickIntercepted(element, url) {
|
|
1769
1880
|
const frame = this.findFrameElement(element);
|
|
1770
1881
|
if (frame) {
|
|
1771
|
-
frame.
|
|
1772
|
-
frame.src = url;
|
|
1882
|
+
frame.delegate.linkClickIntercepted(element, url);
|
|
1773
1883
|
}
|
|
1774
1884
|
}
|
|
1775
1885
|
shouldInterceptFormSubmission(element, submitter) {
|
|
1776
|
-
return this.
|
|
1886
|
+
return this.shouldSubmit(element, submitter);
|
|
1777
1887
|
}
|
|
1778
1888
|
formSubmissionIntercepted(element, submitter) {
|
|
1779
1889
|
const frame = this.findFrameElement(element, submitter);
|
|
@@ -1782,6 +1892,13 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1782
1892
|
frame.delegate.formSubmissionIntercepted(element, submitter);
|
|
1783
1893
|
}
|
|
1784
1894
|
}
|
|
1895
|
+
shouldSubmit(form, submitter) {
|
|
1896
|
+
var _a;
|
|
1897
|
+
const action = getAction(form, submitter);
|
|
1898
|
+
const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
|
|
1899
|
+
const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
|
|
1900
|
+
return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
|
|
1901
|
+
}
|
|
1785
1902
|
shouldRedirect(element, submitter) {
|
|
1786
1903
|
const frame = this.findFrameElement(element, submitter);
|
|
1787
1904
|
return frame ? frame != element.closest("turbo-frame") : false;
|
|
@@ -1939,7 +2056,12 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1939
2056
|
}
|
|
1940
2057
|
proposeVisit(location, options = {}) {
|
|
1941
2058
|
if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
|
|
1942
|
-
|
|
2059
|
+
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
|
|
2060
|
+
this.delegate.visitProposedToLocation(location, options);
|
|
2061
|
+
}
|
|
2062
|
+
else {
|
|
2063
|
+
window.location.href = location.toString();
|
|
2064
|
+
}
|
|
1943
2065
|
}
|
|
1944
2066
|
}
|
|
1945
2067
|
startVisit(locatable, restorationIdentifier, options = {}) {
|
|
@@ -1950,12 +2072,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1950
2072
|
submitForm(form, submitter) {
|
|
1951
2073
|
this.stop();
|
|
1952
2074
|
this.formSubmission = new FormSubmission(this, form, submitter, true);
|
|
1953
|
-
|
|
1954
|
-
this.proposeVisit(this.formSubmission.fetchRequest.url, { action: this.getActionForFormSubmission(this.formSubmission) });
|
|
1955
|
-
}
|
|
1956
|
-
else {
|
|
1957
|
-
this.formSubmission.start();
|
|
1958
|
-
}
|
|
2075
|
+
this.formSubmission.start();
|
|
1959
2076
|
}
|
|
1960
2077
|
stop() {
|
|
1961
2078
|
if (this.formSubmission) {
|
|
@@ -1988,8 +2105,9 @@ Copyright © 2021 Basecamp, LLC
|
|
|
1988
2105
|
if (formSubmission.method != FetchMethod.get) {
|
|
1989
2106
|
this.view.clearSnapshotCache();
|
|
1990
2107
|
}
|
|
1991
|
-
const { statusCode } = fetchResponse;
|
|
1992
|
-
const
|
|
2108
|
+
const { statusCode, redirected } = fetchResponse;
|
|
2109
|
+
const action = this.getActionForFormSubmission(formSubmission);
|
|
2110
|
+
const visitOptions = { action, response: { statusCode, responseHTML, redirected } };
|
|
1993
2111
|
this.proposeVisit(fetchResponse.location, visitOptions);
|
|
1994
2112
|
}
|
|
1995
2113
|
}
|
|
@@ -2022,6 +2140,9 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2022
2140
|
visitCompleted(visit) {
|
|
2023
2141
|
this.delegate.visitCompleted(visit);
|
|
2024
2142
|
}
|
|
2143
|
+
visitCachedSnapshot(visit) {
|
|
2144
|
+
this.delegate.visitCachedSnapshot(visit);
|
|
2145
|
+
}
|
|
2025
2146
|
locationWithActionIsSamePage(location, action) {
|
|
2026
2147
|
const anchor = getAnchor(location);
|
|
2027
2148
|
const currentAnchor = getAnchor(this.view.lastRenderedLocation);
|
|
@@ -2041,7 +2162,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2041
2162
|
}
|
|
2042
2163
|
getActionForFormSubmission(formSubmission) {
|
|
2043
2164
|
const { formElement, submitter } = formSubmission;
|
|
2044
|
-
const action =
|
|
2165
|
+
const action = getAttribute("data-turbo-action", submitter, formElement);
|
|
2045
2166
|
return isAction(action) ? action : "advance";
|
|
2046
2167
|
}
|
|
2047
2168
|
}
|
|
@@ -2491,7 +2612,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2491
2612
|
}
|
|
2492
2613
|
willFollowLinkToLocation(link, location) {
|
|
2493
2614
|
return this.elementDriveEnabled(link)
|
|
2494
|
-
&&
|
|
2615
|
+
&& locationIsVisitable(location, this.snapshot.rootLocation)
|
|
2495
2616
|
&& this.applicationAllowsFollowingLinkToLocation(link, location);
|
|
2496
2617
|
}
|
|
2497
2618
|
followedLinkToLocation(link, location) {
|
|
@@ -2499,14 +2620,24 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2499
2620
|
this.convertLinkWithMethodClickToFormSubmission(link) || this.visit(location.href, { action });
|
|
2500
2621
|
}
|
|
2501
2622
|
convertLinkWithMethodClickToFormSubmission(link) {
|
|
2502
|
-
var _a;
|
|
2503
2623
|
const linkMethod = link.getAttribute("data-turbo-method");
|
|
2504
2624
|
if (linkMethod) {
|
|
2505
2625
|
const form = document.createElement("form");
|
|
2506
2626
|
form.method = linkMethod;
|
|
2507
2627
|
form.action = link.getAttribute("href") || "undefined";
|
|
2508
2628
|
form.hidden = true;
|
|
2509
|
-
(
|
|
2629
|
+
if (link.hasAttribute("data-turbo-confirm")) {
|
|
2630
|
+
form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
|
|
2631
|
+
}
|
|
2632
|
+
const frame = this.getTargetFrameForLink(link);
|
|
2633
|
+
if (frame) {
|
|
2634
|
+
form.setAttribute("data-turbo-frame", frame);
|
|
2635
|
+
form.addEventListener("turbo:submit-start", () => form.remove());
|
|
2636
|
+
}
|
|
2637
|
+
else {
|
|
2638
|
+
form.addEventListener("submit", () => form.remove());
|
|
2639
|
+
}
|
|
2640
|
+
document.body.appendChild(form);
|
|
2510
2641
|
return dispatch("submit", { cancelable: true, target: form });
|
|
2511
2642
|
}
|
|
2512
2643
|
else {
|
|
@@ -2529,6 +2660,8 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2529
2660
|
visitCompleted(visit) {
|
|
2530
2661
|
this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
|
|
2531
2662
|
}
|
|
2663
|
+
visitCachedSnapshot(visit) {
|
|
2664
|
+
}
|
|
2532
2665
|
locationWithActionIsSamePage(location, action) {
|
|
2533
2666
|
return this.navigator.locationWithActionIsSamePage(location, action);
|
|
2534
2667
|
}
|
|
@@ -2536,7 +2669,10 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2536
2669
|
this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
|
|
2537
2670
|
}
|
|
2538
2671
|
willSubmitForm(form, submitter) {
|
|
2539
|
-
|
|
2672
|
+
const action = getAction(form, submitter);
|
|
2673
|
+
return this.elementDriveEnabled(form)
|
|
2674
|
+
&& (!submitter || this.elementDriveEnabled(submitter))
|
|
2675
|
+
&& locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
|
|
2540
2676
|
}
|
|
2541
2677
|
formSubmitted(form, submitter) {
|
|
2542
2678
|
this.navigator.submitForm(form, submitter);
|
|
@@ -2592,6 +2728,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2592
2728
|
return dispatch("turbo:before-visit", { detail: { url: location.href }, cancelable: true });
|
|
2593
2729
|
}
|
|
2594
2730
|
notifyApplicationAfterVisitingLocation(location, action) {
|
|
2731
|
+
markAsBusy(document.documentElement);
|
|
2595
2732
|
return dispatch("turbo:visit", { detail: { url: location.href, action } });
|
|
2596
2733
|
}
|
|
2597
2734
|
notifyApplicationBeforeCachingSnapshot() {
|
|
@@ -2604,6 +2741,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2604
2741
|
return dispatch("turbo:render");
|
|
2605
2742
|
}
|
|
2606
2743
|
notifyApplicationAfterPageLoad(timing = {}) {
|
|
2744
|
+
clearBusyState(document.documentElement);
|
|
2607
2745
|
return dispatch("turbo:load", { detail: { url: this.location.href, timing } });
|
|
2608
2746
|
}
|
|
2609
2747
|
notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
|
|
@@ -2638,8 +2776,17 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2638
2776
|
const action = link.getAttribute("data-turbo-action");
|
|
2639
2777
|
return isAction(action) ? action : "advance";
|
|
2640
2778
|
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2779
|
+
getTargetFrameForLink(link) {
|
|
2780
|
+
const frame = link.getAttribute("data-turbo-frame");
|
|
2781
|
+
if (frame) {
|
|
2782
|
+
return frame;
|
|
2783
|
+
}
|
|
2784
|
+
else {
|
|
2785
|
+
const container = link.closest("turbo-frame");
|
|
2786
|
+
if (container) {
|
|
2787
|
+
return container.id;
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2643
2790
|
}
|
|
2644
2791
|
get snapshot() {
|
|
2645
2792
|
return this.view.snapshot;
|
|
@@ -2682,6 +2829,9 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2682
2829
|
function setProgressBarDelay(delay) {
|
|
2683
2830
|
session.setProgressBarDelay(delay);
|
|
2684
2831
|
}
|
|
2832
|
+
function setConfirmMethod(confirmMethod) {
|
|
2833
|
+
FormSubmission.confirmMethod = confirmMethod;
|
|
2834
|
+
}
|
|
2685
2835
|
|
|
2686
2836
|
var Turbo = /*#__PURE__*/Object.freeze({
|
|
2687
2837
|
__proto__: null,
|
|
@@ -2696,11 +2846,13 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2696
2846
|
disconnectStreamSource: disconnectStreamSource,
|
|
2697
2847
|
renderStreamMessage: renderStreamMessage,
|
|
2698
2848
|
clearCache: clearCache,
|
|
2699
|
-
setProgressBarDelay: setProgressBarDelay
|
|
2849
|
+
setProgressBarDelay: setProgressBarDelay,
|
|
2850
|
+
setConfirmMethod: setConfirmMethod
|
|
2700
2851
|
});
|
|
2701
2852
|
|
|
2702
2853
|
class FrameController {
|
|
2703
2854
|
constructor(element) {
|
|
2855
|
+
this.currentFetchRequest = null;
|
|
2704
2856
|
this.resolveVisitPromise = () => { };
|
|
2705
2857
|
this.connected = false;
|
|
2706
2858
|
this.hasBeenLoaded = false;
|
|
@@ -2760,7 +2912,6 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2760
2912
|
this.appearanceObserver.stop();
|
|
2761
2913
|
await this.element.loaded;
|
|
2762
2914
|
this.hasBeenLoaded = true;
|
|
2763
|
-
session.frameLoaded(this.element);
|
|
2764
2915
|
}
|
|
2765
2916
|
catch (error) {
|
|
2766
2917
|
this.currentURL = previousURL;
|
|
@@ -2783,6 +2934,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2783
2934
|
await this.view.renderPromise;
|
|
2784
2935
|
await this.view.render(renderer);
|
|
2785
2936
|
session.frameRendered(fetchResponse, this.element);
|
|
2937
|
+
session.frameLoaded(this.element);
|
|
2786
2938
|
}
|
|
2787
2939
|
}
|
|
2788
2940
|
catch (error) {
|
|
@@ -2814,20 +2966,15 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2814
2966
|
}
|
|
2815
2967
|
this.reloadable = false;
|
|
2816
2968
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
else {
|
|
2821
|
-
const { fetchRequest } = this.formSubmission;
|
|
2822
|
-
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
|
2823
|
-
this.formSubmission.start();
|
|
2824
|
-
}
|
|
2969
|
+
const { fetchRequest } = this.formSubmission;
|
|
2970
|
+
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
|
2971
|
+
this.formSubmission.start();
|
|
2825
2972
|
}
|
|
2826
2973
|
prepareHeadersForRequest(headers, request) {
|
|
2827
2974
|
headers["Turbo-Frame"] = this.id;
|
|
2828
2975
|
}
|
|
2829
2976
|
requestStarted(request) {
|
|
2830
|
-
this.element
|
|
2977
|
+
markAsBusy(this.element);
|
|
2831
2978
|
}
|
|
2832
2979
|
requestPreventedHandlingResponse(request, response) {
|
|
2833
2980
|
this.resolveVisitPromise();
|
|
@@ -2845,14 +2992,14 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2845
2992
|
this.resolveVisitPromise();
|
|
2846
2993
|
}
|
|
2847
2994
|
requestFinished(request) {
|
|
2848
|
-
this.element
|
|
2995
|
+
clearBusyState(this.element);
|
|
2849
2996
|
}
|
|
2850
|
-
formSubmissionStarted(
|
|
2851
|
-
|
|
2852
|
-
frame.setAttribute("busy", "");
|
|
2997
|
+
formSubmissionStarted({ formElement }) {
|
|
2998
|
+
markAsBusy(formElement, this.findFrameElement(formElement));
|
|
2853
2999
|
}
|
|
2854
3000
|
formSubmissionSucceededWithResponse(formSubmission, response) {
|
|
2855
3001
|
const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
|
|
3002
|
+
this.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
|
|
2856
3003
|
frame.delegate.loadResponse(response);
|
|
2857
3004
|
}
|
|
2858
3005
|
formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
|
|
@@ -2861,9 +3008,8 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2861
3008
|
formSubmissionErrored(formSubmission, error) {
|
|
2862
3009
|
console.error(error);
|
|
2863
3010
|
}
|
|
2864
|
-
formSubmissionFinished(
|
|
2865
|
-
|
|
2866
|
-
frame.removeAttribute("busy");
|
|
3011
|
+
formSubmissionFinished({ formElement }) {
|
|
3012
|
+
clearBusyState(formElement, this.findFrameElement(formElement));
|
|
2867
3013
|
}
|
|
2868
3014
|
allowsImmediateRender(snapshot, resume) {
|
|
2869
3015
|
return true;
|
|
@@ -2873,10 +3019,14 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2873
3019
|
viewInvalidated() {
|
|
2874
3020
|
}
|
|
2875
3021
|
async visit(url) {
|
|
2876
|
-
|
|
3022
|
+
var _a;
|
|
3023
|
+
const request = new FetchRequest(this, FetchMethod.get, expandURL(url), new URLSearchParams, this.element);
|
|
3024
|
+
(_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
3025
|
+
this.currentFetchRequest = request;
|
|
2877
3026
|
return new Promise(resolve => {
|
|
2878
3027
|
this.resolveVisitPromise = () => {
|
|
2879
3028
|
this.resolveVisitPromise = () => { };
|
|
3029
|
+
this.currentFetchRequest = null;
|
|
2880
3030
|
resolve();
|
|
2881
3031
|
};
|
|
2882
3032
|
request.perform();
|
|
@@ -2884,12 +3034,29 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2884
3034
|
}
|
|
2885
3035
|
navigateFrame(element, url, submitter) {
|
|
2886
3036
|
const frame = this.findFrameElement(element, submitter);
|
|
3037
|
+
this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
|
|
2887
3038
|
frame.setAttribute("reloadable", "");
|
|
2888
3039
|
frame.src = url;
|
|
2889
3040
|
}
|
|
3041
|
+
proposeVisitIfNavigatedWithAction(frame, element, submitter) {
|
|
3042
|
+
const action = getAttribute("data-turbo-action", submitter, element, frame);
|
|
3043
|
+
if (isAction(action)) {
|
|
3044
|
+
const delegate = new SnapshotSubstitution(frame);
|
|
3045
|
+
const proposeVisit = (event) => {
|
|
3046
|
+
const { target, detail: { fetchResponse } } = event;
|
|
3047
|
+
if (target instanceof FrameElement && target.src) {
|
|
3048
|
+
const { statusCode, redirected } = fetchResponse;
|
|
3049
|
+
const responseHTML = target.ownerDocument.documentElement.outerHTML;
|
|
3050
|
+
const response = { statusCode, redirected, responseHTML };
|
|
3051
|
+
session.visit(target.src, { action, response, delegate });
|
|
3052
|
+
}
|
|
3053
|
+
};
|
|
3054
|
+
frame.addEventListener("turbo:frame-render", proposeVisit, { once: true });
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
2890
3057
|
findFrameElement(element, submitter) {
|
|
2891
3058
|
var _a;
|
|
2892
|
-
const id =
|
|
3059
|
+
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
2893
3060
|
return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
|
|
2894
3061
|
}
|
|
2895
3062
|
async extractForeignFrameElement(container) {
|
|
@@ -2910,8 +3077,15 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2910
3077
|
}
|
|
2911
3078
|
return new FrameElement();
|
|
2912
3079
|
}
|
|
3080
|
+
formActionIsVisitable(form, submitter) {
|
|
3081
|
+
const action = getAction(form, submitter);
|
|
3082
|
+
return locationIsVisitable(expandURL(action), this.rootLocation);
|
|
3083
|
+
}
|
|
2913
3084
|
shouldInterceptNavigation(element, submitter) {
|
|
2914
|
-
const id =
|
|
3085
|
+
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
3086
|
+
if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
|
|
3087
|
+
return false;
|
|
3088
|
+
}
|
|
2915
3089
|
if (!this.enabled || id == "_top") {
|
|
2916
3090
|
return false;
|
|
2917
3091
|
}
|
|
@@ -2968,6 +3142,28 @@ Copyright © 2021 Basecamp, LLC
|
|
|
2968
3142
|
get isActive() {
|
|
2969
3143
|
return this.element.isActive && this.connected;
|
|
2970
3144
|
}
|
|
3145
|
+
get rootLocation() {
|
|
3146
|
+
var _a;
|
|
3147
|
+
const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
|
|
3148
|
+
const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
|
|
3149
|
+
return expandURL(root);
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
class SnapshotSubstitution {
|
|
3153
|
+
constructor(element) {
|
|
3154
|
+
this.clone = element.cloneNode(true);
|
|
3155
|
+
this.id = element.id;
|
|
3156
|
+
}
|
|
3157
|
+
visitStarted(visit) {
|
|
3158
|
+
this.snapshot = visit.view.snapshot;
|
|
3159
|
+
}
|
|
3160
|
+
visitCachedSnapshot() {
|
|
3161
|
+
var _a;
|
|
3162
|
+
const { snapshot, id, clone } = this;
|
|
3163
|
+
if (snapshot) {
|
|
3164
|
+
(_a = snapshot.element.querySelector("#" + id)) === null || _a === void 0 ? void 0 : _a.replaceWith(clone);
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
2971
3167
|
}
|
|
2972
3168
|
function getFrameElementById(id) {
|
|
2973
3169
|
if (id != null) {
|
|
@@ -3167,6 +3363,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
3167
3363
|
exports.registerAdapter = registerAdapter;
|
|
3168
3364
|
exports.renderStreamMessage = renderStreamMessage;
|
|
3169
3365
|
exports.session = session;
|
|
3366
|
+
exports.setConfirmMethod = setConfirmMethod;
|
|
3170
3367
|
exports.setProgressBarDelay = setProgressBarDelay;
|
|
3171
3368
|
exports.start = start;
|
|
3172
3369
|
exports.visit = visit;
|
|
@@ -36,6 +36,7 @@ export declare class FormSubmission {
|
|
|
36
36
|
readonly mustRedirect: boolean;
|
|
37
37
|
state: FormSubmissionState;
|
|
38
38
|
result?: FormSubmissionResult;
|
|
39
|
+
static confirmMethod(message: string, element: HTMLFormElement): boolean;
|
|
39
40
|
constructor(delegate: FormSubmissionDelegate, formElement: HTMLFormElement, submitter?: HTMLElement, mustRedirect?: boolean);
|
|
40
41
|
get method(): FetchMethod;
|
|
41
42
|
get action(): string;
|
|
@@ -44,6 +45,8 @@ export declare class FormSubmission {
|
|
|
44
45
|
get enctype(): FormEnctype;
|
|
45
46
|
get isIdempotent(): boolean;
|
|
46
47
|
get stringFormData(): [string, string][];
|
|
48
|
+
get confirmationMessage(): string | null;
|
|
49
|
+
get needsConfirmation(): boolean;
|
|
47
50
|
start(): Promise<void | FetchResponse>;
|
|
48
51
|
stop(): true | undefined;
|
|
49
52
|
prepareHeadersForRequest(headers: FetchRequestHeaders, request: FetchRequest): void;
|
|
@@ -27,6 +27,7 @@ export declare class Navigator {
|
|
|
27
27
|
formSubmissionFinished(formSubmission: FormSubmission): void;
|
|
28
28
|
visitStarted(visit: Visit): void;
|
|
29
29
|
visitCompleted(visit: Visit): void;
|
|
30
|
+
visitCachedSnapshot(visit: Visit): void;
|
|
30
31
|
locationWithActionIsSamePage(location: URL, action?: Action): boolean;
|
|
31
32
|
visitScrolledToSamePageLocation(oldURL: URL, newURL: URL): void;
|
|
32
33
|
get location(): URL;
|