@lumjs/compat 1.4.0 → 2.0.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/CHANGELOG.md +15 -1
- package/README.md +11 -30
- package/lib/index.js +133 -22
- package/package.json +6 -13
- package/lib/modulebuilder/index.js +0 -414
- package/lib/modulebuilder/into-core.js +0 -34
- package/lib/v4/deprecated.js +0 -122
- package/lib/v4/descriptors.js +0 -217
- package/lib/v4/index.js +0 -53
- package/lib/v4/loadtracker.js +0 -509
- package/lib/v4/object-helpers.js +0 -194
- package/lib/v4/promise.js +0 -392
- package/lib/v4/prop.js +0 -164
package/lib/v4/loadtracker.js
DELETED
|
@@ -1,509 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const core = require('@lumjs/core');
|
|
3
|
-
const {F,S,B,needObj,needType,def} = core.types
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Track if libraries, plugins, or whatever are loaded.
|
|
7
|
-
*
|
|
8
|
-
* Automatically manages events that are trigged on load.
|
|
9
|
-
* Also will automatically run events assigned *after* the
|
|
10
|
-
* desired item has been loaded.
|
|
11
|
-
*
|
|
12
|
-
* Can handle custom tests in addition to the default
|
|
13
|
-
* test which simply see's if the `loadTracker.mark()`
|
|
14
|
-
* method has been called for the specified name.
|
|
15
|
-
*
|
|
16
|
-
* @exports module:@lumjs/compat/v4/loadtracker
|
|
17
|
-
*/
|
|
18
|
-
class LoadTracker
|
|
19
|
-
{
|
|
20
|
-
/**
|
|
21
|
-
* Build a LoadTracker
|
|
22
|
-
*
|
|
23
|
-
* @param {object} [opts] - Named options for custom behaviours.
|
|
24
|
-
*
|
|
25
|
-
* @param {function} [opts.or] A custom test for `is()` method.
|
|
26
|
-
* If this returns true, `is()` will return true immediately.
|
|
27
|
-
* Mutually exclusive with the `opts.and` option.
|
|
28
|
-
*
|
|
29
|
-
* The function will have `this` set to the `LoadTracker` instance.
|
|
30
|
-
* It will be passed the same arguments sent to the `is()` method.
|
|
31
|
-
* The function must return a boolean value indicating if the item
|
|
32
|
-
* is considered *loaded* for the purposes of this loader instance.
|
|
33
|
-
*
|
|
34
|
-
* @param {function} [opts.and] A custom test for `is()` method.
|
|
35
|
-
* If this returns false, `is()` will return false immediately.
|
|
36
|
-
* Mutually exclusive with the `opts.or` option.
|
|
37
|
-
*
|
|
38
|
-
* The same function notes as `opts.or` apply to this as well.
|
|
39
|
-
*
|
|
40
|
-
* @param {boolean} [opts.before=false] When to run the custom test.
|
|
41
|
-
* If `true` the custom test is run before the standard test.
|
|
42
|
-
* If `false` the custom test is run after the standard test.
|
|
43
|
-
*
|
|
44
|
-
* If `opts.or` was used, and whichever test is ran first returns
|
|
45
|
-
* `true`, the other test won't be run at all.
|
|
46
|
-
*
|
|
47
|
-
* Likewise, if `opts.and` was used, and whichever test is ran first
|
|
48
|
-
* returns `false`, the other test won't be run at all.
|
|
49
|
-
*
|
|
50
|
-
* @param {function} [opts.check] A custom test for the `check*` methods.
|
|
51
|
-
* If specified, this will be ran *before* checking the rest of the
|
|
52
|
-
* arguments. The first parameter passed to the function is a boolean,
|
|
53
|
-
* indicating if only a single return value is expected; `checkOne()`
|
|
54
|
-
* passes `true` while `checkAll()` passes false. All subsequent
|
|
55
|
-
* parameters will be the arguments passed to the calling method.
|
|
56
|
-
* The function will have `this` set to the `LoadTracker` instance.
|
|
57
|
-
*
|
|
58
|
-
* When called from `checkOne()` if it returns a string, that will
|
|
59
|
-
* be returned as the missing item name. Return nil if no errors.
|
|
60
|
-
*
|
|
61
|
-
* When called from `checkAll()` if it returns an Array, all items
|
|
62
|
-
* from that array will be added to the list of missing items, and
|
|
63
|
-
* then the regular `checkAll()` logic will continue. If however it
|
|
64
|
-
* returns a string, then that will be returned as the sole item in
|
|
65
|
-
* the missing list without running any further `checkAll()` logic.
|
|
66
|
-
* Return nil or an *empty* array if no errors.
|
|
67
|
-
*
|
|
68
|
-
*/
|
|
69
|
-
constructor(opts={})
|
|
70
|
-
{
|
|
71
|
-
needObj(opts);
|
|
72
|
-
|
|
73
|
-
def(this, '$loaded', {}); // List of loaded libraries.
|
|
74
|
-
def(this, '$onload', {}); // Events to trigger when libraries are loaded.
|
|
75
|
-
|
|
76
|
-
let isTest = false, testOr = false;
|
|
77
|
-
if (typeof opts.or === F)
|
|
78
|
-
{ // A test that can override
|
|
79
|
-
isTest = opts.or;
|
|
80
|
-
testOr = true;
|
|
81
|
-
}
|
|
82
|
-
else if (typeof opts.and === F)
|
|
83
|
-
{
|
|
84
|
-
isTest = opts.and;
|
|
85
|
-
testOr = false;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
def(this, '$isTest', isTest);
|
|
89
|
-
def(this, '$isOr', testOr);
|
|
90
|
-
def(this, '$is1st', opts.before ?? false);
|
|
91
|
-
def(this, '$check', opts.check ?? false);
|
|
92
|
-
def(this, '$typeOne', opts.type ?? 'item');
|
|
93
|
-
def(this, '$typeAll', opts.types ?? this.$typeOne + 's');
|
|
94
|
-
def(this, '$self', opts.self);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Assign a callback function to be called.
|
|
99
|
-
*
|
|
100
|
-
* All callbacks for a given `name` will be ran when
|
|
101
|
-
* that `name` has been passed to `mark()` or `call()`.
|
|
102
|
-
*
|
|
103
|
-
* @param {string} name - The name of the item to be loaded.
|
|
104
|
-
* @param {function} event - The callback function.
|
|
105
|
-
*
|
|
106
|
-
* @returns {boolean} - Is the method call deferred?
|
|
107
|
-
* If `true` the `name` has not been marked as loaded, so the
|
|
108
|
-
* method has been added to a queue that will be called
|
|
109
|
-
*/
|
|
110
|
-
on(name, event)
|
|
111
|
-
{
|
|
112
|
-
needType(S, name, "Name must be a string");
|
|
113
|
-
needType(F, event, "Event must be a function");
|
|
114
|
-
|
|
115
|
-
if (!Array.isArray(this.$onload[name]))
|
|
116
|
-
{ // Add an array of events.
|
|
117
|
-
def(this.$onload, name, []);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
this.$onload[name].push(event);
|
|
121
|
-
|
|
122
|
-
if (this.is(name))
|
|
123
|
-
{ // Execute the function right now.
|
|
124
|
-
event.call(Lum, name, false);
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Mark an item as loaded.
|
|
133
|
-
*
|
|
134
|
-
* @param {string} name - Item being marked as loaded.
|
|
135
|
-
* @param {boolean} [call=true] Also call `call()` method?
|
|
136
|
-
* @param {boolean} [skipTest=true] Passed to `call()` method.
|
|
137
|
-
*/
|
|
138
|
-
mark(name, call=true, skipTest=true)
|
|
139
|
-
{
|
|
140
|
-
def(this.$loaded, name, true);
|
|
141
|
-
if (call)
|
|
142
|
-
{
|
|
143
|
-
this.call(name, skipTest);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Call all of the callback function for an item.
|
|
149
|
-
*
|
|
150
|
-
* @param {string} name - The name of the item.
|
|
151
|
-
* @param {boolean} [skipTest=false] Skip the `is()` test?
|
|
152
|
-
* If `false` we call `this.is(name)` and will only continue
|
|
153
|
-
* if it returns a true value. If `true` we call without testing.
|
|
154
|
-
*
|
|
155
|
-
* @returns {boolean} - If the events were called or not.
|
|
156
|
-
*/
|
|
157
|
-
call(name, skipTest=false)
|
|
158
|
-
{
|
|
159
|
-
if (!skipTest && !this.is(name))
|
|
160
|
-
{ // Okay, we cannot call if the item isn't loaded.
|
|
161
|
-
console.error("Cannot call events if item is not loaded", name, this);
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (Array.isArray(this.$onload[name]))
|
|
166
|
-
{
|
|
167
|
-
for (const event of this.$onload[name])
|
|
168
|
-
{
|
|
169
|
-
event.call(Lum, name, true);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return true;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Check if the item is loaded.
|
|
178
|
-
*
|
|
179
|
-
* @param {string} name - The item to check.
|
|
180
|
-
* @returns {boolean}
|
|
181
|
-
*/
|
|
182
|
-
is(name)
|
|
183
|
-
{
|
|
184
|
-
if (typeof name !== S)
|
|
185
|
-
{
|
|
186
|
-
console.error("Name must be a string", name);
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
let okay = false;
|
|
191
|
-
|
|
192
|
-
const hasTest = (typeof this.$isTest === F);
|
|
193
|
-
const test1st = this.$is1st;
|
|
194
|
-
const testOr = this.$isOr;
|
|
195
|
-
|
|
196
|
-
// A test that indicates we can return `okay` value.
|
|
197
|
-
const done = () => (!hasTest || (testOr && okay) || (!testOr && !okay));
|
|
198
|
-
|
|
199
|
-
if (hasTest && test1st)
|
|
200
|
-
{ // A custom test that is called before the normal test.
|
|
201
|
-
okay = this.$isTest(...arguments);
|
|
202
|
-
console.debug("is:before", okay, this);
|
|
203
|
-
if (done()) return okay;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Call the normal test, is the name marked?
|
|
207
|
-
okay = (this.$loaded[name] ?? false);
|
|
208
|
-
if (done()) return okay;
|
|
209
|
-
|
|
210
|
-
if (hasTest && !test1st)
|
|
211
|
-
{ // A custom test that is called after the normal test.
|
|
212
|
-
okay = this.$isTest(...arguments);
|
|
213
|
-
console.debug("is:after", okay, this);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return okay;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Get a list of loaded items.
|
|
221
|
-
*
|
|
222
|
-
* This only includes items that have been marked using the
|
|
223
|
-
* `mark()` method.
|
|
224
|
-
*
|
|
225
|
-
* @param {(boolean|function)} [sort=false] Should we sort the results?
|
|
226
|
-
* If this is `true` we use the default sorting algorithm.
|
|
227
|
-
* If this is a function, it's passed to the `array.sort()` method.
|
|
228
|
-
* Any other value and the list will be in the order returned
|
|
229
|
-
* by the `Object.keys()` method.
|
|
230
|
-
*
|
|
231
|
-
* @returns {Array} The list of loaded items.
|
|
232
|
-
*/
|
|
233
|
-
list(sort=false)
|
|
234
|
-
{
|
|
235
|
-
let list = Object.keys(this.$loaded);
|
|
236
|
-
if (sort === true)
|
|
237
|
-
{ // If sort is boolean true, use default sorting algorithm.
|
|
238
|
-
list.sort();
|
|
239
|
-
}
|
|
240
|
-
else if (typeof sort === F)
|
|
241
|
-
{ // A custom sort function.
|
|
242
|
-
list.sort(sort);
|
|
243
|
-
}
|
|
244
|
-
return list;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* The same output as `list(false)` but as a readonly accessor property.
|
|
249
|
-
*/
|
|
250
|
-
get keys()
|
|
251
|
-
{
|
|
252
|
-
return Object.keys(this.$loaded);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Return the first item that isn't loaded.
|
|
257
|
-
*
|
|
258
|
-
* @param {string} ...names - Any items you want to look for.
|
|
259
|
-
*
|
|
260
|
-
* @returns {string|undefined} If no items are missing, will be undefined.
|
|
261
|
-
*/
|
|
262
|
-
checkOne()
|
|
263
|
-
{
|
|
264
|
-
if (typeof this.$check === F)
|
|
265
|
-
{
|
|
266
|
-
const check = this.$check(true, ...arguments);
|
|
267
|
-
console.debug("checkOne:$check", check, this);
|
|
268
|
-
if (typeof check === S && check.trim() !== '')
|
|
269
|
-
{ // A non-empty string was passed.
|
|
270
|
-
return check;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
for (const lib of arguments)
|
|
275
|
-
{
|
|
276
|
-
if (!this.is(lib))
|
|
277
|
-
{
|
|
278
|
-
return lib;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Return a full list of any missing items.
|
|
285
|
-
*
|
|
286
|
-
* @param {string} ...names - Any items you want to look for.
|
|
287
|
-
*
|
|
288
|
-
* @returns {Array} A list of missing items.
|
|
289
|
-
*/
|
|
290
|
-
checkAll()
|
|
291
|
-
{
|
|
292
|
-
const missing = [];
|
|
293
|
-
|
|
294
|
-
if (typeof this.$check === F)
|
|
295
|
-
{
|
|
296
|
-
const check = this.$check(false, ...arguments);
|
|
297
|
-
console.debug("checkAll:$check", check, this);
|
|
298
|
-
if (Array.isArray(check))
|
|
299
|
-
{ // May have missing items, or be empty.
|
|
300
|
-
missing.push(...check);
|
|
301
|
-
}
|
|
302
|
-
else if (typeof check === S)
|
|
303
|
-
{ // A string indicates we can continue no further.
|
|
304
|
-
missing.push(check);
|
|
305
|
-
return missing;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
for (const lib of arguments)
|
|
310
|
-
{
|
|
311
|
-
if (!this.is(lib))
|
|
312
|
-
{
|
|
313
|
-
missing.push(lib);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return missing;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Setup a namespace object with wrapper methods.
|
|
322
|
-
*
|
|
323
|
-
* @param {object} ns - The namespace object, may also be a function.
|
|
324
|
-
* @param {object} [names] A map of alternate names for the methods.
|
|
325
|
-
*
|
|
326
|
-
* The default method names are:
|
|
327
|
-
*
|
|
328
|
-
* `mark` → Call `lt.mark`, returns `this.$self`.
|
|
329
|
-
* `has` → Proxy `lt.is`.
|
|
330
|
-
* `check` → Proxy `lt.checkOne`.
|
|
331
|
-
* `checkAll` → Proxy `lt.checkAll`.
|
|
332
|
-
* `list` → Proxy `lt.list`.
|
|
333
|
-
* `need` → Call `check`, if any missing, throw Error.
|
|
334
|
-
* `want` → Call `check`, return true if all are loaded, false if not.
|
|
335
|
-
* `onLoad` → Proxy `on`.
|
|
336
|
-
*
|
|
337
|
-
* If any of the method names are already present, they will be skipped.
|
|
338
|
-
*
|
|
339
|
-
*/
|
|
340
|
-
setupNamespace(ns, names={})
|
|
341
|
-
{
|
|
342
|
-
needObj(ns, true, "Invalid namespace object");
|
|
343
|
-
needObj(names, false, "Names must be an object");
|
|
344
|
-
|
|
345
|
-
const thisLoad = this; // Contextual instance reference.
|
|
346
|
-
let propName; // Will be set by hasnt() closure.
|
|
347
|
-
|
|
348
|
-
const getname = (name) => names[name] ?? name;
|
|
349
|
-
|
|
350
|
-
const hasnt = (name) =>
|
|
351
|
-
{
|
|
352
|
-
propName = getname(name);
|
|
353
|
-
return (ns[propName] === undefined);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const addfunc = (func) => def(ns, propName, func);
|
|
357
|
-
const addgetter = (func) => def(ns, propName, {get: func});
|
|
358
|
-
|
|
359
|
-
// Options for need() and want().
|
|
360
|
-
const loadOpts = def(ns, '$loadOpts',
|
|
361
|
-
{
|
|
362
|
-
checkAll: false,
|
|
363
|
-
warnings: false,
|
|
364
|
-
}).$loadOpts;
|
|
365
|
-
|
|
366
|
-
if (hasnt('mark'))
|
|
367
|
-
{
|
|
368
|
-
addfunc(function()
|
|
369
|
-
{
|
|
370
|
-
thisLoad.mark(...arguments);
|
|
371
|
-
return thisLoad.$self;
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (hasnt('has'))
|
|
376
|
-
{
|
|
377
|
-
addfunc(function()
|
|
378
|
-
{
|
|
379
|
-
return thisLoad.is(...arguments);
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (hasnt('check'))
|
|
384
|
-
{
|
|
385
|
-
addfunc(function()
|
|
386
|
-
{
|
|
387
|
-
return thisLoad.checkOne(...arguments);
|
|
388
|
-
})
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (hasnt('checkAll'))
|
|
392
|
-
{
|
|
393
|
-
addfunc(function()
|
|
394
|
-
{
|
|
395
|
-
return thisLoad.checkAll(...arguments);
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (hasnt('list'))
|
|
400
|
-
{
|
|
401
|
-
addfunc(function()
|
|
402
|
-
{
|
|
403
|
-
return thisLoad.list(...arguments);
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (hasnt('missing'))
|
|
408
|
-
{
|
|
409
|
-
addfunc(function(
|
|
410
|
-
{
|
|
411
|
-
fatal = false,
|
|
412
|
-
all = loadOpts.checkAll,
|
|
413
|
-
warn = loadOpts.warnings,
|
|
414
|
-
ok = this,
|
|
415
|
-
},
|
|
416
|
-
...items)
|
|
417
|
-
{
|
|
418
|
-
const thisCheck = all ? getname('checkAll') : getname('check');
|
|
419
|
-
const result = ns[thisCheck](...items);
|
|
420
|
-
|
|
421
|
-
if (typeof result === S
|
|
422
|
-
|| (all && Array.isArray(result) && result.length > 0))
|
|
423
|
-
{ // There are missing libraries.
|
|
424
|
-
const typeName = all ? thisLoad.$typeAll : thisLoad.$typeOne;
|
|
425
|
-
const missing = fatal ? JSON.stringify(result) : result;
|
|
426
|
-
|
|
427
|
-
if (fatal)
|
|
428
|
-
{
|
|
429
|
-
throw new Error(`Missing required ${typeName}: ${missing}`);
|
|
430
|
-
}
|
|
431
|
-
else
|
|
432
|
-
{
|
|
433
|
-
if (warn)
|
|
434
|
-
{
|
|
435
|
-
console.warn("Missing", typeName, missing);
|
|
436
|
-
}
|
|
437
|
-
return false;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// If we reached here, nothing was reported as missing.
|
|
442
|
-
return ok;
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
if (hasnt('need'))
|
|
447
|
-
{
|
|
448
|
-
addfunc(function()
|
|
449
|
-
{
|
|
450
|
-
const missing = getname('missing');
|
|
451
|
-
return ns[missing]({fatal: true, ok: thisLoad.$self}, ...arguments);
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (hasnt('want'))
|
|
456
|
-
{
|
|
457
|
-
addfunc(function()
|
|
458
|
-
{
|
|
459
|
-
const missing = getname('missing');
|
|
460
|
-
return ns[missing]({fatal: false, ok: true}, ...arguments);
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (hasnt('all'))
|
|
465
|
-
{
|
|
466
|
-
addgetter(function()
|
|
467
|
-
{
|
|
468
|
-
loadOpts.checkAll = true;
|
|
469
|
-
return ns;
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
if (hasnt('one'))
|
|
474
|
-
{
|
|
475
|
-
addgetter(function()
|
|
476
|
-
{
|
|
477
|
-
loadOpts.checkAll = false;
|
|
478
|
-
return ns;
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (hasnt('showWarnings'))
|
|
483
|
-
{
|
|
484
|
-
addfunc(function(val)
|
|
485
|
-
{
|
|
486
|
-
if (typeof val === B)
|
|
487
|
-
{
|
|
488
|
-
loadOpts.warnings = val;
|
|
489
|
-
return this;
|
|
490
|
-
}
|
|
491
|
-
else
|
|
492
|
-
{
|
|
493
|
-
return loadOpts.warnings;
|
|
494
|
-
}
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if (hasnt('onLoad'))
|
|
499
|
-
{
|
|
500
|
-
addfunc(function()
|
|
501
|
-
{
|
|
502
|
-
return thisLoad.on(...arguments);
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
} // LoadTracker class
|
|
508
|
-
|
|
509
|
-
module.exports = LoadTracker;
|
package/lib/v4/object-helpers.js
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Object helpers module
|
|
3
|
-
* @module @lumjs/compat/v4/object-helpers
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const core = require('@lumjs/core');
|
|
7
|
-
const {O,F,S,B,isObj} = core.types;
|
|
8
|
-
const {clone,copyProps} = core.obj;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A way to handle Mixins/Traits.
|
|
12
|
-
*
|
|
13
|
-
* This is basically a magic wrapper around {@link Lum.obj.into} which we
|
|
14
|
-
* use instead of Object.assign() as we don't want to overwrite properties
|
|
15
|
-
* by default.
|
|
16
|
-
*
|
|
17
|
-
* As it's designed to extend the class prototype and only the prototype,
|
|
18
|
-
* it will see if anything passed to it is a function/class and if so, it
|
|
19
|
-
* will automatically use the prototype of the function/class. If you want
|
|
20
|
-
* to copy static class properties, use {@link Lum.obj.into} instead of this.
|
|
21
|
-
*
|
|
22
|
-
* @param {object|function} target - The target we are copying into.
|
|
23
|
-
* @param {...*} sources - The source traits we want to mix in.
|
|
24
|
-
*
|
|
25
|
-
* @return {object|function} The `target` will be returned.
|
|
26
|
-
*/
|
|
27
|
-
exports.mixin = function (target, ...inSources)
|
|
28
|
-
{
|
|
29
|
-
var outSources = [];
|
|
30
|
-
|
|
31
|
-
function unwrap (what)
|
|
32
|
-
{
|
|
33
|
-
if (typeof what === F && typeof what.prototype === O)
|
|
34
|
-
{
|
|
35
|
-
return what.prototype;
|
|
36
|
-
}
|
|
37
|
-
else if (isObj(what))
|
|
38
|
-
{
|
|
39
|
-
return what;
|
|
40
|
-
}
|
|
41
|
-
else
|
|
42
|
-
{
|
|
43
|
-
throw new Error("Invalid function/object passed to addTraits()");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
target = unwrap(target); // Ensure the target is an object.
|
|
48
|
-
|
|
49
|
-
for (var s in inSources)
|
|
50
|
-
{
|
|
51
|
-
var source = inSources[s];
|
|
52
|
-
if (typeof source === B || typeof source === S)
|
|
53
|
-
{ // A special option statement, push it directly.
|
|
54
|
-
outSources.push(source);
|
|
55
|
-
}
|
|
56
|
-
else
|
|
57
|
-
{ // Anything else needs to be unwrapped.
|
|
58
|
-
outSources.push(unwrap(source));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return exports.into(target, outSources);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Copy properties between objects. Can be used for mixins/traits.
|
|
67
|
-
*
|
|
68
|
-
* For each of the sources specified, this will call:
|
|
69
|
-
*
|
|
70
|
-
* ```Lum.obj.copy(source, target, opts)```
|
|
71
|
-
*
|
|
72
|
-
* The current `opts` can be changed dynamically using special statements.
|
|
73
|
-
* See below for details on the statements to make that work.
|
|
74
|
-
* The default `opts` is `{default: true, overwrite: false}`
|
|
75
|
-
*
|
|
76
|
-
* @param {object|function} target - The target we are copying into.
|
|
77
|
-
* @param {...*} sources - The sources to copy from, and options.
|
|
78
|
-
*
|
|
79
|
-
* If a source is a boolean, it will reset the `opts.overwrite` property.
|
|
80
|
-
*
|
|
81
|
-
* If a source is the string 'default' we set the `opts` to:
|
|
82
|
-
* `{default: true, overwrite: opts.overwrite}`
|
|
83
|
-
*
|
|
84
|
-
* If a source is the string 'all' we set the `opts` to:
|
|
85
|
-
* `{all: true, overwrite: opts.overwrite}`
|
|
86
|
-
*
|
|
87
|
-
* If a source is an object with a property of `__copy_opts` which is `true`
|
|
88
|
-
* then the `opts` will be set to the source itself.
|
|
89
|
-
*
|
|
90
|
-
* If a source is an object with a property of `__copy_opts` which is an
|
|
91
|
-
* `object` then the `opts` will be set to the object, and the rest of
|
|
92
|
-
* the properties from the source will be copied as usual.
|
|
93
|
-
*
|
|
94
|
-
* If the source is any other object or function, it will be considered a
|
|
95
|
-
* valid source to copy into the `target`.
|
|
96
|
-
*
|
|
97
|
-
* Anything else will be invalid and will throw an Error.
|
|
98
|
-
*
|
|
99
|
-
* @return {object|function} The `target` will be returned.
|
|
100
|
-
*/
|
|
101
|
-
exports.into = function (target, ...sources)
|
|
102
|
-
{
|
|
103
|
-
let opts = {default: true, overwrite: false}; // default opts.
|
|
104
|
-
|
|
105
|
-
// console.debug("Lum.obj.copyInto()", target, sources);
|
|
106
|
-
|
|
107
|
-
for (let s in sources)
|
|
108
|
-
{
|
|
109
|
-
let source = sources[s];
|
|
110
|
-
const stype = typeof source;
|
|
111
|
-
// console.debug("source", source, stype);
|
|
112
|
-
if (stype === B)
|
|
113
|
-
{
|
|
114
|
-
opts.overwrite = source;
|
|
115
|
-
}
|
|
116
|
-
else if (stype === S)
|
|
117
|
-
{
|
|
118
|
-
if (source === 'default')
|
|
119
|
-
{
|
|
120
|
-
opts = {default: true, overwrite: opts.overwrite};
|
|
121
|
-
}
|
|
122
|
-
else if (source === 'all')
|
|
123
|
-
{
|
|
124
|
-
opts = {all: true, overwrite: opts.overwrite};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
else if (stype === O || stype === F)
|
|
128
|
-
{
|
|
129
|
-
// console.debug("copying properties", source);
|
|
130
|
-
if (source.__copy_opts === true)
|
|
131
|
-
{ // Source is copy options.
|
|
132
|
-
opts = source;
|
|
133
|
-
continue; // Nothing more to do here.
|
|
134
|
-
}
|
|
135
|
-
else if (isObj(source.__copy_opts))
|
|
136
|
-
{ // Copy options included in source.
|
|
137
|
-
opts = source.__copy_opts;
|
|
138
|
-
source = clone(source); // Make a copy of the source.
|
|
139
|
-
delete(source.__copy_opts); // Remove the __copy_opts.
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Copy the properties.
|
|
143
|
-
copyProps(source, target, opts);
|
|
144
|
-
}
|
|
145
|
-
else
|
|
146
|
-
{
|
|
147
|
-
throw new Error("Invalid function/object passed to copyInto()");
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return target;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Clone a simple object, using the `@lumjs/core/obj.clone` function.
|
|
156
|
-
*
|
|
157
|
-
* By default it uses `CLONE.JSON` mode for cloning, but this can
|
|
158
|
-
* be adjusted as desired by passing a `cloneOpts.mode` option.
|
|
159
|
-
*
|
|
160
|
-
* Can also clone extended properties that aren't serialized in JSON.
|
|
161
|
-
*
|
|
162
|
-
* @param {object} object The object or function to clone.
|
|
163
|
-
*
|
|
164
|
-
* @param {(boolean|object)} [copyProperties] Use `copyProps()` as well.
|
|
165
|
-
*
|
|
166
|
-
* After the regular cloning process, call `@lumjs/core/obj.copyProps()`.
|
|
167
|
-
* If this is an `object`, then it's the options for `copyProps()`.
|
|
168
|
-
* In the current codebase, this value is assigned as `cloneOpts.copy` as
|
|
169
|
-
* the actual code to call `copyProps()` was moved to the upstream core
|
|
170
|
-
* `clone()` function a while ago.
|
|
171
|
-
*
|
|
172
|
-
* @param {object} [cloneOpts] Options for the core `clone()`.
|
|
173
|
-
*
|
|
174
|
-
* This can be any options supported by the core `clone()` function.
|
|
175
|
-
*
|
|
176
|
-
* @return {object} A clone of the object.
|
|
177
|
-
*/
|
|
178
|
-
exports.clone = function(object, copyProperties, cloneOpts)
|
|
179
|
-
{
|
|
180
|
-
if (!isObj(cloneOpts))
|
|
181
|
-
{
|
|
182
|
-
cloneOpts = {mode: clone.MODE.JSON};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (copyProperties)
|
|
186
|
-
{
|
|
187
|
-
cloneOpts.copy = copyProperties;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
//console.debug("Lum.obj.clone", object, copyProperties, cloneOpts);
|
|
191
|
-
|
|
192
|
-
return clone(object, cloneOpts);
|
|
193
|
-
}
|
|
194
|
-
|