UNPKG

8.17 kBJavaScriptView Raw
1'use strict';
2
3var util = require('util');
4var union = require('arr-union');
5var define = require('define-property');
6var staticExtend = require('static-extend');
7var isObj = require('isobject');
8
9/**
10 * Expose class utils
11 */
12
13var cu = module.exports;
14
15/**
16 * Expose class utils: `cu`
17 */
18
19cu.isObject = function isObject(val) {
20 return isObj(val) || typeof val === 'function';
21};
22
23/**
24 * Returns true if an array has any of the given elements, or an
25 * object has any of the give keys.
26 *
27 * ```js
28 * cu.has(['a', 'b', 'c'], 'c');
29 * //=> true
30 *
31 * cu.has(['a', 'b', 'c'], ['c', 'z']);
32 * //=> true
33 *
34 * cu.has({a: 'b', c: 'd'}, ['c', 'z']);
35 * //=> true
36 * ```
37 * @param {Object} `obj`
38 * @param {String|Array} `val`
39 * @return {Boolean}
40 * @api public
41 */
42
43cu.has = function has(obj, val) {
44 val = cu.arrayify(val);
45 var len = val.length;
46
47 if (cu.isObject(obj)) {
48 for (var key in obj) {
49 if (val.indexOf(key) > -1) {
50 return true;
51 }
52 }
53
54 var keys = cu.nativeKeys(obj);
55 return cu.has(keys, val);
56 }
57
58 if (Array.isArray(obj)) {
59 var arr = obj;
60 while (len--) {
61 if (arr.indexOf(val[len]) > -1) {
62 return true;
63 }
64 }
65 return false;
66 }
67
68 throw new TypeError('expected an array or object.');
69};
70
71/**
72 * Returns true if an array or object has all of the given values.
73 *
74 * ```js
75 * cu.hasAll(['a', 'b', 'c'], 'c');
76 * //=> true
77 *
78 * cu.hasAll(['a', 'b', 'c'], ['c', 'z']);
79 * //=> false
80 *
81 * cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']);
82 * //=> false
83 * ```
84 * @param {Object|Array} `val`
85 * @param {String|Array} `values`
86 * @return {Boolean}
87 * @api public
88 */
89
90cu.hasAll = function hasAll(val, values) {
91 values = cu.arrayify(values);
92 var len = values.length;
93 while (len--) {
94 if (!cu.has(val, values[len])) {
95 return false;
96 }
97 }
98 return true;
99};
100
101/**
102 * Cast the given value to an array.
103 *
104 * ```js
105 * cu.arrayify('foo');
106 * //=> ['foo']
107 *
108 * cu.arrayify(['foo']);
109 * //=> ['foo']
110 * ```
111 *
112 * @param {String|Array} `val`
113 * @return {Array}
114 * @api public
115 */
116
117cu.arrayify = function arrayify(val) {
118 return val ? (Array.isArray(val) ? val : [val]) : [];
119};
120
121/**
122 * Noop
123 */
124
125cu.noop = function noop() {
126 return;
127};
128
129/**
130 * Returns the first argument passed to the function.
131 */
132
133cu.identity = function identity(val) {
134 return val;
135};
136
137/**
138 * Returns true if a value has a `contructor`
139 *
140 * ```js
141 * cu.hasConstructor({});
142 * //=> true
143 *
144 * cu.hasConstructor(Object.create(null));
145 * //=> false
146 * ```
147 * @param {Object} `value`
148 * @return {Boolean}
149 * @api public
150 */
151
152cu.hasConstructor = function hasConstructor(val) {
153 return cu.isObject(val) && typeof val.constructor !== 'undefined';
154};
155
156/**
157 * Get the native `ownPropertyNames` from the constructor of the
158 * given `object`. An empty array is returned if the object does
159 * not have a constructor.
160 *
161 * ```js
162 * cu.nativeKeys({a: 'b', b: 'c', c: 'd'})
163 * //=> ['a', 'b', 'c']
164 *
165 * cu.nativeKeys(function(){})
166 * //=> ['length', 'caller']
167 * ```
168 *
169 * @param {Object} `obj` Object that has a `constructor`.
170 * @return {Array} Array of keys.
171 * @api public
172 */
173
174cu.nativeKeys = function nativeKeys(val) {
175 if (!cu.hasConstructor(val)) return [];
176 var keys = Object.getOwnPropertyNames(val);
177 if ('caller' in val) keys.push('caller');
178 return keys;
179};
180
181/**
182 * Returns property descriptor `key` if it's an "own" property
183 * of the given object.
184 *
185 * ```js
186 * function App() {}
187 * Object.defineProperty(App.prototype, 'count', {
188 * get: function() {
189 * return Object.keys(this).length;
190 * }
191 * });
192 * cu.getDescriptor(App.prototype, 'count');
193 * // returns:
194 * // {
195 * // get: [Function],
196 * // set: undefined,
197 * // enumerable: false,
198 * // configurable: false
199 * // }
200 * ```
201 *
202 * @param {Object} `obj`
203 * @param {String} `key`
204 * @return {Object} Returns descriptor `key`
205 * @api public
206 */
207
208cu.getDescriptor = function getDescriptor(obj, key) {
209 if (!cu.isObject(obj)) {
210 throw new TypeError('expected an object.');
211 }
212 if (typeof key !== 'string') {
213 throw new TypeError('expected key to be a string.');
214 }
215 return Object.getOwnPropertyDescriptor(obj, key);
216};
217
218/**
219 * Copy a descriptor from one object to another.
220 *
221 * ```js
222 * function App() {}
223 * Object.defineProperty(App.prototype, 'count', {
224 * get: function() {
225 * return Object.keys(this).length;
226 * }
227 * });
228 * var obj = {};
229 * cu.copyDescriptor(obj, App.prototype, 'count');
230 * ```
231 * @param {Object} `receiver`
232 * @param {Object} `provider`
233 * @param {String} `name`
234 * @return {Object}
235 * @api public
236 */
237
238cu.copyDescriptor = function copyDescriptor(receiver, provider, name) {
239 if (!cu.isObject(receiver)) {
240 throw new TypeError('expected receiving object to be an object.');
241 }
242 if (!cu.isObject(provider)) {
243 throw new TypeError('expected providing object to be an object.');
244 }
245 if (typeof name !== 'string') {
246 throw new TypeError('expected name to be a string.');
247 }
248
249 var val = cu.getDescriptor(provider, name);
250 if (val) Object.defineProperty(receiver, name, val);
251};
252
253/**
254 * Copy static properties, prototype properties, and descriptors
255 * from one object to another.
256 *
257 * @param {Object} `receiver`
258 * @param {Object} `provider`
259 * @param {String|Array} `omit` One or more properties to omit
260 * @return {Object}
261 * @api public
262 */
263
264cu.copy = function copy(receiver, provider, omit) {
265 if (!cu.isObject(receiver)) {
266 throw new TypeError('expected receiving object to be an object.');
267 }
268 if (!cu.isObject(provider)) {
269 throw new TypeError('expected providing object to be an object.');
270 }
271 var props = Object.getOwnPropertyNames(provider);
272 var keys = Object.keys(provider);
273 var len = props.length,
274 key;
275 omit = cu.arrayify(omit);
276
277 while (len--) {
278 key = props[len];
279
280 if (cu.has(keys, key)) {
281 define(receiver, key, provider[key]);
282 } else if (!(key in receiver) && !cu.has(omit, key)) {
283 cu.copyDescriptor(receiver, provider, key);
284 }
285 }
286};
287
288/**
289 * Inherit the static properties, prototype properties, and descriptors
290 * from of an object.
291 *
292 * @param {Object} `receiver`
293 * @param {Object} `provider`
294 * @param {String|Array} `omit` One or more properties to omit
295 * @return {Object}
296 * @api public
297 */
298
299cu.inherit = function inherit(receiver, provider, omit) {
300 if (!cu.isObject(receiver)) {
301 throw new TypeError('expected receiving object to be an object.');
302 }
303 if (!cu.isObject(provider)) {
304 throw new TypeError('expected providing object to be an object.');
305 }
306
307 var keys = [];
308 for (var key in provider) {
309 keys.push(key);
310 receiver[key] = provider[key];
311 }
312
313 keys = keys.concat(cu.arrayify(omit));
314
315 var a = provider.prototype || provider;
316 var b = receiver.prototype || receiver;
317 cu.copy(b, a, keys);
318};
319
320/**
321 * Returns a function for extending the static properties,
322 * prototype properties, and descriptors from the `Parent`
323 * constructor onto `Child` constructors.
324 *
325 * ```js
326 * var extend = cu.extend(Parent);
327 * Parent.extend(Child);
328 *
329 * // optional methods
330 * Parent.extend(Child, {
331 * foo: function() {},
332 * bar: function() {}
333 * });
334 * ```
335 * @param {Function} `Parent` Parent ctor
336 * @param {Function} `extend` Optional extend function to handle custom extensions. Useful when updating methods that require a specific prototype.
337 * @param {Function} `Child` Child ctor
338 * @param {Object} `proto` Optionally pass additional prototype properties to inherit.
339 * @return {Object}
340 * @api public
341 */
342
343cu.extend = function() {
344 // keep it lazy, instead of assigning to `cu.extend`
345 return staticExtend.apply(null, arguments);
346};
347
348/**
349 * Bubble up events emitted from static methods on the Parent ctor.
350 *
351 * @param {Object} `Parent`
352 * @param {Array} `events` Event names to bubble up
353 * @api public
354 */
355
356cu.bubble = function(Parent, events) {
357 events = events || [];
358 Parent.bubble = function(Child, arr) {
359 if (Array.isArray(arr)) {
360 events = union([], events, arr);
361 }
362 var len = events.length;
363 var idx = -1;
364 while (++idx < len) {
365 var name = events[idx];
366 Parent.on(name, Child.emit.bind(Child, name));
367 }
368 cu.bubble(Child, events);
369 };
370};