UNPKG

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