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