@hotwired/turbo 7.2.0-beta.1 → 7.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/turbo.es2017-esm.js +388 -321
- package/dist/turbo.es2017-umd.js +388 -321
- package/dist/types/core/drive/error_renderer.d.ts +1 -1
- package/dist/types/core/drive/head_snapshot.d.ts +3 -3
- package/dist/types/core/drive/navigator.d.ts +3 -3
- package/dist/types/core/drive/page_renderer.d.ts +5 -5
- package/dist/types/core/drive/visit.d.ts +12 -1
- package/dist/types/core/frames/frame_controller.d.ts +26 -16
- package/dist/types/core/frames/frame_redirector.d.ts +12 -10
- package/dist/types/core/frames/frame_renderer.d.ts +1 -1
- package/dist/types/core/index.d.ts +5 -4
- package/dist/types/core/native/adapter.d.ts +1 -1
- package/dist/types/core/native/browser_adapter.d.ts +1 -1
- package/dist/types/core/renderer.d.ts +0 -2
- package/dist/types/core/session.d.ts +18 -11
- package/dist/types/core/streams/stream_message.d.ts +2 -6
- package/dist/types/core/types.d.ts +4 -0
- package/dist/types/core/view.d.ts +1 -1
- package/dist/types/elements/frame_element.d.ts +5 -5
- package/dist/types/elements/stream_element.d.ts +3 -1
- package/dist/types/http/fetch_request.d.ts +1 -0
- package/dist/types/observers/form_link_click_observer.d.ts +14 -0
- package/dist/types/observers/form_submit_observer.d.ts +2 -1
- package/dist/types/observers/link_click_observer.d.ts +3 -2
- package/dist/types/tests/functional/form_mode_tests.d.ts +1 -0
- package/dist/types/tests/unit/deprecated_adapter_support_test.d.ts +1 -1
- package/dist/types/util.d.ts +6 -0
- package/package.json +1 -1
- package/dist/types/core/frames/form_interceptor.d.ts +0 -12
- package/dist/types/core/frames/link_interceptor.d.ts +0 -16
- package/dist/types/observers/form_link_interceptor.d.ts +0 -14
package/dist/turbo.es2017-esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Turbo 7.2.0-beta.
|
|
2
|
+
Turbo 7.2.0-beta.2
|
|
3
3
|
Copyright © 2022 Basecamp, LLC
|
|
4
4
|
*/
|
|
5
5
|
(function () {
|
|
@@ -132,6 +132,7 @@ class FrameElement extends HTMLElement {
|
|
|
132
132
|
this.removeAttribute("complete");
|
|
133
133
|
this.src = null;
|
|
134
134
|
this.src = src;
|
|
135
|
+
return this.loaded;
|
|
135
136
|
}
|
|
136
137
|
attributeChangedCallback(name) {
|
|
137
138
|
if (name == "loading") {
|
|
@@ -310,6 +311,36 @@ class FetchResponse {
|
|
|
310
311
|
}
|
|
311
312
|
}
|
|
312
313
|
|
|
314
|
+
function isAction(action) {
|
|
315
|
+
return action == "advance" || action == "replace" || action == "restore";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function activateScriptElement(element) {
|
|
319
|
+
if (element.getAttribute("data-turbo-eval") == "false") {
|
|
320
|
+
return element;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
const createdScriptElement = document.createElement("script");
|
|
324
|
+
const cspNonce = getMetaContent("csp-nonce");
|
|
325
|
+
if (cspNonce) {
|
|
326
|
+
createdScriptElement.nonce = cspNonce;
|
|
327
|
+
}
|
|
328
|
+
createdScriptElement.textContent = element.textContent;
|
|
329
|
+
createdScriptElement.async = false;
|
|
330
|
+
copyElementAttributes(createdScriptElement, element);
|
|
331
|
+
return createdScriptElement;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function copyElementAttributes(destinationElement, sourceElement) {
|
|
335
|
+
for (const { name, value } of sourceElement.attributes) {
|
|
336
|
+
destinationElement.setAttribute(name, value);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function createDocumentFragment(html) {
|
|
340
|
+
const template = document.createElement("template");
|
|
341
|
+
template.innerHTML = html;
|
|
342
|
+
return template.content;
|
|
343
|
+
}
|
|
313
344
|
function dispatch(eventName, { target, cancelable, detail } = {}) {
|
|
314
345
|
const event = new CustomEvent(eventName, {
|
|
315
346
|
cancelable,
|
|
@@ -389,6 +420,31 @@ function clearBusyState(...elements) {
|
|
|
389
420
|
element.removeAttribute("aria-busy");
|
|
390
421
|
}
|
|
391
422
|
}
|
|
423
|
+
function waitForLoad(element, timeoutInMilliseconds = 2000) {
|
|
424
|
+
return new Promise((resolve) => {
|
|
425
|
+
const onComplete = () => {
|
|
426
|
+
element.removeEventListener("error", onComplete);
|
|
427
|
+
element.removeEventListener("load", onComplete);
|
|
428
|
+
resolve();
|
|
429
|
+
};
|
|
430
|
+
element.addEventListener("load", onComplete, { once: true });
|
|
431
|
+
element.addEventListener("error", onComplete, { once: true });
|
|
432
|
+
setTimeout(resolve, timeoutInMilliseconds);
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
function getHistoryMethodForAction(action) {
|
|
436
|
+
switch (action) {
|
|
437
|
+
case "replace":
|
|
438
|
+
return history.replaceState;
|
|
439
|
+
case "advance":
|
|
440
|
+
case "restore":
|
|
441
|
+
return history.pushState;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function getVisitAction(...elements) {
|
|
445
|
+
const action = getAttribute("data-turbo-action", ...elements);
|
|
446
|
+
return isAction(action) ? action : null;
|
|
447
|
+
}
|
|
392
448
|
function getMetaElement(name) {
|
|
393
449
|
return document.querySelector(`meta[name="${name}"]`);
|
|
394
450
|
}
|
|
@@ -513,6 +569,9 @@ class FetchRequest {
|
|
|
513
569
|
get abortSignal() {
|
|
514
570
|
return this.abortController.signal;
|
|
515
571
|
}
|
|
572
|
+
acceptResponseType(mimeType) {
|
|
573
|
+
this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
|
|
574
|
+
}
|
|
516
575
|
async allowRequestToBeIntercepted(fetchOptions) {
|
|
517
576
|
const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
|
|
518
577
|
const event = dispatch("turbo:before-fetch-request", {
|
|
@@ -557,40 +616,29 @@ class AppearanceObserver {
|
|
|
557
616
|
}
|
|
558
617
|
|
|
559
618
|
class StreamMessage {
|
|
560
|
-
constructor(
|
|
561
|
-
this.
|
|
562
|
-
this.templateElement.innerHTML = html;
|
|
619
|
+
constructor(fragment) {
|
|
620
|
+
this.fragment = importStreamElements(fragment);
|
|
563
621
|
}
|
|
564
622
|
static wrap(message) {
|
|
565
623
|
if (typeof message == "string") {
|
|
566
|
-
return new this(message);
|
|
624
|
+
return new this(createDocumentFragment(message));
|
|
567
625
|
}
|
|
568
626
|
else {
|
|
569
627
|
return message;
|
|
570
628
|
}
|
|
571
629
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
630
|
+
}
|
|
631
|
+
StreamMessage.contentType = "text/vnd.turbo-stream.html";
|
|
632
|
+
function importStreamElements(fragment) {
|
|
633
|
+
for (const element of fragment.querySelectorAll("turbo-stream")) {
|
|
634
|
+
const streamElement = document.importNode(element, true);
|
|
635
|
+
for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
|
|
636
|
+
inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
|
|
576
637
|
}
|
|
577
|
-
|
|
578
|
-
}
|
|
579
|
-
get foreignElements() {
|
|
580
|
-
return this.templateChildren.reduce((streamElements, child) => {
|
|
581
|
-
if (child.tagName.toLowerCase() == "turbo-stream") {
|
|
582
|
-
return [...streamElements, child];
|
|
583
|
-
}
|
|
584
|
-
else {
|
|
585
|
-
return streamElements;
|
|
586
|
-
}
|
|
587
|
-
}, []);
|
|
588
|
-
}
|
|
589
|
-
get templateChildren() {
|
|
590
|
-
return Array.from(this.templateElement.content.children);
|
|
638
|
+
element.replaceWith(streamElement);
|
|
591
639
|
}
|
|
640
|
+
return fragment;
|
|
592
641
|
}
|
|
593
|
-
StreamMessage.contentType = "text/vnd.turbo-stream.html";
|
|
594
642
|
|
|
595
643
|
var FormSubmissionState;
|
|
596
644
|
(function (FormSubmissionState) {
|
|
@@ -705,7 +753,7 @@ class FormSubmission {
|
|
|
705
753
|
}
|
|
706
754
|
}
|
|
707
755
|
if (this.requestAcceptsTurboStreamResponse(request)) {
|
|
708
|
-
|
|
756
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
709
757
|
}
|
|
710
758
|
}
|
|
711
759
|
requestStarted(_request) {
|
|
@@ -741,6 +789,10 @@ class FormSubmission {
|
|
|
741
789
|
}
|
|
742
790
|
requestErrored(request, error) {
|
|
743
791
|
this.result = { success: false, error };
|
|
792
|
+
dispatch("turbo:fetch-request-error", {
|
|
793
|
+
target: this.formElement,
|
|
794
|
+
detail: { request, error },
|
|
795
|
+
});
|
|
744
796
|
this.delegate.formSubmissionErrored(this, error);
|
|
745
797
|
}
|
|
746
798
|
requestFinished(_request) {
|
|
@@ -764,8 +816,8 @@ function buildFormData(formElement, submitter) {
|
|
|
764
816
|
const formData = new FormData(formElement);
|
|
765
817
|
const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
|
|
766
818
|
const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
|
|
767
|
-
if (name
|
|
768
|
-
formData.append(name, value);
|
|
819
|
+
if (name) {
|
|
820
|
+
formData.append(name, value || "");
|
|
769
821
|
}
|
|
770
822
|
return formData;
|
|
771
823
|
}
|
|
@@ -813,7 +865,14 @@ class Snapshot {
|
|
|
813
865
|
return this.element.isConnected;
|
|
814
866
|
}
|
|
815
867
|
get firstAutofocusableElement() {
|
|
816
|
-
|
|
868
|
+
const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
|
|
869
|
+
for (const element of this.element.querySelectorAll("[autofocus]")) {
|
|
870
|
+
if (element.closest(inertDisabledOrHidden) == null)
|
|
871
|
+
return element;
|
|
872
|
+
else
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
return null;
|
|
817
876
|
}
|
|
818
877
|
get permanentElements() {
|
|
819
878
|
return [...this.element.querySelectorAll("[id][data-turbo-permanent]")];
|
|
@@ -834,32 +893,54 @@ class Snapshot {
|
|
|
834
893
|
}
|
|
835
894
|
}
|
|
836
895
|
|
|
837
|
-
class
|
|
838
|
-
constructor(delegate,
|
|
896
|
+
class FormSubmitObserver {
|
|
897
|
+
constructor(delegate, eventTarget) {
|
|
898
|
+
this.started = false;
|
|
899
|
+
this.submitCaptured = () => {
|
|
900
|
+
this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
|
|
901
|
+
this.eventTarget.addEventListener("submit", this.submitBubbled, false);
|
|
902
|
+
};
|
|
839
903
|
this.submitBubbled = ((event) => {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
form instanceof HTMLFormElement &&
|
|
843
|
-
form.closest("turbo-frame, html") == this.element) {
|
|
904
|
+
if (!event.defaultPrevented) {
|
|
905
|
+
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
844
906
|
const submitter = event.submitter || undefined;
|
|
845
|
-
|
|
846
|
-
|
|
907
|
+
if (form &&
|
|
908
|
+
submissionDoesNotDismissDialog(form, submitter) &&
|
|
909
|
+
submissionDoesNotTargetIFrame(form, submitter) &&
|
|
910
|
+
this.delegate.willSubmitForm(form, submitter)) {
|
|
847
911
|
event.preventDefault();
|
|
848
|
-
|
|
849
|
-
this.delegate.formSubmissionIntercepted(form, submitter);
|
|
912
|
+
this.delegate.formSubmitted(form, submitter);
|
|
850
913
|
}
|
|
851
914
|
}
|
|
852
915
|
});
|
|
853
916
|
this.delegate = delegate;
|
|
854
|
-
this.
|
|
917
|
+
this.eventTarget = eventTarget;
|
|
855
918
|
}
|
|
856
919
|
start() {
|
|
857
|
-
|
|
920
|
+
if (!this.started) {
|
|
921
|
+
this.eventTarget.addEventListener("submit", this.submitCaptured, true);
|
|
922
|
+
this.started = true;
|
|
923
|
+
}
|
|
858
924
|
}
|
|
859
925
|
stop() {
|
|
860
|
-
|
|
926
|
+
if (this.started) {
|
|
927
|
+
this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
|
|
928
|
+
this.started = false;
|
|
929
|
+
}
|
|
861
930
|
}
|
|
862
931
|
}
|
|
932
|
+
function submissionDoesNotDismissDialog(form, submitter) {
|
|
933
|
+
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
934
|
+
return method != "dialog";
|
|
935
|
+
}
|
|
936
|
+
function submissionDoesNotTargetIFrame(form, submitter) {
|
|
937
|
+
const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
|
|
938
|
+
for (const element of document.getElementsByName(target)) {
|
|
939
|
+
if (element instanceof HTMLIFrameElement)
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
return true;
|
|
943
|
+
}
|
|
863
944
|
|
|
864
945
|
class View {
|
|
865
946
|
constructor(delegate, element) {
|
|
@@ -911,7 +992,7 @@ class View {
|
|
|
911
992
|
try {
|
|
912
993
|
this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
|
|
913
994
|
this.renderer = renderer;
|
|
914
|
-
this.prepareToRenderSnapshot(renderer);
|
|
995
|
+
await this.prepareToRenderSnapshot(renderer);
|
|
915
996
|
const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
|
|
916
997
|
const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
|
|
917
998
|
const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
|
|
@@ -935,9 +1016,9 @@ class View {
|
|
|
935
1016
|
invalidate(reason) {
|
|
936
1017
|
this.delegate.viewInvalidated(reason);
|
|
937
1018
|
}
|
|
938
|
-
prepareToRenderSnapshot(renderer) {
|
|
1019
|
+
async prepareToRenderSnapshot(renderer) {
|
|
939
1020
|
this.markAsPreview(renderer.isPreview);
|
|
940
|
-
renderer.prepareToRender();
|
|
1021
|
+
await renderer.prepareToRender();
|
|
941
1022
|
}
|
|
942
1023
|
markAsPreview(isPreview) {
|
|
943
1024
|
if (isPreview) {
|
|
@@ -964,64 +1045,84 @@ class FrameView extends View {
|
|
|
964
1045
|
}
|
|
965
1046
|
}
|
|
966
1047
|
|
|
967
|
-
class
|
|
968
|
-
constructor(delegate,
|
|
969
|
-
this.
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
else {
|
|
974
|
-
delete this.clickEvent;
|
|
975
|
-
}
|
|
1048
|
+
class LinkClickObserver {
|
|
1049
|
+
constructor(delegate, eventTarget) {
|
|
1050
|
+
this.started = false;
|
|
1051
|
+
this.clickCaptured = () => {
|
|
1052
|
+
this.eventTarget.removeEventListener("click", this.clickBubbled, false);
|
|
1053
|
+
this.eventTarget.addEventListener("click", this.clickBubbled, false);
|
|
976
1054
|
};
|
|
977
|
-
this.
|
|
978
|
-
if (
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
this.
|
|
1055
|
+
this.clickBubbled = (event) => {
|
|
1056
|
+
if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
|
|
1057
|
+
const target = (event.composedPath && event.composedPath()[0]) || event.target;
|
|
1058
|
+
const link = this.findLinkFromClickTarget(target);
|
|
1059
|
+
if (link && doesNotTargetIFrame(link)) {
|
|
1060
|
+
const location = this.getLocationForLink(link);
|
|
1061
|
+
if (this.delegate.willFollowLinkToLocation(link, location, event)) {
|
|
1062
|
+
event.preventDefault();
|
|
1063
|
+
this.delegate.followedLinkToLocation(link, location);
|
|
1064
|
+
}
|
|
983
1065
|
}
|
|
984
1066
|
}
|
|
985
|
-
|
|
986
|
-
});
|
|
987
|
-
this.willVisit = ((_event) => {
|
|
988
|
-
delete this.clickEvent;
|
|
989
|
-
});
|
|
1067
|
+
};
|
|
990
1068
|
this.delegate = delegate;
|
|
991
|
-
this.
|
|
1069
|
+
this.eventTarget = eventTarget;
|
|
992
1070
|
}
|
|
993
1071
|
start() {
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1072
|
+
if (!this.started) {
|
|
1073
|
+
this.eventTarget.addEventListener("click", this.clickCaptured, true);
|
|
1074
|
+
this.started = true;
|
|
1075
|
+
}
|
|
997
1076
|
}
|
|
998
1077
|
stop() {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1078
|
+
if (this.started) {
|
|
1079
|
+
this.eventTarget.removeEventListener("click", this.clickCaptured, true);
|
|
1080
|
+
this.started = false;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
clickEventIsSignificant(event) {
|
|
1084
|
+
return !((event.target && event.target.isContentEditable) ||
|
|
1085
|
+
event.defaultPrevented ||
|
|
1086
|
+
event.which > 1 ||
|
|
1087
|
+
event.altKey ||
|
|
1088
|
+
event.ctrlKey ||
|
|
1089
|
+
event.metaKey ||
|
|
1090
|
+
event.shiftKey);
|
|
1091
|
+
}
|
|
1092
|
+
findLinkFromClickTarget(target) {
|
|
1093
|
+
if (target instanceof Element) {
|
|
1094
|
+
return target.closest("a[href]:not([target^=_]):not([download])");
|
|
1095
|
+
}
|
|
1002
1096
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
return element && element.closest("turbo-frame, html") == this.element;
|
|
1097
|
+
getLocationForLink(link) {
|
|
1098
|
+
return expandURL(link.getAttribute("href") || "");
|
|
1006
1099
|
}
|
|
1007
1100
|
}
|
|
1101
|
+
function doesNotTargetIFrame(anchor) {
|
|
1102
|
+
for (const element of document.getElementsByName(anchor.target)) {
|
|
1103
|
+
if (element instanceof HTMLIFrameElement)
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1008
1108
|
|
|
1009
|
-
class
|
|
1109
|
+
class FormLinkClickObserver {
|
|
1010
1110
|
constructor(delegate, element) {
|
|
1011
1111
|
this.delegate = delegate;
|
|
1012
|
-
this.
|
|
1112
|
+
this.linkClickObserver = new LinkClickObserver(this, element);
|
|
1013
1113
|
}
|
|
1014
1114
|
start() {
|
|
1015
|
-
this.
|
|
1115
|
+
this.linkClickObserver.start();
|
|
1016
1116
|
}
|
|
1017
1117
|
stop() {
|
|
1018
|
-
this.
|
|
1118
|
+
this.linkClickObserver.stop();
|
|
1019
1119
|
}
|
|
1020
|
-
|
|
1021
|
-
return (this.delegate.
|
|
1022
|
-
|
|
1120
|
+
willFollowLinkToLocation(link, location, originalEvent) {
|
|
1121
|
+
return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
|
|
1122
|
+
link.hasAttribute("data-turbo-method"));
|
|
1023
1123
|
}
|
|
1024
|
-
|
|
1124
|
+
followedLinkToLocation(link, location) {
|
|
1125
|
+
const action = location.href;
|
|
1025
1126
|
const form = document.createElement("form");
|
|
1026
1127
|
form.setAttribute("data-turbo", "true");
|
|
1027
1128
|
form.setAttribute("action", action);
|
|
@@ -1038,7 +1139,7 @@ class FormLinkInterceptor {
|
|
|
1038
1139
|
const turboStream = link.hasAttribute("data-turbo-stream");
|
|
1039
1140
|
if (turboStream)
|
|
1040
1141
|
form.setAttribute("data-turbo-stream", "");
|
|
1041
|
-
this.delegate.
|
|
1142
|
+
this.delegate.submittedFormLinkToLocation(link, location, form);
|
|
1042
1143
|
document.body.appendChild(form);
|
|
1043
1144
|
form.requestSubmit();
|
|
1044
1145
|
form.remove();
|
|
@@ -1122,21 +1223,6 @@ class Renderer {
|
|
|
1122
1223
|
delete this.resolvingFunctions;
|
|
1123
1224
|
}
|
|
1124
1225
|
}
|
|
1125
|
-
createScriptElement(element) {
|
|
1126
|
-
if (element.getAttribute("data-turbo-eval") == "false") {
|
|
1127
|
-
return element;
|
|
1128
|
-
}
|
|
1129
|
-
else {
|
|
1130
|
-
const createdScriptElement = document.createElement("script");
|
|
1131
|
-
if (this.cspNonce) {
|
|
1132
|
-
createdScriptElement.nonce = this.cspNonce;
|
|
1133
|
-
}
|
|
1134
|
-
createdScriptElement.textContent = element.textContent;
|
|
1135
|
-
createdScriptElement.async = false;
|
|
1136
|
-
copyElementAttributes(createdScriptElement, element);
|
|
1137
|
-
return createdScriptElement;
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
1226
|
preservingPermanentElements(callback) {
|
|
1141
1227
|
Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
|
|
1142
1228
|
}
|
|
@@ -1171,14 +1257,6 @@ class Renderer {
|
|
|
1171
1257
|
get permanentElementMap() {
|
|
1172
1258
|
return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
|
|
1173
1259
|
}
|
|
1174
|
-
get cspNonce() {
|
|
1175
|
-
return getMetaContent("csp-nonce");
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
function copyElementAttributes(destinationElement, sourceElement) {
|
|
1179
|
-
for (const { name, value } of [...sourceElement.attributes]) {
|
|
1180
|
-
destinationElement.setAttribute(name, value);
|
|
1181
|
-
}
|
|
1182
1260
|
}
|
|
1183
1261
|
function elementIsFocusable(element) {
|
|
1184
1262
|
return element && typeof element.focus == "function";
|
|
@@ -1216,7 +1294,7 @@ class FrameRenderer extends Renderer {
|
|
|
1216
1294
|
this.activateScriptElements();
|
|
1217
1295
|
}
|
|
1218
1296
|
loadFrameElement() {
|
|
1219
|
-
this.delegate.
|
|
1297
|
+
this.delegate.willRenderFrame(this.currentElement, this.newElement);
|
|
1220
1298
|
this.renderElement(this.currentElement, this.newElement);
|
|
1221
1299
|
}
|
|
1222
1300
|
scrollFrameIntoView() {
|
|
@@ -1233,7 +1311,7 @@ class FrameRenderer extends Renderer {
|
|
|
1233
1311
|
}
|
|
1234
1312
|
activateScriptElements() {
|
|
1235
1313
|
for (const inertScriptElement of this.newScriptElements) {
|
|
1236
|
-
const activatedScriptElement =
|
|
1314
|
+
const activatedScriptElement = activateScriptElement(inertScriptElement);
|
|
1237
1315
|
inertScriptElement.replaceWith(activatedScriptElement);
|
|
1238
1316
|
}
|
|
1239
1317
|
}
|
|
@@ -1519,6 +1597,9 @@ const defaultOptions = {
|
|
|
1519
1597
|
historyChanged: false,
|
|
1520
1598
|
visitCachedSnapshot: () => { },
|
|
1521
1599
|
willRender: true,
|
|
1600
|
+
updateHistory: true,
|
|
1601
|
+
shouldCacheSnapshot: true,
|
|
1602
|
+
acceptsStreamResponse: false,
|
|
1522
1603
|
};
|
|
1523
1604
|
var SystemStatusCode;
|
|
1524
1605
|
(function (SystemStatusCode) {
|
|
@@ -1533,12 +1614,15 @@ class Visit {
|
|
|
1533
1614
|
this.followedRedirect = false;
|
|
1534
1615
|
this.historyChanged = false;
|
|
1535
1616
|
this.scrolled = false;
|
|
1617
|
+
this.shouldCacheSnapshot = true;
|
|
1618
|
+
this.acceptsStreamResponse = false;
|
|
1536
1619
|
this.snapshotCached = false;
|
|
1537
1620
|
this.state = VisitState.initialized;
|
|
1538
1621
|
this.delegate = delegate;
|
|
1539
1622
|
this.location = location;
|
|
1540
1623
|
this.restorationIdentifier = restorationIdentifier || uuid();
|
|
1541
|
-
|
|
1624
|
+
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);
|
|
1542
1626
|
this.action = action;
|
|
1543
1627
|
this.historyChanged = historyChanged;
|
|
1544
1628
|
this.referrer = referrer;
|
|
@@ -1547,7 +1631,10 @@ class Visit {
|
|
|
1547
1631
|
this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
|
|
1548
1632
|
this.visitCachedSnapshot = visitCachedSnapshot;
|
|
1549
1633
|
this.willRender = willRender;
|
|
1634
|
+
this.updateHistory = updateHistory;
|
|
1550
1635
|
this.scrolled = !willRender;
|
|
1636
|
+
this.shouldCacheSnapshot = shouldCacheSnapshot;
|
|
1637
|
+
this.acceptsStreamResponse = acceptsStreamResponse;
|
|
1551
1638
|
}
|
|
1552
1639
|
get adapter() {
|
|
1553
1640
|
return this.delegate.adapter;
|
|
@@ -1579,6 +1666,7 @@ class Visit {
|
|
|
1579
1666
|
}
|
|
1580
1667
|
this.cancelRender();
|
|
1581
1668
|
this.state = VisitState.canceled;
|
|
1669
|
+
this.resolvingFunctions.reject();
|
|
1582
1670
|
}
|
|
1583
1671
|
}
|
|
1584
1672
|
complete() {
|
|
@@ -1590,19 +1678,21 @@ class Visit {
|
|
|
1590
1678
|
this.adapter.visitCompleted(this);
|
|
1591
1679
|
this.delegate.visitCompleted(this);
|
|
1592
1680
|
}
|
|
1681
|
+
this.resolvingFunctions.resolve();
|
|
1593
1682
|
}
|
|
1594
1683
|
}
|
|
1595
1684
|
fail() {
|
|
1596
1685
|
if (this.state == VisitState.started) {
|
|
1597
1686
|
this.state = VisitState.failed;
|
|
1598
1687
|
this.adapter.visitFailed(this);
|
|
1688
|
+
this.resolvingFunctions.reject();
|
|
1599
1689
|
}
|
|
1600
1690
|
}
|
|
1601
1691
|
changeHistory() {
|
|
1602
1692
|
var _a;
|
|
1603
|
-
if (!this.historyChanged) {
|
|
1693
|
+
if (!this.historyChanged && this.updateHistory) {
|
|
1604
1694
|
const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
|
|
1605
|
-
const method =
|
|
1695
|
+
const method = getHistoryMethodForAction(actionForHistory);
|
|
1606
1696
|
this.history.update(method, this.location, this.restorationIdentifier);
|
|
1607
1697
|
this.historyChanged = true;
|
|
1608
1698
|
}
|
|
@@ -1647,11 +1737,13 @@ class Visit {
|
|
|
1647
1737
|
if (this.response) {
|
|
1648
1738
|
const { statusCode, responseHTML } = this.response;
|
|
1649
1739
|
this.render(async () => {
|
|
1650
|
-
this.
|
|
1740
|
+
if (this.shouldCacheSnapshot)
|
|
1741
|
+
this.cacheSnapshot();
|
|
1651
1742
|
if (this.view.renderPromise)
|
|
1652
1743
|
await this.view.renderPromise;
|
|
1653
1744
|
if (isSuccessful(statusCode) && responseHTML != null) {
|
|
1654
1745
|
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
|
|
1746
|
+
this.performScroll();
|
|
1655
1747
|
this.adapter.visitRendered(this);
|
|
1656
1748
|
this.complete();
|
|
1657
1749
|
}
|
|
@@ -1692,6 +1784,7 @@ class Visit {
|
|
|
1692
1784
|
if (this.view.renderPromise)
|
|
1693
1785
|
await this.view.renderPromise;
|
|
1694
1786
|
await this.view.renderPage(snapshot, isPreview, this.willRender, this);
|
|
1787
|
+
this.performScroll();
|
|
1695
1788
|
this.adapter.visitRendered(this);
|
|
1696
1789
|
if (!isPreview) {
|
|
1697
1790
|
this.complete();
|
|
@@ -1715,10 +1808,16 @@ class Visit {
|
|
|
1715
1808
|
if (this.isSamePage) {
|
|
1716
1809
|
this.render(async () => {
|
|
1717
1810
|
this.cacheSnapshot();
|
|
1811
|
+
this.performScroll();
|
|
1718
1812
|
this.adapter.visitRendered(this);
|
|
1719
1813
|
});
|
|
1720
1814
|
}
|
|
1721
1815
|
}
|
|
1816
|
+
prepareHeadersForRequest(headers, request) {
|
|
1817
|
+
if (this.acceptsStreamResponse) {
|
|
1818
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1722
1821
|
requestStarted() {
|
|
1723
1822
|
this.startRequest();
|
|
1724
1823
|
}
|
|
@@ -1760,7 +1859,7 @@ class Visit {
|
|
|
1760
1859
|
this.finishRequest();
|
|
1761
1860
|
}
|
|
1762
1861
|
performScroll() {
|
|
1763
|
-
if (!this.scrolled) {
|
|
1862
|
+
if (!this.scrolled && !this.view.forceReloaded) {
|
|
1764
1863
|
if (this.action == "restore") {
|
|
1765
1864
|
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
|
|
1766
1865
|
}
|
|
@@ -1829,9 +1928,6 @@ class Visit {
|
|
|
1829
1928
|
});
|
|
1830
1929
|
await callback();
|
|
1831
1930
|
delete this.frame;
|
|
1832
|
-
if (!this.view.forceReloaded) {
|
|
1833
|
-
this.performScroll();
|
|
1834
|
-
}
|
|
1835
1931
|
}
|
|
1836
1932
|
cancelRender() {
|
|
1837
1933
|
if (this.frame) {
|
|
@@ -1853,7 +1949,7 @@ class BrowserAdapter {
|
|
|
1853
1949
|
this.session = session;
|
|
1854
1950
|
}
|
|
1855
1951
|
visitProposedToLocation(location, options) {
|
|
1856
|
-
this.navigator.startVisit(location, uuid(), options);
|
|
1952
|
+
return this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
|
|
1857
1953
|
}
|
|
1858
1954
|
visitStarted(visit) {
|
|
1859
1955
|
this.location = visit.location;
|
|
@@ -1963,84 +2059,39 @@ class CacheObserver {
|
|
|
1963
2059
|
}
|
|
1964
2060
|
}
|
|
1965
2061
|
|
|
1966
|
-
class FormSubmitObserver {
|
|
1967
|
-
constructor(delegate) {
|
|
1968
|
-
this.started = false;
|
|
1969
|
-
this.submitCaptured = () => {
|
|
1970
|
-
removeEventListener("submit", this.submitBubbled, false);
|
|
1971
|
-
addEventListener("submit", this.submitBubbled, false);
|
|
1972
|
-
};
|
|
1973
|
-
this.submitBubbled = ((event) => {
|
|
1974
|
-
if (!event.defaultPrevented) {
|
|
1975
|
-
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
1976
|
-
const submitter = event.submitter || undefined;
|
|
1977
|
-
if (form &&
|
|
1978
|
-
submissionDoesNotDismissDialog(form, submitter) &&
|
|
1979
|
-
submissionDoesNotTargetIFrame(form, submitter) &&
|
|
1980
|
-
this.delegate.willSubmitForm(form, submitter)) {
|
|
1981
|
-
event.preventDefault();
|
|
1982
|
-
this.delegate.formSubmitted(form, submitter);
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
});
|
|
1986
|
-
this.delegate = delegate;
|
|
1987
|
-
}
|
|
1988
|
-
start() {
|
|
1989
|
-
if (!this.started) {
|
|
1990
|
-
addEventListener("submit", this.submitCaptured, true);
|
|
1991
|
-
this.started = true;
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
stop() {
|
|
1995
|
-
if (this.started) {
|
|
1996
|
-
removeEventListener("submit", this.submitCaptured, true);
|
|
1997
|
-
this.started = false;
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
}
|
|
2001
|
-
function submissionDoesNotDismissDialog(form, submitter) {
|
|
2002
|
-
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
2003
|
-
return method != "dialog";
|
|
2004
|
-
}
|
|
2005
|
-
function submissionDoesNotTargetIFrame(form, submitter) {
|
|
2006
|
-
const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
|
|
2007
|
-
for (const element of document.getElementsByName(target)) {
|
|
2008
|
-
if (element instanceof HTMLIFrameElement)
|
|
2009
|
-
return false;
|
|
2010
|
-
}
|
|
2011
|
-
return true;
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
2062
|
class FrameRedirector {
|
|
2015
|
-
constructor(element) {
|
|
2063
|
+
constructor(session, element) {
|
|
2064
|
+
this.session = session;
|
|
2016
2065
|
this.element = element;
|
|
2017
|
-
this.
|
|
2018
|
-
this.
|
|
2066
|
+
this.linkClickObserver = new LinkClickObserver(this, element);
|
|
2067
|
+
this.formSubmitObserver = new FormSubmitObserver(this, element);
|
|
2019
2068
|
}
|
|
2020
2069
|
start() {
|
|
2021
|
-
this.
|
|
2022
|
-
this.
|
|
2070
|
+
this.linkClickObserver.start();
|
|
2071
|
+
this.formSubmitObserver.start();
|
|
2023
2072
|
}
|
|
2024
2073
|
stop() {
|
|
2025
|
-
this.
|
|
2026
|
-
this.
|
|
2074
|
+
this.linkClickObserver.stop();
|
|
2075
|
+
this.formSubmitObserver.stop();
|
|
2027
2076
|
}
|
|
2028
|
-
|
|
2077
|
+
willFollowLinkToLocation(element) {
|
|
2029
2078
|
return this.shouldRedirect(element);
|
|
2030
2079
|
}
|
|
2031
|
-
|
|
2080
|
+
followedLinkToLocation(element, url) {
|
|
2032
2081
|
const frame = this.findFrameElement(element);
|
|
2033
2082
|
if (frame) {
|
|
2034
|
-
frame.delegate.
|
|
2083
|
+
frame.delegate.followedLinkToLocation(element, url);
|
|
2035
2084
|
}
|
|
2036
2085
|
}
|
|
2037
|
-
|
|
2038
|
-
return
|
|
2086
|
+
willSubmitForm(element, submitter) {
|
|
2087
|
+
return (element.closest("turbo-frame") == null &&
|
|
2088
|
+
this.shouldSubmit(element, submitter) &&
|
|
2089
|
+
this.shouldRedirect(element, submitter));
|
|
2039
2090
|
}
|
|
2040
|
-
|
|
2091
|
+
formSubmitted(element, submitter) {
|
|
2041
2092
|
const frame = this.findFrameElement(element, submitter);
|
|
2042
2093
|
if (frame) {
|
|
2043
|
-
frame.delegate.
|
|
2094
|
+
frame.delegate.formSubmitted(element, submitter);
|
|
2044
2095
|
}
|
|
2045
2096
|
}
|
|
2046
2097
|
shouldSubmit(form, submitter) {
|
|
@@ -2051,8 +2102,16 @@ class FrameRedirector {
|
|
|
2051
2102
|
return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
|
|
2052
2103
|
}
|
|
2053
2104
|
shouldRedirect(element, submitter) {
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2105
|
+
const isNavigatable = element instanceof HTMLFormElement
|
|
2106
|
+
? this.session.submissionIsNavigatable(element, submitter)
|
|
2107
|
+
: this.session.elementIsNavigatable(element);
|
|
2108
|
+
if (isNavigatable) {
|
|
2109
|
+
const frame = this.findFrameElement(element, submitter);
|
|
2110
|
+
return frame ? frame != element.closest("turbo-frame") : false;
|
|
2111
|
+
}
|
|
2112
|
+
else {
|
|
2113
|
+
return false;
|
|
2114
|
+
}
|
|
2056
2115
|
}
|
|
2057
2116
|
findFrameElement(element, submitter) {
|
|
2058
2117
|
const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
|
|
@@ -2144,70 +2203,6 @@ class History {
|
|
|
2144
2203
|
}
|
|
2145
2204
|
}
|
|
2146
2205
|
|
|
2147
|
-
class LinkClickObserver {
|
|
2148
|
-
constructor(delegate) {
|
|
2149
|
-
this.started = false;
|
|
2150
|
-
this.clickCaptured = () => {
|
|
2151
|
-
removeEventListener("click", this.clickBubbled, false);
|
|
2152
|
-
addEventListener("click", this.clickBubbled, false);
|
|
2153
|
-
};
|
|
2154
|
-
this.clickBubbled = (event) => {
|
|
2155
|
-
if (this.clickEventIsSignificant(event)) {
|
|
2156
|
-
const target = (event.composedPath && event.composedPath()[0]) || event.target;
|
|
2157
|
-
const link = this.findLinkFromClickTarget(target);
|
|
2158
|
-
if (link && doesNotTargetIFrame(link)) {
|
|
2159
|
-
const location = this.getLocationForLink(link);
|
|
2160
|
-
if (this.delegate.willFollowLinkToLocation(link, location, event)) {
|
|
2161
|
-
event.preventDefault();
|
|
2162
|
-
this.delegate.followedLinkToLocation(link, location);
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
};
|
|
2167
|
-
this.delegate = delegate;
|
|
2168
|
-
}
|
|
2169
|
-
start() {
|
|
2170
|
-
if (!this.started) {
|
|
2171
|
-
addEventListener("click", this.clickCaptured, true);
|
|
2172
|
-
this.started = true;
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
stop() {
|
|
2176
|
-
if (this.started) {
|
|
2177
|
-
removeEventListener("click", this.clickCaptured, true);
|
|
2178
|
-
this.started = false;
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
clickEventIsSignificant(event) {
|
|
2182
|
-
return !((event.target && event.target.isContentEditable) ||
|
|
2183
|
-
event.defaultPrevented ||
|
|
2184
|
-
event.which > 1 ||
|
|
2185
|
-
event.altKey ||
|
|
2186
|
-
event.ctrlKey ||
|
|
2187
|
-
event.metaKey ||
|
|
2188
|
-
event.shiftKey);
|
|
2189
|
-
}
|
|
2190
|
-
findLinkFromClickTarget(target) {
|
|
2191
|
-
if (target instanceof Element) {
|
|
2192
|
-
return target.closest("a[href]:not([target^=_]):not([download])");
|
|
2193
|
-
}
|
|
2194
|
-
}
|
|
2195
|
-
getLocationForLink(link) {
|
|
2196
|
-
return expandURL(link.getAttribute("href") || "");
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
function doesNotTargetIFrame(anchor) {
|
|
2200
|
-
for (const element of document.getElementsByName(anchor.target)) {
|
|
2201
|
-
if (element instanceof HTMLIFrameElement)
|
|
2202
|
-
return false;
|
|
2203
|
-
}
|
|
2204
|
-
return true;
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
|
-
function isAction(action) {
|
|
2208
|
-
return action == "advance" || action == "replace" || action == "restore";
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
2206
|
class Navigator {
|
|
2212
2207
|
constructor(delegate) {
|
|
2213
2208
|
this.delegate = delegate;
|
|
@@ -2215,18 +2210,23 @@ class Navigator {
|
|
|
2215
2210
|
proposeVisit(location, options = {}) {
|
|
2216
2211
|
if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
|
|
2217
2212
|
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
|
|
2218
|
-
this.delegate.visitProposedToLocation(location, options);
|
|
2213
|
+
return this.delegate.visitProposedToLocation(location, options);
|
|
2219
2214
|
}
|
|
2220
2215
|
else {
|
|
2221
2216
|
window.location.href = location.toString();
|
|
2217
|
+
return Promise.resolve();
|
|
2222
2218
|
}
|
|
2223
2219
|
}
|
|
2220
|
+
else {
|
|
2221
|
+
return Promise.reject();
|
|
2222
|
+
}
|
|
2224
2223
|
}
|
|
2225
2224
|
startVisit(locatable, restorationIdentifier, options = {}) {
|
|
2226
2225
|
this.lastVisit = this.currentVisit;
|
|
2227
2226
|
this.stop();
|
|
2228
2227
|
this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
|
|
2229
2228
|
this.currentVisit.start();
|
|
2229
|
+
return this.currentVisit.promise;
|
|
2230
2230
|
}
|
|
2231
2231
|
submitForm(form, submitter) {
|
|
2232
2232
|
this.stop();
|
|
@@ -2261,13 +2261,15 @@ class Navigator {
|
|
|
2261
2261
|
if (formSubmission == this.formSubmission) {
|
|
2262
2262
|
const responseHTML = await fetchResponse.responseHTML;
|
|
2263
2263
|
if (responseHTML) {
|
|
2264
|
-
|
|
2264
|
+
const shouldCacheSnapshot = formSubmission.method == FetchMethod.get;
|
|
2265
|
+
if (!shouldCacheSnapshot) {
|
|
2265
2266
|
this.view.clearSnapshotCache();
|
|
2266
2267
|
}
|
|
2267
2268
|
const { statusCode, redirected } = fetchResponse;
|
|
2268
2269
|
const action = this.getActionForFormSubmission(formSubmission);
|
|
2269
2270
|
const visitOptions = {
|
|
2270
2271
|
action,
|
|
2272
|
+
shouldCacheSnapshot,
|
|
2271
2273
|
response: { statusCode, responseHTML, redirected },
|
|
2272
2274
|
};
|
|
2273
2275
|
this.proposeVisit(fetchResponse.location, visitOptions);
|
|
@@ -2466,7 +2468,7 @@ class StreamObserver {
|
|
|
2466
2468
|
}
|
|
2467
2469
|
}
|
|
2468
2470
|
receiveMessageHTML(html) {
|
|
2469
|
-
this.delegate.receivedMessageFromStream(
|
|
2471
|
+
this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
|
|
2470
2472
|
}
|
|
2471
2473
|
}
|
|
2472
2474
|
function fetchResponseFromEvent(event) {
|
|
@@ -2500,7 +2502,7 @@ class ErrorRenderer extends Renderer {
|
|
|
2500
2502
|
for (const replaceableElement of this.scriptElements) {
|
|
2501
2503
|
const parentNode = replaceableElement.parentNode;
|
|
2502
2504
|
if (parentNode) {
|
|
2503
|
-
const element =
|
|
2505
|
+
const element = activateScriptElement(replaceableElement);
|
|
2504
2506
|
parentNode.replaceChild(element, replaceableElement);
|
|
2505
2507
|
}
|
|
2506
2508
|
}
|
|
@@ -2509,7 +2511,7 @@ class ErrorRenderer extends Renderer {
|
|
|
2509
2511
|
return this.newSnapshot.headSnapshot.element;
|
|
2510
2512
|
}
|
|
2511
2513
|
get scriptElements() {
|
|
2512
|
-
return
|
|
2514
|
+
return document.documentElement.querySelectorAll("script");
|
|
2513
2515
|
}
|
|
2514
2516
|
}
|
|
2515
2517
|
|
|
@@ -2537,8 +2539,8 @@ class PageRenderer extends Renderer {
|
|
|
2537
2539
|
};
|
|
2538
2540
|
}
|
|
2539
2541
|
}
|
|
2540
|
-
prepareToRender() {
|
|
2541
|
-
this.mergeHead();
|
|
2542
|
+
async prepareToRender() {
|
|
2543
|
+
await this.mergeHead();
|
|
2542
2544
|
}
|
|
2543
2545
|
async render() {
|
|
2544
2546
|
if (this.willRender) {
|
|
@@ -2560,11 +2562,12 @@ class PageRenderer extends Renderer {
|
|
|
2560
2562
|
get newElement() {
|
|
2561
2563
|
return this.newSnapshot.element;
|
|
2562
2564
|
}
|
|
2563
|
-
mergeHead() {
|
|
2564
|
-
this.copyNewHeadStylesheetElements();
|
|
2565
|
+
async mergeHead() {
|
|
2566
|
+
const newStylesheetElements = this.copyNewHeadStylesheetElements();
|
|
2565
2567
|
this.copyNewHeadScriptElements();
|
|
2566
2568
|
this.removeCurrentHeadProvisionalElements();
|
|
2567
2569
|
this.copyNewHeadProvisionalElements();
|
|
2570
|
+
await newStylesheetElements;
|
|
2568
2571
|
}
|
|
2569
2572
|
replaceBody() {
|
|
2570
2573
|
this.preservingPermanentElements(() => {
|
|
@@ -2575,14 +2578,17 @@ class PageRenderer extends Renderer {
|
|
|
2575
2578
|
get trackedElementsAreIdentical() {
|
|
2576
2579
|
return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
|
|
2577
2580
|
}
|
|
2578
|
-
copyNewHeadStylesheetElements() {
|
|
2581
|
+
async copyNewHeadStylesheetElements() {
|
|
2582
|
+
const loadingElements = [];
|
|
2579
2583
|
for (const element of this.newHeadStylesheetElements) {
|
|
2584
|
+
loadingElements.push(waitForLoad(element));
|
|
2580
2585
|
document.head.appendChild(element);
|
|
2581
2586
|
}
|
|
2587
|
+
await Promise.all(loadingElements);
|
|
2582
2588
|
}
|
|
2583
2589
|
copyNewHeadScriptElements() {
|
|
2584
2590
|
for (const element of this.newHeadScriptElements) {
|
|
2585
|
-
document.head.appendChild(
|
|
2591
|
+
document.head.appendChild(activateScriptElement(element));
|
|
2586
2592
|
}
|
|
2587
2593
|
}
|
|
2588
2594
|
removeCurrentHeadProvisionalElements() {
|
|
@@ -2601,7 +2607,7 @@ class PageRenderer extends Renderer {
|
|
|
2601
2607
|
}
|
|
2602
2608
|
activateNewBodyScriptElements() {
|
|
2603
2609
|
for (const inertScriptElement of this.newBodyScriptElements) {
|
|
2604
|
-
const activatedScriptElement =
|
|
2610
|
+
const activatedScriptElement = activateScriptElement(inertScriptElement);
|
|
2605
2611
|
inertScriptElement.replaceWith(activatedScriptElement);
|
|
2606
2612
|
}
|
|
2607
2613
|
}
|
|
@@ -2764,12 +2770,12 @@ class Session {
|
|
|
2764
2770
|
this.adapter = new BrowserAdapter(this);
|
|
2765
2771
|
this.pageObserver = new PageObserver(this);
|
|
2766
2772
|
this.cacheObserver = new CacheObserver();
|
|
2767
|
-
this.linkClickObserver = new LinkClickObserver(this);
|
|
2768
|
-
this.formSubmitObserver = new FormSubmitObserver(this);
|
|
2773
|
+
this.linkClickObserver = new LinkClickObserver(this, window);
|
|
2774
|
+
this.formSubmitObserver = new FormSubmitObserver(this, document);
|
|
2769
2775
|
this.scrollObserver = new ScrollObserver(this);
|
|
2770
2776
|
this.streamObserver = new StreamObserver(this);
|
|
2771
|
-
this.
|
|
2772
|
-
this.frameRedirector = new FrameRedirector(document.documentElement);
|
|
2777
|
+
this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
|
|
2778
|
+
this.frameRedirector = new FrameRedirector(this, document.documentElement);
|
|
2773
2779
|
this.drive = true;
|
|
2774
2780
|
this.enabled = true;
|
|
2775
2781
|
this.progressBarDelay = 500;
|
|
@@ -2780,7 +2786,7 @@ class Session {
|
|
|
2780
2786
|
if (!this.started) {
|
|
2781
2787
|
this.pageObserver.start();
|
|
2782
2788
|
this.cacheObserver.start();
|
|
2783
|
-
this.
|
|
2789
|
+
this.formLinkClickObserver.start();
|
|
2784
2790
|
this.linkClickObserver.start();
|
|
2785
2791
|
this.formSubmitObserver.start();
|
|
2786
2792
|
this.scrollObserver.start();
|
|
@@ -2799,7 +2805,7 @@ class Session {
|
|
|
2799
2805
|
if (this.started) {
|
|
2800
2806
|
this.pageObserver.stop();
|
|
2801
2807
|
this.cacheObserver.stop();
|
|
2802
|
-
this.
|
|
2808
|
+
this.formLinkClickObserver.stop();
|
|
2803
2809
|
this.linkClickObserver.stop();
|
|
2804
2810
|
this.formSubmitObserver.stop();
|
|
2805
2811
|
this.scrollObserver.stop();
|
|
@@ -2813,7 +2819,14 @@ class Session {
|
|
|
2813
2819
|
this.adapter = adapter;
|
|
2814
2820
|
}
|
|
2815
2821
|
visit(location, options = {}) {
|
|
2816
|
-
|
|
2822
|
+
const frameElement = document.getElementById(options.frame || "");
|
|
2823
|
+
if (frameElement instanceof FrameElement) {
|
|
2824
|
+
frameElement.src = location.toString();
|
|
2825
|
+
return frameElement.loaded;
|
|
2826
|
+
}
|
|
2827
|
+
else {
|
|
2828
|
+
return this.navigator.proposeVisit(expandURL(location), options);
|
|
2829
|
+
}
|
|
2817
2830
|
}
|
|
2818
2831
|
connectStreamSource(source) {
|
|
2819
2832
|
this.streamObserver.connectStreamSource(source);
|
|
@@ -2855,25 +2868,26 @@ class Session {
|
|
|
2855
2868
|
scrollPositionChanged(position) {
|
|
2856
2869
|
this.history.updateRestorationData({ scrollPosition: position });
|
|
2857
2870
|
}
|
|
2858
|
-
|
|
2859
|
-
return
|
|
2871
|
+
willSubmitFormLinkToLocation(link, location) {
|
|
2872
|
+
return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
|
|
2860
2873
|
}
|
|
2861
|
-
|
|
2874
|
+
submittedFormLinkToLocation() { }
|
|
2862
2875
|
willFollowLinkToLocation(link, location, event) {
|
|
2863
|
-
return (this.
|
|
2876
|
+
return (this.elementIsNavigatable(link) &&
|
|
2864
2877
|
locationIsVisitable(location, this.snapshot.rootLocation) &&
|
|
2865
2878
|
this.applicationAllowsFollowingLinkToLocation(link, location, event));
|
|
2866
2879
|
}
|
|
2867
2880
|
followedLinkToLocation(link, location) {
|
|
2868
2881
|
const action = this.getActionForLink(link);
|
|
2869
|
-
|
|
2882
|
+
const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
|
|
2883
|
+
this.visit(location.href, { action, acceptsStreamResponse });
|
|
2870
2884
|
}
|
|
2871
2885
|
allowsVisitingLocationWithAction(location, action) {
|
|
2872
2886
|
return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
|
|
2873
2887
|
}
|
|
2874
2888
|
visitProposedToLocation(location, options) {
|
|
2875
2889
|
extendURLWithDeprecatedProperties(location);
|
|
2876
|
-
this.adapter.visitProposedToLocation(location, options);
|
|
2890
|
+
return this.adapter.visitProposedToLocation(location, options);
|
|
2877
2891
|
}
|
|
2878
2892
|
visitStarted(visit) {
|
|
2879
2893
|
extendURLWithDeprecatedProperties(visit.location);
|
|
@@ -2892,8 +2906,7 @@ class Session {
|
|
|
2892
2906
|
}
|
|
2893
2907
|
willSubmitForm(form, submitter) {
|
|
2894
2908
|
const action = getAction(form, submitter);
|
|
2895
|
-
return (this.
|
|
2896
|
-
(!submitter || this.formElementDriveEnabled(submitter)) &&
|
|
2909
|
+
return (this.submissionIsNavigatable(form, submitter) &&
|
|
2897
2910
|
locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
|
|
2898
2911
|
}
|
|
2899
2912
|
formSubmitted(form, submitter) {
|
|
@@ -2942,6 +2955,10 @@ class Session {
|
|
|
2942
2955
|
frameRendered(fetchResponse, frame) {
|
|
2943
2956
|
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
|
2944
2957
|
}
|
|
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
|
+
}
|
|
2945
2962
|
applicationAllowsFollowingLinkToLocation(link, location, ev) {
|
|
2946
2963
|
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
|
|
2947
2964
|
return !event.defaultPrevented;
|
|
@@ -3001,19 +3018,24 @@ class Session {
|
|
|
3001
3018
|
cancelable: true,
|
|
3002
3019
|
});
|
|
3003
3020
|
}
|
|
3004
|
-
|
|
3021
|
+
submissionIsNavigatable(form, submitter) {
|
|
3005
3022
|
if (this.formMode == "off") {
|
|
3006
3023
|
return false;
|
|
3007
3024
|
}
|
|
3008
|
-
|
|
3009
|
-
const
|
|
3010
|
-
|
|
3025
|
+
else {
|
|
3026
|
+
const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
|
|
3027
|
+
if (this.formMode == "optin") {
|
|
3028
|
+
return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
|
|
3029
|
+
}
|
|
3030
|
+
else {
|
|
3031
|
+
return submitterIsNavigatable && this.elementIsNavigatable(form);
|
|
3032
|
+
}
|
|
3011
3033
|
}
|
|
3012
|
-
return this.elementDriveEnabled(element);
|
|
3013
3034
|
}
|
|
3014
|
-
|
|
3015
|
-
const container = element
|
|
3016
|
-
|
|
3035
|
+
elementIsNavigatable(element) {
|
|
3036
|
+
const container = element.closest("[data-turbo]");
|
|
3037
|
+
const withinFrame = element.closest("turbo-frame");
|
|
3038
|
+
if (this.drive || withinFrame) {
|
|
3017
3039
|
if (container) {
|
|
3018
3040
|
return container.getAttribute("data-turbo") != "false";
|
|
3019
3041
|
}
|
|
@@ -3106,7 +3128,7 @@ function registerAdapter(adapter) {
|
|
|
3106
3128
|
session.registerAdapter(adapter);
|
|
3107
3129
|
}
|
|
3108
3130
|
function visit(location, options) {
|
|
3109
|
-
session.visit(location, options);
|
|
3131
|
+
return session.visit(location, options);
|
|
3110
3132
|
}
|
|
3111
3133
|
function connectStreamSource(source) {
|
|
3112
3134
|
session.connectStreamSource(source);
|
|
@@ -3160,6 +3182,7 @@ class FrameController {
|
|
|
3160
3182
|
this.connected = false;
|
|
3161
3183
|
this.hasBeenLoaded = false;
|
|
3162
3184
|
this.ignoredAttributes = new Set();
|
|
3185
|
+
this.action = null;
|
|
3163
3186
|
this.visitCachedSnapshot = ({ element }) => {
|
|
3164
3187
|
const frame = element.querySelector("#" + this.element.id);
|
|
3165
3188
|
if (frame && this.previousFrameElement) {
|
|
@@ -3170,9 +3193,10 @@ class FrameController {
|
|
|
3170
3193
|
this.element = element;
|
|
3171
3194
|
this.view = new FrameView(this, this.element);
|
|
3172
3195
|
this.appearanceObserver = new AppearanceObserver(this, this.element);
|
|
3173
|
-
this.
|
|
3174
|
-
this.
|
|
3175
|
-
this.
|
|
3196
|
+
this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
|
|
3197
|
+
this.linkClickObserver = new LinkClickObserver(this, this.element);
|
|
3198
|
+
this.restorationIdentifier = uuid();
|
|
3199
|
+
this.formSubmitObserver = new FormSubmitObserver(this, this.element);
|
|
3176
3200
|
}
|
|
3177
3201
|
connect() {
|
|
3178
3202
|
if (!this.connected) {
|
|
@@ -3183,18 +3207,18 @@ class FrameController {
|
|
|
3183
3207
|
else {
|
|
3184
3208
|
this.loadSourceURL();
|
|
3185
3209
|
}
|
|
3186
|
-
this.
|
|
3187
|
-
this.
|
|
3188
|
-
this.
|
|
3210
|
+
this.formLinkClickObserver.start();
|
|
3211
|
+
this.linkClickObserver.start();
|
|
3212
|
+
this.formSubmitObserver.start();
|
|
3189
3213
|
}
|
|
3190
3214
|
}
|
|
3191
3215
|
disconnect() {
|
|
3192
3216
|
if (this.connected) {
|
|
3193
3217
|
this.connected = false;
|
|
3194
3218
|
this.appearanceObserver.stop();
|
|
3195
|
-
this.
|
|
3196
|
-
this.
|
|
3197
|
-
this.
|
|
3219
|
+
this.formLinkClickObserver.stop();
|
|
3220
|
+
this.linkClickObserver.stop();
|
|
3221
|
+
this.formSubmitObserver.stop();
|
|
3198
3222
|
}
|
|
3199
3223
|
}
|
|
3200
3224
|
disabledChanged() {
|
|
@@ -3242,15 +3266,22 @@ class FrameController {
|
|
|
3242
3266
|
const html = await fetchResponse.responseHTML;
|
|
3243
3267
|
if (html) {
|
|
3244
3268
|
const { body } = parseHTMLDocument(html);
|
|
3245
|
-
const
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3269
|
+
const newFrameElement = await this.extractForeignFrameElement(body);
|
|
3270
|
+
if (newFrameElement) {
|
|
3271
|
+
const snapshot = new Snapshot(newFrameElement);
|
|
3272
|
+
const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
|
|
3273
|
+
if (this.view.renderPromise)
|
|
3274
|
+
await this.view.renderPromise;
|
|
3275
|
+
this.changeHistory();
|
|
3276
|
+
await this.view.render(renderer);
|
|
3277
|
+
this.complete = true;
|
|
3278
|
+
session.frameRendered(fetchResponse, this.element);
|
|
3279
|
+
session.frameLoaded(this.element);
|
|
3280
|
+
this.fetchResponseLoaded(fetchResponse);
|
|
3281
|
+
}
|
|
3282
|
+
else if (this.sessionWillHandleMissingFrame(fetchResponse)) {
|
|
3283
|
+
await session.frameMissing(this.element, fetchResponse);
|
|
3284
|
+
}
|
|
3254
3285
|
}
|
|
3255
3286
|
}
|
|
3256
3287
|
catch (error) {
|
|
@@ -3264,24 +3295,24 @@ class FrameController {
|
|
|
3264
3295
|
elementAppearedInViewport(_element) {
|
|
3265
3296
|
this.loadSourceURL();
|
|
3266
3297
|
}
|
|
3267
|
-
|
|
3268
|
-
return this.shouldInterceptNavigation(link);
|
|
3298
|
+
willSubmitFormLinkToLocation(link) {
|
|
3299
|
+
return link.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(link);
|
|
3269
3300
|
}
|
|
3270
|
-
|
|
3301
|
+
submittedFormLinkToLocation(link, _location, form) {
|
|
3271
3302
|
const frame = this.findFrameElement(link);
|
|
3272
3303
|
if (frame)
|
|
3273
3304
|
form.setAttribute("data-turbo-frame", frame.id);
|
|
3274
3305
|
}
|
|
3275
|
-
|
|
3306
|
+
willFollowLinkToLocation(element) {
|
|
3276
3307
|
return this.shouldInterceptNavigation(element);
|
|
3277
3308
|
}
|
|
3278
|
-
|
|
3279
|
-
this.navigateFrame(element,
|
|
3309
|
+
followedLinkToLocation(element, location) {
|
|
3310
|
+
this.navigateFrame(element, location.href);
|
|
3280
3311
|
}
|
|
3281
|
-
|
|
3282
|
-
return this.shouldInterceptNavigation(element, submitter);
|
|
3312
|
+
willSubmitForm(element, submitter) {
|
|
3313
|
+
return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
|
|
3283
3314
|
}
|
|
3284
|
-
|
|
3315
|
+
formSubmitted(element, submitter) {
|
|
3285
3316
|
if (this.formSubmission) {
|
|
3286
3317
|
this.formSubmission.stop();
|
|
3287
3318
|
}
|
|
@@ -3290,8 +3321,12 @@ class FrameController {
|
|
|
3290
3321
|
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
|
3291
3322
|
this.formSubmission.start();
|
|
3292
3323
|
}
|
|
3293
|
-
prepareHeadersForRequest(headers,
|
|
3324
|
+
prepareHeadersForRequest(headers, request) {
|
|
3325
|
+
var _a;
|
|
3294
3326
|
headers["Turbo-Frame"] = this.id;
|
|
3327
|
+
if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
|
|
3328
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
3329
|
+
}
|
|
3295
3330
|
}
|
|
3296
3331
|
requestStarted(_request) {
|
|
3297
3332
|
markAsBusy(this.element);
|
|
@@ -3309,6 +3344,10 @@ class FrameController {
|
|
|
3309
3344
|
}
|
|
3310
3345
|
requestErrored(request, error) {
|
|
3311
3346
|
console.error(error);
|
|
3347
|
+
dispatch("turbo:fetch-request-error", {
|
|
3348
|
+
target: this.element,
|
|
3349
|
+
detail: { request, error },
|
|
3350
|
+
});
|
|
3312
3351
|
this.resolveVisitPromise();
|
|
3313
3352
|
}
|
|
3314
3353
|
requestFinished(_request) {
|
|
@@ -3348,8 +3387,8 @@ class FrameController {
|
|
|
3348
3387
|
session.preloadOnLoadLinksForView(element);
|
|
3349
3388
|
}
|
|
3350
3389
|
viewInvalidated() { }
|
|
3351
|
-
|
|
3352
|
-
this.previousFrameElement =
|
|
3390
|
+
willRenderFrame(currentElement, _newElement) {
|
|
3391
|
+
this.previousFrameElement = currentElement.cloneNode(true);
|
|
3353
3392
|
}
|
|
3354
3393
|
async visit(url) {
|
|
3355
3394
|
var _a;
|
|
@@ -3368,27 +3407,49 @@ class FrameController {
|
|
|
3368
3407
|
navigateFrame(element, url, submitter) {
|
|
3369
3408
|
const frame = this.findFrameElement(element, submitter);
|
|
3370
3409
|
this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
|
|
3371
|
-
|
|
3410
|
+
this.withCurrentNavigationElement(element, () => {
|
|
3411
|
+
frame.src = url;
|
|
3412
|
+
});
|
|
3372
3413
|
}
|
|
3373
3414
|
proposeVisitIfNavigatedWithAction(frame, element, submitter) {
|
|
3374
|
-
|
|
3375
|
-
|
|
3415
|
+
this.action = getVisitAction(submitter, element, frame);
|
|
3416
|
+
this.frame = frame;
|
|
3417
|
+
if (isAction(this.action)) {
|
|
3376
3418
|
const { visitCachedSnapshot } = frame.delegate;
|
|
3377
3419
|
frame.delegate.fetchResponseLoaded = (fetchResponse) => {
|
|
3378
3420
|
if (frame.src) {
|
|
3379
3421
|
const { statusCode, redirected } = fetchResponse;
|
|
3380
3422
|
const responseHTML = frame.ownerDocument.documentElement.outerHTML;
|
|
3381
3423
|
const response = { statusCode, redirected, responseHTML };
|
|
3382
|
-
|
|
3383
|
-
action,
|
|
3424
|
+
const options = {
|
|
3384
3425
|
response,
|
|
3385
3426
|
visitCachedSnapshot,
|
|
3386
3427
|
willRender: false,
|
|
3387
|
-
|
|
3428
|
+
updateHistory: false,
|
|
3429
|
+
restorationIdentifier: this.restorationIdentifier,
|
|
3430
|
+
};
|
|
3431
|
+
if (this.action)
|
|
3432
|
+
options.action = this.action;
|
|
3433
|
+
session.visit(frame.src, options);
|
|
3388
3434
|
}
|
|
3389
3435
|
};
|
|
3390
3436
|
}
|
|
3391
3437
|
}
|
|
3438
|
+
changeHistory() {
|
|
3439
|
+
if (this.action && this.frame) {
|
|
3440
|
+
const method = getHistoryMethodForAction(this.action);
|
|
3441
|
+
session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
sessionWillHandleMissingFrame(fetchResponse) {
|
|
3445
|
+
this.element.setAttribute("complete", "");
|
|
3446
|
+
const event = dispatch("turbo:frame-missing", {
|
|
3447
|
+
target: this.element,
|
|
3448
|
+
detail: { fetchResponse },
|
|
3449
|
+
cancelable: true,
|
|
3450
|
+
});
|
|
3451
|
+
return !event.defaultPrevented;
|
|
3452
|
+
}
|
|
3392
3453
|
findFrameElement(element, submitter) {
|
|
3393
3454
|
var _a;
|
|
3394
3455
|
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
@@ -3407,12 +3468,12 @@ class FrameController {
|
|
|
3407
3468
|
await element.loaded;
|
|
3408
3469
|
return await this.extractForeignFrameElement(element);
|
|
3409
3470
|
}
|
|
3410
|
-
console.error(`Response has no matching <turbo-frame id="${id}"> element`);
|
|
3411
3471
|
}
|
|
3412
3472
|
catch (error) {
|
|
3413
3473
|
console.error(error);
|
|
3474
|
+
return new FrameElement();
|
|
3414
3475
|
}
|
|
3415
|
-
return
|
|
3476
|
+
return null;
|
|
3416
3477
|
}
|
|
3417
3478
|
formActionIsVisitable(form, submitter) {
|
|
3418
3479
|
const action = getAction(form, submitter);
|
|
@@ -3432,10 +3493,10 @@ class FrameController {
|
|
|
3432
3493
|
return !frameElement.disabled;
|
|
3433
3494
|
}
|
|
3434
3495
|
}
|
|
3435
|
-
if (!session.
|
|
3496
|
+
if (!session.elementIsNavigatable(element)) {
|
|
3436
3497
|
return false;
|
|
3437
3498
|
}
|
|
3438
|
-
if (submitter && !session.
|
|
3499
|
+
if (submitter && !session.elementIsNavigatable(submitter)) {
|
|
3439
3500
|
return false;
|
|
3440
3501
|
}
|
|
3441
3502
|
return true;
|
|
@@ -3492,6 +3553,11 @@ class FrameController {
|
|
|
3492
3553
|
callback();
|
|
3493
3554
|
this.ignoredAttributes.delete(attributeName);
|
|
3494
3555
|
}
|
|
3556
|
+
withCurrentNavigationElement(element, callback) {
|
|
3557
|
+
this.currentNavigationElement = element;
|
|
3558
|
+
callback();
|
|
3559
|
+
delete this.currentNavigationElement;
|
|
3560
|
+
}
|
|
3495
3561
|
}
|
|
3496
3562
|
function getFrameElementById(id) {
|
|
3497
3563
|
if (id != null) {
|
|
@@ -3604,6 +3670,7 @@ class StreamElement extends HTMLElement {
|
|
|
3604
3670
|
return new CustomEvent("turbo:before-stream-render", {
|
|
3605
3671
|
bubbles: true,
|
|
3606
3672
|
cancelable: true,
|
|
3673
|
+
detail: { newStream: this },
|
|
3607
3674
|
});
|
|
3608
3675
|
}
|
|
3609
3676
|
get targetElementsById() {
|