stimulus-rails 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,7 +3,7 @@
3
3
  //= link ./stimulus-loading.js
4
4
 
5
5
  /*
6
- Stimulus 3.1.0
6
+ Stimulus 3.1.1
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,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).sort().forEach(key => {
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.split(":").reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {});
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) {
@@ -192,7 +235,7 @@ class Action {
192
235
  }
193
236
  get params() {
194
237
  const params = {};
195
- const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`);
238
+ const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i");
196
239
  for (const { name, value } of Array.from(this.element.attributes)) {
197
240
  const match = name.match(pattern);
198
241
  const key = match && match[1];
@@ -207,13 +250,13 @@ class Action {
207
250
  }
208
251
  }
209
252
  const defaultEventNames = {
210
- "a": e => "click",
211
- "button": e => "click",
212
- "form": e => "submit",
213
- "details": e => "toggle",
214
- "input": e => e.getAttribute("type") == "submit" ? "click" : "input",
215
- "select": e => "change",
216
- "textarea": e => "input"
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",
217
260
  };
218
261
  function getDefaultEventNameForElement(element) {
219
262
  const tagName = element.tagName.toLowerCase();
@@ -251,9 +294,7 @@ class Binding {
251
294
  return this.context.identifier;
252
295
  }
253
296
  handleEvent(event) {
254
- if (this.willBeInvokedByEvent(event) && this.shouldBeInvokedPerSelf(event)) {
255
- this.processStopPropagation(event);
256
- this.processPreventDefault(event);
297
+ if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(event)) {
257
298
  this.invokeWithEvent(event);
258
299
  }
259
300
  }
@@ -267,15 +308,20 @@ class Binding {
267
308
  }
268
309
  throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`);
269
310
  }
270
- processStopPropagation(event) {
271
- if (this.eventOptions.stop) {
272
- event.stopPropagation();
273
- }
274
- }
275
- processPreventDefault(event) {
276
- if (this.eventOptions.prevent) {
277
- event.preventDefault();
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
+ }
278
323
  }
324
+ return passes;
279
325
  }
280
326
  invokeWithEvent(event) {
281
327
  const { target, currentTarget } = event;
@@ -291,14 +337,6 @@ class Binding {
291
337
  this.context.handleError(error, `invoking action "${this.action}"`, detail);
292
338
  }
293
339
  }
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
340
  willBeInvokedByEvent(event) {
303
341
  const eventTarget = event.target;
304
342
  if (this.element === eventTarget) {
@@ -331,7 +369,7 @@ class ElementObserver {
331
369
  this.element = element;
332
370
  this.started = false;
333
371
  this.delegate = delegate;
334
- this.elements = new Set;
372
+ this.elements = new Set();
335
373
  this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
336
374
  }
337
375
  start() {
@@ -519,8 +557,8 @@ class StringMapObserver {
519
557
  this.element = element;
520
558
  this.delegate = delegate;
521
559
  this.started = false;
522
- this.stringMap = new Map;
523
- this.mutationObserver = new MutationObserver(mutations => this.processMutations(mutations));
560
+ this.stringMap = new Map();
561
+ this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
524
562
  }
525
563
  start() {
526
564
  if (!this.started) {
@@ -596,7 +634,7 @@ class StringMapObserver {
596
634
  return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)));
597
635
  }
598
636
  get currentAttributeNames() {
599
- return Array.from(this.element.attributes).map(attribute => attribute.name);
637
+ return Array.from(this.element.attributes).map((attribute) => attribute.name);
600
638
  }
601
639
  get recordedAttributeNames() {
602
640
  return Array.from(this.stringMap.keys());
@@ -655,7 +693,7 @@ class Multimap {
655
693
  }
656
694
  hasValue(value) {
657
695
  const sets = Array.from(this.valuesByKey.values());
658
- return sets.some(set => set.has(value));
696
+ return sets.some((set) => set.has(value));
659
697
  }
660
698
  getValuesForKey(key) {
661
699
  const values = this.valuesByKey.get(key);
@@ -663,15 +701,15 @@ class Multimap {
663
701
  }
664
702
  getKeysForValue(value) {
665
703
  return Array.from(this.valuesByKey)
666
- .filter(([key, values]) => values.has(value))
667
- .map(([key, values]) => key);
704
+ .filter(([_key, values]) => values.has(value))
705
+ .map(([key, _values]) => key);
668
706
  }
669
707
  }
670
708
 
671
709
  class IndexedMultimap extends Multimap {
672
710
  constructor() {
673
711
  super();
674
- this.keysByValue = new Map;
712
+ this.keysByValue = new Map();
675
713
  }
676
714
  get values() {
677
715
  return Array.from(this.keysByValue.keys());
@@ -697,7 +735,7 @@ class TokenListObserver {
697
735
  constructor(element, attributeName, delegate) {
698
736
  this.attributeObserver = new AttributeObserver(element, attributeName, this);
699
737
  this.delegate = delegate;
700
- this.tokensByElement = new Multimap;
738
+ this.tokensByElement = new Multimap();
701
739
  }
702
740
  get started() {
703
741
  return this.attributeObserver.started;
@@ -732,10 +770,10 @@ class TokenListObserver {
732
770
  this.tokensUnmatched(this.tokensByElement.getValuesForKey(element));
733
771
  }
734
772
  tokensMatched(tokens) {
735
- tokens.forEach(token => this.tokenMatched(token));
773
+ tokens.forEach((token) => this.tokenMatched(token));
736
774
  }
737
775
  tokensUnmatched(tokens) {
738
- tokens.forEach(token => this.tokenUnmatched(token));
776
+ tokens.forEach((token) => this.tokenUnmatched(token));
739
777
  }
740
778
  tokenMatched(token) {
741
779
  this.delegate.tokenMatched(token);
@@ -748,8 +786,7 @@ class TokenListObserver {
748
786
  refreshTokensForElement(element) {
749
787
  const previousTokens = this.tokensByElement.getValuesForKey(element);
750
788
  const currentTokens = this.readTokensForElement(element);
751
- const firstDifferingIndex = zip(previousTokens, currentTokens)
752
- .findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
789
+ const firstDifferingIndex = zip(previousTokens, currentTokens).findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
753
790
  if (firstDifferingIndex == -1) {
754
791
  return [[], []];
755
792
  }
@@ -764,7 +801,10 @@ class TokenListObserver {
764
801
  }
765
802
  }
766
803
  function parseTokenString(tokenString, element, attributeName) {
767
- return tokenString.trim().split(/\s+/).filter(content => content.length)
804
+ return tokenString
805
+ .trim()
806
+ .split(/\s+/)
807
+ .filter((content) => content.length)
768
808
  .map((content, index) => ({ element, attributeName, content, index }));
769
809
  }
770
810
  function zip(left, right) {
@@ -779,8 +819,8 @@ class ValueListObserver {
779
819
  constructor(element, attributeName, delegate) {
780
820
  this.tokenListObserver = new TokenListObserver(element, attributeName, this);
781
821
  this.delegate = delegate;
782
- this.parseResultsByToken = new WeakMap;
783
- this.valuesByTokenByElement = new WeakMap;
822
+ this.parseResultsByToken = new WeakMap();
823
+ this.valuesByTokenByElement = new WeakMap();
784
824
  }
785
825
  get started() {
786
826
  return this.tokenListObserver.started;
@@ -827,7 +867,7 @@ class ValueListObserver {
827
867
  fetchValuesByTokenForElement(element) {
828
868
  let valuesByToken = this.valuesByTokenByElement.get(element);
829
869
  if (!valuesByToken) {
830
- valuesByToken = new Map;
870
+ valuesByToken = new Map();
831
871
  this.valuesByTokenByElement.set(element, valuesByToken);
832
872
  }
833
873
  return valuesByToken;
@@ -847,7 +887,7 @@ class BindingObserver {
847
887
  constructor(context, delegate) {
848
888
  this.context = context;
849
889
  this.delegate = delegate;
850
- this.bindingsByAction = new Map;
890
+ this.bindingsByAction = new Map();
851
891
  }
852
892
  start() {
853
893
  if (!this.valueListObserver) {
@@ -890,7 +930,7 @@ class BindingObserver {
890
930
  }
891
931
  }
892
932
  disconnectAllActions() {
893
- this.bindings.forEach(binding => this.delegate.bindingDisconnected(binding));
933
+ this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding, true));
894
934
  this.bindingsByAction.clear();
895
935
  }
896
936
  parseValueForToken(token) {
@@ -977,19 +1017,20 @@ class ValueObserver {
977
1017
  changedMethod.call(this.receiver, value, oldValue);
978
1018
  }
979
1019
  catch (error) {
980
- if (!(error instanceof TypeError))
981
- throw error;
982
- throw new TypeError(`Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error.message}`);
1020
+ if (error instanceof TypeError) {
1021
+ error.message = `Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error.message}`;
1022
+ }
1023
+ throw error;
983
1024
  }
984
1025
  }
985
1026
  }
986
1027
  get valueDescriptors() {
987
1028
  const { valueDescriptorMap } = this;
988
- return Object.keys(valueDescriptorMap).map(key => valueDescriptorMap[key]);
1029
+ return Object.keys(valueDescriptorMap).map((key) => valueDescriptorMap[key]);
989
1030
  }
990
1031
  get valueDescriptorNameMap() {
991
1032
  const descriptors = {};
992
- Object.keys(this.valueDescriptorMap).forEach(key => {
1033
+ Object.keys(this.valueDescriptorMap).forEach((key) => {
993
1034
  const descriptor = this.valueDescriptorMap[key];
994
1035
  descriptors[descriptor.name] = descriptor;
995
1036
  });
@@ -1006,7 +1047,7 @@ class TargetObserver {
1006
1047
  constructor(context, delegate) {
1007
1048
  this.context = context;
1008
1049
  this.delegate = delegate;
1009
- this.targetsByName = new Multimap;
1050
+ this.targetsByName = new Multimap();
1010
1051
  }
1011
1052
  start() {
1012
1053
  if (!this.tokenListObserver) {
@@ -1146,9 +1187,9 @@ class Context {
1146
1187
  function readInheritableStaticArrayValues(constructor, propertyName) {
1147
1188
  const ancestors = getAncestorsForConstructor(constructor);
1148
1189
  return Array.from(ancestors.reduce((values, constructor) => {
1149
- getOwnStaticArrayValues(constructor, propertyName).forEach(name => values.add(name));
1190
+ getOwnStaticArrayValues(constructor, propertyName).forEach((name) => values.add(name));
1150
1191
  return values;
1151
- }, new Set));
1192
+ }, new Set()));
1152
1193
  }
1153
1194
  function readInheritableStaticObjectPairs(constructor, propertyName) {
1154
1195
  const ancestors = getAncestorsForConstructor(constructor);
@@ -1171,7 +1212,7 @@ function getOwnStaticArrayValues(constructor, propertyName) {
1171
1212
  }
1172
1213
  function getOwnStaticObjectPairs(constructor, propertyName) {
1173
1214
  const definition = constructor[propertyName];
1174
- return definition ? Object.keys(definition).map(key => [key, definition[key]]) : [];
1215
+ return definition ? Object.keys(definition).map((key) => [key, definition[key]]) : [];
1175
1216
  }
1176
1217
 
1177
1218
  function bless(constructor) {
@@ -1217,10 +1258,7 @@ function getShadowedDescriptor(prototype, properties, key) {
1217
1258
  }
1218
1259
  const getOwnKeys = (() => {
1219
1260
  if (typeof Object.getOwnPropertySymbols == "function") {
1220
- return (object) => [
1221
- ...Object.getOwnPropertyNames(object),
1222
- ...Object.getOwnPropertySymbols(object)
1223
- ];
1261
+ return (object) => [...Object.getOwnPropertyNames(object), ...Object.getOwnPropertySymbols(object)];
1224
1262
  }
1225
1263
  else {
1226
1264
  return Object.getOwnPropertyNames;
@@ -1232,16 +1270,18 @@ const extend = (() => {
1232
1270
  return Reflect.construct(constructor, arguments, new.target);
1233
1271
  }
1234
1272
  extended.prototype = Object.create(constructor.prototype, {
1235
- constructor: { value: extended }
1273
+ constructor: { value: extended },
1236
1274
  });
1237
1275
  Reflect.setPrototypeOf(extended, constructor);
1238
1276
  return extended;
1239
1277
  }
1240
1278
  function testReflectExtension() {
1241
- const a = function () { this.a.call(this); };
1279
+ const a = function () {
1280
+ this.a.call(this);
1281
+ };
1242
1282
  const b = extendWithReflect(a);
1243
1283
  b.prototype.a = function () { };
1244
- return new b;
1284
+ return new b();
1245
1285
  }
1246
1286
  try {
1247
1287
  testReflectExtension();
@@ -1256,7 +1296,7 @@ const extend = (() => {
1256
1296
  function blessDefinition(definition) {
1257
1297
  return {
1258
1298
  identifier: definition.identifier,
1259
- controllerConstructor: bless(definition.controllerConstructor)
1299
+ controllerConstructor: bless(definition.controllerConstructor),
1260
1300
  };
1261
1301
  }
1262
1302
 
@@ -1264,8 +1304,8 @@ class Module {
1264
1304
  constructor(application, definition) {
1265
1305
  this.application = application;
1266
1306
  this.definition = blessDefinition(definition);
1267
- this.contextsByScope = new WeakMap;
1268
- this.connectedContexts = new Set;
1307
+ this.contextsByScope = new WeakMap();
1308
+ this.connectedContexts = new Set();
1269
1309
  }
1270
1310
  get identifier() {
1271
1311
  return this.definition.identifier;
@@ -1363,13 +1403,13 @@ class DataMap {
1363
1403
 
1364
1404
  class Guide {
1365
1405
  constructor(logger) {
1366
- this.warnedKeysByObject = new WeakMap;
1406
+ this.warnedKeysByObject = new WeakMap();
1367
1407
  this.logger = logger;
1368
1408
  }
1369
1409
  warn(object, key, message) {
1370
1410
  let warnedKeys = this.warnedKeysByObject.get(object);
1371
1411
  if (!warnedKeys) {
1372
- warnedKeys = new Set;
1412
+ warnedKeys = new Set();
1373
1413
  this.warnedKeysByObject.set(object, warnedKeys);
1374
1414
  }
1375
1415
  if (!warnedKeys.has(key)) {
@@ -1400,15 +1440,13 @@ class TargetSet {
1400
1440
  return this.find(targetName) != null;
1401
1441
  }
1402
1442
  find(...targetNames) {
1403
- return targetNames.reduce((target, targetName) => target
1404
- || this.findTarget(targetName)
1405
- || this.findLegacyTarget(targetName), undefined);
1443
+ return targetNames.reduce((target, targetName) => target || this.findTarget(targetName) || this.findLegacyTarget(targetName), undefined);
1406
1444
  }
1407
1445
  findAll(...targetNames) {
1408
1446
  return targetNames.reduce((targets, targetName) => [
1409
1447
  ...targets,
1410
1448
  ...this.findAllTargets(targetName),
1411
- ...this.findAllLegacyTargets(targetName)
1449
+ ...this.findAllLegacyTargets(targetName),
1412
1450
  ], []);
1413
1451
  }
1414
1452
  findTarget(targetName) {
@@ -1429,7 +1467,7 @@ class TargetSet {
1429
1467
  }
1430
1468
  findAllLegacyTargets(targetName) {
1431
1469
  const selector = this.getLegacySelectorForTargetName(targetName);
1432
- return this.scope.findAllElements(selector).map(element => this.deprecate(element, targetName));
1470
+ return this.scope.findAllElements(selector).map((element) => this.deprecate(element, targetName));
1433
1471
  }
1434
1472
  getLegacySelectorForTargetName(targetName) {
1435
1473
  const targetDescriptor = `${this.identifier}.${targetName}`;
@@ -1464,14 +1502,12 @@ class Scope {
1464
1502
  this.guide = new Guide(logger);
1465
1503
  }
1466
1504
  findElement(selector) {
1467
- return this.element.matches(selector)
1468
- ? this.element
1469
- : this.queryElements(selector).find(this.containsElement);
1505
+ return this.element.matches(selector) ? this.element : this.queryElements(selector).find(this.containsElement);
1470
1506
  }
1471
1507
  findAllElements(selector) {
1472
1508
  return [
1473
- ...this.element.matches(selector) ? [this.element] : [],
1474
- ...this.queryElements(selector).filter(this.containsElement)
1509
+ ...(this.element.matches(selector) ? [this.element] : []),
1510
+ ...this.queryElements(selector).filter(this.containsElement),
1475
1511
  ];
1476
1512
  }
1477
1513
  queryElements(selector) {
@@ -1488,8 +1524,8 @@ class ScopeObserver {
1488
1524
  this.schema = schema;
1489
1525
  this.delegate = delegate;
1490
1526
  this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this);
1491
- this.scopesByIdentifierByElement = new WeakMap;
1492
- this.scopeReferenceCounts = new WeakMap;
1527
+ this.scopesByIdentifierByElement = new WeakMap();
1528
+ this.scopeReferenceCounts = new WeakMap();
1493
1529
  }
1494
1530
  start() {
1495
1531
  this.valueListObserver.start();
@@ -1529,7 +1565,7 @@ class ScopeObserver {
1529
1565
  fetchScopesByIdentifierForElement(element) {
1530
1566
  let scopesByIdentifier = this.scopesByIdentifierByElement.get(element);
1531
1567
  if (!scopesByIdentifier) {
1532
- scopesByIdentifier = new Map;
1568
+ scopesByIdentifier = new Map();
1533
1569
  this.scopesByIdentifierByElement.set(element, scopesByIdentifier);
1534
1570
  }
1535
1571
  return scopesByIdentifier;
@@ -1540,8 +1576,8 @@ class Router {
1540
1576
  constructor(application) {
1541
1577
  this.application = application;
1542
1578
  this.scopeObserver = new ScopeObserver(this.element, this.schema, this);
1543
- this.scopesByIdentifier = new Multimap;
1544
- this.modulesByIdentifier = new Map;
1579
+ this.scopesByIdentifier = new Multimap();
1580
+ this.modulesByIdentifier = new Map();
1545
1581
  }
1546
1582
  get element() {
1547
1583
  return this.application.element;
@@ -1581,7 +1617,7 @@ class Router {
1581
1617
  getContextForElementAndIdentifier(element, identifier) {
1582
1618
  const module = this.modulesByIdentifier.get(identifier);
1583
1619
  if (module) {
1584
- return module.contexts.find(context => context.element == element);
1620
+ return module.contexts.find((context) => context.element == element);
1585
1621
  }
1586
1622
  }
1587
1623
  handleError(error, message, detail) {
@@ -1607,12 +1643,12 @@ class Router {
1607
1643
  connectModule(module) {
1608
1644
  this.modulesByIdentifier.set(module.identifier, module);
1609
1645
  const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
1610
- scopes.forEach(scope => module.connectContextForScope(scope));
1646
+ scopes.forEach((scope) => module.connectContextForScope(scope));
1611
1647
  }
1612
1648
  disconnectModule(module) {
1613
1649
  this.modulesByIdentifier.delete(module.identifier);
1614
1650
  const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
1615
- scopes.forEach(scope => module.disconnectContextForScope(scope));
1651
+ scopes.forEach((scope) => module.disconnectContextForScope(scope));
1616
1652
  }
1617
1653
  }
1618
1654
 
@@ -1620,7 +1656,7 @@ const defaultSchema = {
1620
1656
  controllerAttribute: "data-controller",
1621
1657
  actionAttribute: "data-action",
1622
1658
  targetAttribute: "data-target",
1623
- targetAttributeForScope: identifier => `data-${identifier}-target`
1659
+ targetAttributeForScope: (identifier) => `data-${identifier}-target`,
1624
1660
  };
1625
1661
 
1626
1662
  class Application {
@@ -1636,6 +1672,7 @@ class Application {
1636
1672
  this.schema = schema;
1637
1673
  this.dispatcher = new Dispatcher(this);
1638
1674
  this.router = new Router(this);
1675
+ this.actionDescriptorFilters = Object.assign({}, defaultActionDescriptorFilters);
1639
1676
  }
1640
1677
  static start(element, schema) {
1641
1678
  const application = new Application(element, schema);
@@ -1658,9 +1695,12 @@ class Application {
1658
1695
  register(identifier, controllerConstructor) {
1659
1696
  this.load({ identifier, controllerConstructor });
1660
1697
  }
1698
+ registerActionOption(name, filter) {
1699
+ this.actionDescriptorFilters[name] = filter;
1700
+ }
1661
1701
  load(head, ...rest) {
1662
1702
  const definitions = Array.isArray(head) ? head : [head, ...rest];
1663
- definitions.forEach(definition => {
1703
+ definitions.forEach((definition) => {
1664
1704
  if (definition.controllerConstructor.shouldLoad) {
1665
1705
  this.router.loadDefinition(definition);
1666
1706
  }
@@ -1668,10 +1708,10 @@ class Application {
1668
1708
  }
1669
1709
  unload(head, ...rest) {
1670
1710
  const identifiers = Array.isArray(head) ? head : [head, ...rest];
1671
- identifiers.forEach(identifier => this.router.unloadIdentifier(identifier));
1711
+ identifiers.forEach((identifier) => this.router.unloadIdentifier(identifier));
1672
1712
  }
1673
1713
  get controllers() {
1674
- return this.router.contexts.map(context => context.controller);
1714
+ return this.router.contexts.map((context) => context.controller);
1675
1715
  }
1676
1716
  getControllerForElementAndIdentifier(element, identifier) {
1677
1717
  const context = this.router.getContextForElementAndIdentifier(element, identifier);
@@ -1690,7 +1730,7 @@ class Application {
1690
1730
  }
1691
1731
  }
1692
1732
  function domReady() {
1693
- return new Promise(resolve => {
1733
+ return new Promise((resolve) => {
1694
1734
  if (document.readyState == "loading") {
1695
1735
  document.addEventListener("DOMContentLoaded", () => resolve());
1696
1736
  }
@@ -1718,18 +1758,18 @@ function propertiesForClassDefinition(key) {
1718
1758
  const attribute = classes.getAttributeName(key);
1719
1759
  throw new Error(`Missing attribute "${attribute}"`);
1720
1760
  }
1721
- }
1761
+ },
1722
1762
  },
1723
1763
  [`${key}Classes`]: {
1724
1764
  get() {
1725
1765
  return this.classes.getAll(key);
1726
- }
1766
+ },
1727
1767
  },
1728
1768
  [`has${capitalize(key)}Class`]: {
1729
1769
  get() {
1730
1770
  return this.classes.has(key);
1731
- }
1732
- }
1771
+ },
1772
+ },
1733
1773
  };
1734
1774
  }
1735
1775
 
@@ -1750,18 +1790,18 @@ function propertiesForTargetDefinition(name) {
1750
1790
  else {
1751
1791
  throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`);
1752
1792
  }
1753
- }
1793
+ },
1754
1794
  },
1755
1795
  [`${name}Targets`]: {
1756
1796
  get() {
1757
1797
  return this.targets.findAll(name);
1758
- }
1798
+ },
1759
1799
  },
1760
1800
  [`has${capitalize(name)}Target`]: {
1761
1801
  get() {
1762
1802
  return this.targets.has(name);
1763
- }
1764
- }
1803
+ },
1804
+ },
1765
1805
  };
1766
1806
  }
1767
1807
 
@@ -1775,8 +1815,8 @@ function ValuePropertiesBlessing(constructor) {
1775
1815
  const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key);
1776
1816
  return Object.assign(result, { [attributeName]: valueDescriptor });
1777
1817
  }, {});
1778
- }
1779
- }
1818
+ },
1819
+ },
1780
1820
  };
1781
1821
  return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => {
1782
1822
  return Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair));
@@ -1803,13 +1843,13 @@ function propertiesForValueDefinitionPair(valueDefinitionPair, controller) {
1803
1843
  else {
1804
1844
  this.data.set(key, write(value));
1805
1845
  }
1806
- }
1846
+ },
1807
1847
  },
1808
1848
  [`has${capitalize(name)}`]: {
1809
1849
  get() {
1810
1850
  return this.data.has(key) || definition.hasCustomDefaultValue;
1811
- }
1812
- }
1851
+ },
1852
+ },
1813
1853
  };
1814
1854
  }
1815
1855
  function parseValueDefinitionPair([token, typeDefinition], controller) {
@@ -1821,18 +1861,26 @@ function parseValueDefinitionPair([token, typeDefinition], controller) {
1821
1861
  }
1822
1862
  function parseValueTypeConstant(constant) {
1823
1863
  switch (constant) {
1824
- case Array: return "array";
1825
- case Boolean: return "boolean";
1826
- case Number: return "number";
1827
- case Object: return "object";
1828
- case String: return "string";
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";
1829
1874
  }
1830
1875
  }
1831
1876
  function parseValueTypeDefault(defaultValue) {
1832
1877
  switch (typeof defaultValue) {
1833
- case "boolean": return "boolean";
1834
- case "number": return "number";
1835
- case "string": return "string";
1878
+ case "boolean":
1879
+ return "boolean";
1880
+ case "number":
1881
+ return "number";
1882
+ case "string":
1883
+ return "string";
1836
1884
  }
1837
1885
  if (Array.isArray(defaultValue))
1838
1886
  return "array";
@@ -1854,7 +1902,7 @@ function parseValueTypeDefinition(payload) {
1854
1902
  const typeFromObject = parseValueTypeObject({
1855
1903
  controller: payload.controller,
1856
1904
  token: payload.token,
1857
- typeObject: payload.typeDefinition
1905
+ typeObject: payload.typeDefinition,
1858
1906
  });
1859
1907
  const typeFromDefaultValue = parseValueTypeDefault(payload.typeDefinition);
1860
1908
  const typeFromConstant = parseValueTypeConstant(payload.typeDefinition);
@@ -1880,18 +1928,26 @@ function valueDescriptorForTokenAndTypeDefinition(payload) {
1880
1928
  type,
1881
1929
  key,
1882
1930
  name: camelize(key),
1883
- get defaultValue() { return defaultValueForDefinition(payload.typeDefinition); },
1884
- get hasCustomDefaultValue() { return parseValueTypeDefault(payload.typeDefinition) !== undefined; },
1931
+ get defaultValue() {
1932
+ return defaultValueForDefinition(payload.typeDefinition);
1933
+ },
1934
+ get hasCustomDefaultValue() {
1935
+ return parseValueTypeDefault(payload.typeDefinition) !== undefined;
1936
+ },
1885
1937
  reader: readers[type],
1886
- writer: writers[type] || writers.default
1938
+ writer: writers[type] || writers.default,
1887
1939
  };
1888
1940
  }
1889
1941
  const defaultValuesByType = {
1890
- get array() { return []; },
1942
+ get array() {
1943
+ return [];
1944
+ },
1891
1945
  boolean: false,
1892
1946
  number: 0,
1893
- get object() { return {}; },
1894
- string: ""
1947
+ get object() {
1948
+ return {};
1949
+ },
1950
+ string: "",
1895
1951
  };
1896
1952
  const readers = {
1897
1953
  array(value) {
@@ -1916,12 +1972,12 @@ const readers = {
1916
1972
  },
1917
1973
  string(value) {
1918
1974
  return value;
1919
- }
1975
+ },
1920
1976
  };
1921
1977
  const writers = {
1922
1978
  default: writeString,
1923
1979
  array: writeJSON,
1924
- object: writeJSON
1980
+ object: writeJSON,
1925
1981
  };
1926
1982
  function writeJSON(value) {
1927
1983
  return JSON.stringify(value);