@hotwired/turbo 7.1.0-rc.3 → 7.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/turbo.es2017-esm.js +1027 -613
- package/dist/turbo.es2017-umd.js +1032 -614
- 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 +2 -1
- package/dist/types/core/drive/form_submission.d.ts +13 -4
- package/dist/types/core/drive/head_snapshot.d.ts +3 -3
- package/dist/types/core/drive/history.d.ts +1 -1
- package/dist/types/core/drive/navigator.d.ts +4 -3
- package/dist/types/core/drive/page_renderer.d.ts +8 -5
- 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 +15 -4
- package/dist/types/core/frames/frame_controller.d.ts +43 -23
- package/dist/types/core/frames/frame_redirector.d.ts +12 -10
- 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/index.d.ts +14 -4
- package/dist/types/core/native/adapter.d.ts +3 -2
- package/dist/types/core/native/browser_adapter.d.ts +18 -9
- package/dist/types/core/renderer.d.ts +11 -5
- package/dist/types/core/session.d.ts +78 -19
- package/dist/types/core/snapshot.d.ts +2 -1
- package/dist/types/core/streams/stream_message.d.ts +2 -6
- package/dist/types/core/types.d.ts +4 -0
- package/dist/types/core/view.d.ts +13 -7
- package/dist/types/elements/frame_element.d.ts +10 -6
- package/dist/types/elements/stream_element.d.ts +3 -0
- package/dist/types/elements/stream_source_element.d.ts +7 -0
- package/dist/types/http/fetch_request.d.ts +9 -0
- package/dist/types/observers/cache_observer.d.ts +1 -1
- package/dist/types/observers/form_link_click_observer.d.ts +14 -0
- package/dist/types/observers/form_submit_observer.d.ts +2 -1
- package/dist/types/observers/link_click_observer.d.ts +5 -4
- 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_mode_tests.d.ts +1 -0
- package/dist/types/tests/functional/form_submission_tests.d.ts +1 -84
- 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 -14
- 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 +12 -3
- package/package.json +22 -8
- package/CHANGELOG.md +0 -3
- package/dist/types/core/frames/form_interceptor.d.ts +0 -12
- package/dist/types/core/frames/link_interceptor.d.ts +0 -16
- package/dist/types/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.2
|
|
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,13 +129,18 @@ 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;
|
|
135
|
+
return this.loaded;
|
|
134
136
|
}
|
|
135
137
|
attributeChangedCallback(name) {
|
|
136
138
|
if (name == "loading") {
|
|
137
139
|
this.delegate.loadingStyleChanged();
|
|
138
140
|
}
|
|
141
|
+
else if (name == "complete") {
|
|
142
|
+
this.delegate.completeChanged();
|
|
143
|
+
}
|
|
139
144
|
else if (name == "src") {
|
|
140
145
|
this.delegate.sourceURLChanged();
|
|
141
146
|
}
|
|
@@ -200,8 +205,10 @@ class FrameElement extends HTMLElement {
|
|
|
200
205
|
}
|
|
201
206
|
function frameLoadingStyleFromString(style) {
|
|
202
207
|
switch (style.toLowerCase()) {
|
|
203
|
-
case "lazy":
|
|
204
|
-
|
|
208
|
+
case "lazy":
|
|
209
|
+
return FrameLoadingStyle.lazy;
|
|
210
|
+
default:
|
|
211
|
+
return FrameLoadingStyle.eager;
|
|
205
212
|
}
|
|
206
213
|
}
|
|
207
214
|
|
|
@@ -213,7 +220,7 @@ function getAnchor(url) {
|
|
|
213
220
|
if (url.hash) {
|
|
214
221
|
return url.hash.slice(1);
|
|
215
222
|
}
|
|
216
|
-
else if (anchorMatch = url.href.match(/#(.*)$/)) {
|
|
223
|
+
else if ((anchorMatch = url.href.match(/#(.*)$/))) {
|
|
217
224
|
return anchorMatch[1];
|
|
218
225
|
}
|
|
219
226
|
}
|
|
@@ -225,7 +232,7 @@ function getExtension(url) {
|
|
|
225
232
|
return (getLastPathComponent(url).match(/\.[^.]*$/) || [])[0] || "";
|
|
226
233
|
}
|
|
227
234
|
function isHTML(url) {
|
|
228
|
-
return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml))$/);
|
|
235
|
+
return !!getExtension(url).match(/^(?:|\.(?:htm|html|xhtml|php))$/);
|
|
229
236
|
}
|
|
230
237
|
function isPrefixedBy(baseURL, url) {
|
|
231
238
|
const prefix = getPrefix(url);
|
|
@@ -236,9 +243,7 @@ function locationIsVisitable(location, rootLocation) {
|
|
|
236
243
|
}
|
|
237
244
|
function getRequestURL(url) {
|
|
238
245
|
const anchor = getAnchor(url);
|
|
239
|
-
return anchor != null
|
|
240
|
-
? url.href.slice(0, -(anchor.length + 1))
|
|
241
|
-
: url.href;
|
|
246
|
+
return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
|
|
242
247
|
}
|
|
243
248
|
function toCacheKey(url) {
|
|
244
249
|
return getRequestURL(url);
|
|
@@ -306,8 +311,42 @@ class FetchResponse {
|
|
|
306
311
|
}
|
|
307
312
|
}
|
|
308
313
|
|
|
314
|
+
function isAction(action) {
|
|
315
|
+
return action == "advance" || action == "replace" || action == "restore";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function activateScriptElement(element) {
|
|
319
|
+
if (element.getAttribute("data-turbo-eval") == "false") {
|
|
320
|
+
return element;
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
const createdScriptElement = document.createElement("script");
|
|
324
|
+
const cspNonce = getMetaContent("csp-nonce");
|
|
325
|
+
if (cspNonce) {
|
|
326
|
+
createdScriptElement.nonce = cspNonce;
|
|
327
|
+
}
|
|
328
|
+
createdScriptElement.textContent = element.textContent;
|
|
329
|
+
createdScriptElement.async = false;
|
|
330
|
+
copyElementAttributes(createdScriptElement, element);
|
|
331
|
+
return createdScriptElement;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function copyElementAttributes(destinationElement, sourceElement) {
|
|
335
|
+
for (const { name, value } of sourceElement.attributes) {
|
|
336
|
+
destinationElement.setAttribute(name, value);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function createDocumentFragment(html) {
|
|
340
|
+
const template = document.createElement("template");
|
|
341
|
+
template.innerHTML = html;
|
|
342
|
+
return template.content;
|
|
343
|
+
}
|
|
309
344
|
function dispatch(eventName, { target, cancelable, detail } = {}) {
|
|
310
|
-
const event = new CustomEvent(eventName, {
|
|
345
|
+
const event = new CustomEvent(eventName, {
|
|
346
|
+
cancelable,
|
|
347
|
+
bubbles: true,
|
|
348
|
+
detail,
|
|
349
|
+
});
|
|
311
350
|
if (target && target.isConnected) {
|
|
312
351
|
target.dispatchEvent(event);
|
|
313
352
|
}
|
|
@@ -317,10 +356,10 @@ function dispatch(eventName, { target, cancelable, detail } = {}) {
|
|
|
317
356
|
return event;
|
|
318
357
|
}
|
|
319
358
|
function nextAnimationFrame() {
|
|
320
|
-
return new Promise(resolve => requestAnimationFrame(() => resolve()));
|
|
359
|
+
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
321
360
|
}
|
|
322
361
|
function nextEventLoopTick() {
|
|
323
|
-
return new Promise(resolve => setTimeout(() => resolve(), 0));
|
|
362
|
+
return new Promise((resolve) => setTimeout(() => resolve(), 0));
|
|
324
363
|
}
|
|
325
364
|
function nextMicrotask() {
|
|
326
365
|
return Promise.resolve();
|
|
@@ -332,7 +371,7 @@ function unindent(strings, ...values) {
|
|
|
332
371
|
const lines = interpolate(strings, values).replace(/^\n/, "").split("\n");
|
|
333
372
|
const match = lines[0].match(/^\s+/);
|
|
334
373
|
const indent = match ? match[0].length : 0;
|
|
335
|
-
return lines.map(line => line.slice(indent)).join("\n");
|
|
374
|
+
return lines.map((line) => line.slice(indent)).join("\n");
|
|
336
375
|
}
|
|
337
376
|
function interpolate(strings, values) {
|
|
338
377
|
return strings.reduce((result, string, i) => {
|
|
@@ -341,7 +380,8 @@ function interpolate(strings, values) {
|
|
|
341
380
|
}, "");
|
|
342
381
|
}
|
|
343
382
|
function uuid() {
|
|
344
|
-
return Array.
|
|
383
|
+
return Array.from({ length: 36 })
|
|
384
|
+
.map((_, i) => {
|
|
345
385
|
if (i == 8 || i == 13 || i == 18 || i == 23) {
|
|
346
386
|
return "-";
|
|
347
387
|
}
|
|
@@ -354,10 +394,11 @@ function uuid() {
|
|
|
354
394
|
else {
|
|
355
395
|
return Math.floor(Math.random() * 15).toString(16);
|
|
356
396
|
}
|
|
357
|
-
})
|
|
397
|
+
})
|
|
398
|
+
.join("");
|
|
358
399
|
}
|
|
359
400
|
function getAttribute(attributeName, ...elements) {
|
|
360
|
-
for (const value of elements.map(element => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
|
|
401
|
+
for (const value of elements.map((element) => element === null || element === void 0 ? void 0 : element.getAttribute(attributeName))) {
|
|
361
402
|
if (typeof value == "string")
|
|
362
403
|
return value;
|
|
363
404
|
}
|
|
@@ -379,6 +420,48 @@ function clearBusyState(...elements) {
|
|
|
379
420
|
element.removeAttribute("aria-busy");
|
|
380
421
|
}
|
|
381
422
|
}
|
|
423
|
+
function waitForLoad(element, timeoutInMilliseconds = 2000) {
|
|
424
|
+
return new Promise((resolve) => {
|
|
425
|
+
const onComplete = () => {
|
|
426
|
+
element.removeEventListener("error", onComplete);
|
|
427
|
+
element.removeEventListener("load", onComplete);
|
|
428
|
+
resolve();
|
|
429
|
+
};
|
|
430
|
+
element.addEventListener("load", onComplete, { once: true });
|
|
431
|
+
element.addEventListener("error", onComplete, { once: true });
|
|
432
|
+
setTimeout(resolve, timeoutInMilliseconds);
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
function getHistoryMethodForAction(action) {
|
|
436
|
+
switch (action) {
|
|
437
|
+
case "replace":
|
|
438
|
+
return history.replaceState;
|
|
439
|
+
case "advance":
|
|
440
|
+
case "restore":
|
|
441
|
+
return history.pushState;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function getVisitAction(...elements) {
|
|
445
|
+
const action = getAttribute("data-turbo-action", ...elements);
|
|
446
|
+
return isAction(action) ? action : null;
|
|
447
|
+
}
|
|
448
|
+
function getMetaElement(name) {
|
|
449
|
+
return document.querySelector(`meta[name="${name}"]`);
|
|
450
|
+
}
|
|
451
|
+
function getMetaContent(name) {
|
|
452
|
+
const element = getMetaElement(name);
|
|
453
|
+
return element && element.content;
|
|
454
|
+
}
|
|
455
|
+
function setMetaContent(name, content) {
|
|
456
|
+
let element = getMetaElement(name);
|
|
457
|
+
if (!element) {
|
|
458
|
+
element = document.createElement("meta");
|
|
459
|
+
element.setAttribute("name", name);
|
|
460
|
+
document.head.appendChild(element);
|
|
461
|
+
}
|
|
462
|
+
element.setAttribute("content", content);
|
|
463
|
+
return element;
|
|
464
|
+
}
|
|
382
465
|
|
|
383
466
|
var FetchMethod;
|
|
384
467
|
(function (FetchMethod) {
|
|
@@ -390,24 +473,27 @@ var FetchMethod;
|
|
|
390
473
|
})(FetchMethod || (FetchMethod = {}));
|
|
391
474
|
function fetchMethodFromString(method) {
|
|
392
475
|
switch (method.toLowerCase()) {
|
|
393
|
-
case "get":
|
|
394
|
-
|
|
395
|
-
case "
|
|
396
|
-
|
|
397
|
-
case "
|
|
476
|
+
case "get":
|
|
477
|
+
return FetchMethod.get;
|
|
478
|
+
case "post":
|
|
479
|
+
return FetchMethod.post;
|
|
480
|
+
case "put":
|
|
481
|
+
return FetchMethod.put;
|
|
482
|
+
case "patch":
|
|
483
|
+
return FetchMethod.patch;
|
|
484
|
+
case "delete":
|
|
485
|
+
return FetchMethod.delete;
|
|
398
486
|
}
|
|
399
487
|
}
|
|
400
488
|
class FetchRequest {
|
|
401
|
-
constructor(delegate, method, location, body = new URLSearchParams, target = null) {
|
|
402
|
-
this.abortController = new AbortController;
|
|
403
|
-
this.resolveRequestPromise = (
|
|
489
|
+
constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
|
|
490
|
+
this.abortController = new AbortController();
|
|
491
|
+
this.resolveRequestPromise = (_value) => { };
|
|
404
492
|
this.delegate = delegate;
|
|
405
493
|
this.method = method;
|
|
406
494
|
this.headers = this.defaultHeaders;
|
|
407
495
|
this.body = body;
|
|
408
|
-
this.url =
|
|
409
|
-
mergeFormDataEntries(new URL(location.href), this.entries) :
|
|
410
|
-
location;
|
|
496
|
+
this.url = location;
|
|
411
497
|
this.target = target;
|
|
412
498
|
}
|
|
413
499
|
get location() {
|
|
@@ -433,7 +519,7 @@ class FetchRequest {
|
|
|
433
519
|
return await this.receive(response);
|
|
434
520
|
}
|
|
435
521
|
catch (error) {
|
|
436
|
-
if (error.name !==
|
|
522
|
+
if (error.name !== "AbortError") {
|
|
437
523
|
this.delegate.requestErrored(this, error);
|
|
438
524
|
throw error;
|
|
439
525
|
}
|
|
@@ -444,7 +530,11 @@ class FetchRequest {
|
|
|
444
530
|
}
|
|
445
531
|
async receive(response) {
|
|
446
532
|
const fetchResponse = new FetchResponse(response);
|
|
447
|
-
const event = dispatch("turbo:before-fetch-response", {
|
|
533
|
+
const event = dispatch("turbo:before-fetch-response", {
|
|
534
|
+
cancelable: true,
|
|
535
|
+
detail: { fetchResponse },
|
|
536
|
+
target: this.target,
|
|
537
|
+
});
|
|
448
538
|
if (event.defaultPrevented) {
|
|
449
539
|
this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
|
|
450
540
|
}
|
|
@@ -465,12 +555,12 @@ class FetchRequest {
|
|
|
465
555
|
redirect: "follow",
|
|
466
556
|
body: this.isIdempotent ? null : this.body,
|
|
467
557
|
signal: this.abortSignal,
|
|
468
|
-
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
|
|
558
|
+
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
|
|
469
559
|
};
|
|
470
560
|
}
|
|
471
561
|
get defaultHeaders() {
|
|
472
562
|
return {
|
|
473
|
-
|
|
563
|
+
Accept: "text/html, application/xhtml+xml",
|
|
474
564
|
};
|
|
475
565
|
}
|
|
476
566
|
get isIdempotent() {
|
|
@@ -479,36 +569,29 @@ class FetchRequest {
|
|
|
479
569
|
get abortSignal() {
|
|
480
570
|
return this.abortController.signal;
|
|
481
571
|
}
|
|
572
|
+
acceptResponseType(mimeType) {
|
|
573
|
+
this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
|
|
574
|
+
}
|
|
482
575
|
async allowRequestToBeIntercepted(fetchOptions) {
|
|
483
|
-
const requestInterception = new Promise(resolve => this.resolveRequestPromise = resolve);
|
|
576
|
+
const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
|
|
484
577
|
const event = dispatch("turbo:before-fetch-request", {
|
|
485
578
|
cancelable: true,
|
|
486
579
|
detail: {
|
|
487
580
|
fetchOptions,
|
|
488
581
|
url: this.url,
|
|
489
|
-
resume: this.resolveRequestPromise
|
|
582
|
+
resume: this.resolveRequestPromise,
|
|
490
583
|
},
|
|
491
|
-
target: this.target
|
|
584
|
+
target: this.target,
|
|
492
585
|
});
|
|
493
586
|
if (event.defaultPrevented)
|
|
494
587
|
await requestInterception;
|
|
495
588
|
}
|
|
496
589
|
}
|
|
497
|
-
function mergeFormDataEntries(url, entries) {
|
|
498
|
-
const searchParams = new URLSearchParams;
|
|
499
|
-
for (const [name, value] of entries) {
|
|
500
|
-
if (value instanceof File)
|
|
501
|
-
continue;
|
|
502
|
-
searchParams.append(name, value);
|
|
503
|
-
}
|
|
504
|
-
url.search = searchParams.toString();
|
|
505
|
-
return url;
|
|
506
|
-
}
|
|
507
590
|
|
|
508
591
|
class AppearanceObserver {
|
|
509
592
|
constructor(delegate, element) {
|
|
510
593
|
this.started = false;
|
|
511
|
-
this.intersect = entries => {
|
|
594
|
+
this.intersect = (entries) => {
|
|
512
595
|
const lastEntry = entries.slice(-1)[0];
|
|
513
596
|
if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
|
|
514
597
|
this.delegate.elementAppearedInViewport(this.element);
|
|
@@ -533,40 +616,29 @@ class AppearanceObserver {
|
|
|
533
616
|
}
|
|
534
617
|
|
|
535
618
|
class StreamMessage {
|
|
536
|
-
constructor(
|
|
537
|
-
this.
|
|
538
|
-
this.templateElement.innerHTML = html;
|
|
619
|
+
constructor(fragment) {
|
|
620
|
+
this.fragment = importStreamElements(fragment);
|
|
539
621
|
}
|
|
540
622
|
static wrap(message) {
|
|
541
623
|
if (typeof message == "string") {
|
|
542
|
-
return new this(message);
|
|
624
|
+
return new this(createDocumentFragment(message));
|
|
543
625
|
}
|
|
544
626
|
else {
|
|
545
627
|
return message;
|
|
546
628
|
}
|
|
547
629
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
630
|
+
}
|
|
631
|
+
StreamMessage.contentType = "text/vnd.turbo-stream.html";
|
|
632
|
+
function importStreamElements(fragment) {
|
|
633
|
+
for (const element of fragment.querySelectorAll("turbo-stream")) {
|
|
634
|
+
const streamElement = document.importNode(element, true);
|
|
635
|
+
for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
|
|
636
|
+
inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
|
|
552
637
|
}
|
|
553
|
-
|
|
554
|
-
}
|
|
555
|
-
get foreignElements() {
|
|
556
|
-
return this.templateChildren.reduce((streamElements, child) => {
|
|
557
|
-
if (child.tagName.toLowerCase() == "turbo-stream") {
|
|
558
|
-
return [...streamElements, child];
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
return streamElements;
|
|
562
|
-
}
|
|
563
|
-
}, []);
|
|
564
|
-
}
|
|
565
|
-
get templateChildren() {
|
|
566
|
-
return Array.from(this.templateElement.content.children);
|
|
638
|
+
element.replaceWith(streamElement);
|
|
567
639
|
}
|
|
640
|
+
return fragment;
|
|
568
641
|
}
|
|
569
|
-
StreamMessage.contentType = "text/vnd.turbo-stream.html";
|
|
570
642
|
|
|
571
643
|
var FormSubmissionState;
|
|
572
644
|
(function (FormSubmissionState) {
|
|
@@ -585,9 +657,12 @@ var FormEnctype;
|
|
|
585
657
|
})(FormEnctype || (FormEnctype = {}));
|
|
586
658
|
function formEnctypeFromString(encoding) {
|
|
587
659
|
switch (encoding.toLowerCase()) {
|
|
588
|
-
case FormEnctype.multipart:
|
|
589
|
-
|
|
590
|
-
|
|
660
|
+
case FormEnctype.multipart:
|
|
661
|
+
return FormEnctype.multipart;
|
|
662
|
+
case FormEnctype.plain:
|
|
663
|
+
return FormEnctype.plain;
|
|
664
|
+
default:
|
|
665
|
+
return FormEnctype.urlEncoded;
|
|
591
666
|
}
|
|
592
667
|
}
|
|
593
668
|
class FormSubmission {
|
|
@@ -597,11 +672,15 @@ class FormSubmission {
|
|
|
597
672
|
this.formElement = formElement;
|
|
598
673
|
this.submitter = submitter;
|
|
599
674
|
this.formData = buildFormData(formElement, submitter);
|
|
675
|
+
this.location = expandURL(this.action);
|
|
676
|
+
if (this.method == FetchMethod.get) {
|
|
677
|
+
mergeFormDataEntries(this.location, [...this.body.entries()]);
|
|
678
|
+
}
|
|
600
679
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
601
680
|
this.mustRedirect = mustRedirect;
|
|
602
681
|
}
|
|
603
|
-
static confirmMethod(message,
|
|
604
|
-
return confirm(message);
|
|
682
|
+
static confirmMethod(message, _element) {
|
|
683
|
+
return Promise.resolve(confirm(message));
|
|
605
684
|
}
|
|
606
685
|
get method() {
|
|
607
686
|
var _a;
|
|
@@ -610,11 +689,13 @@ class FormSubmission {
|
|
|
610
689
|
}
|
|
611
690
|
get action() {
|
|
612
691
|
var _a;
|
|
613
|
-
const formElementAction = typeof this.formElement.action ===
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
692
|
+
const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
|
|
693
|
+
if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
|
|
694
|
+
return this.submitter.getAttribute("formaction") || "";
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
return this.formElement.getAttribute("action") || formElementAction || "";
|
|
698
|
+
}
|
|
618
699
|
}
|
|
619
700
|
get body() {
|
|
620
701
|
if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
|
|
@@ -637,7 +718,8 @@ class FormSubmission {
|
|
|
637
718
|
}, []);
|
|
638
719
|
}
|
|
639
720
|
get confirmationMessage() {
|
|
640
|
-
|
|
721
|
+
var _a;
|
|
722
|
+
return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-confirm")) || this.formElement.getAttribute("data-turbo-confirm");
|
|
641
723
|
}
|
|
642
724
|
get needsConfirmation() {
|
|
643
725
|
return this.confirmationMessage !== null;
|
|
@@ -645,7 +727,7 @@ class FormSubmission {
|
|
|
645
727
|
async start() {
|
|
646
728
|
const { initialized, requesting } = FormSubmissionState;
|
|
647
729
|
if (this.needsConfirmation) {
|
|
648
|
-
const answer = FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
|
|
730
|
+
const answer = await FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
|
|
649
731
|
if (!answer) {
|
|
650
732
|
return;
|
|
651
733
|
}
|
|
@@ -669,14 +751,19 @@ class FormSubmission {
|
|
|
669
751
|
if (token) {
|
|
670
752
|
headers["X-CSRF-Token"] = token;
|
|
671
753
|
}
|
|
672
|
-
|
|
754
|
+
}
|
|
755
|
+
if (this.requestAcceptsTurboStreamResponse(request)) {
|
|
756
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
673
757
|
}
|
|
674
758
|
}
|
|
675
|
-
requestStarted(
|
|
759
|
+
requestStarted(_request) {
|
|
676
760
|
var _a;
|
|
677
761
|
this.state = FormSubmissionState.waiting;
|
|
678
762
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
|
|
679
|
-
dispatch("turbo:submit-start", {
|
|
763
|
+
dispatch("turbo:submit-start", {
|
|
764
|
+
target: this.formElement,
|
|
765
|
+
detail: { formSubmission: this },
|
|
766
|
+
});
|
|
680
767
|
this.delegate.formSubmissionStarted(this);
|
|
681
768
|
}
|
|
682
769
|
requestPreventedHandlingResponse(request, response) {
|
|
@@ -702,25 +789,35 @@ class FormSubmission {
|
|
|
702
789
|
}
|
|
703
790
|
requestErrored(request, error) {
|
|
704
791
|
this.result = { success: false, error };
|
|
792
|
+
dispatch("turbo:fetch-request-error", {
|
|
793
|
+
target: this.formElement,
|
|
794
|
+
detail: { request, error },
|
|
795
|
+
});
|
|
705
796
|
this.delegate.formSubmissionErrored(this, error);
|
|
706
797
|
}
|
|
707
|
-
requestFinished(
|
|
798
|
+
requestFinished(_request) {
|
|
708
799
|
var _a;
|
|
709
800
|
this.state = FormSubmissionState.stopped;
|
|
710
801
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
|
|
711
|
-
dispatch("turbo:submit-end", {
|
|
802
|
+
dispatch("turbo:submit-end", {
|
|
803
|
+
target: this.formElement,
|
|
804
|
+
detail: Object.assign({ formSubmission: this }, this.result),
|
|
805
|
+
});
|
|
712
806
|
this.delegate.formSubmissionFinished(this);
|
|
713
807
|
}
|
|
714
808
|
requestMustRedirect(request) {
|
|
715
809
|
return !request.isIdempotent && this.mustRedirect;
|
|
716
810
|
}
|
|
811
|
+
requestAcceptsTurboStreamResponse(request) {
|
|
812
|
+
return !request.isIdempotent || this.formElement.hasAttribute("data-turbo-stream");
|
|
813
|
+
}
|
|
717
814
|
}
|
|
718
815
|
function buildFormData(formElement, submitter) {
|
|
719
816
|
const formData = new FormData(formElement);
|
|
720
817
|
const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
|
|
721
818
|
const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
|
|
722
|
-
if (name
|
|
723
|
-
formData.append(name, value);
|
|
819
|
+
if (name) {
|
|
820
|
+
formData.append(name, value || "");
|
|
724
821
|
}
|
|
725
822
|
return formData;
|
|
726
823
|
}
|
|
@@ -734,18 +831,27 @@ function getCookieValue(cookieName) {
|
|
|
734
831
|
}
|
|
735
832
|
}
|
|
736
833
|
}
|
|
737
|
-
function getMetaContent(name) {
|
|
738
|
-
const element = document.querySelector(`meta[name="${name}"]`);
|
|
739
|
-
return element && element.content;
|
|
740
|
-
}
|
|
741
834
|
function responseSucceededWithoutRedirect(response) {
|
|
742
835
|
return response.statusCode == 200 && !response.redirected;
|
|
743
836
|
}
|
|
837
|
+
function mergeFormDataEntries(url, entries) {
|
|
838
|
+
const searchParams = new URLSearchParams();
|
|
839
|
+
for (const [name, value] of entries) {
|
|
840
|
+
if (value instanceof File)
|
|
841
|
+
continue;
|
|
842
|
+
searchParams.append(name, value);
|
|
843
|
+
}
|
|
844
|
+
url.search = searchParams.toString();
|
|
845
|
+
return url;
|
|
846
|
+
}
|
|
744
847
|
|
|
745
848
|
class Snapshot {
|
|
746
849
|
constructor(element) {
|
|
747
850
|
this.element = element;
|
|
748
851
|
}
|
|
852
|
+
get activeElement() {
|
|
853
|
+
return this.element.ownerDocument.activeElement;
|
|
854
|
+
}
|
|
749
855
|
get children() {
|
|
750
856
|
return [...this.element.children];
|
|
751
857
|
}
|
|
@@ -759,7 +865,14 @@ class Snapshot {
|
|
|
759
865
|
return this.element.isConnected;
|
|
760
866
|
}
|
|
761
867
|
get firstAutofocusableElement() {
|
|
762
|
-
|
|
868
|
+
const inertDisabledOrHidden = "[inert], :disabled, [hidden], details:not([open]), dialog:not([open])";
|
|
869
|
+
for (const element of this.element.querySelectorAll("[autofocus]")) {
|
|
870
|
+
if (element.closest(inertDisabledOrHidden) == null)
|
|
871
|
+
return element;
|
|
872
|
+
else
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
return null;
|
|
763
876
|
}
|
|
764
877
|
get permanentElements() {
|
|
765
878
|
return [...this.element.querySelectorAll("[id][data-turbo-permanent]")];
|
|
@@ -780,35 +893,59 @@ class Snapshot {
|
|
|
780
893
|
}
|
|
781
894
|
}
|
|
782
895
|
|
|
783
|
-
class
|
|
784
|
-
constructor(delegate,
|
|
896
|
+
class FormSubmitObserver {
|
|
897
|
+
constructor(delegate, eventTarget) {
|
|
898
|
+
this.started = false;
|
|
899
|
+
this.submitCaptured = () => {
|
|
900
|
+
this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
|
|
901
|
+
this.eventTarget.addEventListener("submit", this.submitBubbled, false);
|
|
902
|
+
};
|
|
785
903
|
this.submitBubbled = ((event) => {
|
|
786
|
-
|
|
787
|
-
|
|
904
|
+
if (!event.defaultPrevented) {
|
|
905
|
+
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
788
906
|
const submitter = event.submitter || undefined;
|
|
789
|
-
|
|
790
|
-
|
|
907
|
+
if (form &&
|
|
908
|
+
submissionDoesNotDismissDialog(form, submitter) &&
|
|
909
|
+
submissionDoesNotTargetIFrame(form, submitter) &&
|
|
910
|
+
this.delegate.willSubmitForm(form, submitter)) {
|
|
791
911
|
event.preventDefault();
|
|
792
|
-
|
|
793
|
-
this.delegate.formSubmissionIntercepted(form, submitter);
|
|
912
|
+
this.delegate.formSubmitted(form, submitter);
|
|
794
913
|
}
|
|
795
914
|
}
|
|
796
915
|
});
|
|
797
916
|
this.delegate = delegate;
|
|
798
|
-
this.
|
|
917
|
+
this.eventTarget = eventTarget;
|
|
799
918
|
}
|
|
800
919
|
start() {
|
|
801
|
-
|
|
920
|
+
if (!this.started) {
|
|
921
|
+
this.eventTarget.addEventListener("submit", this.submitCaptured, true);
|
|
922
|
+
this.started = true;
|
|
923
|
+
}
|
|
802
924
|
}
|
|
803
925
|
stop() {
|
|
804
|
-
|
|
926
|
+
if (this.started) {
|
|
927
|
+
this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
|
|
928
|
+
this.started = false;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
function submissionDoesNotDismissDialog(form, submitter) {
|
|
933
|
+
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
934
|
+
return method != "dialog";
|
|
935
|
+
}
|
|
936
|
+
function submissionDoesNotTargetIFrame(form, submitter) {
|
|
937
|
+
const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
|
|
938
|
+
for (const element of document.getElementsByName(target)) {
|
|
939
|
+
if (element instanceof HTMLIFrameElement)
|
|
940
|
+
return false;
|
|
805
941
|
}
|
|
942
|
+
return true;
|
|
806
943
|
}
|
|
807
944
|
|
|
808
945
|
class View {
|
|
809
946
|
constructor(delegate, element) {
|
|
810
|
-
this.resolveRenderPromise = (
|
|
811
|
-
this.resolveInterceptionPromise = (
|
|
947
|
+
this.resolveRenderPromise = (_value) => { };
|
|
948
|
+
this.resolveInterceptionPromise = (_value) => { };
|
|
812
949
|
this.delegate = delegate;
|
|
813
950
|
this.element = element;
|
|
814
951
|
}
|
|
@@ -853,15 +990,17 @@ class View {
|
|
|
853
990
|
const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
|
|
854
991
|
if (shouldRender) {
|
|
855
992
|
try {
|
|
856
|
-
this.renderPromise = new Promise(resolve => this.resolveRenderPromise = resolve);
|
|
993
|
+
this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
|
|
857
994
|
this.renderer = renderer;
|
|
858
|
-
this.prepareToRenderSnapshot(renderer);
|
|
859
|
-
const renderInterception = new Promise(resolve => this.resolveInterceptionPromise = resolve);
|
|
860
|
-
const
|
|
995
|
+
await this.prepareToRenderSnapshot(renderer);
|
|
996
|
+
const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
|
|
997
|
+
const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
|
|
998
|
+
const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
|
|
861
999
|
if (!immediateRender)
|
|
862
1000
|
await renderInterception;
|
|
863
1001
|
await this.renderSnapshot(renderer);
|
|
864
1002
|
this.delegate.viewRenderedSnapshot(snapshot, isPreview);
|
|
1003
|
+
this.delegate.preloadOnLoadLinksForView(this.element);
|
|
865
1004
|
this.finishRenderingSnapshot(renderer);
|
|
866
1005
|
}
|
|
867
1006
|
finally {
|
|
@@ -871,15 +1010,15 @@ class View {
|
|
|
871
1010
|
}
|
|
872
1011
|
}
|
|
873
1012
|
else {
|
|
874
|
-
this.invalidate();
|
|
1013
|
+
this.invalidate(renderer.reloadReason);
|
|
875
1014
|
}
|
|
876
1015
|
}
|
|
877
|
-
invalidate() {
|
|
878
|
-
this.delegate.viewInvalidated();
|
|
1016
|
+
invalidate(reason) {
|
|
1017
|
+
this.delegate.viewInvalidated(reason);
|
|
879
1018
|
}
|
|
880
|
-
prepareToRenderSnapshot(renderer) {
|
|
1019
|
+
async prepareToRenderSnapshot(renderer) {
|
|
881
1020
|
this.markAsPreview(renderer.isPreview);
|
|
882
|
-
renderer.prepareToRender();
|
|
1021
|
+
await renderer.prepareToRender();
|
|
883
1022
|
}
|
|
884
1023
|
markAsPreview(isPreview) {
|
|
885
1024
|
if (isPreview) {
|
|
@@ -906,65 +1045,122 @@ class FrameView extends View {
|
|
|
906
1045
|
}
|
|
907
1046
|
}
|
|
908
1047
|
|
|
909
|
-
class
|
|
910
|
-
constructor(delegate,
|
|
911
|
-
this.
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
else {
|
|
916
|
-
delete this.clickEvent;
|
|
917
|
-
}
|
|
1048
|
+
class LinkClickObserver {
|
|
1049
|
+
constructor(delegate, eventTarget) {
|
|
1050
|
+
this.started = false;
|
|
1051
|
+
this.clickCaptured = () => {
|
|
1052
|
+
this.eventTarget.removeEventListener("click", this.clickBubbled, false);
|
|
1053
|
+
this.eventTarget.addEventListener("click", this.clickBubbled, false);
|
|
918
1054
|
};
|
|
919
|
-
this.
|
|
920
|
-
if (
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
this.
|
|
1055
|
+
this.clickBubbled = (event) => {
|
|
1056
|
+
if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
|
|
1057
|
+
const target = (event.composedPath && event.composedPath()[0]) || event.target;
|
|
1058
|
+
const link = this.findLinkFromClickTarget(target);
|
|
1059
|
+
if (link && doesNotTargetIFrame(link)) {
|
|
1060
|
+
const location = this.getLocationForLink(link);
|
|
1061
|
+
if (this.delegate.willFollowLinkToLocation(link, location, event)) {
|
|
1062
|
+
event.preventDefault();
|
|
1063
|
+
this.delegate.followedLinkToLocation(link, location);
|
|
1064
|
+
}
|
|
925
1065
|
}
|
|
926
1066
|
}
|
|
927
|
-
delete this.clickEvent;
|
|
928
|
-
});
|
|
929
|
-
this.willVisit = () => {
|
|
930
|
-
delete this.clickEvent;
|
|
931
1067
|
};
|
|
932
1068
|
this.delegate = delegate;
|
|
933
|
-
this.
|
|
1069
|
+
this.eventTarget = eventTarget;
|
|
1070
|
+
}
|
|
1071
|
+
start() {
|
|
1072
|
+
if (!this.started) {
|
|
1073
|
+
this.eventTarget.addEventListener("click", this.clickCaptured, true);
|
|
1074
|
+
this.started = true;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
stop() {
|
|
1078
|
+
if (this.started) {
|
|
1079
|
+
this.eventTarget.removeEventListener("click", this.clickCaptured, true);
|
|
1080
|
+
this.started = false;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
clickEventIsSignificant(event) {
|
|
1084
|
+
return !((event.target && event.target.isContentEditable) ||
|
|
1085
|
+
event.defaultPrevented ||
|
|
1086
|
+
event.which > 1 ||
|
|
1087
|
+
event.altKey ||
|
|
1088
|
+
event.ctrlKey ||
|
|
1089
|
+
event.metaKey ||
|
|
1090
|
+
event.shiftKey);
|
|
1091
|
+
}
|
|
1092
|
+
findLinkFromClickTarget(target) {
|
|
1093
|
+
if (target instanceof Element) {
|
|
1094
|
+
return target.closest("a[href]:not([target^=_]):not([download])");
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
getLocationForLink(link) {
|
|
1098
|
+
return expandURL(link.getAttribute("href") || "");
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function doesNotTargetIFrame(anchor) {
|
|
1102
|
+
for (const element of document.getElementsByName(anchor.target)) {
|
|
1103
|
+
if (element instanceof HTMLIFrameElement)
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
class FormLinkClickObserver {
|
|
1110
|
+
constructor(delegate, element) {
|
|
1111
|
+
this.delegate = delegate;
|
|
1112
|
+
this.linkClickObserver = new LinkClickObserver(this, element);
|
|
934
1113
|
}
|
|
935
1114
|
start() {
|
|
936
|
-
this.
|
|
937
|
-
document.addEventListener("turbo:click", this.linkClicked);
|
|
938
|
-
document.addEventListener("turbo:before-visit", this.willVisit);
|
|
1115
|
+
this.linkClickObserver.start();
|
|
939
1116
|
}
|
|
940
1117
|
stop() {
|
|
941
|
-
this.
|
|
942
|
-
|
|
943
|
-
|
|
1118
|
+
this.linkClickObserver.stop();
|
|
1119
|
+
}
|
|
1120
|
+
willFollowLinkToLocation(link, location, originalEvent) {
|
|
1121
|
+
return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
|
|
1122
|
+
link.hasAttribute("data-turbo-method"));
|
|
944
1123
|
}
|
|
945
|
-
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1124
|
+
followedLinkToLocation(link, location) {
|
|
1125
|
+
const action = location.href;
|
|
1126
|
+
const form = document.createElement("form");
|
|
1127
|
+
form.setAttribute("data-turbo", "true");
|
|
1128
|
+
form.setAttribute("action", action);
|
|
1129
|
+
form.setAttribute("hidden", "");
|
|
1130
|
+
const method = link.getAttribute("data-turbo-method");
|
|
1131
|
+
if (method)
|
|
1132
|
+
form.setAttribute("method", method);
|
|
1133
|
+
const turboFrame = link.getAttribute("data-turbo-frame");
|
|
1134
|
+
if (turboFrame)
|
|
1135
|
+
form.setAttribute("data-turbo-frame", turboFrame);
|
|
1136
|
+
const turboConfirm = link.getAttribute("data-turbo-confirm");
|
|
1137
|
+
if (turboConfirm)
|
|
1138
|
+
form.setAttribute("data-turbo-confirm", turboConfirm);
|
|
1139
|
+
const turboStream = link.hasAttribute("data-turbo-stream");
|
|
1140
|
+
if (turboStream)
|
|
1141
|
+
form.setAttribute("data-turbo-stream", "");
|
|
1142
|
+
this.delegate.submittedFormLinkToLocation(link, location, form);
|
|
1143
|
+
document.body.appendChild(form);
|
|
1144
|
+
form.requestSubmit();
|
|
1145
|
+
form.remove();
|
|
952
1146
|
}
|
|
953
1147
|
}
|
|
954
1148
|
|
|
955
1149
|
class Bardo {
|
|
956
|
-
constructor(permanentElementMap) {
|
|
1150
|
+
constructor(delegate, permanentElementMap) {
|
|
1151
|
+
this.delegate = delegate;
|
|
957
1152
|
this.permanentElementMap = permanentElementMap;
|
|
958
1153
|
}
|
|
959
|
-
static preservingPermanentElements(permanentElementMap, callback) {
|
|
960
|
-
const bardo = new this(permanentElementMap);
|
|
1154
|
+
static preservingPermanentElements(delegate, permanentElementMap, callback) {
|
|
1155
|
+
const bardo = new this(delegate, permanentElementMap);
|
|
961
1156
|
bardo.enter();
|
|
962
1157
|
callback();
|
|
963
1158
|
bardo.leave();
|
|
964
1159
|
}
|
|
965
1160
|
enter() {
|
|
966
1161
|
for (const id in this.permanentElementMap) {
|
|
967
|
-
const [, newPermanentElement] = this.permanentElementMap[id];
|
|
1162
|
+
const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
|
|
1163
|
+
this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
|
|
968
1164
|
this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
|
|
969
1165
|
}
|
|
970
1166
|
}
|
|
@@ -973,6 +1169,7 @@ class Bardo {
|
|
|
973
1169
|
const [currentPermanentElement] = this.permanentElementMap[id];
|
|
974
1170
|
this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
|
|
975
1171
|
this.replacePlaceholderWithPermanentElement(currentPermanentElement);
|
|
1172
|
+
this.delegate.leavingBardo(currentPermanentElement);
|
|
976
1173
|
}
|
|
977
1174
|
}
|
|
978
1175
|
replaceNewPermanentElementWithPlaceholder(permanentElement) {
|
|
@@ -988,7 +1185,7 @@ class Bardo {
|
|
|
988
1185
|
placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
|
|
989
1186
|
}
|
|
990
1187
|
getPlaceholderById(id) {
|
|
991
|
-
return this.placeholders.find(element => element.content == id);
|
|
1188
|
+
return this.placeholders.find((element) => element.content == id);
|
|
992
1189
|
}
|
|
993
1190
|
get placeholders() {
|
|
994
1191
|
return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
|
|
@@ -1002,16 +1199,21 @@ function createPlaceholderForPermanentElement(permanentElement) {
|
|
|
1002
1199
|
}
|
|
1003
1200
|
|
|
1004
1201
|
class Renderer {
|
|
1005
|
-
constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
|
|
1202
|
+
constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
|
1203
|
+
this.activeElement = null;
|
|
1006
1204
|
this.currentSnapshot = currentSnapshot;
|
|
1007
1205
|
this.newSnapshot = newSnapshot;
|
|
1008
1206
|
this.isPreview = isPreview;
|
|
1009
1207
|
this.willRender = willRender;
|
|
1010
|
-
this.
|
|
1208
|
+
this.renderElement = renderElement;
|
|
1209
|
+
this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
|
|
1011
1210
|
}
|
|
1012
1211
|
get shouldRender() {
|
|
1013
1212
|
return true;
|
|
1014
1213
|
}
|
|
1214
|
+
get reloadReason() {
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1015
1217
|
prepareToRender() {
|
|
1016
1218
|
return;
|
|
1017
1219
|
}
|
|
@@ -1021,23 +1223,8 @@ class Renderer {
|
|
|
1021
1223
|
delete this.resolvingFunctions;
|
|
1022
1224
|
}
|
|
1023
1225
|
}
|
|
1024
|
-
createScriptElement(element) {
|
|
1025
|
-
if (element.getAttribute("data-turbo-eval") == "false") {
|
|
1026
|
-
return element;
|
|
1027
|
-
}
|
|
1028
|
-
else {
|
|
1029
|
-
const createdScriptElement = document.createElement("script");
|
|
1030
|
-
if (this.cspNonce) {
|
|
1031
|
-
createdScriptElement.nonce = this.cspNonce;
|
|
1032
|
-
}
|
|
1033
|
-
createdScriptElement.textContent = element.textContent;
|
|
1034
|
-
createdScriptElement.async = false;
|
|
1035
|
-
copyElementAttributes(createdScriptElement, element);
|
|
1036
|
-
return createdScriptElement;
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
1226
|
preservingPermanentElements(callback) {
|
|
1040
|
-
Bardo.preservingPermanentElements(this.permanentElementMap, callback);
|
|
1227
|
+
Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
|
|
1041
1228
|
}
|
|
1042
1229
|
focusFirstAutofocusableElement() {
|
|
1043
1230
|
const element = this.connectedSnapshot.firstAutofocusableElement;
|
|
@@ -1045,6 +1232,19 @@ class Renderer {
|
|
|
1045
1232
|
element.focus();
|
|
1046
1233
|
}
|
|
1047
1234
|
}
|
|
1235
|
+
enteringBardo(currentPermanentElement) {
|
|
1236
|
+
if (this.activeElement)
|
|
1237
|
+
return;
|
|
1238
|
+
if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
|
|
1239
|
+
this.activeElement = this.currentSnapshot.activeElement;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
leavingBardo(currentPermanentElement) {
|
|
1243
|
+
if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
|
|
1244
|
+
this.activeElement.focus();
|
|
1245
|
+
this.activeElement = null;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1048
1248
|
get connectedSnapshot() {
|
|
1049
1249
|
return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
|
|
1050
1250
|
}
|
|
@@ -1057,21 +1257,28 @@ class Renderer {
|
|
|
1057
1257
|
get permanentElementMap() {
|
|
1058
1258
|
return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
|
|
1059
1259
|
}
|
|
1060
|
-
get cspNonce() {
|
|
1061
|
-
var _a;
|
|
1062
|
-
return (_a = document.head.querySelector('meta[name="csp-nonce"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
function copyElementAttributes(destinationElement, sourceElement) {
|
|
1066
|
-
for (const { name, value } of [...sourceElement.attributes]) {
|
|
1067
|
-
destinationElement.setAttribute(name, value);
|
|
1068
|
-
}
|
|
1069
1260
|
}
|
|
1070
1261
|
function elementIsFocusable(element) {
|
|
1071
1262
|
return element && typeof element.focus == "function";
|
|
1072
1263
|
}
|
|
1073
1264
|
|
|
1074
1265
|
class FrameRenderer extends Renderer {
|
|
1266
|
+
constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
|
1267
|
+
super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
|
|
1268
|
+
this.delegate = delegate;
|
|
1269
|
+
}
|
|
1270
|
+
static renderElement(currentElement, newElement) {
|
|
1271
|
+
var _a;
|
|
1272
|
+
const destinationRange = document.createRange();
|
|
1273
|
+
destinationRange.selectNodeContents(currentElement);
|
|
1274
|
+
destinationRange.deleteContents();
|
|
1275
|
+
const frameElement = newElement;
|
|
1276
|
+
const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
|
|
1277
|
+
if (sourceRange) {
|
|
1278
|
+
sourceRange.selectNodeContents(frameElement);
|
|
1279
|
+
currentElement.appendChild(sourceRange.extractContents());
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1075
1282
|
get shouldRender() {
|
|
1076
1283
|
return true;
|
|
1077
1284
|
}
|
|
@@ -1087,23 +1294,16 @@ class FrameRenderer extends Renderer {
|
|
|
1087
1294
|
this.activateScriptElements();
|
|
1088
1295
|
}
|
|
1089
1296
|
loadFrameElement() {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
destinationRange.selectNodeContents(this.currentElement);
|
|
1093
|
-
destinationRange.deleteContents();
|
|
1094
|
-
const frameElement = this.newElement;
|
|
1095
|
-
const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
|
|
1096
|
-
if (sourceRange) {
|
|
1097
|
-
sourceRange.selectNodeContents(frameElement);
|
|
1098
|
-
this.currentElement.appendChild(sourceRange.extractContents());
|
|
1099
|
-
}
|
|
1297
|
+
this.delegate.willRenderFrame(this.currentElement, this.newElement);
|
|
1298
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
1100
1299
|
}
|
|
1101
1300
|
scrollFrameIntoView() {
|
|
1102
1301
|
if (this.currentElement.autoscroll || this.newElement.autoscroll) {
|
|
1103
1302
|
const element = this.currentElement.firstElementChild;
|
|
1104
1303
|
const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
|
|
1304
|
+
const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
|
|
1105
1305
|
if (element) {
|
|
1106
|
-
element.scrollIntoView({ block });
|
|
1306
|
+
element.scrollIntoView({ block, behavior });
|
|
1107
1307
|
return true;
|
|
1108
1308
|
}
|
|
1109
1309
|
}
|
|
@@ -1111,7 +1311,7 @@ class FrameRenderer extends Renderer {
|
|
|
1111
1311
|
}
|
|
1112
1312
|
activateScriptElements() {
|
|
1113
1313
|
for (const inertScriptElement of this.newScriptElements) {
|
|
1114
|
-
const activatedScriptElement =
|
|
1314
|
+
const activatedScriptElement = activateScriptElement(inertScriptElement);
|
|
1115
1315
|
inertScriptElement.replaceWith(activatedScriptElement);
|
|
1116
1316
|
}
|
|
1117
1317
|
}
|
|
@@ -1127,6 +1327,14 @@ function readScrollLogicalPosition(value, defaultValue) {
|
|
|
1127
1327
|
return defaultValue;
|
|
1128
1328
|
}
|
|
1129
1329
|
}
|
|
1330
|
+
function readScrollBehavior(value, defaultValue) {
|
|
1331
|
+
if (value == "auto" || value == "smooth") {
|
|
1332
|
+
return value;
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
return defaultValue;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1130
1338
|
|
|
1131
1339
|
class ProgressBar {
|
|
1132
1340
|
constructor() {
|
|
@@ -1150,7 +1358,7 @@ class ProgressBar {
|
|
|
1150
1358
|
left: 0;
|
|
1151
1359
|
height: 3px;
|
|
1152
1360
|
background: #0076ff;
|
|
1153
|
-
z-index:
|
|
1361
|
+
z-index: 2147483647;
|
|
1154
1362
|
transition:
|
|
1155
1363
|
width ${ProgressBar.animationDuration}ms ease-out,
|
|
1156
1364
|
opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
|
|
@@ -1209,13 +1417,16 @@ class ProgressBar {
|
|
|
1209
1417
|
}
|
|
1210
1418
|
refresh() {
|
|
1211
1419
|
requestAnimationFrame(() => {
|
|
1212
|
-
this.progressElement.style.width = `${10 +
|
|
1420
|
+
this.progressElement.style.width = `${10 + this.value * 90}%`;
|
|
1213
1421
|
});
|
|
1214
1422
|
}
|
|
1215
1423
|
createStylesheetElement() {
|
|
1216
1424
|
const element = document.createElement("style");
|
|
1217
1425
|
element.type = "text/css";
|
|
1218
1426
|
element.textContent = ProgressBar.defaultCSS;
|
|
1427
|
+
if (this.cspNonce) {
|
|
1428
|
+
element.nonce = this.cspNonce;
|
|
1429
|
+
}
|
|
1219
1430
|
return element;
|
|
1220
1431
|
}
|
|
1221
1432
|
createProgressElement() {
|
|
@@ -1223,6 +1434,9 @@ class ProgressBar {
|
|
|
1223
1434
|
element.className = "turbo-progress-bar";
|
|
1224
1435
|
return element;
|
|
1225
1436
|
}
|
|
1437
|
+
get cspNonce() {
|
|
1438
|
+
return getMetaContent("csp-nonce");
|
|
1439
|
+
}
|
|
1226
1440
|
}
|
|
1227
1441
|
ProgressBar.animationDuration = 300;
|
|
1228
1442
|
|
|
@@ -1239,14 +1453,14 @@ class HeadSnapshot extends Snapshot {
|
|
|
1239
1453
|
: {
|
|
1240
1454
|
type: elementType(element),
|
|
1241
1455
|
tracked: elementIsTracked(element),
|
|
1242
|
-
elements: []
|
|
1456
|
+
elements: [],
|
|
1243
1457
|
};
|
|
1244
1458
|
return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
|
|
1245
1459
|
}, {});
|
|
1246
1460
|
}
|
|
1247
1461
|
get trackedElementSignature() {
|
|
1248
1462
|
return Object.keys(this.detailsByOuterHTML)
|
|
1249
|
-
.filter(outerHTML => this.detailsByOuterHTML[outerHTML].tracked)
|
|
1463
|
+
.filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
|
|
1250
1464
|
.join("");
|
|
1251
1465
|
}
|
|
1252
1466
|
getScriptElementsNotInSnapshot(snapshot) {
|
|
@@ -1257,8 +1471,8 @@ class HeadSnapshot extends Snapshot {
|
|
|
1257
1471
|
}
|
|
1258
1472
|
getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
|
|
1259
1473
|
return Object.keys(this.detailsByOuterHTML)
|
|
1260
|
-
.filter(outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))
|
|
1261
|
-
.map(outerHTML => this.detailsByOuterHTML[outerHTML])
|
|
1474
|
+
.filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
|
|
1475
|
+
.map((outerHTML) => this.detailsByOuterHTML[outerHTML])
|
|
1262
1476
|
.filter(({ type }) => type == matchedType)
|
|
1263
1477
|
.map(({ elements: [element] }) => element);
|
|
1264
1478
|
}
|
|
@@ -1278,13 +1492,11 @@ class HeadSnapshot extends Snapshot {
|
|
|
1278
1492
|
}
|
|
1279
1493
|
getMetaValue(name) {
|
|
1280
1494
|
const element = this.findMetaElementByName(name);
|
|
1281
|
-
return element
|
|
1282
|
-
? element.getAttribute("content")
|
|
1283
|
-
: null;
|
|
1495
|
+
return element ? element.getAttribute("content") : null;
|
|
1284
1496
|
}
|
|
1285
1497
|
findMetaElementByName(name) {
|
|
1286
1498
|
return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
|
|
1287
|
-
const { elements: [element] } = this.detailsByOuterHTML[outerHTML];
|
|
1499
|
+
const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
|
|
1288
1500
|
return elementIsMetaElementWithName(element, name) ? element : result;
|
|
1289
1501
|
}, undefined);
|
|
1290
1502
|
}
|
|
@@ -1385,6 +1597,9 @@ const defaultOptions = {
|
|
|
1385
1597
|
historyChanged: false,
|
|
1386
1598
|
visitCachedSnapshot: () => { },
|
|
1387
1599
|
willRender: true,
|
|
1600
|
+
updateHistory: true,
|
|
1601
|
+
shouldCacheSnapshot: true,
|
|
1602
|
+
acceptsStreamResponse: false,
|
|
1388
1603
|
};
|
|
1389
1604
|
var SystemStatusCode;
|
|
1390
1605
|
(function (SystemStatusCode) {
|
|
@@ -1399,12 +1614,15 @@ class Visit {
|
|
|
1399
1614
|
this.followedRedirect = false;
|
|
1400
1615
|
this.historyChanged = false;
|
|
1401
1616
|
this.scrolled = false;
|
|
1617
|
+
this.shouldCacheSnapshot = true;
|
|
1618
|
+
this.acceptsStreamResponse = false;
|
|
1402
1619
|
this.snapshotCached = false;
|
|
1403
1620
|
this.state = VisitState.initialized;
|
|
1404
1621
|
this.delegate = delegate;
|
|
1405
1622
|
this.location = location;
|
|
1406
1623
|
this.restorationIdentifier = restorationIdentifier || uuid();
|
|
1407
|
-
|
|
1624
|
+
this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
|
|
1625
|
+
const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1408
1626
|
this.action = action;
|
|
1409
1627
|
this.historyChanged = historyChanged;
|
|
1410
1628
|
this.referrer = referrer;
|
|
@@ -1413,7 +1631,10 @@ class Visit {
|
|
|
1413
1631
|
this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
|
|
1414
1632
|
this.visitCachedSnapshot = visitCachedSnapshot;
|
|
1415
1633
|
this.willRender = willRender;
|
|
1634
|
+
this.updateHistory = updateHistory;
|
|
1416
1635
|
this.scrolled = !willRender;
|
|
1636
|
+
this.shouldCacheSnapshot = shouldCacheSnapshot;
|
|
1637
|
+
this.acceptsStreamResponse = acceptsStreamResponse;
|
|
1417
1638
|
}
|
|
1418
1639
|
get adapter() {
|
|
1419
1640
|
return this.delegate.adapter;
|
|
@@ -1445,28 +1666,33 @@ class Visit {
|
|
|
1445
1666
|
}
|
|
1446
1667
|
this.cancelRender();
|
|
1447
1668
|
this.state = VisitState.canceled;
|
|
1669
|
+
this.resolvingFunctions.reject();
|
|
1448
1670
|
}
|
|
1449
1671
|
}
|
|
1450
1672
|
complete() {
|
|
1451
1673
|
if (this.state == VisitState.started) {
|
|
1452
1674
|
this.recordTimingMetric(TimingMetric.visitEnd);
|
|
1453
1675
|
this.state = VisitState.completed;
|
|
1454
|
-
this.adapter.visitCompleted(this);
|
|
1455
|
-
this.delegate.visitCompleted(this);
|
|
1456
1676
|
this.followRedirect();
|
|
1677
|
+
if (!this.followedRedirect) {
|
|
1678
|
+
this.adapter.visitCompleted(this);
|
|
1679
|
+
this.delegate.visitCompleted(this);
|
|
1680
|
+
}
|
|
1681
|
+
this.resolvingFunctions.resolve();
|
|
1457
1682
|
}
|
|
1458
1683
|
}
|
|
1459
1684
|
fail() {
|
|
1460
1685
|
if (this.state == VisitState.started) {
|
|
1461
1686
|
this.state = VisitState.failed;
|
|
1462
1687
|
this.adapter.visitFailed(this);
|
|
1688
|
+
this.resolvingFunctions.reject();
|
|
1463
1689
|
}
|
|
1464
1690
|
}
|
|
1465
1691
|
changeHistory() {
|
|
1466
1692
|
var _a;
|
|
1467
|
-
if (!this.historyChanged) {
|
|
1693
|
+
if (!this.historyChanged && this.updateHistory) {
|
|
1468
1694
|
const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
|
|
1469
|
-
const method =
|
|
1695
|
+
const method = getHistoryMethodForAction(actionForHistory);
|
|
1470
1696
|
this.history.update(method, this.location, this.restorationIdentifier);
|
|
1471
1697
|
this.historyChanged = true;
|
|
1472
1698
|
}
|
|
@@ -1511,16 +1737,18 @@ class Visit {
|
|
|
1511
1737
|
if (this.response) {
|
|
1512
1738
|
const { statusCode, responseHTML } = this.response;
|
|
1513
1739
|
this.render(async () => {
|
|
1514
|
-
this.
|
|
1740
|
+
if (this.shouldCacheSnapshot)
|
|
1741
|
+
this.cacheSnapshot();
|
|
1515
1742
|
if (this.view.renderPromise)
|
|
1516
1743
|
await this.view.renderPromise;
|
|
1517
1744
|
if (isSuccessful(statusCode) && responseHTML != null) {
|
|
1518
|
-
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender);
|
|
1745
|
+
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
|
|
1746
|
+
this.performScroll();
|
|
1519
1747
|
this.adapter.visitRendered(this);
|
|
1520
1748
|
this.complete();
|
|
1521
1749
|
}
|
|
1522
1750
|
else {
|
|
1523
|
-
await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML));
|
|
1751
|
+
await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
|
|
1524
1752
|
this.adapter.visitRendered(this);
|
|
1525
1753
|
this.fail();
|
|
1526
1754
|
}
|
|
@@ -1555,7 +1783,8 @@ class Visit {
|
|
|
1555
1783
|
else {
|
|
1556
1784
|
if (this.view.renderPromise)
|
|
1557
1785
|
await this.view.renderPromise;
|
|
1558
|
-
await this.view.renderPage(snapshot, isPreview, this.willRender);
|
|
1786
|
+
await this.view.renderPage(snapshot, isPreview, this.willRender, this);
|
|
1787
|
+
this.performScroll();
|
|
1559
1788
|
this.adapter.visitRendered(this);
|
|
1560
1789
|
if (!isPreview) {
|
|
1561
1790
|
this.complete();
|
|
@@ -1568,8 +1797,9 @@ class Visit {
|
|
|
1568
1797
|
var _a;
|
|
1569
1798
|
if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
|
|
1570
1799
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1571
|
-
action:
|
|
1572
|
-
|
|
1800
|
+
action: "replace",
|
|
1801
|
+
willRender: false,
|
|
1802
|
+
response: this.response,
|
|
1573
1803
|
});
|
|
1574
1804
|
this.followedRedirect = true;
|
|
1575
1805
|
}
|
|
@@ -1578,20 +1808,28 @@ class Visit {
|
|
|
1578
1808
|
if (this.isSamePage) {
|
|
1579
1809
|
this.render(async () => {
|
|
1580
1810
|
this.cacheSnapshot();
|
|
1811
|
+
this.performScroll();
|
|
1581
1812
|
this.adapter.visitRendered(this);
|
|
1582
1813
|
});
|
|
1583
1814
|
}
|
|
1584
1815
|
}
|
|
1816
|
+
prepareHeadersForRequest(headers, request) {
|
|
1817
|
+
if (this.acceptsStreamResponse) {
|
|
1818
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1585
1821
|
requestStarted() {
|
|
1586
1822
|
this.startRequest();
|
|
1587
1823
|
}
|
|
1588
|
-
requestPreventedHandlingResponse(
|
|
1589
|
-
}
|
|
1824
|
+
requestPreventedHandlingResponse(_request, _response) { }
|
|
1590
1825
|
async requestSucceededWithResponse(request, response) {
|
|
1591
1826
|
const responseHTML = await response.responseHTML;
|
|
1592
1827
|
const { redirected, statusCode } = response;
|
|
1593
1828
|
if (responseHTML == undefined) {
|
|
1594
|
-
this.recordResponse({
|
|
1829
|
+
this.recordResponse({
|
|
1830
|
+
statusCode: SystemStatusCode.contentTypeMismatch,
|
|
1831
|
+
redirected,
|
|
1832
|
+
});
|
|
1595
1833
|
}
|
|
1596
1834
|
else {
|
|
1597
1835
|
this.redirectedToLocation = response.redirected ? response.location : undefined;
|
|
@@ -1602,20 +1840,26 @@ class Visit {
|
|
|
1602
1840
|
const responseHTML = await response.responseHTML;
|
|
1603
1841
|
const { redirected, statusCode } = response;
|
|
1604
1842
|
if (responseHTML == undefined) {
|
|
1605
|
-
this.recordResponse({
|
|
1843
|
+
this.recordResponse({
|
|
1844
|
+
statusCode: SystemStatusCode.contentTypeMismatch,
|
|
1845
|
+
redirected,
|
|
1846
|
+
});
|
|
1606
1847
|
}
|
|
1607
1848
|
else {
|
|
1608
1849
|
this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
|
|
1609
1850
|
}
|
|
1610
1851
|
}
|
|
1611
|
-
requestErrored(
|
|
1612
|
-
this.recordResponse({
|
|
1852
|
+
requestErrored(_request, _error) {
|
|
1853
|
+
this.recordResponse({
|
|
1854
|
+
statusCode: SystemStatusCode.networkFailure,
|
|
1855
|
+
redirected: false,
|
|
1856
|
+
});
|
|
1613
1857
|
}
|
|
1614
1858
|
requestFinished() {
|
|
1615
1859
|
this.finishRequest();
|
|
1616
1860
|
}
|
|
1617
1861
|
performScroll() {
|
|
1618
|
-
if (!this.scrolled) {
|
|
1862
|
+
if (!this.scrolled && !this.view.forceReloaded) {
|
|
1619
1863
|
if (this.action == "restore") {
|
|
1620
1864
|
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
|
|
1621
1865
|
}
|
|
@@ -1650,9 +1894,11 @@ class Visit {
|
|
|
1650
1894
|
}
|
|
1651
1895
|
getHistoryMethodForAction(action) {
|
|
1652
1896
|
switch (action) {
|
|
1653
|
-
case "replace":
|
|
1897
|
+
case "replace":
|
|
1898
|
+
return history.replaceState;
|
|
1654
1899
|
case "advance":
|
|
1655
|
-
case "restore":
|
|
1900
|
+
case "restore":
|
|
1901
|
+
return history.pushState;
|
|
1656
1902
|
}
|
|
1657
1903
|
}
|
|
1658
1904
|
hasPreloadedResponse() {
|
|
@@ -1671,18 +1917,17 @@ class Visit {
|
|
|
1671
1917
|
}
|
|
1672
1918
|
cacheSnapshot() {
|
|
1673
1919
|
if (!this.snapshotCached) {
|
|
1674
|
-
this.view.cacheSnapshot().then(snapshot => snapshot && this.visitCachedSnapshot(snapshot));
|
|
1920
|
+
this.view.cacheSnapshot().then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
|
|
1675
1921
|
this.snapshotCached = true;
|
|
1676
1922
|
}
|
|
1677
1923
|
}
|
|
1678
1924
|
async render(callback) {
|
|
1679
1925
|
this.cancelRender();
|
|
1680
|
-
await new Promise(resolve => {
|
|
1926
|
+
await new Promise((resolve) => {
|
|
1681
1927
|
this.frame = requestAnimationFrame(() => resolve());
|
|
1682
1928
|
});
|
|
1683
1929
|
await callback();
|
|
1684
1930
|
delete this.frame;
|
|
1685
|
-
this.performScroll();
|
|
1686
1931
|
}
|
|
1687
1932
|
cancelRender() {
|
|
1688
1933
|
if (this.frame) {
|
|
@@ -1697,19 +1942,19 @@ function isSuccessful(statusCode) {
|
|
|
1697
1942
|
|
|
1698
1943
|
class BrowserAdapter {
|
|
1699
1944
|
constructor(session) {
|
|
1700
|
-
this.progressBar = new ProgressBar;
|
|
1945
|
+
this.progressBar = new ProgressBar();
|
|
1701
1946
|
this.showProgressBar = () => {
|
|
1702
1947
|
this.progressBar.show();
|
|
1703
1948
|
};
|
|
1704
1949
|
this.session = session;
|
|
1705
1950
|
}
|
|
1706
1951
|
visitProposedToLocation(location, options) {
|
|
1707
|
-
this.navigator.startVisit(location, uuid(), options);
|
|
1952
|
+
return this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
|
|
1708
1953
|
}
|
|
1709
1954
|
visitStarted(visit) {
|
|
1955
|
+
this.location = visit.location;
|
|
1710
1956
|
visit.loadCachedSnapshot();
|
|
1711
1957
|
visit.issueRequest();
|
|
1712
|
-
visit.changeHistory();
|
|
1713
1958
|
visit.goToSamePageAnchor();
|
|
1714
1959
|
}
|
|
1715
1960
|
visitRequestStarted(visit) {
|
|
@@ -1729,29 +1974,31 @@ class BrowserAdapter {
|
|
|
1729
1974
|
case SystemStatusCode.networkFailure:
|
|
1730
1975
|
case SystemStatusCode.timeoutFailure:
|
|
1731
1976
|
case SystemStatusCode.contentTypeMismatch:
|
|
1732
|
-
return this.reload(
|
|
1977
|
+
return this.reload({
|
|
1978
|
+
reason: "request_failed",
|
|
1979
|
+
context: {
|
|
1980
|
+
statusCode,
|
|
1981
|
+
},
|
|
1982
|
+
});
|
|
1733
1983
|
default:
|
|
1734
1984
|
return visit.loadResponse();
|
|
1735
1985
|
}
|
|
1736
1986
|
}
|
|
1737
|
-
visitRequestFinished(
|
|
1987
|
+
visitRequestFinished(_visit) {
|
|
1738
1988
|
this.progressBar.setValue(1);
|
|
1739
1989
|
this.hideVisitProgressBar();
|
|
1740
1990
|
}
|
|
1741
|
-
visitCompleted(
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
this.reload();
|
|
1745
|
-
}
|
|
1746
|
-
visitFailed(visit) {
|
|
1991
|
+
visitCompleted(_visit) { }
|
|
1992
|
+
pageInvalidated(reason) {
|
|
1993
|
+
this.reload(reason);
|
|
1747
1994
|
}
|
|
1748
|
-
|
|
1749
|
-
}
|
|
1750
|
-
formSubmissionStarted(
|
|
1995
|
+
visitFailed(_visit) { }
|
|
1996
|
+
visitRendered(_visit) { }
|
|
1997
|
+
formSubmissionStarted(_formSubmission) {
|
|
1751
1998
|
this.progressBar.setValue(0);
|
|
1752
1999
|
this.showFormProgressBarAfterDelay();
|
|
1753
2000
|
}
|
|
1754
|
-
formSubmissionFinished(
|
|
2001
|
+
formSubmissionFinished(_formSubmission) {
|
|
1755
2002
|
this.progressBar.setValue(1);
|
|
1756
2003
|
this.hideFormProgressBar();
|
|
1757
2004
|
}
|
|
@@ -1777,8 +2024,11 @@ class BrowserAdapter {
|
|
|
1777
2024
|
delete this.formProgressBarTimeout;
|
|
1778
2025
|
}
|
|
1779
2026
|
}
|
|
1780
|
-
reload() {
|
|
1781
|
-
|
|
2027
|
+
reload(reason) {
|
|
2028
|
+
dispatch("turbo:reload", { detail: reason });
|
|
2029
|
+
if (!this.location)
|
|
2030
|
+
return;
|
|
2031
|
+
window.location.href = this.location.toString();
|
|
1782
2032
|
}
|
|
1783
2033
|
get navigator() {
|
|
1784
2034
|
return this.session.navigator;
|
|
@@ -1788,94 +2038,60 @@ class BrowserAdapter {
|
|
|
1788
2038
|
class CacheObserver {
|
|
1789
2039
|
constructor() {
|
|
1790
2040
|
this.started = false;
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
addEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
stop() {
|
|
1799
|
-
if (this.started) {
|
|
1800
|
-
this.started = false;
|
|
1801
|
-
removeEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
removeStaleElements() {
|
|
1805
|
-
const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
|
|
1806
|
-
for (const element of staleElements) {
|
|
1807
|
-
element.remove();
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
class FormSubmitObserver {
|
|
1813
|
-
constructor(delegate) {
|
|
1814
|
-
this.started = false;
|
|
1815
|
-
this.submitCaptured = () => {
|
|
1816
|
-
removeEventListener("submit", this.submitBubbled, false);
|
|
1817
|
-
addEventListener("submit", this.submitBubbled, false);
|
|
1818
|
-
};
|
|
1819
|
-
this.submitBubbled = ((event) => {
|
|
1820
|
-
if (!event.defaultPrevented) {
|
|
1821
|
-
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
1822
|
-
const submitter = event.submitter || undefined;
|
|
1823
|
-
if (form) {
|
|
1824
|
-
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
1825
|
-
if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
|
|
1826
|
-
event.preventDefault();
|
|
1827
|
-
this.delegate.formSubmitted(form, submitter);
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
2041
|
+
this.removeStaleElements = ((_event) => {
|
|
2042
|
+
const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
|
|
2043
|
+
for (const element of staleElements) {
|
|
2044
|
+
element.remove();
|
|
1830
2045
|
}
|
|
1831
2046
|
});
|
|
1832
|
-
this.delegate = delegate;
|
|
1833
2047
|
}
|
|
1834
2048
|
start() {
|
|
1835
2049
|
if (!this.started) {
|
|
1836
|
-
addEventListener("submit", this.submitCaptured, true);
|
|
1837
2050
|
this.started = true;
|
|
2051
|
+
addEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1838
2052
|
}
|
|
1839
2053
|
}
|
|
1840
2054
|
stop() {
|
|
1841
2055
|
if (this.started) {
|
|
1842
|
-
removeEventListener("submit", this.submitCaptured, true);
|
|
1843
2056
|
this.started = false;
|
|
2057
|
+
removeEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1844
2058
|
}
|
|
1845
2059
|
}
|
|
1846
2060
|
}
|
|
1847
2061
|
|
|
1848
2062
|
class FrameRedirector {
|
|
1849
|
-
constructor(element) {
|
|
2063
|
+
constructor(session, element) {
|
|
2064
|
+
this.session = session;
|
|
1850
2065
|
this.element = element;
|
|
1851
|
-
this.
|
|
1852
|
-
this.
|
|
2066
|
+
this.linkClickObserver = new LinkClickObserver(this, element);
|
|
2067
|
+
this.formSubmitObserver = new FormSubmitObserver(this, element);
|
|
1853
2068
|
}
|
|
1854
2069
|
start() {
|
|
1855
|
-
this.
|
|
1856
|
-
this.
|
|
2070
|
+
this.linkClickObserver.start();
|
|
2071
|
+
this.formSubmitObserver.start();
|
|
1857
2072
|
}
|
|
1858
2073
|
stop() {
|
|
1859
|
-
this.
|
|
1860
|
-
this.
|
|
2074
|
+
this.linkClickObserver.stop();
|
|
2075
|
+
this.formSubmitObserver.stop();
|
|
1861
2076
|
}
|
|
1862
|
-
|
|
2077
|
+
willFollowLinkToLocation(element) {
|
|
1863
2078
|
return this.shouldRedirect(element);
|
|
1864
2079
|
}
|
|
1865
|
-
|
|
2080
|
+
followedLinkToLocation(element, url) {
|
|
1866
2081
|
const frame = this.findFrameElement(element);
|
|
1867
2082
|
if (frame) {
|
|
1868
|
-
frame.delegate.
|
|
2083
|
+
frame.delegate.followedLinkToLocation(element, url);
|
|
1869
2084
|
}
|
|
1870
2085
|
}
|
|
1871
|
-
|
|
1872
|
-
return
|
|
2086
|
+
willSubmitForm(element, submitter) {
|
|
2087
|
+
return (element.closest("turbo-frame") == null &&
|
|
2088
|
+
this.shouldSubmit(element, submitter) &&
|
|
2089
|
+
this.shouldRedirect(element, submitter));
|
|
1873
2090
|
}
|
|
1874
|
-
|
|
2091
|
+
formSubmitted(element, submitter) {
|
|
1875
2092
|
const frame = this.findFrameElement(element, submitter);
|
|
1876
2093
|
if (frame) {
|
|
1877
|
-
frame.
|
|
1878
|
-
frame.delegate.formSubmissionIntercepted(element, submitter);
|
|
2094
|
+
frame.delegate.formSubmitted(element, submitter);
|
|
1879
2095
|
}
|
|
1880
2096
|
}
|
|
1881
2097
|
shouldSubmit(form, submitter) {
|
|
@@ -1886,8 +2102,16 @@ class FrameRedirector {
|
|
|
1886
2102
|
return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
|
|
1887
2103
|
}
|
|
1888
2104
|
shouldRedirect(element, submitter) {
|
|
1889
|
-
const
|
|
1890
|
-
|
|
2105
|
+
const isNavigatable = element instanceof HTMLFormElement
|
|
2106
|
+
? this.session.submissionIsNavigatable(element, submitter)
|
|
2107
|
+
: this.session.elementIsNavigatable(element);
|
|
2108
|
+
if (isNavigatable) {
|
|
2109
|
+
const frame = this.findFrameElement(element, submitter);
|
|
2110
|
+
return frame ? frame != element.closest("turbo-frame") : false;
|
|
2111
|
+
}
|
|
2112
|
+
else {
|
|
2113
|
+
return false;
|
|
2114
|
+
}
|
|
1891
2115
|
}
|
|
1892
2116
|
findFrameElement(element, submitter) {
|
|
1893
2117
|
const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
|
|
@@ -1917,7 +2141,7 @@ class History {
|
|
|
1917
2141
|
}
|
|
1918
2142
|
}
|
|
1919
2143
|
};
|
|
1920
|
-
this.onPageLoad = async (
|
|
2144
|
+
this.onPageLoad = async (_event) => {
|
|
1921
2145
|
await nextMicrotask();
|
|
1922
2146
|
this.pageLoaded = true;
|
|
1923
2147
|
};
|
|
@@ -1979,81 +2203,30 @@ class History {
|
|
|
1979
2203
|
}
|
|
1980
2204
|
}
|
|
1981
2205
|
|
|
1982
|
-
class
|
|
1983
|
-
constructor(delegate) {
|
|
1984
|
-
this.started = false;
|
|
1985
|
-
this.clickCaptured = () => {
|
|
1986
|
-
removeEventListener("click", this.clickBubbled, false);
|
|
1987
|
-
addEventListener("click", this.clickBubbled, false);
|
|
1988
|
-
};
|
|
1989
|
-
this.clickBubbled = (event) => {
|
|
1990
|
-
if (this.clickEventIsSignificant(event)) {
|
|
1991
|
-
const target = (event.composedPath && event.composedPath()[0]) || event.target;
|
|
1992
|
-
const link = this.findLinkFromClickTarget(target);
|
|
1993
|
-
if (link) {
|
|
1994
|
-
const location = this.getLocationForLink(link);
|
|
1995
|
-
if (this.delegate.willFollowLinkToLocation(link, location)) {
|
|
1996
|
-
event.preventDefault();
|
|
1997
|
-
this.delegate.followedLinkToLocation(link, location);
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
}
|
|
2001
|
-
};
|
|
2002
|
-
this.delegate = delegate;
|
|
2003
|
-
}
|
|
2004
|
-
start() {
|
|
2005
|
-
if (!this.started) {
|
|
2006
|
-
addEventListener("click", this.clickCaptured, true);
|
|
2007
|
-
this.started = true;
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
2010
|
-
stop() {
|
|
2011
|
-
if (this.started) {
|
|
2012
|
-
removeEventListener("click", this.clickCaptured, true);
|
|
2013
|
-
this.started = false;
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
clickEventIsSignificant(event) {
|
|
2017
|
-
return !((event.target && event.target.isContentEditable)
|
|
2018
|
-
|| event.defaultPrevented
|
|
2019
|
-
|| event.which > 1
|
|
2020
|
-
|| event.altKey
|
|
2021
|
-
|| event.ctrlKey
|
|
2022
|
-
|| event.metaKey
|
|
2023
|
-
|| event.shiftKey);
|
|
2024
|
-
}
|
|
2025
|
-
findLinkFromClickTarget(target) {
|
|
2026
|
-
if (target instanceof Element) {
|
|
2027
|
-
return target.closest("a[href]:not([target^=_]):not([download])");
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
getLocationForLink(link) {
|
|
2031
|
-
return expandURL(link.getAttribute("href") || "");
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
function isAction(action) {
|
|
2036
|
-
return action == "advance" || action == "replace" || action == "restore";
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
class Navigator {
|
|
2206
|
+
class Navigator {
|
|
2040
2207
|
constructor(delegate) {
|
|
2041
2208
|
this.delegate = delegate;
|
|
2042
2209
|
}
|
|
2043
2210
|
proposeVisit(location, options = {}) {
|
|
2044
2211
|
if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
|
|
2045
2212
|
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
|
|
2046
|
-
this.delegate.visitProposedToLocation(location, options);
|
|
2213
|
+
return this.delegate.visitProposedToLocation(location, options);
|
|
2047
2214
|
}
|
|
2048
2215
|
else {
|
|
2049
2216
|
window.location.href = location.toString();
|
|
2217
|
+
return Promise.resolve();
|
|
2050
2218
|
}
|
|
2051
2219
|
}
|
|
2220
|
+
else {
|
|
2221
|
+
return Promise.reject();
|
|
2222
|
+
}
|
|
2052
2223
|
}
|
|
2053
2224
|
startVisit(locatable, restorationIdentifier, options = {}) {
|
|
2225
|
+
this.lastVisit = this.currentVisit;
|
|
2054
2226
|
this.stop();
|
|
2055
2227
|
this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
|
|
2056
2228
|
this.currentVisit.start();
|
|
2229
|
+
return this.currentVisit.promise;
|
|
2057
2230
|
}
|
|
2058
2231
|
submitForm(form, submitter) {
|
|
2059
2232
|
this.stop();
|
|
@@ -2080,7 +2253,7 @@ class Navigator {
|
|
|
2080
2253
|
return this.delegate.history;
|
|
2081
2254
|
}
|
|
2082
2255
|
formSubmissionStarted(formSubmission) {
|
|
2083
|
-
if (typeof this.adapter.formSubmissionStarted ===
|
|
2256
|
+
if (typeof this.adapter.formSubmissionStarted === "function") {
|
|
2084
2257
|
this.adapter.formSubmissionStarted(formSubmission);
|
|
2085
2258
|
}
|
|
2086
2259
|
}
|
|
@@ -2088,12 +2261,17 @@ class Navigator {
|
|
|
2088
2261
|
if (formSubmission == this.formSubmission) {
|
|
2089
2262
|
const responseHTML = await fetchResponse.responseHTML;
|
|
2090
2263
|
if (responseHTML) {
|
|
2091
|
-
|
|
2264
|
+
const shouldCacheSnapshot = formSubmission.method == FetchMethod.get;
|
|
2265
|
+
if (!shouldCacheSnapshot) {
|
|
2092
2266
|
this.view.clearSnapshotCache();
|
|
2093
2267
|
}
|
|
2094
2268
|
const { statusCode, redirected } = fetchResponse;
|
|
2095
2269
|
const action = this.getActionForFormSubmission(formSubmission);
|
|
2096
|
-
const visitOptions = {
|
|
2270
|
+
const visitOptions = {
|
|
2271
|
+
action,
|
|
2272
|
+
shouldCacheSnapshot,
|
|
2273
|
+
response: { statusCode, responseHTML, redirected },
|
|
2274
|
+
};
|
|
2097
2275
|
this.proposeVisit(fetchResponse.location, visitOptions);
|
|
2098
2276
|
}
|
|
2099
2277
|
}
|
|
@@ -2103,10 +2281,10 @@ class Navigator {
|
|
|
2103
2281
|
if (responseHTML) {
|
|
2104
2282
|
const snapshot = PageSnapshot.fromHTMLString(responseHTML);
|
|
2105
2283
|
if (fetchResponse.serverError) {
|
|
2106
|
-
await this.view.renderError(snapshot);
|
|
2284
|
+
await this.view.renderError(snapshot, this.currentVisit);
|
|
2107
2285
|
}
|
|
2108
2286
|
else {
|
|
2109
|
-
await this.view.renderPage(snapshot);
|
|
2287
|
+
await this.view.renderPage(snapshot, false, true, this.currentVisit);
|
|
2110
2288
|
}
|
|
2111
2289
|
this.view.scrollToTop();
|
|
2112
2290
|
this.view.clearSnapshotCache();
|
|
@@ -2116,7 +2294,7 @@ class Navigator {
|
|
|
2116
2294
|
console.error(error);
|
|
2117
2295
|
}
|
|
2118
2296
|
formSubmissionFinished(formSubmission) {
|
|
2119
|
-
if (typeof this.adapter.formSubmissionFinished ===
|
|
2297
|
+
if (typeof this.adapter.formSubmissionFinished === "function") {
|
|
2120
2298
|
this.adapter.formSubmissionFinished(formSubmission);
|
|
2121
2299
|
}
|
|
2122
2300
|
}
|
|
@@ -2127,12 +2305,14 @@ class Navigator {
|
|
|
2127
2305
|
this.delegate.visitCompleted(visit);
|
|
2128
2306
|
}
|
|
2129
2307
|
locationWithActionIsSamePage(location, action) {
|
|
2308
|
+
var _a;
|
|
2130
2309
|
const anchor = getAnchor(location);
|
|
2131
|
-
const
|
|
2132
|
-
const
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
(
|
|
2310
|
+
const lastLocation = ((_a = this.lastVisit) === null || _a === void 0 ? void 0 : _a.location) || this.view.lastRenderedLocation;
|
|
2311
|
+
const currentAnchor = getAnchor(lastLocation);
|
|
2312
|
+
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
|
|
2313
|
+
return (action !== "replace" &&
|
|
2314
|
+
getRequestURL(location) === getRequestURL(lastLocation) &&
|
|
2315
|
+
(isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
|
|
2136
2316
|
}
|
|
2137
2317
|
visitScrolledToSamePageLocation(oldURL, newURL) {
|
|
2138
2318
|
this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
|
|
@@ -2238,7 +2418,7 @@ class ScrollObserver {
|
|
|
2238
2418
|
|
|
2239
2419
|
class StreamObserver {
|
|
2240
2420
|
constructor(delegate) {
|
|
2241
|
-
this.sources = new Set;
|
|
2421
|
+
this.sources = new Set();
|
|
2242
2422
|
this.started = false;
|
|
2243
2423
|
this.inspectFetchResponse = ((event) => {
|
|
2244
2424
|
const response = fetchResponseFromEvent(event);
|
|
@@ -2288,7 +2468,7 @@ class StreamObserver {
|
|
|
2288
2468
|
}
|
|
2289
2469
|
}
|
|
2290
2470
|
receiveMessageHTML(html) {
|
|
2291
|
-
this.delegate.receivedMessageFromStream(
|
|
2471
|
+
this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
|
|
2292
2472
|
}
|
|
2293
2473
|
}
|
|
2294
2474
|
function fetchResponseFromEvent(event) {
|
|
@@ -2305,20 +2485,24 @@ function fetchResponseIsStream(response) {
|
|
|
2305
2485
|
}
|
|
2306
2486
|
|
|
2307
2487
|
class ErrorRenderer extends Renderer {
|
|
2488
|
+
static renderElement(currentElement, newElement) {
|
|
2489
|
+
const { documentElement, body } = document;
|
|
2490
|
+
documentElement.replaceChild(newElement, body);
|
|
2491
|
+
}
|
|
2308
2492
|
async render() {
|
|
2309
2493
|
this.replaceHeadAndBody();
|
|
2310
2494
|
this.activateScriptElements();
|
|
2311
2495
|
}
|
|
2312
2496
|
replaceHeadAndBody() {
|
|
2313
|
-
const { documentElement, head
|
|
2497
|
+
const { documentElement, head } = document;
|
|
2314
2498
|
documentElement.replaceChild(this.newHead, head);
|
|
2315
|
-
|
|
2499
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
2316
2500
|
}
|
|
2317
2501
|
activateScriptElements() {
|
|
2318
2502
|
for (const replaceableElement of this.scriptElements) {
|
|
2319
2503
|
const parentNode = replaceableElement.parentNode;
|
|
2320
2504
|
if (parentNode) {
|
|
2321
|
-
const element =
|
|
2505
|
+
const element = activateScriptElement(replaceableElement);
|
|
2322
2506
|
parentNode.replaceChild(element, replaceableElement);
|
|
2323
2507
|
}
|
|
2324
2508
|
}
|
|
@@ -2327,16 +2511,36 @@ class ErrorRenderer extends Renderer {
|
|
|
2327
2511
|
return this.newSnapshot.headSnapshot.element;
|
|
2328
2512
|
}
|
|
2329
2513
|
get scriptElements() {
|
|
2330
|
-
return
|
|
2514
|
+
return document.documentElement.querySelectorAll("script");
|
|
2331
2515
|
}
|
|
2332
2516
|
}
|
|
2333
2517
|
|
|
2334
2518
|
class PageRenderer extends Renderer {
|
|
2519
|
+
static renderElement(currentElement, newElement) {
|
|
2520
|
+
if (document.body && newElement instanceof HTMLBodyElement) {
|
|
2521
|
+
document.body.replaceWith(newElement);
|
|
2522
|
+
}
|
|
2523
|
+
else {
|
|
2524
|
+
document.documentElement.appendChild(newElement);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2335
2527
|
get shouldRender() {
|
|
2336
2528
|
return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
|
|
2337
2529
|
}
|
|
2338
|
-
|
|
2339
|
-
this.
|
|
2530
|
+
get reloadReason() {
|
|
2531
|
+
if (!this.newSnapshot.isVisitable) {
|
|
2532
|
+
return {
|
|
2533
|
+
reason: "turbo_visit_control_is_reload",
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
if (!this.trackedElementsAreIdentical) {
|
|
2537
|
+
return {
|
|
2538
|
+
reason: "tracked_element_mismatch",
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
async prepareToRender() {
|
|
2543
|
+
await this.mergeHead();
|
|
2340
2544
|
}
|
|
2341
2545
|
async render() {
|
|
2342
2546
|
if (this.willRender) {
|
|
@@ -2358,11 +2562,12 @@ class PageRenderer extends Renderer {
|
|
|
2358
2562
|
get newElement() {
|
|
2359
2563
|
return this.newSnapshot.element;
|
|
2360
2564
|
}
|
|
2361
|
-
mergeHead() {
|
|
2362
|
-
this.copyNewHeadStylesheetElements();
|
|
2565
|
+
async mergeHead() {
|
|
2566
|
+
const newStylesheetElements = this.copyNewHeadStylesheetElements();
|
|
2363
2567
|
this.copyNewHeadScriptElements();
|
|
2364
2568
|
this.removeCurrentHeadProvisionalElements();
|
|
2365
2569
|
this.copyNewHeadProvisionalElements();
|
|
2570
|
+
await newStylesheetElements;
|
|
2366
2571
|
}
|
|
2367
2572
|
replaceBody() {
|
|
2368
2573
|
this.preservingPermanentElements(() => {
|
|
@@ -2373,14 +2578,17 @@ class PageRenderer extends Renderer {
|
|
|
2373
2578
|
get trackedElementsAreIdentical() {
|
|
2374
2579
|
return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
|
|
2375
2580
|
}
|
|
2376
|
-
copyNewHeadStylesheetElements() {
|
|
2581
|
+
async copyNewHeadStylesheetElements() {
|
|
2582
|
+
const loadingElements = [];
|
|
2377
2583
|
for (const element of this.newHeadStylesheetElements) {
|
|
2584
|
+
loadingElements.push(waitForLoad(element));
|
|
2378
2585
|
document.head.appendChild(element);
|
|
2379
2586
|
}
|
|
2587
|
+
await Promise.all(loadingElements);
|
|
2380
2588
|
}
|
|
2381
2589
|
copyNewHeadScriptElements() {
|
|
2382
2590
|
for (const element of this.newHeadScriptElements) {
|
|
2383
|
-
document.head.appendChild(
|
|
2591
|
+
document.head.appendChild(activateScriptElement(element));
|
|
2384
2592
|
}
|
|
2385
2593
|
}
|
|
2386
2594
|
removeCurrentHeadProvisionalElements() {
|
|
@@ -2399,17 +2607,12 @@ class PageRenderer extends Renderer {
|
|
|
2399
2607
|
}
|
|
2400
2608
|
activateNewBodyScriptElements() {
|
|
2401
2609
|
for (const inertScriptElement of this.newBodyScriptElements) {
|
|
2402
|
-
const activatedScriptElement =
|
|
2610
|
+
const activatedScriptElement = activateScriptElement(inertScriptElement);
|
|
2403
2611
|
inertScriptElement.replaceWith(activatedScriptElement);
|
|
2404
2612
|
}
|
|
2405
2613
|
}
|
|
2406
2614
|
assignNewBody() {
|
|
2407
|
-
|
|
2408
|
-
document.body.replaceWith(this.newElement);
|
|
2409
|
-
}
|
|
2410
|
-
else {
|
|
2411
|
-
document.documentElement.appendChild(this.newElement);
|
|
2412
|
-
}
|
|
2615
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
2413
2616
|
}
|
|
2414
2617
|
get newHeadStylesheetElements() {
|
|
2415
2618
|
return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
|
|
@@ -2478,13 +2681,21 @@ class PageView extends View {
|
|
|
2478
2681
|
super(...arguments);
|
|
2479
2682
|
this.snapshotCache = new SnapshotCache(10);
|
|
2480
2683
|
this.lastRenderedLocation = new URL(location.href);
|
|
2684
|
+
this.forceReloaded = false;
|
|
2481
2685
|
}
|
|
2482
|
-
renderPage(snapshot, isPreview = false, willRender = true) {
|
|
2483
|
-
const renderer = new PageRenderer(this.snapshot, snapshot, isPreview, willRender);
|
|
2686
|
+
renderPage(snapshot, isPreview = false, willRender = true, visit) {
|
|
2687
|
+
const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
|
|
2688
|
+
if (!renderer.shouldRender) {
|
|
2689
|
+
this.forceReloaded = true;
|
|
2690
|
+
}
|
|
2691
|
+
else {
|
|
2692
|
+
visit === null || visit === void 0 ? void 0 : visit.changeHistory();
|
|
2693
|
+
}
|
|
2484
2694
|
return this.render(renderer);
|
|
2485
2695
|
}
|
|
2486
|
-
renderError(snapshot) {
|
|
2487
|
-
|
|
2696
|
+
renderError(snapshot, visit) {
|
|
2697
|
+
visit === null || visit === void 0 ? void 0 : visit.changeHistory();
|
|
2698
|
+
const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
|
|
2488
2699
|
return this.render(renderer);
|
|
2489
2700
|
}
|
|
2490
2701
|
clearSnapshotCache() {
|
|
@@ -2511,34 +2722,78 @@ class PageView extends View {
|
|
|
2511
2722
|
}
|
|
2512
2723
|
}
|
|
2513
2724
|
|
|
2725
|
+
class Preloader {
|
|
2726
|
+
constructor(delegate) {
|
|
2727
|
+
this.selector = "a[data-turbo-preload]";
|
|
2728
|
+
this.delegate = delegate;
|
|
2729
|
+
}
|
|
2730
|
+
get snapshotCache() {
|
|
2731
|
+
return this.delegate.navigator.view.snapshotCache;
|
|
2732
|
+
}
|
|
2733
|
+
start() {
|
|
2734
|
+
if (document.readyState === "loading") {
|
|
2735
|
+
return document.addEventListener("DOMContentLoaded", () => {
|
|
2736
|
+
this.preloadOnLoadLinksForView(document.body);
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
else {
|
|
2740
|
+
this.preloadOnLoadLinksForView(document.body);
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
preloadOnLoadLinksForView(element) {
|
|
2744
|
+
for (const link of element.querySelectorAll(this.selector)) {
|
|
2745
|
+
this.preloadURL(link);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
async preloadURL(link) {
|
|
2749
|
+
const location = new URL(link.href);
|
|
2750
|
+
if (this.snapshotCache.has(location)) {
|
|
2751
|
+
return;
|
|
2752
|
+
}
|
|
2753
|
+
try {
|
|
2754
|
+
const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
|
|
2755
|
+
const responseText = await response.text();
|
|
2756
|
+
const snapshot = PageSnapshot.fromHTMLString(responseText);
|
|
2757
|
+
this.snapshotCache.put(location, snapshot);
|
|
2758
|
+
}
|
|
2759
|
+
catch (_) {
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2514
2764
|
class Session {
|
|
2515
2765
|
constructor() {
|
|
2516
2766
|
this.navigator = new Navigator(this);
|
|
2517
2767
|
this.history = new History(this);
|
|
2768
|
+
this.preloader = new Preloader(this);
|
|
2518
2769
|
this.view = new PageView(this, document.documentElement);
|
|
2519
2770
|
this.adapter = new BrowserAdapter(this);
|
|
2520
2771
|
this.pageObserver = new PageObserver(this);
|
|
2521
2772
|
this.cacheObserver = new CacheObserver();
|
|
2522
|
-
this.linkClickObserver = new LinkClickObserver(this);
|
|
2523
|
-
this.formSubmitObserver = new FormSubmitObserver(this);
|
|
2773
|
+
this.linkClickObserver = new LinkClickObserver(this, window);
|
|
2774
|
+
this.formSubmitObserver = new FormSubmitObserver(this, document);
|
|
2524
2775
|
this.scrollObserver = new ScrollObserver(this);
|
|
2525
2776
|
this.streamObserver = new StreamObserver(this);
|
|
2526
|
-
this.
|
|
2777
|
+
this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
|
|
2778
|
+
this.frameRedirector = new FrameRedirector(this, document.documentElement);
|
|
2527
2779
|
this.drive = true;
|
|
2528
2780
|
this.enabled = true;
|
|
2529
2781
|
this.progressBarDelay = 500;
|
|
2530
2782
|
this.started = false;
|
|
2783
|
+
this.formMode = "on";
|
|
2531
2784
|
}
|
|
2532
2785
|
start() {
|
|
2533
2786
|
if (!this.started) {
|
|
2534
2787
|
this.pageObserver.start();
|
|
2535
2788
|
this.cacheObserver.start();
|
|
2789
|
+
this.formLinkClickObserver.start();
|
|
2536
2790
|
this.linkClickObserver.start();
|
|
2537
2791
|
this.formSubmitObserver.start();
|
|
2538
2792
|
this.scrollObserver.start();
|
|
2539
2793
|
this.streamObserver.start();
|
|
2540
2794
|
this.frameRedirector.start();
|
|
2541
2795
|
this.history.start();
|
|
2796
|
+
this.preloader.start();
|
|
2542
2797
|
this.started = true;
|
|
2543
2798
|
this.enabled = true;
|
|
2544
2799
|
}
|
|
@@ -2550,6 +2805,7 @@ class Session {
|
|
|
2550
2805
|
if (this.started) {
|
|
2551
2806
|
this.pageObserver.stop();
|
|
2552
2807
|
this.cacheObserver.stop();
|
|
2808
|
+
this.formLinkClickObserver.stop();
|
|
2553
2809
|
this.linkClickObserver.stop();
|
|
2554
2810
|
this.formSubmitObserver.stop();
|
|
2555
2811
|
this.scrollObserver.stop();
|
|
@@ -2563,7 +2819,14 @@ class Session {
|
|
|
2563
2819
|
this.adapter = adapter;
|
|
2564
2820
|
}
|
|
2565
2821
|
visit(location, options = {}) {
|
|
2566
|
-
|
|
2822
|
+
const frameElement = document.getElementById(options.frame || "");
|
|
2823
|
+
if (frameElement instanceof FrameElement) {
|
|
2824
|
+
frameElement.src = location.toString();
|
|
2825
|
+
return frameElement.loaded;
|
|
2826
|
+
}
|
|
2827
|
+
else {
|
|
2828
|
+
return this.navigator.proposeVisit(expandURL(location), options);
|
|
2829
|
+
}
|
|
2567
2830
|
}
|
|
2568
2831
|
connectStreamSource(source) {
|
|
2569
2832
|
this.streamObserver.connectStreamSource(source);
|
|
@@ -2580,6 +2843,9 @@ class Session {
|
|
|
2580
2843
|
setProgressBarDelay(delay) {
|
|
2581
2844
|
this.progressBarDelay = delay;
|
|
2582
2845
|
}
|
|
2846
|
+
setFormMode(mode) {
|
|
2847
|
+
this.formMode = mode;
|
|
2848
|
+
}
|
|
2583
2849
|
get location() {
|
|
2584
2850
|
return this.history.location;
|
|
2585
2851
|
}
|
|
@@ -2588,55 +2854,40 @@ class Session {
|
|
|
2588
2854
|
}
|
|
2589
2855
|
historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
|
|
2590
2856
|
if (this.enabled) {
|
|
2591
|
-
this.navigator.startVisit(location, restorationIdentifier, {
|
|
2857
|
+
this.navigator.startVisit(location, restorationIdentifier, {
|
|
2858
|
+
action: "restore",
|
|
2859
|
+
historyChanged: true,
|
|
2860
|
+
});
|
|
2592
2861
|
}
|
|
2593
2862
|
else {
|
|
2594
|
-
this.adapter.pageInvalidated(
|
|
2863
|
+
this.adapter.pageInvalidated({
|
|
2864
|
+
reason: "turbo_disabled",
|
|
2865
|
+
});
|
|
2595
2866
|
}
|
|
2596
2867
|
}
|
|
2597
2868
|
scrollPositionChanged(position) {
|
|
2598
2869
|
this.history.updateRestorationData({ scrollPosition: position });
|
|
2599
2870
|
}
|
|
2600
|
-
|
|
2601
|
-
return this.
|
|
2602
|
-
|
|
2603
|
-
|
|
2871
|
+
willSubmitFormLinkToLocation(link, location) {
|
|
2872
|
+
return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
|
|
2873
|
+
}
|
|
2874
|
+
submittedFormLinkToLocation() { }
|
|
2875
|
+
willFollowLinkToLocation(link, location, event) {
|
|
2876
|
+
return (this.elementIsNavigatable(link) &&
|
|
2877
|
+
locationIsVisitable(location, this.snapshot.rootLocation) &&
|
|
2878
|
+
this.applicationAllowsFollowingLinkToLocation(link, location, event));
|
|
2604
2879
|
}
|
|
2605
2880
|
followedLinkToLocation(link, location) {
|
|
2606
2881
|
const action = this.getActionForLink(link);
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
convertLinkWithMethodClickToFormSubmission(link) {
|
|
2610
|
-
const linkMethod = link.getAttribute("data-turbo-method");
|
|
2611
|
-
if (linkMethod) {
|
|
2612
|
-
const form = document.createElement("form");
|
|
2613
|
-
form.method = linkMethod;
|
|
2614
|
-
form.action = link.getAttribute("href") || "undefined";
|
|
2615
|
-
form.hidden = true;
|
|
2616
|
-
if (link.hasAttribute("data-turbo-confirm")) {
|
|
2617
|
-
form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
|
|
2618
|
-
}
|
|
2619
|
-
const frame = this.getTargetFrameForLink(link);
|
|
2620
|
-
if (frame) {
|
|
2621
|
-
form.setAttribute("data-turbo-frame", frame);
|
|
2622
|
-
form.addEventListener("turbo:submit-start", () => form.remove());
|
|
2623
|
-
}
|
|
2624
|
-
else {
|
|
2625
|
-
form.addEventListener("submit", () => form.remove());
|
|
2626
|
-
}
|
|
2627
|
-
document.body.appendChild(form);
|
|
2628
|
-
return dispatch("submit", { cancelable: true, target: form });
|
|
2629
|
-
}
|
|
2630
|
-
else {
|
|
2631
|
-
return false;
|
|
2632
|
-
}
|
|
2882
|
+
const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
|
|
2883
|
+
this.visit(location.href, { action, acceptsStreamResponse });
|
|
2633
2884
|
}
|
|
2634
2885
|
allowsVisitingLocationWithAction(location, action) {
|
|
2635
2886
|
return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
|
|
2636
2887
|
}
|
|
2637
2888
|
visitProposedToLocation(location, options) {
|
|
2638
2889
|
extendURLWithDeprecatedProperties(location);
|
|
2639
|
-
this.adapter.visitProposedToLocation(location, options);
|
|
2890
|
+
return this.adapter.visitProposedToLocation(location, options);
|
|
2640
2891
|
}
|
|
2641
2892
|
visitStarted(visit) {
|
|
2642
2893
|
extendURLWithDeprecatedProperties(visit.location);
|
|
@@ -2655,9 +2906,8 @@ class Session {
|
|
|
2655
2906
|
}
|
|
2656
2907
|
willSubmitForm(form, submitter) {
|
|
2657
2908
|
const action = getAction(form, submitter);
|
|
2658
|
-
return this.
|
|
2659
|
-
|
|
2660
|
-
&& locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
|
|
2909
|
+
return (this.submissionIsNavigatable(form, submitter) &&
|
|
2910
|
+
locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
|
|
2661
2911
|
}
|
|
2662
2912
|
formSubmitted(form, submitter) {
|
|
2663
2913
|
this.navigator.submitForm(form, submitter);
|
|
@@ -2681,16 +2931,23 @@ class Session {
|
|
|
2681
2931
|
this.notifyApplicationBeforeCachingSnapshot();
|
|
2682
2932
|
}
|
|
2683
2933
|
}
|
|
2684
|
-
allowsImmediateRender({ element },
|
|
2685
|
-
const event = this.notifyApplicationBeforeRender(element,
|
|
2686
|
-
|
|
2934
|
+
allowsImmediateRender({ element }, options) {
|
|
2935
|
+
const event = this.notifyApplicationBeforeRender(element, options);
|
|
2936
|
+
const { defaultPrevented, detail: { render }, } = event;
|
|
2937
|
+
if (this.view.renderer && render) {
|
|
2938
|
+
this.view.renderer.renderElement = render;
|
|
2939
|
+
}
|
|
2940
|
+
return !defaultPrevented;
|
|
2687
2941
|
}
|
|
2688
|
-
viewRenderedSnapshot(
|
|
2942
|
+
viewRenderedSnapshot(_snapshot, _isPreview) {
|
|
2689
2943
|
this.view.lastRenderedLocation = this.history.location;
|
|
2690
2944
|
this.notifyApplicationAfterRender();
|
|
2691
2945
|
}
|
|
2692
|
-
|
|
2693
|
-
this.
|
|
2946
|
+
preloadOnLoadLinksForView(element) {
|
|
2947
|
+
this.preloader.preloadOnLoadLinksForView(element);
|
|
2948
|
+
}
|
|
2949
|
+
viewInvalidated(reason) {
|
|
2950
|
+
this.adapter.pageInvalidated(reason);
|
|
2694
2951
|
}
|
|
2695
2952
|
frameLoaded(frame) {
|
|
2696
2953
|
this.notifyApplicationAfterFrameLoad(frame);
|
|
@@ -2698,19 +2955,30 @@ class Session {
|
|
|
2698
2955
|
frameRendered(fetchResponse, frame) {
|
|
2699
2956
|
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
|
2700
2957
|
}
|
|
2701
|
-
|
|
2702
|
-
|
|
2958
|
+
frameMissing(frame, fetchResponse) {
|
|
2959
|
+
console.warn(`Completing full-page visit as matching frame for #${frame.id} was missing from the response`);
|
|
2960
|
+
return this.visit(fetchResponse.location);
|
|
2961
|
+
}
|
|
2962
|
+
applicationAllowsFollowingLinkToLocation(link, location, ev) {
|
|
2963
|
+
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
|
|
2703
2964
|
return !event.defaultPrevented;
|
|
2704
2965
|
}
|
|
2705
2966
|
applicationAllowsVisitingLocation(location) {
|
|
2706
2967
|
const event = this.notifyApplicationBeforeVisitingLocation(location);
|
|
2707
2968
|
return !event.defaultPrevented;
|
|
2708
2969
|
}
|
|
2709
|
-
notifyApplicationAfterClickingLinkToLocation(link, location) {
|
|
2710
|
-
return dispatch("turbo:click", {
|
|
2970
|
+
notifyApplicationAfterClickingLinkToLocation(link, location, event) {
|
|
2971
|
+
return dispatch("turbo:click", {
|
|
2972
|
+
target: link,
|
|
2973
|
+
detail: { url: location.href, originalEvent: event },
|
|
2974
|
+
cancelable: true,
|
|
2975
|
+
});
|
|
2711
2976
|
}
|
|
2712
2977
|
notifyApplicationBeforeVisitingLocation(location) {
|
|
2713
|
-
return dispatch("turbo:before-visit", {
|
|
2978
|
+
return dispatch("turbo:before-visit", {
|
|
2979
|
+
detail: { url: location.href },
|
|
2980
|
+
cancelable: true,
|
|
2981
|
+
});
|
|
2714
2982
|
}
|
|
2715
2983
|
notifyApplicationAfterVisitingLocation(location, action) {
|
|
2716
2984
|
markAsBusy(document.documentElement);
|
|
@@ -2719,28 +2987,55 @@ class Session {
|
|
|
2719
2987
|
notifyApplicationBeforeCachingSnapshot() {
|
|
2720
2988
|
return dispatch("turbo:before-cache");
|
|
2721
2989
|
}
|
|
2722
|
-
notifyApplicationBeforeRender(newBody,
|
|
2723
|
-
return dispatch("turbo:before-render", {
|
|
2990
|
+
notifyApplicationBeforeRender(newBody, options) {
|
|
2991
|
+
return dispatch("turbo:before-render", {
|
|
2992
|
+
detail: Object.assign({ newBody }, options),
|
|
2993
|
+
cancelable: true,
|
|
2994
|
+
});
|
|
2724
2995
|
}
|
|
2725
2996
|
notifyApplicationAfterRender() {
|
|
2726
2997
|
return dispatch("turbo:render");
|
|
2727
2998
|
}
|
|
2728
2999
|
notifyApplicationAfterPageLoad(timing = {}) {
|
|
2729
3000
|
clearBusyState(document.documentElement);
|
|
2730
|
-
return dispatch("turbo:load", {
|
|
3001
|
+
return dispatch("turbo:load", {
|
|
3002
|
+
detail: { url: this.location.href, timing },
|
|
3003
|
+
});
|
|
2731
3004
|
}
|
|
2732
3005
|
notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
|
|
2733
|
-
dispatchEvent(new HashChangeEvent("hashchange", {
|
|
3006
|
+
dispatchEvent(new HashChangeEvent("hashchange", {
|
|
3007
|
+
oldURL: oldURL.toString(),
|
|
3008
|
+
newURL: newURL.toString(),
|
|
3009
|
+
}));
|
|
2734
3010
|
}
|
|
2735
3011
|
notifyApplicationAfterFrameLoad(frame) {
|
|
2736
3012
|
return dispatch("turbo:frame-load", { target: frame });
|
|
2737
3013
|
}
|
|
2738
3014
|
notifyApplicationAfterFrameRender(fetchResponse, frame) {
|
|
2739
|
-
return dispatch("turbo:frame-render", {
|
|
3015
|
+
return dispatch("turbo:frame-render", {
|
|
3016
|
+
detail: { fetchResponse },
|
|
3017
|
+
target: frame,
|
|
3018
|
+
cancelable: true,
|
|
3019
|
+
});
|
|
3020
|
+
}
|
|
3021
|
+
submissionIsNavigatable(form, submitter) {
|
|
3022
|
+
if (this.formMode == "off") {
|
|
3023
|
+
return false;
|
|
3024
|
+
}
|
|
3025
|
+
else {
|
|
3026
|
+
const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
|
|
3027
|
+
if (this.formMode == "optin") {
|
|
3028
|
+
return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
|
|
3029
|
+
}
|
|
3030
|
+
else {
|
|
3031
|
+
return submitterIsNavigatable && this.elementIsNavigatable(form);
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
2740
3034
|
}
|
|
2741
|
-
|
|
2742
|
-
const container = element
|
|
2743
|
-
|
|
3035
|
+
elementIsNavigatable(element) {
|
|
3036
|
+
const container = element.closest("[data-turbo]");
|
|
3037
|
+
const withinFrame = element.closest("turbo-frame");
|
|
3038
|
+
if (this.drive || withinFrame) {
|
|
2744
3039
|
if (container) {
|
|
2745
3040
|
return container.getAttribute("data-turbo") != "false";
|
|
2746
3041
|
}
|
|
@@ -2761,18 +3056,6 @@ class Session {
|
|
|
2761
3056
|
const action = link.getAttribute("data-turbo-action");
|
|
2762
3057
|
return isAction(action) ? action : "advance";
|
|
2763
3058
|
}
|
|
2764
|
-
getTargetFrameForLink(link) {
|
|
2765
|
-
const frame = link.getAttribute("data-turbo-frame");
|
|
2766
|
-
if (frame) {
|
|
2767
|
-
return frame;
|
|
2768
|
-
}
|
|
2769
|
-
else {
|
|
2770
|
-
const container = link.closest("turbo-frame");
|
|
2771
|
-
if (container) {
|
|
2772
|
-
return container.id;
|
|
2773
|
-
}
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
3059
|
get snapshot() {
|
|
2777
3060
|
return this.view.snapshot;
|
|
2778
3061
|
}
|
|
@@ -2784,11 +3067,59 @@ const deprecatedLocationPropertyDescriptors = {
|
|
|
2784
3067
|
absoluteURL: {
|
|
2785
3068
|
get() {
|
|
2786
3069
|
return this.toString();
|
|
2787
|
-
}
|
|
3070
|
+
},
|
|
3071
|
+
},
|
|
3072
|
+
};
|
|
3073
|
+
|
|
3074
|
+
class Cache {
|
|
3075
|
+
constructor(session) {
|
|
3076
|
+
this.session = session;
|
|
3077
|
+
}
|
|
3078
|
+
clear() {
|
|
3079
|
+
this.session.clearCache();
|
|
2788
3080
|
}
|
|
3081
|
+
resetCacheControl() {
|
|
3082
|
+
this.setCacheControl("");
|
|
3083
|
+
}
|
|
3084
|
+
exemptPageFromCache() {
|
|
3085
|
+
this.setCacheControl("no-cache");
|
|
3086
|
+
}
|
|
3087
|
+
exemptPageFromPreview() {
|
|
3088
|
+
this.setCacheControl("no-preview");
|
|
3089
|
+
}
|
|
3090
|
+
setCacheControl(value) {
|
|
3091
|
+
setMetaContent("turbo-cache-control", value);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
const StreamActions = {
|
|
3096
|
+
after() {
|
|
3097
|
+
this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
|
|
3098
|
+
},
|
|
3099
|
+
append() {
|
|
3100
|
+
this.removeDuplicateTargetChildren();
|
|
3101
|
+
this.targetElements.forEach((e) => e.append(this.templateContent));
|
|
3102
|
+
},
|
|
3103
|
+
before() {
|
|
3104
|
+
this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
|
|
3105
|
+
},
|
|
3106
|
+
prepend() {
|
|
3107
|
+
this.removeDuplicateTargetChildren();
|
|
3108
|
+
this.targetElements.forEach((e) => e.prepend(this.templateContent));
|
|
3109
|
+
},
|
|
3110
|
+
remove() {
|
|
3111
|
+
this.targetElements.forEach((e) => e.remove());
|
|
3112
|
+
},
|
|
3113
|
+
replace() {
|
|
3114
|
+
this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
|
|
3115
|
+
},
|
|
3116
|
+
update() {
|
|
3117
|
+
this.targetElements.forEach((e) => e.replaceChildren(this.templateContent));
|
|
3118
|
+
},
|
|
2789
3119
|
};
|
|
2790
3120
|
|
|
2791
|
-
const session = new Session;
|
|
3121
|
+
const session = new Session();
|
|
3122
|
+
const cache = new Cache(session);
|
|
2792
3123
|
const { navigator: navigator$1 } = session;
|
|
2793
3124
|
function start() {
|
|
2794
3125
|
session.start();
|
|
@@ -2797,7 +3128,7 @@ function registerAdapter(adapter) {
|
|
|
2797
3128
|
session.registerAdapter(adapter);
|
|
2798
3129
|
}
|
|
2799
3130
|
function visit(location, options) {
|
|
2800
|
-
session.visit(location, options);
|
|
3131
|
+
return session.visit(location, options);
|
|
2801
3132
|
}
|
|
2802
3133
|
function connectStreamSource(source) {
|
|
2803
3134
|
session.connectStreamSource(source);
|
|
@@ -2809,6 +3140,7 @@ function renderStreamMessage(message) {
|
|
|
2809
3140
|
session.renderStreamMessage(message);
|
|
2810
3141
|
}
|
|
2811
3142
|
function clearCache() {
|
|
3143
|
+
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.`");
|
|
2812
3144
|
session.clearCache();
|
|
2813
3145
|
}
|
|
2814
3146
|
function setProgressBarDelay(delay) {
|
|
@@ -2817,13 +3149,18 @@ function setProgressBarDelay(delay) {
|
|
|
2817
3149
|
function setConfirmMethod(confirmMethod) {
|
|
2818
3150
|
FormSubmission.confirmMethod = confirmMethod;
|
|
2819
3151
|
}
|
|
3152
|
+
function setFormMode(mode) {
|
|
3153
|
+
session.setFormMode(mode);
|
|
3154
|
+
}
|
|
2820
3155
|
|
|
2821
3156
|
var Turbo = /*#__PURE__*/Object.freeze({
|
|
2822
3157
|
__proto__: null,
|
|
2823
3158
|
navigator: navigator$1,
|
|
2824
3159
|
session: session,
|
|
3160
|
+
cache: cache,
|
|
2825
3161
|
PageRenderer: PageRenderer,
|
|
2826
3162
|
PageSnapshot: PageSnapshot,
|
|
3163
|
+
FrameRenderer: FrameRenderer,
|
|
2827
3164
|
start: start,
|
|
2828
3165
|
registerAdapter: registerAdapter,
|
|
2829
3166
|
visit: visit,
|
|
@@ -2832,41 +3169,56 @@ var Turbo = /*#__PURE__*/Object.freeze({
|
|
|
2832
3169
|
renderStreamMessage: renderStreamMessage,
|
|
2833
3170
|
clearCache: clearCache,
|
|
2834
3171
|
setProgressBarDelay: setProgressBarDelay,
|
|
2835
|
-
setConfirmMethod: setConfirmMethod
|
|
3172
|
+
setConfirmMethod: setConfirmMethod,
|
|
3173
|
+
setFormMode: setFormMode,
|
|
3174
|
+
StreamActions: StreamActions
|
|
2836
3175
|
});
|
|
2837
3176
|
|
|
2838
3177
|
class FrameController {
|
|
2839
3178
|
constructor(element) {
|
|
2840
|
-
this.fetchResponseLoaded = (
|
|
3179
|
+
this.fetchResponseLoaded = (_fetchResponse) => { };
|
|
2841
3180
|
this.currentFetchRequest = null;
|
|
2842
3181
|
this.resolveVisitPromise = () => { };
|
|
2843
3182
|
this.connected = false;
|
|
2844
3183
|
this.hasBeenLoaded = false;
|
|
2845
|
-
this.
|
|
3184
|
+
this.ignoredAttributes = new Set();
|
|
3185
|
+
this.action = null;
|
|
3186
|
+
this.visitCachedSnapshot = ({ element }) => {
|
|
3187
|
+
const frame = element.querySelector("#" + this.element.id);
|
|
3188
|
+
if (frame && this.previousFrameElement) {
|
|
3189
|
+
frame.replaceChildren(...this.previousFrameElement.children);
|
|
3190
|
+
}
|
|
3191
|
+
delete this.previousFrameElement;
|
|
3192
|
+
};
|
|
2846
3193
|
this.element = element;
|
|
2847
3194
|
this.view = new FrameView(this, this.element);
|
|
2848
3195
|
this.appearanceObserver = new AppearanceObserver(this, this.element);
|
|
2849
|
-
this.
|
|
2850
|
-
this.
|
|
3196
|
+
this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
|
|
3197
|
+
this.linkClickObserver = new LinkClickObserver(this, this.element);
|
|
3198
|
+
this.restorationIdentifier = uuid();
|
|
3199
|
+
this.formSubmitObserver = new FormSubmitObserver(this, this.element);
|
|
2851
3200
|
}
|
|
2852
3201
|
connect() {
|
|
2853
3202
|
if (!this.connected) {
|
|
2854
3203
|
this.connected = true;
|
|
2855
|
-
this.reloadable = false;
|
|
2856
3204
|
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
|
2857
3205
|
this.appearanceObserver.start();
|
|
2858
3206
|
}
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
3207
|
+
else {
|
|
3208
|
+
this.loadSourceURL();
|
|
3209
|
+
}
|
|
3210
|
+
this.formLinkClickObserver.start();
|
|
3211
|
+
this.linkClickObserver.start();
|
|
3212
|
+
this.formSubmitObserver.start();
|
|
2862
3213
|
}
|
|
2863
3214
|
}
|
|
2864
3215
|
disconnect() {
|
|
2865
3216
|
if (this.connected) {
|
|
2866
3217
|
this.connected = false;
|
|
2867
3218
|
this.appearanceObserver.stop();
|
|
2868
|
-
this.
|
|
2869
|
-
this.
|
|
3219
|
+
this.formLinkClickObserver.stop();
|
|
3220
|
+
this.linkClickObserver.stop();
|
|
3221
|
+
this.formSubmitObserver.stop();
|
|
2870
3222
|
}
|
|
2871
3223
|
}
|
|
2872
3224
|
disabledChanged() {
|
|
@@ -2875,10 +3227,20 @@ class FrameController {
|
|
|
2875
3227
|
}
|
|
2876
3228
|
}
|
|
2877
3229
|
sourceURLChanged() {
|
|
3230
|
+
if (this.isIgnoringChangesTo("src"))
|
|
3231
|
+
return;
|
|
3232
|
+
if (this.element.isConnected) {
|
|
3233
|
+
this.complete = false;
|
|
3234
|
+
}
|
|
2878
3235
|
if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
|
|
2879
3236
|
this.loadSourceURL();
|
|
2880
3237
|
}
|
|
2881
3238
|
}
|
|
3239
|
+
completeChanged() {
|
|
3240
|
+
if (this.isIgnoringChangesTo("complete"))
|
|
3241
|
+
return;
|
|
3242
|
+
this.loadSourceURL();
|
|
3243
|
+
}
|
|
2882
3244
|
loadingStyleChanged() {
|
|
2883
3245
|
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
|
2884
3246
|
this.appearanceObserver.start();
|
|
@@ -2889,21 +3251,11 @@ class FrameController {
|
|
|
2889
3251
|
}
|
|
2890
3252
|
}
|
|
2891
3253
|
async loadSourceURL() {
|
|
2892
|
-
if (
|
|
2893
|
-
|
|
2894
|
-
this.
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
this.element.loaded = this.visit(expandURL(this.sourceURL));
|
|
2898
|
-
this.appearanceObserver.stop();
|
|
2899
|
-
await this.element.loaded;
|
|
2900
|
-
this.hasBeenLoaded = true;
|
|
2901
|
-
}
|
|
2902
|
-
catch (error) {
|
|
2903
|
-
this.currentURL = previousURL;
|
|
2904
|
-
throw error;
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
3254
|
+
if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
|
|
3255
|
+
this.element.loaded = this.visit(expandURL(this.sourceURL));
|
|
3256
|
+
this.appearanceObserver.stop();
|
|
3257
|
+
await this.element.loaded;
|
|
3258
|
+
this.hasBeenLoaded = true;
|
|
2907
3259
|
}
|
|
2908
3260
|
}
|
|
2909
3261
|
async loadResponse(fetchResponse) {
|
|
@@ -2914,14 +3266,22 @@ class FrameController {
|
|
|
2914
3266
|
const html = await fetchResponse.responseHTML;
|
|
2915
3267
|
if (html) {
|
|
2916
3268
|
const { body } = parseHTMLDocument(html);
|
|
2917
|
-
const
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
3269
|
+
const newFrameElement = await this.extractForeignFrameElement(body);
|
|
3270
|
+
if (newFrameElement) {
|
|
3271
|
+
const snapshot = new Snapshot(newFrameElement);
|
|
3272
|
+
const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
|
|
3273
|
+
if (this.view.renderPromise)
|
|
3274
|
+
await this.view.renderPromise;
|
|
3275
|
+
this.changeHistory();
|
|
3276
|
+
await this.view.render(renderer);
|
|
3277
|
+
this.complete = true;
|
|
3278
|
+
session.frameRendered(fetchResponse, this.element);
|
|
3279
|
+
session.frameLoaded(this.element);
|
|
3280
|
+
this.fetchResponseLoaded(fetchResponse);
|
|
3281
|
+
}
|
|
3282
|
+
else if (this.sessionWillHandleMissingFrame(fetchResponse)) {
|
|
3283
|
+
await session.frameMissing(this.element, fetchResponse);
|
|
3284
|
+
}
|
|
2925
3285
|
}
|
|
2926
3286
|
}
|
|
2927
3287
|
catch (error) {
|
|
@@ -2932,41 +3292,46 @@ class FrameController {
|
|
|
2932
3292
|
this.fetchResponseLoaded = () => { };
|
|
2933
3293
|
}
|
|
2934
3294
|
}
|
|
2935
|
-
elementAppearedInViewport(
|
|
3295
|
+
elementAppearedInViewport(_element) {
|
|
2936
3296
|
this.loadSourceURL();
|
|
2937
3297
|
}
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
3298
|
+
willSubmitFormLinkToLocation(link) {
|
|
3299
|
+
return link.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(link);
|
|
3300
|
+
}
|
|
3301
|
+
submittedFormLinkToLocation(link, _location, form) {
|
|
3302
|
+
const frame = this.findFrameElement(link);
|
|
3303
|
+
if (frame)
|
|
3304
|
+
form.setAttribute("data-turbo-frame", frame.id);
|
|
3305
|
+
}
|
|
3306
|
+
willFollowLinkToLocation(element) {
|
|
3307
|
+
return this.shouldInterceptNavigation(element);
|
|
2945
3308
|
}
|
|
2946
|
-
|
|
2947
|
-
this.
|
|
2948
|
-
this.navigateFrame(element, url);
|
|
3309
|
+
followedLinkToLocation(element, location) {
|
|
3310
|
+
this.navigateFrame(element, location.href);
|
|
2949
3311
|
}
|
|
2950
|
-
|
|
2951
|
-
return this.shouldInterceptNavigation(element, submitter);
|
|
3312
|
+
willSubmitForm(element, submitter) {
|
|
3313
|
+
return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
|
|
2952
3314
|
}
|
|
2953
|
-
|
|
3315
|
+
formSubmitted(element, submitter) {
|
|
2954
3316
|
if (this.formSubmission) {
|
|
2955
3317
|
this.formSubmission.stop();
|
|
2956
3318
|
}
|
|
2957
|
-
this.reloadable = false;
|
|
2958
3319
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
|
2959
3320
|
const { fetchRequest } = this.formSubmission;
|
|
2960
3321
|
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
|
2961
3322
|
this.formSubmission.start();
|
|
2962
3323
|
}
|
|
2963
3324
|
prepareHeadersForRequest(headers, request) {
|
|
3325
|
+
var _a;
|
|
2964
3326
|
headers["Turbo-Frame"] = this.id;
|
|
3327
|
+
if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
|
|
3328
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
3329
|
+
}
|
|
2965
3330
|
}
|
|
2966
|
-
requestStarted(
|
|
3331
|
+
requestStarted(_request) {
|
|
2967
3332
|
markAsBusy(this.element);
|
|
2968
3333
|
}
|
|
2969
|
-
requestPreventedHandlingResponse(
|
|
3334
|
+
requestPreventedHandlingResponse(_request, _response) {
|
|
2970
3335
|
this.resolveVisitPromise();
|
|
2971
3336
|
}
|
|
2972
3337
|
async requestSucceededWithResponse(request, response) {
|
|
@@ -2979,9 +3344,13 @@ class FrameController {
|
|
|
2979
3344
|
}
|
|
2980
3345
|
requestErrored(request, error) {
|
|
2981
3346
|
console.error(error);
|
|
3347
|
+
dispatch("turbo:fetch-request-error", {
|
|
3348
|
+
target: this.element,
|
|
3349
|
+
detail: { request, error },
|
|
3350
|
+
});
|
|
2982
3351
|
this.resolveVisitPromise();
|
|
2983
3352
|
}
|
|
2984
|
-
requestFinished(
|
|
3353
|
+
requestFinished(_request) {
|
|
2985
3354
|
clearBusyState(this.element);
|
|
2986
3355
|
}
|
|
2987
3356
|
formSubmissionStarted({ formElement }) {
|
|
@@ -3001,19 +3370,32 @@ class FrameController {
|
|
|
3001
3370
|
formSubmissionFinished({ formElement }) {
|
|
3002
3371
|
clearBusyState(formElement, this.findFrameElement(formElement));
|
|
3003
3372
|
}
|
|
3004
|
-
allowsImmediateRender(
|
|
3005
|
-
|
|
3373
|
+
allowsImmediateRender({ element: newFrame }, options) {
|
|
3374
|
+
const event = dispatch("turbo:before-frame-render", {
|
|
3375
|
+
target: this.element,
|
|
3376
|
+
detail: Object.assign({ newFrame }, options),
|
|
3377
|
+
cancelable: true,
|
|
3378
|
+
});
|
|
3379
|
+
const { defaultPrevented, detail: { render }, } = event;
|
|
3380
|
+
if (this.view.renderer && render) {
|
|
3381
|
+
this.view.renderer.renderElement = render;
|
|
3382
|
+
}
|
|
3383
|
+
return !defaultPrevented;
|
|
3006
3384
|
}
|
|
3007
|
-
viewRenderedSnapshot(
|
|
3385
|
+
viewRenderedSnapshot(_snapshot, _isPreview) { }
|
|
3386
|
+
preloadOnLoadLinksForView(element) {
|
|
3387
|
+
session.preloadOnLoadLinksForView(element);
|
|
3008
3388
|
}
|
|
3009
|
-
viewInvalidated() {
|
|
3389
|
+
viewInvalidated() { }
|
|
3390
|
+
willRenderFrame(currentElement, _newElement) {
|
|
3391
|
+
this.previousFrameElement = currentElement.cloneNode(true);
|
|
3010
3392
|
}
|
|
3011
3393
|
async visit(url) {
|
|
3012
3394
|
var _a;
|
|
3013
|
-
const request = new FetchRequest(this, FetchMethod.get, url,
|
|
3395
|
+
const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
|
|
3014
3396
|
(_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
3015
3397
|
this.currentFetchRequest = request;
|
|
3016
|
-
return new Promise(resolve => {
|
|
3398
|
+
return new Promise((resolve) => {
|
|
3017
3399
|
this.resolveVisitPromise = () => {
|
|
3018
3400
|
this.resolveVisitPromise = () => { };
|
|
3019
3401
|
this.currentFetchRequest = null;
|
|
@@ -3025,23 +3407,49 @@ class FrameController {
|
|
|
3025
3407
|
navigateFrame(element, url, submitter) {
|
|
3026
3408
|
const frame = this.findFrameElement(element, submitter);
|
|
3027
3409
|
this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
|
|
3028
|
-
|
|
3029
|
-
|
|
3410
|
+
this.withCurrentNavigationElement(element, () => {
|
|
3411
|
+
frame.src = url;
|
|
3412
|
+
});
|
|
3030
3413
|
}
|
|
3031
3414
|
proposeVisitIfNavigatedWithAction(frame, element, submitter) {
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3415
|
+
this.action = getVisitAction(submitter, element, frame);
|
|
3416
|
+
this.frame = frame;
|
|
3417
|
+
if (isAction(this.action)) {
|
|
3418
|
+
const { visitCachedSnapshot } = frame.delegate;
|
|
3035
3419
|
frame.delegate.fetchResponseLoaded = (fetchResponse) => {
|
|
3036
3420
|
if (frame.src) {
|
|
3037
3421
|
const { statusCode, redirected } = fetchResponse;
|
|
3038
3422
|
const responseHTML = frame.ownerDocument.documentElement.outerHTML;
|
|
3039
3423
|
const response = { statusCode, redirected, responseHTML };
|
|
3040
|
-
|
|
3424
|
+
const options = {
|
|
3425
|
+
response,
|
|
3426
|
+
visitCachedSnapshot,
|
|
3427
|
+
willRender: false,
|
|
3428
|
+
updateHistory: false,
|
|
3429
|
+
restorationIdentifier: this.restorationIdentifier,
|
|
3430
|
+
};
|
|
3431
|
+
if (this.action)
|
|
3432
|
+
options.action = this.action;
|
|
3433
|
+
session.visit(frame.src, options);
|
|
3041
3434
|
}
|
|
3042
3435
|
};
|
|
3043
3436
|
}
|
|
3044
3437
|
}
|
|
3438
|
+
changeHistory() {
|
|
3439
|
+
if (this.action && this.frame) {
|
|
3440
|
+
const method = getHistoryMethodForAction(this.action);
|
|
3441
|
+
session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
sessionWillHandleMissingFrame(fetchResponse) {
|
|
3445
|
+
this.element.setAttribute("complete", "");
|
|
3446
|
+
const event = dispatch("turbo:frame-missing", {
|
|
3447
|
+
target: this.element,
|
|
3448
|
+
detail: { fetchResponse },
|
|
3449
|
+
cancelable: true,
|
|
3450
|
+
});
|
|
3451
|
+
return !event.defaultPrevented;
|
|
3452
|
+
}
|
|
3045
3453
|
findFrameElement(element, submitter) {
|
|
3046
3454
|
var _a;
|
|
3047
3455
|
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
@@ -3051,19 +3459,21 @@ class FrameController {
|
|
|
3051
3459
|
let element;
|
|
3052
3460
|
const id = CSS.escape(this.id);
|
|
3053
3461
|
try {
|
|
3054
|
-
|
|
3462
|
+
element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
|
|
3463
|
+
if (element) {
|
|
3055
3464
|
return element;
|
|
3056
3465
|
}
|
|
3057
|
-
|
|
3466
|
+
element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
|
|
3467
|
+
if (element) {
|
|
3058
3468
|
await element.loaded;
|
|
3059
3469
|
return await this.extractForeignFrameElement(element);
|
|
3060
3470
|
}
|
|
3061
|
-
console.error(`Response has no matching <turbo-frame id="${id}"> element`);
|
|
3062
3471
|
}
|
|
3063
3472
|
catch (error) {
|
|
3064
3473
|
console.error(error);
|
|
3474
|
+
return new FrameElement();
|
|
3065
3475
|
}
|
|
3066
|
-
return
|
|
3476
|
+
return null;
|
|
3067
3477
|
}
|
|
3068
3478
|
formActionIsVisitable(form, submitter) {
|
|
3069
3479
|
const action = getAction(form, submitter);
|
|
@@ -3083,10 +3493,10 @@ class FrameController {
|
|
|
3083
3493
|
return !frameElement.disabled;
|
|
3084
3494
|
}
|
|
3085
3495
|
}
|
|
3086
|
-
if (!session.
|
|
3496
|
+
if (!session.elementIsNavigatable(element)) {
|
|
3087
3497
|
return false;
|
|
3088
3498
|
}
|
|
3089
|
-
if (submitter && !session.
|
|
3499
|
+
if (submitter && !session.elementIsNavigatable(submitter)) {
|
|
3090
3500
|
return false;
|
|
3091
3501
|
}
|
|
3092
3502
|
return true;
|
|
@@ -3102,24 +3512,10 @@ class FrameController {
|
|
|
3102
3512
|
return this.element.src;
|
|
3103
3513
|
}
|
|
3104
3514
|
}
|
|
3105
|
-
get reloadable() {
|
|
3106
|
-
const frame = this.findFrameElement(this.element);
|
|
3107
|
-
return frame.hasAttribute("reloadable");
|
|
3108
|
-
}
|
|
3109
|
-
set reloadable(value) {
|
|
3110
|
-
const frame = this.findFrameElement(this.element);
|
|
3111
|
-
if (value) {
|
|
3112
|
-
frame.setAttribute("reloadable", "");
|
|
3113
|
-
}
|
|
3114
|
-
else {
|
|
3115
|
-
frame.removeAttribute("reloadable");
|
|
3116
|
-
}
|
|
3117
|
-
}
|
|
3118
3515
|
set sourceURL(sourceURL) {
|
|
3119
|
-
this.
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
this.settingSourceURL = false;
|
|
3516
|
+
this.ignoringChangesToAttribute("src", () => {
|
|
3517
|
+
this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
|
|
3518
|
+
});
|
|
3123
3519
|
}
|
|
3124
3520
|
get loadingStyle() {
|
|
3125
3521
|
return this.element.loading;
|
|
@@ -3127,6 +3523,19 @@ class FrameController {
|
|
|
3127
3523
|
get isLoading() {
|
|
3128
3524
|
return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
|
|
3129
3525
|
}
|
|
3526
|
+
get complete() {
|
|
3527
|
+
return this.element.hasAttribute("complete");
|
|
3528
|
+
}
|
|
3529
|
+
set complete(value) {
|
|
3530
|
+
this.ignoringChangesToAttribute("complete", () => {
|
|
3531
|
+
if (value) {
|
|
3532
|
+
this.element.setAttribute("complete", "");
|
|
3533
|
+
}
|
|
3534
|
+
else {
|
|
3535
|
+
this.element.removeAttribute("complete");
|
|
3536
|
+
}
|
|
3537
|
+
});
|
|
3538
|
+
}
|
|
3130
3539
|
get isActive() {
|
|
3131
3540
|
return this.element.isActive && this.connected;
|
|
3132
3541
|
}
|
|
@@ -3136,16 +3545,18 @@ class FrameController {
|
|
|
3136
3545
|
const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
|
|
3137
3546
|
return expandURL(root);
|
|
3138
3547
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
this.
|
|
3548
|
+
isIgnoringChangesTo(attributeName) {
|
|
3549
|
+
return this.ignoredAttributes.has(attributeName);
|
|
3550
|
+
}
|
|
3551
|
+
ignoringChangesToAttribute(attributeName, callback) {
|
|
3552
|
+
this.ignoredAttributes.add(attributeName);
|
|
3553
|
+
callback();
|
|
3554
|
+
this.ignoredAttributes.delete(attributeName);
|
|
3555
|
+
}
|
|
3556
|
+
withCurrentNavigationElement(element, callback) {
|
|
3557
|
+
this.currentNavigationElement = element;
|
|
3558
|
+
callback();
|
|
3559
|
+
delete this.currentNavigationElement;
|
|
3149
3560
|
}
|
|
3150
3561
|
}
|
|
3151
3562
|
function getFrameElementById(id) {
|
|
@@ -3173,35 +3584,6 @@ function activateElement(element, currentURL) {
|
|
|
3173
3584
|
}
|
|
3174
3585
|
}
|
|
3175
3586
|
|
|
3176
|
-
const StreamActions = {
|
|
3177
|
-
after() {
|
|
3178
|
-
this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
|
|
3179
|
-
},
|
|
3180
|
-
append() {
|
|
3181
|
-
this.removeDuplicateTargetChildren();
|
|
3182
|
-
this.targetElements.forEach(e => e.append(this.templateContent));
|
|
3183
|
-
},
|
|
3184
|
-
before() {
|
|
3185
|
-
this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
|
|
3186
|
-
},
|
|
3187
|
-
prepend() {
|
|
3188
|
-
this.removeDuplicateTargetChildren();
|
|
3189
|
-
this.targetElements.forEach(e => e.prepend(this.templateContent));
|
|
3190
|
-
},
|
|
3191
|
-
remove() {
|
|
3192
|
-
this.targetElements.forEach(e => e.remove());
|
|
3193
|
-
},
|
|
3194
|
-
replace() {
|
|
3195
|
-
this.targetElements.forEach(e => e.replaceWith(this.templateContent));
|
|
3196
|
-
},
|
|
3197
|
-
update() {
|
|
3198
|
-
this.targetElements.forEach(e => {
|
|
3199
|
-
e.innerHTML = "";
|
|
3200
|
-
e.append(this.templateContent);
|
|
3201
|
-
});
|
|
3202
|
-
}
|
|
3203
|
-
};
|
|
3204
|
-
|
|
3205
3587
|
class StreamElement extends HTMLElement {
|
|
3206
3588
|
async connectedCallback() {
|
|
3207
3589
|
try {
|
|
@@ -3216,12 +3598,12 @@ class StreamElement extends HTMLElement {
|
|
|
3216
3598
|
}
|
|
3217
3599
|
async render() {
|
|
3218
3600
|
var _a;
|
|
3219
|
-
return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
|
|
3601
|
+
return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
|
|
3220
3602
|
if (this.dispatchEvent(this.beforeRenderEvent)) {
|
|
3221
3603
|
await nextAnimationFrame();
|
|
3222
3604
|
this.performAction();
|
|
3223
3605
|
}
|
|
3224
|
-
})());
|
|
3606
|
+
})()));
|
|
3225
3607
|
}
|
|
3226
3608
|
disconnect() {
|
|
3227
3609
|
try {
|
|
@@ -3230,13 +3612,13 @@ class StreamElement extends HTMLElement {
|
|
|
3230
3612
|
catch (_a) { }
|
|
3231
3613
|
}
|
|
3232
3614
|
removeDuplicateTargetChildren() {
|
|
3233
|
-
this.duplicateChildren.forEach(c => c.remove());
|
|
3615
|
+
this.duplicateChildren.forEach((c) => c.remove());
|
|
3234
3616
|
}
|
|
3235
3617
|
get duplicateChildren() {
|
|
3236
3618
|
var _a;
|
|
3237
|
-
const existingChildren = this.targetElements.flatMap(e => [...e.children]).filter(c => !!c.id);
|
|
3238
|
-
const newChildrenIds = [...(_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children].filter(c => !!c.id).map(c => c.id);
|
|
3239
|
-
return existingChildren.filter(c => newChildrenIds.includes(c.id));
|
|
3619
|
+
const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
|
|
3620
|
+
const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
|
|
3621
|
+
return existingChildren.filter((c) => newChildrenIds.includes(c.id));
|
|
3240
3622
|
}
|
|
3241
3623
|
get performAction() {
|
|
3242
3624
|
if (this.action) {
|
|
@@ -3285,7 +3667,11 @@ class StreamElement extends HTMLElement {
|
|
|
3285
3667
|
return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
|
|
3286
3668
|
}
|
|
3287
3669
|
get beforeRenderEvent() {
|
|
3288
|
-
return new CustomEvent("turbo:before-stream-render", {
|
|
3670
|
+
return new CustomEvent("turbo:before-stream-render", {
|
|
3671
|
+
bubbles: true,
|
|
3672
|
+
cancelable: true,
|
|
3673
|
+
detail: { newStream: this },
|
|
3674
|
+
});
|
|
3289
3675
|
}
|
|
3290
3676
|
get targetElementsById() {
|
|
3291
3677
|
var _a;
|
|
@@ -3309,9 +3695,35 @@ class StreamElement extends HTMLElement {
|
|
|
3309
3695
|
}
|
|
3310
3696
|
}
|
|
3311
3697
|
|
|
3698
|
+
class StreamSourceElement extends HTMLElement {
|
|
3699
|
+
constructor() {
|
|
3700
|
+
super(...arguments);
|
|
3701
|
+
this.streamSource = null;
|
|
3702
|
+
}
|
|
3703
|
+
connectedCallback() {
|
|
3704
|
+
this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
|
|
3705
|
+
connectStreamSource(this.streamSource);
|
|
3706
|
+
}
|
|
3707
|
+
disconnectedCallback() {
|
|
3708
|
+
if (this.streamSource) {
|
|
3709
|
+
disconnectStreamSource(this.streamSource);
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
get src() {
|
|
3713
|
+
return this.getAttribute("src") || "";
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3312
3717
|
FrameElement.delegateConstructor = FrameController;
|
|
3313
|
-
customElements.
|
|
3314
|
-
customElements.define("turbo-
|
|
3718
|
+
if (customElements.get("turbo-frame") === undefined) {
|
|
3719
|
+
customElements.define("turbo-frame", FrameElement);
|
|
3720
|
+
}
|
|
3721
|
+
if (customElements.get("turbo-stream") === undefined) {
|
|
3722
|
+
customElements.define("turbo-stream", StreamElement);
|
|
3723
|
+
}
|
|
3724
|
+
if (customElements.get("turbo-stream-source") === undefined) {
|
|
3725
|
+
customElements.define("turbo-stream-source", StreamSourceElement);
|
|
3726
|
+
}
|
|
3315
3727
|
|
|
3316
3728
|
(() => {
|
|
3317
3729
|
let element = document.currentScript;
|
|
@@ -3319,7 +3731,8 @@ customElements.define("turbo-stream", StreamElement);
|
|
|
3319
3731
|
return;
|
|
3320
3732
|
if (element.hasAttribute("data-turbo-suppress-warning"))
|
|
3321
3733
|
return;
|
|
3322
|
-
|
|
3734
|
+
element = element.parentElement;
|
|
3735
|
+
while (element) {
|
|
3323
3736
|
if (element == document.body) {
|
|
3324
3737
|
return console.warn(unindent `
|
|
3325
3738
|
You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
|
|
@@ -3332,10 +3745,11 @@ customElements.define("turbo-stream", StreamElement);
|
|
|
3332
3745
|
Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
|
|
3333
3746
|
`, element.outerHTML);
|
|
3334
3747
|
}
|
|
3748
|
+
element = element.parentElement;
|
|
3335
3749
|
}
|
|
3336
3750
|
})();
|
|
3337
3751
|
|
|
3338
3752
|
window.Turbo = Turbo;
|
|
3339
3753
|
start();
|
|
3340
3754
|
|
|
3341
|
-
export { PageRenderer, PageSnapshot, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setProgressBarDelay, start, visit };
|
|
3755
|
+
export { FrameRenderer, PageRenderer, PageSnapshot, StreamActions, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
|