stimulus-rails 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1703 +1,1926 @@
1
- //= link ./stimulus@3.0.0-beta.2.js
2
1
  //= link ./stimulus-autoloader.js
3
2
  //= link ./stimulus-importmap-autoloader.js
4
3
 
5
4
  /*
6
- Stimulus 2.0.0
7
- Copyright © 2020 Basecamp, LLC
5
+ Stimulus 3.0.0
6
+ Copyright © 2021 Basecamp, LLC
8
7
  */
9
8
  class EventListener {
10
- constructor(eventTarget, eventName, eventOptions) {
11
- this.eventTarget = eventTarget;
12
- this.eventName = eventName;
13
- this.eventOptions = eventOptions;
14
- this.unorderedBindings = new Set;
15
- }
16
- connect() {
17
- this.eventTarget.addEventListener(this.eventName, this, this.eventOptions);
18
- }
19
- disconnect() {
20
- this.eventTarget.removeEventListener(this.eventName, this, this.eventOptions);
21
- }
22
- bindingConnected(binding) {
23
- this.unorderedBindings.add(binding);
24
- }
25
- bindingDisconnected(binding) {
26
- this.unorderedBindings.delete(binding);
27
- }
28
- handleEvent(event) {
29
- const extendedEvent = extendEvent(event);
30
- for (const binding of this.bindings) {
31
- if (extendedEvent.immediatePropagationStopped) {
32
- break;
33
- } else {
34
- binding.handleEvent(extendedEvent);
35
- }
36
- }
37
- }
38
- get bindings() {
39
- return Array.from(this.unorderedBindings).sort(((left, right) => {
40
- const leftIndex = left.index, rightIndex = right.index;
41
- return leftIndex < rightIndex ? -1 : leftIndex > rightIndex ? 1 : 0;
42
- }));
43
- }
9
+ constructor(eventTarget, eventName, eventOptions) {
10
+ this.eventTarget = eventTarget;
11
+ this.eventName = eventName;
12
+ this.eventOptions = eventOptions;
13
+ this.unorderedBindings = new Set();
14
+ }
15
+ connect() {
16
+ this.eventTarget.addEventListener(this.eventName, this, this.eventOptions);
17
+ }
18
+ disconnect() {
19
+ this.eventTarget.removeEventListener(this.eventName, this, this.eventOptions);
20
+ }
21
+ bindingConnected(binding) {
22
+ this.unorderedBindings.add(binding);
23
+ }
24
+ bindingDisconnected(binding) {
25
+ this.unorderedBindings.delete(binding);
26
+ }
27
+ handleEvent(event) {
28
+ const extendedEvent = extendEvent(event);
29
+ for (const binding of this.bindings) {
30
+ if (extendedEvent.immediatePropagationStopped) {
31
+ break;
32
+ }
33
+ else {
34
+ binding.handleEvent(extendedEvent);
35
+ }
36
+ }
37
+ }
38
+ get bindings() {
39
+ return Array.from(this.unorderedBindings).sort((left, right) => {
40
+ const leftIndex = left.index, rightIndex = right.index;
41
+ return leftIndex < rightIndex ? -1 : leftIndex > rightIndex ? 1 : 0;
42
+ });
43
+ }
44
44
  }
45
-
46
45
  function extendEvent(event) {
47
- if ("immediatePropagationStopped" in event) {
48
- return event;
49
- } else {
50
- const {stopImmediatePropagation: stopImmediatePropagation} = event;
51
- return Object.assign(event, {
52
- immediatePropagationStopped: false,
53
- stopImmediatePropagation() {
54
- this.immediatePropagationStopped = true;
55
- stopImmediatePropagation.call(this);
56
- }
57
- });
58
- }
46
+ if ("immediatePropagationStopped" in event) {
47
+ return event;
48
+ }
49
+ else {
50
+ const { stopImmediatePropagation } = event;
51
+ return Object.assign(event, {
52
+ immediatePropagationStopped: false,
53
+ stopImmediatePropagation() {
54
+ this.immediatePropagationStopped = true;
55
+ stopImmediatePropagation.call(this);
56
+ }
57
+ });
58
+ }
59
59
  }
60
60
 
61
61
  class Dispatcher {
62
- constructor(application) {
63
- this.application = application;
64
- this.eventListenerMaps = new Map;
65
- this.started = false;
66
- }
67
- start() {
68
- if (!this.started) {
69
- this.started = true;
70
- this.eventListeners.forEach((eventListener => eventListener.connect()));
71
- }
72
- }
73
- stop() {
74
- if (this.started) {
75
- this.started = false;
76
- this.eventListeners.forEach((eventListener => eventListener.disconnect()));
77
- }
78
- }
79
- get eventListeners() {
80
- return Array.from(this.eventListenerMaps.values()).reduce(((listeners, map) => listeners.concat(Array.from(map.values()))), []);
81
- }
82
- bindingConnected(binding) {
83
- this.fetchEventListenerForBinding(binding).bindingConnected(binding);
84
- }
85
- bindingDisconnected(binding) {
86
- this.fetchEventListenerForBinding(binding).bindingDisconnected(binding);
87
- }
88
- handleError(error, message, detail = {}) {
89
- this.application.handleError(error, `Error ${message}`, detail);
90
- }
91
- fetchEventListenerForBinding(binding) {
92
- const {eventTarget: eventTarget, eventName: eventName, eventOptions: eventOptions} = binding;
93
- return this.fetchEventListener(eventTarget, eventName, eventOptions);
94
- }
95
- fetchEventListener(eventTarget, eventName, eventOptions) {
96
- const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget);
97
- const cacheKey = this.cacheKey(eventName, eventOptions);
98
- let eventListener = eventListenerMap.get(cacheKey);
99
- if (!eventListener) {
100
- eventListener = this.createEventListener(eventTarget, eventName, eventOptions);
101
- eventListenerMap.set(cacheKey, eventListener);
102
- }
103
- return eventListener;
104
- }
105
- createEventListener(eventTarget, eventName, eventOptions) {
106
- const eventListener = new EventListener(eventTarget, eventName, eventOptions);
107
- if (this.started) {
108
- eventListener.connect();
109
- }
110
- return eventListener;
111
- }
112
- fetchEventListenerMapForEventTarget(eventTarget) {
113
- let eventListenerMap = this.eventListenerMaps.get(eventTarget);
114
- if (!eventListenerMap) {
115
- eventListenerMap = new Map;
116
- this.eventListenerMaps.set(eventTarget, eventListenerMap);
117
- }
118
- return eventListenerMap;
119
- }
120
- cacheKey(eventName, eventOptions) {
121
- const parts = [ eventName ];
122
- Object.keys(eventOptions).sort().forEach((key => {
123
- parts.push(`${eventOptions[key] ? "" : "!"}${key}`);
124
- }));
125
- return parts.join(":");
126
- }
62
+ constructor(application) {
63
+ this.application = application;
64
+ this.eventListenerMaps = new Map;
65
+ this.started = false;
66
+ }
67
+ start() {
68
+ if (!this.started) {
69
+ this.started = true;
70
+ this.eventListeners.forEach(eventListener => eventListener.connect());
71
+ }
72
+ }
73
+ stop() {
74
+ if (this.started) {
75
+ this.started = false;
76
+ this.eventListeners.forEach(eventListener => eventListener.disconnect());
77
+ }
78
+ }
79
+ get eventListeners() {
80
+ return Array.from(this.eventListenerMaps.values())
81
+ .reduce((listeners, map) => listeners.concat(Array.from(map.values())), []);
82
+ }
83
+ bindingConnected(binding) {
84
+ this.fetchEventListenerForBinding(binding).bindingConnected(binding);
85
+ }
86
+ bindingDisconnected(binding) {
87
+ this.fetchEventListenerForBinding(binding).bindingDisconnected(binding);
88
+ }
89
+ handleError(error, message, detail = {}) {
90
+ this.application.handleError(error, `Error ${message}`, detail);
91
+ }
92
+ fetchEventListenerForBinding(binding) {
93
+ const { eventTarget, eventName, eventOptions } = binding;
94
+ return this.fetchEventListener(eventTarget, eventName, eventOptions);
95
+ }
96
+ fetchEventListener(eventTarget, eventName, eventOptions) {
97
+ const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget);
98
+ const cacheKey = this.cacheKey(eventName, eventOptions);
99
+ let eventListener = eventListenerMap.get(cacheKey);
100
+ if (!eventListener) {
101
+ eventListener = this.createEventListener(eventTarget, eventName, eventOptions);
102
+ eventListenerMap.set(cacheKey, eventListener);
103
+ }
104
+ return eventListener;
105
+ }
106
+ createEventListener(eventTarget, eventName, eventOptions) {
107
+ const eventListener = new EventListener(eventTarget, eventName, eventOptions);
108
+ if (this.started) {
109
+ eventListener.connect();
110
+ }
111
+ return eventListener;
112
+ }
113
+ fetchEventListenerMapForEventTarget(eventTarget) {
114
+ let eventListenerMap = this.eventListenerMaps.get(eventTarget);
115
+ if (!eventListenerMap) {
116
+ eventListenerMap = new Map;
117
+ this.eventListenerMaps.set(eventTarget, eventListenerMap);
118
+ }
119
+ return eventListenerMap;
120
+ }
121
+ cacheKey(eventName, eventOptions) {
122
+ const parts = [eventName];
123
+ Object.keys(eventOptions).sort().forEach(key => {
124
+ parts.push(`${eventOptions[key] ? "" : "!"}${key}`);
125
+ });
126
+ return parts.join(":");
127
+ }
127
128
  }
128
129
 
129
130
  const descriptorPattern = /^((.+?)(@(window|document))?->)?(.+?)(#([^:]+?))(:(.+))?$/;
130
-
131
131
  function parseActionDescriptorString(descriptorString) {
132
- const source = descriptorString.trim();
133
- const matches = source.match(descriptorPattern) || [];
134
- return {
135
- eventTarget: parseEventTarget(matches[4]),
136
- eventName: matches[2],
137
- eventOptions: matches[9] ? parseEventOptions(matches[9]) : {},
138
- identifier: matches[5],
139
- methodName: matches[7]
140
- };
132
+ const source = descriptorString.trim();
133
+ const matches = source.match(descriptorPattern) || [];
134
+ return {
135
+ eventTarget: parseEventTarget(matches[4]),
136
+ eventName: matches[2],
137
+ eventOptions: matches[9] ? parseEventOptions(matches[9]) : {},
138
+ identifier: matches[5],
139
+ methodName: matches[7]
140
+ };
141
141
  }
142
-
143
142
  function parseEventTarget(eventTargetName) {
144
- if (eventTargetName == "window") {
145
- return window;
146
- } else if (eventTargetName == "document") {
147
- return document;
148
- }
143
+ if (eventTargetName == "window") {
144
+ return window;
145
+ }
146
+ else if (eventTargetName == "document") {
147
+ return document;
148
+ }
149
149
  }
150
-
151
150
  function parseEventOptions(eventOptions) {
152
- return eventOptions.split(":").reduce(((options, token) => Object.assign(options, {
153
- [token.replace(/^!/, "")]: !/^!/.test(token)
154
- })), {});
151
+ return eventOptions.split(":").reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {});
155
152
  }
156
-
157
153
  function stringifyEventTarget(eventTarget) {
158
- if (eventTarget == window) {
159
- return "window";
160
- } else if (eventTarget == document) {
161
- return "document";
162
- }
154
+ if (eventTarget == window) {
155
+ return "window";
156
+ }
157
+ else if (eventTarget == document) {
158
+ return "document";
159
+ }
163
160
  }
164
161
 
