@lumjs/core 1.38.1 → 1.38.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/arrays/index.js +1 -1
- package/lib/arrays/typed.js +139 -0
- package/lib/events/index.js +103 -70
- package/lib/events/observable.js +2 -237
- package/lib/index.js +13 -12
- package/lib/meta.js +2 -2
- package/lib/obj/assignd.js +52 -5
- package/lib/obj/df.js +221 -74
- package/lib/obj/getprotos.js +25 -0
- package/lib/obj/index.js +26 -6
- package/lib/obj/ns.js +96 -20
- package/lib/objectid.js +43 -13
- package/lib/observable.js +1 -0
- package/lib/types/basics.js +29 -3
- package/lib/types/def.js +4 -1
- package/lib/types/index.js +10 -2
- package/lib/types/isa.js +4 -4
- package/lib/types/lazy.js +12 -219
- package/package.json +3 -2
- package/lib/events/event.js +0 -89
- package/lib/events/listener.js +0 -139
- package/lib/events/registry.js +0 -875
package/lib/events/registry.js
DELETED
|
@@ -1,875 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const {S,F,SY,isObj,def,isIterable} = require('../types');
|
|
4
|
-
const Listener = require('./listener');
|
|
5
|
-
const RegSym = Symbol('@lumjs/core/events:registry');
|
|
6
|
-
const cp = Object.assign;
|
|
7
|
-
|
|
8
|
-
const DEF_EXTENDS =
|
|
9
|
-
{
|
|
10
|
-
registry: 'events',
|
|
11
|
-
listen: 'on',
|
|
12
|
-
emit: 'emit',
|
|
13
|
-
remove: null,
|
|
14
|
-
once: null,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const INT_EXTENDS =
|
|
18
|
-
{
|
|
19
|
-
listeners: true,
|
|
20
|
-
results: true,
|
|
21
|
-
onDemand: false,
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const DEF_OPTIONS =
|
|
25
|
-
{
|
|
26
|
-
delimiter: /\s+/,
|
|
27
|
-
multiMatch: false,
|
|
28
|
-
wildcard: '*',
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const RES_PROPS =
|
|
32
|
-
[
|
|
33
|
-
'eventNames', 'eventTypes', 'targets', 'multiMatch', 'onceRemoved',
|
|
34
|
-
'stopEmitting', 'emitted', 'targetListeners', 'registry',
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Has a target object been registered with an event registry?
|
|
39
|
-
* @param {object} target
|
|
40
|
-
* @returns {boolean}
|
|
41
|
-
* @alias module:@lumjs/core/events.Registry.isRegistered
|
|
42
|
-
*/
|
|
43
|
-
const isRegistered = target => isObj(target[RegSym]);
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get event registry metadata from a target
|
|
47
|
-
* @private
|
|
48
|
-
* @param {object} target - Object to get metadata for
|
|
49
|
-
* @param {boolean} [create=false] Create metadata if it's not found?
|
|
50
|
-
* @returns {(object|undefined)} metadata (TODO: schema docs)
|
|
51
|
-
* @alias module:@lumjs/core/events.Registry.getMetadata
|
|
52
|
-
*/
|
|
53
|
-
function getMetadata(target, create=false)
|
|
54
|
-
{
|
|
55
|
-
if (isRegistered(target))
|
|
56
|
-
{ // Existing registry metadata found
|
|
57
|
-
return target[RegSym];
|
|
58
|
-
}
|
|
59
|
-
else if (create)
|
|
60
|
-
{ // Create new metadata
|
|
61
|
-
const exts = Object.keys(DEF_EXTENDS);
|
|
62
|
-
const tpm =
|
|
63
|
-
{
|
|
64
|
-
r: new Map(),
|
|
65
|
-
p: {},
|
|
66
|
-
x: {},
|
|
67
|
-
}
|
|
68
|
-
for (const ext of exts)
|
|
69
|
-
{
|
|
70
|
-
tpm.x[ext] = [];
|
|
71
|
-
}
|
|
72
|
-
def(target, RegSym, tpm);
|
|
73
|
-
return tpm;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function targetsAre(targets)
|
|
78
|
-
{
|
|
79
|
-
const isaSet = (targets instanceof Set);
|
|
80
|
-
const isaArr = (!isaSet && Array.isArray(targets));
|
|
81
|
-
const tis =
|
|
82
|
-
{
|
|
83
|
-
set: isaSet,
|
|
84
|
-
array: isaArr,
|
|
85
|
-
handled: (isaSet || isaArr),
|
|
86
|
-
}
|
|
87
|
-
return tis;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* A class that handles events for target objects
|
|
92
|
-
*
|
|
93
|
-
* @prop {module:@lumjs/core/events~GetTargets} getTargets
|
|
94
|
-
* A constructor-assigned callback method that returns a set of targets.
|
|
95
|
-
* @prop {object} options - Registry-level options
|
|
96
|
-
* @prop {Set.<module:@lumjs/core/events.Listener>} allListeners
|
|
97
|
-
* All registered event listeners
|
|
98
|
-
* @prop {Map.<string,Set.<module:@lumjs/core/events.Listener>>} listenersFor
|
|
99
|
-
* Each key is a single event name, and the value is a Set of
|
|
100
|
-
* listener objects that handle that event.
|
|
101
|
-
*
|
|
102
|
-
* @alias module:@lumjs/core/events.Registry
|
|
103
|
-
*/
|
|
104
|
-
class LumEventRegistry
|
|
105
|
-
{
|
|
106
|
-
/**
|
|
107
|
-
* Create a new registry instance for one or more target objects
|
|
108
|
-
*
|
|
109
|
-
* @param {(object|module:@lumjs/core/events~GetTargets)} targets
|
|
110
|
-
*
|
|
111
|
-
* If this is an `object`, then any kind of `Iterable` may be used
|
|
112
|
-
* to represent multiple targets, while any non-Iterable object will
|
|
113
|
-
* be considered as single target.
|
|
114
|
-
*
|
|
115
|
-
* If this is a `function`, it will be called to dynamically get a
|
|
116
|
-
* list of target objects whenever an event is triggered.
|
|
117
|
-
*
|
|
118
|
-
* If you want to use a `function` as an actual target, you'll need to
|
|
119
|
-
* wrap it in an array or other iterable object.
|
|
120
|
-
*
|
|
121
|
-
* @param {object} [opts] Options
|
|
122
|
-
*
|
|
123
|
-
* A _compiled_ version is saved to the `options` property.
|
|
124
|
-
* The compiled version includes a bunch of defaults, and various
|
|
125
|
-
* compose rules (mostly for the `.extend` nested options).
|
|
126
|
-
*
|
|
127
|
-
* @param {(RegExp|string)} [opts.delimiter=/\s+/] Used to split event names
|
|
128
|
-
*
|
|
129
|
-
* @param {object} [opts.extend] Options for wrapper methods/properties
|
|
130
|
-
*
|
|
131
|
-
* The `boolean` options determine if extension methods will be added to
|
|
132
|
-
* certain types of objects (and in some cases, when to do so).
|
|
133
|
-
*
|
|
134
|
-
* The `?string` options are each the names of properties/methods in the
|
|
135
|
-
* Registry class. If they are set to a `string` then that will be the
|
|
136
|
-
* name used for the wrapper property/method added to objects. If it
|
|
137
|
-
* is explicitly set to `null` it means skip adding a wrapper for that
|
|
138
|
-
* method/property. If it is omitted entirely the default will be used.
|
|
139
|
-
*
|
|
140
|
-
* @param {boolean} [opts.extend.targets] Extend target objects?
|
|
141
|
-
*
|
|
142
|
-
* The default will be `true` when `targets` is an `object`, or `false`
|
|
143
|
-
* when `targets` is a `function`.
|
|
144
|
-
*
|
|
145
|
-
* @param {boolean} [opts.extend.listeners=true] Extend Listener instances?
|
|
146
|
-
* As returned by `makeListener()`, `listen()`, and `once()`
|
|
147
|
-
* @param {boolean} [opts.extend.results=true] Extend `emit()` results?
|
|
148
|
-
* @param {boolean} [opts.extend.onDemand=false] On-demand target setup
|
|
149
|
-
*
|
|
150
|
-
* If `targets` was a `function` and this is set to `true`, then
|
|
151
|
-
* we'll perform the target setup on every emit() call. The setup process
|
|
152
|
-
* is skipped on any targets that have already been set up, so this
|
|
153
|
-
* is meant for dynamic targets that may change on every call.
|
|
154
|
-
*
|
|
155
|
-
* @param {?string} [opts.extend.registry="events"] Registry property;
|
|
156
|
-
* only added to `targets`, never to listeners or results which have
|
|
157
|
-
* their own inherent `registry` property already.
|
|
158
|
-
*
|
|
159
|
-
* @param {?string} [opts.extend.emit="emit"] `emit()` proxy method
|
|
160
|
-
* @param {?string} [opts.extend.listen="on"] `listen()` proxy method
|
|
161
|
-
* @param {?string} [opts.extend.once=null] `once()` proxy method
|
|
162
|
-
* @param {?string} [opts.extend.remove=null] `remove()` proxy method
|
|
163
|
-
*
|
|
164
|
-
* The `remove` wrapper method added to Listener instances is slightly
|
|
165
|
-
* different than the one added to other objects, as if you call it
|
|
166
|
-
* with no arguments, it will pass the Listener itself as the argument.
|
|
167
|
-
*
|
|
168
|
-
* @param {boolean} [opts.multiMatch=false]
|
|
169
|
-
* If a registered listener has multiple event names, and a call
|
|
170
|
-
* to `emit()` also has multiple event names, the value of this
|
|
171
|
-
* option will determine if the same listener will have its
|
|
172
|
-
* handler function called more than once.
|
|
173
|
-
*
|
|
174
|
-
* If this is `true`, the handler will be called once for every
|
|
175
|
-
* combination of target and event name.
|
|
176
|
-
*
|
|
177
|
-
* If this is `false` (default), then only the first matching event
|
|
178
|
-
* name will be called for each target.
|
|
179
|
-
*
|
|
180
|
-
* @param {boolean} [opts.overwrite=false] Overwrite existing properties?
|
|
181
|
-
*
|
|
182
|
-
* If `true` then when adding wrapper methods, the properties from
|
|
183
|
-
* `opts.extend` will replace any existing ones in each target.
|
|
184
|
-
*
|
|
185
|
-
* @param {module:@lumjs/core/events~SetupEvent} [opts.setupEvent]
|
|
186
|
-
*
|
|
187
|
-
* If this is specified (either here or in individual listeners),
|
|
188
|
-
* it will be called and passed the Event object at the very end of
|
|
189
|
-
* its constructor.
|
|
190
|
-
*
|
|
191
|
-
* @param {module:@lumjs/core/events~SetupListener} [opts.setupListener]
|
|
192
|
-
*
|
|
193
|
-
* If this is specified, it will be called and passed the Listener
|
|
194
|
-
* object at the very end of its constructor.
|
|
195
|
-
*
|
|
196
|
-
* @param {string} [opts.wildcard='*'] Wildcard event name.
|
|
197
|
-
*
|
|
198
|
-
* - If you use this in `listen()` the handler will be used regardless
|
|
199
|
-
* as to what event name was triggered. You can always see which
|
|
200
|
-
* event name was actually triggered by using `event.name`.
|
|
201
|
-
* - If you use this in `remove()` it calls `removeAll()` to remove all
|
|
202
|
-
* registered listeners.
|
|
203
|
-
*/
|
|
204
|
-
constructor(targets, opts={})
|
|
205
|
-
{
|
|
206
|
-
let defExt; // Default opts.extend.targets value
|
|
207
|
-
if (typeof targets === F)
|
|
208
|
-
{ // A dynamic getter method
|
|
209
|
-
this.funTargets = true;
|
|
210
|
-
this.getTargets = targets;
|
|
211
|
-
targets = this.getTargets();
|
|
212
|
-
defExt = false;
|
|
213
|
-
}
|
|
214
|
-
else
|
|
215
|
-
{ // Simple getter for a static value
|
|
216
|
-
if (!(targets instanceof Set))
|
|
217
|
-
{
|
|
218
|
-
if (!isIterable(targets))
|
|
219
|
-
targets = [targets];
|
|
220
|
-
targets = new Set(targets);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
this.funTargets = false;
|
|
224
|
-
this.getTargets = () => targets;
|
|
225
|
-
defExt = true;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Build composite extend rules
|
|
229
|
-
const extend = cp(
|
|
230
|
-
{targets: defExt},
|
|
231
|
-
INT_EXTENDS,
|
|
232
|
-
DEF_EXTENDS,
|
|
233
|
-
opts.extend);
|
|
234
|
-
|
|
235
|
-
// Now compile the final options
|
|
236
|
-
this.options = cp({}, DEF_OPTIONS, opts, {extend});
|
|
237
|
-
|
|
238
|
-
this.allListeners = new Set();
|
|
239
|
-
this.listenersFor = new Map();
|
|
240
|
-
|
|
241
|
-
this.setupTargets(targets);
|
|
242
|
-
} // constructor()
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Set up target objects
|
|
246
|
-
*
|
|
247
|
-
* Always sets the necessary metadata on each target.
|
|
248
|
-
* May also extend the targets with wrapper properties and methods
|
|
249
|
-
* depending on the `options.extend` values set.
|
|
250
|
-
*
|
|
251
|
-
* Not meant to be called from outside code.
|
|
252
|
-
* @private
|
|
253
|
-
* @param {Iterable} targets - Targets to extend
|
|
254
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
255
|
-
*/
|
|
256
|
-
setupTargets(targets)
|
|
257
|
-
{
|
|
258
|
-
const opts = this.options;
|
|
259
|
-
const extOpts = opts.extend;
|
|
260
|
-
const intNames = extOpts.targets ? Object.keys(DEF_EXTENDS) : null;
|
|
261
|
-
|
|
262
|
-
for (const target of targets)
|
|
263
|
-
{
|
|
264
|
-
const tps = {}, tpm = getMetadata(target, true);
|
|
265
|
-
if (tpm.r.has(this)) continue; // Already set up with this registry.
|
|
266
|
-
tpm.r.set(this, tps);
|
|
267
|
-
|
|
268
|
-
if (extOpts.targets)
|
|
269
|
-
{
|
|
270
|
-
for (const iname of intNames)
|
|
271
|
-
{
|
|
272
|
-
if ((typeof extOpts[iname] === S && extOpts[iname].trim() !== '')
|
|
273
|
-
|| typeof extOpts[iname] === SY)
|
|
274
|
-
{
|
|
275
|
-
const ename = extOpts[iname];
|
|
276
|
-
const value = iname === 'registry'
|
|
277
|
-
? this // The registry instance itself
|
|
278
|
-
: (...args) => this[iname](...args) // A proxy method
|
|
279
|
-
if (opts.overwrite || target[ename] === undefined)
|
|
280
|
-
{
|
|
281
|
-
def(target, ename, {value});
|
|
282
|
-
tps[ename] = iname;
|
|
283
|
-
tpm.p[ename] = this;
|
|
284
|
-
tpm.x[iname].push([this, ename]);
|
|
285
|
-
}
|
|
286
|
-
else
|
|
287
|
-
{
|
|
288
|
-
console.error("Won't overwrite existing property",
|
|
289
|
-
{target,iname,ename,registry: this});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return this;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Add extension methods to internal objects;
|
|
301
|
-
* currently supports Listener instances and result/status objects.
|
|
302
|
-
* Not meant to be called from outside code.
|
|
303
|
-
* @private
|
|
304
|
-
* @param {object} obj - Internal object to extend
|
|
305
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
306
|
-
*/
|
|
307
|
-
extendInternal(obj)
|
|
308
|
-
{
|
|
309
|
-
const opts = this.options;
|
|
310
|
-
const extOpts = opts.extend;
|
|
311
|
-
const intNames = Object.keys(DEF_EXTENDS);
|
|
312
|
-
const isLs = (obj instanceof Listener);
|
|
313
|
-
const reserved = isLs ? Listener.classProps : RES_PROPS;
|
|
314
|
-
|
|
315
|
-
for (const iname of intNames)
|
|
316
|
-
{
|
|
317
|
-
if (iname === 'registry') continue; // skip the registry property
|
|
318
|
-
if ((typeof extOpts[iname] === S && extOpts[iname].trim() !== '')
|
|
319
|
-
|| typeof extOpts[iname] === SY)
|
|
320
|
-
{
|
|
321
|
-
const ename = extOpts[iname];
|
|
322
|
-
if (reserved.includes(ename))
|
|
323
|
-
{ // Skip reserved names
|
|
324
|
-
console.warn("reserved property", {ename, obj, registry: this});
|
|
325
|
-
continue;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const value = (isLs && iname === 'remove')
|
|
329
|
-
? (what=obj) => this.remove(what) // special remove wrapper
|
|
330
|
-
: (...args) => this[iname](...args) // regular wrapper method
|
|
331
|
-
if (opts.overwrite || obj[ename] === undefined)
|
|
332
|
-
{
|
|
333
|
-
def(obj, ename, {value});
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return this;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Build a new Listener instance; used by `listen()` method.
|
|
343
|
-
*
|
|
344
|
-
* @param {(string|symbol|object)} eventTypes
|
|
345
|
-
* What this does depends on the type, and the number of arguments passed.
|
|
346
|
-
*
|
|
347
|
-
* If this is an `object` **AND** is *the only argument* passed,
|
|
348
|
-
* it will be used as the `spec`, and the `spec.eventTypes`
|
|
349
|
-
* and `spec.listener` properties will become mandatory.
|
|
350
|
-
*
|
|
351
|
-
* If it's NOT an object *OR* there is more than one argument, this
|
|
352
|
-
* will be used as the `spec.eventTypes` property.
|
|
353
|
-
*
|
|
354
|
-
* @param {module:@lumjs/core/events~Handler} [handler]
|
|
355
|
-
* Used as the `spec.handler` property if specified.
|
|
356
|
-
*
|
|
357
|
-
* This is mandatory if `eventTypes` argument is a string or Symbol!
|
|
358
|
-
*
|
|
359
|
-
* @param {object} [spec] The listener specification rules
|
|
360
|
-
*
|
|
361
|
-
* @param {(string|symbol|Iterable)} [spec.eventTypes] Event type(s)
|
|
362
|
-
*
|
|
363
|
-
* See {@link module:@lumjs/core/events.Registry#getEventNames} for details.
|
|
364
|
-
*
|
|
365
|
-
* @param {(string|symbol|Iterable)} [spec.eventNames] Alias of `eventTypes`
|
|
366
|
-
*
|
|
367
|
-
* @param {module:@lumjs/core/events~Handler} [spec.handler] Event handler
|
|
368
|
-
*
|
|
369
|
-
* @param {(function|object)} [spec.listener] An alias for `handler`
|
|
370
|
-
*
|
|
371
|
-
* @param {object} [spec.options] Options for the listener
|
|
372
|
-
*
|
|
373
|
-
* The option properties can be included directly in the `spec` itself
|
|
374
|
-
* for brevity, but a nested `options` object is supported to be more
|
|
375
|
-
* like the `DOM.addEventListener()` method. Either way works fine.
|
|
376
|
-
*
|
|
377
|
-
* If `spec.options` is used, the properties in it take precedence over
|
|
378
|
-
* those directly in the `spec` object. Note that you cannot use the
|
|
379
|
-
* names `listener`, `handler`, `eventTypes`, or `eventNames` as option
|
|
380
|
-
* properties, and if found, they will be removed.
|
|
381
|
-
*
|
|
382
|
-
* You may also override the `setupEvent` and `setupListener` registry
|
|
383
|
-
* options here if needed.
|
|
384
|
-
*
|
|
385
|
-
* @param {boolean} [spec.options.once=false] Only use the listener once?
|
|
386
|
-
*
|
|
387
|
-
* If this is set to `true`, then the first time this listener is used in
|
|
388
|
-
* an {@link module:@lumjs/core/events.Registry#emit emit()} call, it will
|
|
389
|
-
* be removed from the registry at the end of the emit process (after all
|
|
390
|
-
* events for all targets have been triggered).
|
|
391
|
-
*
|
|
392
|
-
* @returns {module:@lumjs/core/events.Listener} A new `Listener` instance
|
|
393
|
-
*/
|
|
394
|
-
makeListener(...args)
|
|
395
|
-
{
|
|
396
|
-
let spec;
|
|
397
|
-
|
|
398
|
-
if (args.length === 0 || args.length > 3)
|
|
399
|
-
{
|
|
400
|
-
console.error({args, registry: this});
|
|
401
|
-
throw new RangeError("Invalid number of arguments");
|
|
402
|
-
}
|
|
403
|
-
else if (args.length === 1 && isObj(args[0]))
|
|
404
|
-
{ // listen(spec)
|
|
405
|
-
spec = cp({}, args[0]);
|
|
406
|
-
}
|
|
407
|
-
else
|
|
408
|
-
{ // listen(eventTypes, listener, [spec])
|
|
409
|
-
spec = cp({}, args[2]);
|
|
410
|
-
spec.eventTypes = args[0];
|
|
411
|
-
spec.handler = args[1];
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const lsnr = new Listener(this, spec);
|
|
415
|
-
if (this.options.extend.listeners)
|
|
416
|
-
{
|
|
417
|
-
this.extendInternal(lsnr);
|
|
418
|
-
}
|
|
419
|
-
return lsnr;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Assign a new event listener.
|
|
424
|
-
*
|
|
425
|
-
* Calls `this.makeListener()` passing all arguments to it.
|
|
426
|
-
* Then calls `this.add(listener)` passing the newly make `Listener`.
|
|
427
|
-
*
|
|
428
|
-
* @param {...mixed} args
|
|
429
|
-
* @returns {module:@lumjs/core/events.Listener}
|
|
430
|
-
*/
|
|
431
|
-
listen()
|
|
432
|
-
{
|
|
433
|
-
const listener = this.makeListener(...arguments)
|
|
434
|
-
this.add(listener);
|
|
435
|
-
return listener;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Assign a new event listener that will only run once.
|
|
440
|
-
*
|
|
441
|
-
* Calls `this.listen()` passing all arguments to it,
|
|
442
|
-
* then sets the `listener.options.once` to `true`.
|
|
443
|
-
*
|
|
444
|
-
* @param {...mixed} args
|
|
445
|
-
* @returns {module:@lumjs/core/events.Listener}
|
|
446
|
-
*/
|
|
447
|
-
once()
|
|
448
|
-
{
|
|
449
|
-
const listener = this.listen(...arguments);
|
|
450
|
-
listener.options.once = true;
|
|
451
|
-
return listener;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Add a Listener instance.
|
|
456
|
-
*
|
|
457
|
-
* You'd generally use `listen()` or `once()` rather than this, but if
|
|
458
|
-
* you need to (re-)add an existing instance, this is the way to do it.
|
|
459
|
-
*
|
|
460
|
-
* @param {module:@lumjs/core/events.Listener} listener - Listener instance
|
|
461
|
-
*
|
|
462
|
-
* If the same instance is passed more than once it will have no affect,
|
|
463
|
-
* as we store the instances in a `Set` internally, so it'll only ever
|
|
464
|
-
* be stored once.
|
|
465
|
-
*
|
|
466
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
467
|
-
*/
|
|
468
|
-
add(listener)
|
|
469
|
-
{
|
|
470
|
-
if (!(listener instanceof Listener))
|
|
471
|
-
{
|
|
472
|
-
console.error({listener, registry: this});
|
|
473
|
-
throw new TypeError("Invalid listener instance");
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
this.allListeners.add(listener);
|
|
477
|
-
|
|
478
|
-
for (const ename of listener.eventTypes)
|
|
479
|
-
{
|
|
480
|
-
let lset;
|
|
481
|
-
if (this.listenersFor.has(ename))
|
|
482
|
-
{
|
|
483
|
-
lset = this.listenersFor.get(ename);
|
|
484
|
-
}
|
|
485
|
-
else
|
|
486
|
-
{
|
|
487
|
-
lset = new Set();
|
|
488
|
-
this.listenersFor.set(ename, lset);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
lset.add(listener);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return this;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Remove **ALL** registered event listeners!
|
|
499
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
500
|
-
*/
|
|
501
|
-
removeAll()
|
|
502
|
-
{
|
|
503
|
-
this.allListeners.clear();
|
|
504
|
-
this.listenersFor.clear();
|
|
505
|
-
return this;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Remove specific event names.
|
|
510
|
-
*
|
|
511
|
-
* It will remove any of the the specified event names
|
|
512
|
-
* from applicable listener instances, and clear the
|
|
513
|
-
* associated `listenersFor` set.
|
|
514
|
-
*
|
|
515
|
-
* If a listener has no more event names left, that listener
|
|
516
|
-
* will be removed from the `allListeners` set as well.
|
|
517
|
-
*
|
|
518
|
-
* @param {...string} names - Event names to remove
|
|
519
|
-
*
|
|
520
|
-
* If the `wildcard` string is specified here, this will simply
|
|
521
|
-
* remove any wildcard listeners currently registered.
|
|
522
|
-
* See `remove(wildcard)` or `removeAll()` if you really want
|
|
523
|
-
* to remove **ALL** listeners.
|
|
524
|
-
*
|
|
525
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
526
|
-
*/
|
|
527
|
-
removeEvents(...names)
|
|
528
|
-
{
|
|
529
|
-
for (const name of names)
|
|
530
|
-
{
|
|
531
|
-
if (this.listenersFor.has(name))
|
|
532
|
-
{
|
|
533
|
-
const eventListeners = this.listenersFor.get(name);
|
|
534
|
-
for (const lsnr of eventListeners)
|
|
535
|
-
{
|
|
536
|
-
lsnr.eventTypes.delete(name);
|
|
537
|
-
if (!lsnr.hasEvents)
|
|
538
|
-
{ // The last event name was removed.
|
|
539
|
-
this.removeListeners(lsnr);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
eventListeners.clear();
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
return this;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Remove specific Listener instances
|
|
550
|
-
* @param {...module:@lumjs/core/events.Listener} listeners
|
|
551
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
552
|
-
*/
|
|
553
|
-
removeListeners(...listeners)
|
|
554
|
-
{
|
|
555
|
-
for (const listener of listeners)
|
|
556
|
-
{
|
|
557
|
-
if (this.allListeners.has(listener))
|
|
558
|
-
{ // First remove it from allListeners
|
|
559
|
-
this.allListeners.delete(listener);
|
|
560
|
-
|
|
561
|
-
for (const ename of listener.eventTypes)
|
|
562
|
-
{
|
|
563
|
-
if (this.listenersFor.has(ename))
|
|
564
|
-
{
|
|
565
|
-
const lset = this.listenersFor.get(ename);
|
|
566
|
-
lset.delete(listener);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
return this;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
/**
|
|
575
|
-
* Remove listeners based on the value type used.
|
|
576
|
-
*
|
|
577
|
-
* @param {(string|module:@lumjs/core/events.Listener)} what
|
|
578
|
-
*
|
|
579
|
-
* - If this is the `wildcard` string, then this will call `removeAll()`.
|
|
580
|
-
* - If this is any other `string` it will be split using `splitNames()`,
|
|
581
|
-
* and the resulting strings passed as arguments to `removeEvents()`.
|
|
582
|
-
* - If this is a `Symbol` it will be passed to `removeEvents()`.
|
|
583
|
-
* - If this is a `Listener` instance, its passed to `removeListeners()`.
|
|
584
|
-
*
|
|
585
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
586
|
-
* @throws {TypeError} If `what` is none of the above values.
|
|
587
|
-
*/
|
|
588
|
-
remove(what)
|
|
589
|
-
{
|
|
590
|
-
if (what === this.options.wildcard)
|
|
591
|
-
{
|
|
592
|
-
return this.removeAll();
|
|
593
|
-
}
|
|
594
|
-
else if (typeof what === S)
|
|
595
|
-
{
|
|
596
|
-
const events = this.splitNames(what);
|
|
597
|
-
return this.removeEvents(...events);
|
|
598
|
-
}
|
|
599
|
-
else if (typeof what === SY)
|
|
600
|
-
{
|
|
601
|
-
return this.removeEvents(what);
|
|
602
|
-
}
|
|
603
|
-
else if (what instanceof Listener)
|
|
604
|
-
{
|
|
605
|
-
return this.removeListeners(what);
|
|
606
|
-
}
|
|
607
|
-
else
|
|
608
|
-
{
|
|
609
|
-
console.error({what, registry: this});
|
|
610
|
-
throw new TypeError("Invalid event name or listener instance");
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Emit (trigger) one or more events.
|
|
616
|
-
*
|
|
617
|
-
* @param {(string|symbol|Array)} eventTypes - Events to emit;
|
|
618
|
-
* see {@link module:@lumjs/core/events#getEventTypes} for details.
|
|
619
|
-
*
|
|
620
|
-
* @param {object} [data] A data object (highly recommended);
|
|
621
|
-
* will be assigned to `event.data` if specified.
|
|
622
|
-
*
|
|
623
|
-
* @param {...any} [args] Any other arguments;
|
|
624
|
-
* will be assigned to `event.args`.
|
|
625
|
-
*
|
|
626
|
-
* Note: if a `data` object argument was passed, it will always
|
|
627
|
-
* be the first item in `event.args`.
|
|
628
|
-
*
|
|
629
|
-
* @returns {module:@lumjs/core/events~Status}
|
|
630
|
-
*/
|
|
631
|
-
emit(eventTypes, ...args)
|
|
632
|
-
{
|
|
633
|
-
const extOpts = this.options.extend;
|
|
634
|
-
const sti =
|
|
635
|
-
{
|
|
636
|
-
eventTypes: this.getEventTypes(eventTypes),
|
|
637
|
-
multiMatch: this.options.multiMatch,
|
|
638
|
-
onceRemoved: new Set(),
|
|
639
|
-
stopEmitting: false,
|
|
640
|
-
emitted: [],
|
|
641
|
-
registry: this,
|
|
642
|
-
}
|
|
643
|
-
sti.eventNames = sti.eventTypes;
|
|
644
|
-
|
|
645
|
-
if (extOpts.results)
|
|
646
|
-
{
|
|
647
|
-
this.extendInternal(sti);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
{ // Get the targets.
|
|
651
|
-
const tgs = this.getTargets(sti);
|
|
652
|
-
sti.targets = (tgs instanceof Set) ? tgs : new Set(tgs);
|
|
653
|
-
if (this.funTargets && extOpts.onDemand)
|
|
654
|
-
{
|
|
655
|
-
this.setupTargets(sti.targets);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
const wilds = this.listenersFor.get(this.options.wildcard);
|
|
660
|
-
|
|
661
|
-
emitting: for (const tg of sti.targets)
|
|
662
|
-
{
|
|
663
|
-
const called = sti.targetListeners = new Set();
|
|
664
|
-
for (const ename of sti.eventTypes)
|
|
665
|
-
{
|
|
666
|
-
if (!this.listenersFor.has(ename)) continue;
|
|
667
|
-
|
|
668
|
-
let listeners = this.listenersFor.get(ename);
|
|
669
|
-
if (wilds) listeners = listeners.union(wilds);
|
|
670
|
-
|
|
671
|
-
for (const lsnr of listeners)
|
|
672
|
-
{
|
|
673
|
-
if (sti.multiMatch || !called.has(lsnr))
|
|
674
|
-
{ // Let's emit an event!
|
|
675
|
-
called.add(lsnr);
|
|
676
|
-
const event = lsnr.emitEvent(ename, tg, args, sti);
|
|
677
|
-
sti.emitted.push(event);
|
|
678
|
-
if (sti.stopEmitting)
|
|
679
|
-
{
|
|
680
|
-
break emitting;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// Nix the targetListeners property.
|
|
688
|
-
delete sti.targetListeners;
|
|
689
|
-
|
|
690
|
-
// Handle any `onceRemoved` listeners.
|
|
691
|
-
for (const lsnr of sti.onceRemoved)
|
|
692
|
-
{
|
|
693
|
-
this.removeListeners(lsnr);
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
// Return the final status.
|
|
697
|
-
return sti;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* Register additional target objects
|
|
702
|
-
* @param {...object} addTargets - Target objects to register
|
|
703
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
704
|
-
*/
|
|
705
|
-
register(...addTargets)
|
|
706
|
-
{
|
|
707
|
-
const allTargets = this.getTargets();
|
|
708
|
-
const tis = targetsAre(allTargets);
|
|
709
|
-
|
|
710
|
-
if (tis.handled)
|
|
711
|
-
{
|
|
712
|
-
for (const target of addTargets)
|
|
713
|
-
{
|
|
714
|
-
if (tis.set)
|
|
715
|
-
{
|
|
716
|
-
allTargets.add(target);
|
|
717
|
-
}
|
|
718
|
-
else if (tis.array)
|
|
719
|
-
{
|
|
720
|
-
if (allTargets.indexOf(target) === -1)
|
|
721
|
-
{
|
|
722
|
-
allTargets.push(target);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
else
|
|
728
|
-
{
|
|
729
|
-
if (!tis.handled)
|
|
730
|
-
{
|
|
731
|
-
console.warn("cannot add targets to collection",
|
|
732
|
-
{addTargets, allTargets, registry: this});
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
this.setupTargets(addTargets);
|
|
737
|
-
return this;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* Remove a target object from the registry.
|
|
742
|
-
*
|
|
743
|
-
* This will also remove any extension properties added to the
|
|
744
|
-
* target object by this registry instance.
|
|
745
|
-
*
|
|
746
|
-
* @param {...object} [delTargets] Targets to unregister
|
|
747
|
-
*
|
|
748
|
-
* If no targets are specified, this will unregister **ALL** targets
|
|
749
|
-
* from this registry!
|
|
750
|
-
*
|
|
751
|
-
* @returns {module:@lumjs/core/events.Registry} `this`
|
|
752
|
-
*/
|
|
753
|
-
unregister(...delTargets)
|
|
754
|
-
{
|
|
755
|
-
const allTargets = this.getTargets();
|
|
756
|
-
const tis = targetsAre(allTargets);
|
|
757
|
-
|
|
758
|
-
if (delTargets.length === 0)
|
|
759
|
-
{ // Unregister ALL targets.
|
|
760
|
-
delTargets = allTargets;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
if (!tis.handled)
|
|
764
|
-
{
|
|
765
|
-
console.warn("cannot remove targets from collection",
|
|
766
|
-
{delTargets, allTargets, registry: this});
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
for (const target of delTargets)
|
|
770
|
-
{
|
|
771
|
-
if (!isRegistered(target)) continue;
|
|
772
|
-
|
|
773
|
-
const tpm = target[RegSym];
|
|
774
|
-
const tp = tpm.r.get(this);
|
|
775
|
-
|
|
776
|
-
if (tis.set)
|
|
777
|
-
{
|
|
778
|
-
allTargets.delete(target);
|
|
779
|
-
}
|
|
780
|
-
else if (tis.array)
|
|
781
|
-
{
|
|
782
|
-
const tin = allTargets.indexOf(target);
|
|
783
|
-
if (tin !== -1)
|
|
784
|
-
{
|
|
785
|
-
allTargets.splice(tin, 1);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (isObj(tp))
|
|
790
|
-
{ // Remove any added extension properties
|
|
791
|
-
for (const ep in tp)
|
|
792
|
-
{
|
|
793
|
-
if (tpm.p[ep] === this)
|
|
794
|
-
{ // Remove it from the target.
|
|
795
|
-
delete target[ep];
|
|
796
|
-
delete tpm.p[ep];
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
tpm.r.delete(this);
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (tpm.r.size === 0)
|
|
803
|
-
{ // No registries left, remove the metadata too
|
|
804
|
-
delete target[RegSym];
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
return this;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
/**
|
|
812
|
-
* Get a Set of event types/names from various kinds of values
|
|
813
|
-
* @param {(string|symbol|Iterable)} types - Event types source
|
|
814
|
-
*
|
|
815
|
-
* If this is a string, it'll be passed to `splitNames()`.
|
|
816
|
-
* If it's a Symbol, it'll be wrapped in a Set.
|
|
817
|
-
* If it's any kind of Iterable value, it'll be converted to a Set.
|
|
818
|
-
*
|
|
819
|
-
* @returns {Set}
|
|
820
|
-
* @throws {TypeError} If `names` is not a valid value
|
|
821
|
-
*/
|
|
822
|
-
getEventTypes(types)
|
|
823
|
-
{
|
|
824
|
-
if (typeof types === S)
|
|
825
|
-
{
|
|
826
|
-
return this.splitNames(types);
|
|
827
|
-
}
|
|
828
|
-
else if (typeof types === SY)
|
|
829
|
-
{
|
|
830
|
-
return new Set([types]);
|
|
831
|
-
}
|
|
832
|
-
else if (types instanceof Set)
|
|
833
|
-
{
|
|
834
|
-
return types;
|
|
835
|
-
}
|
|
836
|
-
else if (isIterable(types))
|
|
837
|
-
{
|
|
838
|
-
return new Set(types);
|
|
839
|
-
}
|
|
840
|
-
else
|
|
841
|
-
{
|
|
842
|
-
console.error({names: types, registry: this});
|
|
843
|
-
throw new TypeError("Invalid event names");
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
/**
|
|
848
|
-
* Split a (trimmed) string using `this.options.delimiter`
|
|
849
|
-
* @param {string} names - String to split
|
|
850
|
-
* @returns {Set}
|
|
851
|
-
*/
|
|
852
|
-
splitNames(names)
|
|
853
|
-
{
|
|
854
|
-
return new Set(names.trim().split(this.options.delimiter));
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
const LERP = LumEventRegistry.prototype;
|
|
860
|
-
def(LERP, 'getEventNames', LERP.getEventTypes);
|
|
861
|
-
|
|
862
|
-
cp(LumEventRegistry,
|
|
863
|
-
{
|
|
864
|
-
isRegistered, getMetadata, targetsAre,
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
module.exports = LumEventRegistry;
|
|
868
|
-
|
|
869
|
-
/**
|
|
870
|
-
* An alias to `getEventTypes`
|
|
871
|
-
* @function module:@lumjs/core/events.Registry#getEventNames
|
|
872
|
-
* @param {(string|symbol|Iterable)} names - Event names source
|
|
873
|
-
* @returns {Set}
|
|
874
|
-
* @see module:@lumjs/core/events.Registry#getEventTypes
|
|
875
|
-
*/
|