@hotwired/turbo 7.1.0 → 7.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/turbo.es2017-esm.js +1139 -630
- package/dist/turbo.es2017-umd.js +1153 -637
- 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 +12 -5
- 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 +5 -4
- package/dist/types/core/drive/page_renderer.d.ts +11 -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 +17 -5
- package/dist/types/core/frames/frame_controller.d.ts +50 -24
- 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 +12 -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 +81 -21
- package/dist/types/core/snapshot.d.ts +5 -2
- package/dist/types/core/streams/stream_actions.d.ts +4 -2
- package/dist/types/core/streams/stream_message.d.ts +2 -6
- package/dist/types/core/streams/stream_message_renderer.d.ts +7 -0
- 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/index.d.ts +1 -0
- package/dist/types/elements/stream_element.d.ts +8 -1
- package/dist/types/elements/stream_source_element.d.ts +7 -0
- package/dist/types/http/fetch_request.d.ts +11 -3
- package/dist/types/http/index.d.ts +1 -0
- package/dist/types/index.d.ts +2 -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_custom_body_tests.d.ts +1 -0
- 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 -52
- 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 -38
- 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 -15
- package/dist/types/tests/helpers/page.d.ts +50 -0
- package/dist/types/tests/unit/deprecated_adapter_support_test.d.ts +10 -10
- package/dist/types/util.d.ts +14 -3
- package/package.json +23 -9
- 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-rc.1
|
|
3
|
+
Copyright © 2022 Basecamp, LLC
|
|
4
4
|
*/
|
|
5
5
|
(function () {
|
|
6
|
-
if (window.Reflect === undefined ||
|
|
6
|
+
if (window.Reflect === undefined ||
|
|
7
|
+
window.customElements === undefined ||
|
|
7
8
|
window.customElements.polyfillWrapFlushCallback) {
|
|
8
9
|
return;
|
|
9
10
|
}
|
|
10
11
|
const BuiltInHTMLElement = HTMLElement;
|
|
11
12
|
const wrapperForTheName = {
|
|
12
|
-
|
|
13
|
+
HTMLElement: function HTMLElement() {
|
|
13
14
|
return Reflect.construct(BuiltInHTMLElement, [], this.constructor);
|
|
14
|
-
}
|
|
15
|
+
},
|
|
15
16
|
};
|
|
16
|
-
window.HTMLElement =
|
|
17
|
-
wrapperForTheName['HTMLElement'];
|
|
17
|
+
window.HTMLElement = wrapperForTheName["HTMLElement"];
|
|
18
18
|
HTMLElement.prototype = BuiltInHTMLElement.prototype;
|
|
19
19
|
HTMLElement.prototype.constructor = HTMLElement;
|
|
20
20
|
Object.setPrototypeOf(HTMLElement, BuiltInHTMLElement);
|
|
@@ -72,7 +72,7 @@ Copyright © 2021 Basecamp, LLC
|
|
|
72
72
|
}
|
|
73
73
|
})(HTMLFormElement.prototype);
|
|
74
74
|
|
|
75
|
-
const submittersByForm = new WeakMap;
|
|
75
|
+
const submittersByForm = new WeakMap();
|
|
76
76
|
function findSubmitterFromClickTarget(target) {
|
|
77
77
|
const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
78
78
|
const candidate = element ? element.closest("input, button") : null;
|
|
@@ -103,7 +103,7 @@ function clickCaptured(event) {
|
|
|
103
103
|
if (this.type == "submit" && this.target instanceof HTMLFormElement) {
|
|
104
104
|
return submittersByForm.get(this.target);
|
|
105
105
|
}
|
|
106
|
-
}
|
|
106
|
+
},
|
|
107
107
|
});
|
|
108
108
|
})();
|
|
109
109
|
|
|
@@ -119,7 +119,7 @@ class FrameElement extends HTMLElement {
|
|
|
119
119
|
this.delegate = new FrameElement.delegateConstructor(this);
|
|
120
120
|
}
|
|
121
121
|
static get observedAttributes() {
|
|
122
|
-
return ["disabled", "loading", "src"];
|
|
122
|
+
return ["disabled", "complete", "loading", "src"];
|
|
123
123
|
}
|
|
124
124
|
connectedCallback() {
|
|
125
125
|
this.delegate.connect();
|
|
@@ -129,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,15 +394,19 @@ 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
|
}
|
|
364
405
|
return null;
|
|
365
406
|
}
|
|
407
|
+
function hasAttribute(attributeName, ...elements) {
|
|
408
|
+
return elements.some((element) => element && element.hasAttribute(attributeName));
|
|
409
|
+
}
|
|
366
410
|
function markAsBusy(...elements) {
|
|
367
411
|
for (const element of elements) {
|
|
368
412
|
if (element.localName == "turbo-frame") {
|
|
@@ -379,6 +423,51 @@ function clearBusyState(...elements) {
|
|
|
379
423
|
element.removeAttribute("aria-busy");
|
|
380
424
|
}
|
|
381
425
|
}
|
|
426
|
+
function waitForLoad(element, timeoutInMilliseconds = 2000) {
|
|
427
|
+
return new Promise((resolve) => {
|
|
428
|
+
const onComplete = () => {
|
|
429
|
+
element.removeEventListener("error", onComplete);
|
|
430
|
+
element.removeEventListener("load", onComplete);
|
|
431
|
+
resolve();
|
|
432
|
+
};
|
|
433
|
+
element.addEventListener("load", onComplete, { once: true });
|
|
434
|
+
element.addEventListener("error", onComplete, { once: true });
|
|
435
|
+
setTimeout(resolve, timeoutInMilliseconds);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function getHistoryMethodForAction(action) {
|
|
439
|
+
switch (action) {
|
|
440
|
+
case "replace":
|
|
441
|
+
return history.replaceState;
|
|
442
|
+
case "advance":
|
|
443
|
+
case "restore":
|
|
444
|
+
return history.pushState;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
function getVisitAction(...elements) {
|
|
448
|
+
const action = getAttribute("data-turbo-action", ...elements);
|
|
449
|
+
return isAction(action) ? action : null;
|
|
450
|
+
}
|
|
451
|
+
function getBodyElementId() {
|
|
452
|
+
return getMetaContent("turbo-body");
|
|
453
|
+
}
|
|
454
|
+
function getMetaElement(name) {
|
|
455
|
+
return document.querySelector(`meta[name="${name}"]`);
|
|
456
|
+
}
|
|
457
|
+
function getMetaContent(name) {
|
|
458
|
+
const element = getMetaElement(name);
|
|
459
|
+
return element && element.content;
|
|
460
|
+
}
|
|
461
|
+
function setMetaContent(name, content) {
|
|
462
|
+
let element = getMetaElement(name);
|
|
463
|
+
if (!element) {
|
|
464
|
+
element = document.createElement("meta");
|
|
465
|
+
element.setAttribute("name", name);
|
|
466
|
+
document.head.appendChild(element);
|
|
467
|
+
}
|
|
468
|
+
element.setAttribute("content", content);
|
|
469
|
+
return element;
|
|
470
|
+
}
|
|
382
471
|
|
|
383
472
|
var FetchMethod;
|
|
384
473
|
(function (FetchMethod) {
|
|
@@ -390,17 +479,22 @@ var FetchMethod;
|
|
|
390
479
|
})(FetchMethod || (FetchMethod = {}));
|
|
391
480
|
function fetchMethodFromString(method) {
|
|
392
481
|
switch (method.toLowerCase()) {
|
|
393
|
-
case "get":
|
|
394
|
-
|
|
395
|
-
case "
|
|
396
|
-
|
|
397
|
-
case "
|
|
482
|
+
case "get":
|
|
483
|
+
return FetchMethod.get;
|
|
484
|
+
case "post":
|
|
485
|
+
return FetchMethod.post;
|
|
486
|
+
case "put":
|
|
487
|
+
return FetchMethod.put;
|
|
488
|
+
case "patch":
|
|
489
|
+
return FetchMethod.patch;
|
|
490
|
+
case "delete":
|
|
491
|
+
return FetchMethod.delete;
|
|
398
492
|
}
|
|
399
493
|
}
|
|
400
494
|
class FetchRequest {
|
|
401
|
-
constructor(delegate, method, location, body = new URLSearchParams, target = null) {
|
|
402
|
-
this.abortController = new AbortController;
|
|
403
|
-
this.resolveRequestPromise = (
|
|
495
|
+
constructor(delegate, method, location, body = new URLSearchParams(), target = null) {
|
|
496
|
+
this.abortController = new AbortController();
|
|
497
|
+
this.resolveRequestPromise = (_value) => { };
|
|
404
498
|
this.delegate = delegate;
|
|
405
499
|
this.method = method;
|
|
406
500
|
this.headers = this.defaultHeaders;
|
|
@@ -431,7 +525,7 @@ class FetchRequest {
|
|
|
431
525
|
return await this.receive(response);
|
|
432
526
|
}
|
|
433
527
|
catch (error) {
|
|
434
|
-
if (error.name !==
|
|
528
|
+
if (error.name !== "AbortError") {
|
|
435
529
|
this.delegate.requestErrored(this, error);
|
|
436
530
|
throw error;
|
|
437
531
|
}
|
|
@@ -442,7 +536,11 @@ class FetchRequest {
|
|
|
442
536
|
}
|
|
443
537
|
async receive(response) {
|
|
444
538
|
const fetchResponse = new FetchResponse(response);
|
|
445
|
-
const event = dispatch("turbo:before-fetch-response", {
|
|
539
|
+
const event = dispatch("turbo:before-fetch-response", {
|
|
540
|
+
cancelable: true,
|
|
541
|
+
detail: { fetchResponse },
|
|
542
|
+
target: this.target,
|
|
543
|
+
});
|
|
446
544
|
if (event.defaultPrevented) {
|
|
447
545
|
this.delegate.requestPreventedHandlingResponse(this, fetchResponse);
|
|
448
546
|
}
|
|
@@ -463,12 +561,12 @@ class FetchRequest {
|
|
|
463
561
|
redirect: "follow",
|
|
464
562
|
body: this.isIdempotent ? null : this.body,
|
|
465
563
|
signal: this.abortSignal,
|
|
466
|
-
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
|
|
564
|
+
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href,
|
|
467
565
|
};
|
|
468
566
|
}
|
|
469
567
|
get defaultHeaders() {
|
|
470
568
|
return {
|
|
471
|
-
|
|
569
|
+
Accept: "text/html, application/xhtml+xml",
|
|
472
570
|
};
|
|
473
571
|
}
|
|
474
572
|
get isIdempotent() {
|
|
@@ -477,16 +575,19 @@ class FetchRequest {
|
|
|
477
575
|
get abortSignal() {
|
|
478
576
|
return this.abortController.signal;
|
|
479
577
|
}
|
|
578
|
+
acceptResponseType(mimeType) {
|
|
579
|
+
this.headers["Accept"] = [mimeType, this.headers["Accept"]].join(", ");
|
|
580
|
+
}
|
|
480
581
|
async allowRequestToBeIntercepted(fetchOptions) {
|
|
481
|
-
const requestInterception = new Promise(resolve => this.resolveRequestPromise = resolve);
|
|
582
|
+
const requestInterception = new Promise((resolve) => (this.resolveRequestPromise = resolve));
|
|
482
583
|
const event = dispatch("turbo:before-fetch-request", {
|
|
483
584
|
cancelable: true,
|
|
484
585
|
detail: {
|
|
485
586
|
fetchOptions,
|
|
486
587
|
url: this.url,
|
|
487
|
-
resume: this.resolveRequestPromise
|
|
588
|
+
resume: this.resolveRequestPromise,
|
|
488
589
|
},
|
|
489
|
-
target: this.target
|
|
590
|
+
target: this.target,
|
|
490
591
|
});
|
|
491
592
|
if (event.defaultPrevented)
|
|
492
593
|
await requestInterception;
|
|
@@ -496,7 +597,7 @@ class FetchRequest {
|
|
|
496
597
|
class AppearanceObserver {
|
|
497
598
|
constructor(delegate, element) {
|
|
498
599
|
this.started = false;
|
|
499
|
-
this.intersect = entries => {
|
|
600
|
+
this.intersect = (entries) => {
|
|
500
601
|
const lastEntry = entries.slice(-1)[0];
|
|
501
602
|
if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
|
|
502
603
|
this.delegate.elementAppearedInViewport(this.element);
|
|
@@ -521,40 +622,29 @@ class AppearanceObserver {
|
|
|
521
622
|
}
|
|
522
623
|
|
|
523
624
|
class StreamMessage {
|
|
524
|
-
constructor(
|
|
525
|
-
this.
|
|
526
|
-
this.templateElement.innerHTML = html;
|
|
625
|
+
constructor(fragment) {
|
|
626
|
+
this.fragment = importStreamElements(fragment);
|
|
527
627
|
}
|
|
528
628
|
static wrap(message) {
|
|
529
629
|
if (typeof message == "string") {
|
|
530
|
-
return new this(message);
|
|
630
|
+
return new this(createDocumentFragment(message));
|
|
531
631
|
}
|
|
532
632
|
else {
|
|
533
633
|
return message;
|
|
534
634
|
}
|
|
535
635
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
636
|
+
}
|
|
637
|
+
StreamMessage.contentType = "text/vnd.turbo-stream.html";
|
|
638
|
+
function importStreamElements(fragment) {
|
|
639
|
+
for (const element of fragment.querySelectorAll("turbo-stream")) {
|
|
640
|
+
const streamElement = document.importNode(element, true);
|
|
641
|
+
for (const inertScriptElement of streamElement.templateElement.content.querySelectorAll("script")) {
|
|
642
|
+
inertScriptElement.replaceWith(activateScriptElement(inertScriptElement));
|
|
540
643
|
}
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
get foreignElements() {
|
|
544
|
-
return this.templateChildren.reduce((streamElements, child) => {
|
|
545
|
-
if (child.tagName.toLowerCase() == "turbo-stream") {
|
|
546
|
-
return [...streamElements, child];
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
return streamElements;
|
|
550
|
-
}
|
|
551
|
-
}, []);
|
|
552
|
-
}
|
|
553
|
-
get templateChildren() {
|
|
554
|
-
return Array.from(this.templateElement.content.children);
|
|
644
|
+
element.replaceWith(streamElement);
|
|
555
645
|
}
|
|
646
|
+
return fragment;
|
|
556
647
|
}
|
|
557
|
-
StreamMessage.contentType = "text/vnd.turbo-stream.html";
|
|
558
648
|
|
|
559
649
|
var FormSubmissionState;
|
|
560
650
|
(function (FormSubmissionState) {
|
|
@@ -573,9 +663,12 @@ var FormEnctype;
|
|
|
573
663
|
})(FormEnctype || (FormEnctype = {}));
|
|
574
664
|
function formEnctypeFromString(encoding) {
|
|
575
665
|
switch (encoding.toLowerCase()) {
|
|
576
|
-
case FormEnctype.multipart:
|
|
577
|
-
|
|
578
|
-
|
|
666
|
+
case FormEnctype.multipart:
|
|
667
|
+
return FormEnctype.multipart;
|
|
668
|
+
case FormEnctype.plain:
|
|
669
|
+
return FormEnctype.plain;
|
|
670
|
+
default:
|
|
671
|
+
return FormEnctype.urlEncoded;
|
|
579
672
|
}
|
|
580
673
|
}
|
|
581
674
|
class FormSubmission {
|
|
@@ -592,8 +685,8 @@ class FormSubmission {
|
|
|
592
685
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
593
686
|
this.mustRedirect = mustRedirect;
|
|
594
687
|
}
|
|
595
|
-
static confirmMethod(message,
|
|
596
|
-
return confirm(message);
|
|
688
|
+
static confirmMethod(message, _element, _submitter) {
|
|
689
|
+
return Promise.resolve(confirm(message));
|
|
597
690
|
}
|
|
598
691
|
get method() {
|
|
599
692
|
var _a;
|
|
@@ -602,8 +695,13 @@ class FormSubmission {
|
|
|
602
695
|
}
|
|
603
696
|
get action() {
|
|
604
697
|
var _a;
|
|
605
|
-
const formElementAction = typeof this.formElement.action ===
|
|
606
|
-
|
|
698
|
+
const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
|
|
699
|
+
if ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.hasAttribute("formaction")) {
|
|
700
|
+
return this.submitter.getAttribute("formaction") || "";
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
return this.formElement.getAttribute("action") || formElementAction || "";
|
|
704
|
+
}
|
|
607
705
|
}
|
|
608
706
|
get body() {
|
|
609
707
|
if (this.enctype == FormEnctype.urlEncoded || this.method == FetchMethod.get) {
|
|
@@ -625,16 +723,11 @@ class FormSubmission {
|
|
|
625
723
|
return entries.concat(typeof value == "string" ? [[name, value]] : []);
|
|
626
724
|
}, []);
|
|
627
725
|
}
|
|
628
|
-
get confirmationMessage() {
|
|
629
|
-
return this.formElement.getAttribute("data-turbo-confirm");
|
|
630
|
-
}
|
|
631
|
-
get needsConfirmation() {
|
|
632
|
-
return this.confirmationMessage !== null;
|
|
633
|
-
}
|
|
634
726
|
async start() {
|
|
635
727
|
const { initialized, requesting } = FormSubmissionState;
|
|
636
|
-
|
|
637
|
-
|
|
728
|
+
const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
|
|
729
|
+
if (typeof confirmationMessage === "string") {
|
|
730
|
+
const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
|
|
638
731
|
if (!answer) {
|
|
639
732
|
return;
|
|
640
733
|
}
|
|
@@ -658,14 +751,19 @@ class FormSubmission {
|
|
|
658
751
|
if (token) {
|
|
659
752
|
headers["X-CSRF-Token"] = token;
|
|
660
753
|
}
|
|
661
|
-
|
|
754
|
+
}
|
|
755
|
+
if (this.requestAcceptsTurboStreamResponse(request)) {
|
|
756
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
662
757
|
}
|
|
663
758
|
}
|
|
664
|
-
requestStarted(
|
|
759
|
+
requestStarted(_request) {
|
|
665
760
|
var _a;
|
|
666
761
|
this.state = FormSubmissionState.waiting;
|
|
667
762
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.setAttribute("disabled", "");
|
|
668
|
-
dispatch("turbo:submit-start", {
|
|
763
|
+
dispatch("turbo:submit-start", {
|
|
764
|
+
target: this.formElement,
|
|
765
|
+
detail: { formSubmission: this },
|
|
766
|
+
});
|
|
669
767
|
this.delegate.formSubmissionStarted(this);
|
|
670
768
|
}
|
|
671
769
|
requestPreventedHandlingResponse(request, response) {
|
|
@@ -691,25 +789,35 @@ class FormSubmission {
|
|
|
691
789
|
}
|
|
692
790
|
requestErrored(request, error) {
|
|
693
791
|
this.result = { success: false, error };
|
|
792
|
+
dispatch("turbo:fetch-request-error", {
|
|
793
|
+
target: this.formElement,
|
|
794
|
+
detail: { request, error },
|
|
795
|
+
});
|
|
694
796
|
this.delegate.formSubmissionErrored(this, error);
|
|
695
797
|
}
|
|
696
|
-
requestFinished(
|
|
798
|
+
requestFinished(_request) {
|
|
697
799
|
var _a;
|
|
698
800
|
this.state = FormSubmissionState.stopped;
|
|
699
801
|
(_a = this.submitter) === null || _a === void 0 ? void 0 : _a.removeAttribute("disabled");
|
|
700
|
-
dispatch("turbo:submit-end", {
|
|
802
|
+
dispatch("turbo:submit-end", {
|
|
803
|
+
target: this.formElement,
|
|
804
|
+
detail: Object.assign({ formSubmission: this }, this.result),
|
|
805
|
+
});
|
|
701
806
|
this.delegate.formSubmissionFinished(this);
|
|
702
807
|
}
|
|
703
808
|
requestMustRedirect(request) {
|
|
704
809
|
return !request.isIdempotent && this.mustRedirect;
|
|
705
810
|
}
|
|
811
|
+
requestAcceptsTurboStreamResponse(request) {
|
|
812
|
+
return !request.isIdempotent || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
|
|
813
|
+
}
|
|
706
814
|
}
|
|
707
815
|
function buildFormData(formElement, submitter) {
|
|
708
816
|
const formData = new FormData(formElement);
|
|
709
817
|
const name = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("name");
|
|
710
818
|
const value = submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("value");
|
|
711
|
-
if (name
|
|
712
|
-
formData.append(name, value);
|
|
819
|
+
if (name) {
|
|
820
|
+
formData.append(name, value || "");
|
|
713
821
|
}
|
|
714
822
|
return formData;
|
|
715
823
|
}
|
|
@@ -723,15 +831,11 @@ function getCookieValue(cookieName) {
|
|
|
723
831
|
}
|
|
724
832
|
}
|
|
725
833
|
}
|
|
726
|
-
function getMetaContent(name) {
|
|
727
|
-
const element = document.querySelector(`meta[name="${name}"]`);
|
|
728
|
-
return element && element.content;
|
|
729
|
-
}
|
|
730
834
|
function responseSucceededWithoutRedirect(response) {
|
|
731
835
|
return response.statusCode == 200 && !response.redirected;
|
|
732
836
|
}
|
|
733
837
|
function mergeFormDataEntries(url, entries) {
|
|
734
|
-
const searchParams = new URLSearchParams;
|
|
838
|
+
const searchParams = new URLSearchParams();
|
|
735
839
|
for (const [name, value] of entries) {
|
|
736
840
|
if (value instanceof File)
|
|
737
841
|
continue;
|
|
@@ -745,6 +849,9 @@ class Snapshot {
|
|
|
745
849
|
constructor(element) {
|
|
746
850
|
this.element = element;
|
|
747
851
|
}
|
|
852
|
+
get activeElement() {
|
|
853
|
+
return this.element.ownerDocument.activeElement;
|
|
854
|
+
}
|
|
748
855
|
get children() {
|
|
749
856
|
return [...this.element.children];
|
|
750
857
|
}
|
|
@@ -758,13 +865,20 @@ class Snapshot {
|
|
|
758
865
|
return this.element.isConnected;
|
|
759
866
|
}
|
|
760
867
|
get firstAutofocusableElement() {
|
|
761
|
-
|
|
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;
|
|
762
876
|
}
|
|
763
877
|
get permanentElements() {
|
|
764
|
-
return
|
|
878
|
+
return queryPermanentElementsAll(this.element);
|
|
765
879
|
}
|
|
766
880
|
getPermanentElementById(id) {
|
|
767
|
-
return this.element
|
|
881
|
+
return getPermanentElementById(this.element, id);
|
|
768
882
|
}
|
|
769
883
|
getPermanentElementMapForSnapshot(snapshot) {
|
|
770
884
|
const permanentElementMap = {};
|
|
@@ -778,36 +892,66 @@ class Snapshot {
|
|
|
778
892
|
return permanentElementMap;
|
|
779
893
|
}
|
|
780
894
|
}
|
|
895
|
+
function getPermanentElementById(node, id) {
|
|
896
|
+
return node.querySelector(`#${id}[data-turbo-permanent]`);
|
|
897
|
+
}
|
|
898
|
+
function queryPermanentElementsAll(node) {
|
|
899
|
+
return node.querySelectorAll("[id][data-turbo-permanent]");
|
|
900
|
+
}
|
|
781
901
|
|
|
782
|
-
class
|
|
783
|
-
constructor(delegate,
|
|
902
|
+
class FormSubmitObserver {
|
|
903
|
+
constructor(delegate, eventTarget) {
|
|
904
|
+
this.started = false;
|
|
905
|
+
this.submitCaptured = () => {
|
|
906
|
+
this.eventTarget.removeEventListener("submit", this.submitBubbled, false);
|
|
907
|
+
this.eventTarget.addEventListener("submit", this.submitBubbled, false);
|
|
908
|
+
};
|
|
784
909
|
this.submitBubbled = ((event) => {
|
|
785
|
-
|
|
786
|
-
|
|
910
|
+
if (!event.defaultPrevented) {
|
|
911
|
+
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
787
912
|
const submitter = event.submitter || undefined;
|
|
788
|
-
|
|
789
|
-
|
|
913
|
+
if (form &&
|
|
914
|
+
submissionDoesNotDismissDialog(form, submitter) &&
|
|
915
|
+
submissionDoesNotTargetIFrame(form, submitter) &&
|
|
916
|
+
this.delegate.willSubmitForm(form, submitter)) {
|
|
790
917
|
event.preventDefault();
|
|
791
|
-
|
|
792
|
-
this.delegate.formSubmissionIntercepted(form, submitter);
|
|
918
|
+
this.delegate.formSubmitted(form, submitter);
|
|
793
919
|
}
|
|
794
920
|
}
|
|
795
921
|
});
|
|
796
922
|
this.delegate = delegate;
|
|
797
|
-
this.
|
|
923
|
+
this.eventTarget = eventTarget;
|
|
798
924
|
}
|
|
799
925
|
start() {
|
|
800
|
-
|
|
926
|
+
if (!this.started) {
|
|
927
|
+
this.eventTarget.addEventListener("submit", this.submitCaptured, true);
|
|
928
|
+
this.started = true;
|
|
929
|
+
}
|
|
801
930
|
}
|
|
802
931
|
stop() {
|
|
803
|
-
|
|
932
|
+
if (this.started) {
|
|
933
|
+
this.eventTarget.removeEventListener("submit", this.submitCaptured, true);
|
|
934
|
+
this.started = false;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
function submissionDoesNotDismissDialog(form, submitter) {
|
|
939
|
+
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
940
|
+
return method != "dialog";
|
|
941
|
+
}
|
|
942
|
+
function submissionDoesNotTargetIFrame(form, submitter) {
|
|
943
|
+
const target = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formtarget")) || form.target;
|
|
944
|
+
for (const element of document.getElementsByName(target)) {
|
|
945
|
+
if (element instanceof HTMLIFrameElement)
|
|
946
|
+
return false;
|
|
804
947
|
}
|
|
948
|
+
return true;
|
|
805
949
|
}
|
|
806
950
|
|
|
807
951
|
class View {
|
|
808
952
|
constructor(delegate, element) {
|
|
809
|
-
this.resolveRenderPromise = (
|
|
810
|
-
this.resolveInterceptionPromise = (
|
|
953
|
+
this.resolveRenderPromise = (_value) => { };
|
|
954
|
+
this.resolveInterceptionPromise = (_value) => { };
|
|
811
955
|
this.delegate = delegate;
|
|
812
956
|
this.element = element;
|
|
813
957
|
}
|
|
@@ -852,15 +996,17 @@ class View {
|
|
|
852
996
|
const { isPreview, shouldRender, newSnapshot: snapshot } = renderer;
|
|
853
997
|
if (shouldRender) {
|
|
854
998
|
try {
|
|
855
|
-
this.renderPromise = new Promise(resolve => this.resolveRenderPromise = resolve);
|
|
999
|
+
this.renderPromise = new Promise((resolve) => (this.resolveRenderPromise = resolve));
|
|
856
1000
|
this.renderer = renderer;
|
|
857
|
-
this.prepareToRenderSnapshot(renderer);
|
|
858
|
-
const renderInterception = new Promise(resolve => this.resolveInterceptionPromise = resolve);
|
|
859
|
-
const
|
|
1001
|
+
await this.prepareToRenderSnapshot(renderer);
|
|
1002
|
+
const renderInterception = new Promise((resolve) => (this.resolveInterceptionPromise = resolve));
|
|
1003
|
+
const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement };
|
|
1004
|
+
const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
|
|
860
1005
|
if (!immediateRender)
|
|
861
1006
|
await renderInterception;
|
|
862
1007
|
await this.renderSnapshot(renderer);
|
|
863
1008
|
this.delegate.viewRenderedSnapshot(snapshot, isPreview);
|
|
1009
|
+
this.delegate.preloadOnLoadLinksForView(this.element);
|
|
864
1010
|
this.finishRenderingSnapshot(renderer);
|
|
865
1011
|
}
|
|
866
1012
|
finally {
|
|
@@ -870,15 +1016,15 @@ class View {
|
|
|
870
1016
|
}
|
|
871
1017
|
}
|
|
872
1018
|
else {
|
|
873
|
-
this.invalidate();
|
|
1019
|
+
this.invalidate(renderer.reloadReason);
|
|
874
1020
|
}
|
|
875
1021
|
}
|
|
876
|
-
invalidate() {
|
|
877
|
-
this.delegate.viewInvalidated();
|
|
1022
|
+
invalidate(reason) {
|
|
1023
|
+
this.delegate.viewInvalidated(reason);
|
|
878
1024
|
}
|
|
879
|
-
prepareToRenderSnapshot(renderer) {
|
|
1025
|
+
async prepareToRenderSnapshot(renderer) {
|
|
880
1026
|
this.markAsPreview(renderer.isPreview);
|
|
881
|
-
renderer.prepareToRender();
|
|
1027
|
+
await renderer.prepareToRender();
|
|
882
1028
|
}
|
|
883
1029
|
markAsPreview(isPreview) {
|
|
884
1030
|
if (isPreview) {
|
|
@@ -905,65 +1051,125 @@ class FrameView extends View {
|
|
|
905
1051
|
}
|
|
906
1052
|
}
|
|
907
1053
|
|
|
908
|
-
class
|
|
909
|
-
constructor(delegate,
|
|
910
|
-
this.
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
else {
|
|
915
|
-
delete this.clickEvent;
|
|
916
|
-
}
|
|
1054
|
+
class LinkClickObserver {
|
|
1055
|
+
constructor(delegate, eventTarget) {
|
|
1056
|
+
this.started = false;
|
|
1057
|
+
this.clickCaptured = () => {
|
|
1058
|
+
this.eventTarget.removeEventListener("click", this.clickBubbled, false);
|
|
1059
|
+
this.eventTarget.addEventListener("click", this.clickBubbled, false);
|
|
917
1060
|
};
|
|
918
|
-
this.
|
|
919
|
-
if (
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
this.
|
|
1061
|
+
this.clickBubbled = (event) => {
|
|
1062
|
+
if (event instanceof MouseEvent && this.clickEventIsSignificant(event)) {
|
|
1063
|
+
const target = (event.composedPath && event.composedPath()[0]) || event.target;
|
|
1064
|
+
const link = this.findLinkFromClickTarget(target);
|
|
1065
|
+
if (link && doesNotTargetIFrame(link)) {
|
|
1066
|
+
const location = this.getLocationForLink(link);
|
|
1067
|
+
if (this.delegate.willFollowLinkToLocation(link, location, event)) {
|
|
1068
|
+
event.preventDefault();
|
|
1069
|
+
this.delegate.followedLinkToLocation(link, location);
|
|
1070
|
+
}
|
|
924
1071
|
}
|
|
925
1072
|
}
|
|
926
|
-
delete this.clickEvent;
|
|
927
|
-
});
|
|
928
|
-
this.willVisit = () => {
|
|
929
|
-
delete this.clickEvent;
|
|
930
1073
|
};
|
|
931
1074
|
this.delegate = delegate;
|
|
932
|
-
this.
|
|
1075
|
+
this.eventTarget = eventTarget;
|
|
1076
|
+
}
|
|
1077
|
+
start() {
|
|
1078
|
+
if (!this.started) {
|
|
1079
|
+
this.eventTarget.addEventListener("click", this.clickCaptured, true);
|
|
1080
|
+
this.started = true;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
stop() {
|
|
1084
|
+
if (this.started) {
|
|
1085
|
+
this.eventTarget.removeEventListener("click", this.clickCaptured, true);
|
|
1086
|
+
this.started = false;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
clickEventIsSignificant(event) {
|
|
1090
|
+
return !((event.target && event.target.isContentEditable) ||
|
|
1091
|
+
event.defaultPrevented ||
|
|
1092
|
+
event.which > 1 ||
|
|
1093
|
+
event.altKey ||
|
|
1094
|
+
event.ctrlKey ||
|
|
1095
|
+
event.metaKey ||
|
|
1096
|
+
event.shiftKey);
|
|
1097
|
+
}
|
|
1098
|
+
findLinkFromClickTarget(target) {
|
|
1099
|
+
if (target instanceof Element) {
|
|
1100
|
+
return target.closest("a[href]:not([target^=_]):not([download])");
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
getLocationForLink(link) {
|
|
1104
|
+
return expandURL(link.getAttribute("href") || "");
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
function doesNotTargetIFrame(anchor) {
|
|
1108
|
+
for (const element of document.getElementsByName(anchor.target)) {
|
|
1109
|
+
if (element instanceof HTMLIFrameElement)
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
return true;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
class FormLinkClickObserver {
|
|
1116
|
+
constructor(delegate, element) {
|
|
1117
|
+
this.delegate = delegate;
|
|
1118
|
+
this.linkClickObserver = new LinkClickObserver(this, element);
|
|
933
1119
|
}
|
|
934
1120
|
start() {
|
|
935
|
-
this.
|
|
936
|
-
document.addEventListener("turbo:click", this.linkClicked);
|
|
937
|
-
document.addEventListener("turbo:before-visit", this.willVisit);
|
|
1121
|
+
this.linkClickObserver.start();
|
|
938
1122
|
}
|
|
939
1123
|
stop() {
|
|
940
|
-
this.
|
|
941
|
-
|
|
942
|
-
|
|
1124
|
+
this.linkClickObserver.stop();
|
|
1125
|
+
}
|
|
1126
|
+
willFollowLinkToLocation(link, location, originalEvent) {
|
|
1127
|
+
return (this.delegate.willSubmitFormLinkToLocation(link, location, originalEvent) &&
|
|
1128
|
+
link.hasAttribute("data-turbo-method"));
|
|
943
1129
|
}
|
|
944
|
-
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1130
|
+
followedLinkToLocation(link, location) {
|
|
1131
|
+
const action = location.href;
|
|
1132
|
+
const form = document.createElement("form");
|
|
1133
|
+
form.setAttribute("data-turbo", "true");
|
|
1134
|
+
form.setAttribute("action", action);
|
|
1135
|
+
form.setAttribute("hidden", "");
|
|
1136
|
+
const method = link.getAttribute("data-turbo-method");
|
|
1137
|
+
if (method)
|
|
1138
|
+
form.setAttribute("method", method);
|
|
1139
|
+
const turboFrame = link.getAttribute("data-turbo-frame");
|
|
1140
|
+
if (turboFrame)
|
|
1141
|
+
form.setAttribute("data-turbo-frame", turboFrame);
|
|
1142
|
+
const turboAction = link.getAttribute("data-turbo-action");
|
|
1143
|
+
if (turboAction)
|
|
1144
|
+
form.setAttribute("data-turbo-action", turboAction);
|
|
1145
|
+
const turboConfirm = link.getAttribute("data-turbo-confirm");
|
|
1146
|
+
if (turboConfirm)
|
|
1147
|
+
form.setAttribute("data-turbo-confirm", turboConfirm);
|
|
1148
|
+
const turboStream = link.hasAttribute("data-turbo-stream");
|
|
1149
|
+
if (turboStream)
|
|
1150
|
+
form.setAttribute("data-turbo-stream", "");
|
|
1151
|
+
this.delegate.submittedFormLinkToLocation(link, location, form);
|
|
1152
|
+
document.body.appendChild(form);
|
|
1153
|
+
form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
|
|
1154
|
+
requestAnimationFrame(() => form.requestSubmit());
|
|
951
1155
|
}
|
|
952
1156
|
}
|
|
953
1157
|
|
|
954
1158
|
class Bardo {
|
|
955
|
-
constructor(permanentElementMap) {
|
|
1159
|
+
constructor(delegate, permanentElementMap) {
|
|
1160
|
+
this.delegate = delegate;
|
|
956
1161
|
this.permanentElementMap = permanentElementMap;
|
|
957
1162
|
}
|
|
958
|
-
static preservingPermanentElements(permanentElementMap, callback) {
|
|
959
|
-
const bardo = new this(permanentElementMap);
|
|
1163
|
+
static preservingPermanentElements(delegate, permanentElementMap, callback) {
|
|
1164
|
+
const bardo = new this(delegate, permanentElementMap);
|
|
960
1165
|
bardo.enter();
|
|
961
1166
|
callback();
|
|
962
1167
|
bardo.leave();
|
|
963
1168
|
}
|
|
964
1169
|
enter() {
|
|
965
1170
|
for (const id in this.permanentElementMap) {
|
|
966
|
-
const [, newPermanentElement] = this.permanentElementMap[id];
|
|
1171
|
+
const [currentPermanentElement, newPermanentElement] = this.permanentElementMap[id];
|
|
1172
|
+
this.delegate.enteringBardo(currentPermanentElement, newPermanentElement);
|
|
967
1173
|
this.replaceNewPermanentElementWithPlaceholder(newPermanentElement);
|
|
968
1174
|
}
|
|
969
1175
|
}
|
|
@@ -972,6 +1178,7 @@ class Bardo {
|
|
|
972
1178
|
const [currentPermanentElement] = this.permanentElementMap[id];
|
|
973
1179
|
this.replaceCurrentPermanentElementWithClone(currentPermanentElement);
|
|
974
1180
|
this.replacePlaceholderWithPermanentElement(currentPermanentElement);
|
|
1181
|
+
this.delegate.leavingBardo(currentPermanentElement);
|
|
975
1182
|
}
|
|
976
1183
|
}
|
|
977
1184
|
replaceNewPermanentElementWithPlaceholder(permanentElement) {
|
|
@@ -987,7 +1194,7 @@ class Bardo {
|
|
|
987
1194
|
placeholder === null || placeholder === void 0 ? void 0 : placeholder.replaceWith(permanentElement);
|
|
988
1195
|
}
|
|
989
1196
|
getPlaceholderById(id) {
|
|
990
|
-
return this.placeholders.find(element => element.content == id);
|
|
1197
|
+
return this.placeholders.find((element) => element.content == id);
|
|
991
1198
|
}
|
|
992
1199
|
get placeholders() {
|
|
993
1200
|
return [...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")];
|
|
@@ -1001,16 +1208,21 @@ function createPlaceholderForPermanentElement(permanentElement) {
|
|
|
1001
1208
|
}
|
|
1002
1209
|
|
|
1003
1210
|
class Renderer {
|
|
1004
|
-
constructor(currentSnapshot, newSnapshot, isPreview, willRender = true) {
|
|
1211
|
+
constructor(currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
|
1212
|
+
this.activeElement = null;
|
|
1005
1213
|
this.currentSnapshot = currentSnapshot;
|
|
1006
1214
|
this.newSnapshot = newSnapshot;
|
|
1007
1215
|
this.isPreview = isPreview;
|
|
1008
1216
|
this.willRender = willRender;
|
|
1009
|
-
this.
|
|
1217
|
+
this.renderElement = renderElement;
|
|
1218
|
+
this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
|
|
1010
1219
|
}
|
|
1011
1220
|
get shouldRender() {
|
|
1012
1221
|
return true;
|
|
1013
1222
|
}
|
|
1223
|
+
get reloadReason() {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1014
1226
|
prepareToRender() {
|
|
1015
1227
|
return;
|
|
1016
1228
|
}
|
|
@@ -1020,23 +1232,8 @@ class Renderer {
|
|
|
1020
1232
|
delete this.resolvingFunctions;
|
|
1021
1233
|
}
|
|
1022
1234
|
}
|
|
1023
|
-
createScriptElement(element) {
|
|
1024
|
-
if (element.getAttribute("data-turbo-eval") == "false") {
|
|
1025
|
-
return element;
|
|
1026
|
-
}
|
|
1027
|
-
else {
|
|
1028
|
-
const createdScriptElement = document.createElement("script");
|
|
1029
|
-
if (this.cspNonce) {
|
|
1030
|
-
createdScriptElement.nonce = this.cspNonce;
|
|
1031
|
-
}
|
|
1032
|
-
createdScriptElement.textContent = element.textContent;
|
|
1033
|
-
createdScriptElement.async = false;
|
|
1034
|
-
copyElementAttributes(createdScriptElement, element);
|
|
1035
|
-
return createdScriptElement;
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
1235
|
preservingPermanentElements(callback) {
|
|
1039
|
-
Bardo.preservingPermanentElements(this.permanentElementMap, callback);
|
|
1236
|
+
Bardo.preservingPermanentElements(this, this.permanentElementMap, callback);
|
|
1040
1237
|
}
|
|
1041
1238
|
focusFirstAutofocusableElement() {
|
|
1042
1239
|
const element = this.connectedSnapshot.firstAutofocusableElement;
|
|
@@ -1044,6 +1241,19 @@ class Renderer {
|
|
|
1044
1241
|
element.focus();
|
|
1045
1242
|
}
|
|
1046
1243
|
}
|
|
1244
|
+
enteringBardo(currentPermanentElement) {
|
|
1245
|
+
if (this.activeElement)
|
|
1246
|
+
return;
|
|
1247
|
+
if (currentPermanentElement.contains(this.currentSnapshot.activeElement)) {
|
|
1248
|
+
this.activeElement = this.currentSnapshot.activeElement;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
leavingBardo(currentPermanentElement) {
|
|
1252
|
+
if (currentPermanentElement.contains(this.activeElement) && this.activeElement instanceof HTMLElement) {
|
|
1253
|
+
this.activeElement.focus();
|
|
1254
|
+
this.activeElement = null;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1047
1257
|
get connectedSnapshot() {
|
|
1048
1258
|
return this.newSnapshot.isConnected ? this.newSnapshot : this.currentSnapshot;
|
|
1049
1259
|
}
|
|
@@ -1056,21 +1266,28 @@ class Renderer {
|
|
|
1056
1266
|
get permanentElementMap() {
|
|
1057
1267
|
return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot);
|
|
1058
1268
|
}
|
|
1059
|
-
get cspNonce() {
|
|
1060
|
-
var _a;
|
|
1061
|
-
return (_a = document.head.querySelector('meta[name="csp-nonce"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
function copyElementAttributes(destinationElement, sourceElement) {
|
|
1065
|
-
for (const { name, value } of [...sourceElement.attributes]) {
|
|
1066
|
-
destinationElement.setAttribute(name, value);
|
|
1067
|
-
}
|
|
1068
1269
|
}
|
|
1069
1270
|
function elementIsFocusable(element) {
|
|
1070
1271
|
return element && typeof element.focus == "function";
|
|
1071
1272
|
}
|
|
1072
1273
|
|
|
1073
1274
|
class FrameRenderer extends Renderer {
|
|
1275
|
+
constructor(delegate, currentSnapshot, newSnapshot, renderElement, isPreview, willRender = true) {
|
|
1276
|
+
super(currentSnapshot, newSnapshot, renderElement, isPreview, willRender);
|
|
1277
|
+
this.delegate = delegate;
|
|
1278
|
+
}
|
|
1279
|
+
static renderElement(currentElement, newElement) {
|
|
1280
|
+
var _a;
|
|
1281
|
+
const destinationRange = document.createRange();
|
|
1282
|
+
destinationRange.selectNodeContents(currentElement);
|
|
1283
|
+
destinationRange.deleteContents();
|
|
1284
|
+
const frameElement = newElement;
|
|
1285
|
+
const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
|
|
1286
|
+
if (sourceRange) {
|
|
1287
|
+
sourceRange.selectNodeContents(frameElement);
|
|
1288
|
+
currentElement.appendChild(sourceRange.extractContents());
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1074
1291
|
get shouldRender() {
|
|
1075
1292
|
return true;
|
|
1076
1293
|
}
|
|
@@ -1086,23 +1303,16 @@ class FrameRenderer extends Renderer {
|
|
|
1086
1303
|
this.activateScriptElements();
|
|
1087
1304
|
}
|
|
1088
1305
|
loadFrameElement() {
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
destinationRange.selectNodeContents(this.currentElement);
|
|
1092
|
-
destinationRange.deleteContents();
|
|
1093
|
-
const frameElement = this.newElement;
|
|
1094
|
-
const sourceRange = (_a = frameElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.createRange();
|
|
1095
|
-
if (sourceRange) {
|
|
1096
|
-
sourceRange.selectNodeContents(frameElement);
|
|
1097
|
-
this.currentElement.appendChild(sourceRange.extractContents());
|
|
1098
|
-
}
|
|
1306
|
+
this.delegate.willRenderFrame(this.currentElement, this.newElement);
|
|
1307
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
1099
1308
|
}
|
|
1100
1309
|
scrollFrameIntoView() {
|
|
1101
1310
|
if (this.currentElement.autoscroll || this.newElement.autoscroll) {
|
|
1102
1311
|
const element = this.currentElement.firstElementChild;
|
|
1103
1312
|
const block = readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"), "end");
|
|
1313
|
+
const behavior = readScrollBehavior(this.currentElement.getAttribute("data-autoscroll-behavior"), "auto");
|
|
1104
1314
|
if (element) {
|
|
1105
|
-
element.scrollIntoView({ block });
|
|
1315
|
+
element.scrollIntoView({ block, behavior });
|
|
1106
1316
|
return true;
|
|
1107
1317
|
}
|
|
1108
1318
|
}
|
|
@@ -1110,7 +1320,7 @@ class FrameRenderer extends Renderer {
|
|
|
1110
1320
|
}
|
|
1111
1321
|
activateScriptElements() {
|
|
1112
1322
|
for (const inertScriptElement of this.newScriptElements) {
|
|
1113
|
-
const activatedScriptElement =
|
|
1323
|
+
const activatedScriptElement = activateScriptElement(inertScriptElement);
|
|
1114
1324
|
inertScriptElement.replaceWith(activatedScriptElement);
|
|
1115
1325
|
}
|
|
1116
1326
|
}
|
|
@@ -1126,6 +1336,14 @@ function readScrollLogicalPosition(value, defaultValue) {
|
|
|
1126
1336
|
return defaultValue;
|
|
1127
1337
|
}
|
|
1128
1338
|
}
|
|
1339
|
+
function readScrollBehavior(value, defaultValue) {
|
|
1340
|
+
if (value == "auto" || value == "smooth") {
|
|
1341
|
+
return value;
|
|
1342
|
+
}
|
|
1343
|
+
else {
|
|
1344
|
+
return defaultValue;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1129
1347
|
|
|
1130
1348
|
class ProgressBar {
|
|
1131
1349
|
constructor() {
|
|
@@ -1149,7 +1367,7 @@ class ProgressBar {
|
|
|
1149
1367
|
left: 0;
|
|
1150
1368
|
height: 3px;
|
|
1151
1369
|
background: #0076ff;
|
|
1152
|
-
z-index:
|
|
1370
|
+
z-index: 2147483647;
|
|
1153
1371
|
transition:
|
|
1154
1372
|
width ${ProgressBar.animationDuration}ms ease-out,
|
|
1155
1373
|
opacity ${ProgressBar.animationDuration / 2}ms ${ProgressBar.animationDuration / 2}ms ease-in;
|
|
@@ -1208,13 +1426,16 @@ class ProgressBar {
|
|
|
1208
1426
|
}
|
|
1209
1427
|
refresh() {
|
|
1210
1428
|
requestAnimationFrame(() => {
|
|
1211
|
-
this.progressElement.style.width = `${10 +
|
|
1429
|
+
this.progressElement.style.width = `${10 + this.value * 90}%`;
|
|
1212
1430
|
});
|
|
1213
1431
|
}
|
|
1214
1432
|
createStylesheetElement() {
|
|
1215
1433
|
const element = document.createElement("style");
|
|
1216
1434
|
element.type = "text/css";
|
|
1217
1435
|
element.textContent = ProgressBar.defaultCSS;
|
|
1436
|
+
if (this.cspNonce) {
|
|
1437
|
+
element.nonce = this.cspNonce;
|
|
1438
|
+
}
|
|
1218
1439
|
return element;
|
|
1219
1440
|
}
|
|
1220
1441
|
createProgressElement() {
|
|
@@ -1222,6 +1443,9 @@ class ProgressBar {
|
|
|
1222
1443
|
element.className = "turbo-progress-bar";
|
|
1223
1444
|
return element;
|
|
1224
1445
|
}
|
|
1446
|
+
get cspNonce() {
|
|
1447
|
+
return getMetaContent("csp-nonce");
|
|
1448
|
+
}
|
|
1225
1449
|
}
|
|
1226
1450
|
ProgressBar.animationDuration = 300;
|
|
1227
1451
|
|
|
@@ -1238,14 +1462,14 @@ class HeadSnapshot extends Snapshot {
|
|
|
1238
1462
|
: {
|
|
1239
1463
|
type: elementType(element),
|
|
1240
1464
|
tracked: elementIsTracked(element),
|
|
1241
|
-
elements: []
|
|
1465
|
+
elements: [],
|
|
1242
1466
|
};
|
|
1243
1467
|
return Object.assign(Object.assign({}, result), { [outerHTML]: Object.assign(Object.assign({}, details), { elements: [...details.elements, element] }) });
|
|
1244
1468
|
}, {});
|
|
1245
1469
|
}
|
|
1246
1470
|
get trackedElementSignature() {
|
|
1247
1471
|
return Object.keys(this.detailsByOuterHTML)
|
|
1248
|
-
.filter(outerHTML => this.detailsByOuterHTML[outerHTML].tracked)
|
|
1472
|
+
.filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
|
|
1249
1473
|
.join("");
|
|
1250
1474
|
}
|
|
1251
1475
|
getScriptElementsNotInSnapshot(snapshot) {
|
|
@@ -1256,8 +1480,8 @@ class HeadSnapshot extends Snapshot {
|
|
|
1256
1480
|
}
|
|
1257
1481
|
getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
|
|
1258
1482
|
return Object.keys(this.detailsByOuterHTML)
|
|
1259
|
-
.filter(outerHTML => !(outerHTML in snapshot.detailsByOuterHTML))
|
|
1260
|
-
.map(outerHTML => this.detailsByOuterHTML[outerHTML])
|
|
1483
|
+
.filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
|
|
1484
|
+
.map((outerHTML) => this.detailsByOuterHTML[outerHTML])
|
|
1261
1485
|
.filter(({ type }) => type == matchedType)
|
|
1262
1486
|
.map(({ elements: [element] }) => element);
|
|
1263
1487
|
}
|
|
@@ -1277,13 +1501,11 @@ class HeadSnapshot extends Snapshot {
|
|
|
1277
1501
|
}
|
|
1278
1502
|
getMetaValue(name) {
|
|
1279
1503
|
const element = this.findMetaElementByName(name);
|
|
1280
|
-
return element
|
|
1281
|
-
? element.getAttribute("content")
|
|
1282
|
-
: null;
|
|
1504
|
+
return element ? element.getAttribute("content") : null;
|
|
1283
1505
|
}
|
|
1284
1506
|
findMetaElementByName(name) {
|
|
1285
1507
|
return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
|
|
1286
|
-
const { elements: [element] } = this.detailsByOuterHTML[outerHTML];
|
|
1508
|
+
const { elements: [element], } = this.detailsByOuterHTML[outerHTML];
|
|
1287
1509
|
return elementIsMetaElementWithName(element, name) ? element : result;
|
|
1288
1510
|
}, undefined);
|
|
1289
1511
|
}
|
|
@@ -1300,19 +1522,19 @@ function elementIsTracked(element) {
|
|
|
1300
1522
|
return element.getAttribute("data-turbo-track") == "reload";
|
|
1301
1523
|
}
|
|
1302
1524
|
function elementIsScript(element) {
|
|
1303
|
-
const tagName = element.
|
|
1525
|
+
const tagName = element.localName;
|
|
1304
1526
|
return tagName == "script";
|
|
1305
1527
|
}
|
|
1306
1528
|
function elementIsNoscript(element) {
|
|
1307
|
-
const tagName = element.
|
|
1529
|
+
const tagName = element.localName;
|
|
1308
1530
|
return tagName == "noscript";
|
|
1309
1531
|
}
|
|
1310
1532
|
function elementIsStylesheet(element) {
|
|
1311
|
-
const tagName = element.
|
|
1533
|
+
const tagName = element.localName;
|
|
1312
1534
|
return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
|
|
1313
1535
|
}
|
|
1314
1536
|
function elementIsMetaElementWithName(element, name) {
|
|
1315
|
-
const tagName = element.
|
|
1537
|
+
const tagName = element.localName;
|
|
1316
1538
|
return tagName == "meta" && element.getAttribute("name") == name;
|
|
1317
1539
|
}
|
|
1318
1540
|
function elementWithoutNonce(element) {
|
|
@@ -1337,7 +1559,20 @@ class PageSnapshot extends Snapshot {
|
|
|
1337
1559
|
return new this(body, new HeadSnapshot(head));
|
|
1338
1560
|
}
|
|
1339
1561
|
clone() {
|
|
1340
|
-
|
|
1562
|
+
const clonedElement = this.element.cloneNode(true);
|
|
1563
|
+
const selectElements = this.element.querySelectorAll("select");
|
|
1564
|
+
const clonedSelectElements = clonedElement.querySelectorAll("select");
|
|
1565
|
+
for (const [index, source] of selectElements.entries()) {
|
|
1566
|
+
const clone = clonedSelectElements[index];
|
|
1567
|
+
for (const option of clone.selectedOptions)
|
|
1568
|
+
option.selected = false;
|
|
1569
|
+
for (const option of source.selectedOptions)
|
|
1570
|
+
clone.options[option.index].selected = true;
|
|
1571
|
+
}
|
|
1572
|
+
for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
|
|
1573
|
+
clonedPasswordInput.value = "";
|
|
1574
|
+
}
|
|
1575
|
+
return new PageSnapshot(clonedElement, this.headSnapshot);
|
|
1341
1576
|
}
|
|
1342
1577
|
get headElement() {
|
|
1343
1578
|
return this.headSnapshot.element;
|
|
@@ -1384,6 +1619,10 @@ const defaultOptions = {
|
|
|
1384
1619
|
historyChanged: false,
|
|
1385
1620
|
visitCachedSnapshot: () => { },
|
|
1386
1621
|
willRender: true,
|
|
1622
|
+
updateHistory: true,
|
|
1623
|
+
shouldCacheSnapshot: true,
|
|
1624
|
+
acceptsStreamResponse: false,
|
|
1625
|
+
initiator: document.documentElement,
|
|
1387
1626
|
};
|
|
1388
1627
|
var SystemStatusCode;
|
|
1389
1628
|
(function (SystemStatusCode) {
|
|
@@ -1393,17 +1632,19 @@ var SystemStatusCode;
|
|
|
1393
1632
|
})(SystemStatusCode || (SystemStatusCode = {}));
|
|
1394
1633
|
class Visit {
|
|
1395
1634
|
constructor(delegate, location, restorationIdentifier, options = {}) {
|
|
1396
|
-
this.identifier = uuid();
|
|
1397
1635
|
this.timingMetrics = {};
|
|
1398
1636
|
this.followedRedirect = false;
|
|
1399
1637
|
this.historyChanged = false;
|
|
1400
1638
|
this.scrolled = false;
|
|
1639
|
+
this.shouldCacheSnapshot = true;
|
|
1640
|
+
this.acceptsStreamResponse = false;
|
|
1401
1641
|
this.snapshotCached = false;
|
|
1402
1642
|
this.state = VisitState.initialized;
|
|
1403
1643
|
this.delegate = delegate;
|
|
1404
1644
|
this.location = location;
|
|
1405
1645
|
this.restorationIdentifier = restorationIdentifier || uuid();
|
|
1406
|
-
|
|
1646
|
+
this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
|
|
1647
|
+
const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, initiator, } = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1407
1648
|
this.action = action;
|
|
1408
1649
|
this.historyChanged = historyChanged;
|
|
1409
1650
|
this.referrer = referrer;
|
|
@@ -1412,7 +1653,11 @@ class Visit {
|
|
|
1412
1653
|
this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
|
|
1413
1654
|
this.visitCachedSnapshot = visitCachedSnapshot;
|
|
1414
1655
|
this.willRender = willRender;
|
|
1656
|
+
this.updateHistory = updateHistory;
|
|
1415
1657
|
this.scrolled = !willRender;
|
|
1658
|
+
this.shouldCacheSnapshot = shouldCacheSnapshot;
|
|
1659
|
+
this.acceptsStreamResponse = acceptsStreamResponse;
|
|
1660
|
+
this.initiator = initiator;
|
|
1416
1661
|
}
|
|
1417
1662
|
get adapter() {
|
|
1418
1663
|
return this.delegate.adapter;
|
|
@@ -1444,28 +1689,33 @@ class Visit {
|
|
|
1444
1689
|
}
|
|
1445
1690
|
this.cancelRender();
|
|
1446
1691
|
this.state = VisitState.canceled;
|
|
1692
|
+
this.resolvingFunctions.reject();
|
|
1447
1693
|
}
|
|
1448
1694
|
}
|
|
1449
1695
|
complete() {
|
|
1450
1696
|
if (this.state == VisitState.started) {
|
|
1451
1697
|
this.recordTimingMetric(TimingMetric.visitEnd);
|
|
1452
1698
|
this.state = VisitState.completed;
|
|
1453
|
-
this.adapter.visitCompleted(this);
|
|
1454
|
-
this.delegate.visitCompleted(this);
|
|
1455
1699
|
this.followRedirect();
|
|
1700
|
+
if (!this.followedRedirect) {
|
|
1701
|
+
this.adapter.visitCompleted(this);
|
|
1702
|
+
this.delegate.visitCompleted(this);
|
|
1703
|
+
}
|
|
1704
|
+
this.resolvingFunctions.resolve();
|
|
1456
1705
|
}
|
|
1457
1706
|
}
|
|
1458
1707
|
fail() {
|
|
1459
1708
|
if (this.state == VisitState.started) {
|
|
1460
1709
|
this.state = VisitState.failed;
|
|
1461
1710
|
this.adapter.visitFailed(this);
|
|
1711
|
+
this.resolvingFunctions.reject();
|
|
1462
1712
|
}
|
|
1463
1713
|
}
|
|
1464
1714
|
changeHistory() {
|
|
1465
1715
|
var _a;
|
|
1466
|
-
if (!this.historyChanged) {
|
|
1716
|
+
if (!this.historyChanged && this.updateHistory) {
|
|
1467
1717
|
const actionForHistory = this.location.href === ((_a = this.referrer) === null || _a === void 0 ? void 0 : _a.href) ? "replace" : this.action;
|
|
1468
|
-
const method =
|
|
1718
|
+
const method = getHistoryMethodForAction(actionForHistory);
|
|
1469
1719
|
this.history.update(method, this.location, this.restorationIdentifier);
|
|
1470
1720
|
this.historyChanged = true;
|
|
1471
1721
|
}
|
|
@@ -1475,7 +1725,7 @@ class Visit {
|
|
|
1475
1725
|
this.simulateRequest();
|
|
1476
1726
|
}
|
|
1477
1727
|
else if (this.shouldIssueRequest() && !this.request) {
|
|
1478
|
-
this.request = new FetchRequest(this, FetchMethod.get, this.location);
|
|
1728
|
+
this.request = new FetchRequest(this, FetchMethod.get, this.location, undefined, this.initiator);
|
|
1479
1729
|
this.request.perform();
|
|
1480
1730
|
}
|
|
1481
1731
|
}
|
|
@@ -1510,16 +1760,18 @@ class Visit {
|
|
|
1510
1760
|
if (this.response) {
|
|
1511
1761
|
const { statusCode, responseHTML } = this.response;
|
|
1512
1762
|
this.render(async () => {
|
|
1513
|
-
this.
|
|
1763
|
+
if (this.shouldCacheSnapshot)
|
|
1764
|
+
this.cacheSnapshot();
|
|
1514
1765
|
if (this.view.renderPromise)
|
|
1515
1766
|
await this.view.renderPromise;
|
|
1516
1767
|
if (isSuccessful(statusCode) && responseHTML != null) {
|
|
1517
|
-
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender);
|
|
1768
|
+
await this.view.renderPage(PageSnapshot.fromHTMLString(responseHTML), false, this.willRender, this);
|
|
1769
|
+
this.performScroll();
|
|
1518
1770
|
this.adapter.visitRendered(this);
|
|
1519
1771
|
this.complete();
|
|
1520
1772
|
}
|
|
1521
1773
|
else {
|
|
1522
|
-
await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML));
|
|
1774
|
+
await this.view.renderError(PageSnapshot.fromHTMLString(responseHTML), this);
|
|
1523
1775
|
this.adapter.visitRendered(this);
|
|
1524
1776
|
this.fail();
|
|
1525
1777
|
}
|
|
@@ -1554,7 +1806,8 @@ class Visit {
|
|
|
1554
1806
|
else {
|
|
1555
1807
|
if (this.view.renderPromise)
|
|
1556
1808
|
await this.view.renderPromise;
|
|
1557
|
-
await this.view.renderPage(snapshot, isPreview, this.willRender);
|
|
1809
|
+
await this.view.renderPage(snapshot, isPreview, this.willRender, this);
|
|
1810
|
+
this.performScroll();
|
|
1558
1811
|
this.adapter.visitRendered(this);
|
|
1559
1812
|
if (!isPreview) {
|
|
1560
1813
|
this.complete();
|
|
@@ -1567,8 +1820,8 @@ class Visit {
|
|
|
1567
1820
|
var _a;
|
|
1568
1821
|
if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
|
|
1569
1822
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1570
|
-
action:
|
|
1571
|
-
response: this.response
|
|
1823
|
+
action: "replace",
|
|
1824
|
+
response: this.response,
|
|
1572
1825
|
});
|
|
1573
1826
|
this.followedRedirect = true;
|
|
1574
1827
|
}
|
|
@@ -1577,20 +1830,28 @@ class Visit {
|
|
|
1577
1830
|
if (this.isSamePage) {
|
|
1578
1831
|
this.render(async () => {
|
|
1579
1832
|
this.cacheSnapshot();
|
|
1833
|
+
this.performScroll();
|
|
1580
1834
|
this.adapter.visitRendered(this);
|
|
1581
1835
|
});
|
|
1582
1836
|
}
|
|
1583
1837
|
}
|
|
1838
|
+
prepareHeadersForRequest(headers, request) {
|
|
1839
|
+
if (this.acceptsStreamResponse) {
|
|
1840
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1584
1843
|
requestStarted() {
|
|
1585
1844
|
this.startRequest();
|
|
1586
1845
|
}
|
|
1587
|
-
requestPreventedHandlingResponse(
|
|
1588
|
-
}
|
|
1846
|
+
requestPreventedHandlingResponse(_request, _response) { }
|
|
1589
1847
|
async requestSucceededWithResponse(request, response) {
|
|
1590
1848
|
const responseHTML = await response.responseHTML;
|
|
1591
1849
|
const { redirected, statusCode } = response;
|
|
1592
1850
|
if (responseHTML == undefined) {
|
|
1593
|
-
this.recordResponse({
|
|
1851
|
+
this.recordResponse({
|
|
1852
|
+
statusCode: SystemStatusCode.contentTypeMismatch,
|
|
1853
|
+
redirected,
|
|
1854
|
+
});
|
|
1594
1855
|
}
|
|
1595
1856
|
else {
|
|
1596
1857
|
this.redirectedToLocation = response.redirected ? response.location : undefined;
|
|
@@ -1601,20 +1862,26 @@ class Visit {
|
|
|
1601
1862
|
const responseHTML = await response.responseHTML;
|
|
1602
1863
|
const { redirected, statusCode } = response;
|
|
1603
1864
|
if (responseHTML == undefined) {
|
|
1604
|
-
this.recordResponse({
|
|
1865
|
+
this.recordResponse({
|
|
1866
|
+
statusCode: SystemStatusCode.contentTypeMismatch,
|
|
1867
|
+
redirected,
|
|
1868
|
+
});
|
|
1605
1869
|
}
|
|
1606
1870
|
else {
|
|
1607
1871
|
this.recordResponse({ statusCode: statusCode, responseHTML, redirected });
|
|
1608
1872
|
}
|
|
1609
1873
|
}
|
|
1610
|
-
requestErrored(
|
|
1611
|
-
this.recordResponse({
|
|
1874
|
+
requestErrored(_request, _error) {
|
|
1875
|
+
this.recordResponse({
|
|
1876
|
+
statusCode: SystemStatusCode.networkFailure,
|
|
1877
|
+
redirected: false,
|
|
1878
|
+
});
|
|
1612
1879
|
}
|
|
1613
1880
|
requestFinished() {
|
|
1614
1881
|
this.finishRequest();
|
|
1615
1882
|
}
|
|
1616
1883
|
performScroll() {
|
|
1617
|
-
if (!this.scrolled) {
|
|
1884
|
+
if (!this.scrolled && !this.view.forceReloaded) {
|
|
1618
1885
|
if (this.action == "restore") {
|
|
1619
1886
|
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
|
|
1620
1887
|
}
|
|
@@ -1649,9 +1916,11 @@ class Visit {
|
|
|
1649
1916
|
}
|
|
1650
1917
|
getHistoryMethodForAction(action) {
|
|
1651
1918
|
switch (action) {
|
|
1652
|
-
case "replace":
|
|
1919
|
+
case "replace":
|
|
1920
|
+
return history.replaceState;
|
|
1653
1921
|
case "advance":
|
|
1654
|
-
case "restore":
|
|
1922
|
+
case "restore":
|
|
1923
|
+
return history.pushState;
|
|
1655
1924
|
}
|
|
1656
1925
|
}
|
|
1657
1926
|
hasPreloadedResponse() {
|
|
@@ -1670,18 +1939,17 @@ class Visit {
|
|
|
1670
1939
|
}
|
|
1671
1940
|
cacheSnapshot() {
|
|
1672
1941
|
if (!this.snapshotCached) {
|
|
1673
|
-
this.view.cacheSnapshot().then(snapshot => snapshot && this.visitCachedSnapshot(snapshot));
|
|
1942
|
+
this.view.cacheSnapshot().then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot));
|
|
1674
1943
|
this.snapshotCached = true;
|
|
1675
1944
|
}
|
|
1676
1945
|
}
|
|
1677
1946
|
async render(callback) {
|
|
1678
1947
|
this.cancelRender();
|
|
1679
|
-
await new Promise(resolve => {
|
|
1948
|
+
await new Promise((resolve) => {
|
|
1680
1949
|
this.frame = requestAnimationFrame(() => resolve());
|
|
1681
1950
|
});
|
|
1682
1951
|
await callback();
|
|
1683
1952
|
delete this.frame;
|
|
1684
|
-
this.performScroll();
|
|
1685
1953
|
}
|
|
1686
1954
|
cancelRender() {
|
|
1687
1955
|
if (this.frame) {
|
|
@@ -1696,19 +1964,19 @@ function isSuccessful(statusCode) {
|
|
|
1696
1964
|
|
|
1697
1965
|
class BrowserAdapter {
|
|
1698
1966
|
constructor(session) {
|
|
1699
|
-
this.progressBar = new ProgressBar;
|
|
1967
|
+
this.progressBar = new ProgressBar();
|
|
1700
1968
|
this.showProgressBar = () => {
|
|
1701
1969
|
this.progressBar.show();
|
|
1702
1970
|
};
|
|
1703
1971
|
this.session = session;
|
|
1704
1972
|
}
|
|
1705
1973
|
visitProposedToLocation(location, options) {
|
|
1706
|
-
this.navigator.startVisit(location, uuid(), options);
|
|
1974
|
+
return this.navigator.startVisit(location, (options === null || options === void 0 ? void 0 : options.restorationIdentifier) || uuid(), options);
|
|
1707
1975
|
}
|
|
1708
1976
|
visitStarted(visit) {
|
|
1977
|
+
this.location = visit.location;
|
|
1709
1978
|
visit.loadCachedSnapshot();
|
|
1710
1979
|
visit.issueRequest();
|
|
1711
|
-
visit.changeHistory();
|
|
1712
1980
|
visit.goToSamePageAnchor();
|
|
1713
1981
|
}
|
|
1714
1982
|
visitRequestStarted(visit) {
|
|
@@ -1728,29 +1996,31 @@ class BrowserAdapter {
|
|
|
1728
1996
|
case SystemStatusCode.networkFailure:
|
|
1729
1997
|
case SystemStatusCode.timeoutFailure:
|
|
1730
1998
|
case SystemStatusCode.contentTypeMismatch:
|
|
1731
|
-
return this.reload(
|
|
1999
|
+
return this.reload({
|
|
2000
|
+
reason: "request_failed",
|
|
2001
|
+
context: {
|
|
2002
|
+
statusCode,
|
|
2003
|
+
},
|
|
2004
|
+
});
|
|
1732
2005
|
default:
|
|
1733
2006
|
return visit.loadResponse();
|
|
1734
2007
|
}
|
|
1735
2008
|
}
|
|
1736
|
-
visitRequestFinished(
|
|
2009
|
+
visitRequestFinished(_visit) {
|
|
1737
2010
|
this.progressBar.setValue(1);
|
|
1738
2011
|
this.hideVisitProgressBar();
|
|
1739
2012
|
}
|
|
1740
|
-
visitCompleted(
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
this.reload();
|
|
1744
|
-
}
|
|
1745
|
-
visitFailed(visit) {
|
|
2013
|
+
visitCompleted(_visit) { }
|
|
2014
|
+
pageInvalidated(reason) {
|
|
2015
|
+
this.reload(reason);
|
|
1746
2016
|
}
|
|
1747
|
-
|
|
1748
|
-
}
|
|
1749
|
-
formSubmissionStarted(
|
|
2017
|
+
visitFailed(_visit) { }
|
|
2018
|
+
visitRendered(_visit) { }
|
|
2019
|
+
formSubmissionStarted(_formSubmission) {
|
|
1750
2020
|
this.progressBar.setValue(0);
|
|
1751
2021
|
this.showFormProgressBarAfterDelay();
|
|
1752
2022
|
}
|
|
1753
|
-
formSubmissionFinished(
|
|
2023
|
+
formSubmissionFinished(_formSubmission) {
|
|
1754
2024
|
this.progressBar.setValue(1);
|
|
1755
2025
|
this.hideFormProgressBar();
|
|
1756
2026
|
}
|
|
@@ -1776,8 +2046,11 @@ class BrowserAdapter {
|
|
|
1776
2046
|
delete this.formProgressBarTimeout;
|
|
1777
2047
|
}
|
|
1778
2048
|
}
|
|
1779
|
-
reload() {
|
|
1780
|
-
|
|
2049
|
+
reload(reason) {
|
|
2050
|
+
dispatch("turbo:reload", { detail: reason });
|
|
2051
|
+
if (!this.location)
|
|
2052
|
+
return;
|
|
2053
|
+
window.location.href = this.location.toString();
|
|
1781
2054
|
}
|
|
1782
2055
|
get navigator() {
|
|
1783
2056
|
return this.session.navigator;
|
|
@@ -1787,94 +2060,60 @@ class BrowserAdapter {
|
|
|
1787
2060
|
class CacheObserver {
|
|
1788
2061
|
constructor() {
|
|
1789
2062
|
this.started = false;
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
addEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
-
stop() {
|
|
1798
|
-
if (this.started) {
|
|
1799
|
-
this.started = false;
|
|
1800
|
-
removeEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
removeStaleElements() {
|
|
1804
|
-
const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
|
|
1805
|
-
for (const element of staleElements) {
|
|
1806
|
-
element.remove();
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
|
-
class FormSubmitObserver {
|
|
1812
|
-
constructor(delegate) {
|
|
1813
|
-
this.started = false;
|
|
1814
|
-
this.submitCaptured = () => {
|
|
1815
|
-
removeEventListener("submit", this.submitBubbled, false);
|
|
1816
|
-
addEventListener("submit", this.submitBubbled, false);
|
|
1817
|
-
};
|
|
1818
|
-
this.submitBubbled = ((event) => {
|
|
1819
|
-
if (!event.defaultPrevented) {
|
|
1820
|
-
const form = event.target instanceof HTMLFormElement ? event.target : undefined;
|
|
1821
|
-
const submitter = event.submitter || undefined;
|
|
1822
|
-
if (form) {
|
|
1823
|
-
const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.getAttribute("method");
|
|
1824
|
-
if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
|
|
1825
|
-
event.preventDefault();
|
|
1826
|
-
this.delegate.formSubmitted(form, submitter);
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
2063
|
+
this.removeStaleElements = ((_event) => {
|
|
2064
|
+
const staleElements = [...document.querySelectorAll('[data-turbo-cache="false"]')];
|
|
2065
|
+
for (const element of staleElements) {
|
|
2066
|
+
element.remove();
|
|
1829
2067
|
}
|
|
1830
2068
|
});
|
|
1831
|
-
this.delegate = delegate;
|
|
1832
2069
|
}
|
|
1833
2070
|
start() {
|
|
1834
2071
|
if (!this.started) {
|
|
1835
|
-
addEventListener("submit", this.submitCaptured, true);
|
|
1836
2072
|
this.started = true;
|
|
2073
|
+
addEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1837
2074
|
}
|
|
1838
2075
|
}
|
|
1839
2076
|
stop() {
|
|
1840
2077
|
if (this.started) {
|
|
1841
|
-
removeEventListener("submit", this.submitCaptured, true);
|
|
1842
2078
|
this.started = false;
|
|
2079
|
+
removeEventListener("turbo:before-cache", this.removeStaleElements, false);
|
|
1843
2080
|
}
|
|
1844
2081
|
}
|
|
1845
2082
|
}
|
|
1846
2083
|
|
|
1847
2084
|
class FrameRedirector {
|
|
1848
|
-
constructor(element) {
|
|
2085
|
+
constructor(session, element) {
|
|
2086
|
+
this.session = session;
|
|
1849
2087
|
this.element = element;
|
|
1850
|
-
this.
|
|
1851
|
-
this.
|
|
2088
|
+
this.linkClickObserver = new LinkClickObserver(this, element);
|
|
2089
|
+
this.formSubmitObserver = new FormSubmitObserver(this, element);
|
|
1852
2090
|
}
|
|
1853
2091
|
start() {
|
|
1854
|
-
this.
|
|
1855
|
-
this.
|
|
2092
|
+
this.linkClickObserver.start();
|
|
2093
|
+
this.formSubmitObserver.start();
|
|
1856
2094
|
}
|
|
1857
2095
|
stop() {
|
|
1858
|
-
this.
|
|
1859
|
-
this.
|
|
2096
|
+
this.linkClickObserver.stop();
|
|
2097
|
+
this.formSubmitObserver.stop();
|
|
1860
2098
|
}
|
|
1861
|
-
|
|
2099
|
+
willFollowLinkToLocation(element) {
|
|
1862
2100
|
return this.shouldRedirect(element);
|
|
1863
2101
|
}
|
|
1864
|
-
|
|
2102
|
+
followedLinkToLocation(element, url) {
|
|
1865
2103
|
const frame = this.findFrameElement(element);
|
|
1866
2104
|
if (frame) {
|
|
1867
|
-
frame.delegate.
|
|
2105
|
+
frame.delegate.followedLinkToLocation(element, url);
|
|
1868
2106
|
}
|
|
1869
2107
|
}
|
|
1870
|
-
|
|
1871
|
-
return
|
|
2108
|
+
willSubmitForm(element, submitter) {
|
|
2109
|
+
return (element.closest("turbo-frame") == null &&
|
|
2110
|
+
this.shouldSubmit(element, submitter) &&
|
|
2111
|
+
this.shouldRedirect(element, submitter));
|
|
1872
2112
|
}
|
|
1873
|
-
|
|
2113
|
+
formSubmitted(element, submitter) {
|
|
1874
2114
|
const frame = this.findFrameElement(element, submitter);
|
|
1875
2115
|
if (frame) {
|
|
1876
|
-
frame.
|
|
1877
|
-
frame.delegate.formSubmissionIntercepted(element, submitter);
|
|
2116
|
+
frame.delegate.formSubmitted(element, submitter);
|
|
1878
2117
|
}
|
|
1879
2118
|
}
|
|
1880
2119
|
shouldSubmit(form, submitter) {
|
|
@@ -1885,8 +2124,16 @@ class FrameRedirector {
|
|
|
1885
2124
|
return this.shouldRedirect(form, submitter) && locationIsVisitable(action, rootLocation);
|
|
1886
2125
|
}
|
|
1887
2126
|
shouldRedirect(element, submitter) {
|
|
1888
|
-
const
|
|
1889
|
-
|
|
2127
|
+
const isNavigatable = element instanceof HTMLFormElement
|
|
2128
|
+
? this.session.submissionIsNavigatable(element, submitter)
|
|
2129
|
+
: this.session.elementIsNavigatable(element);
|
|
2130
|
+
if (isNavigatable) {
|
|
2131
|
+
const frame = this.findFrameElement(element, submitter);
|
|
2132
|
+
return frame ? frame != element.closest("turbo-frame") : false;
|
|
2133
|
+
}
|
|
2134
|
+
else {
|
|
2135
|
+
return false;
|
|
2136
|
+
}
|
|
1890
2137
|
}
|
|
1891
2138
|
findFrameElement(element, submitter) {
|
|
1892
2139
|
const id = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("data-turbo-frame")) || element.getAttribute("data-turbo-frame");
|
|
@@ -1916,7 +2163,7 @@ class History {
|
|
|
1916
2163
|
}
|
|
1917
2164
|
}
|
|
1918
2165
|
};
|
|
1919
|
-
this.onPageLoad = async (
|
|
2166
|
+
this.onPageLoad = async (_event) => {
|
|
1920
2167
|
await nextMicrotask();
|
|
1921
2168
|
this.pageLoaded = true;
|
|
1922
2169
|
};
|
|
@@ -1978,81 +2225,30 @@ class History {
|
|
|
1978
2225
|
}
|
|
1979
2226
|
}
|
|
1980
2227
|
|
|
1981
|
-
class
|
|
1982
|
-
constructor(delegate) {
|
|
1983
|
-
this.started = false;
|
|
1984
|
-
this.clickCaptured = () => {
|
|
1985
|
-
removeEventListener("click", this.clickBubbled, false);
|
|
1986
|
-
addEventListener("click", this.clickBubbled, false);
|
|
1987
|
-
};
|
|
1988
|
-
this.clickBubbled = (event) => {
|
|
1989
|
-
if (this.clickEventIsSignificant(event)) {
|
|
1990
|
-
const target = (event.composedPath && event.composedPath()[0]) || event.target;
|
|
1991
|
-
const link = this.findLinkFromClickTarget(target);
|
|
1992
|
-
if (link) {
|
|
1993
|
-
const location = this.getLocationForLink(link);
|
|
1994
|
-
if (this.delegate.willFollowLinkToLocation(link, location)) {
|
|
1995
|
-
event.preventDefault();
|
|
1996
|
-
this.delegate.followedLinkToLocation(link, location);
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
};
|
|
2001
|
-
this.delegate = delegate;
|
|
2002
|
-
}
|
|
2003
|
-
start() {
|
|
2004
|
-
if (!this.started) {
|
|
2005
|
-
addEventListener("click", this.clickCaptured, true);
|
|
2006
|
-
this.started = true;
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
stop() {
|
|
2010
|
-
if (this.started) {
|
|
2011
|
-
removeEventListener("click", this.clickCaptured, true);
|
|
2012
|
-
this.started = false;
|
|
2013
|
-
}
|
|
2014
|
-
}
|
|
2015
|
-
clickEventIsSignificant(event) {
|
|
2016
|
-
return !((event.target && event.target.isContentEditable)
|
|
2017
|
-
|| event.defaultPrevented
|
|
2018
|
-
|| event.which > 1
|
|
2019
|
-
|| event.altKey
|
|
2020
|
-
|| event.ctrlKey
|
|
2021
|
-
|| event.metaKey
|
|
2022
|
-
|| event.shiftKey);
|
|
2023
|
-
}
|
|
2024
|
-
findLinkFromClickTarget(target) {
|
|
2025
|
-
if (target instanceof Element) {
|
|
2026
|
-
return target.closest("a[href]:not([target^=_]):not([download])");
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
getLocationForLink(link) {
|
|
2030
|
-
return expandURL(link.getAttribute("href") || "");
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
|
|
2034
|
-
function isAction(action) {
|
|
2035
|
-
return action == "advance" || action == "replace" || action == "restore";
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
class Navigator {
|
|
2228
|
+
class Navigator {
|
|
2039
2229
|
constructor(delegate) {
|
|
2040
2230
|
this.delegate = delegate;
|
|
2041
2231
|
}
|
|
2042
2232
|
proposeVisit(location, options = {}) {
|
|
2043
|
-
if (this.delegate.
|
|
2233
|
+
if (this.delegate.allowsVisitingLocation(location, options)) {
|
|
2044
2234
|
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
|
|
2045
|
-
this.delegate.visitProposedToLocation(location, options);
|
|
2235
|
+
return this.delegate.visitProposedToLocation(location, options);
|
|
2046
2236
|
}
|
|
2047
2237
|
else {
|
|
2048
2238
|
window.location.href = location.toString();
|
|
2239
|
+
return Promise.resolve();
|
|
2049
2240
|
}
|
|
2050
2241
|
}
|
|
2242
|
+
else {
|
|
2243
|
+
return Promise.reject();
|
|
2244
|
+
}
|
|
2051
2245
|
}
|
|
2052
2246
|
startVisit(locatable, restorationIdentifier, options = {}) {
|
|
2247
|
+
this.lastVisit = this.currentVisit;
|
|
2053
2248
|
this.stop();
|
|
2054
2249
|
this.currentVisit = new Visit(this, expandURL(locatable), restorationIdentifier, Object.assign({ referrer: this.location }, options));
|
|
2055
2250
|
this.currentVisit.start();
|
|
2251
|
+
return this.currentVisit.promise;
|
|
2056
2252
|
}
|
|
2057
2253
|
submitForm(form, submitter) {
|
|
2058
2254
|
this.stop();
|
|
@@ -2079,7 +2275,7 @@ class Navigator {
|
|
|
2079
2275
|
return this.delegate.history;
|
|
2080
2276
|
}
|
|
2081
2277
|
formSubmissionStarted(formSubmission) {
|
|
2082
|
-
if (typeof this.adapter.formSubmissionStarted ===
|
|
2278
|
+
if (typeof this.adapter.formSubmissionStarted === "function") {
|
|
2083
2279
|
this.adapter.formSubmissionStarted(formSubmission);
|
|
2084
2280
|
}
|
|
2085
2281
|
}
|
|
@@ -2087,12 +2283,17 @@ class Navigator {
|
|
|
2087
2283
|
if (formSubmission == this.formSubmission) {
|
|
2088
2284
|
const responseHTML = await fetchResponse.responseHTML;
|
|
2089
2285
|
if (responseHTML) {
|
|
2090
|
-
|
|
2286
|
+
const shouldCacheSnapshot = formSubmission.method == FetchMethod.get;
|
|
2287
|
+
if (!shouldCacheSnapshot) {
|
|
2091
2288
|
this.view.clearSnapshotCache();
|
|
2092
2289
|
}
|
|
2093
2290
|
const { statusCode, redirected } = fetchResponse;
|
|
2094
2291
|
const action = this.getActionForFormSubmission(formSubmission);
|
|
2095
|
-
const visitOptions = {
|
|
2292
|
+
const visitOptions = {
|
|
2293
|
+
action,
|
|
2294
|
+
shouldCacheSnapshot,
|
|
2295
|
+
response: { statusCode, responseHTML, redirected },
|
|
2296
|
+
};
|
|
2096
2297
|
this.proposeVisit(fetchResponse.location, visitOptions);
|
|
2097
2298
|
}
|
|
2098
2299
|
}
|
|
@@ -2102,10 +2303,10 @@ class Navigator {
|
|
|
2102
2303
|
if (responseHTML) {
|
|
2103
2304
|
const snapshot = PageSnapshot.fromHTMLString(responseHTML);
|
|
2104
2305
|
if (fetchResponse.serverError) {
|
|
2105
|
-
await this.view.renderError(snapshot);
|
|
2306
|
+
await this.view.renderError(snapshot, this.currentVisit);
|
|
2106
2307
|
}
|
|
2107
2308
|
else {
|
|
2108
|
-
await this.view.renderPage(snapshot);
|
|
2309
|
+
await this.view.renderPage(snapshot, false, true, this.currentVisit);
|
|
2109
2310
|
}
|
|
2110
2311
|
this.view.scrollToTop();
|
|
2111
2312
|
this.view.clearSnapshotCache();
|
|
@@ -2115,7 +2316,7 @@ class Navigator {
|
|
|
2115
2316
|
console.error(error);
|
|
2116
2317
|
}
|
|
2117
2318
|
formSubmissionFinished(formSubmission) {
|
|
2118
|
-
if (typeof this.adapter.formSubmissionFinished ===
|
|
2319
|
+
if (typeof this.adapter.formSubmissionFinished === "function") {
|
|
2119
2320
|
this.adapter.formSubmissionFinished(formSubmission);
|
|
2120
2321
|
}
|
|
2121
2322
|
}
|
|
@@ -2126,12 +2327,14 @@ class Navigator {
|
|
|
2126
2327
|
this.delegate.visitCompleted(visit);
|
|
2127
2328
|
}
|
|
2128
2329
|
locationWithActionIsSamePage(location, action) {
|
|
2330
|
+
var _a;
|
|
2129
2331
|
const anchor = getAnchor(location);
|
|
2130
|
-
const
|
|
2131
|
-
const
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
(
|
|
2332
|
+
const lastLocation = ((_a = this.lastVisit) === null || _a === void 0 ? void 0 : _a.location) || this.view.lastRenderedLocation;
|
|
2333
|
+
const currentAnchor = getAnchor(lastLocation);
|
|
2334
|
+
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
|
|
2335
|
+
return (action !== "replace" &&
|
|
2336
|
+
getRequestURL(location) === getRequestURL(lastLocation) &&
|
|
2337
|
+
(isRestorationToTop || (anchor != null && anchor !== currentAnchor)));
|
|
2135
2338
|
}
|
|
2136
2339
|
visitScrolledToSamePageLocation(oldURL, newURL) {
|
|
2137
2340
|
this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
|
|
@@ -2235,9 +2438,33 @@ class ScrollObserver {
|
|
|
2235
2438
|
}
|
|
2236
2439
|
}
|
|
2237
2440
|
|
|
2441
|
+
class StreamMessageRenderer {
|
|
2442
|
+
render({ fragment }) {
|
|
2443
|
+
Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
|
|
2444
|
+
}
|
|
2445
|
+
enteringBardo(currentPermanentElement, newPermanentElement) {
|
|
2446
|
+
newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
|
|
2447
|
+
}
|
|
2448
|
+
leavingBardo() { }
|
|
2449
|
+
}
|
|
2450
|
+
function getPermanentElementMapForFragment(fragment) {
|
|
2451
|
+
const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
|
|
2452
|
+
const permanentElementMap = {};
|
|
2453
|
+
for (const permanentElementInDocument of permanentElementsInDocument) {
|
|
2454
|
+
const { id } = permanentElementInDocument;
|
|
2455
|
+
for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
|
|
2456
|
+
const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
|
|
2457
|
+
if (elementInStream) {
|
|
2458
|
+
permanentElementMap[id] = [permanentElementInDocument, elementInStream];
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
return permanentElementMap;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2238
2465
|
class StreamObserver {
|
|
2239
2466
|
constructor(delegate) {
|
|
2240
|
-
this.sources = new Set;
|
|
2467
|
+
this.sources = new Set();
|
|
2241
2468
|
this.started = false;
|
|
2242
2469
|
this.inspectFetchResponse = ((event) => {
|
|
2243
2470
|
const response = fetchResponseFromEvent(event);
|
|
@@ -2287,7 +2514,7 @@ class StreamObserver {
|
|
|
2287
2514
|
}
|
|
2288
2515
|
}
|
|
2289
2516
|
receiveMessageHTML(html) {
|
|
2290
|
-
this.delegate.receivedMessageFromStream(
|
|
2517
|
+
this.delegate.receivedMessageFromStream(StreamMessage.wrap(html));
|
|
2291
2518
|
}
|
|
2292
2519
|
}
|
|
2293
2520
|
function fetchResponseFromEvent(event) {
|
|
@@ -2304,20 +2531,24 @@ function fetchResponseIsStream(response) {
|
|
|
2304
2531
|
}
|
|
2305
2532
|
|
|
2306
2533
|
class ErrorRenderer extends Renderer {
|
|
2534
|
+
static renderElement(currentElement, newElement) {
|
|
2535
|
+
const { documentElement, body } = document;
|
|
2536
|
+
documentElement.replaceChild(newElement, body);
|
|
2537
|
+
}
|
|
2307
2538
|
async render() {
|
|
2308
2539
|
this.replaceHeadAndBody();
|
|
2309
2540
|
this.activateScriptElements();
|
|
2310
2541
|
}
|
|
2311
2542
|
replaceHeadAndBody() {
|
|
2312
|
-
const { documentElement, head
|
|
2543
|
+
const { documentElement, head } = document;
|
|
2313
2544
|
documentElement.replaceChild(this.newHead, head);
|
|
2314
|
-
|
|
2545
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
2315
2546
|
}
|
|
2316
2547
|
activateScriptElements() {
|
|
2317
2548
|
for (const replaceableElement of this.scriptElements) {
|
|
2318
2549
|
const parentNode = replaceableElement.parentNode;
|
|
2319
2550
|
if (parentNode) {
|
|
2320
|
-
const element =
|
|
2551
|
+
const element = activateScriptElement(replaceableElement);
|
|
2321
2552
|
parentNode.replaceChild(element, replaceableElement);
|
|
2322
2553
|
}
|
|
2323
2554
|
}
|
|
@@ -2326,16 +2557,44 @@ class ErrorRenderer extends Renderer {
|
|
|
2326
2557
|
return this.newSnapshot.headSnapshot.element;
|
|
2327
2558
|
}
|
|
2328
2559
|
get scriptElements() {
|
|
2329
|
-
return
|
|
2560
|
+
return document.documentElement.querySelectorAll("script");
|
|
2330
2561
|
}
|
|
2331
2562
|
}
|
|
2332
2563
|
|
|
2333
2564
|
class PageRenderer extends Renderer {
|
|
2565
|
+
static async renderElement(currentElement, newElement) {
|
|
2566
|
+
await nextEventLoopTick();
|
|
2567
|
+
if (document.body && newElement instanceof HTMLBodyElement) {
|
|
2568
|
+
const currentBody = PageRenderer.getBodyElement(currentElement);
|
|
2569
|
+
const newBody = PageRenderer.getBodyElement(newElement);
|
|
2570
|
+
currentBody.replaceWith(newBody);
|
|
2571
|
+
}
|
|
2572
|
+
else {
|
|
2573
|
+
document.documentElement.appendChild(newElement);
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2334
2576
|
get shouldRender() {
|
|
2335
|
-
return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical;
|
|
2577
|
+
return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical && this.bodyElementMatches;
|
|
2336
2578
|
}
|
|
2337
|
-
|
|
2338
|
-
this.
|
|
2579
|
+
get reloadReason() {
|
|
2580
|
+
if (!this.newSnapshot.isVisitable) {
|
|
2581
|
+
return {
|
|
2582
|
+
reason: "turbo_visit_control_is_reload",
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
if (!this.trackedElementsAreIdentical) {
|
|
2586
|
+
return {
|
|
2587
|
+
reason: "tracked_element_mismatch",
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2590
|
+
if (!this.bodyElementMatches) {
|
|
2591
|
+
return {
|
|
2592
|
+
reason: "body_element_mismatch",
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
async prepareToRender() {
|
|
2597
|
+
await this.mergeHead();
|
|
2339
2598
|
}
|
|
2340
2599
|
async render() {
|
|
2341
2600
|
if (this.willRender) {
|
|
@@ -2357,11 +2616,12 @@ class PageRenderer extends Renderer {
|
|
|
2357
2616
|
get newElement() {
|
|
2358
2617
|
return this.newSnapshot.element;
|
|
2359
2618
|
}
|
|
2360
|
-
mergeHead() {
|
|
2361
|
-
this.copyNewHeadStylesheetElements();
|
|
2619
|
+
async mergeHead() {
|
|
2620
|
+
const newStylesheetElements = this.copyNewHeadStylesheetElements();
|
|
2362
2621
|
this.copyNewHeadScriptElements();
|
|
2363
2622
|
this.removeCurrentHeadProvisionalElements();
|
|
2364
2623
|
this.copyNewHeadProvisionalElements();
|
|
2624
|
+
await newStylesheetElements;
|
|
2365
2625
|
}
|
|
2366
2626
|
replaceBody() {
|
|
2367
2627
|
this.preservingPermanentElements(() => {
|
|
@@ -2372,14 +2632,27 @@ class PageRenderer extends Renderer {
|
|
|
2372
2632
|
get trackedElementsAreIdentical() {
|
|
2373
2633
|
return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature;
|
|
2374
2634
|
}
|
|
2375
|
-
|
|
2635
|
+
get bodyElementMatches() {
|
|
2636
|
+
return PageRenderer.getBodyElement(this.newElement) !== null;
|
|
2637
|
+
}
|
|
2638
|
+
static get bodySelector() {
|
|
2639
|
+
const bodyId = getBodyElementId();
|
|
2640
|
+
return bodyId ? `#${bodyId}` : "body";
|
|
2641
|
+
}
|
|
2642
|
+
static getBodyElement(element) {
|
|
2643
|
+
return element.querySelector(this.bodySelector) || element;
|
|
2644
|
+
}
|
|
2645
|
+
async copyNewHeadStylesheetElements() {
|
|
2646
|
+
const loadingElements = [];
|
|
2376
2647
|
for (const element of this.newHeadStylesheetElements) {
|
|
2648
|
+
loadingElements.push(waitForLoad(element));
|
|
2377
2649
|
document.head.appendChild(element);
|
|
2378
2650
|
}
|
|
2651
|
+
await Promise.all(loadingElements);
|
|
2379
2652
|
}
|
|
2380
2653
|
copyNewHeadScriptElements() {
|
|
2381
2654
|
for (const element of this.newHeadScriptElements) {
|
|
2382
|
-
document.head.appendChild(
|
|
2655
|
+
document.head.appendChild(activateScriptElement(element));
|
|
2383
2656
|
}
|
|
2384
2657
|
}
|
|
2385
2658
|
removeCurrentHeadProvisionalElements() {
|
|
@@ -2398,17 +2671,12 @@ class PageRenderer extends Renderer {
|
|
|
2398
2671
|
}
|
|
2399
2672
|
activateNewBodyScriptElements() {
|
|
2400
2673
|
for (const inertScriptElement of this.newBodyScriptElements) {
|
|
2401
|
-
const activatedScriptElement =
|
|
2674
|
+
const activatedScriptElement = activateScriptElement(inertScriptElement);
|
|
2402
2675
|
inertScriptElement.replaceWith(activatedScriptElement);
|
|
2403
2676
|
}
|
|
2404
2677
|
}
|
|
2405
2678
|
assignNewBody() {
|
|
2406
|
-
|
|
2407
|
-
document.body.replaceWith(this.newElement);
|
|
2408
|
-
}
|
|
2409
|
-
else {
|
|
2410
|
-
document.documentElement.appendChild(this.newElement);
|
|
2411
|
-
}
|
|
2679
|
+
this.renderElement(this.currentElement, this.newElement);
|
|
2412
2680
|
}
|
|
2413
2681
|
get newHeadStylesheetElements() {
|
|
2414
2682
|
return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot);
|
|
@@ -2477,13 +2745,21 @@ class PageView extends View {
|
|
|
2477
2745
|
super(...arguments);
|
|
2478
2746
|
this.snapshotCache = new SnapshotCache(10);
|
|
2479
2747
|
this.lastRenderedLocation = new URL(location.href);
|
|
2748
|
+
this.forceReloaded = false;
|
|
2480
2749
|
}
|
|
2481
|
-
renderPage(snapshot, isPreview = false, willRender = true) {
|
|
2482
|
-
const renderer = new PageRenderer(this.snapshot, snapshot, isPreview, willRender);
|
|
2750
|
+
renderPage(snapshot, isPreview = false, willRender = true, visit) {
|
|
2751
|
+
const renderer = new PageRenderer(this.snapshot, snapshot, PageRenderer.renderElement, isPreview, willRender);
|
|
2752
|
+
if (!renderer.shouldRender) {
|
|
2753
|
+
this.forceReloaded = true;
|
|
2754
|
+
}
|
|
2755
|
+
else {
|
|
2756
|
+
visit === null || visit === void 0 ? void 0 : visit.changeHistory();
|
|
2757
|
+
}
|
|
2483
2758
|
return this.render(renderer);
|
|
2484
2759
|
}
|
|
2485
|
-
renderError(snapshot) {
|
|
2486
|
-
|
|
2760
|
+
renderError(snapshot, visit) {
|
|
2761
|
+
visit === null || visit === void 0 ? void 0 : visit.changeHistory();
|
|
2762
|
+
const renderer = new ErrorRenderer(this.snapshot, snapshot, ErrorRenderer.renderElement, false);
|
|
2487
2763
|
return this.render(renderer);
|
|
2488
2764
|
}
|
|
2489
2765
|
clearSnapshotCache() {
|
|
@@ -2510,34 +2786,79 @@ class PageView extends View {
|
|
|
2510
2786
|
}
|
|
2511
2787
|
}
|
|
2512
2788
|
|
|
2789
|
+
class Preloader {
|
|
2790
|
+
constructor(delegate) {
|
|
2791
|
+
this.selector = "a[data-turbo-preload]";
|
|
2792
|
+
this.delegate = delegate;
|
|
2793
|
+
}
|
|
2794
|
+
get snapshotCache() {
|
|
2795
|
+
return this.delegate.navigator.view.snapshotCache;
|
|
2796
|
+
}
|
|
2797
|
+
start() {
|
|
2798
|
+
if (document.readyState === "loading") {
|
|
2799
|
+
return document.addEventListener("DOMContentLoaded", () => {
|
|
2800
|
+
this.preloadOnLoadLinksForView(document.body);
|
|
2801
|
+
});
|
|
2802
|
+
}
|
|
2803
|
+
else {
|
|
2804
|
+
this.preloadOnLoadLinksForView(document.body);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
preloadOnLoadLinksForView(element) {
|
|
2808
|
+
for (const link of element.querySelectorAll(this.selector)) {
|
|
2809
|
+
this.preloadURL(link);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
async preloadURL(link) {
|
|
2813
|
+
const location = new URL(link.href);
|
|
2814
|
+
if (this.snapshotCache.has(location)) {
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
try {
|
|
2818
|
+
const response = await fetch(location.toString(), { headers: { "VND.PREFETCH": "true", Accept: "text/html" } });
|
|
2819
|
+
const responseText = await response.text();
|
|
2820
|
+
const snapshot = PageSnapshot.fromHTMLString(responseText);
|
|
2821
|
+
this.snapshotCache.put(location, snapshot);
|
|
2822
|
+
}
|
|
2823
|
+
catch (_) {
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2513
2828
|
class Session {
|
|
2514
2829
|
constructor() {
|
|
2515
2830
|
this.navigator = new Navigator(this);
|
|
2516
2831
|
this.history = new History(this);
|
|
2832
|
+
this.preloader = new Preloader(this);
|
|
2517
2833
|
this.view = new PageView(this, document.documentElement);
|
|
2518
2834
|
this.adapter = new BrowserAdapter(this);
|
|
2519
2835
|
this.pageObserver = new PageObserver(this);
|
|
2520
2836
|
this.cacheObserver = new CacheObserver();
|
|
2521
|
-
this.linkClickObserver = new LinkClickObserver(this);
|
|
2522
|
-
this.formSubmitObserver = new FormSubmitObserver(this);
|
|
2837
|
+
this.linkClickObserver = new LinkClickObserver(this, window);
|
|
2838
|
+
this.formSubmitObserver = new FormSubmitObserver(this, document);
|
|
2523
2839
|
this.scrollObserver = new ScrollObserver(this);
|
|
2524
2840
|
this.streamObserver = new StreamObserver(this);
|
|
2525
|
-
this.
|
|
2841
|
+
this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
|
|
2842
|
+
this.frameRedirector = new FrameRedirector(this, document.documentElement);
|
|
2843
|
+
this.streamMessageRenderer = new StreamMessageRenderer();
|
|
2526
2844
|
this.drive = true;
|
|
2527
2845
|
this.enabled = true;
|
|
2528
2846
|
this.progressBarDelay = 500;
|
|
2529
2847
|
this.started = false;
|
|
2848
|
+
this.formMode = "on";
|
|
2530
2849
|
}
|
|
2531
2850
|
start() {
|
|
2532
2851
|
if (!this.started) {
|
|
2533
2852
|
this.pageObserver.start();
|
|
2534
2853
|
this.cacheObserver.start();
|
|
2854
|
+
this.formLinkClickObserver.start();
|
|
2535
2855
|
this.linkClickObserver.start();
|
|
2536
2856
|
this.formSubmitObserver.start();
|
|
2537
2857
|
this.scrollObserver.start();
|
|
2538
2858
|
this.streamObserver.start();
|
|
2539
2859
|
this.frameRedirector.start();
|
|
2540
2860
|
this.history.start();
|
|
2861
|
+
this.preloader.start();
|
|
2541
2862
|
this.started = true;
|
|
2542
2863
|
this.enabled = true;
|
|
2543
2864
|
}
|
|
@@ -2549,6 +2870,7 @@ class Session {
|
|
|
2549
2870
|
if (this.started) {
|
|
2550
2871
|
this.pageObserver.stop();
|
|
2551
2872
|
this.cacheObserver.stop();
|
|
2873
|
+
this.formLinkClickObserver.stop();
|
|
2552
2874
|
this.linkClickObserver.stop();
|
|
2553
2875
|
this.formSubmitObserver.stop();
|
|
2554
2876
|
this.scrollObserver.stop();
|
|
@@ -2562,7 +2884,14 @@ class Session {
|
|
|
2562
2884
|
this.adapter = adapter;
|
|
2563
2885
|
}
|
|
2564
2886
|
visit(location, options = {}) {
|
|
2565
|
-
|
|
2887
|
+
const frameElement = options.frame ? document.getElementById(options.frame) : null;
|
|
2888
|
+
if (frameElement instanceof FrameElement) {
|
|
2889
|
+
frameElement.src = location.toString();
|
|
2890
|
+
return frameElement.loaded;
|
|
2891
|
+
}
|
|
2892
|
+
else {
|
|
2893
|
+
return this.navigator.proposeVisit(expandURL(location), options);
|
|
2894
|
+
}
|
|
2566
2895
|
}
|
|
2567
2896
|
connectStreamSource(source) {
|
|
2568
2897
|
this.streamObserver.connectStreamSource(source);
|
|
@@ -2571,7 +2900,7 @@ class Session {
|
|
|
2571
2900
|
this.streamObserver.disconnectStreamSource(source);
|
|
2572
2901
|
}
|
|
2573
2902
|
renderStreamMessage(message) {
|
|
2574
|
-
|
|
2903
|
+
this.streamMessageRenderer.render(StreamMessage.wrap(message));
|
|
2575
2904
|
}
|
|
2576
2905
|
clearCache() {
|
|
2577
2906
|
this.view.clearSnapshotCache();
|
|
@@ -2579,6 +2908,9 @@ class Session {
|
|
|
2579
2908
|
setProgressBarDelay(delay) {
|
|
2580
2909
|
this.progressBarDelay = delay;
|
|
2581
2910
|
}
|
|
2911
|
+
setFormMode(mode) {
|
|
2912
|
+
this.formMode = mode;
|
|
2913
|
+
}
|
|
2582
2914
|
get location() {
|
|
2583
2915
|
return this.history.location;
|
|
2584
2916
|
}
|
|
@@ -2587,63 +2919,53 @@ class Session {
|
|
|
2587
2919
|
}
|
|
2588
2920
|
historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
|
|
2589
2921
|
if (this.enabled) {
|
|
2590
|
-
this.navigator.startVisit(location, restorationIdentifier, {
|
|
2922
|
+
this.navigator.startVisit(location, restorationIdentifier, {
|
|
2923
|
+
action: "restore",
|
|
2924
|
+
historyChanged: true,
|
|
2925
|
+
});
|
|
2591
2926
|
}
|
|
2592
2927
|
else {
|
|
2593
|
-
this.adapter.pageInvalidated(
|
|
2928
|
+
this.adapter.pageInvalidated({
|
|
2929
|
+
reason: "turbo_disabled",
|
|
2930
|
+
});
|
|
2594
2931
|
}
|
|
2595
2932
|
}
|
|
2596
2933
|
scrollPositionChanged(position) {
|
|
2597
2934
|
this.history.updateRestorationData({ scrollPosition: position });
|
|
2598
2935
|
}
|
|
2599
|
-
|
|
2600
|
-
return this.
|
|
2601
|
-
|
|
2602
|
-
|
|
2936
|
+
willSubmitFormLinkToLocation(link, location) {
|
|
2937
|
+
return this.elementIsNavigatable(link) && locationIsVisitable(location, this.snapshot.rootLocation);
|
|
2938
|
+
}
|
|
2939
|
+
submittedFormLinkToLocation() { }
|
|
2940
|
+
willFollowLinkToLocation(link, location, event) {
|
|
2941
|
+
return (this.elementIsNavigatable(link) &&
|
|
2942
|
+
locationIsVisitable(location, this.snapshot.rootLocation) &&
|
|
2943
|
+
this.applicationAllowsFollowingLinkToLocation(link, location, event));
|
|
2603
2944
|
}
|
|
2604
2945
|
followedLinkToLocation(link, location) {
|
|
2605
2946
|
const action = this.getActionForLink(link);
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
convertLinkWithMethodClickToFormSubmission(link) {
|
|
2609
|
-
const linkMethod = link.getAttribute("data-turbo-method");
|
|
2610
|
-
if (linkMethod) {
|
|
2611
|
-
const form = document.createElement("form");
|
|
2612
|
-
form.method = linkMethod;
|
|
2613
|
-
form.action = link.getAttribute("href") || "undefined";
|
|
2614
|
-
form.hidden = true;
|
|
2615
|
-
if (link.hasAttribute("data-turbo-confirm")) {
|
|
2616
|
-
form.setAttribute("data-turbo-confirm", link.getAttribute("data-turbo-confirm"));
|
|
2617
|
-
}
|
|
2618
|
-
const frame = this.getTargetFrameForLink(link);
|
|
2619
|
-
if (frame) {
|
|
2620
|
-
form.setAttribute("data-turbo-frame", frame);
|
|
2621
|
-
form.addEventListener("turbo:submit-start", () => form.remove());
|
|
2622
|
-
}
|
|
2623
|
-
else {
|
|
2624
|
-
form.addEventListener("submit", () => form.remove());
|
|
2625
|
-
}
|
|
2626
|
-
document.body.appendChild(form);
|
|
2627
|
-
return dispatch("submit", { cancelable: true, target: form });
|
|
2628
|
-
}
|
|
2629
|
-
else {
|
|
2630
|
-
return false;
|
|
2631
|
-
}
|
|
2947
|
+
const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
|
|
2948
|
+
this.visit(location.href, { action, acceptsStreamResponse, initiator: link });
|
|
2632
2949
|
}
|
|
2633
|
-
|
|
2634
|
-
return this.locationWithActionIsSamePage(location, action) ||
|
|
2950
|
+
allowsVisitingLocation(location, options = {}) {
|
|
2951
|
+
return (this.locationWithActionIsSamePage(location, options.action) ||
|
|
2952
|
+
this.applicationAllowsVisitingLocation(location, options));
|
|
2635
2953
|
}
|
|
2636
2954
|
visitProposedToLocation(location, options) {
|
|
2637
2955
|
extendURLWithDeprecatedProperties(location);
|
|
2638
|
-
this.adapter.visitProposedToLocation(location, options);
|
|
2956
|
+
return this.adapter.visitProposedToLocation(location, options);
|
|
2639
2957
|
}
|
|
2640
2958
|
visitStarted(visit) {
|
|
2959
|
+
if (!visit.acceptsStreamResponse) {
|
|
2960
|
+
markAsBusy(document.documentElement);
|
|
2961
|
+
}
|
|
2641
2962
|
extendURLWithDeprecatedProperties(visit.location);
|
|
2642
2963
|
if (!visit.silent) {
|
|
2643
|
-
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
|
|
2964
|
+
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action, visit.initiator);
|
|
2644
2965
|
}
|
|
2645
2966
|
}
|
|
2646
2967
|
visitCompleted(visit) {
|
|
2968
|
+
clearBusyState(document.documentElement);
|
|
2647
2969
|
this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
|
|
2648
2970
|
}
|
|
2649
2971
|
locationWithActionIsSamePage(location, action) {
|
|
@@ -2654,9 +2976,8 @@ class Session {
|
|
|
2654
2976
|
}
|
|
2655
2977
|
willSubmitForm(form, submitter) {
|
|
2656
2978
|
const action = getAction(form, submitter);
|
|
2657
|
-
return this.
|
|
2658
|
-
|
|
2659
|
-
&& locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
|
|
2979
|
+
return (this.submissionIsNavigatable(form, submitter) &&
|
|
2980
|
+
locationIsVisitable(expandURL(action), this.snapshot.rootLocation));
|
|
2660
2981
|
}
|
|
2661
2982
|
formSubmitted(form, submitter) {
|
|
2662
2983
|
this.navigator.submitForm(form, submitter);
|
|
@@ -2680,16 +3001,23 @@ class Session {
|
|
|
2680
3001
|
this.notifyApplicationBeforeCachingSnapshot();
|
|
2681
3002
|
}
|
|
2682
3003
|
}
|
|
2683
|
-
allowsImmediateRender({ element },
|
|
2684
|
-
const event = this.notifyApplicationBeforeRender(element,
|
|
2685
|
-
|
|
3004
|
+
allowsImmediateRender({ element }, options) {
|
|
3005
|
+
const event = this.notifyApplicationBeforeRender(element, options);
|
|
3006
|
+
const { defaultPrevented, detail: { render }, } = event;
|
|
3007
|
+
if (this.view.renderer && render) {
|
|
3008
|
+
this.view.renderer.renderElement = render;
|
|
3009
|
+
}
|
|
3010
|
+
return !defaultPrevented;
|
|
2686
3011
|
}
|
|
2687
|
-
viewRenderedSnapshot(
|
|
3012
|
+
viewRenderedSnapshot(_snapshot, _isPreview) {
|
|
2688
3013
|
this.view.lastRenderedLocation = this.history.location;
|
|
2689
3014
|
this.notifyApplicationAfterRender();
|
|
2690
3015
|
}
|
|
2691
|
-
|
|
2692
|
-
this.
|
|
3016
|
+
preloadOnLoadLinksForView(element) {
|
|
3017
|
+
this.preloader.preloadOnLoadLinksForView(element);
|
|
3018
|
+
}
|
|
3019
|
+
viewInvalidated(reason) {
|
|
3020
|
+
this.adapter.pageInvalidated(reason);
|
|
2693
3021
|
}
|
|
2694
3022
|
frameLoaded(frame) {
|
|
2695
3023
|
this.notifyApplicationAfterFrameLoad(frame);
|
|
@@ -2697,49 +3025,85 @@ class Session {
|
|
|
2697
3025
|
frameRendered(fetchResponse, frame) {
|
|
2698
3026
|
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
|
2699
3027
|
}
|
|
2700
|
-
applicationAllowsFollowingLinkToLocation(link, location) {
|
|
2701
|
-
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
|
|
3028
|
+
applicationAllowsFollowingLinkToLocation(link, location, ev) {
|
|
3029
|
+
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
|
|
2702
3030
|
return !event.defaultPrevented;
|
|
2703
3031
|
}
|
|
2704
|
-
applicationAllowsVisitingLocation(location) {
|
|
2705
|
-
const event = this.notifyApplicationBeforeVisitingLocation(location);
|
|
3032
|
+
applicationAllowsVisitingLocation(location, options = {}) {
|
|
3033
|
+
const event = this.notifyApplicationBeforeVisitingLocation(location, options.initiator);
|
|
2706
3034
|
return !event.defaultPrevented;
|
|
2707
3035
|
}
|
|
2708
|
-
notifyApplicationAfterClickingLinkToLocation(link, location) {
|
|
2709
|
-
return dispatch("turbo:click", {
|
|
3036
|
+
notifyApplicationAfterClickingLinkToLocation(link, location, event) {
|
|
3037
|
+
return dispatch("turbo:click", {
|
|
3038
|
+
target: link,
|
|
3039
|
+
detail: { url: location.href, originalEvent: event },
|
|
3040
|
+
cancelable: true,
|
|
3041
|
+
});
|
|
2710
3042
|
}
|
|
2711
|
-
notifyApplicationBeforeVisitingLocation(location) {
|
|
2712
|
-
return dispatch("turbo:before-visit", {
|
|
3043
|
+
notifyApplicationBeforeVisitingLocation(location, element) {
|
|
3044
|
+
return dispatch("turbo:before-visit", {
|
|
3045
|
+
target: element,
|
|
3046
|
+
detail: { url: location.href },
|
|
3047
|
+
cancelable: true,
|
|
3048
|
+
});
|
|
2713
3049
|
}
|
|
2714
|
-
notifyApplicationAfterVisitingLocation(location, action) {
|
|
2715
|
-
|
|
2716
|
-
|
|
3050
|
+
notifyApplicationAfterVisitingLocation(location, action, element) {
|
|
3051
|
+
return dispatch("turbo:visit", {
|
|
3052
|
+
target: element,
|
|
3053
|
+
detail: { url: location.href, action },
|
|
3054
|
+
});
|
|
2717
3055
|
}
|
|
2718
3056
|
notifyApplicationBeforeCachingSnapshot() {
|
|
2719
3057
|
return dispatch("turbo:before-cache");
|
|
2720
3058
|
}
|
|
2721
|
-
notifyApplicationBeforeRender(newBody,
|
|
2722
|
-
return dispatch("turbo:before-render", {
|
|
3059
|
+
notifyApplicationBeforeRender(newBody, options) {
|
|
3060
|
+
return dispatch("turbo:before-render", {
|
|
3061
|
+
detail: Object.assign({ newBody }, options),
|
|
3062
|
+
cancelable: true,
|
|
3063
|
+
});
|
|
2723
3064
|
}
|
|
2724
3065
|
notifyApplicationAfterRender() {
|
|
2725
3066
|
return dispatch("turbo:render");
|
|
2726
3067
|
}
|
|
2727
3068
|
notifyApplicationAfterPageLoad(timing = {}) {
|
|
2728
|
-
|
|
2729
|
-
|
|
3069
|
+
return dispatch("turbo:load", {
|
|
3070
|
+
detail: { url: this.location.href, timing },
|
|
3071
|
+
});
|
|
2730
3072
|
}
|
|
2731
3073
|
notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
|
|
2732
|
-
dispatchEvent(new HashChangeEvent("hashchange", {
|
|
3074
|
+
dispatchEvent(new HashChangeEvent("hashchange", {
|
|
3075
|
+
oldURL: oldURL.toString(),
|
|
3076
|
+
newURL: newURL.toString(),
|
|
3077
|
+
}));
|
|
2733
3078
|
}
|
|
2734
3079
|
notifyApplicationAfterFrameLoad(frame) {
|
|
2735
3080
|
return dispatch("turbo:frame-load", { target: frame });
|
|
2736
3081
|
}
|
|
2737
3082
|
notifyApplicationAfterFrameRender(fetchResponse, frame) {
|
|
2738
|
-
return dispatch("turbo:frame-render", {
|
|
3083
|
+
return dispatch("turbo:frame-render", {
|
|
3084
|
+
detail: { fetchResponse },
|
|
3085
|
+
target: frame,
|
|
3086
|
+
cancelable: true,
|
|
3087
|
+
});
|
|
2739
3088
|
}
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
3089
|
+
submissionIsNavigatable(form, submitter) {
|
|
3090
|
+
if (this.formMode == "off") {
|
|
3091
|
+
return false;
|
|
3092
|
+
}
|
|
3093
|
+
else {
|
|
3094
|
+
const submitterIsNavigatable = submitter ? this.elementIsNavigatable(submitter) : true;
|
|
3095
|
+
if (this.formMode == "optin") {
|
|
3096
|
+
return submitterIsNavigatable && form.closest('[data-turbo="true"]') != null;
|
|
3097
|
+
}
|
|
3098
|
+
else {
|
|
3099
|
+
return submitterIsNavigatable && this.elementIsNavigatable(form);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
elementIsNavigatable(element) {
|
|
3104
|
+
const container = element.closest("[data-turbo]");
|
|
3105
|
+
const withinFrame = element.closest("turbo-frame");
|
|
3106
|
+
if (this.drive || withinFrame) {
|
|
2743
3107
|
if (container) {
|
|
2744
3108
|
return container.getAttribute("data-turbo") != "false";
|
|
2745
3109
|
}
|
|
@@ -2760,18 +3124,6 @@ class Session {
|
|
|
2760
3124
|
const action = link.getAttribute("data-turbo-action");
|
|
2761
3125
|
return isAction(action) ? action : "advance";
|
|
2762
3126
|
}
|
|
2763
|
-
getTargetFrameForLink(link) {
|
|
2764
|
-
const frame = link.getAttribute("data-turbo-frame");
|
|
2765
|
-
if (frame) {
|
|
2766
|
-
return frame;
|
|
2767
|
-
}
|
|
2768
|
-
else {
|
|
2769
|
-
const container = link.closest("turbo-frame");
|
|
2770
|
-
if (container) {
|
|
2771
|
-
return container.id;
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
}
|
|
2775
3127
|
get snapshot() {
|
|
2776
3128
|
return this.view.snapshot;
|
|
2777
3129
|
}
|
|
@@ -2783,11 +3135,59 @@ const deprecatedLocationPropertyDescriptors = {
|
|
|
2783
3135
|
absoluteURL: {
|
|
2784
3136
|
get() {
|
|
2785
3137
|
return this.toString();
|
|
2786
|
-
}
|
|
3138
|
+
},
|
|
3139
|
+
},
|
|
3140
|
+
};
|
|
3141
|
+
|
|
3142
|
+
class Cache {
|
|
3143
|
+
constructor(session) {
|
|
3144
|
+
this.session = session;
|
|
3145
|
+
}
|
|
3146
|
+
clear() {
|
|
3147
|
+
this.session.clearCache();
|
|
3148
|
+
}
|
|
3149
|
+
resetCacheControl() {
|
|
3150
|
+
this.setCacheControl("");
|
|
2787
3151
|
}
|
|
3152
|
+
exemptPageFromCache() {
|
|
3153
|
+
this.setCacheControl("no-cache");
|
|
3154
|
+
}
|
|
3155
|
+
exemptPageFromPreview() {
|
|
3156
|
+
this.setCacheControl("no-preview");
|
|
3157
|
+
}
|
|
3158
|
+
setCacheControl(value) {
|
|
3159
|
+
setMetaContent("turbo-cache-control", value);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
const StreamActions = {
|
|
3164
|
+
after() {
|
|
3165
|
+
this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
|
|
3166
|
+
},
|
|
3167
|
+
append() {
|
|
3168
|
+
this.removeDuplicateTargetChildren();
|
|
3169
|
+
this.targetElements.forEach((e) => e.append(this.templateContent));
|
|
3170
|
+
},
|
|
3171
|
+
before() {
|
|
3172
|
+
this.targetElements.forEach((e) => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
|
|
3173
|
+
},
|
|
3174
|
+
prepend() {
|
|
3175
|
+
this.removeDuplicateTargetChildren();
|
|
3176
|
+
this.targetElements.forEach((e) => e.prepend(this.templateContent));
|
|
3177
|
+
},
|
|
3178
|
+
remove() {
|
|
3179
|
+
this.targetElements.forEach((e) => e.remove());
|
|
3180
|
+
},
|
|
3181
|
+
replace() {
|
|
3182
|
+
this.targetElements.forEach((e) => e.replaceWith(this.templateContent));
|
|
3183
|
+
},
|
|
3184
|
+
update() {
|
|
3185
|
+
this.targetElements.forEach((e) => e.replaceChildren(this.templateContent));
|
|
3186
|
+
},
|
|
2788
3187
|
};
|
|
2789
3188
|
|
|
2790
|
-
const session = new Session;
|
|
3189
|
+
const session = new Session();
|
|
3190
|
+
const cache = new Cache(session);
|
|
2791
3191
|
const { navigator: navigator$1 } = session;
|
|
2792
3192
|
function start() {
|
|
2793
3193
|
session.start();
|
|
@@ -2796,7 +3196,7 @@ function registerAdapter(adapter) {
|
|
|
2796
3196
|
session.registerAdapter(adapter);
|
|
2797
3197
|
}
|
|
2798
3198
|
function visit(location, options) {
|
|
2799
|
-
session.visit(location, options);
|
|
3199
|
+
return session.visit(location, options);
|
|
2800
3200
|
}
|
|
2801
3201
|
function connectStreamSource(source) {
|
|
2802
3202
|
session.connectStreamSource(source);
|
|
@@ -2808,6 +3208,7 @@ function renderStreamMessage(message) {
|
|
|
2808
3208
|
session.renderStreamMessage(message);
|
|
2809
3209
|
}
|
|
2810
3210
|
function clearCache() {
|
|
3211
|
+
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.`");
|
|
2811
3212
|
session.clearCache();
|
|
2812
3213
|
}
|
|
2813
3214
|
function setProgressBarDelay(delay) {
|
|
@@ -2816,13 +3217,18 @@ function setProgressBarDelay(delay) {
|
|
|
2816
3217
|
function setConfirmMethod(confirmMethod) {
|
|
2817
3218
|
FormSubmission.confirmMethod = confirmMethod;
|
|
2818
3219
|
}
|
|
3220
|
+
function setFormMode(mode) {
|
|
3221
|
+
session.setFormMode(mode);
|
|
3222
|
+
}
|
|
2819
3223
|
|
|
2820
3224
|
var Turbo = /*#__PURE__*/Object.freeze({
|
|
2821
3225
|
__proto__: null,
|
|
2822
3226
|
navigator: navigator$1,
|
|
2823
3227
|
session: session,
|
|
3228
|
+
cache: cache,
|
|
2824
3229
|
PageRenderer: PageRenderer,
|
|
2825
3230
|
PageSnapshot: PageSnapshot,
|
|
3231
|
+
FrameRenderer: FrameRenderer,
|
|
2826
3232
|
start: start,
|
|
2827
3233
|
registerAdapter: registerAdapter,
|
|
2828
3234
|
visit: visit,
|
|
@@ -2831,41 +3237,56 @@ var Turbo = /*#__PURE__*/Object.freeze({
|
|
|
2831
3237
|
renderStreamMessage: renderStreamMessage,
|
|
2832
3238
|
clearCache: clearCache,
|
|
2833
3239
|
setProgressBarDelay: setProgressBarDelay,
|
|
2834
|
-
setConfirmMethod: setConfirmMethod
|
|
3240
|
+
setConfirmMethod: setConfirmMethod,
|
|
3241
|
+
setFormMode: setFormMode,
|
|
3242
|
+
StreamActions: StreamActions
|
|
2835
3243
|
});
|
|
2836
3244
|
|
|
2837
3245
|
class FrameController {
|
|
2838
3246
|
constructor(element) {
|
|
2839
|
-
this.fetchResponseLoaded = (
|
|
3247
|
+
this.fetchResponseLoaded = (_fetchResponse) => { };
|
|
2840
3248
|
this.currentFetchRequest = null;
|
|
2841
3249
|
this.resolveVisitPromise = () => { };
|
|
2842
3250
|
this.connected = false;
|
|
2843
3251
|
this.hasBeenLoaded = false;
|
|
2844
|
-
this.
|
|
3252
|
+
this.ignoredAttributes = new Set();
|
|
3253
|
+
this.action = null;
|
|
3254
|
+
this.visitCachedSnapshot = ({ element }) => {
|
|
3255
|
+
const frame = element.querySelector("#" + this.element.id);
|
|
3256
|
+
if (frame && this.previousFrameElement) {
|
|
3257
|
+
frame.replaceChildren(...this.previousFrameElement.children);
|
|
3258
|
+
}
|
|
3259
|
+
delete this.previousFrameElement;
|
|
3260
|
+
};
|
|
2845
3261
|
this.element = element;
|
|
2846
3262
|
this.view = new FrameView(this, this.element);
|
|
2847
3263
|
this.appearanceObserver = new AppearanceObserver(this, this.element);
|
|
2848
|
-
this.
|
|
2849
|
-
this.
|
|
3264
|
+
this.formLinkClickObserver = new FormLinkClickObserver(this, this.element);
|
|
3265
|
+
this.linkClickObserver = new LinkClickObserver(this, this.element);
|
|
3266
|
+
this.restorationIdentifier = uuid();
|
|
3267
|
+
this.formSubmitObserver = new FormSubmitObserver(this, this.element);
|
|
2850
3268
|
}
|
|
2851
3269
|
connect() {
|
|
2852
3270
|
if (!this.connected) {
|
|
2853
3271
|
this.connected = true;
|
|
2854
|
-
this.reloadable = false;
|
|
2855
3272
|
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
|
2856
3273
|
this.appearanceObserver.start();
|
|
2857
3274
|
}
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
3275
|
+
else {
|
|
3276
|
+
this.loadSourceURL();
|
|
3277
|
+
}
|
|
3278
|
+
this.formLinkClickObserver.start();
|
|
3279
|
+
this.linkClickObserver.start();
|
|
3280
|
+
this.formSubmitObserver.start();
|
|
2861
3281
|
}
|
|
2862
3282
|
}
|
|
2863
3283
|
disconnect() {
|
|
2864
3284
|
if (this.connected) {
|
|
2865
3285
|
this.connected = false;
|
|
2866
3286
|
this.appearanceObserver.stop();
|
|
2867
|
-
this.
|
|
2868
|
-
this.
|
|
3287
|
+
this.formLinkClickObserver.stop();
|
|
3288
|
+
this.linkClickObserver.stop();
|
|
3289
|
+
this.formSubmitObserver.stop();
|
|
2869
3290
|
}
|
|
2870
3291
|
}
|
|
2871
3292
|
disabledChanged() {
|
|
@@ -2874,10 +3295,20 @@ class FrameController {
|
|
|
2874
3295
|
}
|
|
2875
3296
|
}
|
|
2876
3297
|
sourceURLChanged() {
|
|
3298
|
+
if (this.isIgnoringChangesTo("src"))
|
|
3299
|
+
return;
|
|
3300
|
+
if (this.element.isConnected) {
|
|
3301
|
+
this.complete = false;
|
|
3302
|
+
}
|
|
2877
3303
|
if (this.loadingStyle == FrameLoadingStyle.eager || this.hasBeenLoaded) {
|
|
2878
3304
|
this.loadSourceURL();
|
|
2879
3305
|
}
|
|
2880
3306
|
}
|
|
3307
|
+
completeChanged() {
|
|
3308
|
+
if (this.isIgnoringChangesTo("complete"))
|
|
3309
|
+
return;
|
|
3310
|
+
this.loadSourceURL();
|
|
3311
|
+
}
|
|
2881
3312
|
loadingStyleChanged() {
|
|
2882
3313
|
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
|
2883
3314
|
this.appearanceObserver.start();
|
|
@@ -2888,21 +3319,11 @@ class FrameController {
|
|
|
2888
3319
|
}
|
|
2889
3320
|
}
|
|
2890
3321
|
async loadSourceURL() {
|
|
2891
|
-
if (
|
|
2892
|
-
|
|
2893
|
-
this.
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
this.element.loaded = this.visit(expandURL(this.sourceURL));
|
|
2897
|
-
this.appearanceObserver.stop();
|
|
2898
|
-
await this.element.loaded;
|
|
2899
|
-
this.hasBeenLoaded = true;
|
|
2900
|
-
}
|
|
2901
|
-
catch (error) {
|
|
2902
|
-
this.currentURL = previousURL;
|
|
2903
|
-
throw error;
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
3322
|
+
if (this.enabled && this.isActive && !this.complete && this.sourceURL) {
|
|
3323
|
+
this.element.loaded = this.visit(expandURL(this.sourceURL));
|
|
3324
|
+
this.appearanceObserver.stop();
|
|
3325
|
+
await this.element.loaded;
|
|
3326
|
+
this.hasBeenLoaded = true;
|
|
2906
3327
|
}
|
|
2907
3328
|
}
|
|
2908
3329
|
async loadResponse(fetchResponse) {
|
|
@@ -2913,14 +3334,23 @@ class FrameController {
|
|
|
2913
3334
|
const html = await fetchResponse.responseHTML;
|
|
2914
3335
|
if (html) {
|
|
2915
3336
|
const { body } = parseHTMLDocument(html);
|
|
2916
|
-
const
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
3337
|
+
const newFrameElement = await this.extractForeignFrameElement(body);
|
|
3338
|
+
if (newFrameElement) {
|
|
3339
|
+
const snapshot = new Snapshot(newFrameElement);
|
|
3340
|
+
const renderer = new FrameRenderer(this, this.view.snapshot, snapshot, FrameRenderer.renderElement, false, false);
|
|
3341
|
+
if (this.view.renderPromise)
|
|
3342
|
+
await this.view.renderPromise;
|
|
3343
|
+
this.changeHistory();
|
|
3344
|
+
await this.view.render(renderer);
|
|
3345
|
+
this.complete = true;
|
|
3346
|
+
session.frameRendered(fetchResponse, this.element);
|
|
3347
|
+
session.frameLoaded(this.element);
|
|
3348
|
+
this.fetchResponseLoaded(fetchResponse);
|
|
3349
|
+
}
|
|
3350
|
+
else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
|
|
3351
|
+
console.warn(`A matching frame for #${this.element.id} was missing from the response, transforming into full-page Visit.`);
|
|
3352
|
+
this.visitResponse(fetchResponse.response);
|
|
3353
|
+
}
|
|
2924
3354
|
}
|
|
2925
3355
|
}
|
|
2926
3356
|
catch (error) {
|
|
@@ -2931,56 +3361,66 @@ class FrameController {
|
|
|
2931
3361
|
this.fetchResponseLoaded = () => { };
|
|
2932
3362
|
}
|
|
2933
3363
|
}
|
|
2934
|
-
elementAppearedInViewport(
|
|
3364
|
+
elementAppearedInViewport(_element) {
|
|
2935
3365
|
this.loadSourceURL();
|
|
2936
3366
|
}
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
3367
|
+
willSubmitFormLinkToLocation(link) {
|
|
3368
|
+
return link.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(link);
|
|
3369
|
+
}
|
|
3370
|
+
submittedFormLinkToLocation(link, _location, form) {
|
|
3371
|
+
const frame = this.findFrameElement(link);
|
|
3372
|
+
if (frame)
|
|
3373
|
+
form.setAttribute("data-turbo-frame", frame.id);
|
|
2944
3374
|
}
|
|
2945
|
-
|
|
2946
|
-
this.
|
|
2947
|
-
this.navigateFrame(element, url);
|
|
3375
|
+
willFollowLinkToLocation(element) {
|
|
3376
|
+
return this.shouldInterceptNavigation(element);
|
|
2948
3377
|
}
|
|
2949
|
-
|
|
2950
|
-
|
|
3378
|
+
followedLinkToLocation(element, location) {
|
|
3379
|
+
this.navigateFrame(element, location.href);
|
|
2951
3380
|
}
|
|
2952
|
-
|
|
3381
|
+
willSubmitForm(element, submitter) {
|
|
3382
|
+
return element.closest("turbo-frame") == this.element && this.shouldInterceptNavigation(element, submitter);
|
|
3383
|
+
}
|
|
3384
|
+
formSubmitted(element, submitter) {
|
|
2953
3385
|
if (this.formSubmission) {
|
|
2954
3386
|
this.formSubmission.stop();
|
|
2955
3387
|
}
|
|
2956
|
-
this.reloadable = false;
|
|
2957
3388
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
|
2958
3389
|
const { fetchRequest } = this.formSubmission;
|
|
2959
3390
|
this.prepareHeadersForRequest(fetchRequest.headers, fetchRequest);
|
|
2960
3391
|
this.formSubmission.start();
|
|
2961
3392
|
}
|
|
2962
3393
|
prepareHeadersForRequest(headers, request) {
|
|
3394
|
+
var _a;
|
|
2963
3395
|
headers["Turbo-Frame"] = this.id;
|
|
3396
|
+
if ((_a = this.currentNavigationElement) === null || _a === void 0 ? void 0 : _a.hasAttribute("data-turbo-stream")) {
|
|
3397
|
+
request.acceptResponseType(StreamMessage.contentType);
|
|
3398
|
+
}
|
|
2964
3399
|
}
|
|
2965
|
-
requestStarted(
|
|
3400
|
+
requestStarted(_request) {
|
|
2966
3401
|
markAsBusy(this.element);
|
|
2967
3402
|
}
|
|
2968
|
-
requestPreventedHandlingResponse(
|
|
3403
|
+
requestPreventedHandlingResponse(_request, _response) {
|
|
2969
3404
|
this.resolveVisitPromise();
|
|
2970
3405
|
}
|
|
2971
3406
|
async requestSucceededWithResponse(request, response) {
|
|
2972
3407
|
await this.loadResponse(response);
|
|
2973
3408
|
this.resolveVisitPromise();
|
|
2974
3409
|
}
|
|
2975
|
-
requestFailedWithResponse(request, response) {
|
|
3410
|
+
async requestFailedWithResponse(request, response) {
|
|
2976
3411
|
console.error(response);
|
|
3412
|
+
await this.loadResponse(response);
|
|
2977
3413
|
this.resolveVisitPromise();
|
|
2978
3414
|
}
|
|
2979
3415
|
requestErrored(request, error) {
|
|
2980
3416
|
console.error(error);
|
|
3417
|
+
dispatch("turbo:fetch-request-error", {
|
|
3418
|
+
target: this.element,
|
|
3419
|
+
detail: { request, error },
|
|
3420
|
+
});
|
|
2981
3421
|
this.resolveVisitPromise();
|
|
2982
3422
|
}
|
|
2983
|
-
requestFinished(
|
|
3423
|
+
requestFinished(_request) {
|
|
2984
3424
|
clearBusyState(this.element);
|
|
2985
3425
|
}
|
|
2986
3426
|
formSubmissionStarted({ formElement }) {
|
|
@@ -3000,19 +3440,32 @@ class FrameController {
|
|
|
3000
3440
|
formSubmissionFinished({ formElement }) {
|
|
3001
3441
|
clearBusyState(formElement, this.findFrameElement(formElement));
|
|
3002
3442
|
}
|
|
3003
|
-
allowsImmediateRender(
|
|
3004
|
-
|
|
3443
|
+
allowsImmediateRender({ element: newFrame }, options) {
|
|
3444
|
+
const event = dispatch("turbo:before-frame-render", {
|
|
3445
|
+
target: this.element,
|
|
3446
|
+
detail: Object.assign({ newFrame }, options),
|
|
3447
|
+
cancelable: true,
|
|
3448
|
+
});
|
|
3449
|
+
const { defaultPrevented, detail: { render }, } = event;
|
|
3450
|
+
if (this.view.renderer && render) {
|
|
3451
|
+
this.view.renderer.renderElement = render;
|
|
3452
|
+
}
|
|
3453
|
+
return !defaultPrevented;
|
|
3005
3454
|
}
|
|
3006
|
-
viewRenderedSnapshot(
|
|
3455
|
+
viewRenderedSnapshot(_snapshot, _isPreview) { }
|
|
3456
|
+
preloadOnLoadLinksForView(element) {
|
|
3457
|
+
session.preloadOnLoadLinksForView(element);
|
|
3007
3458
|
}
|
|
3008
|
-
viewInvalidated() {
|
|
3459
|
+
viewInvalidated() { }
|
|
3460
|
+
willRenderFrame(currentElement, _newElement) {
|
|
3461
|
+
this.previousFrameElement = currentElement.cloneNode(true);
|
|
3009
3462
|
}
|
|
3010
3463
|
async visit(url) {
|
|
3011
3464
|
var _a;
|
|
3012
|
-
const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams, this.element);
|
|
3465
|
+
const request = new FetchRequest(this, FetchMethod.get, url, new URLSearchParams(), this.element);
|
|
3013
3466
|
(_a = this.currentFetchRequest) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
3014
3467
|
this.currentFetchRequest = request;
|
|
3015
|
-
return new Promise(resolve => {
|
|
3468
|
+
return new Promise((resolve) => {
|
|
3016
3469
|
this.resolveVisitPromise = () => {
|
|
3017
3470
|
this.resolveVisitPromise = () => { };
|
|
3018
3471
|
this.currentFetchRequest = null;
|
|
@@ -3024,23 +3477,64 @@ class FrameController {
|
|
|
3024
3477
|
navigateFrame(element, url, submitter) {
|
|
3025
3478
|
const frame = this.findFrameElement(element, submitter);
|
|
3026
3479
|
this.proposeVisitIfNavigatedWithAction(frame, element, submitter);
|
|
3027
|
-
|
|
3028
|
-
|
|
3480
|
+
this.withCurrentNavigationElement(element, () => {
|
|
3481
|
+
frame.src = url;
|
|
3482
|
+
});
|
|
3029
3483
|
}
|
|
3030
3484
|
proposeVisitIfNavigatedWithAction(frame, element, submitter) {
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3485
|
+
this.action = getVisitAction(submitter, element, frame);
|
|
3486
|
+
this.frame = frame;
|
|
3487
|
+
if (isAction(this.action)) {
|
|
3488
|
+
const { visitCachedSnapshot } = frame.delegate;
|
|
3034
3489
|
frame.delegate.fetchResponseLoaded = (fetchResponse) => {
|
|
3035
3490
|
if (frame.src) {
|
|
3036
3491
|
const { statusCode, redirected } = fetchResponse;
|
|
3037
3492
|
const responseHTML = frame.ownerDocument.documentElement.outerHTML;
|
|
3038
3493
|
const response = { statusCode, redirected, responseHTML };
|
|
3039
|
-
|
|
3494
|
+
const options = {
|
|
3495
|
+
response,
|
|
3496
|
+
visitCachedSnapshot,
|
|
3497
|
+
willRender: false,
|
|
3498
|
+
updateHistory: false,
|
|
3499
|
+
restorationIdentifier: this.restorationIdentifier,
|
|
3500
|
+
};
|
|
3501
|
+
if (this.action)
|
|
3502
|
+
options.action = this.action;
|
|
3503
|
+
session.visit(frame.src, options);
|
|
3040
3504
|
}
|
|
3041
3505
|
};
|
|
3042
3506
|
}
|
|
3043
3507
|
}
|
|
3508
|
+
changeHistory() {
|
|
3509
|
+
if (this.action && this.frame) {
|
|
3510
|
+
const method = getHistoryMethodForAction(this.action);
|
|
3511
|
+
session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
willHandleFrameMissingFromResponse(fetchResponse) {
|
|
3515
|
+
this.element.setAttribute("complete", "");
|
|
3516
|
+
const response = fetchResponse.response;
|
|
3517
|
+
const visit = async (url, options = {}) => {
|
|
3518
|
+
if (url instanceof Response) {
|
|
3519
|
+
this.visitResponse(url);
|
|
3520
|
+
}
|
|
3521
|
+
else {
|
|
3522
|
+
session.visit(url, options);
|
|
3523
|
+
}
|
|
3524
|
+
};
|
|
3525
|
+
const event = dispatch("turbo:frame-missing", {
|
|
3526
|
+
target: this.element,
|
|
3527
|
+
detail: { response, visit },
|
|
3528
|
+
cancelable: true,
|
|
3529
|
+
});
|
|
3530
|
+
return !event.defaultPrevented;
|
|
3531
|
+
}
|
|
3532
|
+
async visitResponse(response) {
|
|
3533
|
+
const wrapped = new FetchResponse(response);
|
|
3534
|
+
const responseHTML = await wrapped.responseHTML;
|
|
3535
|
+
const { location, redirected, statusCode } = wrapped;
|
|
3536
|
+
return session.visit(location, { response: { redirected, statusCode, responseHTML } });
|
|
3537
|
+
}
|
|
3044
3538
|
findFrameElement(element, submitter) {
|
|
3045
3539
|
var _a;
|
|
3046
3540
|
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
@@ -3050,19 +3544,21 @@ class FrameController {
|
|
|
3050
3544
|
let element;
|
|
3051
3545
|
const id = CSS.escape(this.id);
|
|
3052
3546
|
try {
|
|
3053
|
-
|
|
3547
|
+
element = activateElement(container.querySelector(`turbo-frame#${id}`), this.sourceURL);
|
|
3548
|
+
if (element) {
|
|
3054
3549
|
return element;
|
|
3055
3550
|
}
|
|
3056
|
-
|
|
3551
|
+
element = activateElement(container.querySelector(`turbo-frame[src][recurse~=${id}]`), this.sourceURL);
|
|
3552
|
+
if (element) {
|
|
3057
3553
|
await element.loaded;
|
|
3058
3554
|
return await this.extractForeignFrameElement(element);
|
|
3059
3555
|
}
|
|
3060
|
-
console.error(`Response has no matching <turbo-frame id="${id}"> element`);
|
|
3061
3556
|
}
|
|
3062
3557
|
catch (error) {
|
|
3063
3558
|
console.error(error);
|
|
3559
|
+
return new FrameElement();
|
|
3064
3560
|
}
|
|
3065
|
-
return
|
|
3561
|
+
return null;
|
|
3066
3562
|
}
|
|
3067
3563
|
formActionIsVisitable(form, submitter) {
|
|
3068
3564
|
const action = getAction(form, submitter);
|
|
@@ -3082,10 +3578,10 @@ class FrameController {
|
|
|
3082
3578
|
return !frameElement.disabled;
|
|
3083
3579
|
}
|
|
3084
3580
|
}
|
|
3085
|
-
if (!session.
|
|
3581
|
+
if (!session.elementIsNavigatable(element)) {
|
|
3086
3582
|
return false;
|
|
3087
3583
|
}
|
|
3088
|
-
if (submitter && !session.
|
|
3584
|
+
if (submitter && !session.elementIsNavigatable(submitter)) {
|
|
3089
3585
|
return false;
|
|
3090
3586
|
}
|
|
3091
3587
|
return true;
|
|
@@ -3101,24 +3597,10 @@ class FrameController {
|
|
|
3101
3597
|
return this.element.src;
|
|
3102
3598
|
}
|
|
3103
3599
|
}
|
|
3104
|
-
get reloadable() {
|
|
3105
|
-
const frame = this.findFrameElement(this.element);
|
|
3106
|
-
return frame.hasAttribute("reloadable");
|
|
3107
|
-
}
|
|
3108
|
-
set reloadable(value) {
|
|
3109
|
-
const frame = this.findFrameElement(this.element);
|
|
3110
|
-
if (value) {
|
|
3111
|
-
frame.setAttribute("reloadable", "");
|
|
3112
|
-
}
|
|
3113
|
-
else {
|
|
3114
|
-
frame.removeAttribute("reloadable");
|
|
3115
|
-
}
|
|
3116
|
-
}
|
|
3117
3600
|
set sourceURL(sourceURL) {
|
|
3118
|
-
this.
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
this.settingSourceURL = false;
|
|
3601
|
+
this.ignoringChangesToAttribute("src", () => {
|
|
3602
|
+
this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
|
|
3603
|
+
});
|
|
3122
3604
|
}
|
|
3123
3605
|
get loadingStyle() {
|
|
3124
3606
|
return this.element.loading;
|
|
@@ -3126,6 +3608,19 @@ class FrameController {
|
|
|
3126
3608
|
get isLoading() {
|
|
3127
3609
|
return this.formSubmission !== undefined || this.resolveVisitPromise() !== undefined;
|
|
3128
3610
|
}
|
|
3611
|
+
get complete() {
|
|
3612
|
+
return this.element.hasAttribute("complete");
|
|
3613
|
+
}
|
|
3614
|
+
set complete(value) {
|
|
3615
|
+
this.ignoringChangesToAttribute("complete", () => {
|
|
3616
|
+
if (value) {
|
|
3617
|
+
this.element.setAttribute("complete", "");
|
|
3618
|
+
}
|
|
3619
|
+
else {
|
|
3620
|
+
this.element.removeAttribute("complete");
|
|
3621
|
+
}
|
|
3622
|
+
});
|
|
3623
|
+
}
|
|
3129
3624
|
get isActive() {
|
|
3130
3625
|
return this.element.isActive && this.connected;
|
|
3131
3626
|
}
|
|
@@ -3135,16 +3630,18 @@ class FrameController {
|
|
|
3135
3630
|
const root = (_a = meta === null || meta === void 0 ? void 0 : meta.content) !== null && _a !== void 0 ? _a : "/";
|
|
3136
3631
|
return expandURL(root);
|
|
3137
3632
|
}
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
this.
|
|
3633
|
+
isIgnoringChangesTo(attributeName) {
|
|
3634
|
+
return this.ignoredAttributes.has(attributeName);
|
|
3635
|
+
}
|
|
3636
|
+
ignoringChangesToAttribute(attributeName, callback) {
|
|
3637
|
+
this.ignoredAttributes.add(attributeName);
|
|
3638
|
+
callback();
|
|
3639
|
+
this.ignoredAttributes.delete(attributeName);
|
|
3640
|
+
}
|
|
3641
|
+
withCurrentNavigationElement(element, callback) {
|
|
3642
|
+
this.currentNavigationElement = element;
|
|
3643
|
+
callback();
|
|
3644
|
+
delete this.currentNavigationElement;
|
|
3148
3645
|
}
|
|
3149
3646
|
}
|
|
3150
3647
|
function getFrameElementById(id) {
|
|
@@ -3172,36 +3669,10 @@ function activateElement(element, currentURL) {
|
|
|
3172
3669
|
}
|
|
3173
3670
|
}
|
|
3174
3671
|
|
|
3175
|
-
const StreamActions = {
|
|
3176
|
-
after() {
|
|
3177
|
-
this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e.nextSibling); });
|
|
3178
|
-
},
|
|
3179
|
-
append() {
|
|
3180
|
-
this.removeDuplicateTargetChildren();
|
|
3181
|
-
this.targetElements.forEach(e => e.append(this.templateContent));
|
|
3182
|
-
},
|
|
3183
|
-
before() {
|
|
3184
|
-
this.targetElements.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.insertBefore(this.templateContent, e); });
|
|
3185
|
-
},
|
|
3186
|
-
prepend() {
|
|
3187
|
-
this.removeDuplicateTargetChildren();
|
|
3188
|
-
this.targetElements.forEach(e => e.prepend(this.templateContent));
|
|
3189
|
-
},
|
|
3190
|
-
remove() {
|
|
3191
|
-
this.targetElements.forEach(e => e.remove());
|
|
3192
|
-
},
|
|
3193
|
-
replace() {
|
|
3194
|
-
this.targetElements.forEach(e => e.replaceWith(this.templateContent));
|
|
3195
|
-
},
|
|
3196
|
-
update() {
|
|
3197
|
-
this.targetElements.forEach(e => {
|
|
3198
|
-
e.innerHTML = "";
|
|
3199
|
-
e.append(this.templateContent);
|
|
3200
|
-
});
|
|
3201
|
-
}
|
|
3202
|
-
};
|
|
3203
|
-
|
|
3204
3672
|
class StreamElement extends HTMLElement {
|
|
3673
|
+
static async renderElement(newElement) {
|
|
3674
|
+
await newElement.performAction();
|
|
3675
|
+
}
|
|
3205
3676
|
async connectedCallback() {
|
|
3206
3677
|
try {
|
|
3207
3678
|
await this.render();
|
|
@@ -3215,12 +3686,13 @@ class StreamElement extends HTMLElement {
|
|
|
3215
3686
|
}
|
|
3216
3687
|
async render() {
|
|
3217
3688
|
var _a;
|
|
3218
|
-
return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
|
|
3219
|
-
|
|
3689
|
+
return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
|
|
3690
|
+
const event = this.beforeRenderEvent;
|
|
3691
|
+
if (this.dispatchEvent(event)) {
|
|
3220
3692
|
await nextAnimationFrame();
|
|
3221
|
-
|
|
3693
|
+
await event.detail.render(this);
|
|
3222
3694
|
}
|
|
3223
|
-
})());
|
|
3695
|
+
})()));
|
|
3224
3696
|
}
|
|
3225
3697
|
disconnect() {
|
|
3226
3698
|
try {
|
|
@@ -3229,13 +3701,13 @@ class StreamElement extends HTMLElement {
|
|
|
3229
3701
|
catch (_a) { }
|
|
3230
3702
|
}
|
|
3231
3703
|
removeDuplicateTargetChildren() {
|
|
3232
|
-
this.duplicateChildren.forEach(c => c.remove());
|
|
3704
|
+
this.duplicateChildren.forEach((c) => c.remove());
|
|
3233
3705
|
}
|
|
3234
3706
|
get duplicateChildren() {
|
|
3235
3707
|
var _a;
|
|
3236
|
-
const existingChildren = this.targetElements.flatMap(e => [...e.children]).filter(c => !!c.id);
|
|
3237
|
-
const newChildrenIds = [...(_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children].filter(c => !!c.id).map(c => c.id);
|
|
3238
|
-
return existingChildren.filter(c => newChildrenIds.includes(c.id));
|
|
3708
|
+
const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id);
|
|
3709
|
+
const newChildrenIds = [...(((_a = this.templateContent) === null || _a === void 0 ? void 0 : _a.children) || [])].filter((c) => !!c.id).map((c) => c.id);
|
|
3710
|
+
return existingChildren.filter((c) => newChildrenIds.includes(c.id));
|
|
3239
3711
|
}
|
|
3240
3712
|
get performAction() {
|
|
3241
3713
|
if (this.action) {
|
|
@@ -3262,7 +3734,12 @@ class StreamElement extends HTMLElement {
|
|
|
3262
3734
|
return this.templateElement.content.cloneNode(true);
|
|
3263
3735
|
}
|
|
3264
3736
|
get templateElement() {
|
|
3265
|
-
if (this.firstElementChild
|
|
3737
|
+
if (this.firstElementChild === null) {
|
|
3738
|
+
const template = this.ownerDocument.createElement("template");
|
|
3739
|
+
this.appendChild(template);
|
|
3740
|
+
return template;
|
|
3741
|
+
}
|
|
3742
|
+
else if (this.firstElementChild instanceof HTMLTemplateElement) {
|
|
3266
3743
|
return this.firstElementChild;
|
|
3267
3744
|
}
|
|
3268
3745
|
this.raise("first child element must be a <template> element");
|
|
@@ -3284,7 +3761,11 @@ class StreamElement extends HTMLElement {
|
|
|
3284
3761
|
return (_b = ((_a = this.outerHTML.match(/<[^>]+>/)) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : "<turbo-stream>";
|
|
3285
3762
|
}
|
|
3286
3763
|
get beforeRenderEvent() {
|
|
3287
|
-
return new CustomEvent("turbo:before-stream-render", {
|
|
3764
|
+
return new CustomEvent("turbo:before-stream-render", {
|
|
3765
|
+
bubbles: true,
|
|
3766
|
+
cancelable: true,
|
|
3767
|
+
detail: { newStream: this, render: StreamElement.renderElement },
|
|
3768
|
+
});
|
|
3288
3769
|
}
|
|
3289
3770
|
get targetElementsById() {
|
|
3290
3771
|
var _a;
|
|
@@ -3308,9 +3789,35 @@ class StreamElement extends HTMLElement {
|
|
|
3308
3789
|
}
|
|
3309
3790
|
}
|
|
3310
3791
|
|
|
3792
|
+
class StreamSourceElement extends HTMLElement {
|
|
3793
|
+
constructor() {
|
|
3794
|
+
super(...arguments);
|
|
3795
|
+
this.streamSource = null;
|
|
3796
|
+
}
|
|
3797
|
+
connectedCallback() {
|
|
3798
|
+
this.streamSource = this.src.match(/^ws{1,2}:/) ? new WebSocket(this.src) : new EventSource(this.src);
|
|
3799
|
+
connectStreamSource(this.streamSource);
|
|
3800
|
+
}
|
|
3801
|
+
disconnectedCallback() {
|
|
3802
|
+
if (this.streamSource) {
|
|
3803
|
+
disconnectStreamSource(this.streamSource);
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
get src() {
|
|
3807
|
+
return this.getAttribute("src") || "";
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3311
3811
|
FrameElement.delegateConstructor = FrameController;
|
|
3312
|
-
customElements.
|
|
3313
|
-
customElements.define("turbo-
|
|
3812
|
+
if (customElements.get("turbo-frame") === undefined) {
|
|
3813
|
+
customElements.define("turbo-frame", FrameElement);
|
|
3814
|
+
}
|
|
3815
|
+
if (customElements.get("turbo-stream") === undefined) {
|
|
3816
|
+
customElements.define("turbo-stream", StreamElement);
|
|
3817
|
+
}
|
|
3818
|
+
if (customElements.get("turbo-stream-source") === undefined) {
|
|
3819
|
+
customElements.define("turbo-stream-source", StreamSourceElement);
|
|
3820
|
+
}
|
|
3314
3821
|
|
|
3315
3822
|
(() => {
|
|
3316
3823
|
let element = document.currentScript;
|
|
@@ -3318,7 +3825,8 @@ customElements.define("turbo-stream", StreamElement);
|
|
|
3318
3825
|
return;
|
|
3319
3826
|
if (element.hasAttribute("data-turbo-suppress-warning"))
|
|
3320
3827
|
return;
|
|
3321
|
-
|
|
3828
|
+
element = element.parentElement;
|
|
3829
|
+
while (element) {
|
|
3322
3830
|
if (element == document.body) {
|
|
3323
3831
|
return console.warn(unindent `
|
|
3324
3832
|
You are loading Turbo from a <script> element inside the <body> element. This is probably not what you meant to do!
|
|
@@ -3331,10 +3839,11 @@ customElements.define("turbo-stream", StreamElement);
|
|
|
3331
3839
|
Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
|
|
3332
3840
|
`, element.outerHTML);
|
|
3333
3841
|
}
|
|
3842
|
+
element = element.parentElement;
|
|
3334
3843
|
}
|
|
3335
3844
|
})();
|
|
3336
3845
|
|
|
3337
3846
|
window.Turbo = Turbo;
|
|
3338
3847
|
start();
|
|
3339
3848
|
|
|
3340
|
-
export { PageRenderer, PageSnapshot, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setProgressBarDelay, start, visit };
|
|
3849
|
+
export { FrameElement, FrameLoadingStyle, FrameRenderer, PageRenderer, PageSnapshot, StreamActions, StreamElement, StreamSourceElement, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
|