@lumjs/core 1.33.0 → 1.35.0
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/console.js +1 -1
- package/lib/events/index.js +15 -1
- package/lib/events/listener.js +6 -0
- package/lib/events/observable.js +243 -0
- package/lib/events/registry.js +12 -3
- package/lib/obj/cp.js +2 -2
- package/lib/observable.js +62 -319
- package/lib/old/observable.js +291 -0
- package/lib/traits.js +83 -66
- package/lib/types/basics.js +34 -1
- package/lib/types/index.js +1 -1
- package/lib/types/stringify.js +249 -90
- package/package.json +2 -1
package/lib/console.js
CHANGED
package/lib/events/index.js
CHANGED
|
@@ -36,7 +36,21 @@ exports = module.exports =
|
|
|
36
36
|
*/
|
|
37
37
|
extend(target, opts)
|
|
38
38
|
{
|
|
39
|
-
exports.register(target, opts);
|
|
39
|
+
exports.register([target], opts);
|
|
40
40
|
return target;
|
|
41
41
|
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Apply the compatibility version of the observable API;
|
|
45
|
+
* will load the events/observable sub-module on demand.
|
|
46
|
+
* @param {object} target
|
|
47
|
+
* @param {object} [opts]
|
|
48
|
+
* @returns {object} `target`
|
|
49
|
+
* @see module:@lumjs/core/events/observable.makeObservable
|
|
50
|
+
*/
|
|
51
|
+
observable(target, opts)
|
|
52
|
+
{
|
|
53
|
+
const {makeObservable} = require('./observable');
|
|
54
|
+
return makeObservable(target, opts);
|
|
55
|
+
},
|
|
42
56
|
}
|
package/lib/events/listener.js
CHANGED
|
@@ -77,6 +77,12 @@ class LumEventListener
|
|
|
77
77
|
this.registry = registry;
|
|
78
78
|
this.options = makeOpts(spec);
|
|
79
79
|
this.eventNames = registry.getEventNames(spec.eventNames);
|
|
80
|
+
|
|
81
|
+
const setup = this.options.setupListener ?? registry.options.setupListener;
|
|
82
|
+
if (typeof setup === F)
|
|
83
|
+
{
|
|
84
|
+
setup.call(registry, this);
|
|
85
|
+
}
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
/**
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observable Compatibility API
|
|
3
|
+
* @module @lumjs/core/events/observable
|
|
4
|
+
*/
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
const {B,F,S,def,isComplex,TYPES} = require('../types');
|
|
8
|
+
const lock = Object.freeze;
|
|
9
|
+
const copy = Object.assign;
|
|
10
|
+
const Registry = require('./registry');
|
|
11
|
+
|
|
12
|
+
const ISOB = 'isObservable';
|
|
13
|
+
|
|
14
|
+
const OBS_EXTENDS =
|
|
15
|
+
{
|
|
16
|
+
registry: '$events',
|
|
17
|
+
listen: 'on',
|
|
18
|
+
emit: 'trigger',
|
|
19
|
+
remove: 'off',
|
|
20
|
+
once: 'one',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const OBS_OPTIONS =
|
|
24
|
+
{
|
|
25
|
+
wrapargs: false,
|
|
26
|
+
wrapthis: false,
|
|
27
|
+
wraplock: true,
|
|
28
|
+
wrapsetup: null,
|
|
29
|
+
addme: null,
|
|
30
|
+
// addname: !(wrapargs || wrapthis)
|
|
31
|
+
// addis: (wrapargs || wrapthis)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A version of the observable API using the events module.
|
|
36
|
+
*
|
|
37
|
+
* It's obviously not going to be 100% identical due to
|
|
38
|
+
* the very different nature of the backend engine, but it'll
|
|
39
|
+
* try its best to make existing code work.
|
|
40
|
+
*
|
|
41
|
+
* A few obscure features that I don't think are particularly
|
|
42
|
+
* important and won't affect *most* exising uses will be dropped.
|
|
43
|
+
*
|
|
44
|
+
* @param {(object|function)} el - The target to add observable API to.
|
|
45
|
+
*
|
|
46
|
+
* @param {object} [oo]
|
|
47
|
+
* {@link module:@lumjs/core/observable Observable} options
|
|
48
|
+
*
|
|
49
|
+
* @param {boolean} [oo.addwrap] If this is `true`,
|
|
50
|
+
* and `oo.wrapargs` is `false`, then the `event` object
|
|
51
|
+
* will be appended to the arguments sent to the handler.
|
|
52
|
+
*
|
|
53
|
+
* The default value is: `!oo.wrapthis`
|
|
54
|
+
*
|
|
55
|
+
* @param {object} [ro]
|
|
56
|
+
* {@link module:@lumjs/core/events.Registry Registry} options;
|
|
57
|
+
*
|
|
58
|
+
* The `setupEvent` and `setupListener` options cannot be specified,
|
|
59
|
+
* as the versions from this module will always be used.
|
|
60
|
+
*
|
|
61
|
+
* The `extend` defaults are changed to match those used
|
|
62
|
+
* by the observable API (`on`,`trigger`,`off`,`one`),
|
|
63
|
+
* and `extend.registry` is set to `$events` by default.
|
|
64
|
+
*
|
|
65
|
+
* Setting the `extend` option to `false` will have no effect
|
|
66
|
+
* in this implementation.
|
|
67
|
+
*
|
|
68
|
+
* @param {boolean} [wantReg=false] Return the Registry?
|
|
69
|
+
* @returns {object} normally `el`, unless `wantReg` was `true`
|
|
70
|
+
* @see module:@lumjs/core/observable~API
|
|
71
|
+
* @alias module:@lumjs/core/events/observable.makeObservable
|
|
72
|
+
*/
|
|
73
|
+
function makeObservable(el, oo, ro, wantReg=false)
|
|
74
|
+
{
|
|
75
|
+
if (!isComplex(el))
|
|
76
|
+
{
|
|
77
|
+
throw new TypeError("el was not an object or function");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ro = copy({}, ro);
|
|
81
|
+
ro.extend = copy({}, OBS_EXTENDS, ro.extend);
|
|
82
|
+
ro.setupEvent = setupEvent;
|
|
83
|
+
ro.setupListener = setupListener;
|
|
84
|
+
oo = ro.observable = copy({}, OBS_OPTIONS, oo);
|
|
85
|
+
|
|
86
|
+
let wrapped = (oo.wrapargs || oo.wrapthis);
|
|
87
|
+
if (!wrapped && typeof oo.wrapsetup === F)
|
|
88
|
+
{
|
|
89
|
+
wrapped = oo.wrapargs = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof oo.wildcard === S)
|
|
93
|
+
{ // oo.wildcard takes precedence
|
|
94
|
+
ro.wildcard = oo.wildcard;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const setIf = (opt, get) =>
|
|
98
|
+
{
|
|
99
|
+
if (typeof oo[opt] !== B)
|
|
100
|
+
{
|
|
101
|
+
oo[opt] = get();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
setIf('addname', () => !wrapped);
|
|
106
|
+
setIf('addis', () => wrapped);
|
|
107
|
+
setIf('addwrap', () => !oo.wrapthis);
|
|
108
|
+
|
|
109
|
+
const reg = new Registry([el], ro);
|
|
110
|
+
|
|
111
|
+
if (oo.addis)
|
|
112
|
+
{
|
|
113
|
+
def(el, ISOB, lock({event: false, target: true}));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (typeof oo.addme === S)
|
|
117
|
+
{
|
|
118
|
+
def(el, oo.addme, function(el2, oo2, ro2)
|
|
119
|
+
{
|
|
120
|
+
return makeObservable(el2,
|
|
121
|
+
copy({}, oo, oo2),
|
|
122
|
+
copy({}, ro, ro2)
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return wantReg ? reg : el;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Event setup for observable compatibility
|
|
132
|
+
* @type {module:@lumjs/core/events~SetupEvent}
|
|
133
|
+
* @alias module:@lumjs/core/events/observable.setupEvent
|
|
134
|
+
*/
|
|
135
|
+
function setupEvent(ev)
|
|
136
|
+
{
|
|
137
|
+
const ro = this.registry.options;
|
|
138
|
+
const oo = ro.observable;
|
|
139
|
+
ev.wildcard = this.eventNames.has(ro.wildcard);
|
|
140
|
+
ev.self = ev.target;
|
|
141
|
+
ev.type = ev.name;
|
|
142
|
+
def(ev, ISOB, lock({event: true, target: false}));
|
|
143
|
+
|
|
144
|
+
if (typeof oo.wrapsetup === F)
|
|
145
|
+
{
|
|
146
|
+
oo.wrapsetup.call(ev.target, ev);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (oo.wraplock)
|
|
150
|
+
{
|
|
151
|
+
lock(ev);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Listener setup for observable compatibility
|
|
157
|
+
* @type {module:@lumjs/core/events~SetupListener}
|
|
158
|
+
* @alias module:@lumjs/core/events/observable.setupListener
|
|
159
|
+
*/
|
|
160
|
+
function setupListener(ln)
|
|
161
|
+
{
|
|
162
|
+
const oo = this.options.observable;
|
|
163
|
+
const oh = ln.observableHandler = ln.handler;
|
|
164
|
+
const go = (ev, args) =>
|
|
165
|
+
{
|
|
166
|
+
if (typeof oh === F)
|
|
167
|
+
{
|
|
168
|
+
const thisTarget = oo.wrapthis ? ev : ev.target;
|
|
169
|
+
oh.apply(thisTarget, args);
|
|
170
|
+
}
|
|
171
|
+
else
|
|
172
|
+
{
|
|
173
|
+
oh.handleEvent(...args);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!oo.wrapargs)
|
|
178
|
+
{ // Not wrapping args, we need to wrap the handler.
|
|
179
|
+
const ec = ln.eventNames.size;
|
|
180
|
+
ln.handler = function(ev)
|
|
181
|
+
{
|
|
182
|
+
const args = ev.args;
|
|
183
|
+
if (ec > 1 && oo.addname)
|
|
184
|
+
{
|
|
185
|
+
args.unshift(ev.name);
|
|
186
|
+
}
|
|
187
|
+
if (oo.addwrap)
|
|
188
|
+
{
|
|
189
|
+
args.push(ev);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
go(ev, args);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else if (oo.wrapthis)
|
|
196
|
+
{ // Both wrapargs and wrapthis are in use, woah!
|
|
197
|
+
ln.handler = function(ev)
|
|
198
|
+
{
|
|
199
|
+
go(ev, [ev]);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* See if a value appears to implement the observable API.
|
|
206
|
+
*
|
|
207
|
+
* This is a simple function that relies on duck-typing,
|
|
208
|
+
* and only looks for `trigger` and `on` methods.
|
|
209
|
+
*
|
|
210
|
+
* @param {(object|function)} obj - Value to check for observable API
|
|
211
|
+
* @returns {boolean}
|
|
212
|
+
* @alias module:@lumjs/core/events/observable.isObservable
|
|
213
|
+
*/
|
|
214
|
+
function isObservable(obj)
|
|
215
|
+
{
|
|
216
|
+
return (isComplex(obj)
|
|
217
|
+
&& typeof obj.trigger === F
|
|
218
|
+
&& typeof obj.on === F);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Undocumented alias for the sake of compatibility.
|
|
222
|
+
def(makeObservable, 'is', isObservable);
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Does a value implement the Observable interface?
|
|
226
|
+
* @name module:@lumjs/core/types.doesObservable
|
|
227
|
+
* @function
|
|
228
|
+
* @param {*} v - The expected object/function to test.
|
|
229
|
+
* @returns {boolean}
|
|
230
|
+
* @see module:@lumjs/core/events/observable.isObservable
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Extension type for the {@link module:@lumjs/core/observable~API} interface.
|
|
235
|
+
* @memberof module:@lumjs/core/types.TYPES
|
|
236
|
+
* @member {string} OBSERV - Implements the *Observable* interface.
|
|
237
|
+
*/
|
|
238
|
+
TYPES.add('OBSERV', 'observable', isObservable, 'doesObservable');
|
|
239
|
+
|
|
240
|
+
module.exports =
|
|
241
|
+
{
|
|
242
|
+
makeObservable, setupEvent, setupListener, isObservable,
|
|
243
|
+
}
|
package/lib/events/registry.js
CHANGED
|
@@ -95,6 +95,9 @@ class LumEventRegistry
|
|
|
95
95
|
* If this is a `function`, it will be called to dynamically get a
|
|
96
96
|
* list of target objects whenever an event is triggered.
|
|
97
97
|
*
|
|
98
|
+
* If you want to use a `function` as an actual target, you'll need to
|
|
99
|
+
* wrap it in an array or other iterable object.
|
|
100
|
+
*
|
|
98
101
|
* @param {object} [opts] Options (saved to `options` property).
|
|
99
102
|
*
|
|
100
103
|
* @param {(RegExp|string)} [opts.delimiter=/\s+/] Used to split event names
|
|
@@ -135,12 +138,17 @@ class LumEventRegistry
|
|
|
135
138
|
* If `true` then when adding wrapper methods, the properties from
|
|
136
139
|
* `opts.extend` will replace any existing ones in each target.
|
|
137
140
|
*
|
|
138
|
-
* @param {
|
|
141
|
+
* @param {module:@lumjs/core/events~SetupEvent} [opts.setupEvent]
|
|
139
142
|
*
|
|
140
143
|
* If this is specified (either here or in individual listeners),
|
|
141
144
|
* it will be called and passed the Event object at the very end of
|
|
142
145
|
* its constructor.
|
|
143
146
|
*
|
|
147
|
+
* @param {module:@lumjs/core/events~SetupListener} [opts.setupListener]
|
|
148
|
+
*
|
|
149
|
+
* If this is specified, it will be called and passed the Listener
|
|
150
|
+
* object at the very end of its constructor.
|
|
151
|
+
*
|
|
144
152
|
* @param {string} [opts.wildcard='*'] Wildcard event name.
|
|
145
153
|
*
|
|
146
154
|
* - If you use this in `listen()` the handler will be used regardless
|
|
@@ -248,7 +256,7 @@ class LumEventRegistry
|
|
|
248
256
|
* it will be used as the `spec`, and the `spec.eventNames`
|
|
249
257
|
* and `spec.listener` properties will become mandatory.
|
|
250
258
|
*
|
|
251
|
-
* If it's a string
|
|
259
|
+
* If it's a string *OR* there is more than one argument, this
|
|
252
260
|
* will be used as the `spec.eventNames` property.
|
|
253
261
|
*
|
|
254
262
|
* @param {module:@lumjs/core/events~Handler} [handler]
|
|
@@ -277,7 +285,8 @@ class LumEventRegistry
|
|
|
277
285
|
* names `listener`, `handler` or `eventNames` as option properties,
|
|
278
286
|
* and if found, they will be removed.
|
|
279
287
|
*
|
|
280
|
-
* You may also override the `setupEvent`
|
|
288
|
+
* You may also override the `setupEvent` and `setupListener` registry
|
|
289
|
+
* options here if needed.
|
|
281
290
|
*
|
|
282
291
|
* @param {boolean} [spec.options.once=false] Only use the listener once?
|
|
283
292
|
*
|
package/lib/obj/cp.js
CHANGED
|
@@ -1235,7 +1235,7 @@ cp.from = function()
|
|
|
1235
1235
|
* @param {?object} [opts] Options
|
|
1236
1236
|
*
|
|
1237
1237
|
* If this is an `object`, the clone call will be:
|
|
1238
|
-
* `cp.with(opts).
|
|
1238
|
+
* `cp.with(opts).from(obj).ow.clone();`
|
|
1239
1239
|
*
|
|
1240
1240
|
* If this is `null` or `undefined`, the call will be:
|
|
1241
1241
|
* `cp(obj);`
|
|
@@ -1251,7 +1251,7 @@ cp.clone = function(obj, opts)
|
|
|
1251
1251
|
}
|
|
1252
1252
|
else
|
|
1253
1253
|
{
|
|
1254
|
-
return cp.with(opts).
|
|
1254
|
+
return cp.with(opts).from(obj).ow.clone();
|
|
1255
1255
|
}
|
|
1256
1256
|
}
|
|
1257
1257
|
|