stimulus-rails 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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);