@hotwired/turbo 7.1.0-rc.2 → 7.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/turbo.es2017-esm.js +730 -391
- package/dist/turbo.es2017-umd.js +735 -392
- package/dist/types/core/bardo.d.ts +7 -2
- package/dist/types/core/cache.d.ts +10 -0
- package/dist/types/core/drive/error_renderer.d.ts +1 -0
- package/dist/types/core/drive/form_submission.d.ts +13 -4
- package/dist/types/core/drive/history.d.ts +1 -1
- package/dist/types/core/drive/navigator.d.ts +1 -0
- package/dist/types/core/drive/page_renderer.d.ts +3 -0
- package/dist/types/core/drive/page_view.d.ts +8 -5
- package/dist/types/core/drive/preloader.d.ts +14 -0
- package/dist/types/core/drive/progress_bar.d.ts +1 -0
- package/dist/types/core/drive/visit.d.ts +3 -3
- package/dist/types/core/frames/frame_controller.d.ts +26 -16
- package/dist/types/core/frames/frame_redirector.d.ts +1 -1
- package/dist/types/core/frames/frame_renderer.d.ts +8 -1
- package/dist/types/core/frames/frame_view.d.ts +2 -1
- package/dist/types/core/frames/link_interceptor.d.ts +1 -1
- package/dist/types/core/index.d.ts +11 -2
- package/dist/types/core/native/adapter.d.ts +2 -1
- package/dist/types/core/native/browser_adapter.d.ts +17 -8
- package/dist/types/core/renderer.d.ts +12 -4
- package/dist/types/core/session.d.ts +68 -16
- package/dist/types/core/snapshot.d.ts +2 -1
- package/dist/types/core/view.d.ts +12 -6
- package/dist/types/elements/frame_element.d.ts +5 -1
- package/dist/types/elements/stream_element.d.ts +1 -0
- package/dist/types/elements/stream_source_element.d.ts +7 -0
- package/dist/types/http/fetch_request.d.ts +8 -0
- package/dist/types/observers/cache_observer.d.ts +1 -1
- package/dist/types/observers/form_link_interceptor.d.ts +14 -0
- package/dist/types/observers/link_click_observer.d.ts +2 -2
- package/dist/types/polyfills/submit-event.d.ts +1 -7
- package/dist/types/tests/functional/async_script_tests.d.ts +1 -6
- package/dist/types/tests/functional/autofocus_tests.d.ts +1 -9
- package/dist/types/tests/functional/cache_observer_tests.d.ts +1 -5
- package/dist/types/tests/functional/drive_disabled_tests.d.ts +1 -9
- package/dist/types/tests/functional/drive_tests.d.ts +1 -8
- package/dist/types/tests/functional/form_submission_tests.d.ts +1 -79
- package/dist/types/tests/functional/frame_navigation_tests.d.ts +1 -7
- package/dist/types/tests/functional/frame_tests.d.ts +1 -51
- package/dist/types/tests/functional/import_tests.d.ts +1 -4
- package/dist/types/tests/functional/loading_tests.d.ts +1 -13
- package/dist/types/tests/functional/navigation_tests.d.ts +1 -37
- package/dist/types/tests/functional/pausable_rendering_tests.d.ts +1 -6
- package/dist/types/tests/functional/pausable_requests_tests.d.ts +1 -6
- package/dist/types/tests/functional/preloader_tests.d.ts +1 -0
- package/dist/types/tests/functional/rendering_tests.d.ts +1 -35
- package/dist/types/tests/functional/scroll_restoration_tests.d.ts +1 -6
- package/dist/types/tests/functional/stream_tests.d.ts +1 -6
- package/dist/types/tests/functional/visit_tests.d.ts +1 -13
- package/dist/types/tests/helpers/page.d.ts +44 -0
- package/dist/types/tests/unit/deprecated_adapter_support_test.d.ts +10 -10
- package/dist/types/util.d.ts +6 -3
- package/package.json +22 -8
- package/CHANGELOG.md +0 -3
- package/dist/types/tests/functional/index.d.ts +0 -17
- package/dist/types/tests/helpers/functional_test_case.d.ts +0 -44
- package/dist/types/tests/helpers/remote_channel.d.ts +0 -10
- package/dist/types/tests/helpers/turbo_drive_test_case.d.ts +0 -21
package/dist/turbo.es2017-esm.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Turbo 7.
|
|
3
|
-
Copyright ©
|
|
2
|
+
Turbo 7.2.0-beta.1
|
|
3
|
+
Copyright © 2022 Basecamp, LLC
|
|
4
4
|
*/
|
|
5
5
|
(function () {
|
|
6
|
-
if (window.Reflect === undefined ||
|
|
6
|
+
if (window.Reflect === undefined ||
|
|
7
|
+
window.customElements === undefined ||
|
|
7
8
|
window.customElements.polyfillWrapFlushCallback) {
|
|
8
9
|
return;
|
|
9
10
|
}
|
|
10
11
|
const BuiltInHTMLElement = HTMLElement;
|
|
11
12
|
const wrapperForTheName = {
|
|
12
|
-
|
|
13
|
+
HTMLElement: function HTMLElement() {
|
|
13
14
|
return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
|
|
14
|
-
}
|
|
15
|
+
},
|
|
15
16
|
};
|
|
16
|
-
window.HTMLElement =
|
|
17
|
-
wrapperForTheName['HTMLElement'];
|
|
17
|
+
window.HTMLElement = wrapperForTheName["HTMLElement"];
|
|
18
18
|
HTMLElement.prototype = BuiltInHTMLElement.prototype;
|
|
19
19
|
HTMLElement.prototype.constructor = HTMLElement;
|
|
20
20
|
Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
|
|
@@ -72,7 +72,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
72
72
|
}
|
|
73
73
|
})(HTMLFormElement.prototype);
|
|
74
74
|
|
|
75
|
-
const submittersByForm = new WeakMap;
|
|
75
|
+
const submittersByForm = new WeakMap();
|
|
76
76
|
function findSubmitterFromClickTarget(target) {
|
|
77
77
|
const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
78
78
|
const candidate = element ? element.closest("input, button") : null;
|
|
@@ -103,7 +103,7 @@ function clickCaptured(event) {
|
|
|
103
103
|
if (this.type == "submit" && this.target instanceof HTMLFormElement) {
|
|
104
104
|
return submittersByForm.get(this.target);
|
|
105
105
|
}
|
|
106
|
-
}
|
|
106
|
+
},
|
|
107
107
|
});
|
|
108
108
|
})();
|
|
109
109
|
|
|
@@ -119,7 +119,7 @@ class FrameElement extends HTMLElement {
|
|
|
119
119
|
this.delegate = new FrameElement.delegateConstructor(this);
|
|
120
120
|
}
|
|
121
121
|
static get observedAttributes() {
|
|
122
|
-
return ["disabled", "loading", "src"];
|
|
122
|
+
return ["disabled", "complete", "loading", "src"];
|
|
123
123
|
}
|
|
124
124
|
connectedCallback() {
|
|
125
125
|
this.delegate.connect();
|
|
@@ -129,6 +129,7 @@ class FrameElement extends HTMLElement {
|
|
|
129
129
|
}
|
|
130
130
|
reload() {
|
|
131
131
|
const { src } = this;
|
|
132
|
+
this.removeAttribute("complete");
|
|
132
133
|
this.src = null;
|
|
133
134
|
this.src = src;
|
|
134
135
|
}
|
|
@@ -136,6 +137,9 @@ class FrameElement extends HTMLElement {
|
|
|
136
137
|
if (name == "loading") {
|
|
137
138
|
this.delegate.loadingStyleChanged();
|
|
138
139
|
}
|
|
140
|
+
else if (name == "complete") {
|
|
141
|
+
this.delegate.completeChanged();
|
|
142
|
+
}
|
|
139
143
|
else if (name == "src") {
|
|
140
144
|
this.delegate.sourceURLChanged();
|
|
141
145
|
}
|
|
@@ -200,8 +204,10 @@ class FrameElement extends HTMLElement {
|
|
|
200
204
|
}
|
|
201
205
|
function frameLoadingStyleFromString(style) {
|
|
202
206
|
switch (style.toLowerCase()) {
|
|
203
|
-
case "lazy":
|
|
204
|
-
|
|
207
|
+
case "lazy":
|
|
208
|
+
return FrameLoadingStyle.lazy;
|
|
209
|
+
default:
|
|
210
|
+
return FrameLoadingStyle.eager;
|
|
205
211
|
}
|
|
206
212
|
}
|
|
207
213
|
|
|
@@ -213,7 +219,7 @@ function getAnchor(url) {
|
|
|
213
219
|
if (url.hash) {
|
|
214
220
|
return url.hash.slice(1);
|
|
215
221
|
}
|
|
216
|
-
else if (anchorMatch = url.href.match(/#(.*)$/)) {
|
|
222
|
+
else if ((anchorMatch = url.href.match(/#(.*)$/))) {
|
|
217
223
|
return anchorMatch[1];
|
|
218
224
|
}
|
|
219
225
|
}
|
|
@@ -225,7 +231,7 @@ function getExtension(url) {
|
|
|
225
231
|
return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
|
|
226
232
|
}
|
|
227
233
|
function isHTML(url) {
|
|
228
|
-
return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml))$/);
|
|
234
|
+
return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
|
|
229
235
|
}
|
|
230
236
|
function isPrefixedBy(baseURL, url) {
|
|
231
237
|
const prefix = getPrefix(url);
|
|
@@ -236,9 +242,7 @@ function locationIsVisitable(location, rootLocation) {
|
|
|
236
242
|
}
|
|
237
243
|
function getRequestURL(url) {
|
|
238
244
|
const anchor = getAnchor(url);
|
|
239
|
-
return anchor != null
|
|
240
|
-
? url.href.slice(0, -(anchor.length + 1))
|
|
241
|
-
: url.href;
|
|
245
|
+
return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
|
|
242
246
|
}
|
|
243
247
|
function toCacheKey(url) {
|
|
244
248
|
return getRequestURL(url);
|
|
@@ -307,7 +311,11 @@ class FetchResponse {
|
|
|
307
311
|
}
|
|
308
312
|
|
|
309
313
|
function dispatch(eventName, { target, cancelable, detail } = {}) {
|
|
310
|
-
const event = new CustomEvent(eventName, {
|
|
314
|
+
const event = new CustomEvent(eventName, {
|
|
315
|
+
cancelable,
|
|
316
|
+
bubbles: true,
|
|
317
|
+
detail,
|
|
318
|
+
});
|
|
311
319
|
if (target && target.isConnected) {
|
|
312
320
|
target.dispatchEvent(event);
|
|
313
321
|
}
|
|
@@ -317,10 +325,10 @@ function dispatch(eventName, { target, cancelable, detail } = {}) {
|
|
|
317
325
|
return event;
|
|
318
326
|
}
|
|
319
327
|
function nextAnimationFrame() {
|
|
320
|
-
return new Promise(resolve => requestAnimationFrame(() => resolve()));
|
|
328
|
+
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
321
329
|
}
|
|
322
330
|
function nextEventLoopTick() {
|
|
323
|
-
return new Promise(resolve => setTimeout(() => resolve(), 0));
|
|
331
|
+
return new Promise((resolve) => setTimeout(() => resolve(), 0));
|
|
324
332
|
}
|
|
325
333
|
function nextMicrotask() {
|
|
326
334
|
return Promise.resolve();
|
|
@@ -332,7 +340,7 @@ function unindent(strings, ...values) {
|
|
|
332
340
|
const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
|
|
333
341
|
const match = lines[0].match(/^\s+/);
|
|
334
342
|
const indent = match ? match[0].length : 0;
|
|
335
|
-
return lines.map(line => line.slice(indent)).join("\n");
|
|
343
|
+
return lines.map((line) => line.slice(indent)).join("\n");
|
|
336
344
|
}
|
|
337
345
|
function interpolate(strings, values) {
|
|
338
346
|
return strings.reduce((result, string, i) => {
|
|
@@ -341,7 +349,8 @@ function interpolate(strings, values) {
|
|
|
341
349
|
}, "");
|
|
342
350
|
}
|
|
343
351
|
function uuid() {
|
|
344
|
-
return Array.
|
|
352
|
+
return Array.from({ length: 36 })
|
|
353
|
+
.map((_, i) => {
|
|
345
354
|
if (i == 8 || i == 13 || i == 18 || i == 23) {
|
|
346
355
|
return "-";
|
|
347
356
|
}
|
|
@@ -354,10 +363,11 @@ function uuid() {
|
|
|
354
363
|
else {
|
|
355
364
|
return Math.floor(Math.random() * 15).toString(16);
|
|
356
365
|
}
|
|
357
|
-
})
|
|
366
|
+
})
|
|
367
|
+
.join("");
|
|
358
368
|
}
|
|
359
369
|
function getAttribute(attributeName, ...elements) {
|
|
360
|
-
for (const value of elements.map(element => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
|
|
370
|
+
for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
|
|
361
371
|
if (typeof value == "string")
|
|
362
372
|
return value;
|
|
363
373
|
}
|
|
@@ -379,6 +389,23 @@ function clearBusyState(...elements) {
|
|
|
379
389
|
element.removeAttribute("aria-busy");
|
|
380
390
|
}
|
|
381
391
|
}
|
|
392
|
+
function getMetaElement(name) {
|
|
393
|
+
return document.querySelector(`meta[name="${name}"]`);
|
|
394
|
+
}
|
|
395
|
+
function getMetaContent(name) {
|
|
396
|
+
const element = getMetaElement(name);
|
|
397
|
+
return element && element.content;
|
|
398
|
+
}
|
|
399
|
+
function setMetaContent(name, content) {
|
|
400
|
+
let element = getMetaElement(name);
|
|
401
|
+
if (!element) {
|
|
402
|
+
element = document.createElement("meta");
|
|
403
|
+
element.setAttribute("name", name);
|
|
404
|
+
document.head.appendChild(element);
|
|
405
|
+
}
|
|
406
|
+
element.setAttribute("content", content);
|
|
407
|
+
return element;
|
|
408
|
+
}
|
|
382
409
|
|
|
383
410
|
var FetchMethod;
|
|
384
411
|
(function (FetchMethod) {
|
|
@@ -390,27 +417,27 @@ var FetchMethod;
|
|
|
390
417
|
})(FetchMethod || (FetchMethod = {}));
|
|
391
418
|
function fetchMethodFromString(method) {
|
|
392
419
|
switch (method.toLowerCase()) {
|
|
393
|
-
case "get":
|
|
394
|
-
|
|
395
|
-
case "
|
|
396
|
-
|
|
397
|
-
case "
|
|
420
|
+
case "get":
|
|
421
|
+
return FetchMethod.get;
|
|
422
|
+
case "post":
|
|
423
|
+
return FetchMethod.post;
|
|
424
|
+
case "put":
|
|
425
|
+
return FetchMethod.put;
|
|
426
|
+
case "patch":
|
|
427
|
+
return FetchMethod.patch;
|
|
428
|
+
case "delete":
|
|
429
|
+
return FetchMethod.delete;
|
|
398
430
|
}
|
|
399
431
|
}
|
|
400
432
|
class FetchRequest {
|
|
401
|
-
constructor(delegate, method, location, body = new URLSearchParams, target = null) {
|
|
402
|
-
this.abortController = new AbortController;
|
|
403
|
-
this.resolveRequestPromise = (
|
|
433
|
+
constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
|
|
434
|
+
this.abortController = new AbortController();
|
|
435
|
+
this.resolveRequestPromise = (_value) => { };
|
|
404
436
|
this.delegate = delegate;
|
|
405
437
|
this.method = method;
|
|
406
438
|
this.headers = this.defaultHeaders;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
this.body = body;
|
|
412
|
-
this.url = location;
|
|
413
|
-
}
|
|
439
|
+
this.body = body;
|
|
440
|
+
this.url = location;
|
|
414
441
|
this.target = target;
|
|
415
442
|
}
|
|
416
443
|
get location() {
|
|
@@ -436,7 +463,7 @@ class FetchRequest {
|
|
|
436
463
|
return await this.receive(response);
|
|
437
464
|
}
|
|
438
465
|
catch (error) {
|
|
439
|
-
if (error.name !==
|
|
466
|
+
if (error.name !== "AbortError") {
|
|
440
467
|
this.delegate.requestErrored(this, error);
|
|
441
468
|
throw error;
|
|
442
469
|
}
|
|
@@ -447,7 +474,11 @@ class FetchRequest {
|
|
|
447
474
|
}
|
|
448
475
|
async receive(response) {
|
|
449
476
|
const fetchResponse = new FetchResponse(response);
|
|
450
|
-
const event = dispatch("turbo:before-fetch-response", {
|
|
477
|
+
const event = dispatch("turbo:before-fetch-response", {
|
|
478
|
+
cancelable: true,
|
|
479
|
+
detail: { fetchResponse },
|
|
480
|
+
target: this.target,
|
|
481
|
+
});
|
|
451
482
|
if (event.defaultPrevented) {
|
|
452
483
|
this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
|
|
453
484
|
}
|
|
@@ -466,14 +497,14 @@ class FetchRequest {
|
|
|
466
497
|
credentials: "same-origin",
|
|
467
498
|
headers: this.headers,
|
|
468
499
|
redirect: "follow",
|
|
469
|
-
body: this.body,
|
|
500
|
+
body: this.isIdempotent ? null : this.body,
|
|
470
501
|
signal: this.abortSignal,
|
|
471
|
-
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
|
|
502
|
+
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
|
|
472
503
|
};
|
|
473
504
|
}
|
|
474
505
|
get defaultHeaders() {
|
|
475
506
|
return {
|
|
476
|
-
|
|
507
|
+
Accept: "text/html, application/xhtml+xml",
|
|
477
508
|
};
|
|
478
509
|
}
|
|
479
510
|
get isIdempotent() {
|
|
@@ -483,40 +514,25 @@ class FetchRequest {
|
|
|
483
514
|
return this.abortController.signal;
|
|
484
515
|
}
|
|
485
516
|
async allowRequestToBeIntercepted(fetchOptions) {
|
|
486
|
-
const requestInterception = new Promise(resolve => this.resolveRequestPromise = resolve);
|
|
517
|
+
const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
|
|
487
518
|
const event = dispatch("turbo:before-fetch-request", {
|
|
488
519
|
cancelable: true,
|
|
489
520
|
detail: {
|
|
490
521
|
fetchOptions,
|
|
491
|
-
url: this.url
|
|
492
|
-
resume: this.resolveRequestPromise
|
|
522
|
+
url: this.url,
|
|
523
|
+
resume: this.resolveRequestPromise,
|
|
493
524
|
},
|
|
494
|
-
target: this.target
|
|
525
|
+
target: this.target,
|
|
495
526
|
});
|
|
496
527
|
if (event.defaultPrevented)
|
|
497
528
|
await requestInterception;
|
|
498
529
|
}
|
|
499
530
|
}
|
|
500
|
-
function mergeFormDataEntries(url, entries) {
|
|
501
|
-
const currentSearchParams = new URLSearchParams(url.search);
|
|
502
|
-
for (const [name, value] of entries) {
|
|
503
|
-
if (value instanceof File)
|
|
504
|
-
continue;
|
|
505
|
-
if (currentSearchParams.has(name)) {
|
|
506
|
-
currentSearchParams.delete(name);
|
|
507
|
-
url.searchParams.set(name, value);
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
url.searchParams.append(name, value);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
return url;
|
|
514
|
-
}
|
|
515
531
|
|
|
516
532
|
class AppearanceObserver {
|
|
517
533
|
constructor(delegate, element) {
|
|
518
534
|
this.started = false;
|
|
519
|
-
this.intersect = entries => {
|
|
535
|
+
this.intersect = (entries) => {
|
|
520
536
|
const lastEntry = entries.slice(-1)[0];
|
|
521
537
|
if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
|
|
522
538
|
this.delegate.elementAppearedInViewport(this.element);
|
|
@@ -593,9 +609,12 @@ var FormEnctype;
|
|
|
593
609
|
})(FormEnctype || (FormEnctype = {}));
|
|
594
610
|
function formEnctypeFromString(encoding) {
|
|
595
611
|
switch (encoding.toLowerCase()) {
|
|
596
|
-
case FormEnctype.multipart:
|
|
597
|
-
|
|
598
|
-
|
|
612
|
+
case FormEnctype.multipart:
|
|
613
|
+
return FormEnctype.multipart;
|
|
614
|
+
case FormEnctype.plain:
|
|
615
|
+
return FormEnctype.plain;
|
|
616
|
+
default:
|
|
617
|
+
return FormEnctype.urlEncoded;
|
|
599
618
|
}
|
|
600
619
|
}
|
|
601
620
|
class FormSubmission {
|
|
@@ -605,11 +624,15 @@ class FormSubmission {
|
|
|
605
624
|
this.formElement = formElement;
|
|
606
625
|
this.submitter = submitter;
|
|
607
626
|
this.formData = buildFormData(formElement, submitter);
|
|
627
|
+
this.location = expandURL(this.action);
|
|
628
|
+
if (this.method == FetchMethod.get) {
|
|
629
|
+
mergeFormDataEntries(this.location, [...this.body.entries()]);
|
|
630
|
+
}
|
|
608
631
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
609
632
|
this.mustRedirect = mustRedirect;
|
|
610
633
|
}
|
|
611
|
-
static confirmMethod(message,
|
|
612
|
-
return confirm(message);
|
|
634
|
+
static confirmMethod(message, _element) {
|
|
635
|
+
return Promise.resolve(confirm(message));
|
|
613
636
|
}
|
|
614
637
|
get method() {
|
|
615
638
|
var _a;
|
|
@@ -618,11 +641,13 @@ class FormSubmission {
|
|
|
618
641
|
}
|
|
619
642
|
get action() {
|
|
620
643
|
var _a;
|
|
621
|
-
const formElementAction = typeof this.formElement.action ===
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
644
|
+
const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
|
|
645
|
+
if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
|
|
646
|
+
return this.submitter.getAttribute("formaction") || "";
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
return this.formElement.getAttribute("action") || formElementAction || "";
|
|
650
|
+
}
|
|
626
651
|
}
|
|
627
652
|
get body() {
|
|
628
653
|
if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
|
|
@@ -645,7 +670,8 @@ class FormSubmission {
|
|
|
645
670
|
}, []);
|
|
646
671
|
}
|
|
647
672
|
get confirmationMessage() {
|
|
648
|
-
|
|
673
|
+
var _a;
|
|
674
|
+
return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-confirm")) || this.formElement.getAttribute("data-turbo-confirm");
|
|
649
675
|
}
|
|
650
676
|
get needsConfirmation() {
|
|
651
677
|
return this.confirmationMessage !== null;
|
|
@@ -653,7 +679,7 @@ class FormSubmission {
|
|
|
653
679
|
async start() {
|
|
654
680
|
const { initialized, requesting } = FormSubmissionState;
|
|
655
681
|
if (this.needsConfirmation) {
|
|
656
|
-
const answer = FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
|
|
682
|
+
const answer = await FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
|
|
657
683
|
if (!answer) {
|
|
658
684
|
return;
|
|
659
685
|
}
|
|
@@ -677,14 +703,19 @@ class FormSubmission {
|
|
|
677
703
|
if (token) {
|
|
678
704
|
headers["X-CSRF-Token"] = token;
|
|
679
705
|
}
|
|
706
|
+
}
|
|
707
|
+
if (this.requestAcceptsTurboStreamResponse(request)) {
|
|
680
708
|
headers["Accept"] = [StreamMessage.contentType, headers["Accept"]].join(", ");
|
|
681
709
|
}
|
|
682
710
|
}
|
|
683
|
-
requestStarted(
|
|
711
|
+
requestStarted(_request) {
|
|
684
712
|
var _a;
|
|
685
713
|
this.state = FormSubmissionState.waiting;
|
|
686
714
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
|
|
687
|
-
dispatch("turbo:submit-start", {
|
|
715
|
+
dispatch("turbo:submit-start", {
|
|
716
|
+
target: this.formElement,
|
|
717
|
+
detail: { formSubmission: this },
|
|
718
|
+
});
|
|
688
719
|
this.delegate.formSubmissionStarted(this);
|
|
689
720
|
}
|
|
690
721
|
requestPreventedHandlingResponse(request, response) {
|
|
@@ -712,16 +743,22 @@ class FormSubmission {
|
|
|
712
743
|
this.result = { success: false, error };
|
|
713
744
|
this.delegate.formSubmissionErrored(this, error);
|
|
714
745
|
}
|
|
715
|
-
requestFinished(
|
|
746
|
+
requestFinished(_request) {
|
|
716
747
|
var _a;
|
|
717
748
|
this.state = FormSubmissionState.stopped;
|
|
718
749
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
|
|
719
|
-
dispatch("turbo:submit-end", {
|
|
750
|
+
dispatch("turbo:submit-end", {
|
|
751
|
+
target: this.formElement,
|
|
752
|
+
detail: Object.assign({ formSubmission: this }, this.result),
|
|
753
|
+
});
|
|
720
754
|
this.delegate.formSubmissionFinished(this);
|
|
721
755
|
}
|
|
722
756
|
requestMustRedirect(request) {
|
|
723
757
|
return !request.isIdempotent && this.mustRedirect;
|
|
724
758
|
}
|
|
759
|
+
requestAcceptsTurboStreamResponse(request) {
|
|
760
|
+
return !request.isIdempotent || this.formElement.hasAttribute("data-turbo-stream");
|
|
761
|
+
}
|
|
725
762
|
}
|
|
726
763
|
function buildFormData(formElement, submitter) {
|
|
727
764
|
const formData = new FormData(formElement);
|
|
@@ -742,18 +779,27 @@ function getCookieValue(cookieName) {
|
|
|
742
779
|
}
|
|
743
780
|
}
|
|
744
781
|
}
|
|
745
|
-
function getMetaContent(name) {
|
|
746
|
-
const element = document.querySelector(`meta[name="${name}"]`);
|
|
747
|
-
return element && element.content;
|
|
748
|
-
}
|
|
749
782
|
function responseSucceededWithoutRedirect(response) {
|
|
750
783
|
return response.statusCode == 200 && !response.redirected;
|
|
751
784
|
}
|
|
785
|
+
function mergeFormDataEntries(url, entries) {
|
|
786
|
+
const searchParams = new URLSearchParams();
|
|
787
|
+
for (const [name, value] of entries) {
|
|
788
|
+
if (value instanceof File)
|
|
789
|
+
continue;
|
|
790
|
+
searchParams.append(name, value);
|
|
791
|
+
}
|
|
792
|
+
url.search = searchParams.toString();
|
|
793
|
+
return url;
|
|
794
|
+
}
|
|
752
795
|
|
|
753
796
|
class Snapshot {
|
|
754
797
|
constructor(element) {
|
|
755
798
|
this.element = element;
|
|
756
799
|
}
|
|
800
|
+
get activeElement() {
|
|
801
|
+
return this.element.ownerDocument.activeElement;
|
|
802
|
+
}
|
|
757
803
|
get children() {
|
|
758
804
|
return [...this.element.children];
|
|
759
805
|
}
|
|
@@ -792,7 +838,9 @@ class FormInterceptor {
|
|
|
792
838
|
constructor(delegate, element) {
|
|
793
839
|
this.submitBubbled = ((event) => {
|
|
794
840
|
const form = event.target;
|
|
795
|
-
if (
|
|
841
|
+
if (!event.defaultPrevented &&
|
|
842
|
+
form instanceof HTMLFormElement &&
|
|
843
|
+
form.closest("turbo-frame, html") == this.element) {
|
|
796
844
|
const submitter = event.submitter || undefined;
|
|
797
845
|
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
|
|
798
846
|
if (method != "dialog" && this.delegate.shouldInterceptFormSubmission(form, submitter)) {
|
|
@@ -815,8 +863,8 @@ class FormInterceptor {
|
|
|
815
863
|
|
|
816
864
|
class View {
|
|
817
865
|
constructor(delegate, element) {
|
|
818
|
-
this.resolveRenderPromise = (
|
|
819
|
-
this.resolveInterceptionPromise = (
|
|
866
|
+
this.resolveRenderPromise = (_value) => { };
|
|
867
|
+
this.resolveInterceptionPromise = (_value) => { };
|
|
820
868
|
this.delegate = delegate;
|
|
821
869
|
this.element = element;
|
|
822
870
|
}
|
|
@@ -861,15 +909,17 @@ class View {
|
|
|
861
909
|
const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
|
|
862
910
|
if (shouldRender) {
|
|
863
911
|
try {
|
|
864
|
-
this.renderPromise = new Promise(resolve => this.resolveRenderPromise = resolve);
|
|
912
|
+
this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
|
|
865
913
|
this.renderer = renderer;
|
|
866
914
|
this.prepareToRenderSnapshot(renderer);
|
|
867
|
-
const renderInterception = new Promise(resolve => this.resolveInterceptionPromise = resolve);
|
|
868
|
-
const
|
|
915
|
+
const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
|
|
916
|
+
const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
|
|
917
|
+
const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
|
|
869
918
|
if (!immediateRender)
|
|
870
919
|
await renderInterception;
|
|
871
920
|
await this.renderSnapshot(renderer);
|
|
872
921
|
this.delegate.viewRenderedSnapshot(snapshot, isPreview);
|
|
922
|
+
this.delegate.preloadOnLoadLinksForView(this.element);
|
|
873
923
|
this.finishRenderingSnapshot(renderer);
|
|
874
924
|
}
|
|
875
925
|
finally {
|
|
@@ -879,11 +929,11 @@ class View {
|
|
|
879
929
|
}
|
|
880
930
|
}
|
|
881
931
|
else {
|
|
882
|
-
this.invalidate();
|
|
932
|
+
this.invalidate(renderer.reloadReason);
|
|
883
933
|
}
|
|
884
934
|
}
|
|
885
|
-
invalidate() {
|
|
886
|
-
this.delegate.viewInvalidated();
|
|
935
|
+
invalidate(reason) {
|
|
936
|
+
this.delegate.viewInvalidated(reason);
|
|
887
937
|
}
|
|
888
938
|
prepareToRenderSnapshot(renderer) {
|
|
889
939
|
this.markAsPreview(renderer.isPreview);
|
|
@@ -934,9 +984,9 @@ class LinkInterceptor {
|
|
|
934
984
|
}
|
|
935
985
|
delete this.clickEvent;
|
|
936
986
|
});
|
|
937
|
-
this.willVisit = () => {
|
|
987
|
+
this.willVisit = ((_event) => {
|
|
938
988
|
delete this.clickEvent;
|
|
939
|
-
};
|
|
989
|
+
});
|
|
940
990
|
this.delegate = delegate;
|
|
941
991
|
this.element = element;
|
|
942
992
|
}
|
|
@@ -951,28 +1001,65 @@ class LinkInterceptor {
|
|
|
951
1001
|
document.removeEventListener("turbo:before-visit", this.willVisit);
|
|
952
1002
|
}
|
|
953
1003
|
respondsToEventTarget(target) {
|
|
954
|
-
const element = target instanceof Element
|
|
955
|
-
? target
|
|
956
|
-
: target instanceof Node
|
|
957
|
-
? target.parentElement
|
|
958
|
-
: null;
|
|
1004
|
+
const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
959
1005
|
return element && element.closest("turbo-frame, html") == this.element;
|
|
960
1006
|
}
|
|
961
1007
|
}
|
|
962
1008
|
|
|
1009
|
+
class FormLinkInterceptor {
|
|
1010
|
+
constructor(delegate, element) {
|
|
1011
|
+
this.delegate = delegate;
|
|
1012
|
+
this.linkInterceptor = new LinkInterceptor(this, element);
|
|
1013
|
+
}
|
|
1014
|
+
start() {
|
|
1015
|
+
this.linkInterceptor.start();
|
|
1016
|
+
}
|
|
1017
|
+
stop() {
|
|
1018
|
+
this.linkInterceptor.stop();
|
|
1019
|
+
}
|
|
1020
|
+
shouldInterceptLinkClick(link) {
|
|
1021
|
+
return (this.delegate.shouldInterceptFormLinkClick(link) &&
|
|
1022
|
+
(link.hasAttribute("data-turbo-method") || link.hasAttribute("data-turbo-stream")));
|
|
1023
|
+
}
|
|
1024
|
+
linkClickIntercepted(link, action) {
|
|
1025
|
+
const form = document.createElement("form");
|
|
1026
|
+
form.setAttribute("data-turbo", "true");
|
|
1027
|
+
form.setAttribute("action", action);
|
|
1028
|
+
form.setAttribute("hidden", "");
|
|
1029
|
+
const method = link.getAttribute("data-turbo-method");
|
|
1030
|
+
if (method)
|
|
1031
|
+
form.setAttribute("method", method);
|
|
1032
|
+
const turboFrame = link.getAttribute("data-turbo-frame");
|
|
1033
|
+
if (turboFrame)
|
|
1034
|
+
form.setAttribute("data-turbo-frame", turboFrame);
|
|
1035
|
+
const turboConfirm = link.getAttribute("data-turbo-confirm");
|
|
1036
|
+
if (turboConfirm)
|
|
1037
|
+
form.setAttribute("data-turbo-confirm", turboConfirm);
|
|
1038
|
+
const turboStream = link.hasAttribute("data-turbo-stream");
|
|
1039
|
+
if (turboStream)
|
|
1040
|
+
form.setAttribute("data-turbo-stream", "");
|
|
1041
|
+
this.delegate.formLinkClickIntercepted(link, form);
|
|
1042
|
+
document.body.appendChild(form);
|
|
1043
|
+
form.requestSubmit();
|
|
1044
|
+
form.remove();
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
963
1048
|
class Bardo {
|
|
964
|
-
constructor(permanentElementMap) {
|
|
1049
|
+
constructor(delegate, permanentElementMap) {
|
|
1050
|
+
this.delegate = delegate;
|
|
965
1051
|
this.permanentElementMap = permanentElementMap;
|
|
966
1052
|
}
|
|
967
|
-
static preservingPermanentElements(permanentElementMap, callback) {
|
|
968
|
-
const bardo = new this(permanentElementMap);
|
|
1053
|
+
static preservingPermanentElements(delegate, permanentElementMap, callback) {
|
|
1054
|
+
const bardo = new this(delegate, permanentElementMap);
|
|
969
1055
|
bardo.enter();
|
|
970
1056
|
callback();
|
|
971
1057
|
bardo.leave();
|
|
972
1058
|
}
|
|
973
1059
|
enter() {
|
|
974
1060
|
for (const id in this.permanentElementMap) {
|
|
975
|
-
const [, newPermanentElement] = this.permanentElementMap[id];
|
|
1061
|
+
const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
|
|
1062
|
+
this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
|
|
976
1063
|
this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
|
|
977
1064
|
}
|
|
978
1065
|
}
|
|
@@ -981,6 +1068,7 @@ class Bardo {
|
|
|
981
1068
|
const [currentPermanentElement] = this.permanentElementMap[id];
|
|
982
1069
|
this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
|
|
983
1070
|
this.replacePlaceholderWithPermanentElement(currentPermanentElement);
|
|
1071
|
+
this.delegate.leavingBardo(currentPermanentElement);
|
|
984
1072
|
}
|
|
985
1073
|
}
|
|
986
1074
|
replaceNewPermanentElementWithPlaceholder(permanentElement) {
|
|
@@ -996,7 +1084,7 @@ class Bardo {
|
|
|
996
1084
|
placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
|
|
997
1085
|
}
|
|
998
1086
|
getPlaceholderById(id) {
|
|
999
|
-
return this.placeholders.find(element => element.content == id);
|
|
1087
|
+
return this.placeholders.find((element) => element.content == id);
|
|
1000
1088
|
}
|
|
1001
1089
|
get placeholders() {
|
|
1002
1090
|
return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
|
|
@@ -1010,16 +1098,21 @@ function createPlaceholderForPermanentElement(permanentElement) {
|
|
|
1010
1098
|
}
|
|
1011
1099
|
|
|
1012
1100
|
class Renderer {
|
|
1013
|
-
constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
|
|
1101
|
+
constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
|
1102
|
+
this.activeElement = null;
|
|
1014
1103
|
this.currentSnapshot = currentSnapshot;
|
|
1015
1104
|
this.newSnapshot = newSnapshot;
|
|
1016
1105
|
this.isPreview = isPreview;
|
|
1017
1106
|
this.willRender = willRender;
|
|
1018
|
-
this.
|
|
1107
|
+
this.renderElement = renderElement;
|
|
1108
|
+
this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
|
|
1019
1109
|
}
|
|
1020
1110
|
get shouldRender() {
|
|
1021
1111
|
return true;
|
|
1022
1112
|
}
|
|
1113
|
+
get reloadReason() {
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1023
1116
|
prepareToRender() {
|
|
1024
1117
|
return;
|
|
1025
1118
|
}
|
|
@@ -1045,7 +1138,7 @@ class Renderer {
|
|
|
1045
1138
|
}
|
|
1046
1139
|
}
|
|
1047
1140
|
preservingPermanentElements(callback) {
|
|
1048
|
-
Bardo.preservingPermanentElements(this.permanentElementMap, callback);
|
|
1141
|
+
Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
|
|
1049
1142
|
}
|
|
1050
1143
|
focusFirstAutofocusableElement() {
|
|
1051
1144
|
const element = this.connectedSnapshot.firstAutofocusableElement;
|
|
@@ -1053,6 +1146,19 @@ class Renderer {
|
|
|
1053
1146
|
element.focus();
|
|
1054
1147
|
}
|
|
1055
1148
|
}
|
|
1149
|
+
enteringBardo(currentPermanentElement) {
|
|
1150
|
+
if (this.activeElement)
|
|
1151
|
+
return;
|
|
1152
|
+
if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
|
|
1153
|
+
this.activeElement = this.currentSnapshot.activeElement;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
leavingBardo(currentPermanentElement) {
|
|
1157
|
+
if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
|
|
1158
|
+
this.activeElement.focus();
|
|
1159
|
+
this.activeElement = null;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1056
1162
|
get connectedSnapshot() {
|
|
1057
1163
|
return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
|
|
1058
1164
|
}
|
|
@@ -1066,8 +1172,7 @@ class Renderer {
|
|
|
1066
1172
|
return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
|
|
1067
1173
|
}
|
|
1068
1174
|
get cspNonce() {
|
|
1069
|
-
|
|
1070
|
-
return (_a = document.head.querySelector('meta[name="csp-nonce"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
|
|
1175
|
+
return getMetaContent("csp-nonce");
|
|
1071
1176
|
}
|
|
1072
1177
|
}
|
|
1073
1178
|
function copyElementAttributes(destinationElement, sourceElement) {
|
|
@@ -1080,6 +1185,22 @@ function elementIsFocusable(element) {
|
|
|
1080
1185
|
}
|
|
1081
1186
|
|
|
1082
1187
|
class FrameRenderer extends Renderer {
|
|
1188
|
+
constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
|
1189
|
+
super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
|
|
1190
|
+
this.delegate = delegate;
|
|
1191
|
+
}
|
|
1192
|
+
static renderElement(currentElement, newElement) {
|
|
1193
|
+
var _a;
|
|
1194
|
+
const destinationRange = document.createRange();
|
|
1195
|
+
destinationRange.selectNodeContents(currentElement);
|
|
1196
|
+
destinationRange.deleteContents();
|
|
1197
|
+
const frameElement = newElement;
|
|
1198
|
+
const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
|
|
1199
|
+
if (sourceRange) {
|
|
1200
|
+
sourceRange.selectNodeContents(frameElement);
|
|
1201
|
+
currentElement.appendChild(sourceRange.extractContents());
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1083
1204
|
get shouldRender() {
|
|
1084
1205
|
return true;
|
|
1085
1206
|
}
|
|
@@ -1095,23 +1216,16 @@ class FrameRenderer extends Renderer {
|
|
|
1095
1216
|
this.activateScriptElements();
|
|
1096
1217
|
}
|
|
1097
1218
|
loadFrameElement() {
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
destinationRange.selectNodeContents(this.currentElement);
|
|
1101
|
-
destinationRange.deleteContents();
|
|
1102
|
-
const frameElement = this.newElement;
|
|
1103
|
-
const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
|
|
1104
|
-
if (sourceRange) {
|
|
1105
|
-
sourceRange.selectNodeContents(frameElement);
|
|
1106
|
-
this.currentElement.appendChild(sourceRange.extractContents());
|
|
1107
|
-
}
|
|
1219
|
+
this.delegate.frameExtracted(this.newElement.cloneNode(true));
|
|
1220
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
1108
1221
|
}
|
|
1109
1222
|
scrollFrameIntoView() {
|
|
1110
1223
|
if (this.currentElement.autoscroll || this.newElement.autoscroll) {
|
|
1111
1224
|
const element = this.currentElement.firstElementChild;
|
|
1112
1225
|
const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
|
|
1226
|
+
const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
|
|
1113
1227
|
if (element) {
|
|
1114
|
-
element.scrollIntoView({ block });
|
|
1228
|
+
element.scrollIntoView({ block, behavior });
|
|
1115
1229
|
return true;
|
|
1116
1230
|
}
|
|
1117
1231
|
}
|
|
@@ -1135,6 +1249,14 @@ function readScrollLogicalPosition(value, defaultValue) {
|
|
|
1135
1249
|
return defaultValue;
|
|
1136
1250
|
}
|
|
1137
1251
|
}
|
|
1252
|
+
function readScrollBehavior(value, defaultValue) {
|
|
1253
|
+
if (value == "auto" || value == "smooth") {
|
|
1254
|
+
return value;
|
|
1255
|
+
}
|
|
1256
|
+
else {
|
|
1257
|
+
return defaultValue;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1138
1260
|
|
|
1139
1261
|
class ProgressBar {
|
|
1140
1262
|
constructor() {
|
|
@@ -1158,7 +1280,7 @@ class ProgressBar {
|
|
|
1158
1280
|
left: 0;
|
|
1159
1281
|
height: 3px;
|
|
1160
1282
|
background: #0076ff;
|
|
1161
|
-
z-index:
|
|
1283
|
+
z-index: 2147483647;
|
|
1162
1284
|
transition:
|
|
1163
1285
|
width ${ProgressBar.animationDuration}ms ease-out,
|
|
1164
1286
|
opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
|
|
@@ -1217,13 +1339,16 @@ class ProgressBar {
|
|
|
1217
1339
|
}
|
|
1218
1340
|
refresh() {
|
|
1219
1341
|
requestAnimationFrame(() => {
|
|
1220
|
-
this.progressElement.style.width = `${10 +
|
|
1342
|
+
this.progressElement.style.width = `${10 + this.value * 90}%`;
|
|
1221
1343
|
});
|
|
1222
1344
|
}
|
|
1223
1345
|
createStylesheetElement() {
|
|
1224
1346
|
const element = document.createElement("style");
|
|
1225
1347
|
element.type = "text/css";
|
|
1226
1348
|
element.textContent = ProgressBar.defaultCSS;
|
|
1349
|
+
if (this.cspNonce) {
|
|
1350
|
+
element.nonce = this.cspNonce;
|
|
1351
|
+
}
|
|
1227
1352
|
return element;
|
|
1228
1353
|
}
|
|
1229
1354
|
createProgressElement() {
|
|
@@ -1231,6 +1356,9 @@ class ProgressBar {
|
|
|
1231
1356
|
element.className = "turbo-progress-bar";
|
|
1232
1357
|
return element;
|
|
1233
1358
|
}
|
|
1359
|
+
get cspNonce() {
|
|
1360
|
+
return getMetaContent("csp-nonce");
|
|
1361
|
+
}
|
|
1234
1362
|
}
|
|
1235
1363
|
ProgressBar.animationDuration = 300;
|
|
1236
1364
|
|
|
@@ -1247,14 +1375,14 @@ class HeadSnapshot extends Snapshot {
|
|
|
1247
1375
|
: {
|
|
1248
1376
|
type: elementType(element),
|
|
1249
1377
|
tracked: elementIsTracked(element),
|
|
1250
|
-
elements: []
|
|
1378
|
+
elements: [],
|
|
1251
1379
|
};
|
|
1252
1380
|
return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
|
|
1253
1381
|
}, {});
|
|
1254
1382
|
}
|
|
1255
1383
|
get trackedElementSignature() {
|
|
1256
1384
|
return Object.keys(this.detailsByOuterHTML)
|
|
1257
|
-
.filter(outerHTML => this.detailsByOuterHTML[outerHTML].tracked)
|
|
1385
|
+
.filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
|
|
1258
1386
|
.join("");
|
|
1259
1387
|
}
|
|
1260
1388
|
getScriptElementsNotInSnapshot(snapshot) {
|
|
@@ -1265,8 +1393,8 @@ class HeadSnapshot extends Snapshot {
|
|
|
1265
1393
|
}
|
|
1266
1394
|
getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
|
|
1267
1395
|
return Object.keys(this.detailsByOuterHTML)
|
|
1268
|
-
.filter(outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))
|
|
1269
|
-
.map(outerHTML => this.detailsByOuterHTML[outerHTML])
|
|
1396
|
+
.filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
|
|
1397
|
+
.map((outerHTML) => this.detailsByOuterHTML[outerHTML])
|
|
1270
1398
|
.filter(({ type }) => type == matchedType)
|
|
1271
1399
|
.map(({ elements: [element] }) => element);
|
|
1272
1400
|
}
|
|
@@ -1286,13 +1414,11 @@ class HeadSnapshot extends Snapshot {
|
|
|
1286
1414
|
}
|
|
1287
1415
|
getMetaValue(name) {
|
|
1288
1416
|
const element = this.findMetaElementByName(name);
|
|
1289
|
-
return element
|
|
1290
|
-
? element.getAttribute("content")
|
|
1291
|
-
: null;
|
|
1417
|
+
return element ? element.getAttribute("content") : null;
|
|
1292
1418
|
}
|
|
1293
1419
|
findMetaElementByName(name) {
|
|
1294
1420
|
return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
|
|
1295
|
-
const { elements: [element] } = this.detailsByOuterHTML[outerHTML];
|
|
1421
|
+
const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
|
|
1296
1422
|
return elementIsMetaElementWithName(element, name) ? element : result;
|
|
1297
1423
|
}, undefined);
|
|
1298
1424
|
}
|
|
@@ -1459,9 +1585,11 @@ class Visit {
|
|
|
1459
1585
|
if (this.state == VisitState.started) {
|
|
1460
1586
|
this.recordTimingMetric(TimingMetric.visitEnd);
|
|
1461
1587
|
this.state = VisitState.completed;
|
|
1462
|
-
this.adapter.visitCompleted(this);
|
|
1463
|
-
this.delegate.visitCompleted(this);
|
|
1464
1588
|
this.followRedirect();
|
|
1589
|
+
if (!this.followedRedirect) {
|
|
1590
|
+
this.adapter.visitCompleted(this);
|
|
1591
|
+
this.delegate.visitCompleted(this);
|
|
1592
|
+
}
|
|
1465
1593
|
}
|
|
1466
1594
|
}
|
|
1467
1595
|
fail() {
|
|
@@ -1523,12 +1651,12 @@ class Visit {
|
|
|
1523
1651
|
if (this.view.renderPromise)
|
|
1524
1652
|
await this.view.renderPromise;
|
|
1525
1653
|
if (isSuccessful(statusCode) && responseHTML != null) {
|
|
1526
|
-
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender);
|
|
1654
|
+
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
|
|
1527
1655
|
this.adapter.visitRendered(this);
|
|
1528
1656
|
this.complete();
|
|
1529
1657
|
}
|
|
1530
1658
|
else {
|
|
1531
|
-
await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML));
|
|
1659
|
+
await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
|
|
1532
1660
|
this.adapter.visitRendered(this);
|
|
1533
1661
|
this.fail();
|
|
1534
1662
|
}
|
|
@@ -1563,7 +1691,7 @@ class Visit {
|
|
|
1563
1691
|
else {
|
|
1564
1692
|
if (this.view.renderPromise)
|
|
1565
1693
|
await this.view.renderPromise;
|
|
1566
|
-
await this.view.renderPage(snapshot, isPreview, this.willRender);
|
|
1694
|
+
await this.view.renderPage(snapshot, isPreview, this.willRender, this);
|
|
1567
1695
|
this.adapter.visitRendered(this);
|
|
1568
1696
|
if (!isPreview) {
|
|
1569
1697
|
this.complete();
|
|
@@ -1576,8 +1704,9 @@ class Visit {
|
|
|
1576
1704
|
var _a;
|
|
1577
1705
|
if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
|
|
1578
1706
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1579
|
-
action:
|
|
1580
|
-
|
|
1707
|
+
action: "replace",
|
|
1708
|
+
willRender: false,
|
|
1709
|
+
response: this.response,
|
|
1581
1710
|
});
|
|
1582
1711
|
this.followedRedirect = true;
|
|
1583
1712
|
}
|
|
@@ -1593,13 +1722,15 @@ class Visit {
|
|
|
1593
1722
|
requestStarted() {
|
|
1594
1723
|
this.startRequest();
|
|
1595
1724
|
}
|
|
1596
|
-
requestPreventedHandlingResponse(
|
|
1597
|
-
}
|
|
1725
|
+
requestPreventedHandlingResponse(_request, _response) { }
|
|
1598
1726
|
async requestSucceededWithResponse(request, response) {
|
|
1599
1727
|
const responseHTML = await response.responseHTML;
|
|
1600
1728
|
const { redirected, statusCode } = response;
|
|
1601
1729
|
if (responseHTML == undefined) {
|
|
1602
|
-
this.recordResponse({
|
|
1730
|
+
this.recordResponse({
|
|
1731
|
+
statusCode: SystemStatusCode.contentTypeMismatch,
|
|
1732
|
+
redirected,
|
|
1733
|
+
});
|
|
1603
1734
|
}
|
|
1604
1735
|
else {
|
|
1605
1736
|
this.redirectedToLocation = response.redirected ? response.location : undefined;
|
|
@@ -1610,14 +1741,20 @@ class Visit {
|
|
|
1610
1741
|
const responseHTML = await response.responseHTML;
|
|
1611
1742
|
const { redirected, statusCode } = response;
|
|
1612
1743
|
if (responseHTML == undefined) {
|
|
1613
|
-
this.recordResponse({
|
|
1744
|
+
this.recordResponse({
|
|
1745
|
+
statusCode: SystemStatusCode.contentTypeMismatch,
|
|
1746
|
+
redirected,
|
|
1747
|
+
});
|
|
1614
1748
|
}
|
|
1615
1749
|
else {
|
|
1616
1750
|
this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
|
|
1617
1751
|
}
|
|
1618
1752
|
}
|
|
1619
|
-
requestErrored(
|
|
1620
|
-
this.recordResponse({
|
|
1753
|
+
requestErrored(_request, _error) {
|
|
1754
|
+
this.recordResponse({
|
|
1755
|
+
statusCode: SystemStatusCode.networkFailure,
|
|
1756
|
+
redirected: false,
|
|
1757
|
+
});
|
|
1621
1758
|
}
|
|
1622
1759
|
requestFinished() {
|
|
1623
1760
|
this.finishRequest();
|
|
@@ -1658,9 +1795,11 @@ class Visit {
|
|
|
1658
1795
|
}
|
|
1659
1796
|
getHistoryMethodForAction(action) {
|
|
1660
1797
|
switch (action) {
|
|
1661
|
-
case "replace":
|
|
1798
|
+
case "replace":
|
|
1799
|
+
return history.replaceState;
|
|
1662
1800
|
case "advance":
|
|
1663
|
-
case "restore":
|
|
1801
|
+
case "restore":
|
|
1802
|
+
return history.pushState;
|
|
1664
1803
|
}
|
|
1665
1804
|
}
|
|
1666
1805
|
hasPreloadedResponse() {
|
|
@@ -1679,18 +1818,20 @@ class Visit {
|
|
|
1679
1818
|
}
|
|
1680
1819
|
cacheSnapshot() {
|
|
1681
1820
|
if (!this.snapshotCached) {
|
|
1682
|
-
this.view.cacheSnapshot().then(snapshot => snapshot && this.visitCachedSnapshot(snapshot));
|
|
1821
|
+
this.view.cacheSnapshot().then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
|
|
1683
1822
|
this.snapshotCached = true;
|
|
1684
1823
|
}
|
|
1685
1824
|
}
|
|
1686
1825
|
async render(callback) {
|
|
1687
1826
|
this.cancelRender();
|
|
1688
|
-
await new Promise(resolve => {
|
|
1827
|
+
await new Promise((resolve) => {
|
|
1689
1828
|
this.frame = requestAnimationFrame(() => resolve());
|
|
1690
1829
|
});
|
|
1691
1830
|
await callback();
|
|
1692
1831
|
delete this.frame;
|
|
1693
|
-
this.
|
|
1832
|
+
if (!this.view.forceReloaded) {
|
|
1833
|
+
this.performScroll();
|
|
1834
|
+
}
|
|
1694
1835
|
}
|
|
1695
1836
|
cancelRender() {
|
|
1696
1837
|
if (this.frame) {
|
|
@@ -1705,7 +1846,7 @@ function isSuccessful(statusCode) {
|
|
|
1705
1846
|
|
|
1706
1847
|
class BrowserAdapter {
|
|
1707
1848
|
constructor(session) {
|
|
1708
|
-
this.progressBar = new ProgressBar;
|
|
1849
|
+
this.progressBar = new ProgressBar();
|
|
1709
1850
|
this.showProgressBar = () => {
|
|
1710
1851
|
this.progressBar.show();
|
|
1711
1852
|
};
|
|
@@ -1715,10 +1856,10 @@ class BrowserAdapter {
|
|
|
1715
1856
|
this.navigator.startVisit(location, uuid(), options);
|
|
1716
1857
|
}
|
|
1717
1858
|
visitStarted(visit) {
|
|
1859
|
+
this.location = visit.location;
|
|
1860
|
+
visit.loadCachedSnapshot();
|
|
1718
1861
|
visit.issueRequest();
|
|
1719
|
-
visit.changeHistory();
|
|
1720
1862
|
visit.goToSamePageAnchor();
|
|
1721
|
-
visit.loadCachedSnapshot();
|
|
1722
1863
|
}
|
|
1723
1864
|
visitRequestStarted(visit) {
|
|
1724
1865
|
this.progressBar.setValue(0);
|
|
@@ -1737,29 +1878,31 @@ class BrowserAdapter {
|
|
|
1737
1878
|
case SystemStatusCode.networkFailure:
|
|
1738
1879
|
case SystemStatusCode.timeoutFailure:
|
|
1739
1880
|
case SystemStatusCode.contentTypeMismatch:
|
|
1740
|
-
return this.reload(
|
|
1881
|
+
return this.reload({
|
|
1882
|
+
reason: "request_failed",
|
|
1883
|
+
context: {
|
|
1884
|
+
statusCode,
|
|
1885
|
+
},
|
|
1886
|
+
});
|
|
1741
1887
|
default:
|
|
1742
1888
|
return visit.loadResponse();
|
|
1743
1889
|
}
|
|
1744
1890
|
}
|
|
1745
|
-
visitRequestFinished(
|
|
1891
|
+
visitRequestFinished(_visit) {
|
|
1746
1892
|
this.progressBar.setValue(1);
|
|
1747
1893
|
this.hideVisitProgressBar();
|
|
1748
1894
|
}
|
|
1749
|
-
visitCompleted(
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
this.reload();
|
|
1895
|
+
visitCompleted(_visit) { }
|
|
1896
|
+
pageInvalidated(reason) {
|
|
1897
|
+
this.reload(reason);
|
|
1753
1898
|
}
|
|
1754
|
-
visitFailed(
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
}
|
|
1758
|
-
formSubmissionStarted(formSubmission) {
|
|
1899
|
+
visitFailed(_visit) { }
|
|
1900
|
+
visitRendered(_visit) { }
|
|
1901
|
+
formSubmissionStarted(_formSubmission) {
|
|
1759
1902
|
this.progressBar.setValue(0);
|
|
1760
1903
|
this.showFormProgressBarAfterDelay();
|
|
1761
1904
|
}
|
|
1762
|
-
formSubmissionFinished(
|
|
1905
|
+
formSubmissionFinished(_formSubmission) {
|
|
1763
1906
|
this.progressBar.setValue(1);
|
|
1764
1907
|
this.hideFormProgressBar();
|
|
1765
1908
|
}
|
|
@@ -1785,8 +1928,11 @@ class BrowserAdapter {
|
|
|
1785
1928
|
delete this.formProgressBarTimeout;
|
|
1786
1929
|
}
|
|
1787
1930
|
}
|
|
1788
|
-
reload() {
|
|
1789
|
-
|
|
1931
|
+
reload(reason) {
|
|
1932
|
+
dispatch("turbo:reload", { detail: reason });
|
|
1933
|
+
if (!this.location)
|
|
1934
|
+
return;
|
|
1935
|
+
window.location.href = this.location.toString();
|
|
1790
1936
|
}
|
|
1791
1937
|
get navigator() {
|
|
1792
1938
|
return this.session.navigator;
|
|
@@ -1796,6 +1942,12 @@ class BrowserAdapter {
|
|
|
1796
1942
|
class CacheObserver {
|
|
1797
1943
|
constructor() {
|
|
1798
1944
|
this.started = false;
|
|
1945
|
+
this.removeStaleElements = ((_event) => {
|
|
1946
|
+
const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
|
|
1947
|
+
for (const element of staleElements) {
|
|
1948
|
+
element.remove();
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1799
1951
|
}
|
|
1800
1952
|
start() {
|
|
1801
1953
|
if (!this.started) {
|
|
@@ -1809,12 +1961,6 @@ class CacheObserver {
|
|
|
1809
1961
|
removeEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1810
1962
|
}
|
|
1811
1963
|
}
|
|
1812
|
-
removeStaleElements() {
|
|
1813
|
-
const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
|
|
1814
|
-
for (const element of staleElements) {
|
|
1815
|
-
element.remove();
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
1964
|
}
|
|
1819
1965
|
|
|
1820
1966
|
class FormSubmitObserver {
|
|
@@ -1828,12 +1974,12 @@ class FormSubmitObserver {
|
|
|
1828
1974
|
if (!event.defaultPrevented) {
|
|
1829
1975
|
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
1830
1976
|
const submitter = event.submitter || undefined;
|
|
1831
|
-
if (form
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1977
|
+
if (form &&
|
|
1978
|
+
submissionDoesNotDismissDialog(form, submitter) &&
|
|
1979
|
+
submissionDoesNotTargetIFrame(form, submitter) &&
|
|
1980
|
+
this.delegate.willSubmitForm(form, submitter)) {
|
|
1981
|
+
event.preventDefault();
|
|
1982
|
+
this.delegate.formSubmitted(form, submitter);
|
|
1837
1983
|
}
|
|
1838
1984
|
}
|
|
1839
1985
|
});
|
|
@@ -1852,6 +1998,18 @@ class FormSubmitObserver {
|
|
|
1852
1998
|
}
|
|
1853
1999
|
}
|
|
1854
2000
|
}
|
|
2001
|
+
function submissionDoesNotDismissDialog(form, submitter) {
|
|
2002
|
+
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
2003
|
+
return method != "dialog";
|
|
2004
|
+
}
|
|
2005
|
+
function submissionDoesNotTargetIFrame(form, submitter) {
|
|
2006
|
+
const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
|
|
2007
|
+
for (const element of document.getElementsByName(target)) {
|
|
2008
|
+
if (element instanceof HTMLIFrameElement)
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
2011
|
+
return true;
|
|
2012
|
+
}
|
|
1855
2013
|
|
|
1856
2014
|
class FrameRedirector {
|
|
1857
2015
|
constructor(element) {
|
|
@@ -1867,7 +2025,7 @@ class FrameRedirector {
|
|
|
1867
2025
|
this.linkInterceptor.stop();
|
|
1868
2026
|
this.formInterceptor.stop();
|
|
1869
2027
|
}
|
|
1870
|
-
shouldInterceptLinkClick(element,
|
|
2028
|
+
shouldInterceptLinkClick(element, _url) {
|
|
1871
2029
|
return this.shouldRedirect(element);
|
|
1872
2030
|
}
|
|
1873
2031
|
linkClickIntercepted(element, url) {
|
|
@@ -1882,7 +2040,6 @@ class FrameRedirector {
|
|
|
1882
2040
|
formSubmissionIntercepted(element, submitter) {
|
|
1883
2041
|
const frame = this.findFrameElement(element, submitter);
|
|
1884
2042
|
if (frame) {
|
|
1885
|
-
frame.removeAttribute("reloadable");
|
|
1886
2043
|
frame.delegate.formSubmissionIntercepted(element, submitter);
|
|
1887
2044
|
}
|
|
1888
2045
|
}
|
|
@@ -1925,7 +2082,7 @@ class History {
|
|
|
1925
2082
|
}
|
|
1926
2083
|
}
|
|
1927
2084
|
};
|
|
1928
|
-
this.onPageLoad = async (
|
|
2085
|
+
this.onPageLoad = async (_event) => {
|
|
1929
2086
|
await nextMicrotask();
|
|
1930
2087
|
this.pageLoaded = true;
|
|
1931
2088
|
};
|
|
@@ -1998,9 +2155,9 @@ class LinkClickObserver {
|
|
|
1998
2155
|
if (this.clickEventIsSignificant(event)) {
|
|
1999
2156
|
const target = (event.composedPath && event.composedPath()[0]) || event.target;
|
|
2000
2157
|
const link = this.findLinkFromClickTarget(target);
|
|
2001
|
-
if (link) {
|
|
2158
|
+
if (link && doesNotTargetIFrame(link)) {
|
|
2002
2159
|
const location = this.getLocationForLink(link);
|
|
2003
|
-
if (this.delegate.willFollowLinkToLocation(link, location)) {
|
|
2160
|
+
if (this.delegate.willFollowLinkToLocation(link, location, event)) {
|
|
2004
2161
|
event.preventDefault();
|
|
2005
2162
|
this.delegate.followedLinkToLocation(link, location);
|
|
2006
2163
|
}
|
|
@@ -2022,13 +2179,13 @@ class LinkClickObserver {
|
|
|
2022
2179
|
}
|
|
2023
2180
|
}
|
|
2024
2181
|
clickEventIsSignificant(event) {
|
|
2025
|
-
return !((event.target && event.target.isContentEditable)
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2182
|
+
return !((event.target && event.target.isContentEditable) ||
|
|
2183
|
+
event.defaultPrevented ||
|
|
2184
|
+
event.which > 1 ||
|
|
2185
|
+
event.altKey ||
|
|
2186
|
+
event.ctrlKey ||
|
|
2187
|
+
event.metaKey ||
|
|
2188
|
+
event.shiftKey);
|
|
2032
2189
|
}
|
|
2033
2190
|
findLinkFromClickTarget(target) {
|
|
2034
2191
|
if (target instanceof Element) {
|
|
@@ -2039,6 +2196,13 @@ class LinkClickObserver {
|
|
|
2039
2196
|
return expandURL(link.getAttribute("href") || "");
|
|
2040
2197
|
}
|
|
2041
2198
|
}
|
|
2199
|
+
function doesNotTargetIFrame(anchor) {
|
|
2200
|
+
for (const element of document.getElementsByName(anchor.target)) {
|
|
2201
|
+
if (element instanceof HTMLIFrameElement)
|
|
2202
|
+
return false;
|
|
2203
|
+
}
|
|
2204
|
+
return true;
|
|
2205
|
+
}
|
|
2042
2206
|
|
|
2043
2207
|
function isAction(action) {
|
|
2044
2208
|
return action == "advance" || action == "replace" || action == "restore";
|
|
@@ -2059,6 +2223,7 @@ class Navigator {
|
|
|
2059
2223
|
}
|
|
2060
2224
|
}
|
|
2061
2225
|
startVisit(locatable, restorationIdentifier, options = {}) {
|
|
2226
|
+
this.lastVisit = this.currentVisit;
|
|
2062
2227
|
this.stop();
|
|
2063
2228
|
this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
|
|
2064
2229
|
this.currentVisit.start();
|
|
@@ -2088,7 +2253,7 @@ class Navigator {
|
|
|
2088
2253
|
return this.delegate.history;
|
|
2089
2254
|
}
|
|
2090
2255
|
formSubmissionStarted(formSubmission) {
|
|
2091
|
-
if (typeof this.adapter.formSubmissionStarted ===
|
|
2256
|
+
if (typeof this.adapter.formSubmissionStarted === "function") {
|
|
2092
2257
|
this.adapter.formSubmissionStarted(formSubmission);
|
|
2093
2258
|
}
|
|
2094
2259
|
}
|
|
@@ -2101,7 +2266,10 @@ class Navigator {
|
|
|
2101
2266
|
}
|
|
2102
2267
|
const { statusCode, redirected } = fetchResponse;
|
|
2103
2268
|
const action = this.getActionForFormSubmission(formSubmission);
|
|
2104
|
-
const visitOptions = {
|
|
2269
|
+
const visitOptions = {
|
|
2270
|
+
action,
|
|
2271
|
+
response: { statusCode, responseHTML, redirected },
|
|
2272
|
+
};
|
|
2105
2273
|
this.proposeVisit(fetchResponse.location, visitOptions);
|
|
2106
2274
|
}
|
|
2107
2275
|
}
|
|
@@ -2111,10 +2279,10 @@ class Navigator {
|
|
|
2111
2279
|
if (responseHTML) {
|
|
2112
2280
|
const snapshot = PageSnapshot.fromHTMLString(responseHTML);
|
|
2113
2281
|
if (fetchResponse.serverError) {
|
|
2114
|
-
await this.view.renderError(snapshot);
|
|
2282
|
+
await this.view.renderError(snapshot, this.currentVisit);
|
|
2115
2283
|
}
|
|
2116
2284
|
else {
|
|
2117
|
-
await this.view.renderPage(snapshot);
|
|
2285
|
+
await this.view.renderPage(snapshot, false, true, this.currentVisit);
|
|
2118
2286
|
}
|
|
2119
2287
|
this.view.scrollToTop();
|
|
2120
2288
|
this.view.clearSnapshotCache();
|
|
@@ -2124,7 +2292,7 @@ class Navigator {
|
|
|
2124
2292
|
console.error(error);
|
|
2125
2293
|
}
|
|
2126
2294
|
formSubmissionFinished(formSubmission) {
|
|
2127
|
-
if (typeof this.adapter.formSubmissionFinished ===
|
|
2295
|
+
if (typeof this.adapter.formSubmissionFinished === "function") {
|
|
2128
2296
|
this.adapter.formSubmissionFinished(formSubmission);
|
|
2129
2297
|
}
|
|
2130
2298
|
}
|
|
@@ -2135,12 +2303,14 @@ class Navigator {
|
|
|
2135
2303
|
this.delegate.visitCompleted(visit);
|
|
2136
2304
|
}
|
|
2137
2305
|
locationWithActionIsSamePage(location, action) {
|
|
2306
|
+
var _a;
|
|
2138
2307
|
const anchor = getAnchor(location);
|
|
2139
|
-
const
|
|
2140
|
-
const
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
(
|
|
2308
|
+
const lastLocation = ((_a = this.lastVisit) === null || _a === void 0 ? void 0 : _a.location) || this.view.lastRenderedLocation;
|
|
2309
|
+
const currentAnchor = getAnchor(lastLocation);
|
|
2310
|
+
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
|
|
2311
|
+
return (action !== "replace" &&
|
|
2312
|
+
getRequestURL(location) === getRequestURL(lastLocation) &&
|
|
2313
|
+
(isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
|
|
2144
2314
|
}
|
|
2145
2315
|
visitScrolledToSamePageLocation(oldURL, newURL) {
|
|
2146
2316
|
this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
|
|
@@ -2246,7 +2416,7 @@ class ScrollObserver {
|
|
|
2246
2416
|
|
|
2247
2417
|
class StreamObserver {
|
|
2248
2418
|
constructor(delegate) {
|
|
2249
|
-
this.sources = new Set;
|
|
2419
|
+
this.sources = new Set();
|
|
2250
2420
|
this.started = false;
|
|
2251
2421
|
this.inspectFetchResponse = ((event) => {
|
|
2252
2422
|
const response = fetchResponseFromEvent(event);
|
|
@@ -2313,14 +2483,18 @@ function fetchResponseIsStream(response) {
|
|
|
2313
2483
|
}
|
|
2314
2484
|
|
|
2315
2485
|
class ErrorRenderer extends Renderer {
|
|
2486
|
+
static renderElement(currentElement, newElement) {
|
|
2487
|
+
const { documentElement, body } = document;
|
|
2488
|
+
documentElement.replaceChild(newElement, body);
|
|
2489
|
+
}
|
|
2316
2490
|
async render() {
|
|
2317
2491
|
this.replaceHeadAndBody();
|
|
2318
2492
|
this.activateScriptElements();
|
|
2319
2493
|
}
|
|
2320
2494
|
replaceHeadAndBody() {
|
|
2321
|
-
const { documentElement, head
|
|
2495
|
+
const { documentElement, head } = document;
|
|
2322
2496
|
documentElement.replaceChild(this.newHead, head);
|
|
2323
|
-
|
|
2497
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
2324
2498
|
}
|
|
2325
2499
|
activateScriptElements() {
|
|
2326
2500
|
for (const replaceableElement of this.scriptElements) {
|
|
@@ -2340,9 +2514,29 @@ class ErrorRenderer extends Renderer {
|
|
|
2340
2514
|
}
|
|
2341
2515
|
|
|
2342
2516
|
class PageRenderer extends Renderer {
|
|
2517
|
+
static renderElement(currentElement, newElement) {
|
|
2518
|
+
if (document.body && newElement instanceof HTMLBodyElement) {
|
|
2519
|
+
document.body.replaceWith(newElement);
|
|
2520
|
+
}
|
|
2521
|
+
else {
|
|
2522
|
+
document.documentElement.appendChild(newElement);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2343
2525
|
get shouldRender() {
|
|
2344
2526
|
return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
|
|
2345
2527
|
}
|
|
2528
|
+
get reloadReason() {
|
|
2529
|
+
if (!this.newSnapshot.isVisitable) {
|
|
2530
|
+
return {
|
|
2531
|
+
reason: "turbo_visit_control_is_reload",
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
if (!this.trackedElementsAreIdentical) {
|
|
2535
|
+
return {
|
|
2536
|
+
reason: "tracked_element_mismatch",
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2346
2540
|
prepareToRender() {
|
|
2347
2541
|
this.mergeHead();
|
|
2348
2542
|
}
|
|
@@ -2412,12 +2606,7 @@ class PageRenderer extends Renderer {
|
|
|
2412
2606
|
}
|
|
2413
2607
|
}
|
|
2414
2608
|
assignNewBody() {
|
|
2415
|
-
|
|
2416
|
-
document.body.replaceWith(this.newElement);
|
|
2417
|
-
}
|
|
2418
|
-
else {
|
|
2419
|
-
document.documentElement.appendChild(this.newElement);
|
|
2420
|
-
}
|
|
2609
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
2421
2610
|
}
|
|
2422
2611
|
get newHeadStylesheetElements() {
|
|
2423
2612
|
return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
|
|
@@ -2486,13 +2675,21 @@ class PageView extends View {
|
|
|
2486
2675
|
super(...arguments);
|
|
2487
2676
|
this.snapshotCache = new SnapshotCache(10);
|
|
2488
2677
|
this.lastRenderedLocation = new URL(location.href);
|
|
2678
|
+
this.forceReloaded = false;
|
|
2489
2679
|
}
|
|
2490
|
-
renderPage(snapshot, isPreview = false, willRender = true) {
|
|
2491
|
-
const renderer = new PageRenderer(this.snapshot, snapshot, isPreview, willRender);
|
|
2680
|
+
renderPage(snapshot, isPreview = false, willRender = true, visit) {
|
|
2681
|
+
const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
|
|
2682
|
+
if (!renderer.shouldRender) {
|
|
2683
|
+
this.forceReloaded = true;
|
|
2684
|
+
}
|
|
2685
|
+
else {
|
|
2686
|
+
visit === null || visit === void 0 ? void 0 : visit.changeHistory();
|
|
2687
|
+
}
|
|
2492
2688
|
return this.render(renderer);
|
|
2493
2689
|
}
|
|
2494
|
-
renderError(snapshot) {
|
|
2495
|
-
|
|
2690
|
+
renderError(snapshot, visit) {
|
|
2691
|
+
visit === null || visit === void 0 ? void 0 : visit.changeHistory();
|
|
2692
|
+
const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
|
|
2496
2693
|
return this.render(renderer);
|
|
2497
2694
|
}
|
|
2498
2695
|
clearSnapshotCache() {
|
|
@@ -2519,10 +2716,50 @@ class PageView extends View {
|
|
|
2519
2716
|
}
|
|
2520
2717
|
}
|
|
2521
2718
|
|
|
2719
|
+
class Preloader {
|
|
2720
|
+
constructor(delegate) {
|
|
2721
|
+
this.selector = "a[data-turbo-preload]";
|
|
2722
|
+
this.delegate = delegate;
|
|
2723
|
+
}
|
|
2724
|
+
get snapshotCache() {
|
|
2725
|
+
return this.delegate.navigator.view.snapshotCache;
|
|
2726
|
+
}
|
|
2727
|
+
start() {
|
|
2728
|
+
if (document.readyState === "loading") {
|
|
2729
|
+
return document.addEventListener("DOMContentLoaded", () => {
|
|
2730
|
+
this.preloadOnLoadLinksForView(document.body);
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
else {
|
|
2734
|
+
this.preloadOnLoadLinksForView(document.body);
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
preloadOnLoadLinksForView(element) {
|
|
2738
|
+
for (const link of element.querySelectorAll(this.selector)) {
|
|
2739
|
+
this.preloadURL(link);
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
async preloadURL(link) {
|
|
2743
|
+
const location = new URL(link.href);
|
|
2744
|
+
if (this.snapshotCache.has(location)) {
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
try {
|
|
2748
|
+
const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
|
|
2749
|
+
const responseText = await response.text();
|
|
2750
|
+
const snapshot = PageSnapshot.fromHTMLString(responseText);
|
|
2751
|
+
this.snapshotCache.put(location, snapshot);
|
|
2752
|
+
}
|
|
2753
|
+
catch (_) {
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2522
2758
|
class Session {
|
|
2523
2759
|
constructor() {
|
|
2524
2760
|
this.navigator = new Navigator(this);
|
|
2525
2761
|
this.history = new History(this);
|
|
2762
|
+
this.preloader = new Preloader(this);
|
|
2526
2763
|
this.view = new PageView(this, document.documentElement);
|
|
2527
2764
|
this.adapter = new BrowserAdapter(this);
|
|
2528
2765
|
this.pageObserver = new PageObserver(this);
|
|
@@ -2531,22 +2768,26 @@ class Session {
|
|
|
2531
2768
|
this.formSubmitObserver = new FormSubmitObserver(this);
|
|
2532
2769
|
this.scrollObserver = new ScrollObserver(this);
|
|
2533
2770
|
this.streamObserver = new StreamObserver(this);
|
|
2771
|
+
this.formLinkInterceptor = new FormLinkInterceptor(this, document.documentElement);
|
|
2534
2772
|
this.frameRedirector = new FrameRedirector(document.documentElement);
|
|
2535
2773
|
this.drive = true;
|
|
2536
2774
|
this.enabled = true;
|
|
2537
2775
|
this.progressBarDelay = 500;
|
|
2538
2776
|
this.started = false;
|
|
2777
|
+
this.formMode = "on";
|
|
2539
2778
|
}
|
|
2540
2779
|
start() {
|
|
2541
2780
|
if (!this.started) {
|
|
2542
2781
|
this.pageObserver.start();
|
|
2543
2782
|
this.cacheObserver.start();
|
|
2783
|
+
this.formLinkInterceptor.start();
|
|
2544
2784
|
this.linkClickObserver.start();
|
|
2545
2785
|
this.formSubmitObserver.start();
|
|
2546
2786
|
this.scrollObserver.start();
|
|
2547
2787
|
this.streamObserver.start();
|
|
2548
2788
|
this.frameRedirector.start();
|
|
2549
2789
|
this.history.start();
|
|
2790
|
+
this.preloader.start();
|
|
2550
2791
|
this.started = true;
|
|
2551
2792
|
this.enabled = true;
|
|
2552
2793
|
}
|
|
@@ -2558,6 +2799,7 @@ class Session {
|
|
|
2558
2799
|
if (this.started) {
|
|
2559
2800
|
this.pageObserver.stop();
|
|
2560
2801
|
this.cacheObserver.stop();
|
|
2802
|
+
this.formLinkInterceptor.stop();
|
|
2561
2803
|
this.linkClickObserver.stop();
|
|
2562
2804
|
this.formSubmitObserver.stop();
|
|
2563
2805
|
this.scrollObserver.stop();
|
|
@@ -2588,6 +2830,9 @@ class Session {
|
|
|
2588
2830
|
setProgressBarDelay(delay) {
|
|
2589
2831
|
this.progressBarDelay = delay;
|
|
2590
2832
|
}
|
|
2833
|
+
setFormMode(mode) {
|
|
2834
|
+
this.formMode = mode;
|
|
2835
|
+
}
|
|
2591
2836
|
get location() {
|
|
2592
2837
|
return this.history.location;
|
|
2593
2838
|
}
|
|
@@ -2596,48 +2841,32 @@ class Session {
|
|
|
2596
2841
|
}
|
|
2597
2842
|
historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
|
|
2598
2843
|
if (this.enabled) {
|
|
2599
|
-
this.navigator.startVisit(location, restorationIdentifier, {
|
|
2844
|
+
this.navigator.startVisit(location, restorationIdentifier, {
|
|
2845
|
+
action: "restore",
|
|
2846
|
+
historyChanged: true,
|
|
2847
|
+
});
|
|
2600
2848
|
}
|
|
2601
2849
|
else {
|
|
2602
|
-
this.adapter.pageInvalidated(
|
|
2850
|
+
this.adapter.pageInvalidated({
|
|
2851
|
+
reason: "turbo_disabled",
|
|
2852
|
+
});
|
|
2603
2853
|
}
|
|
2604
2854
|
}
|
|
2605
2855
|
scrollPositionChanged(position) {
|
|
2606
2856
|
this.history.updateRestorationData({ scrollPosition: position });
|
|
2607
2857
|
}
|
|
2608
|
-
|
|
2609
|
-
return
|
|
2610
|
-
|
|
2611
|
-
|
|
2858
|
+
shouldInterceptFormLinkClick(_link) {
|
|
2859
|
+
return true;
|
|
2860
|
+
}
|
|
2861
|
+
formLinkClickIntercepted(_link, _form) { }
|
|
2862
|
+
willFollowLinkToLocation(link, location, event) {
|
|
2863
|
+
return (this.elementDriveEnabled(link) &&
|
|
2864
|
+
locationIsVisitable(location, this.snapshot.rootLocation) &&
|
|
2865
|
+
this.applicationAllowsFollowingLinkToLocation(link, location, event));
|
|
2612
2866
|
}
|
|
2613
2867
|
followedLinkToLocation(link, location) {
|
|
2614
2868
|
const action = this.getActionForLink(link);
|
|
2615
|
-
this.
|
|
2616
|
-
}
|
|
2617
|
-
convertLinkWithMethodClickToFormSubmission(link) {
|
|
2618
|
-
const linkMethod = link.getAttribute("data-turbo-method");
|
|
2619
|
-
if (linkMethod) {
|
|
2620
|
-
const form = document.createElement("form");
|
|
2621
|
-
form.method = linkMethod;
|
|
2622
|
-
form.action = link.getAttribute("href") || "undefined";
|
|
2623
|
-
form.hidden = true;
|
|
2624
|
-
if (link.hasAttribute("data-turbo-confirm")) {
|
|
2625
|
-
form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
|
|
2626
|
-
}
|
|
2627
|
-
const frame = this.getTargetFrameForLink(link);
|
|
2628
|
-
if (frame) {
|
|
2629
|
-
form.setAttribute("data-turbo-frame", frame);
|
|
2630
|
-
form.addEventListener("turbo:submit-start", () => form.remove());
|
|
2631
|
-
}
|
|
2632
|
-
else {
|
|
2633
|
-
form.addEventListener("submit", () => form.remove());
|
|
2634
|
-
}
|
|
2635
|
-
document.body.appendChild(form);
|
|
2636
|
-
return dispatch("submit", { cancelable: true, target: form });
|
|
2637
|
-
}
|
|
2638
|
-
else {
|
|
2639
|
-
return false;
|
|
2640
|
-
}
|
|
2869
|
+
this.visit(location.href, { action });
|
|
2641
2870
|
}
|
|
2642
2871
|
allowsVisitingLocationWithAction(location, action) {
|
|
2643
2872
|
return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
|
|
@@ -2663,9 +2892,9 @@ class Session {
|
|
|
2663
2892
|
}
|
|
2664
2893
|
willSubmitForm(form, submitter) {
|
|
2665
2894
|
const action = getAction(form, submitter);
|
|
2666
|
-
return this.elementDriveEnabled(form)
|
|
2667
|
-
|
|
2668
|
-
|
|
2895
|
+
return (this.elementDriveEnabled(form) &&
|
|
2896
|
+
(!submitter || this.formElementDriveEnabled(submitter)) &&
|
|
2897
|
+
locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
|
|
2669
2898
|
}
|
|
2670
2899
|
formSubmitted(form, submitter) {
|
|
2671
2900
|
this.navigator.submitForm(form, submitter);
|
|
@@ -2689,16 +2918,23 @@ class Session {
|
|
|
2689
2918
|
this.notifyApplicationBeforeCachingSnapshot();
|
|
2690
2919
|
}
|
|
2691
2920
|
}
|
|
2692
|
-
allowsImmediateRender({ element },
|
|
2693
|
-
const event = this.notifyApplicationBeforeRender(element,
|
|
2694
|
-
|
|
2921
|
+
allowsImmediateRender({ element }, options) {
|
|
2922
|
+
const event = this.notifyApplicationBeforeRender(element, options);
|
|
2923
|
+
const { defaultPrevented, detail: { render }, } = event;
|
|
2924
|
+
if (this.view.renderer && render) {
|
|
2925
|
+
this.view.renderer.renderElement = render;
|
|
2926
|
+
}
|
|
2927
|
+
return !defaultPrevented;
|
|
2695
2928
|
}
|
|
2696
|
-
viewRenderedSnapshot(
|
|
2929
|
+
viewRenderedSnapshot(_snapshot, _isPreview) {
|
|
2697
2930
|
this.view.lastRenderedLocation = this.history.location;
|
|
2698
2931
|
this.notifyApplicationAfterRender();
|
|
2699
2932
|
}
|
|
2700
|
-
|
|
2701
|
-
this.
|
|
2933
|
+
preloadOnLoadLinksForView(element) {
|
|
2934
|
+
this.preloader.preloadOnLoadLinksForView(element);
|
|
2935
|
+
}
|
|
2936
|
+
viewInvalidated(reason) {
|
|
2937
|
+
this.adapter.pageInvalidated(reason);
|
|
2702
2938
|
}
|
|
2703
2939
|
frameLoaded(frame) {
|
|
2704
2940
|
this.notifyApplicationAfterFrameLoad(frame);
|
|
@@ -2706,19 +2942,26 @@ class Session {
|
|
|
2706
2942
|
frameRendered(fetchResponse, frame) {
|
|
2707
2943
|
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
|
2708
2944
|
}
|
|
2709
|
-
applicationAllowsFollowingLinkToLocation(link, location) {
|
|
2710
|
-
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
|
|
2945
|
+
applicationAllowsFollowingLinkToLocation(link, location, ev) {
|
|
2946
|
+
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
|
|
2711
2947
|
return !event.defaultPrevented;
|
|
2712
2948
|
}
|
|
2713
2949
|
applicationAllowsVisitingLocation(location) {
|
|
2714
2950
|
const event = this.notifyApplicationBeforeVisitingLocation(location);
|
|
2715
2951
|
return !event.defaultPrevented;
|
|
2716
2952
|
}
|
|
2717
|
-
notifyApplicationAfterClickingLinkToLocation(link, location) {
|
|
2718
|
-
return dispatch("turbo:click", {
|
|
2953
|
+
notifyApplicationAfterClickingLinkToLocation(link, location, event) {
|
|
2954
|
+
return dispatch("turbo:click", {
|
|
2955
|
+
target: link,
|
|
2956
|
+
detail: { url: location.href, originalEvent: event },
|
|
2957
|
+
cancelable: true,
|
|
2958
|
+
});
|
|
2719
2959
|
}
|
|
2720
2960
|
notifyApplicationBeforeVisitingLocation(location) {
|
|
2721
|
-
return dispatch("turbo:before-visit", {
|
|
2961
|
+
return dispatch("turbo:before-visit", {
|
|
2962
|
+
detail: { url: location.href },
|
|
2963
|
+
cancelable: true,
|
|
2964
|
+
});
|
|
2722
2965
|
}
|
|
2723
2966
|
notifyApplicationAfterVisitingLocation(location, action) {
|
|
2724
2967
|
markAsBusy(document.documentElement);
|
|
@@ -2727,24 +2970,46 @@ class Session {
|
|
|
2727
2970
|
notifyApplicationBeforeCachingSnapshot() {
|
|
2728
2971
|
return dispatch("turbo:before-cache");
|
|
2729
2972
|
}
|
|
2730
|
-
notifyApplicationBeforeRender(newBody,
|
|
2731
|
-
return dispatch("turbo:before-render", {
|
|
2973
|
+
notifyApplicationBeforeRender(newBody, options) {
|
|
2974
|
+
return dispatch("turbo:before-render", {
|
|
2975
|
+
detail: Object.assign({ newBody }, options),
|
|
2976
|
+
cancelable: true,
|
|
2977
|
+
});
|
|
2732
2978
|
}
|
|
2733
2979
|
notifyApplicationAfterRender() {
|
|
2734
2980
|
return dispatch("turbo:render");
|
|
2735
2981
|
}
|
|
2736
2982
|
notifyApplicationAfterPageLoad(timing = {}) {
|
|
2737
2983
|
clearBusyState(document.documentElement);
|
|
2738
|
-
return dispatch("turbo:load", {
|
|
2984
|
+
return dispatch("turbo:load", {
|
|
2985
|
+
detail: { url: this.location.href, timing },
|
|
2986
|
+
});
|
|
2739
2987
|
}
|
|
2740
2988
|
notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
|
|
2741
|
-
dispatchEvent(new HashChangeEvent("hashchange", {
|
|
2989
|
+
dispatchEvent(new HashChangeEvent("hashchange", {
|
|
2990
|
+
oldURL: oldURL.toString(),
|
|
2991
|
+
newURL: newURL.toString(),
|
|
2992
|
+
}));
|
|
2742
2993
|
}
|
|
2743
2994
|
notifyApplicationAfterFrameLoad(frame) {
|
|
2744
2995
|
return dispatch("turbo:frame-load", { target: frame });
|
|
2745
2996
|
}
|
|
2746
2997
|
notifyApplicationAfterFrameRender(fetchResponse, frame) {
|
|
2747
|
-
return dispatch("turbo:frame-render", {
|
|
2998
|
+
return dispatch("turbo:frame-render", {
|
|
2999
|
+
detail: { fetchResponse },
|
|
3000
|
+
target: frame,
|
|
3001
|
+
cancelable: true,
|
|
3002
|
+
});
|
|
3003
|
+
}
|
|
3004
|
+
formElementDriveEnabled(element) {
|
|
3005
|
+
if (this.formMode == "off") {
|
|
3006
|
+
return false;
|
|
3007
|
+
}
|
|
3008
|
+
if (this.formMode == "optin") {
|
|
3009
|
+
const form = element === null || element === void 0 ? void 0 : element.closest("form[data-turbo]");
|
|
3010
|
+
return (form === null || form === void 0 ? void 0 : form.getAttribute("data-turbo")) == "true";
|
|
3011
|
+
}
|
|
3012
|
+
return this.elementDriveEnabled(element);
|
|
2748
3013
|
}
|
|
2749
3014
|
elementDriveEnabled(element) {
|
|
2750
3015
|
const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
|
|
@@ -2769,18 +3034,6 @@ class Session {
|
|
|
2769
3034
|
const action = link.getAttribute("data-turbo-action");
|
|
2770
3035
|
return isAction(action) ? action : "advance";
|
|
2771
3036
|
}
|
|
2772
|
-
getTargetFrameForLink(link) {
|
|
2773
|
-
const frame = link.getAttribute("data-turbo-frame");
|
|
2774
|
-
if (frame) {
|
|
2775
|
-
return frame;
|
|
2776
|
-
}
|
|
2777
|
-
else {
|
|
2778
|
-
const container = link.closest("turbo-frame");
|
|
2779
|
-
if (container) {
|
|
2780
|
-
return container.id;
|
|
2781
|
-
}
|
|
2782
|
-
}
|
|
2783
|
-
}
|
|
2784
3037
|
get snapshot() {
|
|
2785
3038
|
return this.view.snapshot;
|
|
2786
3039
|
}
|
|
@@ -2792,11 +3045,59 @@ const deprecatedLocationPropertyDescriptors = {
|
|
|
2792
3045
|
absoluteURL: {
|
|
2793
3046
|
get() {
|
|
2794
3047
|
return this.toString();
|
|
2795
|
-
}
|
|
3048
|
+
},
|
|
3049
|
+
},
|
|
3050
|
+
};
|
|
3051
|
+
|
|
3052
|
+
class Cache {
|
|
3053
|
+
constructor(session) {
|
|
3054
|
+
this.session = session;
|
|
3055
|
+
}
|
|
3056
|
+
clear() {
|
|
3057
|
+
this.session.clearCache();
|
|
3058
|
+
}
|
|
3059
|
+
resetCacheControl() {
|
|
3060
|
+
this.setCacheControl("");
|
|
3061
|
+
}
|
|
3062
|
+
exemptPageFromCache() {
|
|
3063
|
+
this.setCacheControl("no-cache");
|
|
3064
|
+
}
|
|
3065
|
+
exemptPageFromPreview() {
|
|
3066
|
+
this.setCacheControl("no-preview");
|
|
3067
|
+
}
|
|
3068
|
+
setCacheControl(value) {
|
|
3069
|
+
setMetaContent("turbo-cache-control", value);
|
|
2796
3070
|
}
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
const StreamActions = {
|
|
3074
|
+
after() {
|
|
3075
|
+
this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
|
|
3076
|
+
},
|
|
3077
|
+
append() {
|
|
3078
|
+
this.removeDuplicateTargetChildren();
|
|
3079
|
+
this.targetElements.forEach((e) => e.append(this.templateContent));
|
|
3080
|
+
},
|
|
3081
|
+
before() {
|
|
3082
|
+
this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
|
|
3083
|
+
},
|
|
3084
|
+
prepend() {
|
|
3085
|
+
this.removeDuplicateTargetChildren();
|
|
3086
|
+
this.targetElements.forEach((e) => e.prepend(this.templateContent));
|
|
3087
|
+
},
|
|
3088
|
+
remove() {
|
|
3089
|
+
this.targetElements.forEach((e) => e.remove());
|
|
3090
|
+
},
|
|
3091
|
+
replace() {
|
|
3092
|
+
this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
|
|
3093
|
+
},
|
|
3094
|
+
update() {
|
|
3095
|
+
this.targetElements.forEach((e) => e.replaceChildren(this.templateContent));
|
|
3096
|
+
},
|
|
2797
3097
|
};
|
|
2798
3098
|
|
|
2799
|
-
const session = new Session;
|
|
3099
|
+
const session = new Session();
|
|
3100
|
+
const cache = new Cache(session);
|
|
2800
3101
|
const { navigator: navigator$1 } = session;
|
|
2801
3102
|
function start() {
|
|
2802
3103
|
session.start();
|
|
@@ -2817,6 +3118,7 @@ function renderStreamMessage(message) {
|
|
|
2817
3118
|
session.renderStreamMessage(message);
|
|
2818
3119
|
}
|
|
2819
3120
|
function clearCache() {
|
|
3121
|
+
console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
|
|
2820
3122
|
session.clearCache();
|
|
2821
3123
|
}
|
|
2822
3124
|
function setProgressBarDelay(delay) {
|
|
@@ -2825,13 +3127,18 @@ function setProgressBarDelay(delay) {
|
|
|
2825
3127
|
function setConfirmMethod(confirmMethod) {
|
|
2826
3128
|
FormSubmission.confirmMethod = confirmMethod;
|
|
2827
3129
|
}
|
|
3130
|
+
function setFormMode(mode) {
|
|
3131
|
+
session.setFormMode(mode);
|
|
3132
|
+
}
|
|
2828
3133
|
|
|
2829
3134
|
var Turbo = /*#__PURE__*/Object.freeze({
|
|
2830
3135
|
__proto__: null,
|
|
2831
3136
|
navigator: navigator$1,
|
|
2832
3137
|
session: session,
|
|
3138
|
+
cache: cache,
|
|
2833
3139
|
PageRenderer: PageRenderer,
|
|
2834
3140
|
PageSnapshot: PageSnapshot,
|
|
3141
|
+
FrameRenderer: FrameRenderer,
|
|
2835
3142
|
start: start,
|
|
2836
3143
|
registerAdapter: registerAdapter,
|
|
2837
3144
|
visit: visit,
|
|
@@ -2840,39 +3147,52 @@ var Turbo = /*#__PURE__*/Object.freeze({
|
|
|
2840
3147
|
renderStreamMessage: renderStreamMessage,
|
|
2841
3148
|
clearCache: clearCache,
|
|
2842
3149
|
setProgressBarDelay: setProgressBarDelay,
|
|
2843
|
-
setConfirmMethod: setConfirmMethod
|
|
3150
|
+
setConfirmMethod: setConfirmMethod,
|
|
3151
|
+
setFormMode: setFormMode,
|
|
3152
|
+
StreamActions: StreamActions
|
|
2844
3153
|
});
|
|
2845
3154
|
|
|
2846
3155
|
class FrameController {
|
|
2847
3156
|
constructor(element) {
|
|
2848
|
-
this.fetchResponseLoaded = (
|
|
3157
|
+
this.fetchResponseLoaded = (_fetchResponse) => { };
|
|
2849
3158
|
this.currentFetchRequest = null;
|
|
2850
3159
|
this.resolveVisitPromise = () => { };
|
|
2851
3160
|
this.connected = false;
|
|
2852
3161
|
this.hasBeenLoaded = false;
|
|
2853
|
-
this.
|
|
3162
|
+
this.ignoredAttributes = new Set();
|
|
3163
|
+
this.visitCachedSnapshot = ({ element }) => {
|
|
3164
|
+
const frame = element.querySelector("#" + this.element.id);
|
|
3165
|
+
if (frame && this.previousFrameElement) {
|
|
3166
|
+
frame.replaceChildren(...this.previousFrameElement.children);
|
|
3167
|
+
}
|
|
3168
|
+
delete this.previousFrameElement;
|
|
3169
|
+
};
|
|
2854
3170
|
this.element = element;
|
|
2855
3171
|
this.view = new FrameView(this, this.element);
|
|
2856
3172
|
this.appearanceObserver = new AppearanceObserver(this, this.element);
|
|
3173
|
+
this.formLinkInterceptor = new FormLinkInterceptor(this, this.element);
|
|
2857
3174
|
this.linkInterceptor = new LinkInterceptor(this, this.element);
|
|
2858
3175
|
this.formInterceptor = new FormInterceptor(this, this.element);
|
|
2859
3176
|
}
|
|
2860
3177
|
connect() {
|
|
2861
3178
|
if (!this.connected) {
|
|
2862
3179
|
this.connected = true;
|
|
2863
|
-
this.reloadable = false;
|
|
2864
3180
|
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
|
2865
3181
|
this.appearanceObserver.start();
|
|
2866
3182
|
}
|
|
3183
|
+
else {
|
|
3184
|
+
this.loadSourceURL();
|
|
3185
|
+
}
|
|
3186
|
+
this.formLinkInterceptor.start();
|
|
2867
3187
|
this.linkInterceptor.start();
|
|
2868
3188
|
this.formInterceptor.start();
|
|
2869
|
-
this.sourceURLChanged();
|
|
2870
3189
|
}
|
|
2871
3190
|
}
|
|
2872
3191
|
disconnect() {
|
|
2873
3192
|
if (this.connected) {
|
|
2874
3193
|
this.connected = false;
|
|
2875
3194
|
this.appearanceObserver.stop();
|
|
3195
|
+
this.formLinkInterceptor.stop();
|
|
2876
3196
|
this.linkInterceptor.stop();
|
|
2877
3197
|
this.formInterceptor.stop();
|
|
2878
3198
|
}
|
|
@@ -2883,10 +3203,20 @@ class FrameController {
|
|
|
2883
3203
|
}
|
|
2884
3204
|
}
|
|
2885
3205
|
sourceURLChanged() {
|
|
3206
|
+
if (this.isIgnoringChangesTo("src"))
|
|
3207
|
+
return;
|
|
3208
|
+
if (this.element.isConnected) {
|
|
3209
|
+
this.complete = false;
|
|
3210
|
+
}
|
|
2886
3211
|
if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
|
|
2887
3212
|
this.loadSourceURL();
|
|
2888
3213
|
}
|
|
2889
3214
|
}
|
|
3215
|
+
completeChanged() {
|
|
3216
|
+
if (this.isIgnoringChangesTo("complete"))
|
|
3217
|
+
return;
|
|
3218
|
+
this.loadSourceURL();
|
|
3219
|
+
}
|
|
2890
3220
|
loadingStyleChanged() {
|
|
2891
3221
|
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
|
2892
3222
|
this.appearanceObserver.start();
|
|
@@ -2897,21 +3227,11 @@ class FrameController {
|
|
|
2897
3227
|
}
|
|
2898
3228
|
}
|
|
2899
3229
|
async loadSourceURL() {
|
|
2900
|
-
if (
|
|
2901
|
-
|
|
2902
|
-
this.
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
this.element.loaded = this.visit(this.sourceURL);
|
|
2906
|
-
this.appearanceObserver.stop();
|
|
2907
|
-
await this.element.loaded;
|
|
2908
|
-
this.hasBeenLoaded = true;
|
|
2909
|
-
}
|
|
2910
|
-
catch (error) {
|
|
2911
|
-
this.currentURL = previousURL;
|
|
2912
|
-
throw error;
|
|
2913
|
-
}
|
|
2914
|
-
}
|
|
3230
|
+
if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
|
|
3231
|
+
this.element.loaded = this.visit(expandURL(this.sourceURL));
|
|
3232
|
+
this.appearanceObserver.stop();
|
|
3233
|
+
await this.element.loaded;
|
|
3234
|
+
this.hasBeenLoaded = true;
|
|
2915
3235
|
}
|
|
2916
3236
|
}
|
|
2917
3237
|
async loadResponse(fetchResponse) {
|
|
@@ -2923,10 +3243,11 @@ class FrameController {
|
|
|
2923
3243
|
if (html) {
|
|
2924
3244
|
const { body } = parseHTMLDocument(html);
|
|
2925
3245
|
const snapshot = new Snapshot(await this.extractForeignFrameElement(body));
|
|
2926
|
-
const renderer = new FrameRenderer(this.view.snapshot, snapshot, false, false);
|
|
3246
|
+
const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
|
|
2927
3247
|
if (this.view.renderPromise)
|
|
2928
3248
|
await this.view.renderPromise;
|
|
2929
3249
|
await this.view.render(renderer);
|
|
3250
|
+
this.complete = true;
|
|
2930
3251
|
session.frameRendered(fetchResponse, this.element);
|
|
2931
3252
|
session.frameLoaded(this.element);
|
|
2932
3253
|
this.fetchResponseLoaded(fetchResponse);
|
|
@@ -2940,19 +3261,21 @@ class FrameController {
|
|
|
2940
3261
|
this.fetchResponseLoaded = () => { };
|
|
2941
3262
|
}
|
|
2942
3263
|
}
|
|
2943
|
-
elementAppearedInViewport(
|
|
3264
|
+
elementAppearedInViewport(_element) {
|
|
2944
3265
|
this.loadSourceURL();
|
|
2945
3266
|
}
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
3267
|
+
shouldInterceptFormLinkClick(link) {
|
|
3268
|
+
return this.shouldInterceptNavigation(link);
|
|
3269
|
+
}
|
|
3270
|
+
formLinkClickIntercepted(link, form) {
|
|
3271
|
+
const frame = this.findFrameElement(link);
|
|
3272
|
+
if (frame)
|
|
3273
|
+
form.setAttribute("data-turbo-frame", frame.id);
|
|
3274
|
+
}
|
|
3275
|
+
shouldInterceptLinkClick(element, _url) {
|
|
3276
|
+
return this.shouldInterceptNavigation(element);
|
|
2953
3277
|
}
|
|
2954
3278
|
linkClickIntercepted(element, url) {
|
|
2955
|
-
this.reloadable = true;
|
|
2956
3279
|
this.navigateFrame(element, url);
|
|
2957
3280
|
}
|
|
2958
3281
|
shouldInterceptFormSubmission(element, submitter) {
|
|
@@ -2962,19 +3285,18 @@ class FrameController {
|
|
|
2962
3285
|
if (this.formSubmission) {
|
|
2963
3286
|
this.formSubmission.stop();
|
|
2964
3287
|
}
|
|
2965
|
-
this.reloadable = false;
|
|
2966
3288
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
|
2967
3289
|
const { fetchRequest } = this.formSubmission;
|
|
2968
3290
|
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
|
2969
3291
|
this.formSubmission.start();
|
|
2970
3292
|
}
|
|
2971
|
-
prepareHeadersForRequest(headers,
|
|
3293
|
+
prepareHeadersForRequest(headers, _request) {
|
|
2972
3294
|
headers["Turbo-Frame"] = this.id;
|
|
2973
3295
|
}
|
|
2974
|
-
requestStarted(
|
|
3296
|
+
requestStarted(_request) {
|
|
2975
3297
|
markAsBusy(this.element);
|
|
2976
3298
|
}
|
|
2977
|
-
requestPreventedHandlingResponse(
|
|
3299
|
+
requestPreventedHandlingResponse(_request, _response) {
|
|
2978
3300
|
this.resolveVisitPromise();
|
|
2979
3301
|
}
|
|
2980
3302
|
async requestSucceededWithResponse(request, response) {
|
|
@@ -2989,7 +3311,7 @@ class FrameController {
|
|
|
2989
3311
|
console.error(error);
|
|
2990
3312
|
this.resolveVisitPromise();
|
|
2991
3313
|
}
|
|
2992
|
-
requestFinished(
|
|
3314
|
+
requestFinished(_request) {
|
|
2993
3315
|
clearBusyState(this.element);
|
|
2994
3316
|
}
|
|
2995
3317
|
formSubmissionStarted({ formElement }) {
|
|
@@ -3009,19 +3331,32 @@ class FrameController {
|
|
|
3009
3331
|
formSubmissionFinished({ formElement }) {
|
|
3010
3332
|
clearBusyState(formElement, this.findFrameElement(formElement));
|
|
3011
3333
|
}
|
|
3012
|
-
allowsImmediateRender(
|
|
3013
|
-
|
|
3334
|
+
allowsImmediateRender({ element: newFrame }, options) {
|
|
3335
|
+
const event = dispatch("turbo:before-frame-render", {
|
|
3336
|
+
target: this.element,
|
|
3337
|
+
detail: Object.assign({ newFrame }, options),
|
|
3338
|
+
cancelable: true,
|
|
3339
|
+
});
|
|
3340
|
+
const { defaultPrevented, detail: { render }, } = event;
|
|
3341
|
+
if (this.view.renderer && render) {
|
|
3342
|
+
this.view.renderer.renderElement = render;
|
|
3343
|
+
}
|
|
3344
|
+
return !defaultPrevented;
|
|
3014
3345
|
}
|
|
3015
|
-
viewRenderedSnapshot(
|
|
3346
|
+
viewRenderedSnapshot(_snapshot, _isPreview) { }
|
|
3347
|
+
preloadOnLoadLinksForView(element) {
|
|
3348
|
+
session.preloadOnLoadLinksForView(element);
|
|
3016
3349
|
}
|
|
3017
|
-
viewInvalidated() {
|
|
3350
|
+
viewInvalidated() { }
|
|
3351
|
+
frameExtracted(element) {
|
|
3352
|
+
this.previousFrameElement = element;
|
|
3018
3353
|
}
|
|
3019
3354
|
async visit(url) {
|
|
3020
3355
|
var _a;
|
|
3021
|
-
const request = new FetchRequest(this, FetchMethod.get,
|
|
3356
|
+
const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
|
|
3022
3357
|
(_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
3023
3358
|
this.currentFetchRequest = request;
|
|
3024
|
-
return new Promise(resolve => {
|
|
3359
|
+
return new Promise((resolve) => {
|
|
3025
3360
|
this.resolveVisitPromise = () => {
|
|
3026
3361
|
this.resolveVisitPromise = () => { };
|
|
3027
3362
|
this.currentFetchRequest = null;
|
|
@@ -3033,19 +3368,23 @@ class FrameController {
|
|
|
3033
3368
|
navigateFrame(element, url, submitter) {
|
|
3034
3369
|
const frame = this.findFrameElement(element, submitter);
|
|
3035
3370
|
this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
|
|
3036
|
-
frame.setAttribute("reloadable", "");
|
|
3037
3371
|
frame.src = url;
|
|
3038
3372
|
}
|
|
3039
3373
|
proposeVisitIfNavigatedWithAction(frame, element, submitter) {
|
|
3040
3374
|
const action = getAttribute("data-turbo-action", submitter, element, frame);
|
|
3041
3375
|
if (isAction(action)) {
|
|
3042
|
-
const { visitCachedSnapshot } =
|
|
3376
|
+
const { visitCachedSnapshot } = frame.delegate;
|
|
3043
3377
|
frame.delegate.fetchResponseLoaded = (fetchResponse) => {
|
|
3044
3378
|
if (frame.src) {
|
|
3045
3379
|
const { statusCode, redirected } = fetchResponse;
|
|
3046
3380
|
const responseHTML = frame.ownerDocument.documentElement.outerHTML;
|
|
3047
3381
|
const response = { statusCode, redirected, responseHTML };
|
|
3048
|
-
session.visit(frame.src, {
|
|
3382
|
+
session.visit(frame.src, {
|
|
3383
|
+
action,
|
|
3384
|
+
response,
|
|
3385
|
+
visitCachedSnapshot,
|
|
3386
|
+
willRender: false,
|
|
3387
|
+
});
|
|
3049
3388
|
}
|
|
3050
3389
|
};
|
|
3051
3390
|
}
|
|
@@ -3059,10 +3398,12 @@ class FrameController {
|
|
|
3059
3398
|
let element;
|
|
3060
3399
|
const id = CSS.escape(this.id);
|
|
3061
3400
|
try {
|
|
3062
|
-
|
|
3401
|
+
element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
|
|
3402
|
+
if (element) {
|
|
3063
3403
|
return element;
|
|
3064
3404
|
}
|
|
3065
|
-
|
|
3405
|
+
element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
|
|
3406
|
+
if (element) {
|
|
3066
3407
|
await element.loaded;
|
|
3067
3408
|
return await this.extractForeignFrameElement(element);
|
|
3068
3409
|
}
|
|
@@ -3110,24 +3451,10 @@ class FrameController {
|
|
|
3110
3451
|
return this.element.src;
|
|
3111
3452
|
}
|
|
3112
3453
|
}
|
|
3113
|
-
get reloadable() {
|
|
3114
|
-
const frame = this.findFrameElement(this.element);
|
|
3115
|
-
return frame.hasAttribute("reloadable");
|
|
3116
|
-
}
|
|
3117
|
-
set reloadable(value) {
|
|
3118
|
-
const frame = this.findFrameElement(this.element);
|
|
3119
|
-
if (value) {
|
|
3120
|
-
frame.setAttribute("reloadable", "");
|
|
3121
|
-
}
|
|
3122
|
-
else {
|
|
3123
|
-
frame.removeAttribute("reloadable");
|
|
3124
|
-
}
|
|
3125
|
-
}
|
|
3126
3454
|
set sourceURL(sourceURL) {
|
|
3127
|
-
this.
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
this.settingSourceURL = false;
|
|
3455
|
+
this.ignoringChangesToAttribute("src", () => {
|
|
3456
|
+
this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
|
|
3457
|
+
});
|
|
3131
3458
|
}
|
|
3132
3459
|
get loadingStyle() {
|
|
3133
3460
|
return this.element.loading;
|
|
@@ -3135,6 +3462,19 @@ class FrameController {
|
|
|
3135
3462
|
get isLoading() {
|
|
3136
3463
|
return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
|
|
3137
3464
|
}
|
|
3465
|
+
get complete() {
|
|
3466
|
+
return this.element.hasAttribute("complete");
|
|
3467
|
+
}
|
|
3468
|
+
set complete(value) {
|
|
3469
|
+
this.ignoringChangesToAttribute("complete", () => {
|
|
3470
|
+
if (value) {
|
|
3471
|
+
this.element.setAttribute("complete", "");
|
|
3472
|
+
}
|
|
3473
|
+
else {
|
|
3474
|
+
this.element.removeAttribute("complete");
|
|
3475
|
+
}
|
|
3476
|
+
});
|
|
3477
|
+
}
|
|
3138
3478
|
get isActive() {
|
|
3139
3479
|
return this.element.isActive && this.connected;
|
|
3140
3480
|
}
|
|
@@ -3144,16 +3484,13 @@ class FrameController {
|
|
|
3144
3484
|
const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
|
|
3145
3485
|
return expandURL(root);
|
|
3146
3486
|
}
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
};
|
|
3155
|
-
this.clone = element.cloneNode(true);
|
|
3156
|
-
this.id = element.id;
|
|
3487
|
+
isIgnoringChangesTo(attributeName) {
|
|
3488
|
+
return this.ignoredAttributes.has(attributeName);
|
|
3489
|
+
}
|
|
3490
|
+
ignoringChangesToAttribute(attributeName, callback) {
|
|
3491
|
+
this.ignoredAttributes.add(attributeName);
|
|
3492
|
+
callback();
|
|
3493
|
+
this.ignoredAttributes.delete(attributeName);
|
|
3157
3494
|
}
|
|
3158
3495
|
}
|
|
3159
3496
|
function getFrameElementById(id) {
|
|
@@ -3181,35 +3518,6 @@ function activateElement(element, currentURL) {
|
|
|
3181
3518
|
}
|
|
3182
3519
|
}
|
|
3183
3520
|
|
|
3184
|
-
const StreamActions = {
|
|
3185
|
-
after() {
|
|
3186
|
-
this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
|
|
3187
|
-
},
|
|
3188
|
-
append() {
|
|
3189
|
-
this.removeDuplicateTargetChildren();
|
|
3190
|
-
this.targetElements.forEach(e => e.append(this.templateContent));
|
|
3191
|
-
},
|
|
3192
|
-
before() {
|
|
3193
|
-
this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
|
|
3194
|
-
},
|
|
3195
|
-
prepend() {
|
|
3196
|
-
this.removeDuplicateTargetChildren();
|
|
3197
|
-
this.targetElements.forEach(e => e.prepend(this.templateContent));
|
|
3198
|
-
},
|
|
3199
|
-
remove() {
|
|
3200
|
-
this.targetElements.forEach(e => e.remove());
|
|
3201
|
-
},
|
|
3202
|
-
replace() {
|
|
3203
|
-
this.targetElements.forEach(e => e.replaceWith(this.templateContent));
|
|
3204
|
-
},
|
|
3205
|
-
update() {
|
|
3206
|
-
this.targetElements.forEach(e => {
|
|
3207
|
-
e.innerHTML = "";
|
|
3208
|
-
e.append(this.templateContent);
|
|
3209
|
-
});
|
|
3210
|
-
}
|
|
3211
|
-
};
|
|
3212
|
-
|
|
3213
3521
|
class StreamElement extends HTMLElement {
|
|
3214
3522
|
async connectedCallback() {
|
|
3215
3523
|
try {
|
|
@@ -3224,12 +3532,12 @@ class StreamElement extends HTMLElement {
|
|
|
3224
3532
|
}
|
|
3225
3533
|
async render() {
|
|
3226
3534
|
var _a;
|
|
3227
|
-
return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
|
|
3535
|
+
return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
|
|
3228
3536
|
if (this.dispatchEvent(this.beforeRenderEvent)) {
|
|
3229
3537
|
await nextAnimationFrame();
|
|
3230
3538
|
this.performAction();
|
|
3231
3539
|
}
|
|
3232
|
-
})());
|
|
3540
|
+
})()));
|
|
3233
3541
|
}
|
|
3234
3542
|
disconnect() {
|
|
3235
3543
|
try {
|
|
@@ -3238,13 +3546,13 @@ class StreamElement extends HTMLElement {
|
|
|
3238
3546
|
catch (_a) { }
|
|
3239
3547
|
}
|
|
3240
3548
|
removeDuplicateTargetChildren() {
|
|
3241
|
-
this.duplicateChildren.forEach(c => c.remove());
|
|
3549
|
+
this.duplicateChildren.forEach((c) => c.remove());
|
|
3242
3550
|
}
|
|
3243
3551
|
get duplicateChildren() {
|
|
3244
3552
|
var _a;
|
|
3245
|
-
const existingChildren = this.targetElements.flatMap(e => [...e.children]).filter(c => !!c.id);
|
|
3246
|
-
const newChildrenIds = [...(_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children].filter(c => !!c.id).map(c => c.id);
|
|
3247
|
-
return existingChildren.filter(c => newChildrenIds.includes(c.id));
|
|
3553
|
+
const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
|
|
3554
|
+
const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
|
|
3555
|
+
return existingChildren.filter((c) => newChildrenIds.includes(c.id));
|
|
3248
3556
|
}
|
|
3249
3557
|
get performAction() {
|
|
3250
3558
|
if (this.action) {
|
|
@@ -3293,7 +3601,10 @@ class StreamElement extends HTMLElement {
|
|
|
3293
3601
|
return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
|
|
3294
3602
|
}
|
|
3295
3603
|
get beforeRenderEvent() {
|
|
3296
|
-
return new CustomEvent("turbo:before-stream-render", {
|
|
3604
|
+
return new CustomEvent("turbo:before-stream-render", {
|
|
3605
|
+
bubbles: true,
|
|
3606
|
+
cancelable: true,
|
|
3607
|
+
});
|
|
3297
3608
|
}
|
|
3298
3609
|
get targetElementsById() {
|
|
3299
3610
|
var _a;
|
|
@@ -3317,9 +3628,35 @@ class StreamElement extends HTMLElement {
|
|
|
3317
3628
|
}
|
|
3318
3629
|
}
|
|
3319
3630
|
|
|
3631
|
+
class StreamSourceElement extends HTMLElement {
|
|
3632
|
+
constructor() {
|
|
3633
|
+
super(...arguments);
|
|
3634
|
+
this.streamSource = null;
|
|
3635
|
+
}
|
|
3636
|
+
connectedCallback() {
|
|
3637
|
+
this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
|
|
3638
|
+
connectStreamSource(this.streamSource);
|
|
3639
|
+
}
|
|
3640
|
+
disconnectedCallback() {
|
|
3641
|
+
if (this.streamSource) {
|
|
3642
|
+
disconnectStreamSource(this.streamSource);
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
get src() {
|
|
3646
|
+
return this.getAttribute("src") || "";
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3320
3650
|
FrameElement.delegateConstructor = FrameController;
|
|
3321
|
-
customElements.
|
|
3322
|
-
customElements.define("turbo-
|
|
3651
|
+
if (customElements.get("turbo-frame") === undefined) {
|
|
3652
|
+
customElements.define("turbo-frame", FrameElement);
|
|
3653
|
+
}
|
|
3654
|
+
if (customElements.get("turbo-stream") === undefined) {
|
|
3655
|
+
customElements.define("turbo-stream", StreamElement);
|
|
3656
|
+
}
|
|
3657
|
+
if (customElements.get("turbo-stream-source") === undefined) {
|
|
3658
|
+
customElements.define("turbo-stream-source", StreamSourceElement);
|
|
3659
|
+
}
|
|
3323
3660
|
|
|
3324
3661
|
(() => {
|
|
3325
3662
|
let element = document.currentScript;
|
|
@@ -3327,7 +3664,8 @@ customElements.define("turbo-stream", StreamElement);
|
|
|
3327
3664
|
return;
|
|
3328
3665
|
if (element.hasAttribute("data-turbo-suppress-warning"))
|
|
3329
3666
|
return;
|
|
3330
|
-
|
|
3667
|
+
element = element.parentElement;
|
|
3668
|
+
while (element) {
|
|
3331
3669
|
if (element == document.body) {
|
|
3332
3670
|
return console.warn(unindent `
|
|
3333
3671
|
You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
|
|
@@ -3340,10 +3678,11 @@ customElements.define("turbo-stream", StreamElement);
|
|
|
3340
3678
|
Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
|
|
3341
3679
|
`, element.outerHTML);
|
|
3342
3680
|
}
|
|
3681
|
+
element = element.parentElement;
|
|
3343
3682
|
}
|
|
3344
3683
|
})();
|
|
3345
3684
|
|
|
3346
3685
|
window.Turbo = Turbo;
|
|
3347
3686
|
start();
|
|
3348
3687
|
|
|
3349
|
-
export { PageRenderer, PageSnapshot, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setProgressBarDelay, start, visit };
|
|
3688
|
+
export { FrameRenderer, PageRenderer, PageSnapshot, StreamActions, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
|