165
- class Action {
166
- constructor(element, index, descriptor) {
167
- this.element = element;
168
- this.index = index;
169
- this.eventTarget = descriptor.eventTarget || element;
170
- this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name");
171
- this.eventOptions = descriptor.eventOptions || {};
172
- this.identifier = descriptor.identifier || error("missing identifier");
173
- this.methodName = descriptor.methodName || error("missing method name");
174
- }
175
- static forToken(token) {
176
- return new this(token.element, token.index, parseActionDescriptorString(token.content));
177
- }
178
- toString() {
179
- const eventNameSuffix = this.eventTargetName ? `@${this.eventTargetName}` : "";
180
- return `${this.eventName}${eventNameSuffix}->${this.identifier}#${this.methodName}`;
181
- }
182
- get eventTargetName() {
183
- return stringifyEventTarget(this.eventTarget);
184
- }
162
+ function camelize(value) {
163
+ return value.replace(/(?:[_-])([a-z0-9])/g, (_, char) => char.toUpperCase());
164
+ }
165
+ function capitalize(value) {
166
+ return value.charAt(0).toUpperCase() + value.slice(1);
167
+ }
168
+ function dasherize(value) {
169
+ return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`);
170
+ }
171
+ function tokenize(value) {
172
+ return value.match(/[^\s]+/g) || [];
185
173
  }
186
174
 
175
+ class Action {
176
+ constructor(element, index, descriptor) {
177
+ this.element = element;
178
+ this.index = index;
179
+ this.eventTarget = descriptor.eventTarget || element;
180
+ this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name");
181
+ this.eventOptions = descriptor.eventOptions || {};
182
+ this.identifier = descriptor.identifier || error("missing identifier");
183
+ this.methodName = descriptor.methodName || error("missing method name");
184
+ }
185
+ static forToken(token) {
186
+ return new this(token.element, token.index, parseActionDescriptorString(token.content));
187
+ }
188
+ toString() {
189
+ const eventNameSuffix = this.eventTargetName ? `@${this.eventTargetName}` : "";
190
+ return `${this.eventName}${eventNameSuffix}->${this.identifier}#${this.methodName}`;
191
+ }
192
+ get params() {
193
+ if (this.eventTarget instanceof Element) {
194
+ return this.getParamsFromEventTargetAttributes(this.eventTarget);
195
+ }
196
+ else {
197
+ return {};
198
+ }
199
+ }
200
+ getParamsFromEventTargetAttributes(eventTarget) {
201
+ const params = {};
202
+ const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`);
203
+ const attributes = Array.from(eventTarget.attributes);
204
+ attributes.forEach(({ name, value }) => {
205
+ const match = name.match(pattern);
206
+ const key = match && match[1];
207
+ if (key) {
208
+ Object.assign(params, { [camelize(key)]: typecast(value) });
209
+ }
210
+ });
211
+ return params;
212
+ }
213
+ get eventTargetName() {
214
+ return stringifyEventTarget(this.eventTarget);
215
+ }
216
+ }
187
217
  const defaultEventNames = {
188
- a: e => "click",
189
- button: e => "click",
190
- form: e => "submit",
191
- input: e => e.getAttribute("type") == "submit" ? "click" : "input",
192
- select: e => "change",
193
- textarea: e => "input"
218
+ "a": e => "click",
219
+ "button": e => "click",
220
+ "form": e => "submit",
221
+ "input": e => e.getAttribute("type") == "submit" ? "click" : "input",
222
+ "select": e => "change",
223
+ "textarea": e => "input"
194
224
  };
195
-
196
225
  function getDefaultEventNameForElement(element) {
197
- const tagName = element.tagName.toLowerCase();
198
- if (tagName in defaultEventNames) {
199
- return defaultEventNames[tagName](element);
200
- }
226
+ const tagName = element.tagName.toLowerCase();
227
+ if (tagName in defaultEventNames) {
228
+ return defaultEventNames[tagName](element);
229
+ }
201
230
  }
202
-
203
231
  function error(message) {
204
- throw new Error(message);
232
+ throw new Error(message);
233
+ }
234
+ function typecast(value) {
235
+ try {
236
+ return JSON.parse(value);
237
+ }
238
+ catch (o_O) {
239
+ return value;
240
+ }
205
241
  }
206
242
 
207
243
  class Binding {
208
- constructor(context, action) {
209
- this.context = context;
210
- this.action = action;
211
- }
212
- get index() {
213
- return this.action.index;
214
- }
215
- get eventTarget() {
216
- return this.action.eventTarget;
217
- }
218
- get eventOptions() {
219
- return this.action.eventOptions;
220
- }
221
- get identifier() {
222
- return this.context.identifier;
223
- }
224
- handleEvent(event) {
225
- if (this.willBeInvokedByEvent(event)) {
226
- this.invokeWithEvent(event);
227
- }
228
- }
229
- get eventName() {
230
- return this.action.eventName;
231
- }
232
- get method() {
233
- const method = this.controller[this.methodName];
234
- if (typeof method == "function") {
235
- return method;
236
- }
237
- throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`);
238
- }
239
- invokeWithEvent(event) {
240
- try {
241
- this.method.call(this.controller, event);
242
- } catch (error) {
243
- const {identifier: identifier, controller: controller, element: element, index: index} = this;
244
- const detail = {
245
- identifier: identifier,
246
- controller: controller,
247
- element: element,
248
- index: index,
249
- event: event
250
- };
251
- this.context.handleError(error, `invoking action "${this.action}"`, detail);
252
- }
253
- }
254
- willBeInvokedByEvent(event) {
255
- const eventTarget = event.target;
256
- if (this.element === eventTarget) {
257
- return true;
258
- } else if (eventTarget instanceof Element && this.element.contains(eventTarget)) {
259
- return this.scope.containsElement(eventTarget);
260
- } else {
261
- return this.scope.containsElement(this.action.element);
262
- }
263
- }
264
- get controller() {
265
- return this.context.controller;
266
- }
267
- get methodName() {
268
- return this.action.methodName;
269
- }
270
- get element() {
271
- return this.scope.element;
272
- }
273
- get scope() {
274
- return this.context.scope;
275
- }
244
+ constructor(context, action) {
245
+ this.context = context;
246
+ this.action = action;
247
+ }
248
+ get index() {
249
+ return this.action.index;
250
+ }
251
+ get eventTarget() {
252
+ return this.action.eventTarget;
253
+ }
254
+ get eventOptions() {
255
+ return this.action.eventOptions;
256
+ }
257
+ get identifier() {
258
+ return this.context.identifier;
259
+ }
260
+ handleEvent(event) {
261
+ if (this.willBeInvokedByEvent(event)) {
262
+ this.invokeWithEvent(event);
263
+ }
264
+ }
265
+ get eventName() {
266
+ return this.action.eventName;
267
+ }
268
+ get method() {
269
+ const method = this.controller[this.methodName];
270
+ if (typeof method == "function") {
271
+ return method;
272
+ }
273
+ throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`);
274
+ }
275
+ invokeWithEvent(event) {
276
+ const { target, currentTarget } = event;
277
+ try {
278
+ const { params } = this.action;
279
+ const actionEvent = Object.assign(event, { params });
280
+ this.method.call(this.controller, actionEvent);
281
+ this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName });
282
+ }
283
+ catch (error) {
284
+ const { identifier, controller, element, index } = this;
285
+ const detail = { identifier, controller, element, index, event };
286
+ this.context.handleError(error, `invoking action "${this.action}"`, detail);
287
+ }
288
+ }
289
+ willBeInvokedByEvent(event) {
290
+ const eventTarget = event.target;
291
+ if (this.element === eventTarget) {
292
+ return true;
293
+ }
294
+ else if (eventTarget instanceof Element && this.element.contains(eventTarget)) {
295
+ return this.scope.containsElement(eventTarget);
296
+ }
297
+ else {
298
+ return this.scope.containsElement(this.action.element);
299
+ }
300
+ }
301
+ get controller() {
302
+ return this.context.controller;
303
+ }
304
+ get methodName() {
305
+ return this.action.methodName;
306
+ }
307
+ get element() {
308
+ return this.scope.element;
309
+ }
310
+ get scope() {
311
+ return this.context.scope;
312
+ }
276
313
  }
277
314
 
278
315
  class ElementObserver {
279
- constructor(element, delegate) {
280
- this.element = element;
281
- this.started = false;
282
- this.delegate = delegate;
283
- this.elements = new Set;
284
- this.mutationObserver = new MutationObserver((mutations => this.processMutations(mutations)));
285
- }
286
- start() {
287
- if (!this.started) {
288
- this.started = true;
289
- this.mutationObserver.observe(this.element, {
290
- attributes: true,
291
- childList: true,
292
- subtree: true
293
- });
294
- this.refresh();
295
- }
296
- }
297
- stop() {
298
- if (this.started) {
299
- this.mutationObserver.takeRecords();
300
- this.mutationObserver.disconnect();
301
- this.started = false;
302
- }
303
- }
304
- refresh() {
305
- if (this.started) {
306
- const matches = new Set(this.matchElementsInTree());
307
- for (const element of Array.from(this.elements)) {
308
- if (!matches.has(element)) {
309
- this.removeElement(element);
310
- }
311
- }
312
- for (const element of Array.from(matches)) {
313
- this.addElement(element);
314
- }
315
- }
316
- }
317
- processMutations(mutations) {
318
- if (this.started) {
319
- for (const mutation of mutations) {
320
- this.processMutation(mutation);
321
- }
322
- }
323
- }
324
- processMutation(mutation) {
325
- if (mutation.type == "attributes") {
326
- this.processAttributeChange(mutation.target, mutation.attributeName);
327
- } else if (mutation.type == "childList") {
328
- this.processRemovedNodes(mutation.removedNodes);
329
- this.processAddedNodes(mutation.addedNodes);
330
- }
331
- }
332
- processAttributeChange(node, attributeName) {
333
- const element = node;
334
- if (this.elements.has(element)) {
335
- if (this.delegate.elementAttributeChanged && this.matchElement(element)) {
336
- this.delegate.elementAttributeChanged(element, attributeName);
337
- } else {
338
- this.removeElement(element);
339
- }
340
- } else if (this.matchElement(element)) {
341
- this.addElement(element);
342
- }
343
- }
344
- processRemovedNodes(nodes) {
345
- for (const node of Array.from(nodes)) {
346
- const element = this.elementFromNode(node);
347
- if (element) {
348
- this.processTree(element, this.removeElement);
349
- }
350
- }
351
- }
352
- processAddedNodes(nodes) {
353
- for (const node of Array.from(nodes)) {
354
- const element = this.elementFromNode(node);
355
- if (element && this.elementIsActive(element)) {
356
- this.processTree(element, this.addElement);
357
- }
358
- }
359
- }
360
- matchElement(element) {
361
- return this.delegate.matchElement(element);
362
- }
363
- matchElementsInTree(tree = this.element) {
364
- return this.delegate.matchElementsInTree(tree);
365
- }
366
- processTree(tree, processor) {
367
- for (const element of this.matchElementsInTree(tree)) {
368
- processor.call(this, element);
369
- }
370
- }
371
- elementFromNode(node) {
372
- if (node.nodeType == Node.ELEMENT_NODE) {
373
- return node;
374
- }
375
- }
376
- elementIsActive(element) {
377
- if (element.isConnected != this.element.isConnected) {
378
- return false;
379
- } else {
380
- return this.element.contains(element);
381
- }
382
- }
383
- addElement(element) {
384
- if (!this.elements.has(element)) {
385
- if (this.elementIsActive(element)) {
386
- this.elements.add(element);
387
- if (this.delegate.elementMatched) {
388
- this.delegate.elementMatched(element);
389
- }
390
- }
391
- }
392
- }
393
- removeElement(element) {
394
- if (this.elements.has(element)) {
395
- this.elements.delete(element);
396
- if (this.delegate.elementUnmatched) {
397
- this.delegate.elementUnmatched(element);
398
- }
399
- }
400
- }
316
+ constructor(element, delegate) {
317
+ this.element = element;
318
+ this.started = false;
319
+ this.delegate = delegate;
320
+ this.elements = new Set;
321
+ this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations));
322
+ }
323
+ start() {
324
+ if (!this.started) {
325
+ this.started = true;
326
+ this.mutationObserver.observe(this.element, { attributes: true, childList: true, subtree: true });
327
+ this.refresh();
328
+ }
329
+ }
330
+ stop() {
331
+ if (this.started) {
332
+ this.mutationObserver.takeRecords();
333
+ this.mutationObserver.disconnect();
334
+ this.started = false;
335
+ }
336
+ }
337
+ refresh() {
338
+ if (this.started) {
339
+ const matches = new Set(this.matchElementsInTree());
340
+ for (const element of Array.from(this.elements)) {
341
+ if (!matches.has(element)) {
342
+ this.removeElement(element);
343
+ }
344
+ }
345
+ for (const element of Array.from(matches)) {
346
+ this.addElement(element);
347
+ }
348
+ }
349
+ }
350
+ processMutations(mutations) {
351
+ if (this.started) {
352
+ for (const mutation of mutations) {
353
+ this.processMutation(mutation);
354
+ }
355
+ }
356
+ }
357
+ processMutation(mutation) {
358
+ if (mutation.type == "attributes") {
359
+ this.processAttributeChange(mutation.target, mutation.attributeName);
360
+ }
361
+ else if (mutation.type == "childList") {
362
+ this.processRemovedNodes(mutation.removedNodes);
363
+ this.processAddedNodes(mutation.addedNodes);
364
+ }
365
+ }
366
+ processAttributeChange(node, attributeName) {
367
+ const element = node;
368
+ if (this.elements.has(element)) {
369
+ if (this.delegate.elementAttributeChanged && this.matchElement(element)) {
370
+ this.delegate.elementAttributeChanged(element, attributeName);
371
+ }
372
+ else {
373
+ this.removeElement(element);
374
+ }
375
+ }
376
+ else if (this.matchElement(element)) {
377
+ this.addElement(element);
378
+ }
379
+ }
380
+ processRemovedNodes(nodes) {
381
+ for (const node of Array.from(nodes)) {
382
+ const element = this.elementFromNode(node);
383
+ if (element) {
384
+ this.processTree(element, this.removeElement);
385
+ }
386
+ }
387
+ }
388
+ processAddedNodes(nodes) {
389
+ for (const node of Array.from(nodes)) {
390
+ const element = this.elementFromNode(node);
391
+ if (element && this.elementIsActive(element)) {
392
+ this.processTree(element, this.addElement);
393
+ }
394
+ }
395
+ }
396
+ matchElement(element) {
397
+ return this.delegate.matchElement(element);
398
+ }
399
+ matchElementsInTree(tree = this.element) {
400
+ return this.delegate.matchElementsInTree(tree);
401
+ }
402
+ processTree(tree, processor) {
403
+ for (const element of this.matchElementsInTree(tree)) {
404
+ processor.call(this, element);
405
+ }
406
+ }
407
+ elementFromNode(node) {
408
+ if (node.nodeType == Node.ELEMENT_NODE) {
409
+ return node;
410
+ }
411
+ }
412
+ elementIsActive(element) {
413
+ if (element.isConnected != this.element.isConnected) {
414
+ return false;
415
+ }
416
+ else {
417
+ return this.element.contains(element);
418
+ }
419
+ }
420
+ addElement(element) {
421
+ if (!this.elements.has(element)) {
422
+ if (this.elementIsActive(element)) {
423
+ this.elements.add(element);
424
+ if (this.delegate.elementMatched) {
425
+ this.delegate.elementMatched(element);
426
+ }
427
+ }
428
+ }
429
+ }
430
+ removeElement(element) {
431
+ if (this.elements.has(element)) {
432
+ this.elements.delete(element);
433
+ if (this.delegate.elementUnmatched) {
434
+ this.delegate.elementUnmatched(element);
435
+ }
436
+ }
437
+ }
401
438
  }
402
439
 
403
440
  class AttributeObserver {
404
- constructor(element, attributeName, delegate) {
405
- this.attributeName = attributeName;
406
- this.delegate = delegate;
407
- this.elementObserver = new ElementObserver(element, this);
408
- }
409
- get element() {
410
- return this.elementObserver.element;
411
- }
412
- get selector() {
413
- return `[${this.attributeName}]`;
414
- }
415
- start() {
416
- this.elementObserver.start();
417
- }
418
- stop() {
419
- this.elementObserver.stop();
420
- }
421
- refresh() {
422
- this.elementObserver.refresh();
423
- }
424
- get started() {
425
- return this.elementObserver.started;
426
- }
427
- matchElement(element) {
428
- return element.hasAttribute(this.attributeName);
429
- }
430
- matchElementsInTree(tree) {
431
- const match = this.matchElement(tree) ? [ tree ] : [];
432
- const matches = Array.from(tree.querySelectorAll(this.selector));
433
- return match.concat(matches);
434
- }
435
- elementMatched(element) {
436
- if (this.delegate.elementMatchedAttribute) {
437
- this.delegate.elementMatchedAttribute(element, this.attributeName);
438
- }
439
- }
440
- elementUnmatched(element) {
441
- if (this.delegate.elementUnmatchedAttribute) {
442
- this.delegate.elementUnmatchedAttribute(element, this.attributeName);
443
- }
444
- }
445
- elementAttributeChanged(element, attributeName) {
446
- if (this.delegate.elementAttributeValueChanged && this.attributeName == attributeName) {
447
- this.delegate.elementAttributeValueChanged(element, attributeName);
448
- }
449
- }
441
+ constructor(element, attributeName, delegate) {
442
+ this.attributeName = attributeName;
443
+ this.delegate = delegate;
444
+ this.elementObserver = new ElementObserver(element, this);
445
+ }
446
+ get element() {
447
+ return this.elementObserver.element;
448
+ }
449
+ get selector() {
450
+ return `[${this.attributeName}]`;
451
+ }
452
+ start() {
453
+ this.elementObserver.start();
454
+ }
455
+ stop() {
456
+ this.elementObserver.stop();
457
+ }
458
+ refresh() {
459
+ this.elementObserver.refresh();
460
+ }
461
+ get started() {
462
+ return this.elementObserver.started;
463
+ }
464
+ matchElement(element) {
465
+ return element.hasAttribute(this.attributeName);
466
+ }
467
+ matchElementsInTree(tree) {
468
+ const match = this.matchElement(tree) ? [tree] : [];
469
+ const matches = Array.from(tree.querySelectorAll(this.selector));
470
+ return match.concat(matches);
471
+ }
472
+ elementMatched(element) {
473
+ if (this.delegate.elementMatchedAttribute) {
474
+ this.delegate.elementMatchedAttribute(element, this.attributeName);
475
+ }
476
+ }
477
+ elementUnmatched(element) {
478
+ if (this.delegate.elementUnmatchedAttribute) {
479
+ this.delegate.elementUnmatchedAttribute(element, this.attributeName);
480
+ }
481
+ }
482
+ elementAttributeChanged(element, attributeName) {
483
+ if (this.delegate.elementAttributeValueChanged && this.attributeName == attributeName) {
484
+ this.delegate.elementAttributeValueChanged(element, attributeName);
485
+ }
486
+ }
450
487
  }
451
488
 
452
489
  class StringMapObserver {
453
- constructor(element, delegate) {
454
- this.element = element;
455
- this.delegate = delegate;
456
- this.started = false;
457
- this.stringMap = new Map;
458
- this.mutationObserver = new MutationObserver((mutations => this.processMutations(mutations)));
459
- }
460
- start() {
461
- if (!this.started) {
462
- this.started = true;
463
- this.mutationObserver.observe(this.element, {
464
- attributes: true
465
- });
466
- this.refresh();
467
- }
468
- }
469
- stop() {
470
- if (this.started) {
471
- this.mutationObserver.takeRecords();
472
- this.mutationObserver.disconnect();
473
- this.started = false;
474
- }
475
- }
476
- refresh() {
477
- if (this.started) {
478
- for (const attributeName of this.knownAttributeNames) {
479
- this.refreshAttribute(attributeName);
480
- }
481
- }
482
- }
483
- processMutations(mutations) {
484
- if (this.started) {
485
- for (const mutation of mutations) {
486
- this.processMutation(mutation);
487
- }
488
- }
489
- }
490
- processMutation(mutation) {
491
- const attributeName = mutation.attributeName;
492
- if (attributeName) {
493
- this.refreshAttribute(attributeName);
494
- }
495
- }
496
- refreshAttribute(attributeName) {
497
- const key = this.delegate.getStringMapKeyForAttribute(attributeName);
498
- if (key != null) {
499
- if (!this.stringMap.has(attributeName)) {
500
- this.stringMapKeyAdded(key, attributeName);
501
- }
502
- const value = this.element.getAttribute(attributeName);
503
- if (this.stringMap.get(attributeName) != value) {
504
- this.stringMapValueChanged(value, key);
505
- }
506
- if (value == null) {
507
- this.stringMap.delete(attributeName);
508
- this.stringMapKeyRemoved(key, attributeName);
509
- } else {
510
- this.stringMap.set(attributeName, value);
511
- }
512
- }
513
- }
514
- stringMapKeyAdded(key, attributeName) {
515
- if (this.delegate.stringMapKeyAdded) {
516
- this.delegate.stringMapKeyAdded(key, attributeName);
517
- }
518
- }
519
- stringMapValueChanged(value, key) {
520
- if (this.delegate.stringMapValueChanged) {
521
- this.delegate.stringMapValueChanged(value, key);
522
- }
523
- }
524
- stringMapKeyRemoved(key, attributeName) {
525
- if (this.delegate.stringMapKeyRemoved) {
526
- this.delegate.stringMapKeyRemoved(key, attributeName);
527
- }
528
- }
529
- get knownAttributeNames() {
530
- return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)));
531
- }
532
- get currentAttributeNames() {
533
- return Array.from(this.element.attributes).map((attribute => attribute.name));
534
- }
535
- get recordedAttributeNames() {
536
- return Array.from(this.stringMap.keys());
537
- }
490
+ constructor(element, delegate) {
491
+ this.element = element;
492
+ this.delegate = delegate;
493
+ this.started = false;
494
+ this.stringMap = new Map;
495
+ this.mutationObserver = new MutationObserver(mutations => this.processMutations(mutations));
496
+ }
497
+ start() {
498
+ if (!this.started) {
499
+ this.started = true;
500
+ this.mutationObserver.observe(this.element, { attributes: true, attributeOldValue: true });
501
+ this.refresh();
502
+ }
503
+ }
504
+ stop() {
505
+ if (this.started) {
506
+ this.mutationObserver.takeRecords();
507
+ this.mutationObserver.disconnect();
508
+ this.started = false;
509
+ }
510
+ }
511
+ refresh() {
512
+ if (this.started) {
513
+ for (const attributeName of this.knownAttributeNames) {
514
+ this.refreshAttribute(attributeName, null);
515
+ }
516
+ }
517
+ }
518
+ processMutations(mutations) {
519
+ if (this.started) {
520
+ for (const mutation of mutations) {
521
+ this.processMutation(mutation);
522
+ }
523
+ }
524
+ }
525
+ processMutation(mutation) {
526
+ const attributeName = mutation.attributeName;
527
+ if (attributeName) {
528
+ this.refreshAttribute(attributeName, mutation.oldValue);
529
+ }
530
+ }
531
+ refreshAttribute(attributeName, oldValue) {
532
+ const key = this.delegate.getStringMapKeyForAttribute(attributeName);
533
+ if (key != null) {
534
+ if (!this.stringMap.has(attributeName)) {
535
+ this.stringMapKeyAdded(key, attributeName);
536
+ }
537
+ const value = this.element.getAttribute(attributeName);
538
+ if (this.stringMap.get(attributeName) != value) {
539
+ this.stringMapValueChanged(value, key, oldValue);
540
+ }
541
+ if (value == null) {
542
+ const oldValue = this.stringMap.get(attributeName);
543
+ this.stringMap.delete(attributeName);
544
+ if (oldValue)
545
+ this.stringMapKeyRemoved(key, attributeName, oldValue);
546
+ }
547
+ else {
548
+ this.stringMap.set(attributeName, value);
549
+ }
550
+ }
551
+ }
552
+ stringMapKeyAdded(key, attributeName) {
553
+ if (this.delegate.stringMapKeyAdded) {
554
+ this.delegate.stringMapKeyAdded(key, attributeName);
555
+ }
556
+ }
557
+ stringMapValueChanged(value, key, oldValue) {
558
+ if (this.delegate.stringMapValueChanged) {
559
+ this.delegate.stringMapValueChanged(value, key, oldValue);
560
+ }
561
+ }
562
+ stringMapKeyRemoved(key, attributeName, oldValue) {
563
+ if (this.delegate.stringMapKeyRemoved) {
564
+ this.delegate.stringMapKeyRemoved(key, attributeName, oldValue);
565
+ }
566
+ }
567
+ get knownAttributeNames() {
568
+ return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)));
569
+ }
570
+ get currentAttributeNames() {
571
+ return Array.from(this.element.attributes).map(attribute => attribute.name);
572
+ }
573
+ get recordedAttributeNames() {
574
+ return Array.from(this.stringMap.keys());
575
+ }
538
576
  }
539
577
 
540
578
  function add(map, key, value) {
541
- fetch(map, key).add(value);
579
+ fetch(map, key).add(value);
542
580
  }
543
-
544
581
  function del(map, key, value) {
545
- fetch(map, key).delete(value);
546
- prune(map, key);
582
+ fetch(map, key).delete(value);
583
+ prune(map, key);
547
584
  }
548
-
549
585
  function fetch(map, key) {
550
- let values = map.get(key);
551
- if (!values) {
552
- values = new Set;
553
- map.set(key, values);
554
- }
555
- return values;
586
+ let values = map.get(key);
587
+ if (!values) {
588
+ values = new Set();
589
+ map.set(key, values);
590
+ }
591
+ return values;
556
592
  }
557
-
558
593
  function prune(map, key) {
559
- const values = map.get(key);
560
- if (values != null && values.size == 0) {
561
- map.delete(key);
562
- }
594
+ const values = map.get(key);
595
+ if (values != null && values.size == 0) {
596
+ map.delete(key);
597
+ }
563
598
  }
564
599
 
565
600
  class Multimap {
566
- constructor() {
567
- this.valuesByKey = new Map;
568
- }
569
- get values() {
570
- const sets = Array.from(this.valuesByKey.values());
571
- return sets.reduce(((values, set) => values.concat(Array.from(set))), []);
572
- }
573
- get size() {
574
- const sets = Array.from(this.valuesByKey.values());
575
- return sets.reduce(((size, set) => size + set.size), 0);
576
- }
577
- add(key, value) {
578
- add(this.valuesByKey, key, value);
579
- }
580
- delete(key, value) {
581
- del(this.valuesByKey, key, value);
582
- }
583
- has(key, value) {
584
- const values = this.valuesByKey.get(key);
585
- return values != null && values.has(value);
586
- }
587
- hasKey(key) {
588
- return this.valuesByKey.has(key);
589
- }
590
- hasValue(value) {
591
- const sets = Array.from(this.valuesByKey.values());
592
- return sets.some((set => set.has(value)));
593
- }
594
- getValuesForKey(key) {
595
- const values = this.valuesByKey.get(key);
596
- return values ? Array.from(values) : [];
597
- }
598
- getKeysForValue(value) {
599
- return Array.from(this.valuesByKey).filter((([key, values]) => values.has(value))).map((([key, values]) => key));
600
- }
601
+ constructor() {
602
+ this.valuesByKey = new Map();
603
+ }
604
+ get keys() {
605
+ return Array.from(this.valuesByKey.keys());
606
+ }
607
+ get values() {
608
+ const sets = Array.from(this.valuesByKey.values());
609
+ return sets.reduce((values, set) => values.concat(Array.from(set)), []);
610
+ }
611
+ get size() {
612
+ const sets = Array.from(this.valuesByKey.values());
613
+ return sets.reduce((size, set) => size + set.size, 0);
614
+ }
615
+ add(key, value) {
616
+ add(this.valuesByKey, key, value);
617
+ }
618
+ delete(key, value) {
619
+ del(this.valuesByKey, key, value);
620
+ }
621
+ has(key, value) {
622
+ const values = this.valuesByKey.get(key);
623
+ return values != null && values.has(value);
624
+ }
625
+ hasKey(key) {
626
+ return this.valuesByKey.has(key);
627
+ }
628
+ hasValue(value) {
629
+ const sets = Array.from(this.valuesByKey.values());
630
+ return sets.some(set => set.has(value));
631
+ }
632
+ getValuesForKey(key) {
633
+ const values = this.valuesByKey.get(key);
634
+ return values ? Array.from(values) : [];
635
+ }
636
+ getKeysForValue(value) {
637
+ return Array.from(this.valuesByKey)
638
+ .filter(([key, values]) => values.has(value))
639
+ .map(([key, values]) => key);
640
+ }
601
641
  }
602
642
 
603
- class TokenListObserver {
604
- constructor(element, attributeName, delegate) {
605
- this.attributeObserver = new AttributeObserver(element, attributeName, this);
606
- this.delegate = delegate;
607
- this.tokensByElement = new Multimap;
608
- }
609
- get started() {
610
- return this.attributeObserver.started;
611
- }
612
- start() {
613
- this.attributeObserver.start();
614
- }
615
- stop() {
616
- this.attributeObserver.stop();
617
- }
618
- refresh() {
619
- this.attributeObserver.refresh();
620
- }
621
- get element() {
622
- return this.attributeObserver.element;
623
- }
624
- get attributeName() {
625
- return this.attributeObserver.attributeName;
626
- }
627
- elementMatchedAttribute(element) {
628
- this.tokensMatched(this.readTokensForElement(element));
629
- }
630
- elementAttributeValueChanged(element) {
631
- const [unmatchedTokens, matchedTokens] = this.refreshTokensForElement(element);
632
- this.tokensUnmatched(unmatchedTokens);
633
- this.tokensMatched(matchedTokens);
634
- }
635
- elementUnmatchedAttribute(element) {
636
- this.tokensUnmatched(this.tokensByElement.getValuesForKey(element));
637
- }
638
- tokensMatched(tokens) {
639
- tokens.forEach((token => this.tokenMatched(token)));
640
- }
641
- tokensUnmatched(tokens) {
642
- tokens.forEach((token => this.tokenUnmatched(token)));
643
- }
644
- tokenMatched(token) {
645
- this.delegate.tokenMatched(token);
646
- this.tokensByElement.add(token.element, token);
647
- }
648
- tokenUnmatched(token) {
649
- this.delegate.tokenUnmatched(token);
650
- this.tokensByElement.delete(token.element, token);
651
- }
652
- refreshTokensForElement(element) {
653
- const previousTokens = this.tokensByElement.getValuesForKey(element);
654
- const currentTokens = this.readTokensForElement(element);
655
- const firstDifferingIndex = zip(previousTokens, currentTokens).findIndex((([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken)));
656
- if (firstDifferingIndex == -1) {
657
- return [ [], [] ];
658
- } else {
659
- return [ previousTokens.slice(firstDifferingIndex), currentTokens.slice(firstDifferingIndex) ];
660
- }
661
- }
662
- readTokensForElement(element) {
663
- const attributeName = this.attributeName;
664
- const tokenString = element.getAttribute(attributeName) || "";
665
- return parseTokenString(tokenString, element, attributeName);
666
- }
643
+ class IndexedMultimap extends Multimap {
644
+ constructor() {
645
+ super();
646
+ this.keysByValue = new Map;
647
+ }
648
+ get values() {
649
+ return Array.from(this.keysByValue.keys());
650
+ }
651
+ add(key, value) {
652
+ super.add(key, value);
653
+ add(this.keysByValue, value, key);
654
+ }
655
+ delete(key, value) {
656
+ super.delete(key, value);
657
+ del(this.keysByValue, value, key);
658
+ }
659
+ hasValue(value) {
660
+ return this.keysByValue.has(value);
661
+ }
662
+ getKeysForValue(value) {
663
+ const set = this.keysByValue.get(value);
664
+ return set ? Array.from(set) : [];
665
+ }
667
666
  }
668
667
 
668
+ class TokenListObserver {
669
+ constructor(element, attributeName, delegate) {
670
+ this.attributeObserver = new AttributeObserver(element, attributeName, this);
671
+ this.delegate = delegate;
672
+ this.tokensByElement = new Multimap;
673
+ }
674
+ get started() {
675
+ return this.attributeObserver.started;
676
+ }
677
+ start() {
678
+ this.attributeObserver.start();
679
+ }
680
+ stop() {
681
+ this.attributeObserver.stop();
682
+ }
683
+ refresh() {
684
+ this.attributeObserver.refresh();
685
+ }
686
+ get element() {
687
+ return this.attributeObserver.element;
688
+ }
689
+ get attributeName() {
690
+ return this.attributeObserver.attributeName;
691
+ }
692
+ elementMatchedAttribute(element) {
693
+ this.tokensMatched(this.readTokensForElement(element));
694
+ }
695
+ elementAttributeValueChanged(element) {
696
+ const [unmatchedTokens, matchedTokens] = this.refreshTokensForElement(element);
697
+ this.tokensUnmatched(unmatchedTokens);
698
+ this.tokensMatched(matchedTokens);
699
+ }
700
+ elementUnmatchedAttribute(element) {
701
+ this.tokensUnmatched(this.tokensByElement.getValuesForKey(element));
702
+ }
703
+ tokensMatched(tokens) {
704
+ tokens.forEach(token => this.tokenMatched(token));
705
+ }
706
+ tokensUnmatched(tokens) {
707
+ tokens.forEach(token => this.tokenUnmatched(token));
708
+ }
709
+ tokenMatched(token) {
710
+ this.delegate.tokenMatched(token);
711
+ this.tokensByElement.add(token.element, token);
712
+ }
713
+ tokenUnmatched(token) {
714
+ this.delegate.tokenUnmatched(token);
715
+ this.tokensByElement.delete(token.element, token);
716
+ }
717
+ refreshTokensForElement(element) {
718
+ const previousTokens = this.tokensByElement.getValuesForKey(element);
719
+ const currentTokens = this.readTokensForElement(element);
720
+ const firstDifferingIndex = zip(previousTokens, currentTokens)
721
+ .findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken));
722
+ if (firstDifferingIndex == -1) {
723
+ return [[], []];
724
+ }
725
+ else {
726
+ return [previousTokens.slice(firstDifferingIndex), currentTokens.slice(firstDifferingIndex)];
727
+ }
728
+ }
729
+ readTokensForElement(element) {
730
+ const attributeName = this.attributeName;
731
+ const tokenString = element.getAttribute(attributeName) || "";
732
+ return parseTokenString(tokenString, element, attributeName);
733
+ }
734
+ }
669
735
  function parseTokenString(tokenString, element, attributeName) {
670
- return tokenString.trim().split(/\s+/).filter((content => content.length)).map(((content, index) => ({
671
- element: element,
672
- attributeName: attributeName,
673
- content: content,
674
- index: index
675
- })));
736
+ return tokenString.trim().split(/\s+/).filter(content => content.length)
737
+ .map((content, index) => ({ element, attributeName, content, index }));
676
738
  }
677
-
678
739
  function zip(left, right) {
679
- const length = Math.max(left.length, right.length);
680
- return Array.from({
681
- length: length
682
- }, ((_, index) => [ left[index], right[index] ]));
740
+ const length = Math.max(left.length, right.length);
741
+ return Array.from({ length }, (_, index) => [left[index], right[index]]);
683
742
  }
684
-
685
743
  function tokensAreEqual(left, right) {
686
- return left && right && left.index == right.index && left.content == right.content;
744
+ return left && right && left.index == right.index && left.content == right.content;
687
745
  }
688
746
 
689
747
  class ValueListObserver {
690
- constructor(element, attributeName, delegate) {
691
- this.tokenListObserver = new TokenListObserver(element, attributeName, this);
692
- this.delegate = delegate;
693
- this.parseResultsByToken = new WeakMap;
694
- this.valuesByTokenByElement = new WeakMap;
695
- }
696
- get started() {
697
- return this.tokenListObserver.started;
698
- }
699
- start() {
700
- this.tokenListObserver.start();
701
- }
702
- stop() {
703
- this.tokenListObserver.stop();
704
- }
705
- refresh() {
706
- this.tokenListObserver.refresh();
707
- }
708
- get element() {
709
- return this.tokenListObserver.element;
710
- }
711
- get attributeName() {
712
- return this.tokenListObserver.attributeName;
713
- }
714
- tokenMatched(token) {
715
- const {element: element} = token;
716
- const {value: value} = this.fetchParseResultForToken(token);
717
- if (value) {
718
- this.fetchValuesByTokenForElement(element).set(token, value);
719
- this.delegate.elementMatchedValue(element, value);
720
- }
721
- }
722
- tokenUnmatched(token) {
723
- const {element: element} = token;
724
- const {value: value} = this.fetchParseResultForToken(token);
725
- if (value) {
726
- this.fetchValuesByTokenForElement(element).delete(token);
727
- this.delegate.elementUnmatchedValue(element, value);
728
- }
729
- }
730
- fetchParseResultForToken(token) {
731
- let parseResult = this.parseResultsByToken.get(token);
732
- if (!parseResult) {
733
- parseResult = this.parseToken(token);
734
- this.parseResultsByToken.set(token, parseResult);
735
- }
736
- return parseResult;
737
- }
738
- fetchValuesByTokenForElement(element) {
739
- let valuesByToken = this.valuesByTokenByElement.get(element);
740
- if (!valuesByToken) {
741
- valuesByToken = new Map;
742
- this.valuesByTokenByElement.set(element, valuesByToken);
743
- }
744
- return valuesByToken;
745
- }
746
- parseToken(token) {
747
- try {
748
- const value = this.delegate.parseValueForToken(token);
749
- return {
750
- value: value
751
- };
752
- } catch (error) {
753
- return {
754
- error: error
755
- };
748
+ constructor(element, attributeName, delegate) {
749
+ this.tokenListObserver = new TokenListObserver(element, attributeName, this);
750
+ this.delegate = delegate;
751
+ this.parseResultsByToken = new WeakMap;
752
+ this.valuesByTokenByElement = new WeakMap;
753
+ }
754
+ get started() {
755
+ return this.tokenListObserver.started;
756
+ }
757
+ start() {
758
+ this.tokenListObserver.start();
759
+ }
760
+ stop() {
761
+ this.tokenListObserver.stop();
762
+ }
763
+ refresh() {
764
+ this.tokenListObserver.refresh();
765
+ }
766
+ get element() {
767
+ return this.tokenListObserver.element;
768
+ }
769
+ get attributeName() {
770
+ return this.tokenListObserver.attributeName;
771
+ }
772
+ tokenMatched(token) {
773
+ const { element } = token;
774
+ const { value } = this.fetchParseResultForToken(token);
775
+ if (value) {
776
+ this.fetchValuesByTokenForElement(element).set(token, value);
777
+ this.delegate.elementMatchedValue(element, value);
778
+ }
779
+ }
780
+ tokenUnmatched(token) {
781
+ const { element } = token;
782
+ const { value } = this.fetchParseResultForToken(token);
783
+ if (value) {
784
+ this.fetchValuesByTokenForElement(element).delete(token);
785
+ this.delegate.elementUnmatchedValue(element, value);
786
+ }
787
+ }
788
+ fetchParseResultForToken(token) {
789
+ let parseResult = this.parseResultsByToken.get(token);
790
+ if (!parseResult) {
791
+ parseResult = this.parseToken(token);
792
+ this.parseResultsByToken.set(token, parseResult);
793
+ }
794
+ return parseResult;
795
+ }
796
+ fetchValuesByTokenForElement(element) {
797
+ let valuesByToken = this.valuesByTokenByElement.get(element);
798
+ if (!valuesByToken) {
799
+ valuesByToken = new Map;
800
+ this.valuesByTokenByElement.set(element, valuesByToken);
801
+ }
802
+ return valuesByToken;
803
+ }
804
+ parseToken(token) {
805
+ try {
806
+ const value = this.delegate.parseValueForToken(token);
807
+ return { value };
808
+ }
809
+ catch (error) {
810
+ return { error };
811
+ }
756
812
  }
757
- }
758
813
  }
759
814
 
760
815
  class BindingObserver {
761
- constructor(context, delegate) {
762
- this.context = context;
763
- this.delegate = delegate;
764
- this.bindingsByAction = new Map;
765
- }
766
- start() {
767
- if (!this.valueListObserver) {
768
- this.valueListObserver = new ValueListObserver(this.element, this.actionAttribute, this);
769
- this.valueListObserver.start();
770
- }
771
- }
772
- stop() {
773
- if (this.valueListObserver) {
774
- this.valueListObserver.stop();
775
- delete this.valueListObserver;
776
- this.disconnectAllActions();
777
- }
778
- }
779
- get element() {
780
- return this.context.element;
781
- }
782
- get identifier() {
783
- return this.context.identifier;
784
- }
785
- get actionAttribute() {
786
- return this.schema.actionAttribute;
787
- }
788
- get schema() {
789
- return this.context.schema;
790
- }
791
- get bindings() {
792
- return Array.from(this.bindingsByAction.values());
793
- }
794
- connectAction(action) {
795
- const binding = new Binding(this.context, action);
796
- this.bindingsByAction.set(action, binding);
797
- this.delegate.bindingConnected(binding);
798
- }
799
- disconnectAction(action) {
800
- const binding = this.bindingsByAction.get(action);
801
- if (binding) {
802
- this.bindingsByAction.delete(action);
803
- this.delegate.bindingDisconnected(binding);
804
- }
805
- }
806
- disconnectAllActions() {
807
- this.bindings.forEach((binding => this.delegate.bindingDisconnected(binding)));
808
- this.bindingsByAction.clear();
809
- }
810
- parseValueForToken(token) {
811
- const action = Action.forToken(token);
812
- if (action.identifier == this.identifier) {
813
- return action;
814
- }
815
- }
816
- elementMatchedValue(element, action) {
817
- this.connectAction(action);
818
- }
819
- elementUnmatchedValue(element, action) {
820
- this.disconnectAction(action);
821
- }
816
+ constructor(context, delegate) {
817
+ this.context = context;
818
+ this.delegate = delegate;
819
+ this.bindingsByAction = new Map;
820
+ }
821
+ start() {
822
+ if (!this.valueListObserver) {
823
+ this.valueListObserver = new ValueListObserver(this.element, this.actionAttribute, this);
824
+ this.valueListObserver.start();
825
+ }
826
+ }
827
+ stop() {
828
+ if (this.valueListObserver) {
829
+ this.valueListObserver.stop();
830
+ delete this.valueListObserver;
831
+ this.disconnectAllActions();
832
+ }
833
+ }
834
+ get element() {
835
+ return this.context.element;
836
+ }
837
+ get identifier() {
838
+ return this.context.identifier;
839
+ }
840
+ get actionAttribute() {
841
+ return this.schema.actionAttribute;
842
+ }
843
+ get schema() {
844
+ return this.context.schema;
845
+ }
846
+ get bindings() {
847
+ return Array.from(this.bindingsByAction.values());
848
+ }
849
+ connectAction(action) {
850
+ const binding = new Binding(this.context, action);
851
+ this.bindingsByAction.set(action, binding);
852
+ this.delegate.bindingConnected(binding);
853
+ }
854
+ disconnectAction(action) {
855
+ const binding = this.bindingsByAction.get(action);
856
+ if (binding) {
857
+ this.bindingsByAction.delete(action);
858
+ this.delegate.bindingDisconnected(binding);
859
+ }
860
+ }
861
+ disconnectAllActions() {
862
+ this.bindings.forEach(binding => this.delegate.bindingDisconnected(binding));
863
+ this.bindingsByAction.clear();
864
+ }
865
+ parseValueForToken(token) {
866
+ const action = Action.forToken(token);
867
+ if (action.identifier == this.identifier) {
868
+ return action;
869
+ }
870
+ }
871
+ elementMatchedValue(element, action) {
872
+ this.connectAction(action);
873
+ }
874
+ elementUnmatchedValue(element, action) {
875
+ this.disconnectAction(action);
876
+ }
822
877
  }
823
878
 
824
879
  class ValueObserver {
825
- constructor(context, receiver) {
826
- this.context = context;
827
- this.receiver = receiver;
828
- this.stringMapObserver = new StringMapObserver(this.element, this);
829
- this.valueDescriptorMap = this.controller.valueDescriptorMap;
830
- this.invokeChangedCallbacksForDefaultValues();
831
- }
832
- start() {
833
- this.stringMapObserver.start();
834
- }
835
- stop() {
836
- this.stringMapObserver.stop();
837
- }
838
- get element() {
839
- return this.context.element;
840
- }
841
- get controller() {
842
- return this.context.controller;
843
- }
844
- getStringMapKeyForAttribute(attributeName) {
845
- if (attributeName in this.valueDescriptorMap) {
846
- return this.valueDescriptorMap[attributeName].name;
847
- }
848
- }
849
- stringMapValueChanged(attributeValue, name) {
850
- this.invokeChangedCallbackForValue(name);
851
- }
852
- invokeChangedCallbacksForDefaultValues() {
853
- for (const {key: key, name: name, defaultValue: defaultValue} of this.valueDescriptors) {
854
- if (defaultValue != undefined && !this.controller.data.has(key)) {
855
- this.invokeChangedCallbackForValue(name);
856
- }
857
- }
858
- }
859
- invokeChangedCallbackForValue(name) {
860
- const methodName = `${name}Changed`;
861
- const method = this.receiver[methodName];
862
- if (typeof method == "function") {
863
- const value = this.receiver[name];
864
- method.call(this.receiver, value);
865
- }
866
- }
867
- get valueDescriptors() {
868
- const {valueDescriptorMap: valueDescriptorMap} = this;
869
- return Object.keys(valueDescriptorMap).map((key => valueDescriptorMap[key]));
870
- }
880
+ constructor(context, receiver) {
881
+ this.context = context;
882
+ this.receiver = receiver;
883
+ this.stringMapObserver = new StringMapObserver(this.element, this);
884
+ this.valueDescriptorMap = this.controller.valueDescriptorMap;
885
+ this.invokeChangedCallbacksForDefaultValues();
886
+ }
887
+ start() {
888
+ this.stringMapObserver.start();
889
+ }
890
+ stop() {
891
+ this.stringMapObserver.stop();
892
+ }
893
+ get element() {
894
+ return this.context.element;
895
+ }
896
+ get controller() {
897
+ return this.context.controller;
898
+ }
899
+ getStringMapKeyForAttribute(attributeName) {
900
+ if (attributeName in this.valueDescriptorMap) {
901
+ return this.valueDescriptorMap[attributeName].name;
902
+ }
903
+ }
904
+ stringMapKeyAdded(key, attributeName) {
905
+ const descriptor = this.valueDescriptorMap[attributeName];
906
+ if (!this.hasValue(key)) {
907
+ this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), descriptor.writer(descriptor.defaultValue));
908
+ }
909
+ }
910
+ stringMapValueChanged(value, name, oldValue) {
911
+ const descriptor = this.valueDescriptorNameMap[name];
912
+ if (value === null)
913
+ return;
914
+ if (oldValue === null) {
915
+ oldValue = descriptor.writer(descriptor.defaultValue);
916
+ }
917
+ this.invokeChangedCallback(name, value, oldValue);
918
+ }
919
+ stringMapKeyRemoved(key, attributeName, oldValue) {
920
+ const descriptor = this.valueDescriptorNameMap[key];
921
+ if (this.hasValue(key)) {
922
+ this.invokeChangedCallback(key, descriptor.writer(this.receiver[key]), oldValue);
923
+ }
924
+ else {
925
+ this.invokeChangedCallback(key, descriptor.writer(descriptor.defaultValue), oldValue);
926
+ }
927
+ }
928
+ invokeChangedCallbacksForDefaultValues() {
929
+ for (const { key, name, defaultValue, writer } of this.valueDescriptors) {
930
+ if (defaultValue != undefined && !this.controller.data.has(key)) {
931
+ this.invokeChangedCallback(name, writer(defaultValue), undefined);
932
+ }
933
+ }
934
+ }
935
+ invokeChangedCallback(name, rawValue, rawOldValue) {
936
+ const changedMethodName = `${name}Changed`;
937
+ const changedMethod = this.receiver[changedMethodName];
938
+ if (typeof changedMethod == "function") {
939
+ const descriptor = this.valueDescriptorNameMap[name];
940
+ const value = descriptor.reader(rawValue);
941
+ let oldValue = rawOldValue;
942
+ if (rawOldValue) {
943
+ oldValue = descriptor.reader(rawOldValue);
944
+ }
945
+ changedMethod.call(this.receiver, value, oldValue);
946
+ }
947
+ }
948
+ get valueDescriptors() {
949
+ const { valueDescriptorMap } = this;
950
+ return Object.keys(valueDescriptorMap).map(key => valueDescriptorMap[key]);
951
+ }
952
+ get valueDescriptorNameMap() {
953
+ const descriptors = {};
954
+ Object.keys(this.valueDescriptorMap).forEach(key => {
955
+ const descriptor = this.valueDescriptorMap[key];
956
+ descriptors[descriptor.name] = descriptor;
957
+ });
958
+ return descriptors;
959
+ }
960
+ hasValue(attributeName) {
961
+ const descriptor = this.valueDescriptorNameMap[attributeName];
962
+ const hasMethodName = `has${capitalize(descriptor.name)}`;
963
+ return this.receiver[hasMethodName];
964
+ }
965
+ }
966
+
967
+ class TargetObserver {
968
+ constructor(context, delegate) {
969
+ this.context = context;
970
+ this.delegate = delegate;
971
+ this.targetsByName = new Multimap;
972
+ }
973
+ start() {
974
+ if (!this.tokenListObserver) {
975
+ this.tokenListObserver = new TokenListObserver(this.element, this.attributeName, this);
976
+ this.tokenListObserver.start();
977
+ }
978
+ }
979
+ stop() {
980
+ if (this.tokenListObserver) {
981
+ this.disconnectAllTargets();
982
+ this.tokenListObserver.stop();
983
+ delete this.tokenListObserver;
984
+ }
985
+ }
986
+ tokenMatched({ element, content: name }) {
987
+ if (this.scope.containsElement(element)) {
988
+ this.connectTarget(element, name);
989
+ }
990
+ }
991
+ tokenUnmatched({ element, content: name }) {
992
+ this.disconnectTarget(element, name);
993
+ }
994
+ connectTarget(element, name) {
995
+ if (!this.targetsByName.has(name, element)) {
996
+ this.targetsByName.add(name, element);
997
+ this.delegate.targetConnected(element, name);
998
+ }
999
+ }
1000
+ disconnectTarget(element, name) {
1001
+ if (this.targetsByName.has(name, element)) {
1002
+ this.targetsByName.delete(name, element);
1003
+ this.delegate.targetDisconnected(element, name);
1004
+ }
1005
+ }
1006
+ disconnectAllTargets() {
1007
+ for (const name of this.targetsByName.keys) {
1008
+ for (const element of this.targetsByName.getValuesForKey(name)) {
1009
+ this.disconnectTarget(element, name);
1010
+ }
1011
+ }
1012
+ }
1013
+ get attributeName() {
1014
+ return `data-${this.context.identifier}-target`;
1015
+ }
1016
+ get element() {
1017
+ return this.context.element;
1018
+ }
1019
+ get scope() {
1020
+ return this.context.scope;
1021
+ }
871
1022
  }
872
1023
 
873
1024
  class Context {
874
- constructor(module, scope) {
875
- this.module = module;
876
- this.scope = scope;
877
- this.controller = new module.controllerConstructor(this);
878
- this.bindingObserver = new BindingObserver(this, this.dispatcher);
879
- this.valueObserver = new ValueObserver(this, this.controller);
880
- try {
881
- this.controller.initialize();
882
- } catch (error) {
883
- this.handleError(error, "initializing controller");
884
- }
885
- }
886
- connect() {
887
- this.bindingObserver.start();
888
- this.valueObserver.start();
889
- try {
890
- this.controller.connect();
891
- } catch (error) {
892
- this.handleError(error, "connecting controller");
1025
+ constructor(module, scope) {
1026
+ this.logDebugActivity = (functionName, detail = {}) => {
1027
+ const { identifier, controller, element } = this;
1028
+ detail = Object.assign({ identifier, controller, element }, detail);
1029
+ this.application.logDebugActivity(this.identifier, functionName, detail);
1030
+ };
1031
+ this.module = module;
1032
+ this.scope = scope;
1033
+ this.controller = new module.controllerConstructor(this);
1034
+ this.bindingObserver = new BindingObserver(this, this.dispatcher);
1035
+ this.valueObserver = new ValueObserver(this, this.controller);
1036
+ this.targetObserver = new TargetObserver(this, this);
1037
+ try {
1038
+ this.controller.initialize();
1039
+ this.logDebugActivity("initialize");
1040
+ }
1041
+ catch (error) {
1042
+ this.handleError(error, "initializing controller");
1043
+ }
1044
+ }
1045
+ connect() {
1046
+ this.bindingObserver.start();
1047
+ this.valueObserver.start();
1048
+ this.targetObserver.start();
1049
+ try {
1050
+ this.controller.connect();
1051
+ this.logDebugActivity("connect");
1052
+ }
1053
+ catch (error) {
1054
+ this.handleError(error, "connecting controller");
1055
+ }
1056
+ }
1057
+ disconnect() {
1058
+ try {
1059
+ this.controller.disconnect();
1060
+ this.logDebugActivity("disconnect");
1061
+ }
1062
+ catch (error) {
1063
+ this.handleError(error, "disconnecting controller");
1064
+ }
1065
+ this.targetObserver.stop();
1066
+ this.valueObserver.stop();
1067
+ this.bindingObserver.stop();
1068
+ }
1069
+ get application() {
1070
+ return this.module.application;
1071
+ }
1072
+ get identifier() {
1073
+ return this.module.identifier;
1074
+ }
1075
+ get schema() {
1076
+ return this.application.schema;
1077
+ }
1078
+ get dispatcher() {
1079
+ return this.application.dispatcher;
1080
+ }
1081
+ get element() {
1082
+ return this.scope.element;
1083
+ }
1084
+ get parentElement() {
1085
+ return this.element.parentElement;
1086
+ }
1087
+ handleError(error, message, detail = {}) {
1088
+ const { identifier, controller, element } = this;
1089
+ detail = Object.assign({ identifier, controller, element }, detail);
1090
+ this.application.handleError(error, `Error ${message}`, detail);
1091
+ }
1092
+ targetConnected(element, name) {
1093
+ this.invokeControllerMethod(`${name}TargetConnected`, element);
1094
+ }
1095
+ targetDisconnected(element, name) {
1096
+ this.invokeControllerMethod(`${name}TargetDisconnected`, element);
1097
+ }
1098
+ invokeControllerMethod(methodName, ...args) {
1099
+ const controller = this.controller;
1100
+ if (typeof controller[methodName] == "function") {
1101
+ controller[methodName](...args);
1102
+ }
893
1103
  }
894
- }
895
- disconnect() {
896
- try {
897
- this.controller.disconnect();
898
- } catch (error) {
899
- this.handleError(error, "disconnecting controller");
900
- }
901
- this.valueObserver.stop();
902
- this.bindingObserver.stop();
903
- }
904
- get application() {
905
- return this.module.application;
906
- }
907
- get identifier() {
908
- return this.module.identifier;
909
- }
910
- get schema() {
911
- return this.application.schema;
912
- }
913
- get dispatcher() {
914
- return this.application.dispatcher;
915
- }
916
- get element() {
917
- return this.scope.element;
918
- }
919
- get parentElement() {
920
- return this.element.parentElement;
921
- }
922
- handleError(error, message, detail = {}) {
923
- const {identifier: identifier, controller: controller, element: element} = this;
924
- detail = Object.assign({
925
- identifier: identifier,
926
- controller: controller,
927
- element: element
928
- }, detail);
929
- this.application.handleError(error, `Error ${message}`, detail);
930
- }
931
1104
  }
932
1105
 
933
1106
  function readInheritableStaticArrayValues(constructor, propertyName) {
934
- const ancestors = getAncestorsForConstructor(constructor);
935
- return Array.from(ancestors.reduce(((values, constructor) => {
936
- getOwnStaticArrayValues(constructor, propertyName).forEach((name => values.add(name)));
937
- return values;
938
- }), new Set));
1107
+ const ancestors = getAncestorsForConstructor(constructor);
1108
+ return Array.from(ancestors.reduce((values, constructor) => {
1109
+ getOwnStaticArrayValues(constructor, propertyName).forEach(name => values.add(name));
1110
+ return values;
1111
+ }, new Set));
939
1112
  }
940
-
941
1113
  function readInheritableStaticObjectPairs(constructor, propertyName) {
942
- const ancestors = getAncestorsForConstructor(constructor);
943
- return ancestors.reduce(((pairs, constructor) => {
944
- pairs.push(...getOwnStaticObjectPairs(constructor, propertyName));
945
- return pairs;
946
- }), []);
1114
+ const ancestors = getAncestorsForConstructor(constructor);
1115
+ return ancestors.reduce((pairs, constructor) => {
1116
+ pairs.push(...getOwnStaticObjectPairs(constructor, propertyName));
1117
+ return pairs;
1118
+ }, []);
947
1119
  }
948
-
949
1120
  function getAncestorsForConstructor(constructor) {
950
- const ancestors = [];
951
- while (constructor) {
952
- ancestors.push(constructor);
953
- constructor = Object.getPrototypeOf(constructor);
954
- }
955
- return ancestors.reverse();
1121
+ const ancestors = [];
1122
+ while (constructor) {
1123
+ ancestors.push(constructor);
1124
+ constructor = Object.getPrototypeOf(constructor);
1125
+ }
1126
+ return ancestors.reverse();
956
1127
  }
957
-
958
1128
  function getOwnStaticArrayValues(constructor, propertyName) {
959
- const definition = constructor[propertyName];
960
- return Array.isArray(definition) ? definition : [];
1129
+ const definition = constructor[propertyName];
1130
+ return Array.isArray(definition) ? definition : [];
961
1131
  }
962
-
963
1132
  function getOwnStaticObjectPairs(constructor, propertyName) {
964
- const definition = constructor[propertyName];
965
- return definition ? Object.keys(definition).map((key => [ key, definition[key] ])) : [];
1133
+ const definition = constructor[propertyName];
1134
+ return definition ? Object.keys(definition).map(key => [key, definition[key]]) : [];
966
1135
  }
967
1136
 
968
1137
  function bless(constructor) {
969
- return shadow(constructor, getBlessedProperties(constructor));
1138
+ return shadow(constructor, getBlessedProperties(constructor));
970
1139
  }
971
-
972
1140
  function shadow(constructor, properties) {
973
- const shadowConstructor = extend(constructor);
974
- const shadowProperties = getShadowProperties(constructor.prototype, properties);
975
- Object.defineProperties(shadowConstructor.prototype, shadowProperties);
976
- return shadowConstructor;
1141
+ const shadowConstructor = extend(constructor);
1142
+ const shadowProperties = getShadowProperties(constructor.prototype, properties);
1143
+ Object.defineProperties(shadowConstructor.prototype, shadowProperties);
1144
+ return shadowConstructor;
977
1145
  }
978
-
979
1146
  function getBlessedProperties(constructor) {
980
- const blessings = readInheritableStaticArrayValues(constructor, "blessings");
981
- return blessings.reduce(((blessedProperties, blessing) => {
982
- const properties = blessing(constructor);
983
- for (const key in properties) {
984
- const descriptor = blessedProperties[key] || {};
985
- blessedProperties[key] = Object.assign(descriptor, properties[key]);
986
- }
987
- return blessedProperties;
988
- }), {});
1147
+ const blessings = readInheritableStaticArrayValues(constructor, "blessings");
1148
+ return blessings.reduce((blessedProperties, blessing) => {
1149
+ const properties = blessing(constructor);
1150
+ for (const key in properties) {
1151
+ const descriptor = blessedProperties[key] || {};
1152
+ blessedProperties[key] = Object.assign(descriptor, properties[key]);
1153
+ }
1154
+ return blessedProperties;
1155
+ }, {});
989
1156
  }
990
-
991
1157
  function getShadowProperties(prototype, properties) {
992
- return getOwnKeys(properties).reduce(((shadowProperties, key) => {
993
- const descriptor = getShadowedDescriptor(prototype, properties, key);
994
- if (descriptor) {
995
- Object.assign(shadowProperties, {
996
- [key]: descriptor
997
- });
998
- }
999
- return shadowProperties;
1000
- }), {});
1158
+ return getOwnKeys(properties).reduce((shadowProperties, key) => {
1159
+ const descriptor = getShadowedDescriptor(prototype, properties, key);
1160
+ if (descriptor) {
1161
+ Object.assign(shadowProperties, { [key]: descriptor });
1162
+ }
1163
+ return shadowProperties;
1164
+ }, {});
1001
1165
  }
1002
-
1003
1166
  function getShadowedDescriptor(prototype, properties, key) {
1004
- const shadowingDescriptor = Object.getOwnPropertyDescriptor(prototype, key);
1005
- const shadowedByValue = shadowingDescriptor && "value" in shadowingDescriptor;
1006
- if (!shadowedByValue) {
1007
- const descriptor = Object.getOwnPropertyDescriptor(properties, key).value;
1008
- if (shadowingDescriptor) {
1009
- descriptor.get = shadowingDescriptor.get || descriptor.get;
1010
- descriptor.set = shadowingDescriptor.set || descriptor.set;
1167
+ const shadowingDescriptor = Object.getOwnPropertyDescriptor(prototype, key);
1168
+ const shadowedByValue = shadowingDescriptor && "value" in shadowingDescriptor;
1169
+ if (!shadowedByValue) {
1170
+ const descriptor = Object.getOwnPropertyDescriptor(properties, key).value;
1171
+ if (shadowingDescriptor) {
1172
+ descriptor.get = shadowingDescriptor.get || descriptor.get;
1173
+ descriptor.set = shadowingDescriptor.set || descriptor.set;
1174
+ }
1175
+ return descriptor;
1011
1176
  }
1012
- return descriptor;
1013
- }
1014
1177
  }
1015
-
1016
1178
  const getOwnKeys = (() => {
1017
- if (typeof Object.getOwnPropertySymbols == "function") {
1018
- return object => [ ...Object.getOwnPropertyNames(object), ...Object.getOwnPropertySymbols(object) ];
1019
- } else {
1020
- return Object.getOwnPropertyNames;
1021
- }
1179
+ if (typeof Object.getOwnPropertySymbols == "function") {
1180
+ return (object) => [
1181
+ ...Object.getOwnPropertyNames(object),
1182
+ ...Object.getOwnPropertySymbols(object)
1183
+ ];
1184
+ }
1185
+ else {
1186
+ return Object.getOwnPropertyNames;
1187
+ }
1022
1188
  })();
1023
-
1024
1189
  const extend = (() => {
1025
- function extendWithReflect(constructor) {
1026
- function extended() {
1027
- return Reflect.construct(constructor, arguments, new.target);
1028
- }
1029
- extended.prototype = Object.create(constructor.prototype, {
1030
- constructor: {
1031
- value: extended
1032
- }
1033
- });
1034
- Reflect.setPrototypeOf(extended, constructor);
1035
- return extended;
1036
- }
1037
- function testReflectExtension() {
1038
- const a = function() {
1039
- this.a.call(this);
1040
- };
1041
- const b = extendWithReflect(a);
1042
- b.prototype.a = function() {};
1043
- return new b;
1044
- }
1045
- try {
1046
- testReflectExtension();
1047
- return extendWithReflect;
1048
- } catch (error) {
1049
- return constructor => class extended extends constructor {};
1050
- }
1190
+ function extendWithReflect(constructor) {
1191
+ function extended() {
1192
+ return Reflect.construct(constructor, arguments, new.target);
1193
+ }
1194
+ extended.prototype = Object.create(constructor.prototype, {
1195
+ constructor: { value: extended }
1196
+ });
1197
+ Reflect.setPrototypeOf(extended, constructor);
1198
+ return extended;
1199
+ }
1200
+ function testReflectExtension() {
1201
+ const a = function () { this.a.call(this); };
1202
+ const b = extendWithReflect(a);
1203
+ b.prototype.a = function () { };
1204
+ return new b;
1205
+ }
1206
+ try {
1207
+ testReflectExtension();
1208
+ return extendWithReflect;
1209
+ }
1210
+ catch (error) {
1211
+ return (constructor) => class extended extends constructor {
1212
+ };
1213
+ }
1051
1214
  })();
1052
1215
 
1053
1216
  function blessDefinition(definition) {
1054
- return {
1055
- identifier: definition.identifier,
1056
- controllerConstructor: bless(definition.controllerConstructor)
1057
- };
1217
+ return {
1218
+ identifier: definition.identifier,
1219
+ controllerConstructor: bless(definition.controllerConstructor)
1220
+ };
1058
1221
  }
1059
1222
 
1060
1223
  class Module {
1061
- constructor(application, definition) {
1062
- this.application = application;
1063
- this.definition = blessDefinition(definition);
1064
- this.contextsByScope = new WeakMap;
1065
- this.connectedContexts = new Set;
1066
- }
1067
- get identifier() {
1068
- return this.definition.identifier;
1069
- }
1070
- get controllerConstructor() {
1071
- return this.definition.controllerConstructor;
1072
- }
1073
- get contexts() {
1074
- return Array.from(this.connectedContexts);
1075
- }
1076
- connectContextForScope(scope) {
1077
- const context = this.fetchContextForScope(scope);
1078
- this.connectedContexts.add(context);
1079
- context.connect();
1080
- }
1081
- disconnectContextForScope(scope) {
1082
- const context = this.contextsByScope.get(scope);
1083
- if (context) {
1084
- this.connectedContexts.delete(context);
1085
- context.disconnect();
1086
- }
1087
- }
1088
- fetchContextForScope(scope) {
1089
- let context = this.contextsByScope.get(scope);
1090
- if (!context) {
1091
- context = new Context(this, scope);
1092
- this.contextsByScope.set(scope, context);
1093
- }
1094
- return context;
1095
- }
1224
+ constructor(application, definition) {
1225
+ this.application = application;
1226
+ this.definition = blessDefinition(definition);
1227
+ this.contextsByScope = new WeakMap;
1228
+ this.connectedContexts = new Set;
1229
+ }
1230
+ get identifier() {
1231
+ return this.definition.identifier;
1232
+ }
1233
+ get controllerConstructor() {
1234
+ return this.definition.controllerConstructor;
1235
+ }
1236
+ get contexts() {
1237
+ return Array.from(this.connectedContexts);
1238
+ }
1239
+ connectContextForScope(scope) {
1240
+ const context = this.fetchContextForScope(scope);
1241
+ this.connectedContexts.add(context);
1242
+ context.connect();
1243
+ }
1244
+ disconnectContextForScope(scope) {
1245
+ const context = this.contextsByScope.get(scope);
1246
+ if (context) {
1247
+ this.connectedContexts.delete(context);
1248
+ context.disconnect();
1249
+ }
1250
+ }
1251
+ fetchContextForScope(scope) {
1252
+ let context = this.contextsByScope.get(scope);
1253
+ if (!context) {
1254
+ context = new Context(this, scope);
1255
+ this.contextsByScope.set(scope, context);
1256
+ }
1257
+ return context;
1258
+ }
1096
1259
  }
1097
1260
 
1098
1261
  class ClassMap {
1099
- constructor(scope) {
1100
- this.scope = scope;
1101
- }
1102
- has(name) {
1103
- return this.data.has(this.getDataKey(name));
1104
- }
1105
- get(name) {
1106
- return this.data.get(this.getDataKey(name));
1107
- }
1108
- getAttributeName(name) {
1109
- return this.data.getAttributeNameForKey(this.getDataKey(name));
1110
- }
1111
- getDataKey(name) {
1112
- return `${name}-class`;
1113
- }
1114
- get data() {
1115
- return this.scope.data;
1116
- }
1117
- }
1118
-
1119
- function camelize(value) {
1120
- return value.replace(/(?:[_-])([a-z0-9])/g, ((_, char) => char.toUpperCase()));
1121
- }
1122
-
1123
- function capitalize(value) {
1124
- return value.charAt(0).toUpperCase() + value.slice(1);
1125
- }
1126
-
1127
- function dasherize(value) {
1128
- return value.replace(/([A-Z])/g, ((_, char) => `-${char.toLowerCase()}`));
1262
+ constructor(scope) {
1263
+ this.scope = scope;
1264
+ }
1265
+ has(name) {
1266
+ return this.data.has(this.getDataKey(name));
1267
+ }
1268
+ get(name) {
1269
+ return this.getAll(name)[0];
1270
+ }
1271
+ getAll(name) {
1272
+ const tokenString = this.data.get(this.getDataKey(name)) || "";
1273
+ return tokenize(tokenString);
1274
+ }
1275
+ getAttributeName(name) {
1276
+ return this.data.getAttributeNameForKey(this.getDataKey(name));
1277
+ }
1278
+ getDataKey(name) {
1279
+ return `${name}-class`;
1280
+ }
1281
+ get data() {
1282
+ return this.scope.data;
1283
+ }
1129
1284
  }
1130
1285
 
1131
1286
  class DataMap {
1132
- constructor(scope) {
1133
- this.scope = scope;
1134
- }
1135
- get element() {
1136
- return this.scope.element;
1137
- }
1138
- get identifier() {
1139
- return this.scope.identifier;
1140
- }
1141
- get(key) {
1142
- const name = this.getAttributeNameForKey(key);
1143
- return this.element.getAttribute(name);
1144
- }
1145
- set(key, value) {
1146
- const name = this.getAttributeNameForKey(key);
1147
- this.element.setAttribute(name, value);
1148
- return this.get(key);
1149
- }
1150
- has(key) {
1151
- const name = this.getAttributeNameForKey(key);
1152
- return this.element.hasAttribute(name);
1153
- }
1154
- delete(key) {
1155
- if (this.has(key)) {
1156
- const name = this.getAttributeNameForKey(key);
1157
- this.element.removeAttribute(name);
1158
- return true;
1159
- } else {
1160
- return false;
1161
- }
1162
- }
1163
- getAttributeNameForKey(key) {
1164
- return `data-${this.identifier}-${dasherize(key)}`;
1165
- }
1287
+ constructor(scope) {
1288
+ this.scope = scope;
1289
+ }
1290
+ get element() {
1291
+ return this.scope.element;
1292
+ }
1293
+ get identifier() {
1294
+ return this.scope.identifier;
1295
+ }
1296
+ get(key) {
1297
+ const name = this.getAttributeNameForKey(key);
1298
+ return this.element.getAttribute(name);
1299
+ }
1300
+ set(key, value) {
1301
+ const name = this.getAttributeNameForKey(key);
1302
+ this.element.setAttribute(name, value);
1303
+ return this.get(key);
1304
+ }
1305
+ has(key) {
1306
+ const name = this.getAttributeNameForKey(key);
1307
+ return this.element.hasAttribute(name);
1308
+ }
1309
+ delete(key) {
1310
+ if (this.has(key)) {
1311
+ const name = this.getAttributeNameForKey(key);
1312
+ this.element.removeAttribute(name);
1313
+ return true;
1314
+ }
1315
+ else {
1316
+ return false;
1317
+ }
1318
+ }
1319
+ getAttributeNameForKey(key) {
1320
+ return `data-${this.identifier}-${dasherize(key)}`;
1321
+ }
1166
1322
  }
1167
1323
 
1168
1324
  class Guide {
1169
- constructor(logger) {
1170
- this.warnedKeysByObject = new WeakMap;
1171
- this.logger = logger;
1172
- }
1173
- warn(object, key, message) {
1174
- let warnedKeys = this.warnedKeysByObject.get(object);
1175
- if (!warnedKeys) {
1176
- warnedKeys = new Set;
1177
- this.warnedKeysByObject.set(object, warnedKeys);
1178
- }
1179
- if (!warnedKeys.has(key)) {
1180
- warnedKeys.add(key);
1181
- this.logger.warn(message, object);
1182
- }
1183
- }
1325
+ constructor(logger) {
1326
+ this.warnedKeysByObject = new WeakMap;
1327
+ this.logger = logger;
1328
+ }
1329
+ warn(object, key, message) {
1330
+ let warnedKeys = this.warnedKeysByObject.get(object);
1331
+ if (!warnedKeys) {
1332
+ warnedKeys = new Set;
1333
+ this.warnedKeysByObject.set(object, warnedKeys);
1334
+ }
1335
+ if (!warnedKeys.has(key)) {
1336
+ warnedKeys.add(key);
1337
+ this.logger.warn(message, object);
1338
+ }
1339
+ }
1184
1340
  }
1185
1341
 
1186
1342
  function attributeValueContainsToken(attributeName, token) {
1187
- return `[${attributeName}~="${token}"]`;
1343
+ return `[${attributeName}~="${token}"]`;
1188
1344
  }
1189
1345
 
1190
1346
  class TargetSet {
1191
- constructor(scope) {
1192
- this.scope = scope;
1193
- }
1194
- get element() {
1195
- return this.scope.element;
1196
- }
1197
- get identifier() {
1198
- return this.scope.identifier;
1199
- }
1200
- get schema() {
1201
- return this.scope.schema;
1202
- }
1203
- has(targetName) {
1204
- return this.find(targetName) != null;
1205
- }
1206
- find(...targetNames) {
1207
- return targetNames.reduce(((target, targetName) => target || this.findTarget(targetName) || this.findLegacyTarget(targetName)), undefined);
1208
- }
1209
- findAll(...targetNames) {
1210
- return targetNames.reduce(((targets, targetName) => [ ...targets, ...this.findAllTargets(targetName), ...this.findAllLegacyTargets(targetName) ]), []);
1211
- }
1212
- findTarget(targetName) {
1213
- const selector = this.getSelectorForTargetName(targetName);
1214
- return this.scope.findElement(selector);
1215
- }
1216
- findAllTargets(targetName) {
1217
- const selector = this.getSelectorForTargetName(targetName);
1218
- return this.scope.findAllElements(selector);
1219
- }
1220
- getSelectorForTargetName(targetName) {
1221
- const attributeName = `data-${this.identifier}-target`;
1222
- return attributeValueContainsToken(attributeName, targetName);
1223
- }
1224
- findLegacyTarget(targetName) {
1225
- const selector = this.getLegacySelectorForTargetName(targetName);
1226
- return this.deprecate(this.scope.findElement(selector), targetName);
1227
- }
1228
- findAllLegacyTargets(targetName) {
1229
- const selector = this.getLegacySelectorForTargetName(targetName);
1230
- return this.scope.findAllElements(selector).map((element => this.deprecate(element, targetName)));
1231
- }
1232
- getLegacySelectorForTargetName(targetName) {
1233
- const targetDescriptor = `${this.identifier}.${targetName}`;
1234
- return attributeValueContainsToken(this.schema.targetAttribute, targetDescriptor);
1235
- }
1236
- deprecate(element, targetName) {
1237
- if (element) {
1238
- const {identifier: identifier} = this;
1239
- const attributeName = this.schema.targetAttribute;
1240
- this.guide.warn(element, `target:${targetName}`, `Please replace ${attributeName}="${identifier}.${targetName}" with data-${identifier}-target="${targetName}". ` + `The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.`);
1241
- }
1242
- return element;
1243
- }
1244
- get guide() {
1245
- return this.scope.guide;
1246
- }
1347
+ constructor(scope) {
1348
+ this.scope = scope;
1349
+ }
1350
+ get element() {
1351
+ return this.scope.element;
1352
+ }
1353
+ get identifier() {
1354
+ return this.scope.identifier;
1355
+ }
1356
+ get schema() {
1357
+ return this.scope.schema;
1358
+ }
1359
+ has(targetName) {
1360
+ return this.find(targetName) != null;
1361
+ }
1362
+ find(...targetNames) {
1363
+ return targetNames.reduce((target, targetName) => target
1364
+ || this.findTarget(targetName)
1365
+ || this.findLegacyTarget(targetName), undefined);
1366
+ }
1367
+ findAll(...targetNames) {
1368
+ return targetNames.reduce((targets, targetName) => [
1369
+ ...targets,
1370
+ ...this.findAllTargets(targetName),
1371
+ ...this.findAllLegacyTargets(targetName)
1372
+ ], []);
1373
+ }
1374
+ findTarget(targetName) {
1375
+ const selector = this.getSelectorForTargetName(targetName);
1376
+ return this.scope.findElement(selector);
1377
+ }
1378
+ findAllTargets(targetName) {
1379
+ const selector = this.getSelectorForTargetName(targetName);
1380
+ return this.scope.findAllElements(selector);
1381
+ }
1382
+ getSelectorForTargetName(targetName) {
1383
+ const attributeName = this.schema.targetAttributeForScope(this.identifier);
1384
+ return attributeValueContainsToken(attributeName, targetName);
1385
+ }
1386
+ findLegacyTarget(targetName) {
1387
+ const selector = this.getLegacySelectorForTargetName(targetName);
1388
+ return this.deprecate(this.scope.findElement(selector), targetName);
1389
+ }
1390
+ findAllLegacyTargets(targetName) {
1391
+ const selector = this.getLegacySelectorForTargetName(targetName);
1392
+ return this.scope.findAllElements(selector).map(element => this.deprecate(element, targetName));
1393
+ }
1394
+ getLegacySelectorForTargetName(targetName) {
1395
+ const targetDescriptor = `${this.identifier}.${targetName}`;
1396
+ return attributeValueContainsToken(this.schema.targetAttribute, targetDescriptor);
1397
+ }
1398
+ deprecate(element, targetName) {
1399
+ if (element) {
1400
+ const { identifier } = this;
1401
+ const attributeName = this.schema.targetAttribute;
1402
+ const revisedAttributeName = this.schema.targetAttributeForScope(identifier);
1403
+ this.guide.warn(element, `target:${targetName}`, `Please replace ${attributeName}="${identifier}.${targetName}" with ${revisedAttributeName}="${targetName}". ` +
1404
+ `The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.`);
1405
+ }
1406
+ return element;
1407
+ }
1408
+ get guide() {
1409
+ return this.scope.guide;
1410
+ }
1247
1411
  }
1248
1412
 
1249
1413
  class Scope {
1250
- constructor(schema, element, identifier, logger) {
1251
- this.targets = new TargetSet(this);
1252
- this.classes = new ClassMap(this);
1253
- this.data = new DataMap(this);
1254
- this.containsElement = element => element.closest(this.controllerSelector) === this.element;
1255
- this.schema = schema;
1256
- this.element = element;
1257
- this.identifier = identifier;
1258
- this.guide = new Guide(logger);
1259
- }
1260
- findElement(selector) {
1261
- return this.element.matches(selector) ? this.element : this.queryElements(selector).find(this.containsElement);
1262
- }
1263
- findAllElements(selector) {
1264
- return [ ...this.element.matches(selector) ? [ this.element ] : [], ...this.queryElements(selector).filter(this.containsElement) ];
1265
- }
1266
- queryElements(selector) {
1267
- return Array.from(this.element.querySelectorAll(selector));
1268
- }
1269
- get controllerSelector() {
1270
- return attributeValueContainsToken(this.schema.controllerAttribute, this.identifier);
1271
- }
1414
+ constructor(schema, element, identifier, logger) {
1415
+ this.targets = new TargetSet(this);
1416
+ this.classes = new ClassMap(this);
1417
+ this.data = new DataMap(this);
1418
+ this.containsElement = (element) => {
1419
+ return element.closest(this.controllerSelector) === this.element;
1420
+ };
1421
+ this.schema = schema;
1422
+ this.element = element;
1423
+ this.identifier = identifier;
1424
+ this.guide = new Guide(logger);
1425
+ }
1426
+ findElement(selector) {
1427
+ return this.element.matches(selector)
1428
+ ? this.element
1429
+ : this.queryElements(selector).find(this.containsElement);
1430
+ }
1431
+ findAllElements(selector) {
1432
+ return [
1433
+ ...this.element.matches(selector) ? [this.element] : [],
1434
+ ...this.queryElements(selector).filter(this.containsElement)
1435
+ ];
1436
+ }
1437
+ queryElements(selector) {
1438
+ return Array.from(this.element.querySelectorAll(selector));
1439
+ }
1440
+ get controllerSelector() {
1441
+ return attributeValueContainsToken(this.schema.controllerAttribute, this.identifier);
1442
+ }
1272
1443
  }
1273
1444
 
1274
1445
  class ScopeObserver {
1275
- constructor(element, schema, delegate) {
1276
- this.element = element;
1277
- this.schema = schema;
1278
- this.delegate = delegate;
1279
- this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this);
1280
- this.scopesByIdentifierByElement = new WeakMap;
1281
- this.scopeReferenceCounts = new WeakMap;
1282
- }
1283
- start() {
1284
- this.valueListObserver.start();
1285
- }
1286
- stop() {
1287
- this.valueListObserver.stop();
1288
- }
1289
- get controllerAttribute() {
1290
- return this.schema.controllerAttribute;
1291
- }
1292
- parseValueForToken(token) {
1293
- const {element: element, content: identifier} = token;
1294
- const scopesByIdentifier = this.fetchScopesByIdentifierForElement(element);
1295
- let scope = scopesByIdentifier.get(identifier);
1296
- if (!scope) {
1297
- scope = this.delegate.createScopeForElementAndIdentifier(element, identifier);
1298
- scopesByIdentifier.set(identifier, scope);
1299
- }
1300
- return scope;
1301
- }
1302
- elementMatchedValue(element, value) {
1303
- const referenceCount = (this.scopeReferenceCounts.get(value) || 0) + 1;
1304
- this.scopeReferenceCounts.set(value, referenceCount);
1305
- if (referenceCount == 1) {
1306
- this.delegate.scopeConnected(value);
1307
- }
1308
- }
1309
- elementUnmatchedValue(element, value) {
1310
- const referenceCount = this.scopeReferenceCounts.get(value);
1311
- if (referenceCount) {
1312
- this.scopeReferenceCounts.set(value, referenceCount - 1);
1313
- if (referenceCount == 1) {
1314
- this.delegate.scopeDisconnected(value);
1315
- }
1316
- }
1317
- }
1318
- fetchScopesByIdentifierForElement(element) {
1319
- let scopesByIdentifier = this.scopesByIdentifierByElement.get(element);
1320
- if (!scopesByIdentifier) {
1321
- scopesByIdentifier = new Map;
1322
- this.scopesByIdentifierByElement.set(element, scopesByIdentifier);
1323
- }
1324
- return scopesByIdentifier;
1325
- }
1446
+ constructor(element, schema, delegate) {
1447
+ this.element = element;
1448
+ this.schema = schema;
1449
+ this.delegate = delegate;
1450
+ this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this);
1451
+ this.scopesByIdentifierByElement = new WeakMap;
1452
+ this.scopeReferenceCounts = new WeakMap;
1453
+ }
1454
+ start() {
1455
+ this.valueListObserver.start();
1456
+ }
1457
+ stop() {
1458
+ this.valueListObserver.stop();
1459
+ }
1460
+ get controllerAttribute() {
1461
+ return this.schema.controllerAttribute;
1462
+ }
1463
+ parseValueForToken(token) {
1464
+ const { element, content: identifier } = token;
1465
+ const scopesByIdentifier = this.fetchScopesByIdentifierForElement(element);
1466
+ let scope = scopesByIdentifier.get(identifier);
1467
+ if (!scope) {
1468
+ scope = this.delegate.createScopeForElementAndIdentifier(element, identifier);
1469
+ scopesByIdentifier.set(identifier, scope);
1470
+ }
1471
+ return scope;
1472
+ }
1473
+ elementMatchedValue(element, value) {
1474
+ const referenceCount = (this.scopeReferenceCounts.get(value) || 0) + 1;
1475
+ this.scopeReferenceCounts.set(value, referenceCount);
1476
+ if (referenceCount == 1) {
1477
+ this.delegate.scopeConnected(value);
1478
+ }
1479
+ }
1480
+ elementUnmatchedValue(element, value) {
1481
+ const referenceCount = this.scopeReferenceCounts.get(value);
1482
+ if (referenceCount) {
1483
+ this.scopeReferenceCounts.set(value, referenceCount - 1);
1484
+ if (referenceCount == 1) {
1485
+ this.delegate.scopeDisconnected(value);
1486
+ }
1487
+ }
1488
+ }
1489
+ fetchScopesByIdentifierForElement(element) {
1490
+ let scopesByIdentifier = this.scopesByIdentifierByElement.get(element);
1491
+ if (!scopesByIdentifier) {
1492
+ scopesByIdentifier = new Map;
1493
+ this.scopesByIdentifierByElement.set(element, scopesByIdentifier);
1494
+ }
1495
+ return scopesByIdentifier;
1496
+ }
1326
1497
  }
1327
1498
 
1328
1499
  class Router {
1329
- constructor(application) {
1330
- this.application = application;
1331
- this.scopeObserver = new ScopeObserver(this.element, this.schema, this);
1332
- this.scopesByIdentifier = new Multimap;
1333
- this.modulesByIdentifier = new Map;
1334
- }
1335
- get element() {
1336
- return this.application.element;
1337
- }
1338
- get schema() {
1339
- return this.application.schema;
1340
- }
1341
- get logger() {
1342
- return this.application.logger;
1343
- }
1344
- get controllerAttribute() {
1345
- return this.schema.controllerAttribute;
1346
- }
1347
- get modules() {
1348
- return Array.from(this.modulesByIdentifier.values());
1349
- }
1350
- get contexts() {
1351
- return this.modules.reduce(((contexts, module) => contexts.concat(module.contexts)), []);
1352
- }
1353
- start() {
1354
- this.scopeObserver.start();
1355
- }
1356
- stop() {
1357
- this.scopeObserver.stop();
1358
- }
1359
- loadDefinition(definition) {
1360
- this.unloadIdentifier(definition.identifier);
1361
- const module = new Module(this.application, definition);
1362
- this.connectModule(module);
1363
- }
1364
- unloadIdentifier(identifier) {
1365
- const module = this.modulesByIdentifier.get(identifier);
1366
- if (module) {
1367
- this.disconnectModule(module);
1368
- }
1369
- }
1370
- getContextForElementAndIdentifier(element, identifier) {
1371
- const module = this.modulesByIdentifier.get(identifier);
1372
- if (module) {
1373
- return module.contexts.find((context => context.element == element));
1374
- }
1375
- }
1376
- handleError(error, message, detail) {
1377
- this.application.handleError(error, message, detail);
1378
- }
1379
- createScopeForElementAndIdentifier(element, identifier) {
1380
- return new Scope(this.schema, element, identifier, this.logger);
1381
- }
1382
- scopeConnected(scope) {
1383
- this.scopesByIdentifier.add(scope.identifier, scope);
1384
- const module = this.modulesByIdentifier.get(scope.identifier);
1385
- if (module) {
1386
- module.connectContextForScope(scope);
1387
- }
1388
- }
1389
- scopeDisconnected(scope) {
1390
- this.scopesByIdentifier.delete(scope.identifier, scope);
1391
- const module = this.modulesByIdentifier.get(scope.identifier);
1392
- if (module) {
1393
- module.disconnectContextForScope(scope);
1394
- }
1395
- }
1396
- connectModule(module) {
1397
- this.modulesByIdentifier.set(module.identifier, module);
1398
- const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
1399
- scopes.forEach((scope => module.connectContextForScope(scope)));
1400
- }
1401
- disconnectModule(module) {
1402
- this.modulesByIdentifier.delete(module.identifier);
1403
- const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
1404
- scopes.forEach((scope => module.disconnectContextForScope(scope)));
1405
- }
1500
+ constructor(application) {
1501
+ this.application = application;
1502
+ this.scopeObserver = new ScopeObserver(this.element, this.schema, this);
1503
+ this.scopesByIdentifier = new Multimap;
1504
+ this.modulesByIdentifier = new Map;
1505
+ }
1506
+ get element() {
1507
+ return this.application.element;
1508
+ }
1509
+ get schema() {
1510
+ return this.application.schema;
1511
+ }
1512
+ get logger() {
1513
+ return this.application.logger;
1514
+ }
1515
+ get controllerAttribute() {
1516
+ return this.schema.controllerAttribute;
1517
+ }
1518
+ get modules() {
1519
+ return Array.from(this.modulesByIdentifier.values());
1520
+ }
1521
+ get contexts() {
1522
+ return this.modules.reduce((contexts, module) => contexts.concat(module.contexts), []);
1523
+ }
1524
+ start() {
1525
+ this.scopeObserver.start();
1526
+ }
1527
+ stop() {
1528
+ this.scopeObserver.stop();
1529
+ }
1530
+ loadDefinition(definition) {
1531
+ this.unloadIdentifier(definition.identifier);
1532
+ const module = new Module(this.application, definition);
1533
+ this.connectModule(module);
1534
+ }
1535
+ unloadIdentifier(identifier) {
1536
+ const module = this.modulesByIdentifier.get(identifier);
1537
+ if (module) {
1538
+ this.disconnectModule(module);
1539
+ }
1540
+ }
1541
+ getContextForElementAndIdentifier(element, identifier) {
1542
+ const module = this.modulesByIdentifier.get(identifier);
1543
+ if (module) {
1544
+ return module.contexts.find(context => context.element == element);
1545
+ }
1546
+ }
1547
+ handleError(error, message, detail) {
1548
+ this.application.handleError(error, message, detail);
1549
+ }
1550
+ createScopeForElementAndIdentifier(element, identifier) {
1551
+ return new Scope(this.schema, element, identifier, this.logger);
1552
+ }
1553
+ scopeConnected(scope) {
1554
+ this.scopesByIdentifier.add(scope.identifier, scope);
1555
+ const module = this.modulesByIdentifier.get(scope.identifier);
1556
+ if (module) {
1557
+ module.connectContextForScope(scope);
1558
+ }
1559
+ }
1560
+ scopeDisconnected(scope) {
1561
+ this.scopesByIdentifier.delete(scope.identifier, scope);
1562
+ const module = this.modulesByIdentifier.get(scope.identifier);
1563
+ if (module) {
1564
+ module.disconnectContextForScope(scope);
1565
+ }
1566
+ }
1567
+ connectModule(module) {
1568
+ this.modulesByIdentifier.set(module.identifier, module);
1569
+ const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
1570
+ scopes.forEach(scope => module.connectContextForScope(scope));
1571
+ }
1572
+ disconnectModule(module) {
1573
+ this.modulesByIdentifier.delete(module.identifier);
1574
+ const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier);
1575
+ scopes.forEach(scope => module.disconnectContextForScope(scope));
1576
+ }
1406
1577
  }
1407
1578
 
1408
1579
  const defaultSchema = {
1409
- controllerAttribute: "data-controller",
1410
- actionAttribute: "data-action",
1411
- targetAttribute: "data-target",
1412
- classAttribute: "data-class"
1580
+ controllerAttribute: "data-controller",
1581
+ actionAttribute: "data-action",
1582
+ targetAttribute: "data-target",
1583
+ targetAttributeForScope: identifier => `data-${identifier}-target`
1413
1584
  };
1414
1585
 
1415
1586
  class Application {
1416
- constructor(element = document.documentElement, schema = defaultSchema) {
1417
- this.logger = console;
1418
- this.element = element;
1419
- this.schema = schema;
1420
- this.dispatcher = new Dispatcher(this);
1421
- this.router = new Router(this);
1422
- }
1423
- static start(element, schema) {
1424
- const application = new Application(element, schema);
1425
- application.start();
1426
- return application;
1427
- }
1428
- async start() {
1429
- await domReady();
1430
- this.dispatcher.start();
1431
- this.router.start();
1432
- }
1433
- stop() {
1434
- this.dispatcher.stop();
1435
- this.router.stop();
1436
- }
1437
- register(identifier, controllerConstructor) {
1438
- this.load({
1439
- identifier: identifier,
1440
- controllerConstructor: controllerConstructor
1441
- });
1442
- }
1443
- load(head, ...rest) {
1444
- const definitions = Array.isArray(head) ? head : [ head, ...rest ];
1445
- definitions.forEach((definition => this.router.loadDefinition(definition)));
1446
- }
1447
- unload(head, ...rest) {
1448
- const identifiers = Array.isArray(head) ? head : [ head, ...rest ];
1449
- identifiers.forEach((identifier => this.router.unloadIdentifier(identifier)));
1450
- }
1451
- get controllers() {
1452
- return this.router.contexts.map((context => context.controller));
1453
- }
1454
- getControllerForElementAndIdentifier(element, identifier) {
1455
- const context = this.router.getContextForElementAndIdentifier(element, identifier);
1456
- return context ? context.controller : null;
1457
- }
1458
- handleError(error, message, detail) {
1459
- this.logger.error(`%s\n\n%o\n\n%o`, message, error, detail);
1460
- }
1587
+ constructor(element = document.documentElement, schema = defaultSchema) {
1588
+ this.logger = console;
1589
+ this.debug = false;
1590
+ this.logDebugActivity = (identifier, functionName, detail = {}) => {
1591
+ if (this.debug) {
1592
+ this.logFormattedMessage(identifier, functionName, detail);
1593
+ }
1594
+ };
1595
+ this.element = element;
1596
+ this.schema = schema;
1597
+ this.dispatcher = new Dispatcher(this);
1598
+ this.router = new Router(this);
1599
+ }
1600
+ static start(element, schema) {
1601
+ const application = new Application(element, schema);
1602
+ application.start();
1603
+ return application;
1604
+ }
1605
+ async start() {
1606
+ await domReady();
1607
+ this.logDebugActivity("application", "starting");
1608
+ this.dispatcher.start();
1609
+ this.router.start();
1610
+ this.logDebugActivity("application", "start");
1611
+ }
1612
+ stop() {
1613
+ this.logDebugActivity("application", "stopping");
1614
+ this.dispatcher.stop();
1615
+ this.router.stop();
1616
+ this.logDebugActivity("application", "stop");
1617
+ }
1618
+ register(identifier, controllerConstructor) {
1619
+ if (controllerConstructor.shouldLoad) {
1620
+ this.load({ identifier, controllerConstructor });
1621
+ }
1622
+ }
1623
+ load(head, ...rest) {
1624
+ const definitions = Array.isArray(head) ? head : [head, ...rest];
1625
+ definitions.forEach(definition => this.router.loadDefinition(definition));
1626
+ }
1627
+ unload(head, ...rest) {
1628
+ const identifiers = Array.isArray(head) ? head : [head, ...rest];
1629
+ identifiers.forEach(identifier => this.router.unloadIdentifier(identifier));
1630
+ }
1631
+ get controllers() {
1632
+ return this.router.contexts.map(context => context.controller);
1633
+ }
1634
+ getControllerForElementAndIdentifier(element, identifier) {
1635
+ const context = this.router.getContextForElementAndIdentifier(element, identifier);
1636
+ return context ? context.controller : null;
1637
+ }
1638
+ handleError(error, message, detail) {
1639
+ var _a;
1640
+ this.logger.error(`%s\n\n%o\n\n%o`, message, error, detail);
1641
+ (_a = window.onerror) === null || _a === void 0 ? void 0 : _a.call(window, message, "", 0, 0, error);
1642
+ }
1643
+ logFormattedMessage(identifier, functionName, detail = {}) {
1644
+ detail = Object.assign({ application: this }, detail);
1645
+ this.logger.groupCollapsed(`${identifier} #${functionName}`);
1646
+ this.logger.log("details:", Object.assign({}, detail));
1647
+ this.logger.groupEnd();
1648
+ }
1461
1649
  }
