3 var util = require('util');
4 var union = require('arr-union');
5 var define = require('define-property');
6 var staticExtend = require('static-extend');
7 var isObj = require('isobject');
13 var cu = module.exports;
16 * Expose class utils: `cu`
19 cu.isObject = function isObject(val) {
20 return isObj(val) || typeof val === 'function';
24 * Returns true if an array has any of the given elements, or an
25 * object has any of the give keys.
28 * cu.has(['a', 'b', 'c'], 'c');
31 * cu.has(['a', 'b', 'c'], ['c', 'z']);
34 * cu.has({a: 'b', c: 'd'}, ['c', 'z']);
37 * @param {Object} `obj`
38 * @param {String|Array} `val`
43 cu.has = function has(obj, val) {
44 val = cu.arrayify(val);
47 if (cu.isObject(obj)) {
48 for (var key in obj) {
49 if (val.indexOf(key) > -1) {
54 var keys = cu.nativeKeys(obj);
55 return cu.has(keys, val);
58 if (Array.isArray(obj)) {
61 if (arr.indexOf(val[len]) > -1) {
68 throw new TypeError('expected an array or object.');
72 * Returns true if an array or object has all of the given values.
75 * cu.hasAll(['a', 'b', 'c'], 'c');
78 * cu.hasAll(['a', 'b', 'c'], ['c', 'z']);
81 * cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']);
84 * @param {Object|Array} `val`
85 * @param {String|Array} `values`
90 cu.hasAll = function hasAll(val, values) {
91 values = cu.arrayify(values);
92 var len = values.length;
94 if (!cu.has(val, values[len])) {
102 * Cast the given value to an array.
105 * cu.arrayify('foo');
108 * cu.arrayify(['foo']);
112 * @param {String|Array} `val`
117 cu.arrayify = function arrayify(val) {
118 return val ? (Array.isArray(val) ? val : [val]) : [];
125 cu.noop = function noop() {
130 * Returns the first argument passed to the function.
133 cu.identity = function identity(val) {
138 * Returns true if a value has a `contructor`
141 * cu.hasConstructor({});
144 * cu.hasConstructor(Object.create(null));
147 * @param {Object} `value`
152 cu.hasConstructor = function hasConstructor(val) {
153 return cu.isObject(val) && typeof val.constructor !== 'undefined';
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.
162 * cu.nativeKeys({a: 'b', b: 'c', c: 'd'})
163 * //=> ['a', 'b', 'c']
165 * cu.nativeKeys(function(){})
166 * //=> ['length', 'caller']
169 * @param {Object} `obj` Object that has a `constructor`.
170 * @return {Array} Array of keys.
174 cu.nativeKeys = function nativeKeys(val) {
175 if (!cu.hasConstructor(val)) return [];
176 var keys = Object.getOwnPropertyNames(val);
177 if ('caller' in val) keys.push('caller');
182 * Returns property descriptor `key` if it's an "own" property
183 * of the given object.
187 * Object.defineProperty(App.prototype, 'count', {
189 * return Object.keys(this).length;
192 * cu.getDescriptor(App.prototype, 'count');
195 * // get: [Function],
197 * // enumerable: false,
198 * // configurable: false
202 * @param {Object} `obj`
203 * @param {String} `key`
204 * @return {Object} Returns descriptor `key`
208 cu.getDescriptor = function getDescriptor(obj, key) {
209 if (!cu.isObject(obj)) {
210 throw new TypeError('expected an object.');
212 if (typeof key !== 'string') {
213 throw new TypeError('expected key to be a string.');
215 return Object.getOwnPropertyDescriptor(obj, key);
219 * Copy a descriptor from one object to another.
223 * Object.defineProperty(App.prototype, 'count', {
225 * return Object.keys(this).length;
229 * cu.copyDescriptor(obj, App.prototype, 'count');
231 * @param {Object} `receiver`
232 * @param {Object} `provider`
233 * @param {String} `name`
238 cu.copyDescriptor = function copyDescriptor(receiver, provider, name) {
239 if (!cu.isObject(receiver)) {
240 throw new TypeError('expected receiving object to be an object.');
242 if (!cu.isObject(provider)) {
243 throw new TypeError('expected providing object to be an object.');
245 if (typeof name !== 'string') {
246 throw new TypeError('expected name to be a string.');
249 var val = cu.getDescriptor(provider, name);
250 if (val) Object.defineProperty(receiver, name, val);
254 * Copy static properties, prototype properties, and descriptors
255 * from one object to another.
257 * @param {Object} `receiver`
258 * @param {Object} `provider`
259 * @param {String|Array} `omit` One or more properties to omit
264 cu.copy = function copy(receiver, provider, omit) {
265 if (!cu.isObject(receiver)) {
266 throw new TypeError('expected receiving object to be an object.');
268 if (!cu.isObject(provider)) {
269 throw new TypeError('expected providing object to be an object.');
271 var props = Object.getOwnPropertyNames(provider);
272 var keys = Object.keys(provider);
273 var len = props.length,
275 omit = cu.arrayify(omit);
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);
289 * Inherit the static properties, prototype properties, and descriptors
292 * @param {Object} `receiver`
293 * @param {Object} `provider`
294 * @param {String|Array} `omit` One or more properties to omit
299 cu.inherit = function inherit(receiver, provider, omit) {
300 if (!cu.isObject(receiver)) {
301 throw new TypeError('expected receiving object to be an object.');
303 if (!cu.isObject(provider)) {
304 throw new TypeError('expected providing object to be an object.');
308 for (var key in provider) {
310 receiver[key] = provider[key];
313 keys = keys.concat(cu.arrayify(omit));
315 var a = provider.prototype || provider;
316 var b = receiver.prototype || receiver;
321 * Returns a function for extending the static properties,
322 * prototype properties, and descriptors from the `Parent`
323 * constructor onto `Child` constructors.
326 * var extend = cu.extend(Parent);
327 * Parent.extend(Child);
329 * // optional methods
330 * Parent.extend(Child, {
331 * foo: function() {},
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.
343 cu.extend = function() {
344 // keep it lazy, instead of assigning to `cu.extend`
345 return staticExtend.apply(null, arguments);
349 * Bubble up events emitted from static methods on the Parent ctor.
351 * @param {Object} `Parent`
352 * @param {Array} `events` Event names to bubble up
356 cu.bubble = function(Parent, events) {
357 events = events || [];
358 Parent.bubble = function(Child, arr) {
359 if (Array.isArray(arr)) {
360 events = union([], events, arr);
362 var len = events.length;
364 while (++idx < len) {
365 var name = events[idx];
366 Parent.on(name, Child.emit.bind(Child, name));
368 cu.bubble(Child, events);