@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/observable.js
CHANGED
|
@@ -1,15 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Observable API module
|
|
3
|
+
* @module @lumjs/core/observable
|
|
4
|
+
*/
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
const {B,S,def} = require('./types');
|
|
8
|
+
const orig = require('./old/observable');
|
|
9
|
+
const evob = require('./events/observable');
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
|
-
* Make a target object (or function) support the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
12
|
+
* Make a target object (or function) support the observable API.
|
|
13
|
+
*
|
|
14
|
+
* There are currently two implementations of the observable API,
|
|
15
|
+
* and this function will determine which one to call based on the
|
|
16
|
+
* arguments passed (if old options or positional arguments are
|
|
17
|
+
* detected, the original implementation will be called, otherwise
|
|
18
|
+
* the new events-based implementation will be called.)
|
|
10
19
|
*
|
|
11
|
-
*
|
|
20
|
+
* When the older implementation is removed, this function will be
|
|
21
|
+
* replaced by a simple alias to `makeObservable`.
|
|
22
|
+
*
|
|
23
|
+
* NOTE: this function is the *actual* exported value of the `observable`
|
|
24
|
+
* module, but it's also available as `observable.auto` and that is how
|
|
25
|
+
* it's being documented (as jsdoc doesn't like functions being treated
|
|
26
|
+
* like objects with methods defined on them, but I do that a lot.)
|
|
27
|
+
*
|
|
28
|
+
* @param {(object|function)} el - The target to add observable API to.
|
|
12
29
|
* @param {object} [opts] Options that define behaviours.
|
|
30
|
+
*
|
|
31
|
+
* If the `addre` and `reinherit` options are detected, then the original
|
|
32
|
+
* implementation will be used.
|
|
33
|
+
*
|
|
13
34
|
* @param {string} [opts.wildcard='*'] The event name used as a wildcard.
|
|
14
35
|
*
|
|
15
36
|
* @param {boolean} [opts.wrapargs=false] If `true`, the event handlers will
|
|
@@ -76,335 +97,57 @@ const lock = Object.freeze;
|
|
|
76
97
|
* to the `el` object, which is a version of `observable()` with the
|
|
77
98
|
* default options being the same as the current `opts`.
|
|
78
99
|
*
|
|
79
|
-
* @param {
|
|
80
|
-
* to the `el` object, which is a function that can re-build the
|
|
81
|
-
* observable API methods with new `opts` replacing the old ones.
|
|
82
|
-
*
|
|
83
|
-
* The method added takes two arguments, the first being an `object`
|
|
84
|
-
* representing the new options to set on the target.
|
|
100
|
+
* @param {(boolean|object)} [arg3] Version dependent argument.
|
|
85
101
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* specified in the first argument.
|
|
102
|
+
* If this is a `boolean` then the original observable implementation
|
|
103
|
+
* will be called and this will be the `redefine` argument.
|
|
89
104
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
105
|
+
* Otherwise it's assumed to be the `ro` argument of the `makeObservable`
|
|
106
|
+
* compatibility implementation.
|
|
92
107
|
*
|
|
93
|
-
* @param {boolean} [redefine=false] If `true` allow targets already
|
|
94
|
-
* implementing the `on()` and `trigger()` methods to be re-initialized.
|
|
95
|
-
*
|
|
96
|
-
* Generally only needed if you need to change the `opts` for some reason.
|
|
97
|
-
* This is forced to `true` by the method added by `opts.addre`.
|
|
98
|
-
*
|
|
99
108
|
* @returns {object} el
|
|
100
109
|
*
|
|
101
|
-
* @
|
|
110
|
+
* @see module:@lumjs/core/observable.orig
|
|
111
|
+
* @see module:@lumjs/core/events/observable.makeObservable
|
|
112
|
+
* @alias module:@lumjs/core/observable.auto
|
|
102
113
|
*/
|
|
103
|
-
function observable (el={}, opts={},
|
|
114
|
+
function observable (el={}, opts={}, arg3)
|
|
104
115
|
{
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{ // Don't know how to handle this, sorry.
|
|
109
|
-
throw new Error("non-object sent to observable()");
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (isObservable(el) && !redefine)
|
|
113
|
-
{ // It's already observable.
|
|
114
|
-
return el;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (typeof opts === B)
|
|
118
|
-
{ // Assume it's the wrapthis option.
|
|
119
|
-
opts = {wrapthis: opts};
|
|
120
|
-
}
|
|
121
|
-
else if (!isObj(opts))
|
|
122
|
-
{
|
|
123
|
-
opts = {};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const noSpace = /^\S+$/;
|
|
127
|
-
|
|
128
|
-
const wildcard = (typeof opts.wildcard === S
|
|
129
|
-
&& noSpace.test(opts.wildcard))
|
|
130
|
-
? opts.wildcard
|
|
131
|
-
: '*';
|
|
132
|
-
|
|
133
|
-
const wrapsetup = (typeof opts.wrapsetup === F)
|
|
134
|
-
? opts.wrapsetup
|
|
135
|
-
: null;
|
|
136
|
-
|
|
137
|
-
const wrapthis = (typeof opts.wrapthis === B)
|
|
138
|
-
? opts.wrapthis
|
|
139
|
-
: false;
|
|
140
|
-
|
|
141
|
-
const wrapargs = (typeof opts.wrapargs === B)
|
|
142
|
-
? opts.wrapargs
|
|
143
|
-
: (wrapsetup ? !wrapthis : false);
|
|
144
|
-
|
|
145
|
-
const wrapped = (wrapthis || wrapargs);
|
|
146
|
-
|
|
147
|
-
const wraplock = (typeof opts.wraplock === B)
|
|
148
|
-
? opts.wraplock
|
|
149
|
-
: true;
|
|
150
|
-
|
|
151
|
-
const addname = (typeof opts.addname === B)
|
|
152
|
-
? opts.addname
|
|
153
|
-
: !wrapped;
|
|
154
|
-
|
|
155
|
-
const addis = (typeof opts.addis === B)
|
|
156
|
-
? opts.addis
|
|
157
|
-
: wrapped;
|
|
158
|
-
|
|
159
|
-
const validIdent = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
|
|
160
|
-
|
|
161
|
-
const addme = (typeof opts.addme === S
|
|
162
|
-
&& validIdent.test(opts.addme))
|
|
163
|
-
? opts.addme
|
|
164
|
-
: null;
|
|
165
|
-
|
|
166
|
-
const addre = (typeof opts.addre === S
|
|
167
|
-
&& validIdent.test(opts.addre))
|
|
168
|
-
? opts.addre
|
|
169
|
-
: null;
|
|
170
|
-
|
|
171
|
-
const slice = Array.prototype.slice;
|
|
172
|
-
|
|
173
|
-
function onEachEvent (e, fn)
|
|
116
|
+
if (typeof arg3 === B
|
|
117
|
+
|| typeof opts.addre === S
|
|
118
|
+
|| typeof opts.reinherit === B)
|
|
174
119
|
{
|
|
175
|
-
|
|
176
|
-
const me = es.length > 1;
|
|
177
|
-
for (e of es)
|
|
178
|
-
{
|
|
179
|
-
fn(e, me);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const add = def(el);
|
|
184
|
-
|
|
185
|
-
function runCallback (name, fn, args)
|
|
186
|
-
{
|
|
187
|
-
if (fn.busy) return;
|
|
188
|
-
fn.busy = 1;
|
|
189
|
-
|
|
190
|
-
let fobj;
|
|
191
|
-
|
|
192
|
-
if (wrapped)
|
|
193
|
-
{ // Something is going to use our wrapper object.
|
|
194
|
-
const isWild = (name === wildcard);
|
|
195
|
-
const fname = isWild ? (addname ? args[0] : args.shift()) : name;
|
|
196
|
-
|
|
197
|
-
fobj =
|
|
198
|
-
{
|
|
199
|
-
isObservable: lock({event: true, target: false}),
|
|
200
|
-
self: el,
|
|
201
|
-
target: el,
|
|
202
|
-
name: fname,
|
|
203
|
-
type: fname,
|
|
204
|
-
func: fn,
|
|
205
|
-
wildcard: isWild,
|
|
206
|
-
args,
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
if (wrapsetup)
|
|
210
|
-
{
|
|
211
|
-
wrapsetup.call(el, fobj);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (wraplock)
|
|
215
|
-
{
|
|
216
|
-
lock(fobj);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const fthis = wrapthis ? fobj : el;
|
|
221
|
-
const fargs = wrapargs ? [fobj]
|
|
222
|
-
: ((fn.typed && addname) ? [name].concat(args) : args);
|
|
223
|
-
|
|
224
|
-
fn.apply(fthis, fargs);
|
|
225
|
-
fn.busy = 0;
|
|
120
|
+
return orig(el, opts, arg3);
|
|
226
121
|
}
|
|
227
|
-
|
|
228
|
-
let callbacks = {};
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Assign an event handler
|
|
232
|
-
*
|
|
233
|
-
* Listen to the given space separated list of `events` and execute
|
|
234
|
-
* the `callback` each time an event is triggered.
|
|
235
|
-
* @param {string} events - events ids
|
|
236
|
-
* @param {function} fn - callback function
|
|
237
|
-
* @returns {object} el
|
|
238
|
-
*/
|
|
239
|
-
add('on', function(events, fn)
|
|
240
|
-
{
|
|
241
|
-
if (typeof fn !== F)
|
|
242
|
-
{
|
|
243
|
-
console.error("non-function passed to on()");
|
|
244
|
-
return el;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
onEachEvent(events, function(name, typed)
|
|
248
|
-
{
|
|
249
|
-
(callbacks[name] = callbacks[name] || []).push(fn);
|
|
250
|
-
fn.typed = typed;
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
return el;
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Removes the given space separated list of `events` listeners
|
|
258
|
-
*
|
|
259
|
-
* @param {string} events - events ids
|
|
260
|
-
* @param {function} fn - callback function
|
|
261
|
-
* @returns {object} el
|
|
262
|
-
*/
|
|
263
|
-
add('off', function(events, fn)
|
|
264
|
-
{
|
|
265
|
-
if (events === wildcard && !fn)
|
|
266
|
-
{ // Clear all callbacks.
|
|
267
|
-
callbacks = {};
|
|
268
|
-
}
|
|
269
|
-
else
|
|
270
|
-
{
|
|
271
|
-
onEachEvent(events, function(name)
|
|
272
|
-
{
|
|
273
|
-
if (fn)
|
|
274
|
-
{ // Find a specific callback to remove.
|
|
275
|
-
var arr = callbacks[name]
|
|
276
|
-
for (var i = 0, cb; cb = arr && arr[i]; ++i)
|
|
277
|
-
{
|
|
278
|
-
if (cb == fn) arr.splice(i--, 1);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
else
|
|
282
|
-
{ // Remove all callbacks for this event.
|
|
283
|
-
delete callbacks[name];
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
return el
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Add a one-shot event handler.
|
|
292
|
-
*
|
|
293
|
-
* Listen to the given space separated list of `events` and execute
|
|
294
|
-
* the `callback` at most once.
|
|
295
|
-
*
|
|
296
|
-
* @param {string} events - events ids
|
|
297
|
-
* @param {function} fn - callback function
|
|
298
|
-
* @returns {object} el
|
|
299
|
-
*/
|
|
300
|
-
add('one', function(events, fn)
|
|
301
|
-
{
|
|
302
|
-
function on()
|
|
303
|
-
{
|
|
304
|
-
el.off(events, on)
|
|
305
|
-
fn.apply(el, arguments)
|
|
306
|
-
}
|
|
307
|
-
return el.on(events, on);
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Execute all callback functions that listen to the given space
|
|
312
|
-
* separated list of `events`
|
|
313
|
-
* @param {string} events - events ids
|
|
314
|
-
* @returns {object} el
|
|
315
|
-
*/
|
|
316
|
-
add('trigger', function(events)
|
|
317
|
-
{
|
|
318
|
-
// getting the arguments
|
|
319
|
-
// skipping the first one
|
|
320
|
-
const args = slice.call(arguments, 1);
|
|
321
|
-
|
|
322
|
-
onEachEvent(events, function(name)
|
|
323
|
-
{
|
|
324
|
-
const fns = slice.call(callbacks[name] || [], 0);
|
|
325
|
-
|
|
326
|
-
for (var i = 0, fn; fn = fns[i]; ++i)
|
|
327
|
-
{
|
|
328
|
-
runCallback(name, fn, args);
|
|
329
|
-
if (fns[i] !== fn) { i-- }
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (callbacks[wildcard] && name != wildcard)
|
|
333
|
-
{ // Trigger the wildcard.
|
|
334
|
-
el.trigger.apply(el, ['*', name].concat(args));
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
return el
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
if (addis)
|
|
122
|
+
else
|
|
343
123
|
{
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (addme)
|
|
348
|
-
{ // Add a wrapper for observable() that sets new default options.
|
|
349
|
-
add(addme, function (obj=null, mopts={})
|
|
350
|
-
{
|
|
351
|
-
return observable(obj, clone(opts, mopts));
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (addre)
|
|
356
|
-
{ // Add a method to change the observable options.
|
|
357
|
-
const reinherit = opts.reinherit ?? false;
|
|
358
|
-
|
|
359
|
-
add(addre, function(replacementOpts={}, inherit=reinherit)
|
|
360
|
-
{
|
|
361
|
-
const newOpts
|
|
362
|
-
= inherit
|
|
363
|
-
? Object.assign({}, opts, replacementOpts)
|
|
364
|
-
: replacementOpts;
|
|
365
|
-
return observable(el, replacementOpts, true);
|
|
366
|
-
});
|
|
124
|
+
return evob.makeObservable(el, opts, arg3);
|
|
367
125
|
}
|
|
368
|
-
|
|
369
|
-
// Metadata
|
|
370
|
-
add('$$observable$$', lock({opts, observable}));
|
|
371
|
-
|
|
372
|
-
return el
|
|
373
|
-
|
|
374
|
-
} // observable()
|
|
126
|
+
}
|
|
375
127
|
|
|
376
128
|
module.exports = observable;
|
|
377
129
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
* @returns {boolean}
|
|
384
|
-
*/
|
|
385
|
-
function isObservable(obj)
|
|
386
|
-
{
|
|
387
|
-
return (isComplex(obj)
|
|
388
|
-
&& typeof obj.trigger === F
|
|
389
|
-
&& typeof obj.on === F);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Add an 'is()' method to `observable` itself.
|
|
393
|
-
def(observable, 'is', isObservable);
|
|
130
|
+
def(observable)
|
|
131
|
+
('is', evob.isObservable)
|
|
132
|
+
('auto', observable)
|
|
133
|
+
('orig', orig)
|
|
134
|
+
('wrap', evob.makeObservable)
|
|
394
135
|
|
|
395
136
|
/**
|
|
396
|
-
* Does a value implement the
|
|
397
|
-
* @
|
|
398
|
-
* @
|
|
399
|
-
* @param {*} v - The expected object/function to test.
|
|
137
|
+
* Does a value implement the observable interface?
|
|
138
|
+
* @function module:@lumjs/core/observable.is
|
|
139
|
+
* @param {*} v - Value to test
|
|
400
140
|
* @returns {boolean}
|
|
401
|
-
* @see module:@lumjs/core/observable.
|
|
141
|
+
* @see module:@lumjs/core/events/observable.isObservable
|
|
402
142
|
*/
|
|
403
143
|
|
|
404
144
|
/**
|
|
405
|
-
*
|
|
406
|
-
* @
|
|
407
|
-
*
|
|
145
|
+
* An alias to
|
|
146
|
+
* {@link module:@lumjs/core/events/observable.makeObservable makeObservable()}
|
|
147
|
+
*
|
|
148
|
+
* @function module:@lumjs/core/observable.wrap
|
|
149
|
+
* @param {(object|function)} el
|
|
150
|
+
* @param {object} oo
|
|
151
|
+
* @param {object} ro
|
|
152
|
+
* @returns {object}
|
|
408
153
|
*/
|
|
409
|
-
|
|
410
|
-
TYPES.add('OBSERV', 'observable', isObservable, 'doesObservable');
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// Defining these after observable.
|
|
2
|
+
const {B,F,S,def,isObj,isComplex} = require('../types');
|
|
3
|
+
const {isObservable} = require('../events/observable');
|
|
4
|
+
const lock = Object.freeze;
|
|
5
|
+
const copy = Object.assign;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The original implementation of the observable API.
|
|
9
|
+
*
|
|
10
|
+
* This older implementation will be removed entirely when `v2.0` is
|
|
11
|
+
* released, and possibly sooner if I don't find anything broken with
|
|
12
|
+
* the compatibility wrapper. For now it's able to be loaded on demand.
|
|
13
|
+
*
|
|
14
|
+
* @param {(object|function)} el - The target to add observable API to.
|
|
15
|
+
*
|
|
16
|
+
* @param {object} [opts]
|
|
17
|
+
* {@link module:@lumjs/core/observable.observable Observable} options.
|
|
18
|
+
*
|
|
19
|
+
* Includes a few additional options no longer supported by the
|
|
20
|
+
* new events-based implementation. Only the options exclusive to this
|
|
21
|
+
* version will be documented here.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} [opts.addre] If set, add a method with this name
|
|
24
|
+
* to the `el` object, which is a function that can re-build the
|
|
25
|
+
* observable API methods with new `opts` replacing the old ones.
|
|
26
|
+
*
|
|
27
|
+
* The method added takes two arguments, the first being an `object`
|
|
28
|
+
* representing the new options to set on the target.
|
|
29
|
+
*
|
|
30
|
+
* The second is an optional `boolean` value that determines if the
|
|
31
|
+
* existing `opts` should be used as defaults for any options not
|
|
32
|
+
* specified in the first argument.
|
|
33
|
+
*
|
|
34
|
+
* @param {boolean} [opts.reinherit=false] Used as the default value of
|
|
35
|
+
* the second argument of the method added by `opts.addre`.
|
|
36
|
+
*
|
|
37
|
+
* @param {boolean} [redefine=false] If `true` allow targets already
|
|
38
|
+
* implementing the `on()` and `trigger()` methods to be re-initialized.
|
|
39
|
+
*
|
|
40
|
+
* Generally only needed if you need to change the `opts` for some reason.
|
|
41
|
+
* This is forced to `true` by the method added by `opts.addre`.
|
|
42
|
+
*
|
|
43
|
+
* @returns {object} el
|
|
44
|
+
* @see module:@lumjs/core/observable~API
|
|
45
|
+
* @alias module:@lumjs/core/observable.orig
|
|
46
|
+
*/
|
|
47
|
+
function observable (el={}, opts={}, redefine=false)
|
|
48
|
+
{
|
|
49
|
+
//console.debug("observable", el, opts);
|
|
50
|
+
|
|
51
|
+
if (!isComplex(el))
|
|
52
|
+
{ // Don't know how to handle this, sorry.
|
|
53
|
+
throw new Error("non-object sent to observable()");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isObservable(el) && !redefine)
|
|
57
|
+
{ // It's already observable.
|
|
58
|
+
return el;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (typeof opts === B)
|
|
62
|
+
{ // Assume it's the wrapthis option.
|
|
63
|
+
opts = {wrapthis: opts};
|
|
64
|
+
}
|
|
65
|
+
else if (!isObj(opts))
|
|
66
|
+
{
|
|
67
|
+
opts = {};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const noSpace = /^\S+$/;
|
|
71
|
+
|
|
72
|
+
const wildcard = (typeof opts.wildcard === S
|
|
73
|
+
&& noSpace.test(opts.wildcard))
|
|
74
|
+
? opts.wildcard
|
|
75
|
+
: '*';
|
|
76
|
+
|
|
77
|
+
const wrapsetup = (typeof opts.wrapsetup === F)
|
|
78
|
+
? opts.wrapsetup
|
|
79
|
+
: null;
|
|
80
|
+
|
|
81
|
+
const wrapthis = (typeof opts.wrapthis === B)
|
|
82
|
+
? opts.wrapthis
|
|
83
|
+
: false;
|
|
84
|
+
|
|
85
|
+
const wrapargs = (typeof opts.wrapargs === B)
|
|
86
|
+
? opts.wrapargs
|
|
87
|
+
: (wrapsetup ? !wrapthis : false);
|
|
88
|
+
|
|
89
|
+
const wrapped = (wrapthis || wrapargs);
|
|
90
|
+
|
|
91
|
+
const wraplock = (typeof opts.wraplock === B)
|
|
92
|
+
? opts.wraplock
|
|
93
|
+
: true;
|
|
94
|
+
|
|
95
|
+
const addname = (typeof opts.addname === B)
|
|
96
|
+
? opts.addname
|
|
97
|
+
: !wrapped;
|
|
98
|
+
|
|
99
|
+
const addis = (typeof opts.addis === B)
|
|
100
|
+
? opts.addis
|
|
101
|
+
: wrapped;
|
|
102
|
+
|
|
103
|
+
const validIdent = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
|
|
104
|
+
|
|
105
|
+
const addme = (typeof opts.addme === S
|
|
106
|
+
&& validIdent.test(opts.addme))
|
|
107
|
+
? opts.addme
|
|
108
|
+
: null;
|
|
109
|
+
|
|
110
|
+
const addre = (typeof opts.addre === S
|
|
111
|
+
&& validIdent.test(opts.addre))
|
|
112
|
+
? opts.addre
|
|
113
|
+
: null;
|
|
114
|
+
|
|
115
|
+
const slice = Array.prototype.slice;
|
|
116
|
+
|
|
117
|
+
function onEachEvent (e, fn)
|
|
118
|
+
{
|
|
119
|
+
const es = e.split(/\s+/);
|
|
120
|
+
const me = es.length > 1;
|
|
121
|
+
for (e of es)
|
|
122
|
+
{
|
|
123
|
+
fn(e, me);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const add = def(el);
|
|
128
|
+
|
|
129
|
+
function runCallback (name, fn, args)
|
|
130
|
+
{
|
|
131
|
+
if (fn.busy) return;
|
|
132
|
+
fn.busy = 1;
|
|
133
|
+
|
|
134
|
+
let fobj;
|
|
135
|
+
|
|
136
|
+
if (wrapped)
|
|
137
|
+
{ // Something is going to use our wrapper object.
|
|
138
|
+
const isWild = (name === wildcard);
|
|
139
|
+
const fname = isWild ? (addname ? args[0] : args.shift()) : name;
|
|
140
|
+
|
|
141
|
+
fobj =
|
|
142
|
+
{
|
|
143
|
+
isObservable: lock({event: true, target: false}),
|
|
144
|
+
self: el,
|
|
145
|
+
target: el,
|
|
146
|
+
name: fname,
|
|
147
|
+
type: fname,
|
|
148
|
+
func: fn,
|
|
149
|
+
wildcard: isWild,
|
|
150
|
+
args,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (wrapsetup)
|
|
154
|
+
{
|
|
155
|
+
wrapsetup.call(el, fobj);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (wraplock)
|
|
159
|
+
{
|
|
160
|
+
lock(fobj);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const fthis = wrapthis ? fobj : el;
|
|
165
|
+
const fargs = wrapargs ? [fobj]
|
|
166
|
+
: ((fn.typed && addname) ? [name].concat(args) : args);
|
|
167
|
+
|
|
168
|
+
fn.apply(fthis, fargs);
|
|
169
|
+
fn.busy = 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let callbacks = {};
|
|
173
|
+
|
|
174
|
+
add('on', function(events, fn)
|
|
175
|
+
{
|
|
176
|
+
if (typeof fn !== F)
|
|
177
|
+
{
|
|
178
|
+
console.error("non-function passed to on()");
|
|
179
|
+
return el;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
onEachEvent(events, function(name, typed)
|
|
183
|
+
{
|
|
184
|
+
(callbacks[name] = callbacks[name] || []).push(fn);
|
|
185
|
+
fn.typed = typed;
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return el;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
add('off', function(events, fn)
|
|
192
|
+
{
|
|
193
|
+
if (events === wildcard && !fn)
|
|
194
|
+
{ // Clear all callbacks.
|
|
195
|
+
callbacks = {};
|
|
196
|
+
}
|
|
197
|
+
else
|
|
198
|
+
{
|
|
199
|
+
onEachEvent(events, function(name)
|
|
200
|
+
{
|
|
201
|
+
if (fn)
|
|
202
|
+
{ // Find a specific callback to remove.
|
|
203
|
+
var arr = callbacks[name]
|
|
204
|
+
for (var i = 0, cb; cb = arr && arr[i]; ++i)
|
|
205
|
+
{
|
|
206
|
+
if (cb == fn) arr.splice(i--, 1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else
|
|
210
|
+
{ // Remove all callbacks for this event.
|
|
211
|
+
delete callbacks[name];
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return el
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
add('one', function(events, fn)
|
|
219
|
+
{
|
|
220
|
+
function on()
|
|
221
|
+
{
|
|
222
|
+
el.off(events, on)
|
|
223
|
+
fn.apply(el, arguments)
|
|
224
|
+
}
|
|
225
|
+
return el.on(events, on);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
add('trigger', function(events)
|
|
229
|
+
{
|
|
230
|
+
// getting the arguments
|
|
231
|
+
// skipping the first one
|
|
232
|
+
const args = slice.call(arguments, 1);
|
|
233
|
+
|
|
234
|
+
onEachEvent(events, function(name)
|
|
235
|
+
{
|
|
236
|
+
const fns = slice.call(callbacks[name] || [], 0);
|
|
237
|
+
|
|
238
|
+
for (var i = 0, fn; fn = fns[i]; ++i)
|
|
239
|
+
{
|
|
240
|
+
runCallback(name, fn, args);
|
|
241
|
+
if (fns[i] !== fn) { i-- }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (callbacks[wildcard] && name != wildcard)
|
|
245
|
+
{ // Trigger the wildcard.
|
|
246
|
+
el.trigger.apply(el, ['*', name].concat(args));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
return el
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (addis)
|
|
255
|
+
{
|
|
256
|
+
add('isObservable', lock({event: false, target: true}));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (addme)
|
|
260
|
+
{ // Add a wrapper for observable() that sets new default options.
|
|
261
|
+
add(addme, function (obj=null, mopts)
|
|
262
|
+
{
|
|
263
|
+
return observable(obj, copy({}, opts, mopts));
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (addre)
|
|
268
|
+
{ // Add a method to change the observable options.
|
|
269
|
+
const reinherit = opts.reinherit ?? false;
|
|
270
|
+
|
|
271
|
+
add(addre, function(replacementOpts={}, inherit=reinherit)
|
|
272
|
+
{
|
|
273
|
+
const newOpts
|
|
274
|
+
= inherit
|
|
275
|
+
? Object.assign({}, opts, replacementOpts)
|
|
276
|
+
: replacementOpts;
|
|
277
|
+
return observable(el, replacementOpts, true);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Metadata
|
|
282
|
+
add('$$observable$$', lock({opts, observable}));
|
|
283
|
+
|
|
284
|
+
return el
|
|
285
|
+
|
|
286
|
+
} // observable()
|
|
287
|
+
|
|
288
|
+
// Add an 'is()' method to `observable` itself.
|
|
289
|
+
def(observable, 'is', isObservable);
|
|
290
|
+
|
|
291
|
+
module.exports = observable;
|