stimulus-rails 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/stimulus.js +688 -260
- data/app/assets/javascripts/stimulus.min.js +1 -1
- data/app/assets/javascripts/stimulus.min.js.map +1 -1
- data/lib/stimulus/version.rb +1 -1
- metadata +3 -3
@@ -3,7 +3,7 @@
|
|
3
3
|
//= link ./stimulus-loading.js
|
4
4
|
|
5
5
|
/*
|
6
|
-
Stimulus 3.
|
6
|
+
Stimulus 3.2.0
|
7
7
|
Copyright © 2022 Basecamp, LLC
|
8
8
|
*/
|
9
9
|
class EventListener {
|
@@ -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,30 +133,53 @@ 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
|
|
131
|
-
const
|
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
|
+
};
|
172
|
+
const descriptorPattern = /^(?:(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;
|
132
173
|
function parseActionDescriptorString(descriptorString) {
|
133
174
|
const source = descriptorString.trim();
|
134
175
|
const matches = source.match(descriptorPattern) || [];
|
135
176
|
return {
|
136
|
-
eventTarget: parseEventTarget(matches[
|
137
|
-
eventName: matches[
|
138
|
-
eventOptions: matches[
|
139
|
-
identifier: matches[
|
140
|
-
methodName: matches[
|
177
|
+
eventTarget: parseEventTarget(matches[3]),
|
178
|
+
eventName: matches[1],
|
179
|
+
eventOptions: matches[6] ? parseEventOptions(matches[6]) : {},
|
180
|
+
identifier: matches[4],
|
181
|
+
methodName: matches[5],
|
182
|
+
keyFilter: matches[2],
|
141
183
|
};
|
142
184
|
}
|
143
185
|
function parseEventTarget(eventTargetName) {
|
@@ -149,7 +191,9 @@ function parseEventTarget(eventTargetName) {
|
|
149
191
|
}
|
150
192
|
}
|
151
193
|
function parseEventOptions(eventOptions) {
|
152
|
-
return eventOptions
|
194
|
+
return eventOptions
|
195
|
+
.split(":")
|
196
|
+
.reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {});
|
153
197
|
}
|
154
198
|
function stringifyEventTarget(eventTarget) {
|
155
199
|
if (eventTarget == window) {
|
@@ -163,6 +207,9 @@ function stringifyEventTarget(eventTarget) {
|
|
163
207
|
function camelize(value) {
|
164
208
|
return value.replace(/(?:[_-])([a-z0-9])/g, (_, char) => char.toUpperCase());
|
165
209
|
}
|
210
|
+
function namespaceCamelize(value) {
|
211
|
+
return camelize(value.replace(/--/g, "-").replace(/__/g, "_"));
|
212
|
+
}
|
166
213
|
function capitalize(value) {
|
167
214
|
return value.charAt(0).toUpperCase() + value.slice(1);
|
168
215
|
}
|
@@ -174,7 +221,7 @@ function tokenize(value) {
|
|
174
221
|
}
|
175
222
|
|
176
223
|
class Action {
|
177
|
-
constructor(element, index, descriptor) {
|
224
|
+
constructor(element, index, descriptor, schema) {
|
178
225
|
this.element = element;
|
179
226
|
this.index = index;
|
180
227
|
this.eventTarget = descriptor.eventTarget || element;
|
@@ -182,17 +229,39 @@ class Action {
|
|
182
229
|
this.eventOptions = descriptor.eventOptions || {};
|
183
230
|
this.identifier = descriptor.identifier || error("missing identifier");
|
184
231
|
this.methodName = descriptor.methodName || error("missing method name");
|
232
|
+
this.keyFilter = descriptor.keyFilter || "";
|
233
|
+
this.schema = schema;
|
185
234
|
}
|
186
|
-
static forToken(token) {
|
187
|
-
return new this(token.element, token.index, parseActionDescriptorString(token.content));
|
235
|
+
static forToken(token, schema) {
|
236
|
+
return new this(token.element, token.index, parseActionDescriptorString(token.content), schema);
|
188
237
|
}
|
189
238
|
toString() {
|
190
|
-
const
|
191
|
-
|
239
|
+
const eventFilter = this.keyFilter ? `.${this.keyFilter}` : "";
|
240
|
+
const eventTarget = this.eventTargetName ? `@${this.eventTargetName}` : "";
|
241
|
+
return `${this.eventName}${eventFilter}${eventTarget}->${this.identifier}#${this.methodName}`;
|
242
|
+
}
|
243
|
+
isFilterTarget(event) {
|
244
|
+
if (!this.keyFilter) {
|
245
|
+
return false;
|
246
|
+
}
|
247
|
+
const filteres = this.keyFilter.split("+");
|
248
|
+
const modifiers = ["meta", "ctrl", "alt", "shift"];
|
249
|
+
const [meta, ctrl, alt, shift] = modifiers.map((modifier) => filteres.includes(modifier));
|
250
|
+
if (event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== alt || event.shiftKey !== shift) {
|
251
|
+
return true;
|
252
|
+
}
|
253
|
+
const standardFilter = filteres.filter((key) => !modifiers.includes(key))[0];
|
254
|
+
if (!standardFilter) {
|
255
|
+
return false;
|
256
|
+
}
|
257
|
+
if (!Object.prototype.hasOwnProperty.call(this.keyMappings, standardFilter)) {
|
258
|
+
error(`contains unkown key filter: ${this.keyFilter}`);
|
259
|
+
}
|
260
|
+
return this.keyMappings[standardFilter].toLowerCase() !== event.key.toLowerCase();
|
192
261
|
}
|
193
262
|
get params() {
|
194
263
|
const params = {};
|
195
|
-
const pattern = new RegExp(`^data-${this.identifier}-(.+)-param
|
264
|
+
const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i");
|
196
265
|
for (const { name, value } of Array.from(this.element.attributes)) {
|
197
266
|
const match = name.match(pattern);
|
198
267
|
const key = match && match[1];
|
@@ -205,15 +274,18 @@ class Action {
|
|
205
274
|
get eventTargetName() {
|
206
275
|
return stringifyEventTarget(this.eventTarget);
|
207
276
|
}
|
277
|
+
get keyMappings() {
|
278
|
+
return this.schema.keyMappings;
|
279
|
+
}
|
208
280
|
}
|
209
281
|
const defaultEventNames = {
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
282
|
+
a: () => "click",
|
283
|
+
button: () => "click",
|
284
|
+
form: () => "submit",
|
285
|
+
details: () => "toggle",
|
286
|
+
input: (e) => (e.getAttribute("type") == "submit" ? "click" : "input"),
|
287
|
+
select: () => "change",
|
288
|
+
textarea: () => "input",
|
217
289
|
};
|
218
290
|
function getDefaultEventNameForElement(element) {
|
219
291
|
const tagName = element.tagName.toLowerCase();
|
@@ -251,9 +323,7 @@ class Binding {
|
|
251
323
|
return this.context.identifier;
|
252
324
|
}
|
253
325
|
handleEvent(event) {
|
254
|
-
if (this.willBeInvokedByEvent(event) && this.
|
255
|
-
this.processStopPropagation(event);
|
256
|
-
this.processPreventDefault(event);
|
326
|
+
if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(event)) {
|
257
327
|
this.invokeWithEvent(event);
|
258
328
|
}
|
259
329
|
}
|
@@ -267,15 +337,20 @@ class Binding {
|
|
267
337
|
}
|
268
338
|
throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`);
|
269
339
|
}
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
340
|
+
applyEventModifiers(event) {
|
341
|
+
const { element } = this.action;
|
342
|
+
const { actionDescriptorFilters } = this.context.application;
|
343
|
+
let passes = true;
|
344
|
+
for (const [name, value] of Object.entries(this.eventOptions)) {
|
345
|
+
if (name in actionDescriptorFilters) {
|
346
|
+
const filter = actionDescriptorFilters[name];
|
347
|
+
passes = passes && filter({ name, value, event, element });
|
348
|
+
}
|
349
|
+
else {
|
350
|
+
continue;
|
351
|
+
}
|
278
352
|
}
|
353
|
+
return passes;
|
279
354
|
}
|
280
355
|
invokeWithEvent(event) {
|
281
356
|
const { target, currentTarget } = event;
|
@@ -291,16 +366,11 @@ class Binding {
|
|
291
366
|
this.context.handleError(error, `invoking action "${this.action}"`, detail);
|
292
367
|
}
|
293
368
|
}
|
294
|
-
shouldBeInvokedPerSelf(event) {
|
295
|
-
if (this.action.eventOptions.self === true) {
|
296
|
-
return this.action.element === event.target;
|
297
|
-
}
|
298
|
-
else {
|
299
|
-
return true;
|
300
|
-
}
|
301
|
-
}
|
302
369
|
willBeInvokedByEvent(event) {
|
303
370
|
const eventTarget = event.target;
|
371
|
+
if (event instanceof KeyboardEvent && this.action.isFilterTarget(event)) {
|
372
|
+
return false;
|
373
|
+
}
|
304
374
|
if (this.element === eventTarget) {
|
305
375
|
return true;
|
306
376
|
}
|
@@ -331,7 +401,7 @@ class ElementObserver {
|
|
331
401
|
this.element = element;
|
332
402
|
this.started = false;
|
333
403
|
this.delegate = delegate;
|
334
|
-
this.elements = new Set;
|
404
|
+
this.elements = new Set();
|
335
405
|
this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
|
336
406
|
}
|
337
407
|
start() {
|
@@ -514,95 +584,6 @@ class AttributeObserver {
|
|
514
584
|
}
|
515
585
|
}
|
516
586
|
|
517
|
-
class StringMapObserver {
|
518
|
-
constructor(element, delegate) {
|
519
|
-
this.element = element;
|
520
|
-
this.delegate = delegate;
|
521
|
-
this.started = false;
|
522
|
-
this.stringMap = new Map;
|
523
|
-
this.mutationObserver = new MutationObserver(mutations => this.processMutations(mutations));
|
524
|
-
}
|
525
|
-
start() {
|
526
|
-
if (!this.started) {
|
527
|
-
this.started = true;
|
528
|
-
this.mutationObserver.observe(this.element, { attributes: true, attributeOldValue: true });
|
529
|
-
this.refresh();
|
530
|
-
}
|
531
|
-
}
|
532
|
-
stop() {
|
533
|
-
if (this.started) {
|
534
|
-
this.mutationObserver.takeRecords();
|
535
|
-
this.mutationObserver.disconnect();
|
536
|
-
this.started = false;
|
537
|
-
}
|
538
|
-
}
|
539
|
-
refresh() {
|
540
|
-
if (this.started) {
|
541
|
-
for (const attributeName of this.knownAttributeNames) {
|
542
|
-
this.refreshAttribute(attributeName, null);
|
543
|
-
}
|
544
|
-
}
|
545
|
-
}
|
546
|
-
processMutations(mutations) {
|
547
|
-
if (this.started) {
|
548
|
-
for (const mutation of mutations) {
|
549
|
-
this.processMutation(mutation);
|
550
|
-
}
|
551
|
-
}
|
552
|
-
}
|
553
|
-
processMutation(mutation) {
|
554
|
-
const attributeName = mutation.attributeName;
|
555
|
-
if (attributeName) {
|
556
|
-
this.refreshAttribute(attributeName, mutation.oldValue);
|
557
|
-
}
|
558
|
-
}
|
559
|
-
refreshAttribute(attributeName, oldValue) {
|
560
|
-
const key = this.delegate.getStringMapKeyForAttribute(attributeName);
|
561
|
-
if (key != null) {
|
562
|
-
if (!this.stringMap.has(attributeName)) {
|
563
|
-
this.stringMapKeyAdded(key, attributeName);
|
564
|
-
}
|
565
|
-
const value = this.element.getAttribute(attributeName);
|
566
|
-
if (this.stringMap.get(attributeName) != value) {
|
567
|
-
this.stringMapValueChanged(value, key, oldValue);
|
568
|
-
}
|
569
|
-
if (value == null) {
|
570
|
-
const oldValue = this.stringMap.get(attributeName);
|
571
|
-
this.stringMap.delete(attributeName);
|
572
|
-
if (oldValue)
|
573
|
-
this.stringMapKeyRemoved(key, attributeName, oldValue);
|
574
|
-
}
|
575
|
-
else {
|
576
|
-
this.stringMap.set(attributeName, value);
|
577
|
-
}
|
578
|
-
}
|
579
|
-
}
|
580
|
-
stringMapKeyAdded(key, attributeName) {
|
581
|
-
if (this.delegate.stringMapKeyAdded) {
|
582
|
-
this.delegate.stringMapKeyAdded(key, attributeName);
|
583
|
-
}
|
584
|
-
}
|
585
|
-
stringMapValueChanged(value, key, oldValue) {
|
586
|
-
if (this.delegate.stringMapValueChanged) {
|
587
|
-
this.delegate.stringMapValueChanged(value, key, oldValue);
|
588
|
-
}
|
589
|
-
}
|
590
|
-
stringMapKeyRemoved(key, attributeName, oldValue) {
|
591
|
-
if (this.delegate.stringMapKeyRemoved) {
|
592
|
-
this.delegate.stringMapKeyRemoved(key, attributeName, oldValue);
|
593
|
-
}
|
594
|
-
}
|
595
|
-
get knownAttributeNames() {
|
596
|
-
return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)));
|
597
|
-
}
|
598
|
-
get currentAttributeNames() {
|
599
|
-
return Array.from(this.element.attributes).map(attribute => attribute.name);
|
600
|
-
}
|
601
|
-
get recordedAttributeNames() {
|
602
|
-
return Array.from(this.stringMap.keys());
|
603
|
-
}
|
604
|
-
}
|
605
|
-
|
606
587
|
function add(map, key, value) {
|
607
588
|
fetch(map, key).add(value);
|
608
589
|
}
|
@@ -655,7 +636,7 @@ class Multimap {
|
|
655
636
|
}
|
656
637
|
hasValue(value) {
|
657
638
|
const sets = Array.from(this.valuesByKey.values());
|
658
|
-
return sets.some(set => set.has(value));
|
639
|
+
return sets.some((set) => set.has(value));
|
659
640
|
}
|
660
641
|
getValuesForKey(key) {
|
661
642
|
const values = this.valuesByKey.get(key);
|
@@ -663,15 +644,15 @@ class Multimap {
|
|
663
644
|
}
|
664
645
|
getKeysForValue(value) {
|
665
646
|
return Array.from(this.valuesByKey)
|
666
|
-
.filter(([
|
667
|
-
.map(([key,
|
647
|
+
.filter(([_key, values]) => values.has(value))
|
648
|
+
.map(([key, _values]) => key);
|
668
649
|
}
|
669
650
|
}
|
670
651
|
|
671
652
|
class IndexedMultimap extends Multimap {
|
672
653
|
constructor() {
|
673
654
|
super();
|
674
|
-
this.keysByValue = new Map;
|
655
|
+
this.keysByValue = new Map();
|
675
656
|
}
|
676
657
|
get values() {
|
677
658
|
return Array.from(this.keysByValue.keys());
|
@@ -693,11 +674,163 @@ class IndexedMultimap extends Multimap {
|
|
693
674
|
}
|
694
675
|
}
|
695
676
|
|
677
|
+
class SelectorObserver {
|
678
|
+
constructor(element, selector, delegate, details = {}) {
|
679
|
+
this.selector = selector;
|
680
|
+
this.details = details;
|
681
|
+
this.elementObserver = new ElementObserver(element, this);
|
682
|
+
this.delegate = delegate;
|
683
|
+
this.matchesByElement = new Multimap();
|
684
|
+
}
|
685
|
+
get started() {
|
686
|
+
return this.elementObserver.started;
|
687
|
+
}
|
688
|
+
start() {
|
689
|
+
this.elementObserver.start();
|
690
|
+
}
|
691
|
+
pause(callback) {
|
692
|
+
this.elementObserver.pause(callback);
|
693
|
+
}
|
694
|
+
stop() {
|
695
|
+
this.elementObserver.stop();
|
696
|
+
}
|
697
|
+
refresh() {
|
698
|
+
this.elementObserver.refresh();
|
699
|
+
}
|
700
|
+
get element() {
|
701
|
+
return this.elementObserver.element;
|
702
|
+
}
|
703
|
+
matchElement(element) {
|
704
|
+
const matches = element.matches(this.selector);
|
705
|
+
if (this.delegate.selectorMatchElement) {
|
706
|
+
return matches && this.delegate.selectorMatchElement(element, this.details);
|
707
|
+
}
|
708
|
+
return matches;
|
709
|
+
}
|
710
|
+
matchElementsInTree(tree) {
|
711
|
+
const match = this.matchElement(tree) ? [tree] : [];
|
712
|
+
const matches = Array.from(tree.querySelectorAll(this.selector)).filter((match) => this.matchElement(match));
|
713
|
+
return match.concat(matches);
|
714
|
+
}
|
715
|
+
elementMatched(element) {
|
716
|
+
this.selectorMatched(element);
|
717
|
+
}
|
718
|
+
elementUnmatched(element) {
|
719
|
+
this.selectorUnmatched(element);
|
720
|
+
}
|
721
|
+
elementAttributeChanged(element, _attributeName) {
|
722
|
+
const matches = this.matchElement(element);
|
723
|
+
const matchedBefore = this.matchesByElement.has(this.selector, element);
|
724
|
+
if (!matches && matchedBefore) {
|
725
|
+
this.selectorUnmatched(element);
|
726
|
+
}
|
727
|
+
}
|
728
|
+
selectorMatched(element) {
|
729
|
+
if (this.delegate.selectorMatched) {
|
730
|
+
this.delegate.selectorMatched(element, this.selector, this.details);
|
731
|
+
this.matchesByElement.add(this.selector, element);
|
732
|
+
}
|
733
|
+
}
|
734
|
+
selectorUnmatched(element) {
|
735
|
+
this.delegate.selectorUnmatched(element, this.selector, this.details);
|
736
|
+
this.matchesByElement.delete(this.selector, element);
|
737
|
+
}
|
738
|
+
}
|
739
|
+
|
740
|
+
class StringMapObserver {
|
741
|
+
constructor(element, delegate) {
|
742
|
+
this.element = element;
|
743
|
+
this.delegate = delegate;
|
744
|
+
this.started = false;
|
745
|
+
this.stringMap = new Map();
|
746
|
+
this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
|
747
|
+
}
|
748
|
+
start() {
|
749
|
+
if (!this.started) {
|
750
|
+
this.started = true;
|
751
|
+
this.mutationObserver.observe(this.element, { attributes: true, attributeOldValue: true });
|
752
|
+
this.refresh();
|
753
|
+
}
|
754
|
+
}
|
755
|
+
stop() {
|
756
|
+
if (this.started) {
|
757
|
+
this.mutationObserver.takeRecords();
|
758
|
+
this.mutationObserver.disconnect();
|
759
|
+
this.started = false;
|
760
|
+
}
|
761
|
+
}
|
762
|
+
refresh() {
|
763
|
+
if (this.started) {
|
764
|
+
for (const attributeName of this.knownAttributeNames) {
|
765
|
+
this.refreshAttribute(attributeName, null);
|
766
|
+
}
|
767
|
+
}
|
768
|
+
}
|
769
|
+
processMutations(mutations) {
|
770
|
+
if (this.started) {
|
771
|
+
for (const mutation of mutations) {
|
772
|
+
this.processMutation(mutation);
|
773
|
+
}
|
774
|
+
}
|
775
|
+
}
|
776
|
+
processMutation(mutation) {
|
777
|
+
const attributeName = mutation.attributeName;
|
778
|
+
if (attributeName) {
|
779
|
+
this.refreshAttribute(attributeName, mutation.oldValue);
|
780
|
+
}
|
781
|
+
}
|
782
|
+
refreshAttribute(attributeName, oldValue) {
|
783
|
+
const key = this.delegate.getStringMapKeyForAttribute(attributeName);
|
784
|
+
if (key != null) {
|
785
|
+
if (!this.stringMap.has(attributeName)) {
|
786
|
+
this.stringMapKeyAdded(key, attributeName);
|
787
|
+
}
|
788
|
+
const value = this.element.getAttribute(attributeName);
|
789
|
+
if (this.stringMap.get(attributeName) != value) {
|
790
|
+
this.stringMapValueChanged(value, key, oldValue);
|
791
|
+
}
|
792
|
+
if (value == null) {
|
793
|
+
const oldValue = this.stringMap.get(attributeName);
|
794
|
+
this.stringMap.delete(attributeName);
|
795
|
+
if (oldValue)
|
796
|
+
this.stringMapKeyRemoved(key, attributeName, oldValue);
|
797
|
+
}
|
798
|
+
else {
|
799
|
+
this.stringMap.set(attributeName, value);
|
800
|
+
}
|
801
|
+
}
|
802
|
+
}
|
803
|
+
stringMapKeyAdded(key, attributeName) {
|
804
|
+
if (this.delegate.stringMapKeyAdded) {
|
805
|
+
this.delegate.stringMapKeyAdded(key, attributeName);
|
806
|
+
}
|
807
|
+
}
|
808
|
+
stringMapValueChanged(value, key, oldValue) {
|
809
|
+
if (this.delegate.stringMapValueChanged) {
|
810
|
+
this.delegate.stringMapValueChanged(value, key, oldValue);
|
811
|
+
}
|
812
|
+
}
|
813
|
+
stringMapKeyRemoved(key, attributeName, oldValue) {
|
814
|
+
if (this.delegate.stringMapKeyRemoved) {
|
815
|
+
this.delegate.stringMapKeyRemoved(key, attributeName, oldValue);
|
816
|
+
}
|
817
|
+
}
|
818
|
+
get knownAttributeNames() {
|
819
|
+
return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)));
|
820
|
+
}
|
821
|
+
get currentAttributeNames() {
|
822
|
+
return Array.from(this.element.attributes).map((attribute) => attribute.name);
|
823
|
+
}
|
824
|
+
get recordedAttributeNames() {
|
825
|
+
return Array.from(this.stringMap.keys());
|
826
|
+
}
|
827
|
+
}
|
828
|
+
|
696
829
|
class TokenListObserver {
|
697
830
|
constructor(element, attributeName, delegate) {
|
698
831
|
this.attributeObserver = new AttributeObserver(element, attributeName, this);
|
699
832
|
this.delegate = delegate;
|
700
|
-
this.tokensByElement = new Multimap;
|
833
|
+
this.tokensByElement = new Multimap();
|
701
834
|
}
|
702
835
|
get started() {
|
703
836
|
return this.attributeObserver.started;
|
@@ -732,10 +865,10 @@ class TokenListObserver {
|
|
732
865
|
this.tokensUnmatched(this.tokensByElement.getValuesForKey(element));
|
733
866
|
}
|
734
867
|
tokensMatched(tokens) {
|
735
|
-
tokens.forEach(token => this.tokenMatched(token));
|
868
|
+
tokens.forEach((token) => this.tokenMatched(token));
|
736
869
|
}
|
737
870
|
tokensUnmatched(tokens) {
|
738
|
-
tokens.forEach(token => this.tokenUnmatched(token));
|
871
|
+
tokens.forEach((token) => this.tokenUnmatched(token));
|
739
872
|
}
|
740
873
|
tokenMatched(token) {
|
741
874
|
this.delegate.tokenMatched(token);
|
@@ -748,8 +881,7 @@ class TokenListObserver {
|
|
748
881
|
refreshTokensForElement(element) {
|
749
882
|
const previousTokens = this.tokensByElement.getValuesForKey(element);
|
750
883
|
const currentTokens = this.readTokensForElement(element);
|
751
|
-
const firstDifferingIndex = zip(previousTokens, currentTokens)
|
752
|
-
.findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
|
884
|
+
const firstDifferingIndex = zip(previousTokens, currentTokens).findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
|
753
885
|
if (firstDifferingIndex == -1) {
|
754
886
|
return [[], []];
|
755
887
|
}
|
@@ -764,7 +896,10 @@ class TokenListObserver {
|
|
764
896
|
}
|
765
897
|
}
|
766
898
|
function parseTokenString(tokenString, element, attributeName) {
|
767
|
-
return tokenString
|
899
|
+
return tokenString
|
900
|
+
.trim()
|
901
|
+
.split(/\s+/)
|
902
|
+
.filter((content) => content.length)
|
768
903
|
.map((content, index) => ({ element, attributeName, content, index }));
|
769
904
|
}
|
770
905
|
function zip(left, right) {
|
@@ -779,8 +914,8 @@ class ValueListObserver {
|
|
779
914
|
constructor(element, attributeName, delegate) {
|
780
915
|
this.tokenListObserver = new TokenListObserver(element, attributeName, this);
|
781
916
|
this.delegate = delegate;
|
782
|
-
this.parseResultsByToken = new WeakMap;
|
783
|
-
this.valuesByTokenByElement = new WeakMap;
|
917
|
+
this.parseResultsByToken = new WeakMap();
|
918
|
+
this.valuesByTokenByElement = new WeakMap();
|
784
919
|
}
|
785
920
|
get started() {
|
786
921
|
return this.tokenListObserver.started;
|
@@ -827,7 +962,7 @@ class ValueListObserver {
|
|
827
962
|
fetchValuesByTokenForElement(element) {
|
828
963
|
let valuesByToken = this.valuesByTokenByElement.get(element);
|
829
964
|
if (!valuesByToken) {
|
830
|
-
valuesByToken = new Map;
|
965
|
+
valuesByToken = new Map();
|
831
966
|
this.valuesByTokenByElement.set(element, valuesByToken);
|
832
967
|
}
|
833
968
|
return valuesByToken;
|
@@ -847,7 +982,7 @@ class BindingObserver {
|
|
847
982
|
constructor(context, delegate) {
|
848
983
|
this.context = context;
|
849
984
|
this.delegate = delegate;
|
850
|
-
this.bindingsByAction = new Map;
|
985
|
+
this.bindingsByAction = new Map();
|
851
986
|
}
|
852
987
|
start() {
|
853
988
|
if (!this.valueListObserver) {
|
@@ -890,11 +1025,11 @@ class BindingObserver {
|
|
890
1025
|
}
|
891
1026
|
}
|
892
1027
|
disconnectAllActions() {
|
893
|
-
this.bindings.forEach(binding => this.delegate.bindingDisconnected(binding));
|
1028
|
+
this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding, true));
|
894
1029
|
this.bindingsByAction.clear();
|
895
1030
|
}
|
896
1031
|
parseValueForToken(token) {
|
897
|
-
const action = Action.forToken(token);
|
1032
|
+
const action = Action.forToken(token, this.schema);
|
898
1033
|
if (action.identifier == this.identifier) {
|
899
1034
|
return action;
|
900
1035
|
}
|
@@ -977,19 +1112,20 @@ class ValueObserver {
|
|
977
1112
|
changedMethod.call(this.receiver, value, oldValue);
|
978
1113
|
}
|
979
1114
|
catch (error) {
|
980
|
-
if (
|
981
|
-
|
982
|
-
|
1115
|
+
if (error instanceof TypeError) {
|
1116
|
+
error.message = `Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error.message}`;
|
1117
|
+
}
|
1118
|
+
throw error;
|
983
1119
|
}
|
984
1120
|
}
|
985
1121
|
}
|
986
1122
|
get valueDescriptors() {
|
987
1123
|
const { valueDescriptorMap } = this;
|
988
|
-
return Object.keys(valueDescriptorMap).map(key => valueDescriptorMap[key]);
|
1124
|
+
return Object.keys(valueDescriptorMap).map((key) => valueDescriptorMap[key]);
|
989
1125
|
}
|
990
1126
|
get valueDescriptorNameMap() {
|
991
1127
|
const descriptors = {};
|
992
|
-
Object.keys(this.valueDescriptorMap).forEach(key => {
|
1128
|
+
Object.keys(this.valueDescriptorMap).forEach((key) => {
|
993
1129
|
const descriptor = this.valueDescriptorMap[key];
|
994
1130
|
descriptors[descriptor.name] = descriptor;
|
995
1131
|
});
|
@@ -1006,7 +1142,7 @@ class TargetObserver {
|
|
1006
1142
|
constructor(context, delegate) {
|
1007
1143
|
this.context = context;
|
1008
1144
|
this.delegate = delegate;
|
1009
|
-
this.targetsByName = new Multimap;
|
1145
|
+
this.targetsByName = new Multimap();
|
1010
1146
|
}
|
1011
1147
|
start() {
|
1012
1148
|
if (!this.tokenListObserver) {
|
@@ -1061,6 +1197,155 @@ class TargetObserver {
|
|
1061
1197
|
}
|
1062
1198
|
}
|
1063
1199
|
|
1200
|
+
function readInheritableStaticArrayValues(constructor, propertyName) {
|
1201
|
+
const ancestors = getAncestorsForConstructor(constructor);
|
1202
|
+
return Array.from(ancestors.reduce((values, constructor) => {
|
1203
|
+
getOwnStaticArrayValues(constructor, propertyName).forEach((name) => values.add(name));
|
1204
|
+
return values;
|
1205
|
+
}, new Set()));
|
1206
|
+
}
|
1207
|
+
function readInheritableStaticObjectPairs(constructor, propertyName) {
|
1208
|
+
const ancestors = getAncestorsForConstructor(constructor);
|
1209
|
+
return ancestors.reduce((pairs, constructor) => {
|
1210
|
+
pairs.push(...getOwnStaticObjectPairs(constructor, propertyName));
|
1211
|
+
return pairs;
|
1212
|
+
}, []);
|
1213
|
+
}
|
1214
|
+
function getAncestorsForConstructor(constructor) {
|
1215
|
+
const ancestors = [];
|
1216
|
+
while (constructor) {
|
1217
|
+
ancestors.push(constructor);
|
1218
|
+
constructor = Object.getPrototypeOf(constructor);
|
1219
|
+
}
|
1220
|
+
return ancestors.reverse();
|
1221
|
+
}
|
1222
|
+
function getOwnStaticArrayValues(constructor, propertyName) {
|
1223
|
+
const definition = constructor[propertyName];
|
1224
|
+
return Array.isArray(definition) ? definition : [];
|
1225
|
+
}
|
1226
|
+
function getOwnStaticObjectPairs(constructor, propertyName) {
|
1227
|
+
const definition = constructor[propertyName];
|
1228
|
+
return definition ? Object.keys(definition).map((key) => [key, definition[key]]) : [];
|
1229
|
+
}
|
1230
|
+
|
1231
|
+
class OutletObserver {
|
1232
|
+
constructor(context, delegate) {
|
1233
|
+
this.context = context;
|
1234
|
+
this.delegate = delegate;
|
1235
|
+
this.outletsByName = new Multimap();
|
1236
|
+
this.outletElementsByName = new Multimap();
|
1237
|
+
this.selectorObserverMap = new Map();
|
1238
|
+
}
|
1239
|
+
start() {
|
1240
|
+
if (this.selectorObserverMap.size === 0) {
|
1241
|
+
this.outletDefinitions.forEach((outletName) => {
|
1242
|
+
const selector = this.selector(outletName);
|
1243
|
+
const details = { outletName };
|
1244
|
+
if (selector) {
|
1245
|
+
this.selectorObserverMap.set(outletName, new SelectorObserver(document.body, selector, this, details));
|
1246
|
+
}
|
1247
|
+
});
|
1248
|
+
this.selectorObserverMap.forEach((observer) => observer.start());
|
1249
|
+
}
|
1250
|
+
this.dependentContexts.forEach((context) => context.refresh());
|
1251
|
+
}
|
1252
|
+
stop() {
|
1253
|
+
if (this.selectorObserverMap.size > 0) {
|
1254
|
+
this.disconnectAllOutlets();
|
1255
|
+
this.selectorObserverMap.forEach((observer) => observer.stop());
|
1256
|
+
this.selectorObserverMap.clear();
|
1257
|
+
}
|
1258
|
+
}
|
1259
|
+
refresh() {
|
1260
|
+
this.selectorObserverMap.forEach((observer) => observer.refresh());
|
1261
|
+
}
|
1262
|
+
selectorMatched(element, _selector, { outletName }) {
|
1263
|
+
const outlet = this.getOutlet(element, outletName);
|
1264
|
+
if (outlet) {
|
1265
|
+
this.connectOutlet(outlet, element, outletName);
|
1266
|
+
}
|
1267
|
+
}
|
1268
|
+
selectorUnmatched(element, _selector, { outletName }) {
|
1269
|
+
const outlet = this.getOutletFromMap(element, outletName);
|
1270
|
+
if (outlet) {
|
1271
|
+
this.disconnectOutlet(outlet, element, outletName);
|
1272
|
+
}
|
1273
|
+
}
|
1274
|
+
selectorMatchElement(element, { outletName }) {
|
1275
|
+
return (this.hasOutlet(element, outletName) &&
|
1276
|
+
element.matches(`[${this.context.application.schema.controllerAttribute}~=${outletName}]`));
|
1277
|
+
}
|
1278
|
+
connectOutlet(outlet, element, outletName) {
|
1279
|
+
var _a;
|
1280
|
+
if (!this.outletElementsByName.has(outletName, element)) {
|
1281
|
+
this.outletsByName.add(outletName, outlet);
|
1282
|
+
this.outletElementsByName.add(outletName, element);
|
1283
|
+
(_a = this.selectorObserverMap.get(outletName)) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.outletConnected(outlet, element, outletName));
|
1284
|
+
}
|
1285
|
+
}
|
1286
|
+
disconnectOutlet(outlet, element, outletName) {
|
1287
|
+
var _a;
|
1288
|
+
if (this.outletElementsByName.has(outletName, element)) {
|
1289
|
+
this.outletsByName.delete(outletName, outlet);
|
1290
|
+
this.outletElementsByName.delete(outletName, element);
|
1291
|
+
(_a = this.selectorObserverMap
|
1292
|
+
.get(outletName)) === null || _a === void 0 ? void 0 : _a.pause(() => this.delegate.outletDisconnected(outlet, element, outletName));
|
1293
|
+
}
|
1294
|
+
}
|
1295
|
+
disconnectAllOutlets() {
|
1296
|
+
for (const outletName of this.outletElementsByName.keys) {
|
1297
|
+
for (const element of this.outletElementsByName.getValuesForKey(outletName)) {
|
1298
|
+
for (const outlet of this.outletsByName.getValuesForKey(outletName)) {
|
1299
|
+
this.disconnectOutlet(outlet, element, outletName);
|
1300
|
+
}
|
1301
|
+
}
|
1302
|
+
}
|
1303
|
+
}
|
1304
|
+
selector(outletName) {
|
1305
|
+
return this.scope.outlets.getSelectorForOutletName(outletName);
|
1306
|
+
}
|
1307
|
+
get outletDependencies() {
|
1308
|
+
const dependencies = new Multimap();
|
1309
|
+
this.router.modules.forEach((module) => {
|
1310
|
+
const constructor = module.definition.controllerConstructor;
|
1311
|
+
const outlets = readInheritableStaticArrayValues(constructor, "outlets");
|
1312
|
+
outlets.forEach((outlet) => dependencies.add(outlet, module.identifier));
|
1313
|
+
});
|
1314
|
+
return dependencies;
|
1315
|
+
}
|
1316
|
+
get outletDefinitions() {
|
1317
|
+
return this.outletDependencies.getKeysForValue(this.identifier);
|
1318
|
+
}
|
1319
|
+
get dependentControllerIdentifiers() {
|
1320
|
+
return this.outletDependencies.getValuesForKey(this.identifier);
|
1321
|
+
}
|
1322
|
+
get dependentContexts() {
|
1323
|
+
const identifiers = this.dependentControllerIdentifiers;
|
1324
|
+
return this.router.contexts.filter((context) => identifiers.includes(context.identifier));
|
1325
|
+
}
|
1326
|
+
hasOutlet(element, outletName) {
|
1327
|
+
return !!this.getOutlet(element, outletName) || !!this.getOutletFromMap(element, outletName);
|
1328
|
+
}
|
1329
|
+
getOutlet(element, outletName) {
|
1330
|
+
return this.application.getControllerForElementAndIdentifier(element, outletName);
|
1331
|
+
}
|
1332
|
+
getOutletFromMap(element, outletName) {
|
1333
|
+
return this.outletsByName.getValuesForKey(outletName).find((outlet) => outlet.element === element);
|
1334
|
+
}
|
1335
|
+
get scope() {
|
1336
|
+
return this.context.scope;
|
1337
|
+
}
|
1338
|
+
get identifier() {
|
1339
|
+
return this.context.identifier;
|
1340
|
+
}
|
1341
|
+
get application() {
|
1342
|
+
return this.context.application;
|
1343
|
+
}
|
1344
|
+
get router() {
|
1345
|
+
return this.application.router;
|
1346
|
+
}
|
1347
|
+
}
|
1348
|
+
|
1064
1349
|
class Context {
|
1065
1350
|
constructor(module, scope) {
|
1066
1351
|
this.logDebugActivity = (functionName, detail = {}) => {
|
@@ -1074,6 +1359,7 @@ class Context {
|
|
1074
1359
|
this.bindingObserver = new BindingObserver(this, this.dispatcher);
|
1075
1360
|
this.valueObserver = new ValueObserver(this, this.controller);
|
1076
1361
|
this.targetObserver = new TargetObserver(this, this);
|
1362
|
+
this.outletObserver = new OutletObserver(this, this);
|
1077
1363
|
try {
|
1078
1364
|
this.controller.initialize();
|
1079
1365
|
this.logDebugActivity("initialize");
|
@@ -1086,6 +1372,7 @@ class Context {
|
|
1086
1372
|
this.bindingObserver.start();
|
1087
1373
|
this.valueObserver.start();
|
1088
1374
|
this.targetObserver.start();
|
1375
|
+
this.outletObserver.start();
|
1089
1376
|
try {
|
1090
1377
|
this.controller.connect();
|
1091
1378
|
this.logDebugActivity("connect");
|
@@ -1094,6 +1381,9 @@ class Context {
|
|
1094
1381
|
this.handleError(error, "connecting controller");
|
1095
1382
|
}
|
1096
1383
|
}
|
1384
|
+
refresh() {
|
1385
|
+
this.outletObserver.refresh();
|
1386
|
+
}
|
1097
1387
|
disconnect() {
|
1098
1388
|
try {
|
1099
1389
|
this.controller.disconnect();
|
@@ -1102,6 +1392,7 @@ class Context {
|
|
1102
1392
|
catch (error) {
|
1103
1393
|
this.handleError(error, "disconnecting controller");
|
1104
1394
|
}
|
1395
|
+
this.outletObserver.stop();
|
1105
1396
|
this.targetObserver.stop();
|
1106
1397
|
this.valueObserver.stop();
|
1107
1398
|
this.bindingObserver.stop();
|
@@ -1135,6 +1426,12 @@ class Context {
|
|
1135
1426
|
targetDisconnected(element, name) {
|
1136
1427
|
this.invokeControllerMethod(`${name}TargetDisconnected`, element);
|
1137
1428
|
}
|
1429
|
+
outletConnected(outlet, element, name) {
|
1430
|
+
this.invokeControllerMethod(`${namespaceCamelize(name)}OutletConnected`, outlet, element);
|
1431
|
+
}
|
1432
|
+
outletDisconnected(outlet, element, name) {
|
1433
|
+
this.invokeControllerMethod(`${namespaceCamelize(name)}OutletDisconnected`, outlet, element);
|
1434
|
+
}
|
1138
1435
|
invokeControllerMethod(methodName, ...args) {
|
1139
1436
|
const controller = this.controller;
|
1140
1437
|
if (typeof controller[methodName] == "function") {
|
@@ -1143,37 +1440,6 @@ class Context {
|
|
1143
1440
|
}
|
1144
1441
|
}
|
1145
1442
|
|
1146
|
-
function readInheritableStaticArrayValues(constructor, propertyName) {
|
1147
|
-
const ancestors = getAncestorsForConstructor(constructor);
|
1148
|
-
return Array.from(ancestors.reduce((values, constructor) => {
|
1149
|
-
getOwnStaticArrayValues(constructor, propertyName).forEach(name => values.add(name));
|
1150
|
-
return values;
|
1151
|
-
}, new Set));
|
1152
|
-
}
|
1153
|
-
function readInheritableStaticObjectPairs(constructor, propertyName) {
|
1154
|
-
const ancestors = getAncestorsForConstructor(constructor);
|
1155
|
-
return ancestors.reduce((pairs, constructor) => {
|
1156
|
-
pairs.push(...getOwnStaticObjectPairs(constructor, propertyName));
|
1157
|
-
return pairs;
|
1158
|
-
}, []);
|
1159
|
-
}
|
1160
|
-
function getAncestorsForConstructor(constructor) {
|
1161
|
-
const ancestors = [];
|
1162
|
-
while (constructor) {
|
1163
|
-
ancestors.push(constructor);
|
1164
|
-
constructor = Object.getPrototypeOf(constructor);
|
1165
|
-
}
|
1166
|
-
return ancestors.reverse();
|
1167
|
-
}
|
1168
|
-
function getOwnStaticArrayValues(constructor, propertyName) {
|
1169
|
-
const definition = constructor[propertyName];
|
1170
|
-
return Array.isArray(definition) ? definition : [];
|
1171
|
-
}
|
1172
|
-
function getOwnStaticObjectPairs(constructor, propertyName) {
|
1173
|
-
const definition = constructor[propertyName];
|
1174
|
-
return definition ? Object.keys(definition).map(key => [key, definition[key]]) : [];
|
1175
|
-
}
|
1176
|
-
|
1177
1443
|
function bless(constructor) {
|
1178
1444
|
return shadow(constructor, getBlessedProperties(constructor));
|
1179
1445
|
}
|
@@ -1217,10 +1483,7 @@ function getShadowedDescriptor(prototype, properties, key) {
|
|
1217
1483
|
}
|
1218
1484
|
const getOwnKeys = (() => {
|
1219
1485
|
if (typeof Object.getOwnPropertySymbols == "function") {
|
1220
|
-
return (object) => [
|
1221
|
-
...Object.getOwnPropertyNames(object),
|
1222
|
-
...Object.getOwnPropertySymbols(object)
|
1223
|
-
];
|
1486
|
+
return (object) => [...Object.getOwnPropertyNames(object), ...Object.getOwnPropertySymbols(object)];
|
1224
1487
|
}
|
1225
1488
|
else {
|
1226
1489
|
return Object.getOwnPropertyNames;
|
@@ -1232,16 +1495,18 @@ const extend = (() => {
|
|
1232
1495
|
return Reflect.construct(constructor, arguments, new.target);
|
1233
1496
|
}
|
1234
1497
|
extended.prototype = Object.create(constructor.prototype, {
|
1235
|
-
constructor: { value: extended }
|
1498
|
+
constructor: { value: extended },
|
1236
1499
|
});
|
1237
1500
|
Reflect.setPrototypeOf(extended, constructor);
|
1238
1501
|
return extended;
|
1239
1502
|
}
|
1240
1503
|
function testReflectExtension() {
|
1241
|
-
const a = function () {
|
1504
|
+
const a = function () {
|
1505
|
+
this.a.call(this);
|
1506
|
+
};
|
1242
1507
|
const b = extendWithReflect(a);
|
1243
1508
|
b.prototype.a = function () { };
|
1244
|
-
return new b;
|
1509
|
+
return new b();
|
1245
1510
|
}
|
1246
1511
|
try {
|
1247
1512
|
testReflectExtension();
|
@@ -1256,7 +1521,7 @@ const extend = (() => {
|
|
1256
1521
|
function blessDefinition(definition) {
|
1257
1522
|
return {
|
1258
1523
|
identifier: definition.identifier,
|
1259
|
-
controllerConstructor: bless(definition.controllerConstructor)
|
1524
|
+
controllerConstructor: bless(definition.controllerConstructor),
|
1260
1525
|
};
|
1261
1526
|
}
|
1262
1527
|
|
@@ -1264,8 +1529,8 @@ class Module {
|
|
1264
1529
|
constructor(application, definition) {
|
1265
1530
|
this.application = application;
|
1266
1531
|
this.definition = blessDefinition(definition);
|
1267
|
-
this.contextsByScope = new WeakMap;
|
1268
|
-
this.connectedContexts = new Set;
|
1532
|
+
this.contextsByScope = new WeakMap();
|
1533
|
+
this.connectedContexts = new Set();
|
1269
1534
|
}
|
1270
1535
|
get identifier() {
|
1271
1536
|
return this.definition.identifier;
|
@@ -1363,13 +1628,13 @@ class DataMap {
|
|
1363
1628
|
|
1364
1629
|
class Guide {
|
1365
1630
|
constructor(logger) {
|
1366
|
-
this.warnedKeysByObject = new WeakMap;
|
1631
|
+
this.warnedKeysByObject = new WeakMap();
|
1367
1632
|
this.logger = logger;
|
1368
1633
|
}
|
1369
1634
|
warn(object, key, message) {
|
1370
1635
|
let warnedKeys = this.warnedKeysByObject.get(object);
|
1371
1636
|
if (!warnedKeys) {
|
1372
|
-
warnedKeys = new Set;
|
1637
|
+
warnedKeys = new Set();
|
1373
1638
|
this.warnedKeysByObject.set(object, warnedKeys);
|
1374
1639
|
}
|
1375
1640
|
if (!warnedKeys.has(key)) {
|
@@ -1400,15 +1665,13 @@ class TargetSet {
|
|
1400
1665
|
return this.find(targetName) != null;
|
1401
1666
|
}
|
1402
1667
|
find(...targetNames) {
|
1403
|
-
return targetNames.reduce((target, targetName) => target
|
1404
|
-
|| this.findTarget(targetName)
|
1405
|
-
|| this.findLegacyTarget(targetName), undefined);
|
1668
|
+
return targetNames.reduce((target, targetName) => target || this.findTarget(targetName) || this.findLegacyTarget(targetName), undefined);
|
1406
1669
|
}
|
1407
1670
|
findAll(...targetNames) {
|
1408
1671
|
return targetNames.reduce((targets, targetName) => [
|
1409
1672
|
...targets,
|
1410
1673
|
...this.findAllTargets(targetName),
|
1411
|
-
...this.findAllLegacyTargets(targetName)
|
1674
|
+
...this.findAllLegacyTargets(targetName),
|
1412
1675
|
], []);
|
1413
1676
|
}
|
1414
1677
|
findTarget(targetName) {
|
@@ -1429,7 +1692,7 @@ class TargetSet {
|
|
1429
1692
|
}
|
1430
1693
|
findAllLegacyTargets(targetName) {
|
1431
1694
|
const selector = this.getLegacySelectorForTargetName(targetName);
|
1432
|
-
return this.scope.findAllElements(selector).map(element => this.deprecate(element, targetName));
|
1695
|
+
return this.scope.findAllElements(selector).map((element) => this.deprecate(element, targetName));
|
1433
1696
|
}
|
1434
1697
|
getLegacySelectorForTargetName(targetName) {
|
1435
1698
|
const targetDescriptor = `${this.identifier}.${targetName}`;
|
@@ -1450,6 +1713,56 @@ class TargetSet {
|
|
1450
1713
|
}
|
1451
1714
|
}
|
1452
1715
|
|
1716
|
+
class OutletSet {
|
1717
|
+
constructor(scope, controllerElement) {
|
1718
|
+
this.scope = scope;
|
1719
|
+
this.controllerElement = controllerElement;
|
1720
|
+
}
|
1721
|
+
get element() {
|
1722
|
+
return this.scope.element;
|
1723
|
+
}
|
1724
|
+
get identifier() {
|
1725
|
+
return this.scope.identifier;
|
1726
|
+
}
|
1727
|
+
get schema() {
|
1728
|
+
return this.scope.schema;
|
1729
|
+
}
|
1730
|
+
has(outletName) {
|
1731
|
+
return this.find(outletName) != null;
|
1732
|
+
}
|
1733
|
+
find(...outletNames) {
|
1734
|
+
return outletNames.reduce((outlet, outletName) => outlet || this.findOutlet(outletName), undefined);
|
1735
|
+
}
|
1736
|
+
findAll(...outletNames) {
|
1737
|
+
return outletNames.reduce((outlets, outletName) => [...outlets, ...this.findAllOutlets(outletName)], []);
|
1738
|
+
}
|
1739
|
+
getSelectorForOutletName(outletName) {
|
1740
|
+
const attributeName = this.schema.outletAttributeForScope(this.identifier, outletName);
|
1741
|
+
return this.controllerElement.getAttribute(attributeName);
|
1742
|
+
}
|
1743
|
+
findOutlet(outletName) {
|
1744
|
+
const selector = this.getSelectorForOutletName(outletName);
|
1745
|
+
if (selector)
|
1746
|
+
return this.findElement(selector, outletName);
|
1747
|
+
}
|
1748
|
+
findAllOutlets(outletName) {
|
1749
|
+
const selector = this.getSelectorForOutletName(outletName);
|
1750
|
+
return selector ? this.findAllElements(selector, outletName) : [];
|
1751
|
+
}
|
1752
|
+
findElement(selector, outletName) {
|
1753
|
+
const elements = this.scope.queryElements(selector);
|
1754
|
+
return elements.filter((element) => this.matchesElement(element, selector, outletName))[0];
|
1755
|
+
}
|
1756
|
+
findAllElements(selector, outletName) {
|
1757
|
+
const elements = this.scope.queryElements(selector);
|
1758
|
+
return elements.filter((element) => this.matchesElement(element, selector, outletName));
|
1759
|
+
}
|
1760
|
+
matchesElement(element, selector, outletName) {
|
1761
|
+
const controllerAttribute = element.getAttribute(this.scope.schema.controllerAttribute) || "";
|
1762
|
+
return element.matches(selector) && controllerAttribute.split(" ").includes(outletName);
|
1763
|
+
}
|
1764
|
+
}
|
1765
|
+
|
1453
1766
|
class Scope {
|
1454
1767
|
constructor(schema, element, identifier, logger) {
|
1455
1768
|
this.targets = new TargetSet(this);
|
@@ -1462,16 +1775,15 @@ class Scope {
|
|
1462
1775
|
this.element = element;
|
1463
1776
|
this.identifier = identifier;
|
1464
1777
|
this.guide = new Guide(logger);
|
1778
|
+
this.outlets = new OutletSet(this.documentScope, element);
|
1465
1779
|
}
|
1466
1780
|
findElement(selector) {
|
1467
|
-
return this.element.matches(selector)
|
1468
|
-
? this.element
|
1469
|
-
: this.queryElements(selector).find(this.containsElement);
|
1781
|
+
return this.element.matches(selector) ? this.element : this.queryElements(selector).find(this.containsElement);
|
1470
1782
|
}
|
1471
1783
|
findAllElements(selector) {
|
1472
1784
|
return [
|
1473
|
-
...this.element.matches(selector) ? [this.element] : [],
|
1474
|
-
...this.queryElements(selector).filter(this.containsElement)
|
1785
|
+
...(this.element.matches(selector) ? [this.element] : []),
|
1786
|
+
...this.queryElements(selector).filter(this.containsElement),
|
1475
1787
|
];
|
1476
1788
|
}
|
1477
1789
|
queryElements(selector) {
|
@@ -1480,6 +1792,14 @@ class Scope {
|
|
1480
1792
|
get controllerSelector() {
|
1481
1793
|
return attributeValueContainsToken(this.schema.controllerAttribute, this.identifier);
|
1482
1794
|
}
|
1795
|
+
get isDocumentScope() {
|
1796
|
+
return this.element === document.documentElement;
|
1797
|
+
}
|
1798
|
+
get documentScope() {
|
1799
|
+
return this.isDocumentScope
|
1800
|
+
? this
|
1801
|
+
: new Scope(this.schema, document.documentElement, this.identifier, this.guide.logger);
|
1802
|
+
}
|
1483
1803
|
}
|
1484
1804
|
|
1485
1805
|
class ScopeObserver {
|
@@ -1488,8 +1808,8 @@ class ScopeObserver {
|
|
1488
1808
|
this.schema = schema;
|
1489
1809
|
this.delegate = delegate;
|
1490
1810
|
this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this);
|
1491
|
-
this.scopesByIdentifierByElement = new WeakMap;
|
1492
|
-
this.scopeReferenceCounts = new WeakMap;
|
1811
|
+
this.scopesByIdentifierByElement = new WeakMap();
|
1812
|
+
this.scopeReferenceCounts = new WeakMap();
|
1493
1813
|
}
|
1494
1814
|
start() {
|
1495
1815
|
this.valueListObserver.start();
|
@@ -1529,7 +1849,7 @@ class ScopeObserver {
|
|
1529
1849
|
fetchScopesByIdentifierForElement(element) {
|
1530
1850
|
let scopesByIdentifier = this.scopesByIdentifierByElement.get(element);
|
1531
1851
|
if (!scopesByIdentifier) {
|
1532
|
-
scopesByIdentifier = new Map;
|
1852
|
+
scopesByIdentifier = new Map();
|
1533
1853
|
this.scopesByIdentifierByElement.set(element, scopesByIdentifier);
|
1534
1854
|
}
|
1535
1855
|
return scopesByIdentifier;
|
@@ -1540,8 +1860,8 @@ class Router {
|
|
1540
1860
|
constructor(application) {
|
1541
1861
|
this.application = application;
|
1542
1862
|
this.scopeObserver = new ScopeObserver(this.element, this.schema, this);
|
1543
|
-
this.scopesByIdentifier = new Multimap;
|
1544
|
-
this.modulesByIdentifier = new Map;
|
1863
|
+
this.scopesByIdentifier = new Multimap();
|
1864
|
+
this.modulesByIdentifier = new Map();
|
1545
1865
|
}
|
1546
1866
|
get element() {
|
1547
1867
|
return this.application.element;
|
@@ -1571,6 +1891,10 @@ class Router {
|
|
1571
1891
|
this.unloadIdentifier(definition.identifier);
|
1572
1892
|
const module = new Module(this.application, definition);
|
1573
1893
|
this.connectModule(module);
|
1894
|
+
const afterLoad = definition.controllerConstructor.afterLoad;
|
1895
|
+
if (afterLoad) {
|
1896
|
+
afterLoad(definition.identifier, this.application);
|
1897
|
+
}
|
1574
1898
|
}
|
1575
1899
|
unloadIdentifier(identifier) {
|
1576
1900
|
const module = this.modulesByIdentifier.get(identifier);
|
@@ -1581,7 +1905,7 @@ class Router {
|
|
1581
1905
|
getContextForElementAndIdentifier(element, identifier) {
|
1582
1906
|
const module = this.modulesByIdentifier.get(identifier);
|
1583
1907
|
if (module) {
|
1584
|
-
return module.contexts.find(context => context.element == element);
|
1908
|
+
return module.contexts.find((context) => context.element == element);
|
1585
1909
|
}
|
1586
1910
|
}
|
1587
1911
|
handleError(error, message, detail) {
|
@@ -1607,12 +1931,12 @@ class Router {
|
|
1607
1931
|
connectModule(module) {
|
1608
1932
|
this.modulesByIdentifier.set(module.identifier, module);
|
1609
1933
|
const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
|
1610
|
-
scopes.forEach(scope => module.connectContextForScope(scope));
|
1934
|
+
scopes.forEach((scope) => module.connectContextForScope(scope));
|
1611
1935
|
}
|
1612
1936
|
disconnectModule(module) {
|
1613
1937
|
this.modulesByIdentifier.delete(module.identifier);
|
1614
1938
|
const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
|
1615
|
-
scopes.forEach(scope => module.disconnectContextForScope(scope));
|
1939
|
+
scopes.forEach((scope) => module.disconnectContextForScope(scope));
|
1616
1940
|
}
|
1617
1941
|
}
|
1618
1942
|
|
@@ -1620,8 +1944,13 @@ const defaultSchema = {
|
|
1620
1944
|
controllerAttribute: "data-controller",
|
1621
1945
|
actionAttribute: "data-action",
|
1622
1946
|
targetAttribute: "data-target",
|
1623
|
-
targetAttributeForScope: identifier => `data-${identifier}-target
|
1947
|
+
targetAttributeForScope: (identifier) => `data-${identifier}-target`,
|
1948
|
+
outletAttributeForScope: (identifier, outlet) => `data-${identifier}-${outlet}-outlet`,
|
1949
|
+
keyMappings: Object.assign(Object.assign({ enter: "Enter", tab: "Tab", esc: "Escape", space: " ", up: "ArrowUp", down: "ArrowDown", left: "ArrowLeft", right: "ArrowRight", home: "Home", end: "End" }, objectFromEntries("abcdefghijklmnopqrstuvwxyz".split("").map((c) => [c, c]))), objectFromEntries("0123456789".split("").map((n) => [n, n]))),
|
1624
1950
|
};
|
1951
|
+
function objectFromEntries(array) {
|
1952
|
+
return array.reduce((memo, [k, v]) => (Object.assign(Object.assign({}, memo), { [k]: v })), {});
|
1953
|
+
}
|
1625
1954
|
|
1626
1955
|
class Application {
|
1627
1956
|
constructor(element = document.documentElement, schema = defaultSchema) {
|
@@ -1636,9 +1965,10 @@ class Application {
|
|
1636
1965
|
this.schema = schema;
|
1637
1966
|
this.dispatcher = new Dispatcher(this);
|
1638
1967
|
this.router = new Router(this);
|
1968
|
+
this.actionDescriptorFilters = Object.assign({}, defaultActionDescriptorFilters);
|
1639
1969
|
}
|
1640
1970
|
static start(element, schema) {
|
1641
|
-
const application = new
|
1971
|
+
const application = new this(element, schema);
|
1642
1972
|
application.start();
|
1643
1973
|
return application;
|
1644
1974
|
}
|
@@ -1658,9 +1988,12 @@ class Application {
|
|
1658
1988
|
register(identifier, controllerConstructor) {
|
1659
1989
|
this.load({ identifier, controllerConstructor });
|
1660
1990
|
}
|
1991
|
+
registerActionOption(name, filter) {
|
1992
|
+
this.actionDescriptorFilters[name] = filter;
|
1993
|
+
}
|
1661
1994
|
load(head, ...rest) {
|
1662
1995
|
const definitions = Array.isArray(head) ? head : [head, ...rest];
|
1663
|
-
definitions.forEach(definition => {
|
1996
|
+
definitions.forEach((definition) => {
|
1664
1997
|
if (definition.controllerConstructor.shouldLoad) {
|
1665
1998
|
this.router.loadDefinition(definition);
|
1666
1999
|
}
|
@@ -1668,10 +2001,10 @@ class Application {
|
|
1668
2001
|
}
|
1669
2002
|
unload(head, ...rest) {
|
1670
2003
|
const identifiers = Array.isArray(head) ? head : [head, ...rest];
|
1671
|
-
identifiers.forEach(identifier => this.router.unloadIdentifier(identifier));
|
2004
|
+
identifiers.forEach((identifier) => this.router.unloadIdentifier(identifier));
|
1672
2005
|
}
|
1673
2006
|
get controllers() {
|
1674
|
-
return this.router.contexts.map(context => context.controller);
|
2007
|
+
return this.router.contexts.map((context) => context.controller);
|
1675
2008
|
}
|
1676
2009
|
getControllerForElementAndIdentifier(element, identifier) {
|
1677
2010
|
const context = this.router.getContextForElementAndIdentifier(element, identifier);
|
@@ -1690,7 +2023,7 @@ class Application {
|
|
1690
2023
|
}
|
1691
2024
|
}
|
1692
2025
|
function domReady() {
|
1693
|
-
return new Promise(resolve => {
|
2026
|
+
return new Promise((resolve) => {
|
1694
2027
|
if (document.readyState == "loading") {
|
1695
2028
|
document.addEventListener("DOMContentLoaded", () => resolve());
|
1696
2029
|
}
|
@@ -1718,18 +2051,85 @@ function propertiesForClassDefinition(key) {
|
|
1718
2051
|
const attribute = classes.getAttributeName(key);
|
1719
2052
|
throw new Error(`Missing attribute "${attribute}"`);
|
1720
2053
|
}
|
1721
|
-
}
|
2054
|
+
},
|
1722
2055
|
},
|
1723
2056
|
[`${key}Classes`]: {
|
1724
2057
|
get() {
|
1725
2058
|
return this.classes.getAll(key);
|
1726
|
-
}
|
2059
|
+
},
|
1727
2060
|
},
|
1728
2061
|
[`has${capitalize(key)}Class`]: {
|
1729
2062
|
get() {
|
1730
2063
|
return this.classes.has(key);
|
1731
|
-
}
|
1732
|
-
}
|
2064
|
+
},
|
2065
|
+
},
|
2066
|
+
};
|
2067
|
+
}
|
2068
|
+
|
2069
|
+
function OutletPropertiesBlessing(constructor) {
|
2070
|
+
const outlets = readInheritableStaticArrayValues(constructor, "outlets");
|
2071
|
+
return outlets.reduce((properties, outletDefinition) => {
|
2072
|
+
return Object.assign(properties, propertiesForOutletDefinition(outletDefinition));
|
2073
|
+
}, {});
|
2074
|
+
}
|
2075
|
+
function propertiesForOutletDefinition(name) {
|
2076
|
+
const camelizedName = namespaceCamelize(name);
|
2077
|
+
return {
|
2078
|
+
[`${camelizedName}Outlet`]: {
|
2079
|
+
get() {
|
2080
|
+
const outlet = this.outlets.find(name);
|
2081
|
+
if (outlet) {
|
2082
|
+
const outletController = this.application.getControllerForElementAndIdentifier(outlet, name);
|
2083
|
+
if (outletController) {
|
2084
|
+
return outletController;
|
2085
|
+
}
|
2086
|
+
else {
|
2087
|
+
throw new Error(`Missing "data-controller=${name}" attribute on outlet element for "${this.identifier}" controller`);
|
2088
|
+
}
|
2089
|
+
}
|
2090
|
+
throw new Error(`Missing outlet element "${name}" for "${this.identifier}" controller`);
|
2091
|
+
},
|
2092
|
+
},
|
2093
|
+
[`${camelizedName}Outlets`]: {
|
2094
|
+
get() {
|
2095
|
+
const outlets = this.outlets.findAll(name);
|
2096
|
+
if (outlets.length > 0) {
|
2097
|
+
return outlets
|
2098
|
+
.map((outlet) => {
|
2099
|
+
const controller = this.application.getControllerForElementAndIdentifier(outlet, name);
|
2100
|
+
if (controller) {
|
2101
|
+
return controller;
|
2102
|
+
}
|
2103
|
+
else {
|
2104
|
+
console.warn(`The provided outlet element is missing the outlet controller "${name}" for "${this.identifier}"`, outlet);
|
2105
|
+
}
|
2106
|
+
})
|
2107
|
+
.filter((controller) => controller);
|
2108
|
+
}
|
2109
|
+
return [];
|
2110
|
+
},
|
2111
|
+
},
|
2112
|
+
[`${camelizedName}OutletElement`]: {
|
2113
|
+
get() {
|
2114
|
+
const outlet = this.outlets.find(name);
|
2115
|
+
if (outlet) {
|
2116
|
+
return outlet;
|
2117
|
+
}
|
2118
|
+
else {
|
2119
|
+
throw new Error(`Missing outlet element "${name}" for "${this.identifier}" controller`);
|
2120
|
+
}
|
2121
|
+
},
|
2122
|
+
},
|
2123
|
+
[`${camelizedName}OutletElements`]: {
|
2124
|
+
get() {
|
2125
|
+
return this.outlets.findAll(name);
|
2126
|
+
},
|
2127
|
+
},
|
2128
|
+
[`has${capitalize(camelizedName)}Outlet`]: {
|
2129
|
+
get() {
|
2130
|
+
return this.outlets.has(name);
|
2131
|
+
},
|
2132
|
+
},
|
1733
2133
|
};
|
1734
2134
|
}
|
1735
2135
|
|
@@ -1750,18 +2150,18 @@ function propertiesForTargetDefinition(name) {
|
|
1750
2150
|
else {
|
1751
2151
|
throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`);
|
1752
2152
|
}
|
1753
|
-
}
|
2153
|
+
},
|
1754
2154
|
},
|
1755
2155
|
[`${name}Targets`]: {
|
1756
2156
|
get() {
|
1757
2157
|
return this.targets.findAll(name);
|
1758
|
-
}
|
2158
|
+
},
|
1759
2159
|
},
|
1760
2160
|
[`has${capitalize(name)}Target`]: {
|
1761
2161
|
get() {
|
1762
2162
|
return this.targets.has(name);
|
1763
|
-
}
|
1764
|
-
}
|
2163
|
+
},
|
2164
|
+
},
|
1765
2165
|
};
|
1766
2166
|
}
|
1767
2167
|
|
@@ -1775,8 +2175,8 @@ function ValuePropertiesBlessing(constructor) {
|
|
1775
2175
|
const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key);
|
1776
2176
|
return Object.assign(result, { [attributeName]: valueDescriptor });
|
1777
2177
|
}, {});
|
1778
|
-
}
|
1779
|
-
}
|
2178
|
+
},
|
2179
|
+
},
|
1780
2180
|
};
|
1781
2181
|
return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => {
|
1782
2182
|
return Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair));
|
@@ -1803,13 +2203,13 @@ function propertiesForValueDefinitionPair(valueDefinitionPair, controller) {
|
|
1803
2203
|
else {
|
1804
2204
|
this.data.set(key, write(value));
|
1805
2205
|
}
|
1806
|
-
}
|
2206
|
+
},
|
1807
2207
|
},
|
1808
2208
|
[`has${capitalize(name)}`]: {
|
1809
2209
|
get() {
|
1810
2210
|
return this.data.has(key) || definition.hasCustomDefaultValue;
|
1811
|
-
}
|
1812
|
-
}
|
2211
|
+
},
|
2212
|
+
},
|
1813
2213
|
};
|
1814
2214
|
}
|
1815
2215
|
function parseValueDefinitionPair([token, typeDefinition], controller) {
|
@@ -1821,18 +2221,26 @@ function parseValueDefinitionPair([token, typeDefinition], controller) {
|
|
1821
2221
|
}
|
1822
2222
|
function parseValueTypeConstant(constant) {
|
1823
2223
|
switch (constant) {
|
1824
|
-
case Array:
|
1825
|
-
|
1826
|
-
case
|
1827
|
-
|
1828
|
-
case
|
2224
|
+
case Array:
|
2225
|
+
return "array";
|
2226
|
+
case Boolean:
|
2227
|
+
return "boolean";
|
2228
|
+
case Number:
|
2229
|
+
return "number";
|
2230
|
+
case Object:
|
2231
|
+
return "object";
|
2232
|
+
case String:
|
2233
|
+
return "string";
|
1829
2234
|
}
|
1830
2235
|
}
|
1831
2236
|
function parseValueTypeDefault(defaultValue) {
|
1832
2237
|
switch (typeof defaultValue) {
|
1833
|
-
case "boolean":
|
1834
|
-
|
1835
|
-
case "
|
2238
|
+
case "boolean":
|
2239
|
+
return "boolean";
|
2240
|
+
case "number":
|
2241
|
+
return "number";
|
2242
|
+
case "string":
|
2243
|
+
return "string";
|
1836
2244
|
}
|
1837
2245
|
if (Array.isArray(defaultValue))
|
1838
2246
|
return "array";
|
@@ -1854,7 +2262,7 @@ function parseValueTypeDefinition(payload) {
|
|
1854
2262
|
const typeFromObject = parseValueTypeObject({
|
1855
2263
|
controller: payload.controller,
|
1856
2264
|
token: payload.token,
|
1857
|
-
typeObject: payload.typeDefinition
|
2265
|
+
typeObject: payload.typeDefinition,
|
1858
2266
|
});
|
1859
2267
|
const typeFromDefaultValue = parseValueTypeDefault(payload.typeDefinition);
|
1860
2268
|
const typeFromConstant = parseValueTypeConstant(payload.typeDefinition);
|
@@ -1880,18 +2288,26 @@ function valueDescriptorForTokenAndTypeDefinition(payload) {
|
|
1880
2288
|
type,
|
1881
2289
|
key,
|
1882
2290
|
name: camelize(key),
|
1883
|
-
get defaultValue() {
|
1884
|
-
|
2291
|
+
get defaultValue() {
|
2292
|
+
return defaultValueForDefinition(payload.typeDefinition);
|
2293
|
+
},
|
2294
|
+
get hasCustomDefaultValue() {
|
2295
|
+
return parseValueTypeDefault(payload.typeDefinition) !== undefined;
|
2296
|
+
},
|
1885
2297
|
reader: readers[type],
|
1886
|
-
writer: writers[type] || writers.default
|
2298
|
+
writer: writers[type] || writers.default,
|
1887
2299
|
};
|
1888
2300
|
}
|
1889
2301
|
const defaultValuesByType = {
|
1890
|
-
get array() {
|
2302
|
+
get array() {
|
2303
|
+
return [];
|
2304
|
+
},
|
1891
2305
|
boolean: false,
|
1892
2306
|
number: 0,
|
1893
|
-
get object() {
|
1894
|
-
|
2307
|
+
get object() {
|
2308
|
+
return {};
|
2309
|
+
},
|
2310
|
+
string: "",
|
1895
2311
|
};
|
1896
2312
|
const readers = {
|
1897
2313
|
array(value) {
|
@@ -1916,12 +2332,12 @@ const readers = {
|
|
1916
2332
|
},
|
1917
2333
|
string(value) {
|
1918
2334
|
return value;
|
1919
|
-
}
|
2335
|
+
},
|
1920
2336
|
};
|
1921
2337
|
const writers = {
|
1922
2338
|
default: writeString,
|
1923
2339
|
array: writeJSON,
|
1924
|
-
object: writeJSON
|
2340
|
+
object: writeJSON,
|
1925
2341
|
};
|
1926
2342
|
function writeJSON(value) {
|
1927
2343
|
return JSON.stringify(value);
|
@@ -1937,6 +2353,9 @@ class Controller {
|
|
1937
2353
|
static get shouldLoad() {
|
1938
2354
|
return true;
|
1939
2355
|
}
|
2356
|
+
static afterLoad(_identifier, _application) {
|
2357
|
+
return;
|
2358
|
+
}
|
1940
2359
|
get application() {
|
1941
2360
|
return this.context.application;
|
1942
2361
|
}
|
@@ -1952,6 +2371,9 @@ class Controller {
|
|
1952
2371
|
get targets() {
|
1953
2372
|
return this.scope.targets;
|
1954
2373
|
}
|
2374
|
+
get outlets() {
|
2375
|
+
return this.scope.outlets;
|
2376
|
+
}
|
1955
2377
|
get classes() {
|
1956
2378
|
return this.scope.classes;
|
1957
2379
|
}
|
@@ -1971,8 +2393,14 @@ class Controller {
|
|
1971
2393
|
return event;
|
1972
2394
|
}
|
1973
2395
|
}
|
1974
|
-
Controller.blessings = [
|
2396
|
+
Controller.blessings = [
|
2397
|
+
ClassPropertiesBlessing,
|
2398
|
+
TargetPropertiesBlessing,
|
2399
|
+
ValuePropertiesBlessing,
|
2400
|
+
OutletPropertiesBlessing,
|
2401
|
+
];
|
1975
2402
|
Controller.targets = [];
|
2403
|
+
Controller.outlets = [];
|
1976
2404
|
Controller.values = {};
|
1977
2405
|
|
1978
|
-
export { Application, AttributeObserver, Context, Controller, ElementObserver, IndexedMultimap, Multimap, StringMapObserver, TokenListObserver, ValueListObserver, add, defaultSchema, del, fetch, prune };
|
2406
|
+
export { Application, AttributeObserver, Context, Controller, ElementObserver, IndexedMultimap, Multimap, SelectorObserver, StringMapObserver, TokenListObserver, ValueListObserver, add, defaultSchema, del, fetch, prune };
|