turbo-rails 0.5.3 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -1
- data/app/assets/javascripts/turbo.js +243 -129
- data/app/helpers/turbo/includes_helper.rb +2 -0
- data/app/helpers/turbo/streams_helper.rb +2 -2
- data/app/models/concerns/turbo/broadcastable.rb +8 -3
- data/lib/install/turbo_with_asset_pipeline.rb +19 -3
- data/lib/install/turbo_with_webpacker.rb +1 -2
- data/lib/turbo/engine.rb +1 -1
- data/lib/turbo/version.rb +1 -1
- metadata +7 -173
- data/.github/workflows/ci.yml +0 -30
- data/.gitignore +0 -2
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -147
- data/package.json +0 -47
- data/rollup.config.js +0 -23
- data/test/drive/drive_helper_test.rb +0 -8
- data/test/dummy/.babelrc +0 -18
- data/test/dummy/.gitignore +0 -3
- data/test/dummy/.postcssrc.yml +0 -3
- data/test/dummy/Rakefile +0 -6
- data/test/dummy/app/assets/config/manifest.js +0 -2
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +0 -15
- data/test/dummy/app/assets/stylesheets/scaffold.css +0 -80
- data/test/dummy/app/channels/application_cable/channel.rb +0 -4
- data/test/dummy/app/channels/application_cable/connection.rb +0 -4
- data/test/dummy/app/controllers/application_controller.rb +0 -2
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/controllers/messages_controller.rb +0 -12
- data/test/dummy/app/controllers/trays_controller.rb +0 -17
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/javascript/packs/application.js +0 -0
- data/test/dummy/app/jobs/application_job.rb +0 -2
- data/test/dummy/app/mailboxes/application_mailbox.rb +0 -2
- data/test/dummy/app/mailboxes/messages_mailbox.rb +0 -4
- data/test/dummy/app/mailers/application_mailer.rb +0 -4
- data/test/dummy/app/models/application_record.rb +0 -3
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/message.rb +0 -29
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/app/views/layouts/mailer.html.erb +0 -13
- data/test/dummy/app/views/layouts/mailer.text.erb +0 -1
- data/test/dummy/app/views/messages/_message.html.erb +0 -1
- data/test/dummy/app/views/messages/_message.turbo_stream.erb +0 -1
- data/test/dummy/app/views/messages/show.turbo_stream.erb +0 -9
- data/test/dummy/app/views/trays/index.html.erb +0 -3
- data/test/dummy/app/views/trays/show.html.erb +0 -3
- data/test/dummy/bin/bundle +0 -3
- data/test/dummy/bin/rails +0 -4
- data/test/dummy/bin/rake +0 -4
- data/test/dummy/bin/setup +0 -36
- data/test/dummy/bin/update +0 -31
- data/test/dummy/bin/yarn +0 -11
- data/test/dummy/config.ru +0 -5
- data/test/dummy/config/application.rb +0 -22
- data/test/dummy/config/boot.rb +0 -5
- data/test/dummy/config/cable.yml +0 -10
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -34
- data/test/dummy/config/environments/production.rb +0 -96
- data/test/dummy/config/environments/test.rb +0 -38
- data/test/dummy/config/initializers/application_controller_renderer.rb +0 -8
- data/test/dummy/config/initializers/assets.rb +0 -14
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/content_security_policy.rb +0 -22
- data/test/dummy/config/initializers/cookies_serializer.rb +0 -5
- data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
- data/test/dummy/config/initializers/inflections.rb +0 -16
- data/test/dummy/config/initializers/mime_types.rb +0 -4
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -33
- data/test/dummy/config/puma.rb +0 -34
- data/test/dummy/config/routes.rb +0 -4
- data/test/dummy/config/spring.rb +0 -6
- data/test/dummy/config/webpack/development.js +0 -3
- data/test/dummy/config/webpack/environment.js +0 -3
- data/test/dummy/config/webpack/production.js +0 -3
- data/test/dummy/config/webpack/test.js +0 -3
- data/test/dummy/config/webpacker.yml +0 -65
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +0 -67
- data/test/dummy/public/422.html +0 -67
- data/test/dummy/public/500.html +0 -66
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/frames/frame_request_controller_test.rb +0 -21
- data/test/frames/frames_helper_test.rb +0 -21
- data/test/native/navigation_controller_test.rb +0 -42
- data/test/streams/broadcastable_test.rb +0 -80
- data/test/streams/streams_channel_test.rb +0 -111
- data/test/streams/streams_controller_test.rb +0 -29
- data/test/turbo_test.rb +0 -10
- data/turbo-rails.gemspec +0 -17
- data/yarn.lock +0 -283
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bde12fe28a415f37f7000160bfe9577c066d0a450b2a8b3a072acb11a620f229
|
4
|
+
data.tar.gz: 06e64c67ade4fa00e7ba13eabf31e3f78fef72e8e9faedd66861b0d370c9e94e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d18cfb444d1511997d07d15e6696ef4a22dafef448ddbe21b0037c60c1b565ab4a24a623393dd9dbadb8685336f0fd6487d0350d4895d336b041b5d07bca7f66
|
7
|
+
data.tar.gz: 1e094bc98631469c394117c65f8e73387c577c4d3e1fdfa329c9bf5ef3f5fd62ea7ba0a4efc394f8696e087f8f06c8870e7fe1ab4066bbe1049f659f22aa8163
|
data/README.md
CHANGED
@@ -40,8 +40,14 @@ The JavaScript for Turbo can either be run through the asset pipeline, which is
|
|
40
40
|
|
41
41
|
Running `turbo:install` will install through NPM if Webpacker is installed in the application. Otherwise the asset pipeline version is used.
|
42
42
|
|
43
|
-
If you're using Webpack and need to use either the cable consumer or the Turbo instance, you can import [`Turbo`](https://turbo.hotwire.dev/reference/drive) and/or [`cable`](https://github.com/hotwired/turbo-rails/blob/
|
43
|
+
If you're using Webpack and need to use either the cable consumer or the Turbo instance, you can import [`Turbo`](https://turbo.hotwire.dev/reference/drive) and/or [`cable`](https://github.com/hotwired/turbo-rails/blob/main/app/javascript/turbo/cable.js) (`import { Turbo, cable } from "@hotwired/turbo-rails"`), but ensure that your application actually *uses* the members it `import`s when using this style (see [turbo-rails#48](https://github.com/hotwired/turbo-rails/issues/48)).
|
44
44
|
|
45
|
+
If you're using a [native adapter](https://turbo.hotwire.dev/handbook/native), you'll need to assign `window.Turbo`, even if it's not used for anything else:
|
46
|
+
|
47
|
+
```js
|
48
|
+
import { Turbo } from "@hotwired/turbo-rails"
|
49
|
+
window.Turbo = Turbo
|
50
|
+
```
|
45
51
|
|
46
52
|
## Usage
|
47
53
|
|
@@ -41,6 +41,97 @@ function clickCaptured(event) {
|
|
41
41
|
});
|
42
42
|
})();
|
43
43
|
|
44
|
+
var FrameLoadingStyle;
|
45
|
+
|
46
|
+
(function(FrameLoadingStyle) {
|
47
|
+
FrameLoadingStyle["eager"] = "eager";
|
48
|
+
FrameLoadingStyle["lazy"] = "lazy";
|
49
|
+
})(FrameLoadingStyle || (FrameLoadingStyle = {}));
|
50
|
+
|
51
|
+
class FrameElement extends HTMLElement {
|
52
|
+
constructor() {
|
53
|
+
super();
|
54
|
+
this.loaded = Promise.resolve();
|
55
|
+
this.delegate = new FrameElement.delegateConstructor(this);
|
56
|
+
}
|
57
|
+
static get observedAttributes() {
|
58
|
+
return [ "loading", "src" ];
|
59
|
+
}
|
60
|
+
connectedCallback() {
|
61
|
+
this.delegate.connect();
|
62
|
+
}
|
63
|
+
disconnectedCallback() {
|
64
|
+
this.delegate.disconnect();
|
65
|
+
}
|
66
|
+
attributeChangedCallback(name) {
|
67
|
+
if (name == "loading") {
|
68
|
+
this.delegate.loadingStyleChanged();
|
69
|
+
} else if (name == "src") {
|
70
|
+
this.delegate.sourceURLChanged();
|
71
|
+
}
|
72
|
+
}
|
73
|
+
get src() {
|
74
|
+
return this.getAttribute("src");
|
75
|
+
}
|
76
|
+
set src(value) {
|
77
|
+
if (value) {
|
78
|
+
this.setAttribute("src", value);
|
79
|
+
} else {
|
80
|
+
this.removeAttribute("src");
|
81
|
+
}
|
82
|
+
}
|
83
|
+
get loading() {
|
84
|
+
return frameLoadingStyleFromString(this.getAttribute("loading") || "");
|
85
|
+
}
|
86
|
+
set loading(value) {
|
87
|
+
if (value) {
|
88
|
+
this.setAttribute("loading", value);
|
89
|
+
} else {
|
90
|
+
this.removeAttribute("loading");
|
91
|
+
}
|
92
|
+
}
|
93
|
+
get disabled() {
|
94
|
+
return this.hasAttribute("disabled");
|
95
|
+
}
|
96
|
+
set disabled(value) {
|
97
|
+
if (value) {
|
98
|
+
this.setAttribute("disabled", "");
|
99
|
+
} else {
|
100
|
+
this.removeAttribute("disabled");
|
101
|
+
}
|
102
|
+
}
|
103
|
+
get autoscroll() {
|
104
|
+
return this.hasAttribute("autoscroll");
|
105
|
+
}
|
106
|
+
set autoscroll(value) {
|
107
|
+
if (value) {
|
108
|
+
this.setAttribute("autoscroll", "");
|
109
|
+
} else {
|
110
|
+
this.removeAttribute("autoscroll");
|
111
|
+
}
|
112
|
+
}
|
113
|
+
get complete() {
|
114
|
+
return !this.delegate.isLoading;
|
115
|
+
}
|
116
|
+
get isActive() {
|
117
|
+
return this.ownerDocument === document && !this.isPreview;
|
118
|
+
}
|
119
|
+
get isPreview() {
|
120
|
+
var _a, _b;
|
121
|
+
return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.hasAttribute("data-turbo-preview");
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
function frameLoadingStyleFromString(style) {
|
126
|
+
switch (style.toLowerCase()) {
|
127
|
+
case "lazy":
|
128
|
+
return FrameLoadingStyle.lazy;
|
129
|
+
|
130
|
+
default:
|
131
|
+
return FrameLoadingStyle.eager;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
44
135
|
class Location {
|
45
136
|
constructor(url) {
|
46
137
|
const linkWithAnchor = document.createElement("a");
|
@@ -142,7 +233,7 @@ class FetchResponse {
|
|
142
233
|
return Location.wrap(this.response.url);
|
143
234
|
}
|
144
235
|
get isHTML() {
|
145
|
-
return this.contentType && this.contentType.match(/^text\/html
|
236
|
+
return this.contentType && this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/);
|
146
237
|
}
|
147
238
|
get statusCode() {
|
148
239
|
return this.response.status;
|
@@ -336,27 +427,30 @@ class FetchRequest {
|
|
336
427
|
}
|
337
428
|
}
|
338
429
|
|
339
|
-
class
|
430
|
+
class AppearanceObserver {
|
340
431
|
constructor(delegate, element) {
|
341
|
-
this.
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
event.preventDefault();
|
347
|
-
event.stopImmediatePropagation();
|
348
|
-
this.delegate.formSubmissionIntercepted(form, submitter);
|
349
|
-
}
|
432
|
+
this.started = false;
|
433
|
+
this.intersect = entries => {
|
434
|
+
const lastEntry = entries.slice(-1)[0];
|
435
|
+
if (lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.isIntersecting) {
|
436
|
+
this.delegate.elementAppearedInViewport(this.element);
|
350
437
|
}
|
351
438
|
};
|
352
439
|
this.delegate = delegate;
|
353
440
|
this.element = element;
|
441
|
+
this.intersectionObserver = new IntersectionObserver(this.intersect);
|
354
442
|
}
|
355
443
|
start() {
|
356
|
-
|
444
|
+
if (!this.started) {
|
445
|
+
this.started = true;
|
446
|
+
this.intersectionObserver.observe(this.element);
|
447
|
+
}
|
357
448
|
}
|
358
449
|
stop() {
|
359
|
-
|
450
|
+
if (this.started) {
|
451
|
+
this.started = false;
|
452
|
+
this.intersectionObserver.unobserve(this.element);
|
453
|
+
}
|
360
454
|
}
|
361
455
|
}
|
362
456
|
|
@@ -437,7 +531,7 @@ class FormSubmission {
|
|
437
531
|
requestSucceededWithResponse(request, response) {
|
438
532
|
if (response.clientError || response.serverError) {
|
439
533
|
this.delegate.formSubmissionFailedWithResponse(this, response);
|
440
|
-
} else if (this.requestMustRedirect(request) &&
|
534
|
+
} else if (this.requestMustRedirect(request) && responseSucceededWithoutRedirect(response)) {
|
441
535
|
const error = new Error("Form responses must redirect to another location");
|
442
536
|
this.delegate.formSubmissionErrored(this, error);
|
443
537
|
} else {
|
@@ -504,6 +598,34 @@ function getMetaContent(name) {
|
|
504
598
|
return element && element.content;
|
505
599
|
}
|
506
600
|
|
601
|
+
function responseSucceededWithoutRedirect(response) {
|
602
|
+
return response.statusCode == 200 && !response.redirected;
|
603
|
+
}
|
604
|
+
|
605
|
+
class FormInterceptor {
|
606
|
+
constructor(delegate, element) {
|
607
|
+
this.submitBubbled = event => {
|
608
|
+
if (event.target instanceof HTMLFormElement) {
|
609
|
+
const form = event.target;
|
610
|
+
const submitter = event.submitter || undefined;
|
611
|
+
if (this.delegate.shouldInterceptFormSubmission(form, submitter)) {
|
612
|
+
event.preventDefault();
|
613
|
+
event.stopImmediatePropagation();
|
614
|
+
this.delegate.formSubmissionIntercepted(form, submitter);
|
615
|
+
}
|
616
|
+
}
|
617
|
+
};
|
618
|
+
this.delegate = delegate;
|
619
|
+
this.element = element;
|
620
|
+
}
|
621
|
+
start() {
|
622
|
+
this.element.addEventListener("submit", this.submitBubbled);
|
623
|
+
}
|
624
|
+
stop() {
|
625
|
+
this.element.removeEventListener("submit", this.submitBubbled);
|
626
|
+
}
|
627
|
+
}
|
628
|
+
|
507
629
|
class LinkInterceptor {
|
508
630
|
constructor(delegate, element) {
|
509
631
|
this.clickBubbled = event => {
|
@@ -549,17 +671,61 @@ class FrameController {
|
|
549
671
|
constructor(element) {
|
550
672
|
this.resolveVisitPromise = () => {};
|
551
673
|
this.element = element;
|
674
|
+
this.appearanceObserver = new AppearanceObserver(this, this.element);
|
552
675
|
this.linkInterceptor = new LinkInterceptor(this, this.element);
|
553
676
|
this.formInterceptor = new FormInterceptor(this, this.element);
|
554
677
|
}
|
555
678
|
connect() {
|
679
|
+
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
680
|
+
this.appearanceObserver.start();
|
681
|
+
}
|
556
682
|
this.linkInterceptor.start();
|
557
683
|
this.formInterceptor.start();
|
558
684
|
}
|
559
685
|
disconnect() {
|
686
|
+
this.appearanceObserver.stop();
|
560
687
|
this.linkInterceptor.stop();
|
561
688
|
this.formInterceptor.stop();
|
562
689
|
}
|
690
|
+
sourceURLChanged() {
|
691
|
+
if (this.loadingStyle == FrameLoadingStyle.eager) {
|
692
|
+
this.loadSourceURL();
|
693
|
+
}
|
694
|
+
}
|
695
|
+
loadingStyleChanged() {
|
696
|
+
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
697
|
+
this.appearanceObserver.start();
|
698
|
+
} else {
|
699
|
+
this.appearanceObserver.stop();
|
700
|
+
this.loadSourceURL();
|
701
|
+
}
|
702
|
+
}
|
703
|
+
async loadSourceURL() {
|
704
|
+
if (this.isActive && this.sourceURL && this.sourceURL != this.loadingURL) {
|
705
|
+
try {
|
706
|
+
this.loadingURL = this.sourceURL;
|
707
|
+
this.element.loaded = this.visit(this.sourceURL);
|
708
|
+
this.appearanceObserver.stop();
|
709
|
+
await this.element.loaded;
|
710
|
+
} finally {
|
711
|
+
delete this.loadingURL;
|
712
|
+
}
|
713
|
+
}
|
714
|
+
}
|
715
|
+
async loadResponse(response) {
|
716
|
+
const fragment = fragmentFromHTML(await response.responseHTML);
|
717
|
+
if (fragment) {
|
718
|
+
const element = await this.extractForeignFrameElement(fragment);
|
719
|
+
await nextAnimationFrame();
|
720
|
+
this.loadFrameElement(element);
|
721
|
+
this.scrollFrameIntoView(element);
|
722
|
+
await nextAnimationFrame();
|
723
|
+
this.focusFirstAutofocusableElement();
|
724
|
+
}
|
725
|
+
}
|
726
|
+
elementAppearedInViewport(element) {
|
727
|
+
this.loadSourceURL();
|
728
|
+
}
|
563
729
|
shouldInterceptLinkClick(element, url) {
|
564
730
|
return this.shouldInterceptNavigation(element);
|
565
731
|
}
|
@@ -580,17 +746,6 @@ class FrameController {
|
|
580
746
|
this.formSubmission.start();
|
581
747
|
}
|
582
748
|
}
|
583
|
-
async visit(url) {
|
584
|
-
const location = Location.wrap(url);
|
585
|
-
const request = new FetchRequest(this, FetchMethod.get, location);
|
586
|
-
return new Promise((resolve => {
|
587
|
-
this.resolveVisitPromise = () => {
|
588
|
-
this.resolveVisitPromise = () => {};
|
589
|
-
resolve();
|
590
|
-
};
|
591
|
-
request.perform();
|
592
|
-
}));
|
593
|
-
}
|
594
749
|
additionalHeadersForRequest(request) {
|
595
750
|
return {
|
596
751
|
"Turbo-Frame": this.id
|
@@ -620,13 +775,24 @@ class FrameController {
|
|
620
775
|
formSubmissionStarted(formSubmission) {}
|
621
776
|
formSubmissionSucceededWithResponse(formSubmission, response) {
|
622
777
|
const frame = this.findFrameElement(formSubmission.formElement);
|
623
|
-
frame.
|
778
|
+
frame.delegate.loadResponse(response);
|
624
779
|
}
|
625
780
|
formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
|
626
|
-
this.element.
|
781
|
+
this.element.delegate.loadResponse(fetchResponse);
|
627
782
|
}
|
628
783
|
formSubmissionErrored(formSubmission, error) {}
|
629
784
|
formSubmissionFinished(formSubmission) {}
|
785
|
+
async visit(url) {
|
786
|
+
const location = Location.wrap(url);
|
787
|
+
const request = new FetchRequest(this, FetchMethod.get, location);
|
788
|
+
return new Promise((resolve => {
|
789
|
+
this.resolveVisitPromise = () => {
|
790
|
+
this.resolveVisitPromise = () => {};
|
791
|
+
resolve();
|
792
|
+
};
|
793
|
+
request.perform();
|
794
|
+
}));
|
795
|
+
}
|
630
796
|
navigateFrame(element, url) {
|
631
797
|
const frame = this.findFrameElement(element);
|
632
798
|
frame.src = url;
|
@@ -636,17 +802,6 @@ class FrameController {
|
|
636
802
|
const id = element.getAttribute("data-turbo-frame");
|
637
803
|
return (_a = getFrameElementById(id)) !== null && _a !== void 0 ? _a : this.element;
|
638
804
|
}
|
639
|
-
async loadResponse(response) {
|
640
|
-
const fragment = fragmentFromHTML(await response.responseHTML);
|
641
|
-
const element = await this.extractForeignFrameElement(fragment);
|
642
|
-
if (element) {
|
643
|
-
await nextAnimationFrame();
|
644
|
-
this.loadFrameElement(element);
|
645
|
-
this.scrollFrameIntoView(element);
|
646
|
-
await nextAnimationFrame();
|
647
|
-
this.focusFirstAutofocusableElement();
|
648
|
-
}
|
649
|
-
}
|
650
805
|
async extractForeignFrameElement(container) {
|
651
806
|
let element;
|
652
807
|
const id = CSS.escape(this.id);
|
@@ -657,6 +812,8 @@ class FrameController {
|
|
657
812
|
await element.loaded;
|
658
813
|
return await this.extractForeignFrameElement(element);
|
659
814
|
}
|
815
|
+
console.error(`Response has no matching <turbo-frame id="${id}"> element`);
|
816
|
+
return new FrameElement;
|
660
817
|
}
|
661
818
|
loadFrameElement(frameElement) {
|
662
819
|
var _a;
|
@@ -713,6 +870,18 @@ class FrameController {
|
|
713
870
|
get enabled() {
|
714
871
|
return !this.element.disabled;
|
715
872
|
}
|
873
|
+
get sourceURL() {
|
874
|
+
return this.element.src;
|
875
|
+
}
|
876
|
+
get loadingStyle() {
|
877
|
+
return this.element.loading;
|
878
|
+
}
|
879
|
+
get isLoading() {
|
880
|
+
return this.formSubmission !== undefined || this.loadingURL !== undefined;
|
881
|
+
}
|
882
|
+
get isActive() {
|
883
|
+
return this.element.isActive;
|
884
|
+
}
|
716
885
|
}
|
717
886
|
|
718
887
|
function getFrameElementById(id) {
|
@@ -732,9 +901,11 @@ function readScrollLogicalPosition(value, defaultValue) {
|
|
732
901
|
}
|
733
902
|
}
|
734
903
|
|
735
|
-
function fragmentFromHTML(html
|
736
|
-
|
737
|
-
|
904
|
+
function fragmentFromHTML(html) {
|
905
|
+
if (html) {
|
906
|
+
const foreignDocument = document.implementation.createHTMLDocument();
|
907
|
+
return foreignDocument.createRange().createContextualFragment(html);
|
908
|
+
}
|
738
909
|
}
|
739
910
|
|
740
911
|
function activateElement(element) {
|
@@ -746,76 +917,6 @@ function activateElement(element) {
|
|
746
917
|
}
|
747
918
|
}
|
748
919
|
|
749
|
-
class FrameElement extends HTMLElement {
|
750
|
-
constructor() {
|
751
|
-
super();
|
752
|
-
this.controller = new FrameController(this);
|
753
|
-
}
|
754
|
-
static get observedAttributes() {
|
755
|
-
return [ "src" ];
|
756
|
-
}
|
757
|
-
connectedCallback() {
|
758
|
-
this.controller.connect();
|
759
|
-
}
|
760
|
-
disconnectedCallback() {
|
761
|
-
this.controller.disconnect();
|
762
|
-
}
|
763
|
-
attributeChangedCallback() {
|
764
|
-
if (this.src && this.isActive) {
|
765
|
-
const value = this.controller.visit(this.src);
|
766
|
-
Object.defineProperty(this, "loaded", {
|
767
|
-
value: value,
|
768
|
-
configurable: true
|
769
|
-
});
|
770
|
-
}
|
771
|
-
}
|
772
|
-
formSubmissionIntercepted(element, submitter) {
|
773
|
-
this.controller.formSubmissionIntercepted(element, submitter);
|
774
|
-
}
|
775
|
-
get src() {
|
776
|
-
return this.getAttribute("src");
|
777
|
-
}
|
778
|
-
set src(value) {
|
779
|
-
if (value) {
|
780
|
-
this.setAttribute("src", value);
|
781
|
-
} else {
|
782
|
-
this.removeAttribute("src");
|
783
|
-
}
|
784
|
-
}
|
785
|
-
get loaded() {
|
786
|
-
return Promise.resolve(undefined);
|
787
|
-
}
|
788
|
-
get disabled() {
|
789
|
-
return this.hasAttribute("disabled");
|
790
|
-
}
|
791
|
-
set disabled(value) {
|
792
|
-
if (value) {
|
793
|
-
this.setAttribute("disabled", "");
|
794
|
-
} else {
|
795
|
-
this.removeAttribute("disabled");
|
796
|
-
}
|
797
|
-
}
|
798
|
-
get autoscroll() {
|
799
|
-
return this.hasAttribute("autoscroll");
|
800
|
-
}
|
801
|
-
set autoscroll(value) {
|
802
|
-
if (value) {
|
803
|
-
this.setAttribute("autoscroll", "");
|
804
|
-
} else {
|
805
|
-
this.removeAttribute("autoscroll");
|
806
|
-
}
|
807
|
-
}
|
808
|
-
get isActive() {
|
809
|
-
return this.ownerDocument === document && !this.isPreview;
|
810
|
-
}
|
811
|
-
get isPreview() {
|
812
|
-
var _a, _b;
|
813
|
-
return (_b = (_a = this.ownerDocument) === null || _a === void 0 ? void 0 : _a.documentElement) === null || _b === void 0 ? void 0 : _b.hasAttribute("data-turbo-preview");
|
814
|
-
}
|
815
|
-
}
|
816
|
-
|
817
|
-
customElements.define("turbo-frame", FrameElement);
|
818
|
-
|
819
920
|
const StreamActions = {
|
820
921
|
append() {
|
821
922
|
var _a;
|
@@ -912,6 +1013,10 @@ class StreamElement extends HTMLElement {
|
|
912
1013
|
}
|
913
1014
|
}
|
914
1015
|
|
1016
|
+
FrameElement.delegateConstructor = FrameController;
|
1017
|
+
|
1018
|
+
customElements.define("turbo-frame", FrameElement);
|
1019
|
+
|
915
1020
|
customElements.define("turbo-stream", StreamElement);
|
916
1021
|
|
917
1022
|
(() => {
|
@@ -1633,7 +1738,7 @@ class FrameRedirector {
|
|
1633
1738
|
formSubmissionIntercepted(element, submitter) {
|
1634
1739
|
const frame = this.findFrameElement(element);
|
1635
1740
|
if (frame) {
|
1636
|
-
frame.formSubmissionIntercepted(element, submitter);
|
1741
|
+
frame.delegate.formSubmissionIntercepted(element, submitter);
|
1637
1742
|
}
|
1638
1743
|
}
|
1639
1744
|
shouldRedirect(element, submitter) {
|
@@ -1677,8 +1782,6 @@ class History {
|
|
1677
1782
|
}
|
1678
1783
|
start() {
|
1679
1784
|
if (!this.started) {
|
1680
|
-
this.previousScrollRestoration = history.scrollRestoration;
|
1681
|
-
history.scrollRestoration = "manual";
|
1682
1785
|
addEventListener("popstate", this.onPopState, false);
|
1683
1786
|
addEventListener("load", this.onPageLoad, false);
|
1684
1787
|
this.started = true;
|
@@ -1686,9 +1789,7 @@ class History {
|
|
1686
1789
|
}
|
1687
1790
|
}
|
1688
1791
|
stop() {
|
1689
|
-
var _a;
|
1690
1792
|
if (this.started) {
|
1691
|
-
history.scrollRestoration = (_a = this.previousScrollRestoration) !== null && _a !== void 0 ? _a : "auto";
|
1692
1793
|
removeEventListener("popstate", this.onPopState, false);
|
1693
1794
|
removeEventListener("load", this.onPageLoad, false);
|
1694
1795
|
this.started = false;
|
@@ -1718,6 +1819,19 @@ class History {
|
|
1718
1819
|
const restorationData = this.restorationData[restorationIdentifier];
|
1719
1820
|
this.restorationData[restorationIdentifier] = Object.assign(Object.assign({}, restorationData), additionalData);
|
1720
1821
|
}
|
1822
|
+
assumeControlOfScrollRestoration() {
|
1823
|
+
var _a;
|
1824
|
+
if (!this.previousScrollRestoration) {
|
1825
|
+
this.previousScrollRestoration = (_a = history.scrollRestoration) !== null && _a !== void 0 ? _a : "auto";
|
1826
|
+
history.scrollRestoration = "manual";
|
1827
|
+
}
|
1828
|
+
}
|
1829
|
+
relinquishControlOfScrollRestoration() {
|
1830
|
+
if (this.previousScrollRestoration) {
|
1831
|
+
history.scrollRestoration = this.previousScrollRestoration;
|
1832
|
+
delete this.previousScrollRestoration;
|
1833
|
+
}
|
1834
|
+
}
|
1721
1835
|
shouldHandlePopState() {
|
1722
1836
|
return this.pageIsLoaded();
|
1723
1837
|
}
|
@@ -1864,7 +1978,6 @@ var PageStage;
|
|
1864
1978
|
PageStage[PageStage["loading"] = 1] = "loading";
|
1865
1979
|
PageStage[PageStage["interactive"] = 2] = "interactive";
|
1866
1980
|
PageStage[PageStage["complete"] = 3] = "complete";
|
1867
|
-
PageStage[PageStage["invalidated"] = 4] = "invalidated";
|
1868
1981
|
})(PageStage || (PageStage = {}));
|
1869
1982
|
|
1870
1983
|
class PageObserver {
|
@@ -1879,6 +1992,9 @@ class PageObserver {
|
|
1879
1992
|
this.pageIsComplete();
|
1880
1993
|
}
|
1881
1994
|
};
|
1995
|
+
this.pageWillUnload = () => {
|
1996
|
+
this.delegate.pageWillUnload();
|
1997
|
+
};
|
1882
1998
|
this.delegate = delegate;
|
1883
1999
|
}
|
1884
2000
|
start() {
|
@@ -1887,21 +2003,17 @@ class PageObserver {
|
|
1887
2003
|
this.stage = PageStage.loading;
|
1888
2004
|
}
|
1889
2005
|
document.addEventListener("readystatechange", this.interpretReadyState, false);
|
2006
|
+
addEventListener("pagehide", this.pageWillUnload, false);
|
1890
2007
|
this.started = true;
|
1891
2008
|
}
|
1892
2009
|
}
|
1893
2010
|
stop() {
|
1894
2011
|
if (this.started) {
|
1895
2012
|
document.removeEventListener("readystatechange", this.interpretReadyState, false);
|
2013
|
+
removeEventListener("pagehide", this.pageWillUnload, false);
|
1896
2014
|
this.started = false;
|
1897
2015
|
}
|
1898
2016
|
}
|
1899
|
-
invalidate() {
|
1900
|
-
if (this.stage != PageStage.invalidated) {
|
1901
|
-
this.stage = PageStage.invalidated;
|
1902
|
-
this.delegate.pageInvalidated();
|
1903
|
-
}
|
1904
|
-
}
|
1905
2017
|
pageIsInteractive() {
|
1906
2018
|
if (this.stage == PageStage.loading) {
|
1907
2019
|
this.stage = PageStage.interactive;
|
@@ -1991,7 +2103,7 @@ class StreamObserver {
|
|
1991
2103
|
const fetchOptions = (_a = event.detail) === null || _a === void 0 ? void 0 : _a.fetchOptions;
|
1992
2104
|
if (fetchOptions) {
|
1993
2105
|
const {headers: headers} = fetchOptions;
|
1994
|
-
headers.Accept = [ "text/
|
2106
|
+
headers.Accept = [ "text/vnd.turbo-stream.html", headers.Accept ].join(", ");
|
1995
2107
|
}
|
1996
2108
|
};
|
1997
2109
|
this.inspectFetchResponse = event => {
|
@@ -2059,7 +2171,7 @@ function fetchResponseFromEvent(event) {
|
|
2059
2171
|
function fetchResponseIsStream(response) {
|
2060
2172
|
var _a;
|
2061
2173
|
const contentType = (_a = response.contentType) !== null && _a !== void 0 ? _a : "";
|
2062
|
-
return
|
2174
|
+
return /^text\/vnd\.turbo-stream\.html\b/.test(contentType);
|
2063
2175
|
}
|
2064
2176
|
|
2065
2177
|
function isAction(action) {
|
@@ -2518,9 +2630,11 @@ class Session {
|
|
2518
2630
|
this.view.lastRenderedLocation = this.location;
|
2519
2631
|
this.notifyApplicationAfterPageLoad();
|
2520
2632
|
}
|
2521
|
-
pageLoaded() {
|
2522
|
-
|
2523
|
-
|
2633
|
+
pageLoaded() {
|
2634
|
+
this.history.assumeControlOfScrollRestoration();
|
2635
|
+
}
|
2636
|
+
pageWillUnload() {
|
2637
|
+
this.history.relinquishControlOfScrollRestoration();
|
2524
2638
|
}
|
2525
2639
|
receivedMessageFromStream(message) {
|
2526
2640
|
this.renderStreamMessage(message);
|
@@ -2533,7 +2647,7 @@ class Session {
|
|
2533
2647
|
this.notifyApplicationAfterRender();
|
2534
2648
|
}
|
2535
2649
|
viewInvalidated() {
|
2536
|
-
this.
|
2650
|
+
this.adapter.pageInvalidated();
|
2537
2651
|
}
|
2538
2652
|
viewWillCacheSnapshot() {
|
2539
2653
|
this.notifyApplicationBeforeCachingSnapshot();
|