turbo-rails 1.3.3 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +15 -2
- data/app/assets/javascripts/turbo.js +136 -72
- data/app/assets/javascripts/turbo.min.js +3 -3
- data/app/assets/javascripts/turbo.min.js.map +1 -1
- data/app/controllers/turbo/frames/frame_request.rb +15 -7
- data/app/javascript/turbo/cable_stream_source_element.js +13 -1
- data/app/models/concerns/turbo/broadcastable.rb +22 -3
- data/app/models/turbo/streams/tag_builder.rb +2 -0
- data/app/views/layouts/turbo_rails/frame.html.erb +8 -0
- data/lib/turbo/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40ccdc123db34929af83c559e6bf6c65ca3027b5fd765546143f6093d34662a7
|
4
|
+
data.tar.gz: 7236691e0dbf00c61fb51039212c9316b7d0fc5d03370e939c5c318b56ab70c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7b6bdb711d3e712ac96bd52fc6f909d2ea5beaa2ba664d10e4678792afc58bb61ecce45ccb9b745e8f3706e5221917c754c2a461caff4be864e294050aba10d
|
7
|
+
data.tar.gz: a6d86dc5249f31161e4f7c3e31cfcda725b1173f0c63348c81f0cd62135688408b4b963540f80564d5eec1fb86f7601090e24c00ed5a55fa5e9c5277a18d75a4
|
data/Rakefile
CHANGED
@@ -9,7 +9,20 @@ load "rails/tasks/statistics.rake"
|
|
9
9
|
Rake::TestTask.new do |test|
|
10
10
|
test.libs << "test"
|
11
11
|
test.test_files = FileList["test/**/*_test.rb"]
|
12
|
-
test.warning = false
|
13
12
|
end
|
14
13
|
|
15
|
-
task
|
14
|
+
task :test_prereq do
|
15
|
+
puts "Installing Ruby dependencies"
|
16
|
+
`bundle install`
|
17
|
+
|
18
|
+
puts "Installing JavaScript dependencies"
|
19
|
+
`yarn install`
|
20
|
+
|
21
|
+
puts "Building JavaScript"
|
22
|
+
`yarn build`
|
23
|
+
|
24
|
+
puts "Preparing test database"
|
25
|
+
`cd test/dummy; ./bin/rails db:test:prepare; cd ../..`
|
26
|
+
end
|
27
|
+
|
28
|
+
task default: [:test_prereq, :test]
|
@@ -56,13 +56,11 @@ function clickCaptured(event) {
|
|
56
56
|
|
57
57
|
(function() {
|
58
58
|
if ("submitter" in Event.prototype) return;
|
59
|
-
let prototype;
|
59
|
+
let prototype = window.Event.prototype;
|
60
60
|
if ("SubmitEvent" in window && /Apple Computer/.test(navigator.vendor)) {
|
61
61
|
prototype = window.SubmitEvent.prototype;
|
62
62
|
} else if ("SubmitEvent" in window) {
|
63
63
|
return;
|
64
|
-
} else {
|
65
|
-
prototype = window.Event.prototype;
|
66
64
|
}
|
67
65
|
addEventListener("click", clickCaptured, true);
|
68
66
|
Object.defineProperty(prototype, "submitter", {
|
@@ -82,14 +80,14 @@ var FrameLoadingStyle;
|
|
82
80
|
})(FrameLoadingStyle || (FrameLoadingStyle = {}));
|
83
81
|
|
84
82
|
class FrameElement extends HTMLElement {
|
83
|
+
static get observedAttributes() {
|
84
|
+
return [ "disabled", "complete", "loading", "src" ];
|
85
|
+
}
|
85
86
|
constructor() {
|
86
87
|
super();
|
87
88
|
this.loaded = Promise.resolve();
|
88
89
|
this.delegate = new FrameElement.delegateConstructor(this);
|
89
90
|
}
|
90
|
-
static get observedAttributes() {
|
91
|
-
return [ "disabled", "complete", "loading", "src" ];
|
92
|
-
}
|
93
91
|
connectedCallback() {
|
94
92
|
this.delegate.connect();
|
95
93
|
}
|
@@ -560,7 +558,7 @@ class FetchRequest {
|
|
560
558
|
credentials: "same-origin",
|
561
559
|
headers: this.headers,
|
562
560
|
redirect: "follow",
|
563
|
-
body: this.
|
561
|
+
body: this.isSafe ? null : this.body,
|
564
562
|
signal: this.abortSignal,
|
565
563
|
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
|
566
564
|
};
|
@@ -570,8 +568,8 @@ class FetchRequest {
|
|
570
568
|
Accept: "text/html, application/xhtml+xml"
|
571
569
|
};
|
572
570
|
}
|
573
|
-
get
|
574
|
-
return this.method
|
571
|
+
get isSafe() {
|
572
|
+
return this.method === FetchMethod.get;
|
575
573
|
}
|
576
574
|
get abortSignal() {
|
577
575
|
return this.abortController.signal;
|
@@ -633,9 +631,6 @@ class AppearanceObserver {
|
|
633
631
|
}
|
634
632
|
|
635
633
|
class StreamMessage {
|
636
|
-
constructor(fragment) {
|
637
|
-
this.fragment = importStreamElements(fragment);
|
638
|
-
}
|
639
634
|
static wrap(message) {
|
640
635
|
if (typeof message == "string") {
|
641
636
|
return new this(createDocumentFragment(message));
|
@@ -643,6 +638,9 @@ class StreamMessage {
|
|
643
638
|
return message;
|
644
639
|
}
|
645
640
|
}
|
641
|
+
constructor(fragment) {
|
642
|
+
this.fragment = importStreamElements(fragment);
|
643
|
+
}
|
646
644
|
}
|
647
645
|
|
648
646
|
StreamMessage.contentType = "text/vnd.turbo-stream.html";
|
@@ -691,6 +689,9 @@ function formEnctypeFromString(encoding) {
|
|
691
689
|
}
|
692
690
|
|
693
691
|
class FormSubmission {
|
692
|
+
static confirmMethod(message, _element, _submitter) {
|
693
|
+
return Promise.resolve(confirm(message));
|
694
|
+
}
|
694
695
|
constructor(delegate, formElement, submitter, mustRedirect = false) {
|
695
696
|
this.state = FormSubmissionState.initialized;
|
696
697
|
this.delegate = delegate;
|
@@ -704,9 +705,6 @@ class FormSubmission {
|
|
704
705
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
705
706
|
this.mustRedirect = mustRedirect;
|
706
707
|
}
|
707
|
-
static confirmMethod(message, _element, _submitter) {
|
708
|
-
return Promise.resolve(confirm(message));
|
709
|
-
}
|
710
708
|
get method() {
|
711
709
|
var _a;
|
712
710
|
const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
|
@@ -732,8 +730,8 @@ class FormSubmission {
|
|
732
730
|
var _a;
|
733
731
|
return formEnctypeFromString(((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formenctype")) || this.formElement.enctype);
|
734
732
|
}
|
735
|
-
get
|
736
|
-
return this.fetchRequest.
|
733
|
+
get isSafe() {
|
734
|
+
return this.fetchRequest.isSafe;
|
737
735
|
}
|
738
736
|
get stringFormData() {
|
739
737
|
return [ ...this.formData ].reduce(((entries, [name, value]) => entries.concat(typeof value == "string" ? [ [ name, value ] ] : [])), []);
|
@@ -761,7 +759,7 @@ class FormSubmission {
|
|
761
759
|
}
|
762
760
|
}
|
763
761
|
prepareRequest(request) {
|
764
|
-
if (!request.
|
762
|
+
if (!request.isSafe) {
|
765
763
|
const token = getCookieValue(getMetaContent("csrf-param")) || getMetaContent("csrf-token");
|
766
764
|
if (token) {
|
767
765
|
request.headers["X-CSRF-Token"] = token;
|
@@ -775,6 +773,7 @@ class FormSubmission {
|
|
775
773
|
var _a;
|
776
774
|
this.state = FormSubmissionState.waiting;
|
777
775
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
|
776
|
+
this.setSubmitsWith();
|
778
777
|
dispatch("turbo:submit-start", {
|
779
778
|
target: this.formElement,
|
780
779
|
detail: {
|
@@ -822,6 +821,7 @@ class FormSubmission {
|
|
822
821
|
var _a;
|
823
822
|
this.state = FormSubmissionState.stopped;
|
824
823
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
|
824
|
+
this.resetSubmitterText();
|
825
825
|
dispatch("turbo:submit-end", {
|
826
826
|
target: this.formElement,
|
827
827
|
detail: Object.assign({
|
@@ -830,11 +830,35 @@ class FormSubmission {
|
|
830
830
|
});
|
831
831
|
this.delegate.formSubmissionFinished(this);
|
832
832
|
}
|
833
|
+
setSubmitsWith() {
|
834
|
+
if (!this.submitter || !this.submitsWith) return;
|
835
|
+
if (this.submitter.matches("button")) {
|
836
|
+
this.originalSubmitText = this.submitter.innerHTML;
|
837
|
+
this.submitter.innerHTML = this.submitsWith;
|
838
|
+
} else if (this.submitter.matches("input")) {
|
839
|
+
const input = this.submitter;
|
840
|
+
this.originalSubmitText = input.value;
|
841
|
+
input.value = this.submitsWith;
|
842
|
+
}
|
843
|
+
}
|
844
|
+
resetSubmitterText() {
|
845
|
+
if (!this.submitter || !this.originalSubmitText) return;
|
846
|
+
if (this.submitter.matches("button")) {
|
847
|
+
this.submitter.innerHTML = this.originalSubmitText;
|
848
|
+
} else if (this.submitter.matches("input")) {
|
849
|
+
const input = this.submitter;
|
850
|
+
input.value = this.originalSubmitText;
|
851
|
+
}
|
852
|
+
}
|
833
853
|
requestMustRedirect(request) {
|
834
|
-
return !request.
|
854
|
+
return !request.isSafe && this.mustRedirect;
|
835
855
|
}
|
836
856
|
requestAcceptsTurboStreamResponse(request) {
|
837
|
-
return !request.
|
857
|
+
return !request.isSafe || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
|
858
|
+
}
|
859
|
+
get submitsWith() {
|
860
|
+
var _a;
|
861
|
+
return (_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-submits-with");
|
838
862
|
}
|
839
863
|
}
|
840
864
|
|
@@ -1076,8 +1100,8 @@ class View {
|
|
1076
1100
|
}
|
1077
1101
|
|
1078
1102
|
class FrameView extends View {
|
1079
|
-
|
1080
|
-
this.element.innerHTML = ""
|
1103
|
+
missing() {
|
1104
|
+
this.element.innerHTML = `<strong class="turbo-frame-error">Content missing</strong>`;
|
1081
1105
|
}
|
1082
1106
|
get snapshot() {
|
1083
1107
|
return new Snapshot(this.element);
|
@@ -1232,16 +1256,16 @@ class FormLinkClickObserver {
|
|
1232
1256
|
}
|
1233
1257
|
|
1234
1258
|
class Bardo {
|
1235
|
-
constructor(delegate, permanentElementMap) {
|
1236
|
-
this.delegate = delegate;
|
1237
|
-
this.permanentElementMap = permanentElementMap;
|
1238
|
-
}
|
1239
1259
|
static async preservingPermanentElements(delegate, permanentElementMap, callback) {
|
1240
1260
|
const bardo = new this(delegate, permanentElementMap);
|
1241
1261
|
bardo.enter();
|
1242
1262
|
await callback();
|
1243
1263
|
bardo.leave();
|
1244
1264
|
}
|
1265
|
+
constructor(delegate, permanentElementMap) {
|
1266
|
+
this.delegate = delegate;
|
1267
|
+
this.permanentElementMap = permanentElementMap;
|
1268
|
+
}
|
1245
1269
|
enter() {
|
1246
1270
|
for (const id in this.permanentElementMap) {
|
1247
1271
|
const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
|
@@ -1352,10 +1376,6 @@ function elementIsFocusable(element) {
|
|
1352
1376
|
}
|
1353
1377
|
|
1354
1378
|
class FrameRenderer extends Renderer {
|
1355
|
-
constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
1356
|
-
super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
|
1357
|
-
this.delegate = delegate;
|
1358
|
-
}
|
1359
1379
|
static renderElement(currentElement, newElement) {
|
1360
1380
|
var _a;
|
1361
1381
|
const destinationRange = document.createRange();
|
@@ -1368,6 +1388,10 @@ class FrameRenderer extends Renderer {
|
|
1368
1388
|
currentElement.appendChild(sourceRange.extractContents());
|
1369
1389
|
}
|
1370
1390
|
}
|
1391
|
+
constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
1392
|
+
super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
|
1393
|
+
this.delegate = delegate;
|
1394
|
+
}
|
1371
1395
|
get shouldRender() {
|
1372
1396
|
return true;
|
1373
1397
|
}
|
@@ -1429,18 +1453,6 @@ function readScrollBehavior(value, defaultValue) {
|
|
1429
1453
|
}
|
1430
1454
|
|
1431
1455
|
class ProgressBar {
|
1432
|
-
constructor() {
|
1433
|
-
this.hiding = false;
|
1434
|
-
this.value = 0;
|
1435
|
-
this.visible = false;
|
1436
|
-
this.trickle = () => {
|
1437
|
-
this.setValue(this.value + Math.random() / 100);
|
1438
|
-
};
|
1439
|
-
this.stylesheetElement = this.createStylesheetElement();
|
1440
|
-
this.progressElement = this.createProgressElement();
|
1441
|
-
this.installStylesheetElement();
|
1442
|
-
this.setValue(0);
|
1443
|
-
}
|
1444
1456
|
static get defaultCSS() {
|
1445
1457
|
return unindent`
|
1446
1458
|
.turbo-progress-bar {
|
@@ -1458,6 +1470,18 @@ class ProgressBar {
|
|
1458
1470
|
}
|
1459
1471
|
`;
|
1460
1472
|
}
|
1473
|
+
constructor() {
|
1474
|
+
this.hiding = false;
|
1475
|
+
this.value = 0;
|
1476
|
+
this.visible = false;
|
1477
|
+
this.trickle = () => {
|
1478
|
+
this.setValue(this.value + Math.random() / 100);
|
1479
|
+
};
|
1480
|
+
this.stylesheetElement = this.createStylesheetElement();
|
1481
|
+
this.progressElement = this.createProgressElement();
|
1482
|
+
this.installStylesheetElement();
|
1483
|
+
this.setValue(0);
|
1484
|
+
}
|
1461
1485
|
show() {
|
1462
1486
|
if (!this.visible) {
|
1463
1487
|
this.visible = true;
|
@@ -1626,10 +1650,6 @@ function elementWithoutNonce(element) {
|
|
1626
1650
|
}
|
1627
1651
|
|
1628
1652
|
class PageSnapshot extends Snapshot {
|
1629
|
-
constructor(element, headSnapshot) {
|
1630
|
-
super(element);
|
1631
|
-
this.headSnapshot = headSnapshot;
|
1632
|
-
}
|
1633
1653
|
static fromHTMLString(html = "") {
|
1634
1654
|
return this.fromDocument(parseHTMLDocument(html));
|
1635
1655
|
}
|
@@ -1639,6 +1659,10 @@ class PageSnapshot extends Snapshot {
|
|
1639
1659
|
static fromDocument({head: head, body: body}) {
|
1640
1660
|
return new this(body, new HeadSnapshot(head));
|
1641
1661
|
}
|
1662
|
+
constructor(element, headSnapshot) {
|
1663
|
+
super(element);
|
1664
|
+
this.headSnapshot = headSnapshot;
|
1665
|
+
}
|
1642
1666
|
clone() {
|
1643
1667
|
const clonedElement = this.element.cloneNode(true);
|
1644
1668
|
const selectElements = this.element.querySelectorAll("select");
|
@@ -2143,10 +2167,11 @@ class BrowserAdapter {
|
|
2143
2167
|
|
2144
2168
|
class CacheObserver {
|
2145
2169
|
constructor() {
|
2170
|
+
this.selector = "[data-turbo-temporary]";
|
2171
|
+
this.deprecatedSelector = "[data-turbo-cache=false]";
|
2146
2172
|
this.started = false;
|
2147
|
-
this.
|
2148
|
-
const
|
2149
|
-
for (const element of staleElements) {
|
2173
|
+
this.removeTemporaryElements = _event => {
|
2174
|
+
for (const element of this.temporaryElements) {
|
2150
2175
|
element.remove();
|
2151
2176
|
}
|
2152
2177
|
};
|
@@ -2154,15 +2179,25 @@ class CacheObserver {
|
|
2154
2179
|
start() {
|
2155
2180
|
if (!this.started) {
|
2156
2181
|
this.started = true;
|
2157
|
-
addEventListener("turbo:before-cache", this.
|
2182
|
+
addEventListener("turbo:before-cache", this.removeTemporaryElements, false);
|
2158
2183
|
}
|
2159
2184
|
}
|
2160
2185
|
stop() {
|
2161
2186
|
if (this.started) {
|
2162
2187
|
this.started = false;
|
2163
|
-
removeEventListener("turbo:before-cache", this.
|
2188
|
+
removeEventListener("turbo:before-cache", this.removeTemporaryElements, false);
|
2164
2189
|
}
|
2165
2190
|
}
|
2191
|
+
get temporaryElements() {
|
2192
|
+
return [ ...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation ];
|
2193
|
+
}
|
2194
|
+
get temporaryElementsWithDeprecation() {
|
2195
|
+
const elements = document.querySelectorAll(this.deprecatedSelector);
|
2196
|
+
if (elements.length) {
|
2197
|
+
console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`);
|
2198
|
+
}
|
2199
|
+
return [ ...elements ];
|
2200
|
+
}
|
2166
2201
|
}
|
2167
2202
|
|
2168
2203
|
class FrameRedirector {
|
@@ -2361,7 +2396,7 @@ class Navigator {
|
|
2361
2396
|
if (formSubmission == this.formSubmission) {
|
2362
2397
|
const responseHTML = await fetchResponse.responseHTML;
|
2363
2398
|
if (responseHTML) {
|
2364
|
-
const shouldCacheSnapshot = formSubmission.
|
2399
|
+
const shouldCacheSnapshot = formSubmission.isSafe;
|
2365
2400
|
if (!shouldCacheSnapshot) {
|
2366
2401
|
this.view.clearSnapshotCache();
|
2367
2402
|
}
|
@@ -3362,6 +3397,8 @@ var Turbo = Object.freeze({
|
|
3362
3397
|
StreamActions: StreamActions
|
3363
3398
|
});
|
3364
3399
|
|
3400
|
+
class TurboFrameMissingError extends Error {}
|
3401
|
+
|
3365
3402
|
class FrameController {
|
3366
3403
|
constructor(element) {
|
3367
3404
|
this.fetchResponseLoaded = _fetchResponse => {};
|
@@ -3458,26 +3495,14 @@ class FrameController {
|
|
3458
3495
|
try {
|
3459
3496
|
const html = await fetchResponse.responseHTML;
|
3460
3497
|
if (html) {
|
3461
|
-
const
|
3462
|
-
const
|
3463
|
-
if (
|
3464
|
-
|
3465
|
-
|
3466
|
-
|
3467
|
-
this.changeHistory();
|
3468
|
-
await this.view.render(renderer);
|
3469
|
-
this.complete = true;
|
3470
|
-
session.frameRendered(fetchResponse, this.element);
|
3471
|
-
session.frameLoaded(this.element);
|
3472
|
-
this.fetchResponseLoaded(fetchResponse);
|
3473
|
-
} else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
|
3474
|
-
console.warn(`A matching frame for #${this.element.id} was missing from the response, transforming into full-page Visit.`);
|
3475
|
-
this.visitResponse(fetchResponse.response);
|
3498
|
+
const document = parseHTMLDocument(html);
|
3499
|
+
const pageSnapshot = PageSnapshot.fromDocument(document);
|
3500
|
+
if (pageSnapshot.isVisitable) {
|
3501
|
+
await this.loadFrameResponse(fetchResponse, document);
|
3502
|
+
} else {
|
3503
|
+
await this.handleUnvisitableFrameResponse(fetchResponse);
|
3476
3504
|
}
|
3477
3505
|
}
|
3478
|
-
} catch (error) {
|
3479
|
-
console.error(error);
|
3480
|
-
this.view.invalidate();
|
3481
3506
|
} finally {
|
3482
3507
|
this.fetchResponseLoaded = () => {};
|
3483
3508
|
}
|
@@ -3529,7 +3554,6 @@ class FrameController {
|
|
3529
3554
|
this.resolveVisitPromise();
|
3530
3555
|
}
|
3531
3556
|
async requestFailedWithResponse(request, response) {
|
3532
|
-
console.error(response);
|
3533
3557
|
await this.loadResponse(response);
|
3534
3558
|
this.resolveVisitPromise();
|
3535
3559
|
}
|
@@ -3547,9 +3571,13 @@ class FrameController {
|
|
3547
3571
|
const frame = this.findFrameElement(formSubmission.formElement, formSubmission.submitter);
|
3548
3572
|
frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
|
3549
3573
|
frame.delegate.loadResponse(response);
|
3574
|
+
if (!formSubmission.isSafe) {
|
3575
|
+
session.clearCache();
|
3576
|
+
}
|
3550
3577
|
}
|
3551
3578
|
formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
|
3552
3579
|
this.element.delegate.loadResponse(fetchResponse);
|
3580
|
+
session.clearCache();
|
3553
3581
|
}
|
3554
3582
|
formSubmissionErrored(formSubmission, error) {
|
3555
3583
|
console.error(error);
|
@@ -3579,6 +3607,22 @@ class FrameController {
|
|
3579
3607
|
willRenderFrame(currentElement, _newElement) {
|
3580
3608
|
this.previousFrameElement = currentElement.cloneNode(true);
|
3581
3609
|
}
|
3610
|
+
async loadFrameResponse(fetchResponse, document) {
|
3611
|
+
const newFrameElement = await this.extractForeignFrameElement(document.body);
|
3612
|
+
if (newFrameElement) {
|
3613
|
+
const snapshot = new Snapshot(newFrameElement);
|
3614
|
+
const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
|
3615
|
+
if (this.view.renderPromise) await this.view.renderPromise;
|
3616
|
+
this.changeHistory();
|
3617
|
+
await this.view.render(renderer);
|
3618
|
+
this.complete = true;
|
3619
|
+
session.frameRendered(fetchResponse, this.element);
|
3620
|
+
session.frameLoaded(this.element);
|
3621
|
+
this.fetchResponseLoaded(fetchResponse);
|
3622
|
+
} else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
|
3623
|
+
this.handleFrameMissingFromResponse(fetchResponse);
|
3624
|
+
}
|
3625
|
+
}
|
3582
3626
|
async visit(url) {
|
3583
3627
|
var _a;
|
3584
3628
|
const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams, this.element);
|
@@ -3634,6 +3678,10 @@ class FrameController {
|
|
3634
3678
|
session.history.update(method, expandURL(this.element.src || ""), this.restorationIdentifier);
|
3635
3679
|
}
|
3636
3680
|
}
|
3681
|
+
async handleUnvisitableFrameResponse(fetchResponse) {
|
3682
|
+
console.warn(`The response (${fetchResponse.statusCode}) from <turbo-frame id="${this.element.id}"> is performing a full page visit due to turbo-visit-control.`);
|
3683
|
+
await this.visitResponse(fetchResponse.response);
|
3684
|
+
}
|
3637
3685
|
willHandleFrameMissingFromResponse(fetchResponse) {
|
3638
3686
|
this.element.setAttribute("complete", "");
|
3639
3687
|
const response = fetchResponse.response;
|
@@ -3654,6 +3702,14 @@ class FrameController {
|
|
3654
3702
|
});
|
3655
3703
|
return !event.defaultPrevented;
|
3656
3704
|
}
|
3705
|
+
handleFrameMissingFromResponse(fetchResponse) {
|
3706
|
+
this.view.missing();
|
3707
|
+
this.throwFrameMissingError(fetchResponse);
|
3708
|
+
}
|
3709
|
+
throwFrameMissingError(fetchResponse) {
|
3710
|
+
const message = `The response (${fetchResponse.statusCode}) did not contain the expected <turbo-frame id="${this.element.id}"> and will be ignored. To perform a full page visit instead, set turbo-visit-control to reload.`;
|
3711
|
+
throw new TurboFrameMissingError(message);
|
3712
|
+
}
|
3657
3713
|
async visitResponse(response) {
|
3658
3714
|
const wrapped = new FetchResponse(response);
|
3659
3715
|
const responseHTML = await wrapped.responseHTML;
|
@@ -4048,7 +4104,9 @@ class TurboCableStreamSourceElement extends HTMLElement {
|
|
4048
4104
|
async connectedCallback() {
|
4049
4105
|
connectStreamSource(this);
|
4050
4106
|
this.subscription = await subscribeTo(this.channel, {
|
4051
|
-
received: this.dispatchMessageEvent.bind(this)
|
4107
|
+
received: this.dispatchMessageEvent.bind(this),
|
4108
|
+
connected: this.subscriptionConnected.bind(this),
|
4109
|
+
disconnected: this.subscriptionDisconnected.bind(this)
|
4052
4110
|
});
|
4053
4111
|
}
|
4054
4112
|
disconnectedCallback() {
|
@@ -4061,6 +4119,12 @@ class TurboCableStreamSourceElement extends HTMLElement {
|
|
4061
4119
|
});
|
4062
4120
|
return this.dispatchEvent(event);
|
4063
4121
|
}
|
4122
|
+
subscriptionConnected() {
|
4123
|
+
this.setAttribute("connected", "");
|
4124
|
+
}
|
4125
|
+
subscriptionDisconnected() {
|
4126
|
+
this.removeAttribute("connected");
|
4127
|
+
}
|
4064
4128
|
get channel() {
|
4065
4129
|
const channel = this.getAttribute("channel");
|
4066
4130
|
const signed_stream_name = this.getAttribute("signed-stream-name");
|