index.js 7.8 KB

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