turbo-rails 0.5.9 → 0.7.14
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.
- checksums.yaml +4 -4
- data/README.md +19 -14
- data/Rakefile +4 -0
- data/app/assets/javascripts/turbo.js +1230 -807
- data/app/channels/turbo/streams/broadcasts.rb +43 -21
- data/app/helpers/turbo/frames_helper.rb +4 -0
- data/app/helpers/turbo/streams/action_helper.rb +17 -5
- data/app/helpers/turbo/streams_helper.rb +5 -3
- data/app/javascript/turbo/cable.js +6 -3
- data/app/jobs/turbo/streams/action_broadcast_job.rb +2 -0
- data/app/jobs/turbo/streams/broadcast_job.rb +2 -0
- data/app/models/concerns/turbo/broadcastable.rb +79 -16
- data/app/models/turbo/streams/tag_builder.rb +136 -17
- data/lib/install/turbo_needs_redis.rb +9 -0
- data/lib/install/turbo_with_importmap.rb +5 -0
- data/lib/install/turbo_with_node.rb +9 -0
- data/lib/tasks/turbo_tasks.rake +31 -8
- data/lib/turbo/engine.rb +6 -2
- data/lib/turbo/test_assertions.rb +2 -3
- data/lib/turbo/version.rb +1 -1
- metadata +6 -5
- data/lib/install/turbo_with_asset_pipeline.rb +0 -36
- data/lib/install/turbo_with_webpacker.rb +0 -23
@@ -31,6 +31,7 @@ function clickCaptured(event) {
|
|
31
31
|
|
32
32
|
(function() {
|
33
33
|
if ("SubmitEvent" in window) return;
|
34
|
+
if ("submitter" in Event.prototype) return;
|
34
35
|
addEventListener("click", clickCaptured, true);
|
35
36
|
Object.defineProperty(Event.prototype, "submitter", {
|
36
37
|
get() {
|
@@ -55,7 +56,7 @@ class FrameElement extends HTMLElement {
|
|
55
56
|
this.delegate = new FrameElement.delegateConstructor(this);
|
56
57
|
}
|
57
58
|
static get observedAttributes() {
|
58
|
-
return [ "loading", "src" ];
|
59
|
+
return [ "disabled", "loading", "src" ];
|
59
60
|
}
|
60
61
|
connectedCallback() {
|
61
62
|
this.delegate.connect();
|
@@ -63,11 +64,18 @@ class FrameElement extends HTMLElement {
|
|
63
64
|
disconnectedCallback() {
|
64
65
|
this.delegate.disconnect();
|
65
66
|
}
|
67
|
+
reload() {
|
68
|
+
const {src: src} = this;
|
69
|
+
this.src = null;
|
70
|
+
this.src = src;
|
71
|
+
}
|
66
72
|
attributeChangedCallback(name) {
|
67
73
|
if (name == "loading") {
|
68
74
|
this.delegate.loadingStyleChanged();
|
69
75
|
} else if (name == "src") {
|
70
76
|
this.delegate.sourceURLChanged();
|
77
|
+
} else {
|
78
|
+
this.delegate.disabledChanged();
|
71
79
|
}
|
72
80
|
}
|
73
81
|
get src() {
|
@@ -133,9 +141,7 @@ function frameLoadingStyleFromString(style) {
|
|
133
141
|
}
|
134
142
|
|
135
143
|
function expandURL(locatable) {
|
136
|
-
|
137
|
-
anchor.href = locatable.toString();
|
138
|
-
return new URL(anchor.href);
|
144
|
+
return new URL(locatable.toString(), document.baseURI);
|
139
145
|
}
|
140
146
|
|
141
147
|
function getAnchor(url) {
|
@@ -144,8 +150,6 @@ function getAnchor(url) {
|
|
144
150
|
return url.hash.slice(1);
|
145
151
|
} else if (anchorMatch = url.href.match(/#(.*)$/)) {
|
146
152
|
return anchorMatch[1];
|
147
|
-
} else {
|
148
|
-
return "";
|
149
153
|
}
|
150
154
|
}
|
151
155
|
|
@@ -162,13 +166,17 @@ function isPrefixedBy(baseURL, url) {
|
|
162
166
|
return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
|
163
167
|
}
|
164
168
|
|
169
|
+
function getRequestURL(url) {
|
170
|
+
const anchor = getAnchor(url);
|
171
|
+
return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
|
172
|
+
}
|
173
|
+
|
165
174
|
function toCacheKey(url) {
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
}
|
175
|
+
return getRequestURL(url);
|
176
|
+
}
|
177
|
+
|
178
|
+
function urlsAreEqual(left, right) {
|
179
|
+
return expandURL(left).href == expandURL(right).href;
|
172
180
|
}
|
173
181
|
|
174
182
|
function getPathComponents(url) {
|
@@ -219,11 +227,11 @@ class FetchResponse {
|
|
219
227
|
return this.header("Content-Type");
|
220
228
|
}
|
221
229
|
get responseText() {
|
222
|
-
return this.response.text();
|
230
|
+
return this.response.clone().text();
|
223
231
|
}
|
224
232
|
get responseHTML() {
|
225
233
|
if (this.isHTML) {
|
226
|
-
return this.response.text();
|
234
|
+
return this.response.clone().text();
|
227
235
|
} else {
|
228
236
|
return Promise.resolve(undefined);
|
229
237
|
}
|
@@ -319,16 +327,19 @@ function fetchMethodFromString(method) {
|
|
319
327
|
}
|
320
328
|
|
321
329
|
class FetchRequest {
|
322
|
-
constructor(delegate, method, location, body = new URLSearchParams) {
|
330
|
+
constructor(delegate, method, location, body = new URLSearchParams, target = null) {
|
323
331
|
this.abortController = new AbortController;
|
332
|
+
this.resolveRequestPromise = value => {};
|
324
333
|
this.delegate = delegate;
|
325
334
|
this.method = method;
|
335
|
+
this.headers = this.defaultHeaders;
|
326
336
|
if (this.isIdempotent) {
|
327
337
|
this.url = mergeFormDataEntries(location, [ ...body.entries() ]);
|
328
338
|
} else {
|
329
339
|
this.body = body;
|
330
340
|
this.url = location;
|
331
341
|
}
|
342
|
+
this.target = target;
|
332
343
|
}
|
333
344
|
get location() {
|
334
345
|
return this.url;
|
@@ -343,19 +354,19 @@ class FetchRequest {
|
|
343
354
|
this.abortController.abort();
|
344
355
|
}
|
345
356
|
async perform() {
|
357
|
+
var _a, _b;
|
346
358
|
const {fetchOptions: fetchOptions} = this;
|
347
|
-
|
348
|
-
|
349
|
-
fetchOptions: fetchOptions
|
350
|
-
}
|
351
|
-
});
|
359
|
+
(_b = (_a = this.delegate).prepareHeadersForRequest) === null || _b === void 0 ? void 0 : _b.call(_a, this.headers, this);
|
360
|
+
await this.allowRequestToBeIntercepted(fetchOptions);
|
352
361
|
try {
|
353
362
|
this.delegate.requestStarted(this);
|
354
363
|
const response = await fetch(this.url.href, fetchOptions);
|
355
364
|
return await this.receive(response);
|
356
365
|
} catch (error) {
|
357
|
-
|
358
|
-
|
366
|
+
if (error.name !== "AbortError") {
|
367
|
+
this.delegate.requestErrored(this, error);
|
368
|
+
throw error;
|
369
|
+
}
|
359
370
|
} finally {
|
360
371
|
this.delegate.requestFinished(this);
|
361
372
|
}
|
@@ -366,7 +377,8 @@ class FetchRequest {
|
|
366
377
|
cancelable: true,
|
367
378
|
detail: {
|
368
379
|
fetchResponse: fetchResponse
|
369
|
-
}
|
380
|
+
},
|
381
|
+
target: this.target
|
370
382
|
});
|
371
383
|
if (event.defaultPrevented) {
|
372
384
|
this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
|
@@ -378,32 +390,40 @@ class FetchRequest {
|
|
378
390
|
return fetchResponse;
|
379
391
|
}
|
380
392
|
get fetchOptions() {
|
393
|
+
var _a;
|
381
394
|
return {
|
382
395
|
method: FetchMethod[this.method].toUpperCase(),
|
383
396
|
credentials: "same-origin",
|
384
397
|
headers: this.headers,
|
385
398
|
redirect: "follow",
|
386
399
|
body: this.body,
|
387
|
-
signal: this.abortSignal
|
400
|
+
signal: this.abortSignal,
|
401
|
+
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
|
402
|
+
};
|
403
|
+
}
|
404
|
+
get defaultHeaders() {
|
405
|
+
return {
|
406
|
+
Accept: "text/html, application/xhtml+xml"
|
388
407
|
};
|
389
408
|
}
|
390
409
|
get isIdempotent() {
|
391
410
|
return this.method == FetchMethod.get;
|
392
411
|
}
|
393
|
-
get headers() {
|
394
|
-
const headers = Object.assign({}, this.defaultHeaders);
|
395
|
-
if (typeof this.delegate.prepareHeadersForRequest == "function") {
|
396
|
-
this.delegate.prepareHeadersForRequest(headers, this);
|
397
|
-
}
|
398
|
-
return headers;
|
399
|
-
}
|
400
412
|
get abortSignal() {
|
401
413
|
return this.abortController.signal;
|
402
414
|
}
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
415
|
+
async allowRequestToBeIntercepted(fetchOptions) {
|
416
|
+
const requestInterception = new Promise((resolve => this.resolveRequestPromise = resolve));
|
417
|
+
const event = dispatch("turbo:before-fetch-request", {
|
418
|
+
cancelable: true,
|
419
|
+
detail: {
|
420
|
+
fetchOptions: fetchOptions,
|
421
|
+
url: this.url.href,
|
422
|
+
resume: this.resolveRequestPromise
|
423
|
+
},
|
424
|
+
target: this.target
|
425
|
+
});
|
426
|
+
if (event.defaultPrevented) await requestInterception;
|
407
427
|
}
|
408
428
|
}
|
409
429
|
|
@@ -522,7 +542,7 @@ class FormSubmission {
|
|
522
542
|
this.formElement = formElement;
|
523
543
|
this.submitter = submitter;
|
524
544
|
this.formData = buildFormData(formElement, submitter);
|
525
|
-
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body);
|
545
|
+
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
526
546
|
this.mustRedirect = mustRedirect;
|
527
547
|
}
|
528
548
|
get method() {
|
@@ -532,7 +552,8 @@ class FormSubmission {
|
|
532
552
|
}
|
533
553
|
get action() {
|
534
554
|
var _a;
|
535
|
-
|
555
|
+
const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
|
556
|
+
return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
|
536
557
|
}
|
537
558
|
get location() {
|
538
559
|
return expandURL(this.action);
|
@@ -548,6 +569,9 @@ class FormSubmission {
|
|
548
569
|
var _a;
|
549
570
|
return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
|
550
571
|
}
|
572
|
+
get isIdempotent() {
|
573
|
+
return this.fetchRequest.isIdempotent;
|
574
|
+
}
|
551
575
|
get stringFormData() {
|
552
576
|
return [ ...this.formData ].reduce(((entries, [name, value]) => entries.concat(typeof value == "string" ? [ [ name, value ] ] : [])), []);
|
553
577
|
}
|
@@ -639,8 +663,8 @@ function buildFormData(formElement, submitter) {
|
|
639
663
|
const formData = new FormData(formElement);
|
640
664
|
const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
|
641
665
|
const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
|
642
|
-
if (name && formData.get(name) != value) {
|
643
|
-
formData.append(name, value
|
666
|
+
if (name && value != null && formData.get(name) != value) {
|
667
|
+
formData.append(name, value);
|
644
668
|
}
|
645
669
|
return formData;
|
646
670
|
}
|
@@ -676,11 +700,10 @@ class Snapshot {
|
|
676
700
|
return this.getElementForAnchor(anchor) != null;
|
677
701
|
}
|
678
702
|
getElementForAnchor(anchor) {
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
}
|
703
|
+
return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
|
704
|
+
}
|
705
|
+
get isConnected() {
|
706
|
+
return this.element.isConnected;
|
684
707
|
}
|
685
708
|
get firstAutofocusableElement() {
|
686
709
|
return this.element.querySelector("[autofocus]");
|
@@ -691,8 +714,16 @@ class Snapshot {
|
|
691
714
|
getPermanentElementById(id) {
|
692
715
|
return this.element.querySelector(`#${id}[data-turbo-permanent]`);
|
693
716
|
}
|
694
|
-
|
695
|
-
|
717
|
+
getPermanentElementMapForSnapshot(snapshot) {
|
718
|
+
const permanentElementMap = {};
|
719
|
+
for (const currentPermanentElement of this.permanentElements) {
|
720
|
+
const {id: id} = currentPermanentElement;
|
721
|
+
const newPermanentElement = snapshot.getPermanentElementById(id);
|
722
|
+
if (newPermanentElement) {
|
723
|
+
permanentElementMap[id] = [ currentPermanentElement, newPermanentElement ];
|
724
|
+
}
|
725
|
+
}
|
726
|
+
return permanentElementMap;
|
696
727
|
}
|
697
728
|
}
|
698
729
|
|
@@ -722,6 +753,8 @@ class FormInterceptor {
|
|
722
753
|
|
723
754
|
class View {
|
724
755
|
constructor(delegate, element) {
|
756
|
+
this.resolveRenderPromise = value => {};
|
757
|
+
this.resolveInterceptionPromise = value => {};
|
725
758
|
this.delegate = delegate;
|
726
759
|
this.element = element;
|
727
760
|
}
|
@@ -729,6 +762,7 @@ class View {
|
|
729
762
|
const element = this.snapshot.getElementForAnchor(anchor);
|
730
763
|
if (element) {
|
731
764
|
this.scrollToElement(element);
|
765
|
+
this.focusElement(element);
|
732
766
|
} else {
|
733
767
|
this.scrollToPosition({
|
734
768
|
x: 0,
|
@@ -736,30 +770,52 @@ class View {
|
|
736
770
|
});
|
737
771
|
}
|
738
772
|
}
|
773
|
+
scrollToAnchorFromLocation(location) {
|
774
|
+
this.scrollToAnchor(getAnchor(location));
|
775
|
+
}
|
739
776
|
scrollToElement(element) {
|
740
777
|
element.scrollIntoView();
|
741
778
|
}
|
779
|
+
focusElement(element) {
|
780
|
+
if (element instanceof HTMLElement) {
|
781
|
+
if (element.hasAttribute("tabindex")) {
|
782
|
+
element.focus();
|
783
|
+
} else {
|
784
|
+
element.setAttribute("tabindex", "-1");
|
785
|
+
element.focus();
|
786
|
+
element.removeAttribute("tabindex");
|
787
|
+
}
|
788
|
+
}
|
789
|
+
}
|
742
790
|
scrollToPosition({x: x, y: y}) {
|
743
791
|
this.scrollRoot.scrollTo(x, y);
|
744
792
|
}
|
793
|
+
scrollToTop() {
|
794
|
+
this.scrollToPosition({
|
795
|
+
x: 0,
|
796
|
+
y: 0
|
797
|
+
});
|
798
|
+
}
|
745
799
|
get scrollRoot() {
|
746
800
|
return window;
|
747
801
|
}
|
748
802
|
async render(renderer) {
|
749
|
-
if (this.renderer) {
|
750
|
-
throw new Error("rendering is already in progress");
|
751
|
-
}
|
752
803
|
const {isPreview: isPreview, shouldRender: shouldRender, newSnapshot: snapshot} = renderer;
|
753
804
|
if (shouldRender) {
|
754
805
|
try {
|
806
|
+
this.renderPromise = new Promise((resolve => this.resolveRenderPromise = resolve));
|
755
807
|
this.renderer = renderer;
|
756
808
|
this.prepareToRenderSnapshot(renderer);
|
757
|
-
this.
|
809
|
+
const renderInterception = new Promise((resolve => this.resolveInterceptionPromise = resolve));
|
810
|
+
const immediateRender = this.delegate.allowsImmediateRender(snapshot, this.resolveInterceptionPromise);
|
811
|
+
if (!immediateRender) await renderInterception;
|
758
812
|
await this.renderSnapshot(renderer);
|
759
813
|
this.delegate.viewRenderedSnapshot(snapshot, isPreview);
|
760
814
|
this.finishRenderingSnapshot(renderer);
|
761
815
|
} finally {
|
762
816
|
delete this.renderer;
|
817
|
+
this.resolveRenderPromise(undefined);
|
818
|
+
delete this.renderPromise;
|
763
819
|
}
|
764
820
|
} else {
|
765
821
|
this.invalidate();
|
@@ -837,6 +893,56 @@ class LinkInterceptor {
|
|
837
893
|
}
|
838
894
|
}
|
839
895
|
|
896
|
+
class Bardo {
|
897
|
+
constructor(permanentElementMap) {
|
898
|
+
this.permanentElementMap = permanentElementMap;
|
899
|
+
}
|
900
|
+
static preservingPermanentElements(permanentElementMap, callback) {
|
901
|
+
const bardo = new this(permanentElementMap);
|
902
|
+
bardo.enter();
|
903
|
+
callback();
|
904
|
+
bardo.leave();
|
905
|
+
}
|
906
|
+
enter() {
|
907
|
+
for (const id in this.permanentElementMap) {
|
908
|
+
const [, newPermanentElement] = this.permanentElementMap[id];
|
909
|
+
this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
|
910
|
+
}
|
911
|
+
}
|
912
|
+
leave() {
|
913
|
+
for (const id in this.permanentElementMap) {
|
914
|
+
const [currentPermanentElement] = this.permanentElementMap[id];
|
915
|
+
this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
|
916
|
+
this.replacePlaceholderWithPermanentElement(currentPermanentElement);
|
917
|
+
}
|
918
|
+
}
|
919
|
+
replaceNewPermanentElementWithPlaceholder(permanentElement) {
|
920
|
+
const placeholder = createPlaceholderForPermanentElement(permanentElement);
|
921
|
+
permanentElement.replaceWith(placeholder);
|
922
|
+
}
|
923
|
+
replaceCurrentPermanentElementWithClone(permanentElement) {
|
924
|
+
const clone = permanentElement.cloneNode(true);
|
925
|
+
permanentElement.replaceWith(clone);
|
926
|
+
}
|
927
|
+
replacePlaceholderWithPermanentElement(permanentElement) {
|
928
|
+
const placeholder = this.getPlaceholderById(permanentElement.id);
|
929
|
+
placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
|
930
|
+
}
|
931
|
+
getPlaceholderById(id) {
|
932
|
+
return this.placeholders.find((element => element.content == id));
|
933
|
+
}
|
934
|
+
get placeholders() {
|
935
|
+
return [ ...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]") ];
|
936
|
+
}
|
937
|
+
}
|
938
|
+
|
939
|
+
function createPlaceholderForPermanentElement(permanentElement) {
|
940
|
+
const element = document.createElement("meta");
|
941
|
+
element.setAttribute("name", "turbo-permanent-placeholder");
|
942
|
+
element.setAttribute("content", permanentElement.id);
|
943
|
+
return element;
|
944
|
+
}
|
945
|
+
|
840
946
|
class Renderer {
|
841
947
|
constructor(currentSnapshot, newSnapshot, isPreview) {
|
842
948
|
this.currentSnapshot = currentSnapshot;
|
@@ -864,6 +970,9 @@ class Renderer {
|
|
864
970
|
return element;
|
865
971
|
} else {
|
866
972
|
const createdScriptElement = document.createElement("script");
|
973
|
+
if (this.cspNonce) {
|
974
|
+
createdScriptElement.nonce = this.cspNonce;
|
975
|
+
}
|
867
976
|
createdScriptElement.textContent = element.textContent;
|
868
977
|
createdScriptElement.async = false;
|
869
978
|
copyElementAttributes(createdScriptElement, element);
|
@@ -871,28 +980,29 @@ class Renderer {
|
|
871
980
|
}
|
872
981
|
}
|
873
982
|
preservingPermanentElements(callback) {
|
874
|
-
|
875
|
-
callback();
|
876
|
-
replacePlaceholderElementsWithClonedPermanentElements(placeholders);
|
983
|
+
Bardo.preservingPermanentElements(this.permanentElementMap, callback);
|
877
984
|
}
|
878
985
|
focusFirstAutofocusableElement() {
|
879
|
-
const element = this.
|
986
|
+
const element = this.connectedSnapshot.firstAutofocusableElement;
|
880
987
|
if (elementIsFocusable(element)) {
|
881
988
|
element.focus();
|
882
989
|
}
|
883
990
|
}
|
991
|
+
get connectedSnapshot() {
|
992
|
+
return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
|
993
|
+
}
|
884
994
|
get currentElement() {
|
885
995
|
return this.currentSnapshot.element;
|
886
996
|
}
|
887
997
|
get newElement() {
|
888
998
|
return this.newSnapshot.element;
|
889
999
|
}
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
return
|
1000
|
+
get permanentElementMap() {
|
1001
|
+
return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
|
1002
|
+
}
|
1003
|
+
get cspNonce() {
|
1004
|
+
var _a;
|
1005
|
+
return (_a = document.head.querySelector('meta[name="csp-nonce"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
|
896
1006
|
}
|
897
1007
|
}
|
898
1008
|
|
@@ -902,37 +1012,6 @@ function copyElementAttributes(destinationElement, sourceElement) {
|
|
902
1012
|
}
|
903
1013
|
}
|
904
1014
|
|
905
|
-
function createPlaceholderForPermanentElement(permanentElement) {
|
906
|
-
const element = document.createElement("meta");
|
907
|
-
element.setAttribute("name", "turbo-permanent-placeholder");
|
908
|
-
element.setAttribute("content", permanentElement.id);
|
909
|
-
return {
|
910
|
-
element: element,
|
911
|
-
permanentElement: permanentElement
|
912
|
-
};
|
913
|
-
}
|
914
|
-
|
915
|
-
function replacePlaceholderElementsWithClonedPermanentElements(placeholders) {
|
916
|
-
for (const {element: element, permanentElement: permanentElement} of placeholders) {
|
917
|
-
const clonedElement = permanentElement.cloneNode(true);
|
918
|
-
replaceElementWithElement(element, clonedElement);
|
919
|
-
}
|
920
|
-
}
|
921
|
-
|
922
|
-
function relocatePermanentElements(currentSnapshot, newSnapshot) {
|
923
|
-
return currentSnapshot.getPermanentElementsPresentInSnapshot(newSnapshot).reduce(((placeholders, permanentElement) => {
|
924
|
-
const newElement = newSnapshot.getPermanentElementById(permanentElement.id);
|
925
|
-
if (newElement) {
|
926
|
-
const placeholder = createPlaceholderForPermanentElement(permanentElement);
|
927
|
-
replaceElementWithElement(permanentElement, placeholder.element);
|
928
|
-
replaceElementWithElement(newElement, permanentElement);
|
929
|
-
return [ ...placeholders, placeholder ];
|
930
|
-
} else {
|
931
|
-
return placeholders;
|
932
|
-
}
|
933
|
-
}), []);
|
934
|
-
}
|
935
|
-
|
936
1015
|
function elementIsFocusable(element) {
|
937
1016
|
return element && typeof element.focus == "function";
|
938
1017
|
}
|
@@ -949,6 +1028,8 @@ class FrameRenderer extends Renderer {
|
|
949
1028
|
this.scrollFrameIntoView();
|
950
1029
|
await nextAnimationFrame();
|
951
1030
|
this.focusFirstAutofocusableElement();
|
1031
|
+
await nextAnimationFrame();
|
1032
|
+
this.activateScriptElements();
|
952
1033
|
}
|
953
1034
|
loadFrameElement() {
|
954
1035
|
var _a;
|
@@ -975,6 +1056,15 @@ class FrameRenderer extends Renderer {
|
|
975
1056
|
}
|
976
1057
|
return false;
|
977
1058
|
}
|
1059
|
+
activateScriptElements() {
|
1060
|
+
for (const inertScriptElement of this.newScriptElements) {
|
1061
|
+
const activatedScriptElement = this.createScriptElement(inertScriptElement);
|
1062
|
+
inertScriptElement.replaceWith(activatedScriptElement);
|
1063
|
+
}
|
1064
|
+
}
|
1065
|
+
get newScriptElements() {
|
1066
|
+
return this.currentElement.querySelectorAll("script");
|
1067
|
+
}
|
978
1068
|
}
|
979
1069
|
|
980
1070
|
function readScrollLogicalPosition(value, defaultValue) {
|
@@ -985,577 +1075,264 @@ function readScrollLogicalPosition(value, defaultValue) {
|
|
985
1075
|
}
|
986
1076
|
}
|
987
1077
|
|
988
|
-
class
|
989
|
-
constructor(
|
990
|
-
this.
|
991
|
-
this.
|
992
|
-
this.
|
993
|
-
this.
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
}
|
1001
|
-
this.linkInterceptor.start();
|
1002
|
-
this.formInterceptor.start();
|
1003
|
-
}
|
1004
|
-
disconnect() {
|
1005
|
-
this.appearanceObserver.stop();
|
1006
|
-
this.linkInterceptor.stop();
|
1007
|
-
this.formInterceptor.stop();
|
1008
|
-
}
|
1009
|
-
sourceURLChanged() {
|
1010
|
-
if (this.loadingStyle == FrameLoadingStyle.eager) {
|
1011
|
-
this.loadSourceURL();
|
1012
|
-
}
|
1013
|
-
}
|
1014
|
-
loadingStyleChanged() {
|
1015
|
-
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
1016
|
-
this.appearanceObserver.start();
|
1017
|
-
} else {
|
1018
|
-
this.appearanceObserver.stop();
|
1019
|
-
this.loadSourceURL();
|
1020
|
-
}
|
1078
|
+
class ProgressBar {
|
1079
|
+
constructor() {
|
1080
|
+
this.hiding = false;
|
1081
|
+
this.value = 0;
|
1082
|
+
this.visible = false;
|
1083
|
+
this.trickle = () => {
|
1084
|
+
this.setValue(this.value + Math.random() / 100);
|
1085
|
+
};
|
1086
|
+
this.stylesheetElement = this.createStylesheetElement();
|
1087
|
+
this.progressElement = this.createProgressElement();
|
1088
|
+
this.installStylesheetElement();
|
1089
|
+
this.setValue(0);
|
1021
1090
|
}
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1091
|
+
static get defaultCSS() {
|
1092
|
+
return unindent`
|
1093
|
+
.turbo-progress-bar {
|
1094
|
+
position: fixed;
|
1095
|
+
display: block;
|
1096
|
+
top: 0;
|
1097
|
+
left: 0;
|
1098
|
+
height: 3px;
|
1099
|
+
background: #0076ff;
|
1100
|
+
z-index: 9999;
|
1101
|
+
transition:
|
1102
|
+
width ${ProgressBar.animationDuration}ms ease-out,
|
1103
|
+
opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
|
1104
|
+
transform: translate3d(0, 0, 0);
|
1031
1105
|
}
|
1106
|
+
`;
|
1107
|
+
}
|
1108
|
+
show() {
|
1109
|
+
if (!this.visible) {
|
1110
|
+
this.visible = true;
|
1111
|
+
this.installProgressElement();
|
1112
|
+
this.startTrickling();
|
1032
1113
|
}
|
1033
1114
|
}
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
}
|
1043
|
-
} catch (error) {
|
1044
|
-
console.error(error);
|
1045
|
-
this.view.invalidate();
|
1115
|
+
hide() {
|
1116
|
+
if (this.visible && !this.hiding) {
|
1117
|
+
this.hiding = true;
|
1118
|
+
this.fadeProgressElement((() => {
|
1119
|
+
this.uninstallProgressElement();
|
1120
|
+
this.stopTrickling();
|
1121
|
+
this.visible = false;
|
1122
|
+
this.hiding = false;
|
1123
|
+
}));
|
1046
1124
|
}
|
1047
1125
|
}
|
1048
|
-
|
1049
|
-
this.
|
1126
|
+
setValue(value) {
|
1127
|
+
this.value = value;
|
1128
|
+
this.refresh();
|
1050
1129
|
}
|
1051
|
-
|
1052
|
-
|
1130
|
+
installStylesheetElement() {
|
1131
|
+
document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
|
1053
1132
|
}
|
1054
|
-
|
1055
|
-
this.
|
1133
|
+
installProgressElement() {
|
1134
|
+
this.progressElement.style.width = "0";
|
1135
|
+
this.progressElement.style.opacity = "1";
|
1136
|
+
document.documentElement.insertBefore(this.progressElement, document.body);
|
1137
|
+
this.refresh();
|
1056
1138
|
}
|
1057
|
-
|
1058
|
-
|
1139
|
+
fadeProgressElement(callback) {
|
1140
|
+
this.progressElement.style.opacity = "0";
|
1141
|
+
setTimeout(callback, ProgressBar.animationDuration * 1.5);
|
1059
1142
|
}
|
1060
|
-
|
1061
|
-
if (this.
|
1062
|
-
|
1063
|
-
}
|
1064
|
-
this.formSubmission = new FormSubmission(this, element, submitter);
|
1065
|
-
if (this.formSubmission.fetchRequest.isIdempotent) {
|
1066
|
-
this.navigateFrame(element, this.formSubmission.fetchRequest.url.href);
|
1067
|
-
} else {
|
1068
|
-
this.formSubmission.start();
|
1143
|
+
uninstallProgressElement() {
|
1144
|
+
if (this.progressElement.parentNode) {
|
1145
|
+
document.documentElement.removeChild(this.progressElement);
|
1069
1146
|
}
|
1070
1147
|
}
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
this.element.setAttribute("busy", "");
|
1076
|
-
}
|
1077
|
-
requestPreventedHandlingResponse(request, response) {
|
1078
|
-
this.resolveVisitPromise();
|
1148
|
+
startTrickling() {
|
1149
|
+
if (!this.trickleInterval) {
|
1150
|
+
this.trickleInterval = window.setInterval(this.trickle, ProgressBar.animationDuration);
|
1151
|
+
}
|
1079
1152
|
}
|
1080
|
-
|
1081
|
-
|
1082
|
-
this.
|
1153
|
+
stopTrickling() {
|
1154
|
+
window.clearInterval(this.trickleInterval);
|
1155
|
+
delete this.trickleInterval;
|
1083
1156
|
}
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1157
|
+
refresh() {
|
1158
|
+
requestAnimationFrame((() => {
|
1159
|
+
this.progressElement.style.width = `${10 + this.value * 90}%`;
|
1160
|
+
}));
|
1087
1161
|
}
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1162
|
+
createStylesheetElement() {
|
1163
|
+
const element = document.createElement("style");
|
1164
|
+
element.type = "text/css";
|
1165
|
+
element.textContent = ProgressBar.defaultCSS;
|
1166
|
+
return element;
|
1091
1167
|
}
|
1092
|
-
|
1093
|
-
|
1168
|
+
createProgressElement() {
|
1169
|
+
const element = document.createElement("div");
|
1170
|
+
element.className = "turbo-progress-bar";
|
1171
|
+
return element;
|
1094
1172
|
}
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
async visit(url) {
|
1109
|
-
const request = new FetchRequest(this, FetchMethod.get, expandURL(url));
|
1110
|
-
return new Promise((resolve => {
|
1111
|
-
this.resolveVisitPromise = () => {
|
1112
|
-
this.resolveVisitPromise = () => {};
|
1113
|
-
resolve();
|
1173
|
+
}
|
1174
|
+
|
1175
|
+
ProgressBar.animationDuration = 300;
|
1176
|
+
|
1177
|
+
class HeadSnapshot extends Snapshot {
|
1178
|
+
constructor() {
|
1179
|
+
super(...arguments);
|
1180
|
+
this.detailsByOuterHTML = this.children.filter((element => !elementIsNoscript(element))).map((element => elementWithoutNonce(element))).reduce(((result, element) => {
|
1181
|
+
const {outerHTML: outerHTML} = element;
|
1182
|
+
const details = outerHTML in result ? result[outerHTML] : {
|
1183
|
+
type: elementType(element),
|
1184
|
+
tracked: elementIsTracked(element),
|
1185
|
+
elements: []
|
1114
1186
|
};
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
}
|
1122
|
-
findFrameElement(element) {
|
1123
|
-
var _a;
|
1124
|
-
const id = element.getAttribute("data-turbo-frame") || this.element.getAttribute("target");
|
1125
|
-
return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
|
1126
|
-
}
|
1127
|
-
async extractForeignFrameElement(container) {
|
1128
|
-
let element;
|
1129
|
-
const id = CSS.escape(this.id);
|
1130
|
-
if (element = activateElement(container.querySelector(`turbo-frame#${id}`))) {
|
1131
|
-
return element;
|
1132
|
-
}
|
1133
|
-
if (element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`))) {
|
1134
|
-
await element.loaded;
|
1135
|
-
return await this.extractForeignFrameElement(element);
|
1136
|
-
}
|
1137
|
-
console.error(`Response has no matching <turbo-frame id="${id}"> element`);
|
1138
|
-
return new FrameElement;
|
1187
|
+
return Object.assign(Object.assign({}, result), {
|
1188
|
+
[outerHTML]: Object.assign(Object.assign({}, details), {
|
1189
|
+
elements: [ ...details.elements, element ]
|
1190
|
+
})
|
1191
|
+
});
|
1192
|
+
}), {});
|
1139
1193
|
}
|
1140
|
-
|
1141
|
-
|
1142
|
-
if (!this.enabled || id == "_top") {
|
1143
|
-
return false;
|
1144
|
-
}
|
1145
|
-
if (id) {
|
1146
|
-
const frameElement = getFrameElementById(id);
|
1147
|
-
if (frameElement) {
|
1148
|
-
return !frameElement.disabled;
|
1149
|
-
}
|
1150
|
-
}
|
1151
|
-
return true;
|
1194
|
+
get trackedElementSignature() {
|
1195
|
+
return Object.keys(this.detailsByOuterHTML).filter((outerHTML => this.detailsByOuterHTML[outerHTML].tracked)).join("");
|
1152
1196
|
}
|
1153
|
-
|
1154
|
-
return this.
|
1197
|
+
getScriptElementsNotInSnapshot(snapshot) {
|
1198
|
+
return this.getElementsMatchingTypeNotInSnapshot("script", snapshot);
|
1155
1199
|
}
|
1156
|
-
|
1157
|
-
return
|
1200
|
+
getStylesheetElementsNotInSnapshot(snapshot) {
|
1201
|
+
return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot);
|
1158
1202
|
}
|
1159
|
-
|
1160
|
-
return this.element
|
1203
|
+
getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
|
1204
|
+
return Object.keys(this.detailsByOuterHTML).filter((outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))).map((outerHTML => this.detailsByOuterHTML[outerHTML])).filter((({type: type}) => type == matchedType)).map((({elements: [element]}) => element));
|
1161
1205
|
}
|
1162
|
-
get
|
1163
|
-
return this.
|
1206
|
+
get provisionalElements() {
|
1207
|
+
return Object.keys(this.detailsByOuterHTML).reduce(((result, outerHTML) => {
|
1208
|
+
const {type: type, tracked: tracked, elements: elements} = this.detailsByOuterHTML[outerHTML];
|
1209
|
+
if (type == null && !tracked) {
|
1210
|
+
return [ ...result, ...elements ];
|
1211
|
+
} else if (elements.length > 1) {
|
1212
|
+
return [ ...result, ...elements.slice(1) ];
|
1213
|
+
} else {
|
1214
|
+
return result;
|
1215
|
+
}
|
1216
|
+
}), []);
|
1164
1217
|
}
|
1165
|
-
|
1166
|
-
|
1218
|
+
getMetaValue(name) {
|
1219
|
+
const element = this.findMetaElementByName(name);
|
1220
|
+
return element ? element.getAttribute("content") : null;
|
1167
1221
|
}
|
1168
|
-
|
1169
|
-
return this.
|
1222
|
+
findMetaElementByName(name) {
|
1223
|
+
return Object.keys(this.detailsByOuterHTML).reduce(((result, outerHTML) => {
|
1224
|
+
const {elements: [element]} = this.detailsByOuterHTML[outerHTML];
|
1225
|
+
return elementIsMetaElementWithName(element, name) ? element : result;
|
1226
|
+
}), undefined);
|
1170
1227
|
}
|
1171
1228
|
}
|
1172
1229
|
|
1173
|
-
function
|
1174
|
-
if (
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
}
|
1230
|
+
function elementType(element) {
|
1231
|
+
if (elementIsScript(element)) {
|
1232
|
+
return "script";
|
1233
|
+
} else if (elementIsStylesheet(element)) {
|
1234
|
+
return "stylesheet";
|
1179
1235
|
}
|
1180
1236
|
}
|
1181
1237
|
|
1182
|
-
function
|
1183
|
-
|
1184
|
-
element = document.importNode(element, true);
|
1185
|
-
}
|
1186
|
-
if (element instanceof FrameElement) {
|
1187
|
-
return element;
|
1188
|
-
}
|
1238
|
+
function elementIsTracked(element) {
|
1239
|
+
return element.getAttribute("data-turbo-track") == "reload";
|
1189
1240
|
}
|
1190
1241
|
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1242
|
+
function elementIsScript(element) {
|
1243
|
+
const tagName = element.tagName.toLowerCase();
|
1244
|
+
return tagName == "script";
|
1245
|
+
}
|
1246
|
+
|
1247
|
+
function elementIsNoscript(element) {
|
1248
|
+
const tagName = element.tagName.toLowerCase();
|
1249
|
+
return tagName == "noscript";
|
1250
|
+
}
|
1251
|
+
|
1252
|
+
function elementIsStylesheet(element) {
|
1253
|
+
const tagName = element.tagName.toLowerCase();
|
1254
|
+
return tagName == "style" || tagName == "link" && element.getAttribute("rel") == "stylesheet";
|
1255
|
+
}
|
1256
|
+
|
1257
|
+
function elementIsMetaElementWithName(element, name) {
|
1258
|
+
const tagName = element.tagName.toLowerCase();
|
1259
|
+
return tagName == "meta" && element.getAttribute("name") == name;
|
1260
|
+
}
|
1261
|
+
|
1262
|
+
function elementWithoutNonce(element) {
|
1263
|
+
if (element.hasAttribute("nonce")) {
|
1264
|
+
element.setAttribute("nonce", "");
|
1213
1265
|
}
|
1214
|
-
|
1266
|
+
return element;
|
1267
|
+
}
|
1215
1268
|
|
1216
|
-
class
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
} catch (error) {
|
1221
|
-
console.error(error);
|
1222
|
-
} finally {
|
1223
|
-
this.disconnect();
|
1224
|
-
}
|
1269
|
+
class PageSnapshot extends Snapshot {
|
1270
|
+
constructor(element, headSnapshot) {
|
1271
|
+
super(element);
|
1272
|
+
this.headSnapshot = headSnapshot;
|
1225
1273
|
}
|
1226
|
-
|
1227
|
-
|
1228
|
-
return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : this.renderPromise = (async () => {
|
1229
|
-
if (this.dispatchEvent(this.beforeRenderEvent)) {
|
1230
|
-
await nextAnimationFrame();
|
1231
|
-
this.performAction();
|
1232
|
-
}
|
1233
|
-
})();
|
1274
|
+
static fromHTMLString(html = "") {
|
1275
|
+
return this.fromDocument(parseHTMLDocument(html));
|
1234
1276
|
}
|
1235
|
-
|
1236
|
-
|
1237
|
-
this.remove();
|
1238
|
-
} catch (_a) {}
|
1277
|
+
static fromElement(element) {
|
1278
|
+
return this.fromDocument(element.ownerDocument);
|
1239
1279
|
}
|
1240
|
-
|
1241
|
-
|
1242
|
-
const actionFunction = StreamActions[this.action];
|
1243
|
-
if (actionFunction) {
|
1244
|
-
return actionFunction;
|
1245
|
-
}
|
1246
|
-
this.raise("unknown action");
|
1247
|
-
}
|
1248
|
-
this.raise("action attribute is missing");
|
1280
|
+
static fromDocument({head: head, body: body}) {
|
1281
|
+
return new this(body, new HeadSnapshot(head));
|
1249
1282
|
}
|
1250
|
-
|
1251
|
-
|
1252
|
-
if (this.target) {
|
1253
|
-
return (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
|
1254
|
-
}
|
1255
|
-
this.raise("target attribute is missing");
|
1283
|
+
clone() {
|
1284
|
+
return new PageSnapshot(this.element.cloneNode(true), this.headSnapshot);
|
1256
1285
|
}
|
1257
|
-
get
|
1258
|
-
return this.
|
1286
|
+
get headElement() {
|
1287
|
+
return this.headSnapshot.element;
|
1259
1288
|
}
|
1260
|
-
get
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
this.raise("first child element must be a <template> element");
|
1289
|
+
get rootLocation() {
|
1290
|
+
var _a;
|
1291
|
+
const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
|
1292
|
+
return expandURL(root);
|
1265
1293
|
}
|
1266
|
-
get
|
1267
|
-
return this.
|
1294
|
+
get cacheControlValue() {
|
1295
|
+
return this.getSetting("cache-control");
|
1268
1296
|
}
|
1269
|
-
get
|
1270
|
-
return this.
|
1297
|
+
get isPreviewable() {
|
1298
|
+
return this.cacheControlValue != "no-preview";
|
1271
1299
|
}
|
1272
|
-
|
1273
|
-
|
1300
|
+
get isCacheable() {
|
1301
|
+
return this.cacheControlValue != "no-cache";
|
1274
1302
|
}
|
1275
|
-
get
|
1276
|
-
|
1277
|
-
return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
|
1303
|
+
get isVisitable() {
|
1304
|
+
return this.getSetting("visit-control") != "reload";
|
1278
1305
|
}
|
1279
|
-
|
1280
|
-
return
|
1281
|
-
bubbles: true,
|
1282
|
-
cancelable: true
|
1283
|
-
});
|
1306
|
+
getSetting(name) {
|
1307
|
+
return this.headSnapshot.getMetaValue(`turbo-${name}`);
|
1284
1308
|
}
|
1285
1309
|
}
|
1286
1310
|
|
1287
|
-
|
1311
|
+
var TimingMetric;
|
1288
1312
|
|
1289
|
-
|
1313
|
+
(function(TimingMetric) {
|
1314
|
+
TimingMetric["visitStart"] = "visitStart";
|
1315
|
+
TimingMetric["requestStart"] = "requestStart";
|
1316
|
+
TimingMetric["requestEnd"] = "requestEnd";
|
1317
|
+
TimingMetric["visitEnd"] = "visitEnd";
|
1318
|
+
})(TimingMetric || (TimingMetric = {}));
|
1290
1319
|
|
1291
|
-
|
1320
|
+
var VisitState;
|
1292
1321
|
|
1293
|
-
(()
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
|
1322
|
+
(function(VisitState) {
|
1323
|
+
VisitState["initialized"] = "initialized";
|
1324
|
+
VisitState["started"] = "started";
|
1325
|
+
VisitState["canceled"] = "canceled";
|
1326
|
+
VisitState["failed"] = "failed";
|
1327
|
+
VisitState["completed"] = "completed";
|
1328
|
+
})(VisitState || (VisitState = {}));
|
1301
1329
|
|
1302
|
-
|
1330
|
+
const defaultOptions = {
|
1331
|
+
action: "advance",
|
1332
|
+
historyChanged: false
|
1333
|
+
};
|
1303
1334
|
|
1304
|
-
|
1305
|
-
|
1306
|
-
——
|
1307
|
-
Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
|
1308
|
-
`, element.outerHTML);
|
1309
|
-
}
|
1310
|
-
}
|
1311
|
-
})();
|
1312
|
-
|
1313
|
-
class ProgressBar {
|
1314
|
-
constructor() {
|
1315
|
-
this.hiding = false;
|
1316
|
-
this.value = 0;
|
1317
|
-
this.visible = false;
|
1318
|
-
this.trickle = () => {
|
1319
|
-
this.setValue(this.value + Math.random() / 100);
|
1320
|
-
};
|
1321
|
-
this.stylesheetElement = this.createStylesheetElement();
|
1322
|
-
this.progressElement = this.createProgressElement();
|
1323
|
-
this.installStylesheetElement();
|
1324
|
-
this.setValue(0);
|
1325
|
-
}
|
1326
|
-
static get defaultCSS() {
|
1327
|
-
return unindent`
|
1328
|
-
.turbo-progress-bar {
|
1329
|
-
position: fixed;
|
1330
|
-
display: block;
|
1331
|
-
top: 0;
|
1332
|
-
left: 0;
|
1333
|
-
height: 3px;
|
1334
|
-
background: #0076ff;
|
1335
|
-
z-index: 9999;
|
1336
|
-
transition:
|
1337
|
-
width ${ProgressBar.animationDuration}ms ease-out,
|
1338
|
-
opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
|
1339
|
-
transform: translate3d(0, 0, 0);
|
1340
|
-
}
|
1341
|
-
`;
|
1342
|
-
}
|
1343
|
-
show() {
|
1344
|
-
if (!this.visible) {
|
1345
|
-
this.visible = true;
|
1346
|
-
this.installProgressElement();
|
1347
|
-
this.startTrickling();
|
1348
|
-
}
|
1349
|
-
}
|
1350
|
-
hide() {
|
1351
|
-
if (this.visible && !this.hiding) {
|
1352
|
-
this.hiding = true;
|
1353
|
-
this.fadeProgressElement((() => {
|
1354
|
-
this.uninstallProgressElement();
|
1355
|
-
this.stopTrickling();
|
1356
|
-
this.visible = false;
|
1357
|
-
this.hiding = false;
|
1358
|
-
}));
|
1359
|
-
}
|
1360
|
-
}
|
1361
|
-
setValue(value) {
|
1362
|
-
this.value = value;
|
1363
|
-
this.refresh();
|
1364
|
-
}
|
1365
|
-
installStylesheetElement() {
|
1366
|
-
document.head.insertBefore(this.stylesheetElement, document.head.firstChild);
|
1367
|
-
}
|
1368
|
-
installProgressElement() {
|
1369
|
-
this.progressElement.style.width = "0";
|
1370
|
-
this.progressElement.style.opacity = "1";
|
1371
|
-
document.documentElement.insertBefore(this.progressElement, document.body);
|
1372
|
-
this.refresh();
|
1373
|
-
}
|
1374
|
-
fadeProgressElement(callback) {
|
1375
|
-
this.progressElement.style.opacity = "0";
|
1376
|
-
setTimeout(callback, ProgressBar.animationDuration * 1.5);
|
1377
|
-
}
|
1378
|
-
uninstallProgressElement() {
|
1379
|
-
if (this.progressElement.parentNode) {
|
1380
|
-
document.documentElement.removeChild(this.progressElement);
|
1381
|
-
}
|
1382
|
-
}
|
1383
|
-
startTrickling() {
|
1384
|
-
if (!this.trickleInterval) {
|
1385
|
-
this.trickleInterval = window.setInterval(this.trickle, ProgressBar.animationDuration);
|
1386
|
-
}
|
1387
|
-
}
|
1388
|
-
stopTrickling() {
|
1389
|
-
window.clearInterval(this.trickleInterval);
|
1390
|
-
delete this.trickleInterval;
|
1391
|
-
}
|
1392
|
-
refresh() {
|
1393
|
-
requestAnimationFrame((() => {
|
1394
|
-
this.progressElement.style.width = `${10 + this.value * 90}%`;
|
1395
|
-
}));
|
1396
|
-
}
|
1397
|
-
createStylesheetElement() {
|
1398
|
-
const element = document.createElement("style");
|
1399
|
-
element.type = "text/css";
|
1400
|
-
element.textContent = ProgressBar.defaultCSS;
|
1401
|
-
return element;
|
1402
|
-
}
|
1403
|
-
createProgressElement() {
|
1404
|
-
const element = document.createElement("div");
|
1405
|
-
element.className = "turbo-progress-bar";
|
1406
|
-
return element;
|
1407
|
-
}
|
1408
|
-
}
|
1409
|
-
|
1410
|
-
ProgressBar.animationDuration = 300;
|
1411
|
-
|
1412
|
-
class HeadSnapshot extends Snapshot {
|
1413
|
-
constructor() {
|
1414
|
-
super(...arguments);
|
1415
|
-
this.detailsByOuterHTML = this.children.reduce(((result, element) => {
|
1416
|
-
const {outerHTML: outerHTML} = element;
|
1417
|
-
const details = outerHTML in result ? result[outerHTML] : {
|
1418
|
-
type: elementType(element),
|
1419
|
-
tracked: elementIsTracked(element),
|
1420
|
-
elements: []
|
1421
|
-
};
|
1422
|
-
return Object.assign(Object.assign({}, result), {
|
1423
|
-
[outerHTML]: Object.assign(Object.assign({}, details), {
|
1424
|
-
elements: [ ...details.elements, element ]
|
1425
|
-
})
|
1426
|
-
});
|
1427
|
-
}), {});
|
1428
|
-
}
|
1429
|
-
get trackedElementSignature() {
|
1430
|
-
return Object.keys(this.detailsByOuterHTML).filter((outerHTML => this.detailsByOuterHTML[outerHTML].tracked)).join("");
|
1431
|
-
}
|
1432
|
-
getScriptElementsNotInSnapshot(snapshot) {
|
1433
|
-
return this.getElementsMatchingTypeNotInSnapshot("script", snapshot);
|
1434
|
-
}
|
1435
|
-
getStylesheetElementsNotInSnapshot(snapshot) {
|
1436
|
-
return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot);
|
1437
|
-
}
|
1438
|
-
getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
|
1439
|
-
return Object.keys(this.detailsByOuterHTML).filter((outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))).map((outerHTML => this.detailsByOuterHTML[outerHTML])).filter((({type: type}) => type == matchedType)).map((({elements: [element]}) => element));
|
1440
|
-
}
|
1441
|
-
get provisionalElements() {
|
1442
|
-
return Object.keys(this.detailsByOuterHTML).reduce(((result, outerHTML) => {
|
1443
|
-
const {type: type, tracked: tracked, elements: elements} = this.detailsByOuterHTML[outerHTML];
|
1444
|
-
if (type == null && !tracked) {
|
1445
|
-
return [ ...result, ...elements ];
|
1446
|
-
} else if (elements.length > 1) {
|
1447
|
-
return [ ...result, ...elements.slice(1) ];
|
1448
|
-
} else {
|
1449
|
-
return result;
|
1450
|
-
}
|
1451
|
-
}), []);
|
1452
|
-
}
|
1453
|
-
getMetaValue(name) {
|
1454
|
-
const element = this.findMetaElementByName(name);
|
1455
|
-
return element ? element.getAttribute("content") : null;
|
1456
|
-
}
|
1457
|
-
findMetaElementByName(name) {
|
1458
|
-
return Object.keys(this.detailsByOuterHTML).reduce(((result, outerHTML) => {
|
1459
|
-
const {elements: [element]} = this.detailsByOuterHTML[outerHTML];
|
1460
|
-
return elementIsMetaElementWithName(element, name) ? element : result;
|
1461
|
-
}), undefined);
|
1462
|
-
}
|
1463
|
-
}
|
1464
|
-
|
1465
|
-
function elementType(element) {
|
1466
|
-
if (elementIsScript(element)) {
|
1467
|
-
return "script";
|
1468
|
-
} else if (elementIsStylesheet(element)) {
|
1469
|
-
return "stylesheet";
|
1470
|
-
}
|
1471
|
-
}
|
1472
|
-
|
1473
|
-
function elementIsTracked(element) {
|
1474
|
-
return element.getAttribute("data-turbo-track") == "reload";
|
1475
|
-
}
|
1476
|
-
|
1477
|
-
function elementIsScript(element) {
|
1478
|
-
const tagName = element.tagName.toLowerCase();
|
1479
|
-
return tagName == "script";
|
1480
|
-
}
|
1481
|
-
|
1482
|
-
function elementIsStylesheet(element) {
|
1483
|
-
const tagName = element.tagName.toLowerCase();
|
1484
|
-
return tagName == "style" || tagName == "link" && element.getAttribute("rel") == "stylesheet";
|
1485
|
-
}
|
1486
|
-
|
1487
|
-
function elementIsMetaElementWithName(element, name) {
|
1488
|
-
const tagName = element.tagName.toLowerCase();
|
1489
|
-
return tagName == "meta" && element.getAttribute("name") == name;
|
1490
|
-
}
|
1491
|
-
|
1492
|
-
class PageSnapshot extends Snapshot {
|
1493
|
-
constructor(element, headSnapshot) {
|
1494
|
-
super(element);
|
1495
|
-
this.headSnapshot = headSnapshot;
|
1496
|
-
}
|
1497
|
-
static fromHTMLString(html = "") {
|
1498
|
-
return this.fromDocument(parseHTMLDocument(html));
|
1499
|
-
}
|
1500
|
-
static fromElement(element) {
|
1501
|
-
return this.fromDocument(element.ownerDocument);
|
1502
|
-
}
|
1503
|
-
static fromDocument({head: head, body: body}) {
|
1504
|
-
return new this(body, new HeadSnapshot(head));
|
1505
|
-
}
|
1506
|
-
clone() {
|
1507
|
-
return new PageSnapshot(this.element.cloneNode(true), this.headSnapshot);
|
1508
|
-
}
|
1509
|
-
get headElement() {
|
1510
|
-
return this.headSnapshot.element;
|
1511
|
-
}
|
1512
|
-
get rootLocation() {
|
1513
|
-
var _a;
|
1514
|
-
const root = (_a = this.getSetting("root")) !== null && _a !== void 0 ? _a : "/";
|
1515
|
-
return expandURL(root);
|
1516
|
-
}
|
1517
|
-
get cacheControlValue() {
|
1518
|
-
return this.getSetting("cache-control");
|
1519
|
-
}
|
1520
|
-
get isPreviewable() {
|
1521
|
-
return this.cacheControlValue != "no-preview";
|
1522
|
-
}
|
1523
|
-
get isCacheable() {
|
1524
|
-
return this.cacheControlValue != "no-cache";
|
1525
|
-
}
|
1526
|
-
get isVisitable() {
|
1527
|
-
return this.getSetting("visit-control") != "reload";
|
1528
|
-
}
|
1529
|
-
getSetting(name) {
|
1530
|
-
return this.headSnapshot.getMetaValue(`turbo-${name}`);
|
1531
|
-
}
|
1532
|
-
}
|
1533
|
-
|
1534
|
-
var TimingMetric;
|
1535
|
-
|
1536
|
-
(function(TimingMetric) {
|
1537
|
-
TimingMetric["visitStart"] = "visitStart";
|
1538
|
-
TimingMetric["requestStart"] = "requestStart";
|
1539
|
-
TimingMetric["requestEnd"] = "requestEnd";
|
1540
|
-
TimingMetric["visitEnd"] = "visitEnd";
|
1541
|
-
})(TimingMetric || (TimingMetric = {}));
|
1542
|
-
|
1543
|
-
var VisitState;
|
1544
|
-
|
1545
|
-
(function(VisitState) {
|
1546
|
-
VisitState["initialized"] = "initialized";
|
1547
|
-
VisitState["started"] = "started";
|
1548
|
-
VisitState["canceled"] = "canceled";
|
1549
|
-
VisitState["failed"] = "failed";
|
1550
|
-
VisitState["completed"] = "completed";
|
1551
|
-
})(VisitState || (VisitState = {}));
|
1552
|
-
|
1553
|
-
const defaultOptions = {
|
1554
|
-
action: "advance",
|
1555
|
-
historyChanged: false
|
1556
|
-
};
|
1557
|
-
|
1558
|
-
var SystemStatusCode;
|
1335
|
+
var SystemStatusCode;
|
1559
1336
|
|
1560
1337
|
(function(SystemStatusCode) {
|
1561
1338
|
SystemStatusCode[SystemStatusCode["networkFailure"] = 0] = "networkFailure";
|
@@ -1581,6 +1358,7 @@ class Visit {
|
|
1581
1358
|
this.referrer = referrer;
|
1582
1359
|
this.snapshotHTML = snapshotHTML;
|
1583
1360
|
this.response = response;
|
1361
|
+
this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
|
1584
1362
|
}
|
1585
1363
|
get adapter() {
|
1586
1364
|
return this.delegate.adapter;
|
@@ -1594,6 +1372,9 @@ class Visit {
|
|
1594
1372
|
get restorationData() {
|
1595
1373
|
return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
|
1596
1374
|
}
|
1375
|
+
get silent() {
|
1376
|
+
return this.isSamePage;
|
1377
|
+
}
|
1597
1378
|
start() {
|
1598
1379
|
if (this.state == VisitState.initialized) {
|
1599
1380
|
this.recordTimingMetric(TimingMetric.visitStart);
|
@@ -1617,6 +1398,7 @@ class Visit {
|
|
1617
1398
|
this.state = VisitState.completed;
|
1618
1399
|
this.adapter.visitCompleted(this);
|
1619
1400
|
this.delegate.visitCompleted(this);
|
1401
|
+
this.followRedirect();
|
1620
1402
|
}
|
1621
1403
|
}
|
1622
1404
|
fail() {
|
@@ -1673,6 +1455,7 @@ class Visit {
|
|
1673
1455
|
const {statusCode: statusCode, responseHTML: responseHTML} = this.response;
|
1674
1456
|
this.render((async () => {
|
1675
1457
|
this.cacheSnapshot();
|
1458
|
+
if (this.view.renderPromise) await this.view.renderPromise;
|
1676
1459
|
if (isSuccessful(statusCode) && responseHTML != null) {
|
1677
1460
|
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML));
|
1678
1461
|
this.adapter.visitRendered(this);
|
@@ -1707,21 +1490,36 @@ class Visit {
|
|
1707
1490
|
const isPreview = this.shouldIssueRequest();
|
1708
1491
|
this.render((async () => {
|
1709
1492
|
this.cacheSnapshot();
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
this.
|
1493
|
+
if (this.isSamePage) {
|
1494
|
+
this.adapter.visitRendered(this);
|
1495
|
+
} else {
|
1496
|
+
if (this.view.renderPromise) await this.view.renderPromise;
|
1497
|
+
await this.view.renderPage(snapshot, isPreview);
|
1498
|
+
this.adapter.visitRendered(this);
|
1499
|
+
if (!isPreview) {
|
1500
|
+
this.complete();
|
1501
|
+
}
|
1714
1502
|
}
|
1715
1503
|
}));
|
1716
1504
|
}
|
1717
1505
|
}
|
1718
1506
|
followRedirect() {
|
1719
1507
|
if (this.redirectedToLocation && !this.followedRedirect) {
|
1720
|
-
this.
|
1721
|
-
|
1508
|
+
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
1509
|
+
action: "replace",
|
1510
|
+
response: this.response
|
1511
|
+
});
|
1722
1512
|
this.followedRedirect = true;
|
1723
1513
|
}
|
1724
1514
|
}
|
1515
|
+
goToSamePageAnchor() {
|
1516
|
+
if (this.isSamePage) {
|
1517
|
+
this.render((async () => {
|
1518
|
+
this.cacheSnapshot();
|
1519
|
+
this.adapter.visitRendered(this);
|
1520
|
+
}));
|
1521
|
+
}
|
1522
|
+
}
|
1725
1523
|
requestStarted() {
|
1726
1524
|
this.startRequest();
|
1727
1525
|
}
|
@@ -1764,9 +1562,12 @@ class Visit {
|
|
1764
1562
|
performScroll() {
|
1765
1563
|
if (!this.scrolled) {
|
1766
1564
|
if (this.action == "restore") {
|
1767
|
-
this.scrollToRestoredPosition() || this.scrollToTop();
|
1565
|
+
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
|
1768
1566
|
} else {
|
1769
|
-
this.scrollToAnchor() || this.scrollToTop();
|
1567
|
+
this.scrollToAnchor() || this.view.scrollToTop();
|
1568
|
+
}
|
1569
|
+
if (this.isSamePage) {
|
1570
|
+
this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
|
1770
1571
|
}
|
1771
1572
|
this.scrolled = true;
|
1772
1573
|
}
|
@@ -1779,17 +1580,12 @@ class Visit {
|
|
1779
1580
|
}
|
1780
1581
|
}
|
1781
1582
|
scrollToAnchor() {
|
1782
|
-
|
1783
|
-
|
1583
|
+
const anchor = getAnchor(this.location);
|
1584
|
+
if (anchor != null) {
|
1585
|
+
this.view.scrollToAnchor(anchor);
|
1784
1586
|
return true;
|
1785
1587
|
}
|
1786
1588
|
}
|
1787
|
-
scrollToTop() {
|
1788
|
-
this.view.scrollToPosition({
|
1789
|
-
x: 0,
|
1790
|
-
y: 0
|
1791
|
-
});
|
1792
|
-
}
|
1793
1589
|
recordTimingMetric(metric) {
|
1794
1590
|
this.timingMetrics[metric] = (new Date).getTime();
|
1795
1591
|
}
|
@@ -1810,7 +1606,13 @@ class Visit {
|
|
1810
1606
|
return typeof this.response == "object";
|
1811
1607
|
}
|
1812
1608
|
shouldIssueRequest() {
|
1813
|
-
|
1609
|
+
if (this.isSamePage) {
|
1610
|
+
return false;
|
1611
|
+
} else if (this.action == "restore") {
|
1612
|
+
return !this.hasCachedSnapshot();
|
1613
|
+
} else {
|
1614
|
+
return true;
|
1615
|
+
}
|
1814
1616
|
}
|
1815
1617
|
cacheSnapshot() {
|
1816
1618
|
if (!this.snapshotCached) {
|
@@ -1823,7 +1625,7 @@ class Visit {
|
|
1823
1625
|
await new Promise((resolve => {
|
1824
1626
|
this.frame = requestAnimationFrame((() => resolve()));
|
1825
1627
|
}));
|
1826
|
-
callback();
|
1628
|
+
await callback();
|
1827
1629
|
delete this.frame;
|
1828
1630
|
this.performScroll();
|
1829
1631
|
}
|
@@ -1853,12 +1655,13 @@ class BrowserAdapter {
|
|
1853
1655
|
visitStarted(visit) {
|
1854
1656
|
visit.issueRequest();
|
1855
1657
|
visit.changeHistory();
|
1658
|
+
visit.goToSamePageAnchor();
|
1856
1659
|
visit.loadCachedSnapshot();
|
1857
1660
|
}
|
1858
1661
|
visitRequestStarted(visit) {
|
1859
1662
|
this.progressBar.setValue(0);
|
1860
1663
|
if (visit.hasCachedSnapshot() || visit.action != "restore") {
|
1861
|
-
this.
|
1664
|
+
this.showVisitProgressBarAfterDelay();
|
1862
1665
|
} else {
|
1863
1666
|
this.showProgressBar();
|
1864
1667
|
}
|
@@ -1879,24 +1682,42 @@ class BrowserAdapter {
|
|
1879
1682
|
}
|
1880
1683
|
visitRequestFinished(visit) {
|
1881
1684
|
this.progressBar.setValue(1);
|
1882
|
-
this.
|
1883
|
-
}
|
1884
|
-
visitCompleted(visit) {
|
1885
|
-
visit.followRedirect();
|
1685
|
+
this.hideVisitProgressBar();
|
1886
1686
|
}
|
1687
|
+
visitCompleted(visit) {}
|
1887
1688
|
pageInvalidated() {
|
1888
1689
|
this.reload();
|
1889
1690
|
}
|
1890
1691
|
visitFailed(visit) {}
|
1891
1692
|
visitRendered(visit) {}
|
1892
|
-
|
1893
|
-
this.
|
1693
|
+
formSubmissionStarted(formSubmission) {
|
1694
|
+
this.progressBar.setValue(0);
|
1695
|
+
this.showFormProgressBarAfterDelay();
|
1696
|
+
}
|
1697
|
+
formSubmissionFinished(formSubmission) {
|
1698
|
+
this.progressBar.setValue(1);
|
1699
|
+
this.hideFormProgressBar();
|
1894
1700
|
}
|
1895
|
-
|
1701
|
+
showVisitProgressBarAfterDelay() {
|
1702
|
+
this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
|
1703
|
+
}
|
1704
|
+
hideVisitProgressBar() {
|
1896
1705
|
this.progressBar.hide();
|
1897
|
-
if (this.
|
1898
|
-
window.clearTimeout(this.
|
1899
|
-
delete this.
|
1706
|
+
if (this.visitProgressBarTimeout != null) {
|
1707
|
+
window.clearTimeout(this.visitProgressBarTimeout);
|
1708
|
+
delete this.visitProgressBarTimeout;
|
1709
|
+
}
|
1710
|
+
}
|
1711
|
+
showFormProgressBarAfterDelay() {
|
1712
|
+
if (this.formProgressBarTimeout == null) {
|
1713
|
+
this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
|
1714
|
+
}
|
1715
|
+
}
|
1716
|
+
hideFormProgressBar() {
|
1717
|
+
this.progressBar.hide();
|
1718
|
+
if (this.formProgressBarTimeout != null) {
|
1719
|
+
window.clearTimeout(this.formProgressBarTimeout);
|
1720
|
+
delete this.formProgressBarTimeout;
|
1900
1721
|
}
|
1901
1722
|
}
|
1902
1723
|
reload() {
|
@@ -1907,6 +1728,30 @@ class BrowserAdapter {
|
|
1907
1728
|
}
|
1908
1729
|
}
|
1909
1730
|
|
1731
|
+
class CacheObserver {
|
1732
|
+
constructor() {
|
1733
|
+
this.started = false;
|
1734
|
+
}
|
1735
|
+
start() {
|
1736
|
+
if (!this.started) {
|
1737
|
+
this.started = true;
|
1738
|
+
addEventListener("turbo:before-cache", this.removeStaleElements, false);
|
1739
|
+
}
|
1740
|
+
}
|
1741
|
+
stop() {
|
1742
|
+
if (this.started) {
|
1743
|
+
this.started = false;
|
1744
|
+
removeEventListener("turbo:before-cache", this.removeStaleElements, false);
|
1745
|
+
}
|
1746
|
+
}
|
1747
|
+
removeStaleElements() {
|
1748
|
+
const staleElements = [ ...document.querySelectorAll('[data-turbo-cache="false"]') ];
|
1749
|
+
for (const element of staleElements) {
|
1750
|
+
element.remove();
|
1751
|
+
}
|
1752
|
+
}
|
1753
|
+
}
|
1754
|
+
|
1910
1755
|
class FormSubmitObserver {
|
1911
1756
|
constructor(delegate) {
|
1912
1757
|
this.started = false;
|
@@ -1963,6 +1808,7 @@ class FrameRedirector {
|
|
1963
1808
|
linkClickIntercepted(element, url) {
|
1964
1809
|
const frame = this.findFrameElement(element);
|
1965
1810
|
if (frame) {
|
1811
|
+
frame.setAttribute("reloadable", "");
|
1966
1812
|
frame.src = url;
|
1967
1813
|
}
|
1968
1814
|
}
|
@@ -1970,17 +1816,18 @@ class FrameRedirector {
|
|
1970
1816
|
return this.shouldRedirect(element, submitter);
|
1971
1817
|
}
|
1972
1818
|
formSubmissionIntercepted(element, submitter) {
|
1973
|
-
const frame = this.findFrameElement(element);
|
1819
|
+
const frame = this.findFrameElement(element, submitter);
|
1974
1820
|
if (frame) {
|
1821
|
+
frame.removeAttribute("reloadable");
|
1975
1822
|
frame.delegate.formSubmissionIntercepted(element, submitter);
|
1976
1823
|
}
|
1977
1824
|
}
|
1978
1825
|
shouldRedirect(element, submitter) {
|
1979
|
-
const frame = this.findFrameElement(element);
|
1826
|
+
const frame = this.findFrameElement(element, submitter);
|
1980
1827
|
return frame ? frame != element.closest("turbo-frame") : false;
|
1981
1828
|
}
|
1982
|
-
findFrameElement(element) {
|
1983
|
-
const id = element.getAttribute("data-turbo-frame");
|
1829
|
+
findFrameElement(element, submitter) {
|
1830
|
+
const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
|
1984
1831
|
if (id && id != "_top") {
|
1985
1832
|
const frame = this.element.querySelector(`#${id}:not([disabled])`);
|
1986
1833
|
if (frame instanceof FrameElement) {
|
@@ -2082,7 +1929,8 @@ class LinkClickObserver {
|
|
2082
1929
|
};
|
2083
1930
|
this.clickBubbled = event => {
|
2084
1931
|
if (this.clickEventIsSignificant(event)) {
|
2085
|
-
const
|
1932
|
+
const target = event.composedPath && event.composedPath()[0] || event.target;
|
1933
|
+
const link = this.findLinkFromClickTarget(target);
|
2086
1934
|
if (link) {
|
2087
1935
|
const location = this.getLocationForLink(link);
|
2088
1936
|
if (this.delegate.willFollowLinkToLocation(link, location)) {
|
@@ -2119,12 +1967,16 @@ class LinkClickObserver {
|
|
2119
1967
|
}
|
2120
1968
|
}
|
2121
1969
|
|
1970
|
+
function isAction(action) {
|
1971
|
+
return action == "advance" || action == "replace" || action == "restore";
|
1972
|
+
}
|
1973
|
+
|
2122
1974
|
class Navigator {
|
2123
1975
|
constructor(delegate) {
|
2124
1976
|
this.delegate = delegate;
|
2125
1977
|
}
|
2126
1978
|
proposeVisit(location, options = {}) {
|
2127
|
-
if (this.delegate.
|
1979
|
+
if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
|
2128
1980
|
this.delegate.visitProposedToLocation(location, options);
|
2129
1981
|
}
|
2130
1982
|
}
|
@@ -2138,8 +1990,10 @@ class Navigator {
|
|
2138
1990
|
submitForm(form, submitter) {
|
2139
1991
|
this.stop();
|
2140
1992
|
this.formSubmission = new FormSubmission(this, form, submitter, true);
|
2141
|
-
if (this.formSubmission.
|
2142
|
-
this.proposeVisit(this.formSubmission.fetchRequest.url
|
1993
|
+
if (this.formSubmission.isIdempotent) {
|
1994
|
+
this.proposeVisit(this.formSubmission.fetchRequest.url, {
|
1995
|
+
action: this.getActionForFormSubmission(this.formSubmission)
|
1996
|
+
});
|
2143
1997
|
} else {
|
2144
1998
|
this.formSubmission.start();
|
2145
1999
|
}
|
@@ -2163,7 +2017,11 @@ class Navigator {
|
|
2163
2017
|
get history() {
|
2164
2018
|
return this.delegate.history;
|
2165
2019
|
}
|
2166
|
-
formSubmissionStarted(formSubmission) {
|
2020
|
+
formSubmissionStarted(formSubmission) {
|
2021
|
+
if (typeof this.adapter.formSubmissionStarted === "function") {
|
2022
|
+
this.adapter.formSubmissionStarted(formSubmission);
|
2023
|
+
}
|
2024
|
+
}
|
2167
2025
|
async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
|
2168
2026
|
if (formSubmission == this.formSubmission) {
|
2169
2027
|
const responseHTML = await fetchResponse.responseHTML;
|
@@ -2186,24 +2044,49 @@ class Navigator {
|
|
2186
2044
|
const responseHTML = await fetchResponse.responseHTML;
|
2187
2045
|
if (responseHTML) {
|
2188
2046
|
const snapshot = PageSnapshot.fromHTMLString(responseHTML);
|
2189
|
-
|
2047
|
+
if (fetchResponse.serverError) {
|
2048
|
+
await this.view.renderError(snapshot);
|
2049
|
+
} else {
|
2050
|
+
await this.view.renderPage(snapshot);
|
2051
|
+
}
|
2052
|
+
this.view.scrollToTop();
|
2190
2053
|
this.view.clearSnapshotCache();
|
2191
2054
|
}
|
2192
2055
|
}
|
2193
|
-
formSubmissionErrored(formSubmission, error) {
|
2194
|
-
|
2056
|
+
formSubmissionErrored(formSubmission, error) {
|
2057
|
+
console.error(error);
|
2058
|
+
}
|
2059
|
+
formSubmissionFinished(formSubmission) {
|
2060
|
+
if (typeof this.adapter.formSubmissionFinished === "function") {
|
2061
|
+
this.adapter.formSubmissionFinished(formSubmission);
|
2062
|
+
}
|
2063
|
+
}
|
2195
2064
|
visitStarted(visit) {
|
2196
2065
|
this.delegate.visitStarted(visit);
|
2197
2066
|
}
|
2198
2067
|
visitCompleted(visit) {
|
2199
2068
|
this.delegate.visitCompleted(visit);
|
2200
2069
|
}
|
2070
|
+
locationWithActionIsSamePage(location, action) {
|
2071
|
+
const anchor = getAnchor(location);
|
2072
|
+
const currentAnchor = getAnchor(this.view.lastRenderedLocation);
|
2073
|
+
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
|
2074
|
+
return action !== "replace" && getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) && (isRestorationToTop || anchor != null && anchor !== currentAnchor);
|
2075
|
+
}
|
2076
|
+
visitScrolledToSamePageLocation(oldURL, newURL) {
|
2077
|
+
this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
|
2078
|
+
}
|
2201
2079
|
get location() {
|
2202
2080
|
return this.history.location;
|
2203
2081
|
}
|
2204
2082
|
get restorationIdentifier() {
|
2205
2083
|
return this.history.restorationIdentifier;
|
2206
2084
|
}
|
2085
|
+
getActionForFormSubmission(formSubmission) {
|
2086
|
+
const {formElement: formElement, submitter: submitter} = formSubmission;
|
2087
|
+
const action = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-action")) || formElement.getAttribute("data-turbo-action");
|
2088
|
+
return isAction(action) ? action : "advance";
|
2089
|
+
}
|
2207
2090
|
}
|
2208
2091
|
|
2209
2092
|
var PageStage;
|
@@ -2366,10 +2249,6 @@ function fetchResponseIsStream(response) {
|
|
2366
2249
|
return contentType.startsWith(StreamMessage.contentType);
|
2367
2250
|
}
|
2368
2251
|
|
2369
|
-
function isAction(action) {
|
2370
|
-
return action == "advance" || action == "replace" || action == "restore";
|
2371
|
-
}
|
2372
|
-
|
2373
2252
|
class ErrorRenderer extends Renderer {
|
2374
2253
|
async render() {
|
2375
2254
|
this.replaceHeadAndBody();
|
@@ -2409,7 +2288,7 @@ class PageRenderer extends Renderer {
|
|
2409
2288
|
}
|
2410
2289
|
finishRendering() {
|
2411
2290
|
super.finishRendering();
|
2412
|
-
if (this.isPreview) {
|
2291
|
+
if (!this.isPreview) {
|
2413
2292
|
this.focusFirstAutofocusableElement();
|
2414
2293
|
}
|
2415
2294
|
}
|
@@ -2464,12 +2343,12 @@ class PageRenderer extends Renderer {
|
|
2464
2343
|
activateNewBodyScriptElements() {
|
2465
2344
|
for (const inertScriptElement of this.newBodyScriptElements) {
|
2466
2345
|
const activatedScriptElement = this.createScriptElement(inertScriptElement);
|
2467
|
-
|
2346
|
+
inertScriptElement.replaceWith(activatedScriptElement);
|
2468
2347
|
}
|
2469
2348
|
}
|
2470
2349
|
assignNewBody() {
|
2471
2350
|
if (document.body && this.newElement instanceof HTMLBodyElement) {
|
2472
|
-
|
2351
|
+
document.body.replaceWith(this.newElement);
|
2473
2352
|
} else {
|
2474
2353
|
document.documentElement.appendChild(this.newElement);
|
2475
2354
|
}
|
@@ -2487,7 +2366,7 @@ class PageRenderer extends Renderer {
|
|
2487
2366
|
return this.newHeadSnapshot.provisionalElements;
|
2488
2367
|
}
|
2489
2368
|
get newBodyScriptElements() {
|
2490
|
-
return
|
2369
|
+
return this.newElement.querySelectorAll("script");
|
2491
2370
|
}
|
2492
2371
|
}
|
2493
2372
|
|
@@ -2547,7 +2426,7 @@ class PageView extends View {
|
|
2547
2426
|
}
|
2548
2427
|
renderError(snapshot) {
|
2549
2428
|
const renderer = new ErrorRenderer(this.snapshot, snapshot, false);
|
2550
|
-
this.render(renderer);
|
2429
|
+
return this.render(renderer);
|
2551
2430
|
}
|
2552
2431
|
clearSnapshotCache() {
|
2553
2432
|
this.snapshotCache.clear();
|
@@ -2578,11 +2457,13 @@ class Session {
|
|
2578
2457
|
this.view = new PageView(this, document.documentElement);
|
2579
2458
|
this.adapter = new BrowserAdapter(this);
|
2580
2459
|
this.pageObserver = new PageObserver(this);
|
2460
|
+
this.cacheObserver = new CacheObserver;
|
2581
2461
|
this.linkClickObserver = new LinkClickObserver(this);
|
2582
2462
|
this.formSubmitObserver = new FormSubmitObserver(this);
|
2583
2463
|
this.scrollObserver = new ScrollObserver(this);
|
2584
2464
|
this.streamObserver = new StreamObserver(this);
|
2585
2465
|
this.frameRedirector = new FrameRedirector(document.documentElement);
|
2466
|
+
this.drive = true;
|
2586
2467
|
this.enabled = true;
|
2587
2468
|
this.progressBarDelay = 500;
|
2588
2469
|
this.started = false;
|
@@ -2590,6 +2471,7 @@ class Session {
|
|
2590
2471
|
start() {
|
2591
2472
|
if (!this.started) {
|
2592
2473
|
this.pageObserver.start();
|
2474
|
+
this.cacheObserver.start();
|
2593
2475
|
this.linkClickObserver.start();
|
2594
2476
|
this.formSubmitObserver.start();
|
2595
2477
|
this.scrollObserver.start();
|
@@ -2606,6 +2488,7 @@ class Session {
|
|
2606
2488
|
stop() {
|
2607
2489
|
if (this.started) {
|
2608
2490
|
this.pageObserver.stop();
|
2491
|
+
this.cacheObserver.stop();
|
2609
2492
|
this.linkClickObserver.stop();
|
2610
2493
|
this.formSubmitObserver.stop();
|
2611
2494
|
this.scrollObserver.stop();
|
@@ -2642,9 +2525,9 @@ class Session {
|
|
2642
2525
|
get restorationIdentifier() {
|
2643
2526
|
return this.history.restorationIdentifier;
|
2644
2527
|
}
|
2645
|
-
historyPoppedToLocationWithRestorationIdentifier(location) {
|
2528
|
+
historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
|
2646
2529
|
if (this.enabled) {
|
2647
|
-
this.navigator.
|
2530
|
+
this.navigator.startVisit(location, restorationIdentifier, {
|
2648
2531
|
action: "restore",
|
2649
2532
|
historyChanged: true
|
2650
2533
|
});
|
@@ -2652,197 +2535,733 @@ class Session {
|
|
2652
2535
|
this.adapter.pageInvalidated();
|
2653
2536
|
}
|
2654
2537
|
}
|
2655
|
-
scrollPositionChanged(position) {
|
2656
|
-
this.history.updateRestorationData({
|
2657
|
-
scrollPosition: position
|
2658
|
-
});
|
2538
|
+
scrollPositionChanged(position) {
|
2539
|
+
this.history.updateRestorationData({
|
2540
|
+
scrollPosition: position
|
2541
|
+
});
|
2542
|
+
}
|
2543
|
+
willFollowLinkToLocation(link, location) {
|
2544
|
+
return this.elementDriveEnabled(link) && this.locationIsVisitable(location) && this.applicationAllowsFollowingLinkToLocation(link, location);
|
2545
|
+
}
|
2546
|
+
followedLinkToLocation(link, location) {
|
2547
|
+
const action = this.getActionForLink(link);
|
2548
|
+
this.convertLinkWithMethodClickToFormSubmission(link) || this.visit(location.href, {
|
2549
|
+
action: action
|
2550
|
+
});
|
2551
|
+
}
|
2552
|
+
convertLinkWithMethodClickToFormSubmission(link) {
|
2553
|
+
var _a;
|
2554
|
+
const linkMethod = link.getAttribute("data-turbo-method");
|
2555
|
+
if (linkMethod) {
|
2556
|
+
const form = document.createElement("form");
|
2557
|
+
form.method = linkMethod;
|
2558
|
+
form.action = link.getAttribute("href") || "undefined";
|
2559
|
+
(_a = link.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(form, link);
|
2560
|
+
return dispatch("submit", {
|
2561
|
+
cancelable: true,
|
2562
|
+
target: form
|
2563
|
+
});
|
2564
|
+
} else {
|
2565
|
+
return false;
|
2566
|
+
}
|
2567
|
+
}
|
2568
|
+
allowsVisitingLocationWithAction(location, action) {
|
2569
|
+
return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
|
2570
|
+
}
|
2571
|
+
visitProposedToLocation(location, options) {
|
2572
|
+
extendURLWithDeprecatedProperties(location);
|
2573
|
+
this.adapter.visitProposedToLocation(location, options);
|
2574
|
+
}
|
2575
|
+
visitStarted(visit) {
|
2576
|
+
extendURLWithDeprecatedProperties(visit.location);
|
2577
|
+
if (!visit.silent) {
|
2578
|
+
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
|
2579
|
+
}
|
2580
|
+
}
|
2581
|
+
visitCompleted(visit) {
|
2582
|
+
this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
|
2583
|
+
}
|
2584
|
+
locationWithActionIsSamePage(location, action) {
|
2585
|
+
return this.navigator.locationWithActionIsSamePage(location, action);
|
2586
|
+
}
|
2587
|
+
visitScrolledToSamePageLocation(oldURL, newURL) {
|
2588
|
+
this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
|
2589
|
+
}
|
2590
|
+
willSubmitForm(form, submitter) {
|
2591
|
+
return this.elementDriveEnabled(form) && this.elementDriveEnabled(submitter);
|
2592
|
+
}
|
2593
|
+
formSubmitted(form, submitter) {
|
2594
|
+
this.navigator.submitForm(form, submitter);
|
2595
|
+
}
|
2596
|
+
pageBecameInteractive() {
|
2597
|
+
this.view.lastRenderedLocation = this.location;
|
2598
|
+
this.notifyApplicationAfterPageLoad();
|
2599
|
+
}
|
2600
|
+
pageLoaded() {
|
2601
|
+
this.history.assumeControlOfScrollRestoration();
|
2602
|
+
}
|
2603
|
+
pageWillUnload() {
|
2604
|
+
this.history.relinquishControlOfScrollRestoration();
|
2605
|
+
}
|
2606
|
+
receivedMessageFromStream(message) {
|
2607
|
+
this.renderStreamMessage(message);
|
2608
|
+
}
|
2609
|
+
viewWillCacheSnapshot() {
|
2610
|
+
var _a;
|
2611
|
+
if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
|
2612
|
+
this.notifyApplicationBeforeCachingSnapshot();
|
2613
|
+
}
|
2614
|
+
}
|
2615
|
+
allowsImmediateRender({element: element}, resume) {
|
2616
|
+
const event = this.notifyApplicationBeforeRender(element, resume);
|
2617
|
+
return !event.defaultPrevented;
|
2618
|
+
}
|
2619
|
+
viewRenderedSnapshot(snapshot, isPreview) {
|
2620
|
+
this.view.lastRenderedLocation = this.history.location;
|
2621
|
+
this.notifyApplicationAfterRender();
|
2622
|
+
}
|
2623
|
+
viewInvalidated() {
|
2624
|
+
this.adapter.pageInvalidated();
|
2625
|
+
}
|
2626
|
+
frameLoaded(frame) {
|
2627
|
+
this.notifyApplicationAfterFrameLoad(frame);
|
2628
|
+
}
|
2629
|
+
frameRendered(fetchResponse, frame) {
|
2630
|
+
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
2631
|
+
}
|
2632
|
+
applicationAllowsFollowingLinkToLocation(link, location) {
|
2633
|
+
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
|
2634
|
+
return !event.defaultPrevented;
|
2635
|
+
}
|
2636
|
+
applicationAllowsVisitingLocation(location) {
|
2637
|
+
const event = this.notifyApplicationBeforeVisitingLocation(location);
|
2638
|
+
return !event.defaultPrevented;
|
2639
|
+
}
|
2640
|
+
notifyApplicationAfterClickingLinkToLocation(link, location) {
|
2641
|
+
return dispatch("turbo:click", {
|
2642
|
+
target: link,
|
2643
|
+
detail: {
|
2644
|
+
url: location.href
|
2645
|
+
},
|
2646
|
+
cancelable: true
|
2647
|
+
});
|
2648
|
+
}
|
2649
|
+
notifyApplicationBeforeVisitingLocation(location) {
|
2650
|
+
return dispatch("turbo:before-visit", {
|
2651
|
+
detail: {
|
2652
|
+
url: location.href
|
2653
|
+
},
|
2654
|
+
cancelable: true
|
2655
|
+
});
|
2656
|
+
}
|
2657
|
+
notifyApplicationAfterVisitingLocation(location, action) {
|
2658
|
+
return dispatch("turbo:visit", {
|
2659
|
+
detail: {
|
2660
|
+
url: location.href,
|
2661
|
+
action: action
|
2662
|
+
}
|
2663
|
+
});
|
2664
|
+
}
|
2665
|
+
notifyApplicationBeforeCachingSnapshot() {
|
2666
|
+
return dispatch("turbo:before-cache");
|
2667
|
+
}
|
2668
|
+
notifyApplicationBeforeRender(newBody, resume) {
|
2669
|
+
return dispatch("turbo:before-render", {
|
2670
|
+
detail: {
|
2671
|
+
newBody: newBody,
|
2672
|
+
resume: resume
|
2673
|
+
},
|
2674
|
+
cancelable: true
|
2675
|
+
});
|
2676
|
+
}
|
2677
|
+
notifyApplicationAfterRender() {
|
2678
|
+
return dispatch("turbo:render");
|
2679
|
+
}
|
2680
|
+
notifyApplicationAfterPageLoad(timing = {}) {
|
2681
|
+
return dispatch("turbo:load", {
|
2682
|
+
detail: {
|
2683
|
+
url: this.location.href,
|
2684
|
+
timing: timing
|
2685
|
+
}
|
2686
|
+
});
|
2687
|
+
}
|
2688
|
+
notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
|
2689
|
+
dispatchEvent(new HashChangeEvent("hashchange", {
|
2690
|
+
oldURL: oldURL.toString(),
|
2691
|
+
newURL: newURL.toString()
|
2692
|
+
}));
|
2693
|
+
}
|
2694
|
+
notifyApplicationAfterFrameLoad(frame) {
|
2695
|
+
return dispatch("turbo:frame-load", {
|
2696
|
+
target: frame
|
2697
|
+
});
|
2698
|
+
}
|
2699
|
+
notifyApplicationAfterFrameRender(fetchResponse, frame) {
|
2700
|
+
return dispatch("turbo:frame-render", {
|
2701
|
+
detail: {
|
2702
|
+
fetchResponse: fetchResponse
|
2703
|
+
},
|
2704
|
+
target: frame,
|
2705
|
+
cancelable: true
|
2706
|
+
});
|
2707
|
+
}
|
2708
|
+
elementDriveEnabled(element) {
|
2709
|
+
const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
|
2710
|
+
if (this.drive) {
|
2711
|
+
if (container) {
|
2712
|
+
return container.getAttribute("data-turbo") != "false";
|
2713
|
+
} else {
|
2714
|
+
return true;
|
2715
|
+
}
|
2716
|
+
} else {
|
2717
|
+
if (container) {
|
2718
|
+
return container.getAttribute("data-turbo") == "true";
|
2719
|
+
} else {
|
2720
|
+
return false;
|
2721
|
+
}
|
2722
|
+
}
|
2723
|
+
}
|
2724
|
+
getActionForLink(link) {
|
2725
|
+
const action = link.getAttribute("data-turbo-action");
|
2726
|
+
return isAction(action) ? action : "advance";
|
2727
|
+
}
|
2728
|
+
locationIsVisitable(location) {
|
2729
|
+
return isPrefixedBy(location, this.snapshot.rootLocation) && isHTML(location);
|
2730
|
+
}
|
2731
|
+
get snapshot() {
|
2732
|
+
return this.view.snapshot;
|
2733
|
+
}
|
2734
|
+
}
|
2735
|
+
|
2736
|
+
function extendURLWithDeprecatedProperties(url) {
|
2737
|
+
Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
|
2738
|
+
}
|
2739
|
+
|
2740
|
+
const deprecatedLocationPropertyDescriptors = {
|
2741
|
+
absoluteURL: {
|
2742
|
+
get() {
|
2743
|
+
return this.toString();
|
2744
|
+
}
|
2745
|
+
}
|
2746
|
+
};
|
2747
|
+
|
2748
|
+
const session = new Session;
|
2749
|
+
|
2750
|
+
const {navigator: navigator} = session;
|
2751
|
+
|
2752
|
+
function start() {
|
2753
|
+
session.start();
|
2754
|
+
}
|
2755
|
+
|
2756
|
+
function registerAdapter(adapter) {
|
2757
|
+
session.registerAdapter(adapter);
|
2758
|
+
}
|
2759
|
+
|
2760
|
+
function visit(location, options) {
|
2761
|
+
session.visit(location, options);
|
2762
|
+
}
|
2763
|
+
|
2764
|
+
function connectStreamSource(source) {
|
2765
|
+
session.connectStreamSource(source);
|
2766
|
+
}
|
2767
|
+
|
2768
|
+
function disconnectStreamSource(source) {
|
2769
|
+
session.disconnectStreamSource(source);
|
2770
|
+
}
|
2771
|
+
|
2772
|
+
function renderStreamMessage(message) {
|
2773
|
+
session.renderStreamMessage(message);
|
2774
|
+
}
|
2775
|
+
|
2776
|
+
function clearCache() {
|
2777
|
+
session.clearCache();
|
2778
|
+
}
|
2779
|
+
|
2780
|
+
function setProgressBarDelay(delay) {
|
2781
|
+
session.setProgressBarDelay(delay);
|
2782
|
+
}
|
2783
|
+
|
2784
|
+
var Turbo = Object.freeze({
|
2785
|
+
__proto__: null,
|
2786
|
+
navigator: navigator,
|
2787
|
+
session: session,
|
2788
|
+
PageRenderer: PageRenderer,
|
2789
|
+
PageSnapshot: PageSnapshot,
|
2790
|
+
start: start,
|
2791
|
+
registerAdapter: registerAdapter,
|
2792
|
+
visit: visit,
|
2793
|
+
connectStreamSource: connectStreamSource,
|
2794
|
+
disconnectStreamSource: disconnectStreamSource,
|
2795
|
+
renderStreamMessage: renderStreamMessage,
|
2796
|
+
clearCache: clearCache,
|
2797
|
+
setProgressBarDelay: setProgressBarDelay
|
2798
|
+
});
|
2799
|
+
|
2800
|
+
class FrameController {
|
2801
|
+
constructor(element) {
|
2802
|
+
this.resolveVisitPromise = () => {};
|
2803
|
+
this.connected = false;
|
2804
|
+
this.hasBeenLoaded = false;
|
2805
|
+
this.settingSourceURL = false;
|
2806
|
+
this.element = element;
|
2807
|
+
this.view = new FrameView(this, this.element);
|
2808
|
+
this.appearanceObserver = new AppearanceObserver(this, this.element);
|
2809
|
+
this.linkInterceptor = new LinkInterceptor(this, this.element);
|
2810
|
+
this.formInterceptor = new FormInterceptor(this, this.element);
|
2811
|
+
}
|
2812
|
+
connect() {
|
2813
|
+
if (!this.connected) {
|
2814
|
+
this.connected = true;
|
2815
|
+
this.reloadable = false;
|
2816
|
+
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
2817
|
+
this.appearanceObserver.start();
|
2818
|
+
}
|
2819
|
+
this.linkInterceptor.start();
|
2820
|
+
this.formInterceptor.start();
|
2821
|
+
this.sourceURLChanged();
|
2822
|
+
}
|
2823
|
+
}
|
2824
|
+
disconnect() {
|
2825
|
+
if (this.connected) {
|
2826
|
+
this.connected = false;
|
2827
|
+
this.appearanceObserver.stop();
|
2828
|
+
this.linkInterceptor.stop();
|
2829
|
+
this.formInterceptor.stop();
|
2830
|
+
}
|
2831
|
+
}
|
2832
|
+
disabledChanged() {
|
2833
|
+
if (this.loadingStyle == FrameLoadingStyle.eager) {
|
2834
|
+
this.loadSourceURL();
|
2835
|
+
}
|
2836
|
+
}
|
2837
|
+
sourceURLChanged() {
|
2838
|
+
if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
|
2839
|
+
this.loadSourceURL();
|
2840
|
+
}
|
2841
|
+
}
|
2842
|
+
loadingStyleChanged() {
|
2843
|
+
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
2844
|
+
this.appearanceObserver.start();
|
2845
|
+
} else {
|
2846
|
+
this.appearanceObserver.stop();
|
2847
|
+
this.loadSourceURL();
|
2848
|
+
}
|
2849
|
+
}
|
2850
|
+
async loadSourceURL() {
|
2851
|
+
if (!this.settingSourceURL && this.enabled && this.isActive && (this.reloadable || this.sourceURL != this.currentURL)) {
|
2852
|
+
const previousURL = this.currentURL;
|
2853
|
+
this.currentURL = this.sourceURL;
|
2854
|
+
if (this.sourceURL) {
|
2855
|
+
try {
|
2856
|
+
this.element.loaded = this.visit(this.sourceURL);
|
2857
|
+
this.appearanceObserver.stop();
|
2858
|
+
await this.element.loaded;
|
2859
|
+
this.hasBeenLoaded = true;
|
2860
|
+
session.frameLoaded(this.element);
|
2861
|
+
} catch (error) {
|
2862
|
+
this.currentURL = previousURL;
|
2863
|
+
throw error;
|
2864
|
+
}
|
2865
|
+
}
|
2866
|
+
}
|
2867
|
+
}
|
2868
|
+
async loadResponse(fetchResponse) {
|
2869
|
+
if (fetchResponse.redirected) {
|
2870
|
+
this.sourceURL = fetchResponse.response.url;
|
2871
|
+
}
|
2872
|
+
try {
|
2873
|
+
const html = await fetchResponse.responseHTML;
|
2874
|
+
if (html) {
|
2875
|
+
const {body: body} = parseHTMLDocument(html);
|
2876
|
+
const snapshot = new Snapshot(await this.extractForeignFrameElement(body));
|
2877
|
+
const renderer = new FrameRenderer(this.view.snapshot, snapshot, false);
|
2878
|
+
if (this.view.renderPromise) await this.view.renderPromise;
|
2879
|
+
await this.view.render(renderer);
|
2880
|
+
session.frameRendered(fetchResponse, this.element);
|
2881
|
+
}
|
2882
|
+
} catch (error) {
|
2883
|
+
console.error(error);
|
2884
|
+
this.view.invalidate();
|
2885
|
+
}
|
2886
|
+
}
|
2887
|
+
elementAppearedInViewport(element) {
|
2888
|
+
this.loadSourceURL();
|
2889
|
+
}
|
2890
|
+
shouldInterceptLinkClick(element, url) {
|
2891
|
+
if (element.hasAttribute("data-turbo-method")) {
|
2892
|
+
return false;
|
2893
|
+
} else {
|
2894
|
+
return this.shouldInterceptNavigation(element);
|
2895
|
+
}
|
2896
|
+
}
|
2897
|
+
linkClickIntercepted(element, url) {
|
2898
|
+
this.reloadable = true;
|
2899
|
+
this.navigateFrame(element, url);
|
2900
|
+
}
|
2901
|
+
shouldInterceptFormSubmission(element, submitter) {
|
2902
|
+
return this.shouldInterceptNavigation(element, submitter);
|
2903
|
+
}
|
2904
|
+
formSubmissionIntercepted(element, submitter) {
|
2905
|
+
if (this.formSubmission) {
|
2906
|
+
this.formSubmission.stop();
|
2907
|
+
}
|
2908
|
+
this.reloadable = false;
|
2909
|
+
this.formSubmission = new FormSubmission(this, element, submitter);
|
2910
|
+
if (this.formSubmission.fetchRequest.isIdempotent) {
|
2911
|
+
this.navigateFrame(element, this.formSubmission.fetchRequest.url.href, submitter);
|
2912
|
+
} else {
|
2913
|
+
const {fetchRequest: fetchRequest} = this.formSubmission;
|
2914
|
+
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
2915
|
+
this.formSubmission.start();
|
2916
|
+
}
|
2917
|
+
}
|
2918
|
+
prepareHeadersForRequest(headers, request) {
|
2919
|
+
headers["Turbo-Frame"] = this.id;
|
2920
|
+
}
|
2921
|
+
requestStarted(request) {
|
2922
|
+
this.element.setAttribute("busy", "");
|
2923
|
+
}
|
2924
|
+
requestPreventedHandlingResponse(request, response) {
|
2925
|
+
this.resolveVisitPromise();
|
2926
|
+
}
|
2927
|
+
async requestSucceededWithResponse(request, response) {
|
2928
|
+
await this.loadResponse(response);
|
2929
|
+
this.resolveVisitPromise();
|
2930
|
+
}
|
2931
|
+
requestFailedWithResponse(request, response) {
|
2932
|
+
console.error(response);
|
2933
|
+
this.resolveVisitPromise();
|
2934
|
+
}
|
2935
|
+
requestErrored(request, error) {
|
2936
|
+
console.error(error);
|
2937
|
+
this.resolveVisitPromise();
|
2938
|
+
}
|
2939
|
+
requestFinished(request) {
|
2940
|
+
this.element.removeAttribute("busy");
|
2941
|
+
}
|
2942
|
+
formSubmissionStarted(formSubmission) {
|
2943
|
+
const frame = this.findFrameElement(formSubmission.formElement);
|
2944
|
+
frame.setAttribute("busy", "");
|
2945
|
+
}
|
2946
|
+
formSubmissionSucceededWithResponse(formSubmission, response) {
|
2947
|
+
const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
|
2948
|
+
frame.delegate.loadResponse(response);
|
2949
|
+
}
|
2950
|
+
formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
|
2951
|
+
this.element.delegate.loadResponse(fetchResponse);
|
2952
|
+
}
|
2953
|
+
formSubmissionErrored(formSubmission, error) {
|
2954
|
+
console.error(error);
|
2955
|
+
}
|
2956
|
+
formSubmissionFinished(formSubmission) {
|
2957
|
+
const frame = this.findFrameElement(formSubmission.formElement);
|
2958
|
+
frame.removeAttribute("busy");
|
2959
|
+
}
|
2960
|
+
allowsImmediateRender(snapshot, resume) {
|
2961
|
+
return true;
|
2962
|
+
}
|
2963
|
+
viewRenderedSnapshot(snapshot, isPreview) {}
|
2964
|
+
viewInvalidated() {}
|
2965
|
+
async visit(url) {
|
2966
|
+
const request = new FetchRequest(this, FetchMethod.get, expandURL(url), undefined, this.element);
|
2967
|
+
return new Promise((resolve => {
|
2968
|
+
this.resolveVisitPromise = () => {
|
2969
|
+
this.resolveVisitPromise = () => {};
|
2970
|
+
resolve();
|
2971
|
+
};
|
2972
|
+
request.perform();
|
2973
|
+
}));
|
2974
|
+
}
|
2975
|
+
navigateFrame(element, url, submitter) {
|
2976
|
+
const frame = this.findFrameElement(element, submitter);
|
2977
|
+
frame.setAttribute("reloadable", "");
|
2978
|
+
frame.src = url;
|
2979
|
+
}
|
2980
|
+
findFrameElement(element, submitter) {
|
2981
|
+
var _a;
|
2982
|
+
const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame") || this.element.getAttribute("target");
|
2983
|
+
return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
|
2984
|
+
}
|
2985
|
+
async extractForeignFrameElement(container) {
|
2986
|
+
let element;
|
2987
|
+
const id = CSS.escape(this.id);
|
2988
|
+
try {
|
2989
|
+
if (element = activateElement(container.querySelector(`turbo-frame#${id}`), this.currentURL)) {
|
2990
|
+
return element;
|
2991
|
+
}
|
2992
|
+
if (element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.currentURL)) {
|
2993
|
+
await element.loaded;
|
2994
|
+
return await this.extractForeignFrameElement(element);
|
2995
|
+
}
|
2996
|
+
console.error(`Response has no matching <turbo-frame id="${id}"> element`);
|
2997
|
+
} catch (error) {
|
2998
|
+
console.error(error);
|
2999
|
+
}
|
3000
|
+
return new FrameElement;
|
3001
|
+
}
|
3002
|
+
shouldInterceptNavigation(element, submitter) {
|
3003
|
+
const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame") || this.element.getAttribute("target");
|
3004
|
+
if (!this.enabled || id == "_top") {
|
3005
|
+
return false;
|
3006
|
+
}
|
3007
|
+
if (id) {
|
3008
|
+
const frameElement = getFrameElementById(id);
|
3009
|
+
if (frameElement) {
|
3010
|
+
return !frameElement.disabled;
|
3011
|
+
}
|
3012
|
+
}
|
3013
|
+
if (!session.elementDriveEnabled(element)) {
|
3014
|
+
return false;
|
3015
|
+
}
|
3016
|
+
if (submitter && !session.elementDriveEnabled(submitter)) {
|
3017
|
+
return false;
|
3018
|
+
}
|
3019
|
+
return true;
|
3020
|
+
}
|
3021
|
+
get id() {
|
3022
|
+
return this.element.id;
|
3023
|
+
}
|
3024
|
+
get enabled() {
|
3025
|
+
return !this.element.disabled;
|
2659
3026
|
}
|
2660
|
-
|
2661
|
-
|
3027
|
+
get sourceURL() {
|
3028
|
+
if (this.element.src) {
|
3029
|
+
return this.element.src;
|
3030
|
+
}
|
2662
3031
|
}
|
2663
|
-
|
2664
|
-
const
|
2665
|
-
|
2666
|
-
action: action
|
2667
|
-
});
|
3032
|
+
get reloadable() {
|
3033
|
+
const frame = this.findFrameElement(this.element);
|
3034
|
+
return frame.hasAttribute("reloadable");
|
2668
3035
|
}
|
2669
|
-
|
2670
|
-
|
3036
|
+
set reloadable(value) {
|
3037
|
+
const frame = this.findFrameElement(this.element);
|
3038
|
+
if (value) {
|
3039
|
+
frame.setAttribute("reloadable", "");
|
3040
|
+
} else {
|
3041
|
+
frame.removeAttribute("reloadable");
|
3042
|
+
}
|
2671
3043
|
}
|
2672
|
-
|
2673
|
-
|
2674
|
-
this.
|
3044
|
+
set sourceURL(sourceURL) {
|
3045
|
+
this.settingSourceURL = true;
|
3046
|
+
this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
|
3047
|
+
this.currentURL = this.element.src;
|
3048
|
+
this.settingSourceURL = false;
|
2675
3049
|
}
|
2676
|
-
|
2677
|
-
|
2678
|
-
this.notifyApplicationAfterVisitingLocation(visit.location);
|
3050
|
+
get loadingStyle() {
|
3051
|
+
return this.element.loading;
|
2679
3052
|
}
|
2680
|
-
|
2681
|
-
this.
|
3053
|
+
get isLoading() {
|
3054
|
+
return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
|
2682
3055
|
}
|
2683
|
-
|
2684
|
-
return this.
|
3056
|
+
get isActive() {
|
3057
|
+
return this.element.isActive && this.connected;
|
2685
3058
|
}
|
2686
|
-
|
2687
|
-
|
3059
|
+
}
|
3060
|
+
|
3061
|
+
function getFrameElementById(id) {
|
3062
|
+
if (id != null) {
|
3063
|
+
const element = document.getElementById(id);
|
3064
|
+
if (element instanceof FrameElement) {
|
3065
|
+
return element;
|
3066
|
+
}
|
2688
3067
|
}
|
2689
|
-
|
2690
|
-
|
2691
|
-
|
3068
|
+
}
|
3069
|
+
|
3070
|
+
function activateElement(element, currentURL) {
|
3071
|
+
if (element) {
|
3072
|
+
const src = element.getAttribute("src");
|
3073
|
+
if (src != null && currentURL != null && urlsAreEqual(src, currentURL)) {
|
3074
|
+
throw new Error(`Matching <turbo-frame id="${element.id}"> element has a source URL which references itself`);
|
3075
|
+
}
|
3076
|
+
if (element.ownerDocument !== document) {
|
3077
|
+
element = document.importNode(element, true);
|
3078
|
+
}
|
3079
|
+
if (element instanceof FrameElement) {
|
3080
|
+
element.connectedCallback();
|
3081
|
+
return element;
|
3082
|
+
}
|
2692
3083
|
}
|
2693
|
-
|
2694
|
-
|
3084
|
+
}
|
3085
|
+
|
3086
|
+
const StreamActions = {
|
3087
|
+
after() {
|
3088
|
+
this.targetElements.forEach((e => {
|
3089
|
+
var _a;
|
3090
|
+
return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling);
|
3091
|
+
}));
|
3092
|
+
},
|
3093
|
+
append() {
|
3094
|
+
this.removeDuplicateTargetChildren();
|
3095
|
+
this.targetElements.forEach((e => e.append(this.templateContent)));
|
3096
|
+
},
|
3097
|
+
before() {
|
3098
|
+
this.targetElements.forEach((e => {
|
3099
|
+
var _a;
|
3100
|
+
return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e);
|
3101
|
+
}));
|
3102
|
+
},
|
3103
|
+
prepend() {
|
3104
|
+
this.removeDuplicateTargetChildren();
|
3105
|
+
this.targetElements.forEach((e => e.prepend(this.templateContent)));
|
3106
|
+
},
|
3107
|
+
remove() {
|
3108
|
+
this.targetElements.forEach((e => e.remove()));
|
3109
|
+
},
|
3110
|
+
replace() {
|
3111
|
+
this.targetElements.forEach((e => e.replaceWith(this.templateContent)));
|
3112
|
+
},
|
3113
|
+
update() {
|
3114
|
+
this.targetElements.forEach((e => {
|
3115
|
+
e.innerHTML = "";
|
3116
|
+
e.append(this.templateContent);
|
3117
|
+
}));
|
2695
3118
|
}
|
2696
|
-
|
2697
|
-
|
3119
|
+
};
|
3120
|
+
|
3121
|
+
class StreamElement extends HTMLElement {
|
3122
|
+
async connectedCallback() {
|
3123
|
+
try {
|
3124
|
+
await this.render();
|
3125
|
+
} catch (error) {
|
3126
|
+
console.error(error);
|
3127
|
+
} finally {
|
3128
|
+
this.disconnect();
|
3129
|
+
}
|
2698
3130
|
}
|
2699
|
-
|
2700
|
-
|
3131
|
+
async render() {
|
3132
|
+
var _a;
|
3133
|
+
return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : this.renderPromise = (async () => {
|
3134
|
+
if (this.dispatchEvent(this.beforeRenderEvent)) {
|
3135
|
+
await nextAnimationFrame();
|
3136
|
+
this.performAction();
|
3137
|
+
}
|
3138
|
+
})();
|
2701
3139
|
}
|
2702
|
-
|
2703
|
-
|
3140
|
+
disconnect() {
|
3141
|
+
try {
|
3142
|
+
this.remove();
|
3143
|
+
} catch (_a) {}
|
2704
3144
|
}
|
2705
|
-
|
2706
|
-
this.
|
3145
|
+
removeDuplicateTargetChildren() {
|
3146
|
+
this.duplicateChildren.forEach((c => c.remove()));
|
2707
3147
|
}
|
2708
|
-
|
2709
|
-
|
2710
|
-
this.
|
3148
|
+
get duplicateChildren() {
|
3149
|
+
var _a;
|
3150
|
+
const existingChildren = this.targetElements.flatMap((e => [ ...e.children ])).filter((c => !!c.id));
|
3151
|
+
const newChildrenIds = [ ...(_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children ].filter((c => !!c.id)).map((c => c.id));
|
3152
|
+
return existingChildren.filter((c => newChildrenIds.includes(c.id)));
|
2711
3153
|
}
|
2712
|
-
|
2713
|
-
this.
|
3154
|
+
get performAction() {
|
3155
|
+
if (this.action) {
|
3156
|
+
const actionFunction = StreamActions[this.action];
|
3157
|
+
if (actionFunction) {
|
3158
|
+
return actionFunction;
|
3159
|
+
}
|
3160
|
+
this.raise("unknown action");
|
3161
|
+
}
|
3162
|
+
this.raise("action attribute is missing");
|
2714
3163
|
}
|
2715
|
-
|
2716
|
-
|
2717
|
-
|
3164
|
+
get targetElements() {
|
3165
|
+
if (this.target) {
|
3166
|
+
return this.targetElementsById;
|
3167
|
+
} else if (this.targets) {
|
3168
|
+
return this.targetElementsByQuery;
|
3169
|
+
} else {
|
3170
|
+
this.raise("target or targets attribute is missing");
|
3171
|
+
}
|
2718
3172
|
}
|
2719
|
-
|
2720
|
-
|
2721
|
-
return !event.defaultPrevented;
|
3173
|
+
get templateContent() {
|
3174
|
+
return this.templateElement.content.cloneNode(true);
|
2722
3175
|
}
|
2723
|
-
|
2724
|
-
|
2725
|
-
|
2726
|
-
|
2727
|
-
|
2728
|
-
},
|
2729
|
-
cancelable: true
|
2730
|
-
});
|
3176
|
+
get templateElement() {
|
3177
|
+
if (this.firstElementChild instanceof HTMLTemplateElement) {
|
3178
|
+
return this.firstElementChild;
|
3179
|
+
}
|
3180
|
+
this.raise("first child element must be a <template> element");
|
2731
3181
|
}
|
2732
|
-
|
2733
|
-
return
|
2734
|
-
detail: {
|
2735
|
-
url: location.href
|
2736
|
-
},
|
2737
|
-
cancelable: true
|
2738
|
-
});
|
3182
|
+
get action() {
|
3183
|
+
return this.getAttribute("action");
|
2739
3184
|
}
|
2740
|
-
|
2741
|
-
return
|
2742
|
-
detail: {
|
2743
|
-
url: location.href
|
2744
|
-
}
|
2745
|
-
});
|
3185
|
+
get target() {
|
3186
|
+
return this.getAttribute("target");
|
2746
3187
|
}
|
2747
|
-
|
2748
|
-
return
|
3188
|
+
get targets() {
|
3189
|
+
return this.getAttribute("targets");
|
2749
3190
|
}
|
2750
|
-
|
2751
|
-
|
2752
|
-
detail: {
|
2753
|
-
newBody: newBody
|
2754
|
-
}
|
2755
|
-
});
|
3191
|
+
raise(message) {
|
3192
|
+
throw new Error(`${this.description}: ${message}`);
|
2756
3193
|
}
|
2757
|
-
|
2758
|
-
|
3194
|
+
get description() {
|
3195
|
+
var _a, _b;
|
3196
|
+
return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
|
2759
3197
|
}
|
2760
|
-
|
2761
|
-
return
|
2762
|
-
|
2763
|
-
|
2764
|
-
timing: timing
|
2765
|
-
}
|
3198
|
+
get beforeRenderEvent() {
|
3199
|
+
return new CustomEvent("turbo:before-stream-render", {
|
3200
|
+
bubbles: true,
|
3201
|
+
cancelable: true
|
2766
3202
|
});
|
2767
3203
|
}
|
2768
|
-
|
2769
|
-
|
2770
|
-
|
2771
|
-
|
2772
|
-
|
2773
|
-
const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
|
2774
|
-
if (container) {
|
2775
|
-
return container.getAttribute("data-turbo") != "false";
|
3204
|
+
get targetElementsById() {
|
3205
|
+
var _a;
|
3206
|
+
const element = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.getElementById(this.target);
|
3207
|
+
if (element !== null) {
|
3208
|
+
return [ element ];
|
2776
3209
|
} else {
|
2777
|
-
return
|
3210
|
+
return [];
|
2778
3211
|
}
|
2779
3212
|
}
|
2780
|
-
|
2781
|
-
|
2782
|
-
|
2783
|
-
|
2784
|
-
|
2785
|
-
|
2786
|
-
|
2787
|
-
|
2788
|
-
function extendURLWithDeprecatedProperties(url) {
|
2789
|
-
Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
|
2790
|
-
}
|
2791
|
-
|
2792
|
-
const deprecatedLocationPropertyDescriptors = {
|
2793
|
-
absoluteURL: {
|
2794
|
-
get() {
|
2795
|
-
return this.toString();
|
3213
|
+
get targetElementsByQuery() {
|
3214
|
+
var _a;
|
3215
|
+
const elements = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.querySelectorAll(this.targets);
|
3216
|
+
if (elements.length !== 0) {
|
3217
|
+
return Array.prototype.slice.call(elements);
|
3218
|
+
} else {
|
3219
|
+
return [];
|
2796
3220
|
}
|
2797
3221
|
}
|
2798
|
-
};
|
2799
|
-
|
2800
|
-
const session = new Session;
|
2801
|
-
|
2802
|
-
const {navigator: navigator} = session;
|
2803
|
-
|
2804
|
-
function start() {
|
2805
|
-
session.start();
|
2806
3222
|
}
|
2807
3223
|
|
2808
|
-
|
2809
|
-
session.registerAdapter(adapter);
|
2810
|
-
}
|
3224
|
+
FrameElement.delegateConstructor = FrameController;
|
2811
3225
|
|
2812
|
-
|
2813
|
-
session.visit(location, options);
|
2814
|
-
}
|
3226
|
+
customElements.define("turbo-frame", FrameElement);
|
2815
3227
|
|
2816
|
-
|
2817
|
-
session.connectStreamSource(source);
|
2818
|
-
}
|
3228
|
+
customElements.define("turbo-stream", StreamElement);
|
2819
3229
|
|
2820
|
-
|
2821
|
-
|
2822
|
-
|
3230
|
+
(() => {
|
3231
|
+
let element = document.currentScript;
|
3232
|
+
if (!element) return;
|
3233
|
+
if (element.hasAttribute("data-turbo-suppress-warning")) return;
|
3234
|
+
while (element = element.parentElement) {
|
3235
|
+
if (element == document.body) {
|
3236
|
+
return console.warn(unindent`
|
3237
|
+
You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
|
2823
3238
|
|
2824
|
-
|
2825
|
-
session.renderStreamMessage(message);
|
2826
|
-
}
|
3239
|
+
Load your application’s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.
|
2827
3240
|
|
2828
|
-
|
2829
|
-
session.clearCache();
|
2830
|
-
}
|
3241
|
+
For more information, see: https://turbo.hotwired.dev/handbook/building#working-with-script-elements
|
2831
3242
|
|
2832
|
-
|
2833
|
-
|
2834
|
-
|
3243
|
+
——
|
3244
|
+
Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
|
3245
|
+
`, element.outerHTML);
|
3246
|
+
}
|
3247
|
+
}
|
3248
|
+
})();
|
3249
|
+
|
3250
|
+
window.Turbo = Turbo;
|
2835
3251
|
|
2836
3252
|
start();
|
2837
3253
|
|
2838
3254
|
var turbo_es2017Esm = Object.freeze({
|
2839
3255
|
__proto__: null,
|
3256
|
+
PageRenderer: PageRenderer,
|
3257
|
+
PageSnapshot: PageSnapshot,
|
2840
3258
|
clearCache: clearCache,
|
2841
3259
|
connectStreamSource: connectStreamSource,
|
2842
3260
|
disconnectStreamSource: disconnectStreamSource,
|
2843
3261
|
navigator: navigator,
|
2844
3262
|
registerAdapter: registerAdapter,
|
2845
3263
|
renderStreamMessage: renderStreamMessage,
|
3264
|
+
session: session,
|
2846
3265
|
setProgressBarDelay: setProgressBarDelay,
|
2847
3266
|
start: start,
|
2848
3267
|
visit: visit
|
@@ -2851,17 +3270,20 @@ var turbo_es2017Esm = Object.freeze({
|
|
2851
3270
|
let consumer;
|
2852
3271
|
|
2853
3272
|
async function getConsumer() {
|
2854
|
-
|
2855
|
-
const {createConsumer: createConsumer} = await Promise.resolve().then((function() {
|
2856
|
-
return index;
|
2857
|
-
}));
|
2858
|
-
return setConsumer(createConsumer());
|
3273
|
+
return consumer || setConsumer(createConsumer().then(setConsumer));
|
2859
3274
|
}
|
2860
3275
|
|
2861
3276
|
function setConsumer(newConsumer) {
|
2862
3277
|
return consumer = newConsumer;
|
2863
3278
|
}
|
2864
3279
|
|
3280
|
+
async function createConsumer() {
|
3281
|
+
const {createConsumer: createConsumer} = await Promise.resolve().then((function() {
|
3282
|
+
return index;
|
3283
|
+
}));
|
3284
|
+
return createConsumer();
|
3285
|
+
}
|
3286
|
+
|
2865
3287
|
async function subscribeTo(channel, mixin) {
|
2866
3288
|
const {subscriptions: subscriptions} = await getConsumer();
|
2867
3289
|
return subscriptions.create(channel, mixin);
|
@@ -2871,6 +3293,7 @@ var cable = Object.freeze({
|
|
2871
3293
|
__proto__: null,
|
2872
3294
|
getConsumer: getConsumer,
|
2873
3295
|
setConsumer: setConsumer,
|
3296
|
+
createConsumer: createConsumer,
|
2874
3297
|
subscribeTo: subscribeTo
|
2875
3298
|
});
|
2876
3299
|
|
@@ -3334,7 +3757,7 @@ function createWebSocketURL(url) {
|
|
3334
3757
|
}
|
3335
3758
|
}
|
3336
3759
|
|
3337
|
-
function createConsumer(url = getConfig("url") || INTERNAL.default_mount_path) {
|
3760
|
+
function createConsumer$1(url = getConfig("url") || INTERNAL.default_mount_path) {
|
3338
3761
|
return new Consumer(url);
|
3339
3762
|
}
|
3340
3763
|
|
@@ -3356,7 +3779,7 @@ var index = Object.freeze({
|
|
3356
3779
|
adapters: adapters,
|
3357
3780
|
createWebSocketURL: createWebSocketURL,
|
3358
3781
|
logger: logger,
|
3359
|
-
createConsumer: createConsumer,
|
3782
|
+
createConsumer: createConsumer$1,
|
3360
3783
|
getConfig: getConfig
|
3361
3784
|
});
|
3362
3785
|
|