@lumjs/core 1.0.0-beta.1
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 +17 -0
- package/README.md +27 -0
- package/TODO.md +23 -0
- package/index.js +64 -0
- package/package.json +15 -0
- package/src/context.js +47 -0
- package/src/descriptors.js +243 -0
- package/src/enum.js +123 -0
- package/src/flags.js +45 -0
- package/src/lazy.js +102 -0
- package/src/meta.js +61 -0
- package/src/obj/clone.js +201 -0
- package/src/obj/copyall.js +18 -0
- package/src/obj/copyprops.js +104 -0
- package/src/obj/index.js +18 -0
- package/src/obj/lock.js +64 -0
- package/src/obj/merge.js +74 -0
- package/src/obj/ns.js +194 -0
- package/src/objectid.js +85 -0
- package/src/observable.js +292 -0
- package/src/opt.js +60 -0
- package/src/prop.js +170 -0
- package/src/strings.js +76 -0
- package/src/types.js +545 -0
- package/test/types.js +185 -0
package/src/obj/ns.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// Import required bits here.
|
|
2
|
+
const
|
|
3
|
+
{
|
|
4
|
+
B, root, isObj, needObj, def, nonEmptyArray, notNil
|
|
5
|
+
} = require('../types');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal: need a String or Array
|
|
9
|
+
*/
|
|
10
|
+
function SOA(name, err=true)
|
|
11
|
+
{
|
|
12
|
+
const msg = (typeof name === S)
|
|
13
|
+
? name + ' ' + this.message
|
|
14
|
+
: this.message;
|
|
15
|
+
return err ? (new TypeError(msg)) : msg;
|
|
16
|
+
}
|
|
17
|
+
SOA.message = "must be a string or non-empty array";
|
|
18
|
+
def(SOA, 'toString', function() { return this.message; });
|
|
19
|
+
|
|
20
|
+
exports.SOA = SOA;
|
|
21
|
+
|
|
22
|
+
function nsString(ns, name='Namespace')
|
|
23
|
+
{
|
|
24
|
+
if (nonEmptyArray(ns))
|
|
25
|
+
{
|
|
26
|
+
return ns.join('.');
|
|
27
|
+
}
|
|
28
|
+
else if (typeof ns !== S)
|
|
29
|
+
{
|
|
30
|
+
throw SOA(name);
|
|
31
|
+
}
|
|
32
|
+
return ns;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
exports.nsString = nsString;
|
|
36
|
+
|
|
37
|
+
function nsArray(ns, name='Namespace')
|
|
38
|
+
{
|
|
39
|
+
if (typeof ns === S)
|
|
40
|
+
{
|
|
41
|
+
return ns.split('.');
|
|
42
|
+
}
|
|
43
|
+
else if (!nonEmptyArray(ns))
|
|
44
|
+
{
|
|
45
|
+
throw SOA(name);
|
|
46
|
+
}
|
|
47
|
+
return ns;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
exports.nsArray = nsArray;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get a (nested) property from an object with a given path.
|
|
54
|
+
*
|
|
55
|
+
* @param {object} obj - Object we're looking in.
|
|
56
|
+
* @param {(string|Array)} proppath - Property path we're looking for.
|
|
57
|
+
* Generally a string of dot (`.`) separated nested property names.
|
|
58
|
+
* @param {object} [opts] TBD.
|
|
59
|
+
* @return {*} The property if found, or `opts.default` if not.
|
|
60
|
+
*/
|
|
61
|
+
function getObjectPath(obj, proppath, opts={})
|
|
62
|
+
{
|
|
63
|
+
needObj(obj);
|
|
64
|
+
|
|
65
|
+
if (typeof opts === B)
|
|
66
|
+
opts = {log: opts};
|
|
67
|
+
else if (!isObj(opts))
|
|
68
|
+
opts = {};
|
|
69
|
+
|
|
70
|
+
proppath = nsArray(proppath);
|
|
71
|
+
|
|
72
|
+
for (let p = 0; p < proppath.length; p++)
|
|
73
|
+
{
|
|
74
|
+
const propname = proppath[p];
|
|
75
|
+
if (obj[propname] === undefined)
|
|
76
|
+
{ // End of search, sorry.
|
|
77
|
+
if (opts.log)
|
|
78
|
+
{
|
|
79
|
+
console.error("Object property path not found",
|
|
80
|
+
propname, p, proppath, obj);
|
|
81
|
+
}
|
|
82
|
+
return opts.default;
|
|
83
|
+
}
|
|
84
|
+
obj = obj[propname];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return obj;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
exports.getObjectPath = getObjectPath;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create a nested property path if it does not exist.
|
|
94
|
+
*
|
|
95
|
+
* @param {object} obj - Object the property path is for.
|
|
96
|
+
* @param {(string|Array)} proppath - Property path to create.
|
|
97
|
+
* @param {object} [opts] TBD.
|
|
98
|
+
* @return {*} Generally the last object in the nested path.
|
|
99
|
+
* However the output may vary depending on the options.
|
|
100
|
+
*/
|
|
101
|
+
function setObjectPath(obj, proppath, opts={})
|
|
102
|
+
{
|
|
103
|
+
needObj(obj); needObj(opts);
|
|
104
|
+
proppath = nsArray(proppath);
|
|
105
|
+
|
|
106
|
+
let assign;
|
|
107
|
+
if (isObj(opts.desc))
|
|
108
|
+
{ // An explicit descriptor.
|
|
109
|
+
assign = (o,p,v={}) =>
|
|
110
|
+
{
|
|
111
|
+
const desc = clone(opts.desc);
|
|
112
|
+
desc.value = v;
|
|
113
|
+
def(o,p,desc);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else if (opts.assign)
|
|
117
|
+
{ // Use direct property assignment.
|
|
118
|
+
assign = (o,p,v={}) => o[p] = v;
|
|
119
|
+
}
|
|
120
|
+
else
|
|
121
|
+
{ // Use def with default descriptor.
|
|
122
|
+
assign = (o,p,v={}) => def(o,p,v);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let cns = obj;
|
|
126
|
+
const nsc = proppath.length;
|
|
127
|
+
const lastns = nsc - 1;
|
|
128
|
+
|
|
129
|
+
console.debug("setObjectPath", obj, proppath, opts, nsc, arguments);
|
|
130
|
+
|
|
131
|
+
for (let n = 0; n < nsc; n++)
|
|
132
|
+
{
|
|
133
|
+
const ns = proppath[n];
|
|
134
|
+
|
|
135
|
+
if (cns[ns] === undefined)
|
|
136
|
+
{ // Nothing currently here. Let's fix that.
|
|
137
|
+
if (n == lastns && notNil(opts.value))
|
|
138
|
+
{ // We're at the end and have a value to assign.
|
|
139
|
+
assign(cns, ns, opts.value);
|
|
140
|
+
}
|
|
141
|
+
else
|
|
142
|
+
{ // Create a new empty object.
|
|
143
|
+
assign(cns, ns);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (opts.overwrite && n == lastns && notNil(opts.value))
|
|
147
|
+
{ // We have a value, and overwrite mode is on.
|
|
148
|
+
assign(cns, ns, opts.value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
cns = cns[ns];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (opts.returnThis)
|
|
155
|
+
{
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
else if (opts.returnObj)
|
|
159
|
+
{
|
|
160
|
+
return obj;
|
|
161
|
+
}
|
|
162
|
+
else
|
|
163
|
+
{ // Default is to return the last namespace object.
|
|
164
|
+
return cns;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
exports.setObjectPath = setObjectPath;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get a global namespace path if it exists.
|
|
172
|
+
*
|
|
173
|
+
* - TODO: document this.
|
|
174
|
+
* - TODO: rewrite `ns.get` to use this behind the scenes.
|
|
175
|
+
*/
|
|
176
|
+
function getNamespace(namespaces, opts={})
|
|
177
|
+
{
|
|
178
|
+
return getObjectPath(root, namespaces, opts);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
exports.getNamespace = getNamespace;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Create a global namespace path if it does not exist.
|
|
185
|
+
*
|
|
186
|
+
* - TODO: document this.
|
|
187
|
+
* - TODO: rewrite `ns.add` to use this behind the scenes.
|
|
188
|
+
*/
|
|
189
|
+
function setNamespace(namespaces, opts={})
|
|
190
|
+
{
|
|
191
|
+
return setObjectPath(root, namespaces, opts);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
exports.setNamespace = setNamespace;
|
package/src/objectid.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
const {notNil,def,isNil} = require('./types');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate a large random number.
|
|
6
|
+
*
|
|
7
|
+
* Takes advantage of
|
|
8
|
+
*
|
|
9
|
+
* @returns {number}
|
|
10
|
+
*/
|
|
11
|
+
function randomNumber()
|
|
12
|
+
{
|
|
13
|
+
return Math.floor(Math.random() * Date.now());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
exports.randomNumber = randomNumber;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A class for creating unique identifier objects.
|
|
20
|
+
* Generally only used by my own inernal libraries, thus the name.
|
|
21
|
+
*/
|
|
22
|
+
class InternalObjectId
|
|
23
|
+
{
|
|
24
|
+
/**
|
|
25
|
+
* Build a unique InternalObjectId instance.
|
|
26
|
+
*
|
|
27
|
+
* @param {object} opts - Named options to change default behaviours.
|
|
28
|
+
* @param {string} [opts.name] A friendly name for diagnostics.
|
|
29
|
+
* @param {(string|number)} [opts.id] An internal id value.
|
|
30
|
+
* Default value is a large random number.
|
|
31
|
+
* @param {(string|Symbol)} [opts.property] The property name or Symbol.
|
|
32
|
+
* This is the property that will be set on objects that are tagged
|
|
33
|
+
* with this InternalObjectId. Default is `Symbol(this.id)`.
|
|
34
|
+
* @param {boolean} [opts.useInstance=true] Store object or id value?
|
|
35
|
+
* If this is `true` (now the default), we store the instance itself.
|
|
36
|
+
* If this is `false` (the old default), we store just the `id` value.
|
|
37
|
+
*
|
|
38
|
+
*/
|
|
39
|
+
constructor(opts={})
|
|
40
|
+
{
|
|
41
|
+
this.name = opts.name;
|
|
42
|
+
this.id = opts.id ?? randomNumber();
|
|
43
|
+
this.property = opts.property ?? Symbol(this.id);
|
|
44
|
+
this.useInstance = opts.useInstance ?? true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Tag an object with the ObjectId.
|
|
49
|
+
*
|
|
50
|
+
* @param {*} obj - The object to tag with the unique object id.
|
|
51
|
+
* @returns {*} `obj`
|
|
52
|
+
*/
|
|
53
|
+
tag(obj)
|
|
54
|
+
{
|
|
55
|
+
if (isNil(obj)) throw new TypeError("Cannot tag a null or undefined value");
|
|
56
|
+
const val = this.useInstance ? this : this.id;
|
|
57
|
+
return def(obj, this.property, val);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Is the specified object tagged with this object id?
|
|
62
|
+
*
|
|
63
|
+
* @param {*} obj - The object to test.
|
|
64
|
+
* @returns {boolean} If it's tagged or not.
|
|
65
|
+
*/
|
|
66
|
+
is(obj)
|
|
67
|
+
{
|
|
68
|
+
const want = this.useInstance ? this : this.id;
|
|
69
|
+
return (notNil(obj) && obj[this.property] === want);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate a function that calls `instance.is()`
|
|
74
|
+
*
|
|
75
|
+
* @returns {function} The wrapper function.
|
|
76
|
+
*/
|
|
77
|
+
isFunction()
|
|
78
|
+
{
|
|
79
|
+
const oid = this;
|
|
80
|
+
return function(obj) { return oid.is(obj); }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
exports.InternalObjectId = InternalObjectId;
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// This library was original based on the observable library from riot.js,
|
|
2
|
+
// but has been refactored and expanded a lot since then.
|
|
3
|
+
|
|
4
|
+
const {B,F,S,def,isObj,isComplex} = require('./types');;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Make an object support the observable API.
|
|
8
|
+
*
|
|
9
|
+
* Adds `on()`, `off()`, `one()`, and `trigger()` methods.
|
|
10
|
+
*
|
|
11
|
+
* @param {object} el - The object we are making observable.
|
|
12
|
+
* @param {object} [opts] Options that define behaviours.
|
|
13
|
+
* @param {string} [opts.wildcard='*'] The event name used as a wildcard.
|
|
14
|
+
* @param {boolean} [opts.wrapthis=false] If `true`, `this` will be a wrapper.
|
|
15
|
+
*
|
|
16
|
+
* If 'wrapthis' is true, the function will be called with a wrapper object as
|
|
17
|
+
* the 'this' variable instead of the target object. The wrapper will be:
|
|
18
|
+
*
|
|
19
|
+
* ```js
|
|
20
|
+
* {
|
|
21
|
+
* self: el, // The target object.
|
|
22
|
+
* name: event, // The event name that was triggered.
|
|
23
|
+
* wildcard: bool, // Will be true if this was a wildcard event handler.
|
|
24
|
+
* func: function, // The function being called.
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @param {boolean} [opts.addname=!opts.wrapthis] If `true` callbacks with
|
|
29
|
+
* multiple events will have the name of the triggered event added as
|
|
30
|
+
* the first parameter.
|
|
31
|
+
*
|
|
32
|
+
* @param {boolean} [opts.addis=opts.wrapthis] If `true` add immutable
|
|
33
|
+
* property named `isObservable` which will have a value of `true`.
|
|
34
|
+
* @param {string} [opts.addme] If set, add a method with this name
|
|
35
|
+
* to the object, which is a version of `observable()` with the
|
|
36
|
+
* default options being the same as the current `opts`.
|
|
37
|
+
* If `opts.wrapthis` is `true`, then this defaults to `'makeObservable'`.
|
|
38
|
+
* In any other case it defaults to `null`.
|
|
39
|
+
*
|
|
40
|
+
* @returns {object} el
|
|
41
|
+
*/
|
|
42
|
+
function observable (el={}, opts={})
|
|
43
|
+
{
|
|
44
|
+
//console.debug("observable", el, opts);
|
|
45
|
+
|
|
46
|
+
if (!isComplex(el))
|
|
47
|
+
{ // Don't know how to handle this, sorry.
|
|
48
|
+
throw new Error("non-object sent to observable()");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (observable.is(el))
|
|
52
|
+
{ // It's already observable.
|
|
53
|
+
return el;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (typeof opts === B)
|
|
57
|
+
{ // Assume it's the wrapthis option.
|
|
58
|
+
opts = {wrapthis: opts};
|
|
59
|
+
}
|
|
60
|
+
else if (!isObj(opts))
|
|
61
|
+
{
|
|
62
|
+
opts = {};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const noSpace = /^\S+$/;
|
|
66
|
+
|
|
67
|
+
const wildcard = (typeof opts.wildcard === S
|
|
68
|
+
&& noSpace.test(opts.wildcard))
|
|
69
|
+
? opts.wildcard
|
|
70
|
+
: '*';
|
|
71
|
+
|
|
72
|
+
const wrapthis = (typeof opts.wrapthis === 'boolean')
|
|
73
|
+
? opts.wrapthis
|
|
74
|
+
: false;
|
|
75
|
+
|
|
76
|
+
const addname = (typeof opts.addname === 'boolean')
|
|
77
|
+
? opts.addname
|
|
78
|
+
: !wrapthis;
|
|
79
|
+
|
|
80
|
+
const addis = (typeof opts.addis === 'boolean')
|
|
81
|
+
? opts.addis
|
|
82
|
+
: wrapthis;
|
|
83
|
+
|
|
84
|
+
const validIdent = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
|
|
85
|
+
|
|
86
|
+
const addme = (typeof opts.addme === 'string'
|
|
87
|
+
&& validIdent.test(opts.addme))
|
|
88
|
+
? opts.addme
|
|
89
|
+
: (wrapthis ? 'makeObservable' : null);
|
|
90
|
+
|
|
91
|
+
const slice = Array.prototype.slice;
|
|
92
|
+
|
|
93
|
+
function onEachEvent (e, fn)
|
|
94
|
+
{
|
|
95
|
+
e.replace(/\S+/g, fn);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const add = def(el);
|
|
99
|
+
|
|
100
|
+
function runCallback (name, fn, args)
|
|
101
|
+
{
|
|
102
|
+
if (fn.busy) return;
|
|
103
|
+
fn.busy = 1;
|
|
104
|
+
|
|
105
|
+
let fthis;
|
|
106
|
+
|
|
107
|
+
if (wrapthis)
|
|
108
|
+
{
|
|
109
|
+
const isWild = (name === wildcard);
|
|
110
|
+
const fname = isWild ? (addname ? args[0] : args.shift()) : name;
|
|
111
|
+
fthis =
|
|
112
|
+
{
|
|
113
|
+
self: el,
|
|
114
|
+
name: fname,
|
|
115
|
+
func: fn,
|
|
116
|
+
wildcard: isWild,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
{
|
|
121
|
+
fthis = el;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let fargs = (fn.typed && addname) ? [name].concat(args) : args;
|
|
125
|
+
|
|
126
|
+
fn.apply(fthis, fargs);
|
|
127
|
+
|
|
128
|
+
fn.busy = 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let callbacks = {};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Assign an event handler
|
|
135
|
+
*
|
|
136
|
+
* Listen to the given space separated list of `events` and execute
|
|
137
|
+
* the `callback` each time an event is triggered.
|
|
138
|
+
* @param {string} events - events ids
|
|
139
|
+
* @param {function} fn - callback function
|
|
140
|
+
* @returns {object} el
|
|
141
|
+
*/
|
|
142
|
+
add('on', function(events, fn)
|
|
143
|
+
{
|
|
144
|
+
if (typeof fn !== F)
|
|
145
|
+
{
|
|
146
|
+
console.error("non-function passed to on()");
|
|
147
|
+
return el;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
onEachEvent(events, function(name, pos)
|
|
151
|
+
{
|
|
152
|
+
(callbacks[name] = callbacks[name] || []).push(fn);
|
|
153
|
+
fn.typed = pos > 0;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return el;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Removes the given space separated list of `events` listeners
|
|
161
|
+
*
|
|
162
|
+
* @param {string} events - events ids
|
|
163
|
+
* @param {function} fn - callback function
|
|
164
|
+
* @returns {object} el
|
|
165
|
+
*/
|
|
166
|
+
add('off', function(events, fn)
|
|
167
|
+
{
|
|
168
|
+
if (events === wildcard && !fn)
|
|
169
|
+
{ // Clear all callbacks.
|
|
170
|
+
callbacks = {};
|
|
171
|
+
}
|
|
172
|
+
else
|
|
173
|
+
{
|
|
174
|
+
onEachEvent(events, function(name)
|
|
175
|
+
{
|
|
176
|
+
if (fn)
|
|
177
|
+
{ // Find a specific callback to remove.
|
|
178
|
+
var arr = callbacks[name]
|
|
179
|
+
for (var i = 0, cb; cb = arr && arr[i]; ++i)
|
|
180
|
+
{
|
|
181
|
+
if (cb == fn) arr.splice(i--, 1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else
|
|
185
|
+
{ // Remove all callbacks for this event.
|
|
186
|
+
delete callbacks[name];
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return el
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Add a one-shot event handler.
|
|
195
|
+
*
|
|
196
|
+
* Listen to the given space separated list of `events` and execute
|
|
197
|
+
* the `callback` at most once.
|
|
198
|
+
*
|
|
199
|
+
* @param {string} events - events ids
|
|
200
|
+
* @param {function} fn - callback function
|
|
201
|
+
* @returns {object} el
|
|
202
|
+
*/
|
|
203
|
+
add('one', function(events, fn)
|
|
204
|
+
{
|
|
205
|
+
function on()
|
|
206
|
+
{
|
|
207
|
+
el.off(events, on)
|
|
208
|
+
fn.apply(el, arguments)
|
|
209
|
+
}
|
|
210
|
+
return el.on(events, on);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Execute all callback functions that listen to the given space
|
|
215
|
+
* separated list of `events`
|
|
216
|
+
* @param {string} events - events ids
|
|
217
|
+
* @returns {object} el
|
|
218
|
+
*/
|
|
219
|
+
add('trigger', function(events)
|
|
220
|
+
{
|
|
221
|
+
// getting the arguments
|
|
222
|
+
// skipping the first one
|
|
223
|
+
const args = slice.call(arguments, 1);
|
|
224
|
+
|
|
225
|
+
onEachEvent(events, function(name)
|
|
226
|
+
{
|
|
227
|
+
const fns = slice.call(callbacks[name] || [], 0);
|
|
228
|
+
|
|
229
|
+
for (var i = 0, fn; fn = fns[i]; ++i)
|
|
230
|
+
{
|
|
231
|
+
runCallback(name, fn, args);
|
|
232
|
+
if (fns[i] !== fn) { i-- }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (callbacks[wildcard] && name != wildcard)
|
|
236
|
+
{ // Trigger the wildcard.
|
|
237
|
+
el.trigger.apply(el, ['*', name].concat(args));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return el
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (addis)
|
|
246
|
+
{
|
|
247
|
+
add('isObservable', true);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (addme)
|
|
251
|
+
{ // Add a wrapper for observable() that sets new default options.
|
|
252
|
+
const ourProps = Object.keys(opts);
|
|
253
|
+
add(addme, function (obj=null, mopts={})
|
|
254
|
+
{
|
|
255
|
+
if (ourProps.length > 0)
|
|
256
|
+
{
|
|
257
|
+
for (const prop of ourProps)
|
|
258
|
+
{
|
|
259
|
+
if (mopts[prop] === undefined)
|
|
260
|
+
{
|
|
261
|
+
mopts[prop] = opts[prop];
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return observable(obj, mopts);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return el
|
|
270
|
+
|
|
271
|
+
} // observable()
|
|
272
|
+
|
|
273
|
+
module.exports = observable;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* See if an object appears to be observable.
|
|
277
|
+
*
|
|
278
|
+
* @param {object} obj
|
|
279
|
+
* @returns {boolean}
|
|
280
|
+
*/
|
|
281
|
+
function isObservable(obj)
|
|
282
|
+
{
|
|
283
|
+
return (isObj(obj)
|
|
284
|
+
&& typeof obj.trigger === F
|
|
285
|
+
&& typeof obj.on === F);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Add an 'is()' method to `observable` itself.
|
|
289
|
+
def(observable, 'is', isObservable);
|
|
290
|
+
|
|
291
|
+
// And make it available as `types.doesObservable`
|
|
292
|
+
require('./types').doesObservable = isObservable;
|
package/src/opt.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Simple option handling.
|
|
2
|
+
|
|
3
|
+
const {U,needObj,needType} = require('./types');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* See if a value is *set*, and if not, return a default value.
|
|
7
|
+
*
|
|
8
|
+
* @param {*} opt - The value we are testing.
|
|
9
|
+
* @param {*} defvalue - The default value if opt was null or undefined.
|
|
10
|
+
*
|
|
11
|
+
* @param {boolean} [allowNull=false] If true, allow null to count as *set*.
|
|
12
|
+
* @param {boolean} [isLazy=false] If true, and `defvalue` is a function,
|
|
13
|
+
* use the value from the function as
|
|
14
|
+
* the default.
|
|
15
|
+
* @param {object} [lazyThis=null] If `isLazy` is true, this object will
|
|
16
|
+
* be used as `this` for the function.
|
|
17
|
+
*
|
|
18
|
+
* @return {*} Either the specified `opt` value or the default value.
|
|
19
|
+
*/
|
|
20
|
+
function val(opt, defvalue, allowNull=false, isLazy=false, lazyThis=null)
|
|
21
|
+
{
|
|
22
|
+
if (typeof opt === U || (!allowNull && opt === null))
|
|
23
|
+
{ // The defined value was not "set" as per our rules.
|
|
24
|
+
if (isLazy && typeof defvalue === F)
|
|
25
|
+
{ // Get the default value from a passed in function.
|
|
26
|
+
return defvalue.call(lazyThis);
|
|
27
|
+
}
|
|
28
|
+
return defvalue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return opt;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
exports.val = val;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* See if a property in an object is set.
|
|
38
|
+
*
|
|
39
|
+
* If it is, return the property, otherwise return a default value.
|
|
40
|
+
* This uses the `val()` method, and as such supports the same options.
|
|
41
|
+
* However read the parameters carefully, as the defaults may be different!
|
|
42
|
+
*
|
|
43
|
+
* @param {object} obj - An object to test for a property in.
|
|
44
|
+
* @param {string} optname - The property name we're checking for.
|
|
45
|
+
* @param {*} defvalue - The default value.
|
|
46
|
+
*
|
|
47
|
+
* @param {bool} [allowNull=true] Same as `val()`, but the default is `true`.
|
|
48
|
+
* @param {bool} [isLazy=false] Same as `val()`.
|
|
49
|
+
* @param {object} [lazyThis=opts] Same as `val()`.
|
|
50
|
+
*
|
|
51
|
+
* @return {*} Either the property value, or the default value.
|
|
52
|
+
*/
|
|
53
|
+
function get(obj, optname, defvalue, allowNull=true, isLazy=false, lazyThis=obj)
|
|
54
|
+
{
|
|
55
|
+
needObj(obj);
|
|
56
|
+
needType(S, optname);
|
|
57
|
+
return val(obj[optname], defvalue, allowNull, isLazy, lazyThis);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
exports.get = get;
|