1462
-
1463
1650
  function domReady() {
1464
- return new Promise((resolve => {
1465
- if (document.readyState == "loading") {
1466
- document.addEventListener("DOMContentLoaded", resolve);
1467
- } else {
1468
- resolve();
1469
- }
1470
- }));
1651
+ return new Promise(resolve => {
1652
+ if (document.readyState == "loading") {
1653
+ document.addEventListener("DOMContentLoaded", () => resolve());
1654
+ }
1655
+ else {
1656
+ resolve();
1657
+ }
1658
+ });
1471
1659
  }
1472
1660
 
1473
1661
  function ClassPropertiesBlessing(constructor) {
1474
- const classes = readInheritableStaticArrayValues(constructor, "classes");
1475
- return classes.reduce(((properties, classDefinition) => Object.assign(properties, propertiesForClassDefinition(classDefinition))), {});
1662
+ const classes = readInheritableStaticArrayValues(constructor, "classes");
1663
+ return classes.reduce((properties, classDefinition) => {
1664
+ return Object.assign(properties, propertiesForClassDefinition(classDefinition));
1665
+ }, {});
1476
1666
  }
1477
-
1478
1667
  function propertiesForClassDefinition(key) {
1479
- const name = `${key}Class`;
1480
- return {
1481
- [name]: {
1482
- get() {
1483
- const {classes: classes} = this;
1484
- if (classes.has(key)) {
1485
- return classes.get(key);
1486
- } else {
1487
- const attribute = classes.getAttributeName(key);
1488
- throw new Error(`Missing attribute "${attribute}"`);
1489
- }
1490
- }
1491
- },
1492
- [`has${capitalize(name)}`]: {
1493
- get() {
1494
- return this.classes.has(key);
1495
- }
1496
- }
1497
- };
1668
+ return {
1669
+ [`${key}Class`]: {
1670
+ get() {
1671
+ const { classes } = this;
1672
+ if (classes.has(key)) {
1673
+ return classes.get(key);
1674
+ }
1675
+ else {
1676
+ const attribute = classes.getAttributeName(key);
1677
+ throw new Error(`Missing attribute "${attribute}"`);
1678
+ }
1679
+ }
1680
+ },
1681
+ [`${key}Classes`]: {
1682
+ get() {
1683
+ return this.classes.getAll(key);
1684
+ }
1685
+ },
1686
+ [`has${capitalize(key)}Class`]: {
1687
+ get() {
1688
+ return this.classes.has(key);
1689
+ }
1690
+ }
1691
+ };
1498
1692
  }
