stimulus-rails 1.0.4 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/stimulus.js +244 -158
- data/app/assets/javascripts/stimulus.min.js +1 -1
- data/app/assets/javascripts/stimulus.min.js.map +1 -1
- data/lib/stimulus/engine.rb +9 -1
- data/lib/stimulus/manifest.rb +1 -1
- data/lib/stimulus/version.rb +1 -1
- metadata +3 -3
@@ -3,8 +3,8 @@
|
|
3
3
|
//= link ./stimulus-loading.js
|
4
4
|
|
5
5
|
/*
|
6
|
-
Stimulus 3.
|
7
|
-
Copyright ©
|
6
|
+
Stimulus 3.1.1
|
7
|
+
Copyright © 2022 Basecamp, LLC
|
8
8
|
*/
|
9
9
|
class EventListener {
|
10
10
|
constructor(eventTarget, eventName, eventOptions) {
|
@@ -36,6 +36,9 @@ class EventListener {
|
|
36
36
|
}
|
37
37
|
}
|
38
38
|
}
|
39
|
+
hasBindings() {
|
40
|
+
return this.unorderedBindings.size > 0;
|
41
|
+
}
|
39
42
|
get bindings() {
|
40
43
|
return Array.from(this.unorderedBindings).sort((left, right) => {
|
41
44
|
const leftIndex = left.index, rightIndex = right.index;
|
@@ -54,7 +57,7 @@ function extendEvent(event) {
|
|
54
57
|
stopImmediatePropagation() {
|
55
58
|
this.immediatePropagationStopped = true;
|
56
59
|
stopImmediatePropagation.call(this);
|
57
|
-
}
|
60
|
+
},
|
58
61
|
});
|
59
62
|
}
|
60
63
|
}
|
@@ -62,34 +65,50 @@ function extendEvent(event) {
|
|
62
65
|
class Dispatcher {
|
63
66
|
constructor(application) {
|
64
67
|
this.application = application;
|
65
|
-
this.eventListenerMaps = new Map;
|
68
|
+
this.eventListenerMaps = new Map();
|
66
69
|
this.started = false;
|
67
70
|
}
|
68
71
|
start() {
|
69
72
|
if (!this.started) {
|
70
73
|
this.started = true;
|
71
|
-
this.eventListeners.forEach(eventListener => eventListener.connect());
|
74
|
+
this.eventListeners.forEach((eventListener) => eventListener.connect());
|
72
75
|
}
|
73
76
|
}
|
74
77
|
stop() {
|
75
78
|
if (this.started) {
|
76
79
|
this.started = false;
|
77
|
-
this.eventListeners.forEach(eventListener => eventListener.disconnect());
|
80
|
+
this.eventListeners.forEach((eventListener) => eventListener.disconnect());
|
78
81
|
}
|
79
82
|
}
|
80
83
|
get eventListeners() {
|
81
|
-
return Array.from(this.eventListenerMaps.values())
|
82
|
-
.reduce((listeners, map) => listeners.concat(Array.from(map.values())), []);
|
84
|
+
return Array.from(this.eventListenerMaps.values()).reduce((listeners, map) => listeners.concat(Array.from(map.values())), []);
|
83
85
|
}
|
84
86
|
bindingConnected(binding) {
|
85
87
|
this.fetchEventListenerForBinding(binding).bindingConnected(binding);
|
86
88
|
}
|
87
|
-
bindingDisconnected(binding) {
|
89
|
+
bindingDisconnected(binding, clearEventListeners = false) {
|
88
90
|
this.fetchEventListenerForBinding(binding).bindingDisconnected(binding);
|
91
|
+
if (clearEventListeners)
|
92
|
+
this.clearEventListenersForBinding(binding);
|
89
93
|
}
|
90
94
|
handleError(error, message, detail = {}) {
|
91
95
|
this.application.handleError(error, `Error ${message}`, detail);
|
92
96
|
}
|
97
|
+
clearEventListenersForBinding(binding) {
|
98
|
+
const eventListener = this.fetchEventListenerForBinding(binding);
|
99
|
+
if (!eventListener.hasBindings()) {
|
100
|
+
eventListener.disconnect();
|
101
|
+
this.removeMappedEventListenerFor(binding);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
removeMappedEventListenerFor(binding) {
|
105
|
+
const { eventTarget, eventName, eventOptions } = binding;
|
106
|
+
const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget);
|
107
|
+
const cacheKey = this.cacheKey(eventName, eventOptions);
|
108
|
+
eventListenerMap.delete(cacheKey);
|
109
|
+
if (eventListenerMap.size == 0)
|
110
|
+
this.eventListenerMaps.delete(eventTarget);
|
111
|
+
}
|
93
112
|
fetchEventListenerForBinding(binding) {
|
94
113
|
const { eventTarget, eventName, eventOptions } = binding;
|
95
114
|
return this.fetchEventListener(eventTarget, eventName, eventOptions);
|
@@ -114,20 +133,42 @@ class Dispatcher {
|
|
114
133
|
fetchEventListenerMapForEventTarget(eventTarget) {
|
115
134
|
let eventListenerMap = this.eventListenerMaps.get(eventTarget);
|
116
135
|
if (!eventListenerMap) {
|
117
|
-
eventListenerMap = new Map;
|
136
|
+
eventListenerMap = new Map();
|
118
137
|
this.eventListenerMaps.set(eventTarget, eventListenerMap);
|
119
138
|
}
|
120
139
|
return eventListenerMap;
|
121
140
|
}
|
122
141
|
cacheKey(eventName, eventOptions) {
|
123
142
|
const parts = [eventName];
|
124
|
-
Object.keys(eventOptions)
|
143
|
+
Object.keys(eventOptions)
|
144
|
+
.sort()
|
145
|
+
.forEach((key) => {
|
125
146
|
parts.push(`${eventOptions[key] ? "" : "!"}${key}`);
|
126
147
|
});
|
127
148
|
return parts.join(":");
|
128
149
|
}
|
129
150
|
}
|
130
151
|
|
152
|
+
const defaultActionDescriptorFilters = {
|
153
|
+
stop({ event, value }) {
|
154
|
+
if (value)
|
155
|
+
event.stopPropagation();
|
156
|
+
return true;
|
157
|
+
},
|
158
|
+
prevent({ event, value }) {
|
159
|
+
if (value)
|
160
|
+
event.preventDefault();
|
161
|
+
return true;
|
162
|
+
},
|
163
|
+
self({ event, value, element }) {
|
164
|
+
if (value) {
|
165
|
+
return element === event.target;
|
166
|
+
}
|
167
|
+
else {
|
168
|
+
return true;
|
169
|
+
}
|
170
|
+
},
|
171
|
+
};
|
131
172
|
const descriptorPattern = /^((.+?)(@(window|document))?->)?(.+?)(#([^:]+?))(:(.+))?$/;
|
132
173
|
function parseActionDescriptorString(descriptorString) {
|
133
174
|
const source = descriptorString.trim();
|
@@ -137,7 +178,7 @@ function parseActionDescriptorString(descriptorString) {
|
|
137
178
|
eventName: matches[2],
|
138
179
|
eventOptions: matches[9] ? parseEventOptions(matches[9]) : {},
|
139
180
|
identifier: matches[5],
|
140
|
-
methodName: matches[7]
|
181
|
+
methodName: matches[7],
|
141
182
|
};
|
142
183
|
}
|
143
184
|
function parseEventTarget(eventTargetName) {
|
@@ -149,7 +190,9 @@ function parseEventTarget(eventTargetName) {
|
|
149
190
|
}
|
150
191
|
}
|
151
192
|
function parseEventOptions(eventOptions) {
|
152
|
-
return eventOptions
|
193
|
+
return eventOptions
|
194
|
+
.split(":")
|
195
|
+
.reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {});
|
153
196
|
}
|
154
197
|
function stringifyEventTarget(eventTarget) {
|
155
198
|
if (eventTarget == window) {
|
@@ -191,24 +234,15 @@ class Action {
|
|
191
234
|
return `${this.eventName}${eventNameSuffix}->${this.identifier}#${this.methodName}`;
|
192
235
|
}
|
193
236
|
get params() {
|
194
|
-
if (this.eventTarget instanceof Element) {
|
195
|
-
return this.getParamsFromEventTargetAttributes(this.eventTarget);
|
196
|
-
}
|
197
|
-
else {
|
198
|
-
return {};
|
199
|
-
}
|
200
|
-
}
|
201
|
-
getParamsFromEventTargetAttributes(eventTarget) {
|
202
237
|
const params = {};
|
203
|
-
const pattern = new RegExp(`^data-${this.identifier}-(.+)-param
|
204
|
-
const
|
205
|
-
attributes.forEach(({ name, value }) => {
|
238
|
+
const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i");
|
239
|
+
for (const { name, value } of Array.from(this.element.attributes)) {
|
206
240
|
const match = name.match(pattern);
|
207
241
|
const key = match && match[1];
|
208
242
|
if (key) {
|
209
|
-
|
243
|
+
params[camelize(key)] = typecast(value);
|
210
244
|
}
|
211
|
-
}
|
245
|
+
}
|
212
246
|
return params;
|
213
247
|
}
|
214
248
|
get eventTargetName() {
|
@@ -216,13 +250,13 @@ class Action {
|
|
216
250
|
}
|
217
251
|
}
|
218
252
|
const defaultEventNames = {
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
253
|
+
a: () => "click",
|
254
|
+
button: () => "click",
|
255
|
+
form: () => "submit",
|
256
|
+
details: () => "toggle",
|
257
|
+
input: (e) => (e.getAttribute("type") == "submit" ? "click" : "input"),
|
258
|
+
select: () => "change",
|
259
|
+
textarea: () => "input",
|
226
260
|
};
|
227
261
|
function getDefaultEventNameForElement(element) {
|
228
262
|
const tagName = element.tagName.toLowerCase();
|
@@ -260,7 +294,7 @@ class Binding {
|
|
260
294
|
return this.context.identifier;
|
261
295
|
}
|
262
296
|
handleEvent(event) {
|
263
|
-
if (this.willBeInvokedByEvent(event)) {
|
297
|
+
if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(event)) {
|
264
298
|
this.invokeWithEvent(event);
|
265
299
|
}
|
266
300
|
}
|
@@ -274,6 +308,21 @@ class Binding {
|
|
274
308
|
}
|
275
309
|
throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`);
|
276
310
|
}
|
311
|
+
applyEventModifiers(event) {
|
312
|
+
const { element } = this.action;
|
313
|
+
const { actionDescriptorFilters } = this.context.application;
|
314
|
+
let passes = true;
|
315
|
+
for (const [name, value] of Object.entries(this.eventOptions)) {
|
316
|
+
if (name in actionDescriptorFilters) {
|
317
|
+
const filter = actionDescriptorFilters[name];
|
318
|
+
passes = passes && filter({ name, value, event, element });
|
319
|
+
}
|
320
|
+
else {
|
321
|
+
continue;
|
322
|
+
}
|
323
|
+
}
|
324
|
+
return passes;
|
325
|
+
}
|
277
326
|
invokeWithEvent(event) {
|
278
327
|
const { target, currentTarget } = event;
|
279
328
|
try {
|
@@ -320,7 +369,7 @@ class ElementObserver {
|
|
320
369
|
this.element = element;
|
321
370
|
this.started = false;
|
322
371
|
this.delegate = delegate;
|
323
|
-
this.elements = new Set;
|
372
|
+
this.elements = new Set();
|
324
373
|
this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
|
325
374
|
}
|
326
375
|
start() {
|
@@ -508,8 +557,8 @@ class StringMapObserver {
|
|
508
557
|
this.element = element;
|
509
558
|
this.delegate = delegate;
|
510
559
|
this.started = false;
|
511
|
-
this.stringMap = new Map;
|
512
|
-
this.mutationObserver = new MutationObserver(mutations => this.processMutations(mutations));
|
560
|
+
this.stringMap = new Map();
|
561
|
+
this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
|
513
562
|
}
|
514
563
|
start() {
|
515
564
|
if (!this.started) {
|
@@ -585,7 +634,7 @@ class StringMapObserver {
|
|
585
634
|
return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)));
|
586
635
|
}
|
587
636
|
get currentAttributeNames() {
|
588
|
-
return Array.from(this.element.attributes).map(attribute => attribute.name);
|
637
|
+
return Array.from(this.element.attributes).map((attribute) => attribute.name);
|
589
638
|
}
|
590
639
|
get recordedAttributeNames() {
|
591
640
|
return Array.from(this.stringMap.keys());
|
@@ -644,7 +693,7 @@ class Multimap {
|
|
644
693
|
}
|
645
694
|
hasValue(value) {
|
646
695
|
const sets = Array.from(this.valuesByKey.values());
|
647
|
-
return sets.some(set => set.has(value));
|
696
|
+
return sets.some((set) => set.has(value));
|
648
697
|
}
|
649
698
|
getValuesForKey(key) {
|
650
699
|
const values = this.valuesByKey.get(key);
|
@@ -652,15 +701,15 @@ class Multimap {
|
|
652
701
|
}
|
653
702
|
getKeysForValue(value) {
|
654
703
|
return Array.from(this.valuesByKey)
|
655
|
-
.filter(([
|
656
|
-
.map(([key,
|
704
|
+
.filter(([_key, values]) => values.has(value))
|
705
|
+
.map(([key, _values]) => key);
|
657
706
|
}
|
658
707
|
}
|
659
708
|
|
660
709
|
class IndexedMultimap extends Multimap {
|
661
710
|
constructor() {
|
662
711
|
super();
|
663
|
-
this.keysByValue = new Map;
|
712
|
+
this.keysByValue = new Map();
|
664
713
|
}
|
665
714
|
get values() {
|
666
715
|
return Array.from(this.keysByValue.keys());
|
@@ -686,7 +735,7 @@ class TokenListObserver {
|
|
686
735
|
constructor(element, attributeName, delegate) {
|
687
736
|
this.attributeObserver = new AttributeObserver(element, attributeName, this);
|
688
737
|
this.delegate = delegate;
|
689
|
-
this.tokensByElement = new Multimap;
|
738
|
+
this.tokensByElement = new Multimap();
|
690
739
|
}
|
691
740
|
get started() {
|
692
741
|
return this.attributeObserver.started;
|
@@ -721,10 +770,10 @@ class TokenListObserver {
|
|
721
770
|
this.tokensUnmatched(this.tokensByElement.getValuesForKey(element));
|
722
771
|
}
|
723
772
|
tokensMatched(tokens) {
|
724
|
-
tokens.forEach(token => this.tokenMatched(token));
|
773
|
+
tokens.forEach((token) => this.tokenMatched(token));
|
725
774
|
}
|
726
775
|
tokensUnmatched(tokens) {
|
727
|
-
tokens.forEach(token => this.tokenUnmatched(token));
|
776
|
+
tokens.forEach((token) => this.tokenUnmatched(token));
|
728
777
|
}
|
729
778
|
tokenMatched(token) {
|
730
779
|
this.delegate.tokenMatched(token);
|
@@ -737,8 +786,7 @@ class TokenListObserver {
|
|
737
786
|
refreshTokensForElement(element) {
|
738
787
|
const previousTokens = this.tokensByElement.getValuesForKey(element);
|
739
788
|
const currentTokens = this.readTokensForElement(element);
|
740
|
-
const firstDifferingIndex = zip(previousTokens, currentTokens)
|
741
|
-
.findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
|
789
|
+
const firstDifferingIndex = zip(previousTokens, currentTokens).findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
|
742
790
|
if (firstDifferingIndex == -1) {
|
743
791
|
return [[], []];
|
744
792
|
}
|
@@ -753,7 +801,10 @@ class TokenListObserver {
|
|
753
801
|
}
|
754
802
|
}
|
755
803
|
function parseTokenString(tokenString, element, attributeName) {
|
756
|
-
return tokenString
|
804
|
+
return tokenString
|
805
|
+
.trim()
|
806
|
+
.split(/\s+/)
|
807
|
+
.filter((content) => content.length)
|
757
808
|
.map((content, index) => ({ element, attributeName, content, index }));
|
758
809
|
}
|
759
810
|
function zip(left, right) {
|
@@ -768,8 +819,8 @@ class ValueListObserver {
|
|
768
819
|
constructor(element, attributeName, delegate) {
|
769
820
|
this.tokenListObserver = new TokenListObserver(element, attributeName, this);
|
770
821
|
this.delegate = delegate;
|
771
|
-
this.parseResultsByToken = new WeakMap;
|
772
|
-
this.valuesByTokenByElement = new WeakMap;
|
822
|
+
this.parseResultsByToken = new WeakMap();
|
823
|
+
this.valuesByTokenByElement = new WeakMap();
|
773
824
|
}
|
774
825
|
get started() {
|
775
826
|
return this.tokenListObserver.started;
|
@@ -816,7 +867,7 @@ class ValueListObserver {
|
|
816
867
|
fetchValuesByTokenForElement(element) {
|
817
868
|
let valuesByToken = this.valuesByTokenByElement.get(element);
|
818
869
|
if (!valuesByToken) {
|
819
|
-
valuesByToken = new Map;
|
870
|
+
valuesByToken = new Map();
|
820
871
|
this.valuesByTokenByElement.set(element, valuesByToken);
|
821
872
|
}
|
822
873
|
return valuesByToken;
|
@@ -836,7 +887,7 @@ class BindingObserver {
|
|
836
887
|
constructor(context, delegate) {
|
837
888
|
this.context = context;
|
838
889
|
this.delegate = delegate;
|
839
|
-
this.bindingsByAction = new Map;
|
890
|
+
this.bindingsByAction = new Map();
|
840
891
|
}
|
841
892
|
start() {
|
842
893
|
if (!this.valueListObserver) {
|
@@ -879,7 +930,7 @@ class BindingObserver {
|
|
879
930
|
}
|
880
931
|
}
|
881
932
|
disconnectAllActions() {
|
882
|
-
this.bindings.forEach(binding => this.delegate.bindingDisconnected(binding));
|
933
|
+
this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding, true));
|
883
934
|
this.bindingsByAction.clear();
|
884
935
|
}
|
885
936
|
parseValueForToken(token) {
|
@@ -902,10 +953,10 @@ class ValueObserver {
|
|
902
953
|
this.receiver = receiver;
|
903
954
|
this.stringMapObserver = new StringMapObserver(this.element, this);
|
904
955
|
this.valueDescriptorMap = this.controller.valueDescriptorMap;
|
905
|
-
this.invokeChangedCallbacksForDefaultValues();
|
906
956
|
}
|
907
957
|
start() {
|
908
958
|
this.stringMapObserver.start();
|
959
|
+
this.invokeChangedCallbacksForDefaultValues();
|
909
960
|
}
|
910
961
|
stop() {
|
911
962
|
this.stringMapObserver.stop();
|
@@ -957,21 +1008,29 @@ class ValueObserver {
|
|
957
1008
|
const changedMethod = this.receiver[changedMethodName];
|
958
1009
|
if (typeof changedMethod == "function") {
|
959
1010
|
const descriptor = this.valueDescriptorNameMap[name];
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
1011
|
+
try {
|
1012
|
+
const value = descriptor.reader(rawValue);
|
1013
|
+
let oldValue = rawOldValue;
|
1014
|
+
if (rawOldValue) {
|
1015
|
+
oldValue = descriptor.reader(rawOldValue);
|
1016
|
+
}
|
1017
|
+
changedMethod.call(this.receiver, value, oldValue);
|
1018
|
+
}
|
1019
|
+
catch (error) {
|
1020
|
+
if (error instanceof TypeError) {
|
1021
|
+
error.message = `Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error.message}`;
|
1022
|
+
}
|
1023
|
+
throw error;
|
964
1024
|
}
|
965
|
-
changedMethod.call(this.receiver, value, oldValue);
|
966
1025
|
}
|
967
1026
|
}
|
968
1027
|
get valueDescriptors() {
|
969
1028
|
const { valueDescriptorMap } = this;
|
970
|
-
return Object.keys(valueDescriptorMap).map(key => valueDescriptorMap[key]);
|
1029
|
+
return Object.keys(valueDescriptorMap).map((key) => valueDescriptorMap[key]);
|
971
1030
|
}
|
972
1031
|
get valueDescriptorNameMap() {
|
973
1032
|
const descriptors = {};
|
974
|
-
Object.keys(this.valueDescriptorMap).forEach(key => {
|
1033
|
+
Object.keys(this.valueDescriptorMap).forEach((key) => {
|
975
1034
|
const descriptor = this.valueDescriptorMap[key];
|
976
1035
|
descriptors[descriptor.name] = descriptor;
|
977
1036
|
});
|
@@ -988,7 +1047,7 @@ class TargetObserver {
|
|
988
1047
|
constructor(context, delegate) {
|
989
1048
|
this.context = context;
|
990
1049
|
this.delegate = delegate;
|
991
|
-
this.targetsByName = new Multimap;
|
1050
|
+
this.targetsByName = new Multimap();
|
992
1051
|
}
|
993
1052
|
start() {
|
994
1053
|
if (!this.tokenListObserver) {
|
@@ -1128,9 +1187,9 @@ class Context {
|
|
1128
1187
|
function readInheritableStaticArrayValues(constructor, propertyName) {
|
1129
1188
|
const ancestors = getAncestorsForConstructor(constructor);
|
1130
1189
|
return Array.from(ancestors.reduce((values, constructor) => {
|
1131
|
-
getOwnStaticArrayValues(constructor, propertyName).forEach(name => values.add(name));
|
1190
|
+
getOwnStaticArrayValues(constructor, propertyName).forEach((name) => values.add(name));
|
1132
1191
|
return values;
|
1133
|
-
}, new Set));
|
1192
|
+
}, new Set()));
|
1134
1193
|
}
|
1135
1194
|
function readInheritableStaticObjectPairs(constructor, propertyName) {
|
1136
1195
|
const ancestors = getAncestorsForConstructor(constructor);
|
@@ -1153,7 +1212,7 @@ function getOwnStaticArrayValues(constructor, propertyName) {
|
|
1153
1212
|
}
|
1154
1213
|
function getOwnStaticObjectPairs(constructor, propertyName) {
|
1155
1214
|
const definition = constructor[propertyName];
|
1156
|
-
return definition ? Object.keys(definition).map(key => [key, definition[key]]) : [];
|
1215
|
+
return definition ? Object.keys(definition).map((key) => [key, definition[key]]) : [];
|
1157
1216
|
}
|
1158
1217
|
|
1159
1218
|
function bless(constructor) {
|
@@ -1199,10 +1258,7 @@ function getShadowedDescriptor(prototype, properties, key) {
|
|
1199
1258
|
}
|
1200
1259
|
const getOwnKeys = (() => {
|
1201
1260
|
if (typeof Object.getOwnPropertySymbols == "function") {
|
1202
|
-
return (object) => [
|
1203
|
-
...Object.getOwnPropertyNames(object),
|
1204
|
-
...Object.getOwnPropertySymbols(object)
|
1205
|
-
];
|
1261
|
+
return (object) => [...Object.getOwnPropertyNames(object), ...Object.getOwnPropertySymbols(object)];
|
1206
1262
|
}
|
1207
1263
|
else {
|
1208
1264
|
return Object.getOwnPropertyNames;
|
@@ -1214,16 +1270,18 @@ const extend = (() => {
|
|
1214
1270
|
return Reflect.construct(constructor, arguments, new.target);
|
1215
1271
|
}
|
1216
1272
|
extended.prototype = Object.create(constructor.prototype, {
|
1217
|
-
constructor: { value: extended }
|
1273
|
+
constructor: { value: extended },
|
1218
1274
|
});
|
1219
1275
|
Reflect.setPrototypeOf(extended, constructor);
|
1220
1276
|
return extended;
|
1221
1277
|
}
|
1222
1278
|
function testReflectExtension() {
|
1223
|
-
const a = function () {
|
1279
|
+
const a = function () {
|
1280
|
+
this.a.call(this);
|
1281
|
+
};
|
1224
1282
|
const b = extendWithReflect(a);
|
1225
1283
|
b.prototype.a = function () { };
|
1226
|
-
return new b;
|
1284
|
+
return new b();
|
1227
1285
|
}
|
1228
1286
|
try {
|
1229
1287
|
testReflectExtension();
|
@@ -1238,7 +1296,7 @@ const extend = (() => {
|
|
1238
1296
|
function blessDefinition(definition) {
|
1239
1297
|
return {
|
1240
1298
|
identifier: definition.identifier,
|
1241
|
-
controllerConstructor: bless(definition.controllerConstructor)
|
1299
|
+
controllerConstructor: bless(definition.controllerConstructor),
|
1242
1300
|
};
|
1243
1301
|
}
|
1244
1302
|
|
@@ -1246,8 +1304,8 @@ class Module {
|
|
1246
1304
|
constructor(application, definition) {
|
1247
1305
|
this.application = application;
|
1248
1306
|
this.definition = blessDefinition(definition);
|
1249
|
-
this.contextsByScope = new WeakMap;
|
1250
|
-
this.connectedContexts = new Set;
|
1307
|
+
this.contextsByScope = new WeakMap();
|
1308
|
+
this.connectedContexts = new Set();
|
1251
1309
|
}
|
1252
1310
|
get identifier() {
|
1253
1311
|
return this.definition.identifier;
|
@@ -1345,13 +1403,13 @@ class DataMap {
|
|
1345
1403
|
|
1346
1404
|
class Guide {
|
1347
1405
|
constructor(logger) {
|
1348
|
-
this.warnedKeysByObject = new WeakMap;
|
1406
|
+
this.warnedKeysByObject = new WeakMap();
|
1349
1407
|
this.logger = logger;
|
1350
1408
|
}
|
1351
1409
|
warn(object, key, message) {
|
1352
1410
|
let warnedKeys = this.warnedKeysByObject.get(object);
|
1353
1411
|
if (!warnedKeys) {
|
1354
|
-
warnedKeys = new Set;
|
1412
|
+
warnedKeys = new Set();
|
1355
1413
|
this.warnedKeysByObject.set(object, warnedKeys);
|
1356
1414
|
}
|
1357
1415
|
if (!warnedKeys.has(key)) {
|
@@ -1382,15 +1440,13 @@ class TargetSet {
|
|
1382
1440
|
return this.find(targetName) != null;
|
1383
1441
|
}
|
1384
1442
|
find(...targetNames) {
|
1385
|
-
return targetNames.reduce((target, targetName) => target
|
1386
|
-
|| this.findTarget(targetName)
|
1387
|
-
|| this.findLegacyTarget(targetName), undefined);
|
1443
|
+
return targetNames.reduce((target, targetName) => target || this.findTarget(targetName) || this.findLegacyTarget(targetName), undefined);
|
1388
1444
|
}
|
1389
1445
|
findAll(...targetNames) {
|
1390
1446
|
return targetNames.reduce((targets, targetName) => [
|
1391
1447
|
...targets,
|
1392
1448
|
...this.findAllTargets(targetName),
|
1393
|
-
...this.findAllLegacyTargets(targetName)
|
1449
|
+
...this.findAllLegacyTargets(targetName),
|
1394
1450
|
], []);
|
1395
1451
|
}
|
1396
1452
|
findTarget(targetName) {
|
@@ -1411,7 +1467,7 @@ class TargetSet {
|
|
1411
1467
|
}
|
1412
1468
|
findAllLegacyTargets(targetName) {
|
1413
1469
|
const selector = this.getLegacySelectorForTargetName(targetName);
|
1414
|
-
return this.scope.findAllElements(selector).map(element => this.deprecate(element, targetName));
|
1470
|
+
return this.scope.findAllElements(selector).map((element) => this.deprecate(element, targetName));
|
1415
1471
|
}
|
1416
1472
|
getLegacySelectorForTargetName(targetName) {
|
1417
1473
|
const targetDescriptor = `${this.identifier}.${targetName}`;
|
@@ -1446,14 +1502,12 @@ class Scope {
|
|
1446
1502
|
this.guide = new Guide(logger);
|
1447
1503
|
}
|
1448
1504
|
findElement(selector) {
|
1449
|
-
return this.element.matches(selector)
|
1450
|
-
? this.element
|
1451
|
-
: this.queryElements(selector).find(this.containsElement);
|
1505
|
+
return this.element.matches(selector) ? this.element : this.queryElements(selector).find(this.containsElement);
|
1452
1506
|
}
|
1453
1507
|
findAllElements(selector) {
|
1454
1508
|
return [
|
1455
|
-
...this.element.matches(selector) ? [this.element] : [],
|
1456
|
-
...this.queryElements(selector).filter(this.containsElement)
|
1509
|
+
...(this.element.matches(selector) ? [this.element] : []),
|
1510
|
+
...this.queryElements(selector).filter(this.containsElement),
|
1457
1511
|
];
|
1458
1512
|
}
|
1459
1513
|
queryElements(selector) {
|
@@ -1470,8 +1524,8 @@ class ScopeObserver {
|
|
1470
1524
|
this.schema = schema;
|
1471
1525
|
this.delegate = delegate;
|
1472
1526
|
this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this);
|
1473
|
-
this.scopesByIdentifierByElement = new WeakMap;
|
1474
|
-
this.scopeReferenceCounts = new WeakMap;
|
1527
|
+
this.scopesByIdentifierByElement = new WeakMap();
|
1528
|
+
this.scopeReferenceCounts = new WeakMap();
|
1475
1529
|
}
|
1476
1530
|
start() {
|
1477
1531
|
this.valueListObserver.start();
|
@@ -1511,7 +1565,7 @@ class ScopeObserver {
|
|
1511
1565
|
fetchScopesByIdentifierForElement(element) {
|
1512
1566
|
let scopesByIdentifier = this.scopesByIdentifierByElement.get(element);
|
1513
1567
|
if (!scopesByIdentifier) {
|
1514
|
-
scopesByIdentifier = new Map;
|
1568
|
+
scopesByIdentifier = new Map();
|
1515
1569
|
this.scopesByIdentifierByElement.set(element, scopesByIdentifier);
|
1516
1570
|
}
|
1517
1571
|
return scopesByIdentifier;
|
@@ -1522,8 +1576,8 @@ class Router {
|
|
1522
1576
|
constructor(application) {
|
1523
1577
|
this.application = application;
|
1524
1578
|
this.scopeObserver = new ScopeObserver(this.element, this.schema, this);
|
1525
|
-
this.scopesByIdentifier = new Multimap;
|
1526
|
-
this.modulesByIdentifier = new Map;
|
1579
|
+
this.scopesByIdentifier = new Multimap();
|
1580
|
+
this.modulesByIdentifier = new Map();
|
1527
1581
|
}
|
1528
1582
|
get element() {
|
1529
1583
|
return this.application.element;
|
@@ -1563,7 +1617,7 @@ class Router {
|
|
1563
1617
|
getContextForElementAndIdentifier(element, identifier) {
|
1564
1618
|
const module = this.modulesByIdentifier.get(identifier);
|
1565
1619
|
if (module) {
|
1566
|
-
return module.contexts.find(context => context.element == element);
|
1620
|
+
return module.contexts.find((context) => context.element == element);
|
1567
1621
|
}
|
1568
1622
|
}
|
1569
1623
|
handleError(error, message, detail) {
|
@@ -1589,12 +1643,12 @@ class Router {
|
|
1589
1643
|
connectModule(module) {
|
1590
1644
|
this.modulesByIdentifier.set(module.identifier, module);
|
1591
1645
|
const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
|
1592
|
-
scopes.forEach(scope => module.connectContextForScope(scope));
|
1646
|
+
scopes.forEach((scope) => module.connectContextForScope(scope));
|
1593
1647
|
}
|
1594
1648
|
disconnectModule(module) {
|
1595
1649
|
this.modulesByIdentifier.delete(module.identifier);
|
1596
1650
|
const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
|
1597
|
-
scopes.forEach(scope => module.disconnectContextForScope(scope));
|
1651
|
+
scopes.forEach((scope) => module.disconnectContextForScope(scope));
|
1598
1652
|
}
|
1599
1653
|
}
|
1600
1654
|
|
@@ -1602,7 +1656,7 @@ const defaultSchema = {
|
|
1602
1656
|
controllerAttribute: "data-controller",
|
1603
1657
|
actionAttribute: "data-action",
|
1604
1658
|
targetAttribute: "data-target",
|
1605
|
-
targetAttributeForScope: identifier => `data-${identifier}-target
|
1659
|
+
targetAttributeForScope: (identifier) => `data-${identifier}-target`,
|
1606
1660
|
};
|
1607
1661
|
|
1608
1662
|
class Application {
|
@@ -1618,6 +1672,7 @@ class Application {
|
|
1618
1672
|
this.schema = schema;
|
1619
1673
|
this.dispatcher = new Dispatcher(this);
|
1620
1674
|
this.router = new Router(this);
|
1675
|
+
this.actionDescriptorFilters = Object.assign({}, defaultActionDescriptorFilters);
|
1621
1676
|
}
|
1622
1677
|
static start(element, schema) {
|
1623
1678
|
const application = new Application(element, schema);
|
@@ -1638,20 +1693,25 @@ class Application {
|
|
1638
1693
|
this.logDebugActivity("application", "stop");
|
1639
1694
|
}
|
1640
1695
|
register(identifier, controllerConstructor) {
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1696
|
+
this.load({ identifier, controllerConstructor });
|
1697
|
+
}
|
1698
|
+
registerActionOption(name, filter) {
|
1699
|
+
this.actionDescriptorFilters[name] = filter;
|
1644
1700
|
}
|
1645
1701
|
load(head, ...rest) {
|
1646
1702
|
const definitions = Array.isArray(head) ? head : [head, ...rest];
|
1647
|
-
definitions.forEach(definition =>
|
1703
|
+
definitions.forEach((definition) => {
|
1704
|
+
if (definition.controllerConstructor.shouldLoad) {
|
1705
|
+
this.router.loadDefinition(definition);
|
1706
|
+
}
|
1707
|
+
});
|
1648
1708
|
}
|
1649
1709
|
unload(head, ...rest) {
|
1650
1710
|
const identifiers = Array.isArray(head) ? head : [head, ...rest];
|
1651
|
-
identifiers.forEach(identifier => this.router.unloadIdentifier(identifier));
|
1711
|
+
identifiers.forEach((identifier) => this.router.unloadIdentifier(identifier));
|
1652
1712
|
}
|
1653
1713
|
get controllers() {
|
1654
|
-
return this.router.contexts.map(context => context.controller);
|
1714
|
+
return this.router.contexts.map((context) => context.controller);
|
1655
1715
|
}
|
1656
1716
|
getControllerForElementAndIdentifier(element, identifier) {
|
1657
1717
|
const context = this.router.getContextForElementAndIdentifier(element, identifier);
|
@@ -1670,7 +1730,7 @@ class Application {
|
|
1670
1730
|
}
|
1671
1731
|
}
|
1672
1732
|
function domReady() {
|
1673
|
-
return new Promise(resolve => {
|
1733
|
+
return new Promise((resolve) => {
|
1674
1734
|
if (document.readyState == "loading") {
|
1675
1735
|
document.addEventListener("DOMContentLoaded", () => resolve());
|
1676
1736
|
}
|
@@ -1698,18 +1758,18 @@ function propertiesForClassDefinition(key) {
|
|
1698
1758
|
const attribute = classes.getAttributeName(key);
|
1699
1759
|
throw new Error(`Missing attribute "${attribute}"`);
|
1700
1760
|
}
|
1701
|
-
}
|
1761
|
+
},
|
1702
1762
|
},
|
1703
1763
|
[`${key}Classes`]: {
|
1704
1764
|
get() {
|
1705
1765
|
return this.classes.getAll(key);
|
1706
|
-
}
|
1766
|
+
},
|
1707
1767
|
},
|
1708
1768
|
[`has${capitalize(key)}Class`]: {
|
1709
1769
|
get() {
|
1710
1770
|
return this.classes.has(key);
|
1711
|
-
}
|
1712
|
-
}
|
1771
|
+
},
|
1772
|
+
},
|
1713
1773
|
};
|
1714
1774
|
}
|
1715
1775
|
|
@@ -1730,18 +1790,18 @@ function propertiesForTargetDefinition(name) {
|
|
1730
1790
|
else {
|
1731
1791
|
throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`);
|
1732
1792
|
}
|
1733
|
-
}
|
1793
|
+
},
|
1734
1794
|
},
|
1735
1795
|
[`${name}Targets`]: {
|
1736
1796
|
get() {
|
1737
1797
|
return this.targets.findAll(name);
|
1738
|
-
}
|
1798
|
+
},
|
1739
1799
|
},
|
1740
1800
|
[`has${capitalize(name)}Target`]: {
|
1741
1801
|
get() {
|
1742
1802
|
return this.targets.has(name);
|
1743
|
-
}
|
1744
|
-
}
|
1803
|
+
},
|
1804
|
+
},
|
1745
1805
|
};
|
1746
1806
|
}
|
1747
1807
|
|
@@ -1751,19 +1811,19 @@ function ValuePropertiesBlessing(constructor) {
|
|
1751
1811
|
valueDescriptorMap: {
|
1752
1812
|
get() {
|
1753
1813
|
return valueDefinitionPairs.reduce((result, valueDefinitionPair) => {
|
1754
|
-
const valueDescriptor = parseValueDefinitionPair(valueDefinitionPair);
|
1814
|
+
const valueDescriptor = parseValueDefinitionPair(valueDefinitionPair, this.identifier);
|
1755
1815
|
const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key);
|
1756
1816
|
return Object.assign(result, { [attributeName]: valueDescriptor });
|
1757
1817
|
}, {});
|
1758
|
-
}
|
1759
|
-
}
|
1818
|
+
},
|
1819
|
+
},
|
1760
1820
|
};
|
1761
1821
|
return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => {
|
1762
1822
|
return Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair));
|
1763
1823
|
}, propertyDescriptorMap);
|
1764
1824
|
}
|
1765
|
-
function propertiesForValueDefinitionPair(valueDefinitionPair) {
|
1766
|
-
const definition = parseValueDefinitionPair(valueDefinitionPair);
|
1825
|
+
function propertiesForValueDefinitionPair(valueDefinitionPair, controller) {
|
1826
|
+
const definition = parseValueDefinitionPair(valueDefinitionPair, controller);
|
1767
1827
|
const { key, name, reader: read, writer: write } = definition;
|
1768
1828
|
return {
|
1769
1829
|
[name]: {
|
@@ -1783,56 +1843,74 @@ function propertiesForValueDefinitionPair(valueDefinitionPair) {
|
|
1783
1843
|
else {
|
1784
1844
|
this.data.set(key, write(value));
|
1785
1845
|
}
|
1786
|
-
}
|
1846
|
+
},
|
1787
1847
|
},
|
1788
1848
|
[`has${capitalize(name)}`]: {
|
1789
1849
|
get() {
|
1790
1850
|
return this.data.has(key) || definition.hasCustomDefaultValue;
|
1791
|
-
}
|
1792
|
-
}
|
1851
|
+
},
|
1852
|
+
},
|
1793
1853
|
};
|
1794
1854
|
}
|
1795
|
-
function parseValueDefinitionPair([token, typeDefinition]) {
|
1796
|
-
return valueDescriptorForTokenAndTypeDefinition(
|
1855
|
+
function parseValueDefinitionPair([token, typeDefinition], controller) {
|
1856
|
+
return valueDescriptorForTokenAndTypeDefinition({
|
1857
|
+
controller,
|
1858
|
+
token,
|
1859
|
+
typeDefinition,
|
1860
|
+
});
|
1797
1861
|
}
|
1798
1862
|
function parseValueTypeConstant(constant) {
|
1799
1863
|
switch (constant) {
|
1800
|
-
case Array:
|
1801
|
-
|
1802
|
-
case
|
1803
|
-
|
1804
|
-
case
|
1864
|
+
case Array:
|
1865
|
+
return "array";
|
1866
|
+
case Boolean:
|
1867
|
+
return "boolean";
|
1868
|
+
case Number:
|
1869
|
+
return "number";
|
1870
|
+
case Object:
|
1871
|
+
return "object";
|
1872
|
+
case String:
|
1873
|
+
return "string";
|
1805
1874
|
}
|
1806
1875
|
}
|
1807
1876
|
function parseValueTypeDefault(defaultValue) {
|
1808
1877
|
switch (typeof defaultValue) {
|
1809
|
-
case "boolean":
|
1810
|
-
|
1811
|
-
case "
|
1878
|
+
case "boolean":
|
1879
|
+
return "boolean";
|
1880
|
+
case "number":
|
1881
|
+
return "number";
|
1882
|
+
case "string":
|
1883
|
+
return "string";
|
1812
1884
|
}
|
1813
1885
|
if (Array.isArray(defaultValue))
|
1814
1886
|
return "array";
|
1815
1887
|
if (Object.prototype.toString.call(defaultValue) === "[object Object]")
|
1816
1888
|
return "object";
|
1817
1889
|
}
|
1818
|
-
function parseValueTypeObject(
|
1819
|
-
const typeFromObject = parseValueTypeConstant(typeObject.type);
|
1820
|
-
if (typeFromObject)
|
1821
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
}
|
1825
|
-
|
1826
|
-
}
|
1827
|
-
|
1828
|
-
|
1829
|
-
|
1830
|
-
const
|
1831
|
-
|
1890
|
+
function parseValueTypeObject(payload) {
|
1891
|
+
const typeFromObject = parseValueTypeConstant(payload.typeObject.type);
|
1892
|
+
if (!typeFromObject)
|
1893
|
+
return;
|
1894
|
+
const defaultValueType = parseValueTypeDefault(payload.typeObject.default);
|
1895
|
+
if (typeFromObject !== defaultValueType) {
|
1896
|
+
const propertyPath = payload.controller ? `${payload.controller}.${payload.token}` : payload.token;
|
1897
|
+
throw new Error(`The specified default value for the Stimulus Value "${propertyPath}" must match the defined type "${typeFromObject}". The provided default value of "${payload.typeObject.default}" is of type "${defaultValueType}".`);
|
1898
|
+
}
|
1899
|
+
return typeFromObject;
|
1900
|
+
}
|
1901
|
+
function parseValueTypeDefinition(payload) {
|
1902
|
+
const typeFromObject = parseValueTypeObject({
|
1903
|
+
controller: payload.controller,
|
1904
|
+
token: payload.token,
|
1905
|
+
typeObject: payload.typeDefinition,
|
1906
|
+
});
|
1907
|
+
const typeFromDefaultValue = parseValueTypeDefault(payload.typeDefinition);
|
1908
|
+
const typeFromConstant = parseValueTypeConstant(payload.typeDefinition);
|
1832
1909
|
const type = typeFromObject || typeFromDefaultValue || typeFromConstant;
|
1833
1910
|
if (type)
|
1834
1911
|
return type;
|
1835
|
-
|
1912
|
+
const propertyPath = payload.controller ? `${payload.controller}.${payload.typeDefinition}` : payload.token;
|
1913
|
+
throw new Error(`Unknown value type "${propertyPath}" for "${payload.token}" value`);
|
1836
1914
|
}
|
1837
1915
|
function defaultValueForDefinition(typeDefinition) {
|
1838
1916
|
const constant = parseValueTypeConstant(typeDefinition);
|
@@ -1843,36 +1921,44 @@ function defaultValueForDefinition(typeDefinition) {
|
|
1843
1921
|
return defaultValue;
|
1844
1922
|
return typeDefinition;
|
1845
1923
|
}
|
1846
|
-
function valueDescriptorForTokenAndTypeDefinition(
|
1847
|
-
const key = `${dasherize(token)}-value`;
|
1848
|
-
const type = parseValueTypeDefinition(
|
1924
|
+
function valueDescriptorForTokenAndTypeDefinition(payload) {
|
1925
|
+
const key = `${dasherize(payload.token)}-value`;
|
1926
|
+
const type = parseValueTypeDefinition(payload);
|
1849
1927
|
return {
|
1850
1928
|
type,
|
1851
1929
|
key,
|
1852
1930
|
name: camelize(key),
|
1853
|
-
get defaultValue() {
|
1854
|
-
|
1931
|
+
get defaultValue() {
|
1932
|
+
return defaultValueForDefinition(payload.typeDefinition);
|
1933
|
+
},
|
1934
|
+
get hasCustomDefaultValue() {
|
1935
|
+
return parseValueTypeDefault(payload.typeDefinition) !== undefined;
|
1936
|
+
},
|
1855
1937
|
reader: readers[type],
|
1856
|
-
writer: writers[type] || writers.default
|
1938
|
+
writer: writers[type] || writers.default,
|
1857
1939
|
};
|
1858
1940
|
}
|
1859
1941
|
const defaultValuesByType = {
|
1860
|
-
get array() {
|
1942
|
+
get array() {
|
1943
|
+
return [];
|
1944
|
+
},
|
1861
1945
|
boolean: false,
|
1862
1946
|
number: 0,
|
1863
|
-
get object() {
|
1864
|
-
|
1947
|
+
get object() {
|
1948
|
+
return {};
|
1949
|
+
},
|
1950
|
+
string: "",
|
1865
1951
|
};
|
1866
1952
|
const readers = {
|
1867
1953
|
array(value) {
|
1868
1954
|
const array = JSON.parse(value);
|
1869
1955
|
if (!Array.isArray(array)) {
|
1870
|
-
throw new TypeError("
|
1956
|
+
throw new TypeError(`expected value of type "array" but instead got value "${value}" of type "${parseValueTypeDefault(array)}"`);
|
1871
1957
|
}
|
1872
1958
|
return array;
|
1873
1959
|
},
|
1874
1960
|
boolean(value) {
|
1875
|
-
return !(value == "0" || value == "false");
|
1961
|
+
return !(value == "0" || String(value).toLowerCase() == "false");
|
1876
1962
|
},
|
1877
1963
|
number(value) {
|
1878
1964
|
return Number(value);
|
@@ -1880,18 +1966,18 @@ const readers = {
|
|
1880
1966
|
object(value) {
|
1881
1967
|
const object = JSON.parse(value);
|
1882
1968
|
if (object === null || typeof object != "object" || Array.isArray(object)) {
|
1883
|
-
throw new TypeError("
|
1969
|
+
throw new TypeError(`expected value of type "object" but instead got value "${value}" of type "${parseValueTypeDefault(object)}"`);
|
1884
1970
|
}
|
1885
1971
|
return object;
|
1886
1972
|
},
|
1887
1973
|
string(value) {
|
1888
1974
|
return value;
|
1889
|
-
}
|
1975
|
+
},
|
1890
1976
|
};
|
1891
1977
|
const writers = {
|
1892
1978
|
default: writeString,
|
1893
1979
|
array: writeJSON,
|
1894
|
-
object: writeJSON
|
1980
|
+
object: writeJSON,
|
1895
1981
|
};
|
1896
1982
|
function writeJSON(value) {
|
1897
1983
|
return JSON.stringify(value);
|