@hotwired/turbo 7.0.1 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/dist/turbo.es2017-esm.js +278 -93
- package/dist/turbo.es2017-umd.js +278 -92
- package/dist/types/core/drive/form_submission.d.ts +4 -1
- package/dist/types/core/drive/page_view.d.ts +2 -2
- package/dist/types/core/drive/visit.d.ts +6 -0
- package/dist/types/core/frames/frame_controller.d.ts +8 -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/renderer.d.ts +2 -1
- package/dist/types/core/session.d.ts +1 -1
- package/dist/types/core/url.d.ts +2 -0
- package/dist/types/elements/frame_element.d.ts +2 -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 +34 -3
- package/dist/types/tests/functional/frame_tests.d.ts +22 -1
- package/dist/types/tests/functional/navigation_tests.d.ts +5 -2
- package/dist/types/tests/functional/visit_tests.d.ts +2 -0
- 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-esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Turbo 7.
|
|
2
|
+
Turbo 7.1.0
|
|
3
3
|
Copyright © 2021 Basecamp, LLC
|
|
4
4
|
*/
|
|
5
5
|
(function () {
|
|
@@ -20,6 +20,58 @@ Copyright © 2021 Basecamp, LLC
|
|
|
20
20
|
Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
|
|
21
21
|
})();
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The MIT License (MIT)
|
|
25
|
+
*
|
|
26
|
+
* Copyright (c) 2019 Javan Makhmali
|
|
27
|
+
*
|
|
28
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
29
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
30
|
+
* in the Software without restriction, including without limitation the rights
|
|
31
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
33
|
+
* furnished to do so, subject to the following conditions:
|
|
34
|
+
*
|
|
35
|
+
* The above copyright notice and this permission notice shall be included in
|
|
36
|
+
* all copies or substantial portions of the Software.
|
|
37
|
+
*
|
|
38
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
39
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
40
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
41
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
42
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
43
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
44
|
+
* THE SOFTWARE.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
(function(prototype) {
|
|
48
|
+
if (typeof prototype.requestSubmit == "function") return
|
|
49
|
+
|
|
50
|
+
prototype.requestSubmit = function(submitter) {
|
|
51
|
+
if (submitter) {
|
|
52
|
+
validateSubmitter(submitter, this);
|
|
53
|
+
submitter.click();
|
|
54
|
+
} else {
|
|
55
|
+
submitter = document.createElement("input");
|
|
56
|
+
submitter.type = "submit";
|
|
57
|
+
submitter.hidden = true;
|
|
58
|
+
this.appendChild(submitter);
|
|
59
|
+
submitter.click();
|
|
60
|
+
this.removeChild(submitter);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function validateSubmitter(submitter, form) {
|
|
65
|
+
submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
|
|
66
|
+
submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
|
|
67
|
+
submitter.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function raise(errorConstructor, message, name) {
|
|
71
|
+
throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name)
|
|
72
|
+
}
|
|
73
|
+
})(HTMLFormElement.prototype);
|
|
74
|
+
|
|
23
75
|
const submittersByForm = new WeakMap;
|
|
24
76
|
function findSubmitterFromClickTarget(target) {
|
|
25
77
|
const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
@@ -165,6 +217,10 @@ function getAnchor(url) {
|
|
|
165
217
|
return anchorMatch[1];
|
|
166
218
|
}
|
|
167
219
|
}
|
|
220
|
+
function getAction(form, submitter) {
|
|
221
|
+
const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formaction")) || form.getAttribute("action") || form.action;
|
|
222
|
+
return expandURL(action);
|
|
223
|
+
}
|
|
168
224
|
function getExtension(url) {
|
|
169
225
|
return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
|
|
170
226
|
}
|
|
@@ -175,6 +231,9 @@ function isPrefixedBy(baseURL, url) {
|
|
|
175
231
|
const prefix = getPrefix(url);
|
|
176
232
|
return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
|
|
177
233
|
}
|
|
234
|
+
function locationIsVisitable(location, rootLocation) {
|
|
235
|
+
return isPrefixedBy(location, rootLocation) && isHTML(location);
|
|
236
|
+
}
|
|
178
237
|
function getRequestURL(url) {
|
|
179
238
|
const anchor = getAnchor(url);
|
|
180
239
|
return anchor != null
|
|
@@ -297,6 +356,29 @@ function uuid() {
|
|
|
297
356
|
}
|
|
298
357
|
}).join("");
|
|
299
358
|
}
|
|
359
|
+
function getAttribute(attributeName, ...elements) {
|
|
360
|
+
for (const value of elements.map(element => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
|
|
361
|
+
if (typeof value == "string")
|
|
362
|
+
return value;
|
|
363
|
+
}
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
function markAsBusy(...elements) {
|
|
367
|
+
for (const element of elements) {
|
|
368
|
+
if (element.localName == "turbo-frame") {
|
|
369
|
+
element.setAttribute("busy", "");
|
|
370
|
+
}
|
|
371
|
+
element.setAttribute("aria-busy", "true");
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function clearBusyState(...elements) {
|
|
375
|
+
for (const element of elements) {
|
|
376
|
+
if (element.localName == "turbo-frame") {
|
|
377
|
+
element.removeAttribute("busy");
|
|
378
|
+
}
|
|
379
|
+
element.removeAttribute("aria-busy");
|
|
380
|
+
}
|
|
381
|
+
}
|
|
300
382
|
|
|
301
383
|
var FetchMethod;
|
|
302
384
|
(function (FetchMethod) {
|
|
@@ -322,13 +404,8 @@ class FetchRequest {
|
|
|
322
404
|
this.delegate = delegate;
|
|
323
405
|
this.method = method;
|
|
324
406
|
this.headers = this.defaultHeaders;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
this.body = body;
|
|
330
|
-
this.url = location;
|
|
331
|
-
}
|
|
407
|
+
this.body = body;
|
|
408
|
+
this.url = location;
|
|
332
409
|
this.target = target;
|
|
333
410
|
}
|
|
334
411
|
get location() {
|
|
@@ -384,7 +461,7 @@ class FetchRequest {
|
|
|
384
461
|
credentials: "same-origin",
|
|
385
462
|
headers: this.headers,
|
|
386
463
|
redirect: "follow",
|
|
387
|
-
body: this.body,
|
|
464
|
+
body: this.isIdempotent ? null : this.body,
|
|
388
465
|
signal: this.abortSignal,
|
|
389
466
|
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
|
|
390
467
|
};
|
|
@@ -406,7 +483,7 @@ class FetchRequest {
|
|
|
406
483
|
cancelable: true,
|
|
407
484
|
detail: {
|
|
408
485
|
fetchOptions,
|
|
409
|
-
url: this.url
|
|
486
|
+
url: this.url,
|
|
410
487
|
resume: this.resolveRequestPromise
|
|
411
488
|
},
|
|
412
489
|
target: this.target
|
|
@@ -415,21 +492,6 @@ class FetchRequest {
|
|
|
415
492
|
await requestInterception;
|
|
416
493
|
}
|
|
417
494
|
}
|
|
418
|
-
function mergeFormDataEntries(url, entries) {
|
|
419
|
-
const currentSearchParams = new URLSearchParams(url.search);
|
|
420
|
-
for (const [name, value] of entries) {
|
|
421
|
-
if (value instanceof File)
|
|
422
|
-
continue;
|
|
423
|
-
if (currentSearchParams.has(name)) {
|
|
424
|
-
currentSearchParams.delete(name);
|
|
425
|
-
url.searchParams.set(name, value);
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
url.searchParams.append(name, value);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
return url;
|
|
432
|
-
}
|
|
433
495
|
|
|
434
496
|
class AppearanceObserver {
|
|
435
497
|
constructor(delegate, element) {
|
|
@@ -523,9 +585,16 @@ class FormSubmission {
|
|
|
523
585
|
this.formElement = formElement;
|
|
524
586
|
this.submitter = submitter;
|
|
525
587
|
this.formData = buildFormData(formElement, submitter);
|
|
588
|
+
this.location = expandURL(this.action);
|
|
589
|
+
if (this.method == FetchMethod.get) {
|
|
590
|
+
mergeFormDataEntries(this.location, [...this.body.entries()]);
|
|
591
|
+
}
|
|
526
592
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
527
593
|
this.mustRedirect = mustRedirect;
|
|
528
594
|
}
|
|
595
|
+
static confirmMethod(message, element) {
|
|
596
|
+
return confirm(message);
|
|
597
|
+
}
|
|
529
598
|
get method() {
|
|
530
599
|
var _a;
|
|
531
600
|
const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
|
|
@@ -536,9 +605,6 @@ class FormSubmission {
|
|
|
536
605
|
const formElementAction = typeof this.formElement.action === 'string' ? this.formElement.action : null;
|
|
537
606
|
return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
|
|
538
607
|
}
|
|
539
|
-
get location() {
|
|
540
|
-
return expandURL(this.action);
|
|
541
|
-
}
|
|
542
608
|
get body() {
|
|
543
609
|
if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
|
|
544
610
|
return new URLSearchParams(this.stringFormData);
|
|
@@ -559,8 +625,20 @@ class FormSubmission {
|
|
|
559
625
|
return entries.concat(typeof value == "string" ? [[name, value]] : []);
|
|
560
626
|
}, []);
|
|
561
627
|
}
|
|
628
|
+
get confirmationMessage() {
|
|
629
|
+
return this.formElement.getAttribute("data-turbo-confirm");
|
|
630
|
+
}
|
|
631
|
+
get needsConfirmation() {
|
|
632
|
+
return this.confirmationMessage !== null;
|
|
633
|
+
}
|
|
562
634
|
async start() {
|
|
563
635
|
const { initialized, requesting } = FormSubmissionState;
|
|
636
|
+
if (this.needsConfirmation) {
|
|
637
|
+
const answer = FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
|
|
638
|
+
if (!answer) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
564
642
|
if (this.state == initialized) {
|
|
565
643
|
this.state = requesting;
|
|
566
644
|
return this.fetchRequest.perform();
|
|
@@ -584,7 +662,9 @@ class FormSubmission {
|
|
|
584
662
|
}
|
|
585
663
|
}
|
|
586
664
|
requestStarted(request) {
|
|
665
|
+
var _a;
|
|
587
666
|
this.state = FormSubmissionState.waiting;
|
|
667
|
+
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
|
|
588
668
|
dispatch("turbo:submit-start", { target: this.formElement, detail: { formSubmission: this } });
|
|
589
669
|
this.delegate.formSubmissionStarted(this);
|
|
590
670
|
}
|
|
@@ -614,7 +694,9 @@ class FormSubmission {
|
|
|
614
694
|
this.delegate.formSubmissionErrored(this, error);
|
|
615
695
|
}
|
|
616
696
|
requestFinished(request) {
|
|
697
|
+
var _a;
|
|
617
698
|
this.state = FormSubmissionState.stopped;
|
|
699
|
+
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
|
|
618
700
|
dispatch("turbo:submit-end", { target: this.formElement, detail: Object.assign({ formSubmission: this }, this.result) });
|
|
619
701
|
this.delegate.formSubmissionFinished(this);
|
|
620
702
|
}
|
|
@@ -648,6 +730,16 @@ function getMetaContent(name) {
|
|
|
648
730
|
function responseSucceededWithoutRedirect(response) {
|
|
649
731
|
return response.statusCode == 200 && !response.redirected;
|
|
650
732
|
}
|
|
733
|
+
function mergeFormDataEntries(url, entries) {
|
|
734
|
+
const searchParams = new URLSearchParams;
|
|
735
|
+
for (const [name, value] of entries) {
|
|
736
|
+
if (value instanceof File)
|
|
737
|
+
continue;
|
|
738
|
+
searchParams.append(name, value);
|
|
739
|
+
}
|
|
740
|
+
url.search = searchParams.toString();
|
|
741
|
+
return url;
|
|
742
|
+
}
|
|
651
743
|
|
|
652
744
|
class Snapshot {
|
|
653
745
|
constructor(element) {
|
|
@@ -691,9 +783,10 @@ class FormInterceptor {
|
|
|
691
783
|
constructor(delegate, element) {
|
|
692
784
|
this.submitBubbled = ((event) => {
|
|
693
785
|
const form = event.target;
|
|
694
|
-
if (form instanceof HTMLFormElement && form.closest("turbo-frame, html") == this.element) {
|
|
786
|
+
if (!event.defaultPrevented && form instanceof HTMLFormElement && form.closest("turbo-frame, html") == this.element) {
|
|
695
787
|
const submitter = event.submitter || undefined;
|
|
696
|
-
|
|
788
|
+
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
|
|
789
|
+
if (method != "dialog" && this.delegate.shouldInterceptFormSubmission(form, submitter)) {
|
|
697
790
|
event.preventDefault();
|
|
698
791
|
event.stopImmediatePropagation();
|
|
699
792
|
this.delegate.formSubmissionIntercepted(form, submitter);
|
|
@@ -908,10 +1001,11 @@ function createPlaceholderForPermanentElement(permanentElement) {
|
|
|
908
1001
|
}
|
|
909
1002
|
|
|
910
1003
|
class Renderer {
|
|
911
|
-
constructor(currentSnapshot, newSnapshot, isPreview) {
|
|
1004
|
+
constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
|
|
912
1005
|
this.currentSnapshot = currentSnapshot;
|
|
913
1006
|
this.newSnapshot = newSnapshot;
|
|
914
1007
|
this.isPreview = isPreview;
|
|
1008
|
+
this.willRender = willRender;
|
|
915
1009
|
this.promise = new Promise((resolve, reject) => this.resolvingFunctions = { resolve, reject });
|
|
916
1010
|
}
|
|
917
1011
|
get shouldRender() {
|
|
@@ -1287,7 +1381,9 @@ var VisitState;
|
|
|
1287
1381
|
})(VisitState || (VisitState = {}));
|
|
1288
1382
|
const defaultOptions = {
|
|
1289
1383
|
action: "advance",
|
|
1290
|
-
historyChanged: false
|
|
1384
|
+
historyChanged: false,
|
|
1385
|
+
visitCachedSnapshot: () => { },
|
|
1386
|
+
willRender: true,
|
|
1291
1387
|
};
|
|
1292
1388
|
var SystemStatusCode;
|
|
1293
1389
|
(function (SystemStatusCode) {
|
|
@@ -1307,13 +1403,16 @@ class Visit {
|
|
|
1307
1403
|
this.delegate = delegate;
|
|
1308
1404
|
this.location = location;
|
|
1309
1405
|
this.restorationIdentifier = restorationIdentifier || uuid();
|
|
1310
|
-
const { action, historyChanged, referrer, snapshotHTML, response } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1406
|
+
const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1311
1407
|
this.action = action;
|
|
1312
1408
|
this.historyChanged = historyChanged;
|
|
1313
1409
|
this.referrer = referrer;
|
|
1314
1410
|
this.snapshotHTML = snapshotHTML;
|
|
1315
1411
|
this.response = response;
|
|
1316
1412
|
this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
|
|
1413
|
+
this.visitCachedSnapshot = visitCachedSnapshot;
|
|
1414
|
+
this.willRender = willRender;
|
|
1415
|
+
this.scrolled = !willRender;
|
|
1317
1416
|
}
|
|
1318
1417
|
get adapter() {
|
|
1319
1418
|
return this.delegate.adapter;
|
|
@@ -1415,7 +1514,7 @@ class Visit {
|
|
|
1415
1514
|
if (this.view.renderPromise)
|
|
1416
1515
|
await this.view.renderPromise;
|
|
1417
1516
|
if (isSuccessful(statusCode) && responseHTML != null) {
|
|
1418
|
-
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML));
|
|
1517
|
+
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender);
|
|
1419
1518
|
this.adapter.visitRendered(this);
|
|
1420
1519
|
this.complete();
|
|
1421
1520
|
}
|
|
@@ -1455,7 +1554,7 @@ class Visit {
|
|
|
1455
1554
|
else {
|
|
1456
1555
|
if (this.view.renderPromise)
|
|
1457
1556
|
await this.view.renderPromise;
|
|
1458
|
-
await this.view.renderPage(snapshot, isPreview);
|
|
1557
|
+
await this.view.renderPage(snapshot, isPreview, this.willRender);
|
|
1459
1558
|
this.adapter.visitRendered(this);
|
|
1460
1559
|
if (!isPreview) {
|
|
1461
1560
|
this.complete();
|
|
@@ -1465,7 +1564,8 @@ class Visit {
|
|
|
1465
1564
|
}
|
|
1466
1565
|
}
|
|
1467
1566
|
followRedirect() {
|
|
1468
|
-
|
|
1567
|
+
var _a;
|
|
1568
|
+
if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
|
|
1469
1569
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1470
1570
|
action: 'replace',
|
|
1471
1571
|
response: this.response
|
|
@@ -1488,25 +1588,27 @@ class Visit {
|
|
|
1488
1588
|
}
|
|
1489
1589
|
async requestSucceededWithResponse(request, response) {
|
|
1490
1590
|
const responseHTML = await response.responseHTML;
|
|
1591
|
+
const { redirected, statusCode } = response;
|
|
1491
1592
|
if (responseHTML == undefined) {
|
|
1492
|
-
this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch });
|
|
1593
|
+
this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
|
|
1493
1594
|
}
|
|
1494
1595
|
else {
|
|
1495
1596
|
this.redirectedToLocation = response.redirected ? response.location : undefined;
|
|
1496
|
-
this.recordResponse({ statusCode:
|
|
1597
|
+
this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
|
|
1497
1598
|
}
|
|
1498
1599
|
}
|
|
1499
1600
|
async requestFailedWithResponse(request, response) {
|
|
1500
1601
|
const responseHTML = await response.responseHTML;
|
|
1602
|
+
const { redirected, statusCode } = response;
|
|
1501
1603
|
if (responseHTML == undefined) {
|
|
1502
|
-
this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch });
|
|
1604
|
+
this.recordResponse({ statusCode: SystemStatusCode.contentTypeMismatch, redirected });
|
|
1503
1605
|
}
|
|
1504
1606
|
else {
|
|
1505
|
-
this.recordResponse({ statusCode:
|
|
1607
|
+
this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
|
|
1506
1608
|
}
|
|
1507
1609
|
}
|
|
1508
1610
|
requestErrored(request, error) {
|
|
1509
|
-
this.recordResponse({ statusCode: SystemStatusCode.networkFailure });
|
|
1611
|
+
this.recordResponse({ statusCode: SystemStatusCode.networkFailure, redirected: false });
|
|
1510
1612
|
}
|
|
1511
1613
|
requestFinished() {
|
|
1512
1614
|
this.finishRequest();
|
|
@@ -1563,12 +1665,12 @@ class Visit {
|
|
|
1563
1665
|
return !this.hasCachedSnapshot();
|
|
1564
1666
|
}
|
|
1565
1667
|
else {
|
|
1566
|
-
return
|
|
1668
|
+
return this.willRender;
|
|
1567
1669
|
}
|
|
1568
1670
|
}
|
|
1569
1671
|
cacheSnapshot() {
|
|
1570
1672
|
if (!this.snapshotCached) {
|
|
1571
|
-
this.view.cacheSnapshot();
|
|
1673
|
+
this.view.cacheSnapshot().then(snapshot => snapshot && this.visitCachedSnapshot(snapshot));
|
|
1572
1674
|
this.snapshotCached = true;
|
|
1573
1675
|
}
|
|
1574
1676
|
}
|
|
@@ -1604,10 +1706,10 @@ class BrowserAdapter {
|
|
|
1604
1706
|
this.navigator.startVisit(location, uuid(), options);
|
|
1605
1707
|
}
|
|
1606
1708
|
visitStarted(visit) {
|
|
1709
|
+
visit.loadCachedSnapshot();
|
|
1607
1710
|
visit.issueRequest();
|
|
1608
1711
|
visit.changeHistory();
|
|
1609
1712
|
visit.goToSamePageAnchor();
|
|
1610
|
-
visit.loadCachedSnapshot();
|
|
1611
1713
|
}
|
|
1612
1714
|
visitRequestStarted(visit) {
|
|
1613
1715
|
this.progressBar.setValue(0);
|
|
@@ -1718,7 +1820,7 @@ class FormSubmitObserver {
|
|
|
1718
1820
|
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
1719
1821
|
const submitter = event.submitter || undefined;
|
|
1720
1822
|
if (form) {
|
|
1721
|
-
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
|
|
1823
|
+
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
1722
1824
|
if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
|
|
1723
1825
|
event.preventDefault();
|
|
1724
1826
|
this.delegate.formSubmitted(form, submitter);
|
|
@@ -1762,12 +1864,11 @@ class FrameRedirector {
|
|
|
1762
1864
|
linkClickIntercepted(element, url) {
|
|
1763
1865
|
const frame = this.findFrameElement(element);
|
|
1764
1866
|
if (frame) {
|
|
1765
|
-
frame.
|
|
1766
|
-
frame.src = url;
|
|
1867
|
+
frame.delegate.linkClickIntercepted(element, url);
|
|
1767
1868
|
}
|
|
1768
1869
|
}
|
|
1769
1870
|
shouldInterceptFormSubmission(element, submitter) {
|
|
1770
|
-
return this.
|
|
1871
|
+
return this.shouldSubmit(element, submitter);
|
|
1771
1872
|
}
|
|
1772
1873
|
formSubmissionIntercepted(element, submitter) {
|
|
1773
1874
|
const frame = this.findFrameElement(element, submitter);
|
|
@@ -1776,6 +1877,13 @@ class FrameRedirector {
|
|
|
1776
1877
|
frame.delegate.formSubmissionIntercepted(element, submitter);
|
|
1777
1878
|
}
|
|
1778
1879
|
}
|
|
1880
|
+
shouldSubmit(form, submitter) {
|
|
1881
|
+
var _a;
|
|
1882
|
+
const action = getAction(form, submitter);
|
|
1883
|
+
const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
|
|
1884
|
+
const rootLocation = expandURL((_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/");
|
|
1885
|
+
return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
|
|
1886
|
+
}
|
|
1779
1887
|
shouldRedirect(element, submitter) {
|
|
1780
1888
|
const frame = this.findFrameElement(element, submitter);
|
|
1781
1889
|
return frame ? frame != element.closest("turbo-frame") : false;
|
|
@@ -1933,7 +2041,12 @@ class Navigator {
|
|
|
1933
2041
|
}
|
|
1934
2042
|
proposeVisit(location, options = {}) {
|
|
1935
2043
|
if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
|
|
1936
|
-
|
|
2044
|
+
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
|
|
2045
|
+
this.delegate.visitProposedToLocation(location, options);
|
|
2046
|
+
}
|
|
2047
|
+
else {
|
|
2048
|
+
window.location.href = location.toString();
|
|
2049
|
+
}
|
|
1937
2050
|
}
|
|
1938
2051
|
}
|
|
1939
2052
|
startVisit(locatable, restorationIdentifier, options = {}) {
|
|
@@ -1944,12 +2057,7 @@ class Navigator {
|
|
|
1944
2057
|
submitForm(form, submitter) {
|
|
1945
2058
|
this.stop();
|
|
1946
2059
|
this.formSubmission = new FormSubmission(this, form, submitter, true);
|
|
1947
|
-
|
|
1948
|
-
this.proposeVisit(this.formSubmission.fetchRequest.url, { action: this.getActionForFormSubmission(this.formSubmission) });
|
|
1949
|
-
}
|
|
1950
|
-
else {
|
|
1951
|
-
this.formSubmission.start();
|
|
1952
|
-
}
|
|
2060
|
+
this.formSubmission.start();
|
|
1953
2061
|
}
|
|
1954
2062
|
stop() {
|
|
1955
2063
|
if (this.formSubmission) {
|
|
@@ -1982,8 +2090,9 @@ class Navigator {
|
|
|
1982
2090
|
if (formSubmission.method != FetchMethod.get) {
|
|
1983
2091
|
this.view.clearSnapshotCache();
|
|
1984
2092
|
}
|
|
1985
|
-
const { statusCode } = fetchResponse;
|
|
1986
|
-
const
|
|
2093
|
+
const { statusCode, redirected } = fetchResponse;
|
|
2094
|
+
const action = this.getActionForFormSubmission(formSubmission);
|
|
2095
|
+
const visitOptions = { action, response: { statusCode, responseHTML, redirected } };
|
|
1987
2096
|
this.proposeVisit(fetchResponse.location, visitOptions);
|
|
1988
2097
|
}
|
|
1989
2098
|
}
|
|
@@ -2035,7 +2144,7 @@ class Navigator {
|
|
|
2035
2144
|
}
|
|
2036
2145
|
getActionForFormSubmission(formSubmission) {
|
|
2037
2146
|
const { formElement, submitter } = formSubmission;
|
|
2038
|
-
const action =
|
|
2147
|
+
const action = getAttribute("data-turbo-action", submitter, formElement);
|
|
2039
2148
|
return isAction(action) ? action : "advance";
|
|
2040
2149
|
}
|
|
2041
2150
|
}
|
|
@@ -2229,7 +2338,9 @@ class PageRenderer extends Renderer {
|
|
|
2229
2338
|
this.mergeHead();
|
|
2230
2339
|
}
|
|
2231
2340
|
async render() {
|
|
2232
|
-
this.
|
|
2341
|
+
if (this.willRender) {
|
|
2342
|
+
this.replaceBody();
|
|
2343
|
+
}
|
|
2233
2344
|
}
|
|
2234
2345
|
finishRendering() {
|
|
2235
2346
|
super.finishRendering();
|
|
@@ -2367,8 +2478,8 @@ class PageView extends View {
|
|
|
2367
2478
|
this.snapshotCache = new SnapshotCache(10);
|
|
2368
2479
|
this.lastRenderedLocation = new URL(location.href);
|
|
2369
2480
|
}
|
|
2370
|
-
renderPage(snapshot, isPreview = false) {
|
|
2371
|
-
const renderer = new PageRenderer(this.snapshot, snapshot, isPreview);
|
|
2481
|
+
renderPage(snapshot, isPreview = false, willRender = true) {
|
|
2482
|
+
const renderer = new PageRenderer(this.snapshot, snapshot, isPreview, willRender);
|
|
2372
2483
|
return this.render(renderer);
|
|
2373
2484
|
}
|
|
2374
2485
|
renderError(snapshot) {
|
|
@@ -2383,7 +2494,9 @@ class PageView extends View {
|
|
|
2383
2494
|
this.delegate.viewWillCacheSnapshot();
|
|
2384
2495
|
const { snapshot, lastRenderedLocation: location } = this;
|
|
2385
2496
|
await nextEventLoopTick();
|
|
2386
|
-
|
|
2497
|
+
const cachedSnapshot = snapshot.clone();
|
|
2498
|
+
this.snapshotCache.put(location, cachedSnapshot);
|
|
2499
|
+
return cachedSnapshot;
|
|
2387
2500
|
}
|
|
2388
2501
|
}
|
|
2389
2502
|
getCachedSnapshotForLocation(location) {
|
|
@@ -2485,7 +2598,7 @@ class Session {
|
|
|
2485
2598
|
}
|
|
2486
2599
|
willFollowLinkToLocation(link, location) {
|
|
2487
2600
|
return this.elementDriveEnabled(link)
|
|
2488
|
-
&&
|
|
2601
|
+
&& locationIsVisitable(location, this.snapshot.rootLocation)
|
|
2489
2602
|
&& this.applicationAllowsFollowingLinkToLocation(link, location);
|
|
2490
2603
|
}
|
|
2491
2604
|
followedLinkToLocation(link, location) {
|
|
@@ -2493,14 +2606,24 @@ class Session {
|
|
|
2493
2606
|
this.convertLinkWithMethodClickToFormSubmission(link) || this.visit(location.href, { action });
|
|
2494
2607
|
}
|
|
2495
2608
|
convertLinkWithMethodClickToFormSubmission(link) {
|
|
2496
|
-
var _a;
|
|
2497
2609
|
const linkMethod = link.getAttribute("data-turbo-method");
|
|
2498
2610
|
if (linkMethod) {
|
|
2499
2611
|
const form = document.createElement("form");
|
|
2500
2612
|
form.method = linkMethod;
|
|
2501
2613
|
form.action = link.getAttribute("href") || "undefined";
|
|
2502
2614
|
form.hidden = true;
|
|
2503
|
-
(
|
|
2615
|
+
if (link.hasAttribute("data-turbo-confirm")) {
|
|
2616
|
+
form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
|
|
2617
|
+
}
|
|
2618
|
+
const frame = this.getTargetFrameForLink(link);
|
|
2619
|
+
if (frame) {
|
|
2620
|
+
form.setAttribute("data-turbo-frame", frame);
|
|
2621
|
+
form.addEventListener("turbo:submit-start", () => form.remove());
|
|
2622
|
+
}
|
|
2623
|
+
else {
|
|
2624
|
+
form.addEventListener("submit", () => form.remove());
|
|
2625
|
+
}
|
|
2626
|
+
document.body.appendChild(form);
|
|
2504
2627
|
return dispatch("submit", { cancelable: true, target: form });
|
|
2505
2628
|
}
|
|
2506
2629
|
else {
|
|
@@ -2530,7 +2653,10 @@ class Session {
|
|
|
2530
2653
|
this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
|
|
2531
2654
|
}
|
|
2532
2655
|
willSubmitForm(form, submitter) {
|
|
2533
|
-
|
|
2656
|
+
const action = getAction(form, submitter);
|
|
2657
|
+
return this.elementDriveEnabled(form)
|
|
2658
|
+
&& (!submitter || this.elementDriveEnabled(submitter))
|
|
2659
|
+
&& locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
|
|
2534
2660
|
}
|
|
2535
2661
|
formSubmitted(form, submitter) {
|
|
2536
2662
|
this.navigator.submitForm(form, submitter);
|
|
@@ -2586,6 +2712,7 @@ class Session {
|
|
|
2586
2712
|
return dispatch("turbo:before-visit", { detail: { url: location.href }, cancelable: true });
|
|
2587
2713
|
}
|
|
2588
2714
|
notifyApplicationAfterVisitingLocation(location, action) {
|
|
2715
|
+
markAsBusy(document.documentElement);
|
|
2589
2716
|
return dispatch("turbo:visit", { detail: { url: location.href, action } });
|
|
2590
2717
|
}
|
|
2591
2718
|
notifyApplicationBeforeCachingSnapshot() {
|
|
@@ -2598,6 +2725,7 @@ class Session {
|
|
|
2598
2725
|
return dispatch("turbo:render");
|
|
2599
2726
|
}
|
|
2600
2727
|
notifyApplicationAfterPageLoad(timing = {}) {
|
|
2728
|
+
clearBusyState(document.documentElement);
|
|
2601
2729
|
return dispatch("turbo:load", { detail: { url: this.location.href, timing } });
|
|
2602
2730
|
}
|
|
2603
2731
|
notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
|
|
@@ -2632,8 +2760,17 @@ class Session {
|
|
|
2632
2760
|
const action = link.getAttribute("data-turbo-action");
|
|
2633
2761
|
return isAction(action) ? action : "advance";
|
|
2634
2762
|
}
|
|
2635
|
-
|
|
2636
|
-
|
|
2763
|
+
getTargetFrameForLink(link) {
|
|
2764
|
+
const frame = link.getAttribute("data-turbo-frame");
|
|
2765
|
+
if (frame) {
|
|
2766
|
+
return frame;
|
|
2767
|
+
}
|
|
2768
|
+
else {
|
|
2769
|
+
const container = link.closest("turbo-frame");
|
|
2770
|
+
if (container) {
|
|
2771
|
+
return container.id;
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2637
2774
|
}
|
|
2638
2775
|
get snapshot() {
|
|
2639
2776
|
return this.view.snapshot;
|
|
@@ -2676,6 +2813,9 @@ function clearCache() {
|
|
|
2676
2813
|
function setProgressBarDelay(delay) {
|
|
2677
2814
|
session.setProgressBarDelay(delay);
|
|
2678
2815
|
}
|
|
2816
|
+
function setConfirmMethod(confirmMethod) {
|
|
2817
|
+
FormSubmission.confirmMethod = confirmMethod;
|
|
2818
|
+
}
|
|
2679
2819
|
|
|
2680
2820
|
var Turbo = /*#__PURE__*/Object.freeze({
|
|
2681
2821
|
__proto__: null,
|
|
@@ -2690,11 +2830,14 @@ var Turbo = /*#__PURE__*/Object.freeze({
|
|
|
2690
2830
|
disconnectStreamSource: disconnectStreamSource,
|
|
2691
2831
|
renderStreamMessage: renderStreamMessage,
|
|
2692
2832
|
clearCache: clearCache,
|
|
2693
|
-
setProgressBarDelay: setProgressBarDelay
|
|
2833
|
+
setProgressBarDelay: setProgressBarDelay,
|
|
2834
|
+
setConfirmMethod: setConfirmMethod
|
|
2694
2835
|
});
|
|
2695
2836
|
|
|
2696
2837
|
class FrameController {
|
|
2697
2838
|
constructor(element) {
|
|
2839
|
+
this.fetchResponseLoaded = (fetchResponse) => { };
|
|
2840
|
+
this.currentFetchRequest = null;
|
|
2698
2841
|
this.resolveVisitPromise = () => { };
|
|
2699
2842
|
this.connected = false;
|
|
2700
2843
|
this.hasBeenLoaded = false;
|
|
@@ -2750,11 +2893,10 @@ class FrameController {
|
|
|
2750
2893
|
this.currentURL = this.sourceURL;
|
|
2751
2894
|
if (this.sourceURL) {
|
|
2752
2895
|
try {
|
|
2753
|
-
this.element.loaded = this.visit(this.sourceURL);
|
|
2896
|
+
this.element.loaded = this.visit(expandURL(this.sourceURL));
|
|
2754
2897
|
this.appearanceObserver.stop();
|
|
2755
2898
|
await this.element.loaded;
|
|
2756
2899
|
this.hasBeenLoaded = true;
|
|
2757
|
-
session.frameLoaded(this.element);
|
|
2758
2900
|
}
|
|
2759
2901
|
catch (error) {
|
|
2760
2902
|
this.currentURL = previousURL;
|
|
@@ -2764,7 +2906,7 @@ class FrameController {
|
|
|
2764
2906
|
}
|
|
2765
2907
|
}
|
|
2766
2908
|
async loadResponse(fetchResponse) {
|
|
2767
|
-
if (fetchResponse.redirected) {
|
|
2909
|
+
if (fetchResponse.redirected || (fetchResponse.succeeded && fetchResponse.isHTML)) {
|
|
2768
2910
|
this.sourceURL = fetchResponse.response.url;
|
|
2769
2911
|
}
|
|
2770
2912
|
try {
|
|
@@ -2772,17 +2914,22 @@ class FrameController {
|
|
|
2772
2914
|
if (html) {
|
|
2773
2915
|
const { body } = parseHTMLDocument(html);
|
|
2774
2916
|
const snapshot = new Snapshot(await this.extractForeignFrameElement(body));
|
|
2775
|
-
const renderer = new FrameRenderer(this.view.snapshot, snapshot, false);
|
|
2917
|
+
const renderer = new FrameRenderer(this.view.snapshot, snapshot, false, false);
|
|
2776
2918
|
if (this.view.renderPromise)
|
|
2777
2919
|
await this.view.renderPromise;
|
|
2778
2920
|
await this.view.render(renderer);
|
|
2779
2921
|
session.frameRendered(fetchResponse, this.element);
|
|
2922
|
+
session.frameLoaded(this.element);
|
|
2923
|
+
this.fetchResponseLoaded(fetchResponse);
|
|
2780
2924
|
}
|
|
2781
2925
|
}
|
|
2782
2926
|
catch (error) {
|
|
2783
2927
|
console.error(error);
|
|
2784
2928
|
this.view.invalidate();
|
|
2785
2929
|
}
|
|
2930
|
+
finally {
|
|
2931
|
+
this.fetchResponseLoaded = () => { };
|
|
2932
|
+
}
|
|
2786
2933
|
}
|
|
2787
2934
|
elementAppearedInViewport(element) {
|
|
2788
2935
|
this.loadSourceURL();
|
|
@@ -2808,20 +2955,15 @@ class FrameController {
|
|
|
2808
2955
|
}
|
|
2809
2956
|
this.reloadable = false;
|
|
2810
2957
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
else {
|
|
2815
|
-
const { fetchRequest } = this.formSubmission;
|
|
2816
|
-
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
|
2817
|
-
this.formSubmission.start();
|
|
2818
|
-
}
|
|
2958
|
+
const { fetchRequest } = this.formSubmission;
|
|
2959
|
+
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
|
2960
|
+
this.formSubmission.start();
|
|
2819
2961
|
}
|
|
2820
2962
|
prepareHeadersForRequest(headers, request) {
|
|
2821
2963
|
headers["Turbo-Frame"] = this.id;
|
|
2822
2964
|
}
|
|
2823
2965
|
requestStarted(request) {
|
|
2824
|
-
this.element
|
|
2966
|
+
markAsBusy(this.element);
|
|
2825
2967
|
}
|
|
2826
2968
|
requestPreventedHandlingResponse(request, response) {
|
|
2827
2969
|
this.resolveVisitPromise();
|
|
@@ -2839,14 +2981,14 @@ class FrameController {
|
|
|
2839
2981
|
this.resolveVisitPromise();
|
|
2840
2982
|
}
|
|
2841
2983
|
requestFinished(request) {
|
|
2842
|
-
this.element
|
|
2984
|
+
clearBusyState(this.element);
|
|
2843
2985
|
}
|
|
2844
|
-
formSubmissionStarted(
|
|
2845
|
-
|
|
2846
|
-
frame.setAttribute("busy", "");
|
|
2986
|
+
formSubmissionStarted({ formElement }) {
|
|
2987
|
+
markAsBusy(formElement, this.findFrameElement(formElement));
|
|
2847
2988
|
}
|
|
2848
2989
|
formSubmissionSucceededWithResponse(formSubmission, response) {
|
|
2849
2990
|
const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
|
|
2991
|
+
this.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
|
|
2850
2992
|
frame.delegate.loadResponse(response);
|
|
2851
2993
|
}
|
|
2852
2994
|
formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
|
|
@@ -2855,9 +2997,8 @@ class FrameController {
|
|
|
2855
2997
|
formSubmissionErrored(formSubmission, error) {
|
|
2856
2998
|
console.error(error);
|
|
2857
2999
|
}
|
|
2858
|
-
formSubmissionFinished(
|
|
2859
|
-
|
|
2860
|
-
frame.removeAttribute("busy");
|
|
3000
|
+
formSubmissionFinished({ formElement }) {
|
|
3001
|
+
clearBusyState(formElement, this.findFrameElement(formElement));
|
|
2861
3002
|
}
|
|
2862
3003
|
allowsImmediateRender(snapshot, resume) {
|
|
2863
3004
|
return true;
|
|
@@ -2867,10 +3008,14 @@ class FrameController {
|
|
|
2867
3008
|
viewInvalidated() {
|
|
2868
3009
|
}
|
|
2869
3010
|
async visit(url) {
|
|
2870
|
-
|
|
3011
|
+
var _a;
|
|
3012
|
+
const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams, this.element);
|
|
3013
|
+
(_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
3014
|
+
this.currentFetchRequest = request;
|
|
2871
3015
|
return new Promise(resolve => {
|
|
2872
3016
|
this.resolveVisitPromise = () => {
|
|
2873
3017
|
this.resolveVisitPromise = () => { };
|
|
3018
|
+
this.currentFetchRequest = null;
|
|
2874
3019
|
resolve();
|
|
2875
3020
|
};
|
|
2876
3021
|
request.perform();
|
|
@@ -2878,12 +3023,27 @@ class FrameController {
|
|
|
2878
3023
|
}
|
|
2879
3024
|
navigateFrame(element, url, submitter) {
|
|
2880
3025
|
const frame = this.findFrameElement(element, submitter);
|
|
3026
|
+
this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
|
|
2881
3027
|
frame.setAttribute("reloadable", "");
|
|
2882
3028
|
frame.src = url;
|
|
2883
3029
|
}
|
|
3030
|
+
proposeVisitIfNavigatedWithAction(frame, element, submitter) {
|
|
3031
|
+
const action = getAttribute("data-turbo-action", submitter, element, frame);
|
|
3032
|
+
if (isAction(action)) {
|
|
3033
|
+
const { visitCachedSnapshot } = new SnapshotSubstitution(frame);
|
|
3034
|
+
frame.delegate.fetchResponseLoaded = (fetchResponse) => {
|
|
3035
|
+
if (frame.src) {
|
|
3036
|
+
const { statusCode, redirected } = fetchResponse;
|
|
3037
|
+
const responseHTML = frame.ownerDocument.documentElement.outerHTML;
|
|
3038
|
+
const response = { statusCode, redirected, responseHTML };
|
|
3039
|
+
session.visit(frame.src, { action, response, visitCachedSnapshot, willRender: false });
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
2884
3044
|
findFrameElement(element, submitter) {
|
|
2885
3045
|
var _a;
|
|
2886
|
-
const id =
|
|
3046
|
+
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
2887
3047
|
return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
|
|
2888
3048
|
}
|
|
2889
3049
|
async extractForeignFrameElement(container) {
|
|
@@ -2904,8 +3064,15 @@ class FrameController {
|
|
|
2904
3064
|
}
|
|
2905
3065
|
return new FrameElement();
|
|
2906
3066
|
}
|
|
3067
|
+
formActionIsVisitable(form, submitter) {
|
|
3068
|
+
const action = getAction(form, submitter);
|
|
3069
|
+
return locationIsVisitable(expandURL(action), this.rootLocation);
|
|
3070
|
+
}
|
|
2907
3071
|
shouldInterceptNavigation(element, submitter) {
|
|
2908
|
-
const id =
|
|
3072
|
+
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
3073
|
+
if (element instanceof HTMLFormElement && !this.formActionIsVisitable(element, submitter)) {
|
|
3074
|
+
return false;
|
|
3075
|
+
}
|
|
2909
3076
|
if (!this.enabled || id == "_top") {
|
|
2910
3077
|
return false;
|
|
2911
3078
|
}
|
|
@@ -2962,6 +3129,23 @@ class FrameController {
|
|
|
2962
3129
|
get isActive() {
|
|
2963
3130
|
return this.element.isActive && this.connected;
|
|
2964
3131
|
}
|
|
3132
|
+
get rootLocation() {
|
|
3133
|
+
var _a;
|
|
3134
|
+
const meta = this.element.ownerDocument.querySelector(`meta[name="turbo-root"]`);
|
|
3135
|
+
const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
|
|
3136
|
+
return expandURL(root);
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
class SnapshotSubstitution {
|
|
3140
|
+
constructor(element) {
|
|
3141
|
+
this.visitCachedSnapshot = ({ element }) => {
|
|
3142
|
+
var _a;
|
|
3143
|
+
const { id, clone } = this;
|
|
3144
|
+
(_a = element.querySelector("#" + id)) === null || _a === void 0 ? void 0 : _a.replaceWith(clone);
|
|
3145
|
+
};
|
|
3146
|
+
this.clone = element.cloneNode(true);
|
|
3147
|
+
this.id = element.id;
|
|
3148
|
+
}
|
|
2965
3149
|
}
|
|
2966
3150
|
function getFrameElementById(id) {
|
|
2967
3151
|
if (id != null) {
|
|
@@ -2982,6 +3166,7 @@ function activateElement(element, currentURL) {
|
|
|
2982
3166
|
}
|
|
2983
3167
|
if (element instanceof FrameElement) {
|
|
2984
3168
|
element.connectedCallback();
|
|
3169
|
+
element.disconnectedCallback();
|
|
2985
3170
|
return element;
|
|
2986
3171
|
}
|
|
2987
3172
|
}
|
|
@@ -3152,4 +3337,4 @@ customElements.define("turbo-stream", StreamElement);
|
|
|
3152
3337
|
window.Turbo = Turbo;
|
|
3153
3338
|
start();
|
|
3154
3339
|
|
|
3155
|
-
export { PageRenderer, PageSnapshot, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setProgressBarDelay, start, visit };
|
|
3340
|
+
export { PageRenderer, PageSnapshot, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setProgressBarDelay, start, visit };
|