1499
1693
 
1500
1694
  function TargetPropertiesBlessing(constructor) {
1501
- const targets = readInheritableStaticArrayValues(constructor, "targets");
1502
- return targets.reduce(((properties, targetDefinition) => Object.assign(properties, propertiesForTargetDefinition(targetDefinition))), {});
1695
+ const targets = readInheritableStaticArrayValues(constructor, "targets");
1696
+ return targets.reduce((properties, targetDefinition) => {
1697
+ return Object.assign(properties, propertiesForTargetDefinition(targetDefinition));
1698
+ }, {});
1503
1699
  }
1504
-
1505
1700
  function propertiesForTargetDefinition(name) {
1506
- return {
1507
- [`${name}Target`]: {
1508
- get() {
1509
- const target = this.targets.find(name);
1510
- if (target) {
1511
- return target;
1512
- } else {
1513
- throw new Error(`Missing target element "${this.identifier}.${name}"`);
1514
- }
1515
- }
1516
- },
1517
- [`${name}Targets`]: {
1518
- get() {
1519
- return this.targets.findAll(name);
1520
- }
1521
- },
1522
- [`has${capitalize(name)}Target`]: {
1523
- get() {
1524
- return this.targets.has(name);
1525
- }
1526
- }
1527
- };
1701
+ return {
1702
+ [`${name}Target`]: {
1703
+ get() {
1704
+ const target = this.targets.find(name);
1705
+ if (target) {
1706
+ return target;
1707
+ }
1708
+ else {
1709
+ throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`);
1710
+ }
1711
+ }
1712
+ },
1713
+ [`${name}Targets`]: {
1714
+ get() {
1715
+ return this.targets.findAll(name);
1716
+ }
1717
+ },
1718
+ [`has${capitalize(name)}Target`]: {
1719
+ get() {
1720
+ return this.targets.has(name);
1721
+ }
1722
+ }
1723
+ };
1528
1724
  }
1529
1725
 
1530
1726
  function ValuePropertiesBlessing(constructor) {
1531
- const valueDefinitionPairs = readInheritableStaticObjectPairs(constructor, "values");
1532
- const propertyDescriptorMap = {
1533
- valueDescriptorMap: {
1534
- get() {
1535
- return valueDefinitionPairs.reduce(((result, valueDefinitionPair) => {
1536
- const valueDescriptor = parseValueDefinitionPair(valueDefinitionPair);
1537
- const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key);
1538
- return Object.assign(result, {
1539
- [attributeName]: valueDescriptor
1540
- });
1541
- }), {});
1542
- }
1543
- }
1544
- };
1545
- return valueDefinitionPairs.reduce(((properties, valueDefinitionPair) => Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair))), propertyDescriptorMap);
1727
+ const valueDefinitionPairs = readInheritableStaticObjectPairs(constructor, "values");
1728
+ const propertyDescriptorMap = {
1729
+ valueDescriptorMap: {
1730
+ get() {
1731
+ return valueDefinitionPairs.reduce((result, valueDefinitionPair) => {
1732
+ const valueDescriptor = parseValueDefinitionPair(valueDefinitionPair);
1733
+ const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key);
1734
+ return Object.assign(result, { [attributeName]: valueDescriptor });
1735
+ }, {});
1736
+ }
1737
+ }
1738
+ };
1739
+ return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => {
1740
+ return Object.assign(properties, propertiesForValueDefinitionPair(valueDefinitionPair));
1741
+ }, propertyDescriptorMap);
1546
1742
  }
1547
-
1548
1743
  function propertiesForValueDefinitionPair(valueDefinitionPair) {
1549
- const definition = parseValueDefinitionPair(valueDefinitionPair);
1550
- const {type: type, key: key, name: name} = definition;
1551
- const read = readers[type], write = writers[type] || writers.default;
1552
- return {
1553
- [name]: {
1554
- get() {
1555
- const value = this.data.get(key);
1556
- if (value !== null) {
1557
- return read(value);
1558
- } else {
1559
- return definition.defaultValue;
1560
- }
1561
- },
1562
- set(value) {
1563
- if (value === undefined) {
1564
- this.data.delete(key);
1565
- } else {
1566
- this.data.set(key, write(value));
1567
- }
1568
- }
1569
- },
1570
- [`has${capitalize(name)}`]: {
1571
- get() {
1572
- return this.data.has(key);
1573
- }
1574
- }
1575
- };
1576
- }
1577
-
1578
- function parseValueDefinitionPair([token, typeConstant]) {
1579
- const type = parseValueTypeConstant(typeConstant);
1580
- return valueDescriptorForTokenAndType(token, type);
1581
- }
1582
-
1583
- function parseValueTypeConstant(typeConstant) {
1584
- switch (typeConstant) {
1585
- case Array:
1586
- return "array";
1587
-
1588
- case Boolean:
1589
- return "boolean";
1590
-
1591
- case Number:
1592
- return "number";
1593
-
1594
- case Object:
1595
- return "object";
1596
-
1597
- case String:
1598
- return "string";
1599
- }
1600
- throw new Error(`Unknown value type constant "${typeConstant}"`);
1744
+ const definition = parseValueDefinitionPair(valueDefinitionPair);
1745
+ const { key, name, reader: read, writer: write } = definition;
1746
+ return {
1747
+ [name]: {
1748
+ get() {
1749
+ const value = this.data.get(key);
1750
+ if (value !== null) {
1751
+ return read(value);
1752
+ }
1753
+ else {
1754
+ return definition.defaultValue;
1755
+ }
1756
+ },
1757
+ set(value) {
1758
+ if (value === undefined) {
1759
+ this.data.delete(key);
1760
+ }
1761
+ else {
1762
+ this.data.set(key, write(value));
1763
+ }
1764
+ }
1765
+ },
1766
+ [`has${capitalize(name)}`]: {
1767
+ get() {
1768
+ return this.data.has(key) || definition.hasCustomDefaultValue;
1769
+ }
1770
+ }
1771
+ };
1601
1772
  }
1602
-
1603
- function valueDescriptorForTokenAndType(token, type) {
1604
- const key = `${dasherize(token)}-value`;
1605
- return {
1606
- type: type,
1607
- key: key,
1608
- name: camelize(key),
1609
- get defaultValue() {
1610
- return defaultValuesByType[type];
1611
- }
1612
- };
1773
+ function parseValueDefinitionPair([token, typeDefinition]) {
1774
+ return valueDescriptorForTokenAndTypeDefinition(token, typeDefinition);
1775
+ }
1776
+ function parseValueTypeConstant(constant) {
1777
+ switch (constant) {
1778
+ case Array: return "array";
1779
+ case Boolean: return "boolean";
1780
+ case Number: return "number";
1781
+ case Object: return "object";
1782
+ case String: return "string";
1783
+ }
1784
+ }
1785
+ function parseValueTypeDefault(defaultValue) {
1786
+ switch (typeof defaultValue) {
1787
+ case "boolean": return "boolean";
1788
+ case "number": return "number";
1789
+ case "string": return "string";
1790
+ }
1791
+ if (Array.isArray(defaultValue))
1792
+ return "array";
1793
+ if (Object.prototype.toString.call(defaultValue) === "[object Object]")
1794
+ return "object";
1795
+ }
1796
+ function parseValueTypeObject(typeObject) {
1797
+ const typeFromObject = parseValueTypeConstant(typeObject.type);
1798
+ if (typeFromObject) {
1799
+ const defaultValueType = parseValueTypeDefault(typeObject.default);
1800
+ if (typeFromObject !== defaultValueType) {
1801
+ throw new Error(`Type "${typeFromObject}" must match the type of the default value. Given default value: "${typeObject.default}" as "${defaultValueType}"`);
1802
+ }
1803
+ return typeFromObject;
1804
+ }
1805
+ }
1806
+ function parseValueTypeDefinition(typeDefinition) {
1807
+ const typeFromObject = parseValueTypeObject(typeDefinition);
1808
+ const typeFromDefaultValue = parseValueTypeDefault(typeDefinition);
1809
+ const typeFromConstant = parseValueTypeConstant(typeDefinition);
1810
+ const type = typeFromObject || typeFromDefaultValue || typeFromConstant;
1811
+ if (type)
1812
+ return type;
1813
+ throw new Error(`Unknown value type "${typeDefinition}"`);
1814
+ }
1815
+ function defaultValueForDefinition(typeDefinition) {
1816
+ const constant = parseValueTypeConstant(typeDefinition);
1817
+ if (constant)
1818
+ return defaultValuesByType[constant];
1819
+ const defaultValue = typeDefinition.default;
1820
+ if (defaultValue !== undefined)
1821
+ return defaultValue;
1822
+ return typeDefinition;
1823
+ }
1824
+ function valueDescriptorForTokenAndTypeDefinition(token, typeDefinition) {
1825
+ const key = `${dasherize(token)}-value`;
1826
+ const type = parseValueTypeDefinition(typeDefinition);
1827
+ return {
1828
+ type,
1829
+ key,
1830
+ name: camelize(key),
1831
+ get defaultValue() { return defaultValueForDefinition(typeDefinition); },
1832
+ get hasCustomDefaultValue() { return parseValueTypeDefault(typeDefinition) !== undefined; },
1833
+ reader: readers[type],
1834
+ writer: writers[type] || writers.default
1835
+ };
1613
1836
  }
1614
-
1615
1837
  const defaultValuesByType = {
1616
- get array() {
1617
- return [];
1618
- },
1619
- boolean: false,
1620
- number: 0,
1621
- get object() {
1622
- return {};
1623
- },
1624
- string: ""
1838
+ get array() { return []; },
1839
+ boolean: false,
1840
+ number: 0,
1841
+ get object() { return {}; },
1842
+ string: ""
1625
1843
  };
1626
-
1627
1844
  const readers = {
1628
- array(value) {
1629
- const array = JSON.parse(value);
1630
- if (!Array.isArray(array)) {
1631
- throw new TypeError("Expected array");
1632
- }
1633
- return array;
1634
- },
1635
- boolean(value) {
1636
- return !(value == "0" || value == "false");
1637
- },
1638
- number(value) {
1639
- return parseFloat(value);
1640
- },
1641
- object(value) {
1642
- const object = JSON.parse(value);
1643
- if (object === null || typeof object != "object" || Array.isArray(object)) {
1644
- throw new TypeError("Expected object");
1645
- }
1646
- return object;
1647
- },
1648
- string(value) {
1649
- return value;
1650
- }
1845
+ array(value) {
1846
+ const array = JSON.parse(value);
1847
+ if (!Array.isArray(array)) {
1848
+ throw new TypeError("Expected array");
1849
+ }
1850
+ return array;
1851
+ },
1852
+ boolean(value) {
1853
+ return !(value == "0" || value == "false");
1854
+ },
1855
+ number(value) {
1856
+ return Number(value);
1857
+ },
1858
+ object(value) {
1859
+ const object = JSON.parse(value);
1860
+ if (object === null || typeof object != "object" || Array.isArray(object)) {
1861
+ throw new TypeError("Expected object");
1862
+ }
1863
+ return object;
1864
+ },
1865
+ string(value) {
1866
+ return value;
1867
+ }
1651
1868
  };
1652
-
1653
1869
  const writers = {
1654
- default: writeString,
1655
- array: writeJSON,
1656
- object: writeJSON
1870
+ default: writeString,
1871
+ array: writeJSON,
1872
+ object: writeJSON
1657
1873
  };
1658
-
1659
1874
  function writeJSON(value) {
1660
- return JSON.stringify(value);
1875
+ return JSON.stringify(value);
1661
1876
  }
1662
-
1663
1877
  function writeString(value) {
1664
- return `${value}`;
1878
+ return `${value}`;
1665
1879
  }
1666
1880
 
1667
1881
  class Controller {
1668
- constructor(context) {
1669
- this.context = context;
1670
- }
1671
- get application() {
1672
- return this.context.application;
1673
- }
1674
- get scope() {
1675
- return this.context.scope;
1676
- }
1677
- get element() {
1678
- return this.scope.element;
1679
- }
1680
- get identifier() {
1681
- return this.scope.identifier;
1682
- }
1683
- get targets() {
1684
- return this.scope.targets;
1685
- }
1686
- get classes() {
1687
- return this.scope.classes;
1688
- }
1689
- get data() {
1690
- return this.scope.data;
1691
- }
1692
- initialize() {}
1693
- connect() {}
1694
- disconnect() {}
1882
+ constructor(context) {
1883
+ this.context = context;
1884
+ }
1885
+ static get shouldLoad() {
1886
+ return true;
1887
+ }
1888
+ get application() {
1889
+ return this.context.application;
1890
+ }
1891
+ get scope() {
1892
+ return this.context.scope;
1893
+ }
1894
+ get element() {
1895
+ return this.scope.element;
1896
+ }
1897
+ get identifier() {
1898
+ return this.scope.identifier;
1899
+ }
1900
+ get targets() {
1901
+ return this.scope.targets;
1902
+ }
1903
+ get classes() {
1904
+ return this.scope.classes;
1905
+ }
1906
+ get data() {
1907
+ return this.scope.data;
1908
+ }
1909
+ initialize() {
1910
+ }
1911
+ connect() {
1912
+ }
1913
+ disconnect() {
1914
+ }
1915
+ dispatch(eventName, { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true } = {}) {
1916
+ const type = prefix ? `${prefix}:${eventName}` : eventName;
1917
+ const event = new CustomEvent(type, { detail, bubbles, cancelable });
1918
+ target.dispatchEvent(event);
1919
+ return event;
1920
+ }
1695
1921
  }
1696
-
1697
- Controller.blessings = [ ClassPropertiesBlessing, TargetPropertiesBlessing, ValuePropertiesBlessing ];
1698
-
1922
+ Controller.blessings = [ClassPropertiesBlessing, TargetPropertiesBlessing, ValuePropertiesBlessing];
1699
1923
  Controller.targets = [];
1700
-
1701
1924
  Controller.values = {};
1702
1925
 
1703
- export { Application, Context, Controller, defaultSchema };
1926
+ export { Application, AttributeObserver, Context, Controller, ElementObserver, IndexedMultimap, Multimap, StringMapObserver, TokenListObserver, ValueListObserver, add, defaultSchema, del, fetch, prune };