Overview
Coverage100.00 SLOC12226 LOC3278 Missed0

async.js
Coverage100.00 SLOC1198 LOC153 Missed0
  1. /**
  2. * @ignoreCode
  3. * @name async
  4. * @memberOf comb
  5. * @namespace utilities for working with promises.
  6. */
  7. 1var promise = require("./promise.js"),
  8. when = promise.when,
  9. serial = promise.serial,
  10. PromiseList = promise.PromiseList,
  11. base = require("./base"),
  12. merge = base.merge,
  13. isDefined = base.isDefined,
  14. isNumber = base.isNumber,
  15. isString = base.isString,
  16. argsToArray = base.argsToArray,
  17. array = base.array,
  18. isArray = base.isArray,
  19. Promise = promise.Promise,
  20. isFunction = base.isFunction,
  21. sum = array.sum,
  22. avg = array.avg,
  23. sort = array.sort,
  24. min = array.min,
  25. max = array.max,
  26. map = array.map,
  27. forEach = array.forEach,
  28. difference = array.difference,
  29. removeDuplicates = array.removeDuplicates,
  30. unique = array.unique,
  31. rotate = array.rotate,
  32. permutations = array.permutations,
  33. zip = array.zip,
  34. transpose = array.transpose,
  35. valuesAt = array.valuesAt,
  36. union = array.union,
  37. intersect = array.intersect,
  38. powerSet = array.powerSet,
  39. cartesian = array.cartesian,
  40. compact = array.compact,
  41. multiply = array.multiply,
  42. flatten = array.flatten,
  43. invoke = array.invoke;
  44. 1function asyncLoop(promise, cb, scope, limit) {
  45. 52 return when(promise).chain(function (results) {
  46. 52 return serial(map((isArray(results) ? results : [results]), function () {
  47. 211 var args = arguments;
  48. 211 return function () {
  49. 211 try {
  50. 211 return when(cb.apply(scope || null, args));
  51. } catch (e) {
  52. 5 throw e;
  53. }
  54. };
  55. })).chain(function (loopResults) {
  56. 42 return {loopResults:loopResults || [], arr:results};
  57. });
  58. });
  59. }
  60. 1function normalizeResult(result) {
  61. 92 return isArray(result) ? result : isDefined(result) ? [result] : result;
  62. }
  63. /**
  64. * Loops through the results of an promise. The promise can return an array or just a single item.
  65. *
  66. * ```
  67. * function asyncArr(){
  68. * var ret = new comb.Promise();
  69. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  70. * return ret.promise;
  71. * }
  72. *
  73. * comb.async.forEach(asyncArr(), function(){
  74. * //do something with it
  75. * }).then(function(arr){
  76. * console.log(arr); //[1,2,3,4,5];
  77. * });
  78. *
  79. * ```
  80. *
  81. * You may also return a promise from the iterator block.
  82. *
  83. * ```
  84. * var myNewArr = [];
  85. *
  86. * comb.async.forEach(asyncArr(), function(item, index){
  87. * var ret = new comb.Promise();
  88. * process.nextTick(function(){
  89. * myNewArr.push([item, index]);
  90. * ret.callback();
  91. * });
  92. * return ret.promise();
  93. * }).then(function(){
  94. * console.log(myNewArr) //[[1,0], [2,1], [3,2], [4,3], [5,4]]
  95. * });
  96. * ```
  97. *
  98. *
  99. * @param {comb.Promise|Array} promise the promise or array to loop through
  100. * @param {Function} iterator a function to invoke for each item
  101. * @param [scope] optional scope to execute the function in.
  102. * @return {comb.Promise} a promise that is resolved with the original array.
  103. * @static
  104. * @memberof comb.async
  105. * @name forEach
  106. */
  107. 1function asyncForEach(promise, iterator, scope) {
  108. 11 return asyncArray(asyncLoop(promise, iterator, scope).chain(function (results) {
  109. 9 return results.arr;
  110. }));
  111. }
  112. /**
  113. * Loops through the results of an promise resolving with the return value of the iterator function.
  114. * The promise can return an array or just a single item.
  115. *
  116. * ```
  117. * function asyncArr(){
  118. * var ret = new comb.Promise();
  119. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  120. * return ret.promise;
  121. * }
  122. *
  123. * comb.async.map(asyncArr(), function(item){
  124. * return item * 2;
  125. * }).then(function(arr){
  126. * console.log(arr); //[2,4,6,8,10];
  127. * });
  128. *
  129. * ```
  130. *
  131. * You may also return a promise from the iterator block.
  132. *
  133. * ```
  134. * comb.async.map(asyncArr(), function(item, index){
  135. * var ret = new comb.Promise();
  136. * process.nextTick(function(){
  137. * ret.callback(item * 2);
  138. * });
  139. * return ret.promise();
  140. * }).then(function(){
  141. * console.log(myNewArr) //[2,4,6,8,10];
  142. * });
  143. * ```
  144. *
  145. *
  146. * @param {comb.Promise|Array} promise the promise or array to loop through
  147. * @param {Function} iterator a function to invoke for each item
  148. * @param [scope] optional scope to execute the function in.
  149. * @return {comb.Promise} a promise that is resolved with the mapped array.
  150. * @static
  151. * @memberof comb.async
  152. * @name map
  153. */
  154. 1function asyncMap(promise, iterator, scope) {
  155. 15 return asyncArray(asyncLoop(promise, iterator, scope).chain(function (results) {
  156. 13 return results.loopResults;
  157. }));
  158. }
  159. /**
  160. * Loops through the results of an promise resolving with the filtered array.
  161. * The promise can return an array or just a single item.
  162. *
  163. * ```
  164. * function asyncArr(){
  165. * var ret = new comb.Promise();
  166. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  167. * return ret.promise;
  168. * }
  169. *
  170. * comb.async.filter(asyncArr(), function(item){
  171. * return item % 2;
  172. * }).then(function(arr){
  173. * console.log(arr); //[1,3,5];
  174. * });
  175. *
  176. * ```
  177. *
  178. * You may also return a promise from the iterator block.
  179. *
  180. * ```
  181. * comb.async.filter(asyncArr(), function(item, index){
  182. * var ret = new comb.Promise();
  183. * process.nextTick(function(){
  184. * ret.callback(item % 2);
  185. * });
  186. * return ret.promise();
  187. * }).then(function(){
  188. * console.log(myNewArr) //[1,3,5];
  189. * })
  190. * ```
  191. *
  192. *
  193. * @param {comb.Promise|Array} promise the promise or array to loop through
  194. * @param {Function} iterator a function to invoke for each item
  195. * @param [scope] optional scope to execute the function in.
  196. * @return {comb.Promise} a promise that is resolved with the filtered array.
  197. * @static
  198. * @memberof comb.async
  199. * @name filter
  200. */
  201. 1function asyncFilter(promise, iterator, scope) {
  202. 7 return asyncArray(asyncLoop(promise, iterator, scope).chain(function (results) {
  203. 5 var loopResults = results.loopResults, resultArr = results.arr;
  204. 5 return (isArray(resultArr) ? resultArr : [resultArr]).filter(function (res, i) {
  205. 21 return loopResults[i];
  206. });
  207. }));
  208. }
  209. /**
  210. * Loops through the results of an promise resolving with true if every item passed, false otherwise.
  211. * The promise can return an array or just a single item.
  212. *
  213. * ```
  214. * function asyncArr(){
  215. * var ret = new comb.Promise();
  216. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  217. * return ret.promise;
  218. * }
  219. *
  220. * comb.async.every(asyncArr(), function(item){
  221. * return item <= 5;
  222. * }).then(function(every){
  223. * console.log(every); //true
  224. * });
  225. *
  226. * ```
  227. *
  228. * You may also return a promise from the iterator block.
  229. *
  230. * ```
  231. * comb.async.every(asyncArr(), function(item, index){
  232. * var ret = new comb.Promise();
  233. * process.nextTick(function(){
  234. * ret.callback(item == 1);
  235. * });
  236. * return ret.promise();
  237. * }).then(function(){
  238. * console.log(myNewArr) //false;
  239. * })
  240. * ```
  241. *
  242. *
  243. * @param {comb.Promise|Array} promise the promise or array to loop through
  244. * @param {Function} iterator a function to invoke for each item
  245. * @param [scope] optional scope to execute the function in.
  246. * @return {comb.Promise} a promise that is resolved true if every item passed false otherwise.
  247. * @static
  248. * @memberof comb.async
  249. * @name every
  250. */
  251. 1function asyncEvery(promise, iterator, scope) {
  252. 9 return asyncArray(asyncLoop(promise, iterator, scope).chain(function (results) {
  253. 7 return results.loopResults.every(function (res) {
  254. 19 return !!res;
  255. });
  256. }));
  257. }
  258. /**
  259. * Loops through the results of an promise resolving with true if some items passed, false otherwise.
  260. * The promise can return an array or just a single item.
  261. *
  262. * ```
  263. * function asyncArr(){
  264. * var ret = new comb.Promise();
  265. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  266. * return ret.promise;
  267. * }
  268. *
  269. * comb.async.some(asyncArr(), function(item){
  270. * return item == 1;
  271. * }).then(function(every){
  272. * console.log(every); //true
  273. * });
  274. *
  275. * ```
  276. *
  277. * You may also return a promise from the iterator block.
  278. *
  279. * ```
  280. * comb.async.some(asyncArr(), function(item, index){
  281. * var ret = new comb.Promise();
  282. * process.nextTick(function(){
  283. * ret.callback(item > 5);
  284. * });
  285. * return ret.promise();
  286. * }).then(function(){
  287. * console.log(myNewArr) //false;
  288. * })
  289. * ```
  290. *
  291. *
  292. * @param {comb.Promise|Array} promise the promise or array to loop through
  293. * @param {Function} iterator a function to invoke for each item
  294. * @param [scope] optional scope to execute the function in.
  295. * @return {comb.Promise} a promise that is resolved with true if some items passed false otherwise.
  296. * @static
  297. * @memberof comb.async
  298. * @name some
  299. */
  300. 1function asyncSome(promise, iterator, scope) {
  301. 10 return asyncArray(asyncLoop(promise, iterator, scope).chain(function (results) {
  302. 8 return results.loopResults.some(function (res) {
  303. 20 return !!res;
  304. });
  305. }));
  306. }
  307. /**
  308. * Zips results from promises into an array.
  309. *
  310. * ```
  311. * function asyncArr(){
  312. * var ret = new comb.Promise();
  313. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  314. * return ret.promise;
  315. * }
  316. *
  317. * comb.async.zip(asyncArr(), asyncArr()).then(function(zipped){
  318. * console.log(zipped); //[[1,1],[2,2],[3,3], [4,4], [5,5]]
  319. * });
  320. *
  321. * comb.async.array(asyncArr()).zip(asyncArr()).then(function(zipped){
  322. * console.log(zipped); //[[1,1],[2,2],[3,3], [4,4], [5,5]]
  323. * });
  324. *
  325. * ```
  326. *
  327. *
  328. * @return {comb.Promise} an array with all the arrays zipped together.
  329. * @static
  330. * @memberof comb.async
  331. * @name zip
  332. */
  333. 1function asyncZip() {
  334. 1 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  335. 1 return zip.apply(array, normalizeResult(result).map(function (arg) {
  336. 3 return isArray(arg) ? arg : [arg];
  337. }));
  338. }));
  339. }
  340. /**
  341. * Async version of {@link comb.array.avg}.
  342. *
  343. *
  344. * ```
  345. * function asyncArr(){
  346. * var ret = new comb.Promise();
  347. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  348. * return ret.promise;
  349. * }
  350. *
  351. * comb.async.array(asyncArr()).avg().then(function(avg){
  352. * console.log(avg); //3
  353. * })
  354. *
  355. * comb.async.avg(asyncArr()).then(function(avg){
  356. * console.log(avg); //3
  357. * })
  358. *
  359. * ```
  360. *
  361. * @static
  362. * @memberof comb.async
  363. * @name avg
  364. */
  365. 1function asyncAvg(avgPromise) {
  366. 5 return when(avgPromise).chain(function (result) {
  367. 5 return avg.call(array, normalizeResult(result));
  368. });
  369. }
  370. /**
  371. * Async version of {@link comb.array.cartesian}.
  372. *
  373. * ```
  374. * function asyncArr(){
  375. * var ret = new comb.Promise();
  376. * process.nextTick(ret.callback.bind(ret, [1,2]);
  377. * return ret.promise;
  378. * }
  379. *
  380. * comb.async.array(asyncArr()).cartesian([1,2,3]).then(function(avg){
  381. * console.log(avg); //[ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 2 ], [ 2, 3 ] ]
  382. * })
  383. *
  384. * comb.async.cartesian(asyncArr(), [1,2,3]).then(function(avg){
  385. * console.log(avg); //[ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 2 ], [ 2, 3 ] ]
  386. * })
  387. *
  388. * ```
  389. *
  390. * @static
  391. * @memberof comb.async
  392. * @name cartesian
  393. */
  394. 1function asyncCartesian() {
  395. 3 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  396. 3 return cartesian.apply(array, normalizeResult(result).map(function (arg) {
  397. 6 return isArray(arg) ? arg : [arg];
  398. }));
  399. }));
  400. }
  401. /**
  402. * Async version of {@link comb.array.compact}.
  403. *
  404. * ```
  405. * function asyncArr(){
  406. * var ret = new comb.Promise();
  407. * process.nextTick(ret.callback.bind(ret, [1,null,2,null,3,null,4,null,5]);
  408. * return ret.promise;
  409. * }
  410. *
  411. * comb.async.array(asyncArr()).compact().then(function(compacted){
  412. * console.log(compacted); //[1,2,3,4,5]
  413. * })
  414. *
  415. * comb.async.compact(asyncArr()).then(function(compacted){
  416. * console.log(compacted); //[1,2,3,4,5]
  417. * })
  418. *
  419. * ```
  420. *
  421. * @static
  422. * @memberof comb.async
  423. * @name compact
  424. */
  425. 1function asyncCompact(arr) {
  426. 2 return asyncArray(when(arr).chain(function (result) {
  427. 2 return compact.call(array, normalizeResult(result));
  428. }));
  429. }
  430. /**
  431. * Async version of {@link comb.array.difference}.
  432. *
  433. * ```
  434. * function asyncArr(){
  435. * var ret = new comb.Promise();
  436. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  437. * return ret.promise;
  438. * }
  439. *
  440. * comb.async.array(asyncArr()).difference([3,4,5]).then(function(diff){
  441. * console.log(diff); //[1,2]
  442. * })
  443. *
  444. * comb.async.difference(asyncArr(), [3,4,5]).then(function(diff){
  445. * console.log(diff); //[1,2]
  446. * })
  447. *
  448. * ```
  449. *
  450. * @static
  451. * @memberof comb.async
  452. * @name difference
  453. */
  454. 1function asyncDifference() {
  455. 5 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  456. 5 return difference.apply(array, normalizeResult(result).map(function (arg) {
  457. 11 return isArray(arg) ? arg : [arg];
  458. }));
  459. }));
  460. }
  461. /**
  462. * Async version of {@link comb.array.flatten}.
  463. *
  464. * ```
  465. * function asyncArr(){
  466. * var ret = new comb.Promise();
  467. * process.nextTick(ret.callback.bind(ret, [[1],[2],[3],[4],[5]]);
  468. * return ret.promise;
  469. * }
  470. *
  471. * comb.async.array(asyncArr()).flatten().then(function(flat){
  472. * console.log(flat); //[1,2,3,4,5]
  473. * });
  474. *
  475. * comb.async.flatten(asyncArr()).then(function(flat){
  476. * console.log(flat); //[1,2,3,4,5]
  477. * });
  478. *
  479. * ```
  480. *
  481. * @static
  482. * @memberof comb.async
  483. * @name flatten
  484. */
  485. 1function asyncFlatten() {
  486. 4 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  487. 4 return flatten.apply(array, normalizeResult(result).map(function (arg) {
  488. 12 return isArray(arg) ? arg : [arg];
  489. }));
  490. }));
  491. }
  492. /**
  493. * Async version of {@link comb.array.intersect}.
  494. *
  495. * ```
  496. * function asyncArr(){
  497. * var ret = new comb.Promise();
  498. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  499. * return ret.promise;
  500. * }
  501. *
  502. * comb.async.array(asyncArr()).intersect([3,4], [3]).then(function(intersection){
  503. * console.log(intersection); //[3]
  504. * });
  505. *
  506. * comb.async.intersect(asyncArr(), [3,4]).then(function(intersection){
  507. * console.log(intersection); //[3,4]
  508. * });
  509. *
  510. * ```
  511. *
  512. * @static
  513. * @memberof comb.async
  514. * @name intersect
  515. */
  516. 1function asyncIntersect() {
  517. 5 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  518. 5 return intersect.apply(array, normalizeResult(result).map(function (arg) {
  519. 15 return isArray(arg) ? arg : [arg];
  520. }));
  521. }));
  522. }
  523. /**
  524. * Async version of {@link comb.array.max}.
  525. *
  526. * ```
  527. * function asyncArr(){
  528. * var ret = new comb.Promise();
  529. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  530. * return ret.promise;
  531. * }
  532. *
  533. * comb.async.array(asyncArr()).max().then(function(max){
  534. * console.log(max); //5
  535. * })
  536. *
  537. * comb.async.max(asyncArr()).then(function(max){
  538. * console.log(max); //5
  539. * })
  540. *
  541. * ```
  542. *
  543. * @static
  544. * @memberof comb.async
  545. * @name max
  546. */
  547. 1function asyncMax() {
  548. 7 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  549. 7 if (isFunction(last) || isString(last)) {
  550. 3 cmp = last;
  551. } else {
  552. 4 args.push(last);
  553. }
  554. 7 return when.apply(null, args).chain(function (result) {
  555. 7 return max.call(array, normalizeResult(result), cmp);
  556. });
  557. }
  558. /**
  559. * Async version of {@link comb.array.min}.
  560. *
  561. * ```
  562. * function asyncArr(){
  563. * var ret = new comb.Promise();
  564. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  565. * return ret.promise;
  566. * }
  567. *
  568. * comb.async.array(asyncArr()).min().then(function(min){
  569. * console.log(min) //3
  570. * });
  571. *
  572. * comb.async.min(asyncArr()).then(function(min){
  573. * console.log(min) //3
  574. * });
  575. *
  576. * ```
  577. *
  578. * @static
  579. * @memberof comb.async
  580. * @name min
  581. */
  582. 1function asyncMin() {
  583. 7 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  584. 7 if (isFunction(last) || isString(last)) {
  585. 3 cmp = last;
  586. } else {
  587. 4 args.push(last);
  588. }
  589. 7 return when.apply(null, args).chain(function (result) {
  590. 7 return min.call(array, normalizeResult(result), cmp);
  591. });
  592. }
  593. /**
  594. * Async version of {@link comb.array.sort}.
  595. *
  596. * ```
  597. * function asyncArr(){
  598. * var ret = new comb.Promise();
  599. * process.nextTick(ret.callback.bind(ret, [1,3,2,5,4]);
  600. * return ret.promise;
  601. * }
  602. *
  603. * comb.async.array(asyncArr()).sort().then(function(sorted){
  604. * console.log(sorted); //[1,2,3,4,5]
  605. * });
  606. *
  607. * comb.async.sort(asyncArr()).then(function(sorted){
  608. * console.log(sorted); //[1,2,3,4,5]
  609. * });
  610. *
  611. * ```
  612. *
  613. * @static
  614. * @memberof comb.async
  615. * @name sort
  616. */
  617. 1function asyncSort() {
  618. 8 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  619. 8 if (isFunction(last) || isString(last)) {
  620. 4 cmp = last;
  621. } else {
  622. 4 args.push(last);
  623. }
  624. 8 return asyncArray(when.apply(null, args).chain(function (result) {
  625. 8 return sort.call(array, normalizeResult(result), cmp);
  626. }));
  627. }
  628. /**
  629. * Async version of {@link comb.array.multiply}.
  630. *
  631. * ```
  632. * function asyncArr(){
  633. * var ret = new comb.Promise();
  634. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  635. * return ret.promise;
  636. * }
  637. *
  638. * comb.async.array(asyncArr()).multiply(2).then(function(mult){
  639. * console.log(mult); //[1,2,3,4,5,1,2,3,4,5]
  640. * });
  641. *
  642. * comb.async.multiply(asyncArr(),2 ).then(function(mult){
  643. * console.log(mult); //[1,2,3,4,5,1,2,3,4,5]
  644. * });
  645. *
  646. * ```
  647. *
  648. * @static
  649. * @memberof comb.async
  650. * @name multiply
  651. */
  652. 1function asyncMultiply() {
  653. 4 var args = argsToArray(arguments), last = args.pop(), times = null;
  654. 4 if (isNumber(last)) {
  655. 3 times = last;
  656. } else {
  657. 1 args.push(last);
  658. }
  659. 4 return asyncArray(when.apply(null, args).chain(function (result) {
  660. 4 return multiply.call(array, normalizeResult(result), times);
  661. }));
  662. }
  663. /**
  664. * Async version of {@link comb.array.permutations}.
  665. *
  666. * ```
  667. * function asyncArr(){
  668. * var ret = new comb.Promise();
  669. * process.nextTick(ret.callback.bind(ret, [1,2,3]);
  670. * return ret.promise;
  671. * }
  672. *
  673. * comb.async.array(asyncArr()).permutations().then(function(permutations){
  674. * console.log(permutations) //[ [ 1, 2, 3 ],
  675. * // [ 1, 3, 2 ],
  676. * // [ 2, 3, 1 ],
  677. * // [ 2, 1, 3 ],
  678. * // [ 3, 1, 2 ],
  679. * // [ 3, 2, 1 ] ]
  680. * });
  681. *
  682. * comb.async.permutations(asyncArr()).then(function(permutations){
  683. * console.log(permutations) //[ [ 1, 2, 3 ],
  684. * // [ 1, 3, 2 ],
  685. * // [ 2, 3, 1 ],
  686. * // [ 2, 1, 3 ],
  687. * // [ 3, 1, 2 ],
  688. * // [ 3, 2, 1 ] ]
  689. * });
  690. *
  691. * ```
  692. *
  693. * @static
  694. * @memberof comb.async
  695. * @name permutations
  696. */
  697. 1function asyncPermutations() {
  698. 5 var args = argsToArray(arguments), last = args.pop(), times = null;
  699. 5 if (isNumber(last)) {
  700. 4 times = last;
  701. } else {
  702. 1 args.push(last);
  703. }
  704. 5 return asyncArray(when.apply(null, args).chain(function (result) {
  705. 5 return permutations.call(array, normalizeResult(result), times);
  706. }));
  707. }
  708. /**
  709. * Async version of {@link comb.array.powerSet}.
  710. *
  711. * ```
  712. * function asyncArr(){
  713. * var ret = new comb.Promise();
  714. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  715. * return ret.promise;
  716. * }
  717. *
  718. * comb.async.array(asyncArr()).powerSet().then(function(set){
  719. * console.log(set); //[ [], [ 1 ], [ 2 ], [ 1, 2 ], [ 3 ], [ 1, 3 ], [ 2, 3 ], [ 1, 2, 3 ] ]
  720. * });
  721. *
  722. * comb.async.powerSet(asyncArr()).then(function(set){
  723. * console.log(set); //[ [], [ 1 ], [ 2 ], [ 1, 2 ], [ 3 ], [ 1, 3 ], [ 2, 3 ], [ 1, 2, 3 ] ]
  724. * });
  725. *
  726. * ```
  727. *
  728. * @static
  729. * @memberof comb.async
  730. * @name powerSet
  731. */
  732. 1function asyncPowerSet(arr) {
  733. 3 return asyncArray(when(arr).chain(function (result) {
  734. 3 return powerSet.call(array, normalizeResult(result));
  735. }));
  736. }
  737. /**
  738. * Async version of {@link comb.array.removeDuplicates}.
  739. *
  740. * ```
  741. * function asyncArr(){
  742. * var ret = new comb.Promise();
  743. * process.nextTick(ret.callback.bind(ret, [1,2,2,3,3,3,4,4,4]);
  744. * return ret.promise;
  745. * }
  746. *
  747. * comb.async.array(asyncArr()).removeDuplicates().then(function(unique){
  748. * console.log(unique); // [1, 2, 3, 4]
  749. * });
  750. *
  751. * comb.async.removeDuplicates(asyncArray()).then(function(unique){
  752. * console.log(unique); // [1, 2, 3, 4]
  753. * });
  754. * ```
  755. *
  756. * @static
  757. * @memberof comb.async
  758. * @name removeDuplicates
  759. */
  760. 1function asyncRemoveDuplicates(arr) {
  761. 4 return asyncArray(when(arr).chain(function (result) {
  762. 4 return removeDuplicates.call(array, normalizeResult(result));
  763. }));
  764. }
  765. /**
  766. * Async version of {@link comb.array.rotate}.
  767. *
  768. * ```
  769. * function asyncArr(){
  770. * var ret = new comb.Promise();
  771. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  772. * return ret.promise;
  773. * }
  774. *
  775. * comb.async.array(asyncArr()).rotate(2).then(function(rotated){
  776. * console.log(rotated); // [ 3, 4, 5, 1, 2 ]
  777. * });
  778. *
  779. * comb.async.rotate(asyncArr(), 2).then(function(rotated){
  780. * console.log(rotated); // [ 3, 4, 5, 1, 2 ]
  781. * });
  782. *
  783. * ```
  784. *
  785. * @static
  786. * @memberof comb.async
  787. * @name rotate
  788. */
  789. 1function asyncRotate() {
  790. 8 var args = argsToArray(arguments), last = args.pop(), times = null;
  791. 8 if (isNumber(last)) {
  792. 7 times = last;
  793. } else {
  794. 1 args.push(last);
  795. }
  796. 8 return asyncArray(when.apply(null, args).chain(function (result) {
  797. 8 return rotate.call(array, normalizeResult(result), times);
  798. }));
  799. }
  800. /**
  801. * Async version of {@link comb.array.sum}.
  802. *
  803. * ```
  804. * function asyncArr(){
  805. * var ret = new comb.Promise();
  806. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  807. * return ret.promise;
  808. * }
  809. *
  810. * comb.async.array(asyncArr()).sum().then(function(sum){
  811. * console.log(sum) //15
  812. * })
  813. *
  814. * comb.async.sum(asyncArr()).then(function(sum){
  815. * console.log(sum) //15
  816. * })
  817. *
  818. * ```
  819. *
  820. * @static
  821. * @memberof comb.async
  822. * @name sum
  823. */
  824. 1function asyncSum(arr) {
  825. 6 return when(arr).chain(function (result) {
  826. 6 return sum.call(array, normalizeResult(result));
  827. });
  828. }
  829. /**
  830. * Async version of {@link comb.array.transpose}.
  831. *
  832. * ```
  833. * function asyncArr(){
  834. * var ret = new comb.Promise();
  835. * process.nextTick(ret.callback.bind(ret, [[1, 2, 3], [4, 5, 6]]);
  836. * return ret.promise;
  837. * }
  838. *
  839. * comb.async.array(asyncArr()).transpose().then(function(transposed){
  840. * console.log(transposed); //[ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
  841. * });
  842. *
  843. * comb.async.transpose(asyncArr()).then(function(transposed){
  844. * console.log(transposed); //[ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
  845. * });
  846. *
  847. * ```
  848. *
  849. * @static
  850. * @memberof comb.async
  851. * @name transpose
  852. */
  853. 1function asyncTranspose(arr) {
  854. 3 return asyncArray(when(arr).chain(function (result) {
  855. 3 return transpose.call(array, normalizeResult(result));
  856. }));
  857. }
  858. /**
  859. * Async version of {@link comb.array.union}.
  860. *
  861. * ```
  862. * function asyncArr(){
  863. * var ret = new comb.Promise();
  864. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  865. * return ret.promise;
  866. * }
  867. *
  868. * comb.async.array(asyncArr()).union([3],[7], [9,10]).then(function(union){
  869. * console.log(union); //[1,2,3,4,5,7,9,10]
  870. * });
  871. *
  872. * comb.async.union(asyncArr(), [3],[7], [9,10]).then(function(union){
  873. * console.log(union); //[1,2,3,4,5,7,9,10]
  874. * });
  875. *
  876. * ```
  877. *
  878. * @static
  879. * @memberof comb.async
  880. * @name union
  881. */
  882. 1function asyncUnion() {
  883. 2 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  884. 2 return union.apply(array, (normalizeResult(result)).map(function (arg) {
  885. 7 return isArray(arg) ? arg : [arg];
  886. }));
  887. }));
  888. }
  889. /**
  890. * Async version of {@link comb.array.unique}.
  891. *
  892. * ```
  893. * function asyncArr(){
  894. * var ret = new comb.Promise();
  895. * process.nextTick(ret.callback.bind(ret, [1,2,2,3,3,4,4,5]);
  896. * return ret.promise;
  897. * }
  898. *
  899. * comb.async.array(asyncArr()).unique().then(function(unique){
  900. * console.log(unique); //[1,2,3,4,5]
  901. * });
  902. *
  903. * comb.async.unique(asyncArr()).then(function(unique){
  904. * console.log(unique); //[1,2,3,4,5]
  905. * });
  906. *
  907. * ```
  908. *
  909. * @static
  910. * @memberof comb.async
  911. * @name unique
  912. */
  913. 1function asyncUnique() {
  914. 2 return asyncRemoveDuplicates.apply(null, arguments);
  915. }
  916. /**
  917. * Async version of {@link comb.array.valuesAt}.
  918. *
  919. * ```
  920. * function asyncArr(){
  921. * var ret = new comb.Promise();
  922. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  923. * return ret.promise;
  924. * }
  925. *
  926. * comb.async.array(asyncArr()).valuesAt(2,3,4).then(function(values){
  927. * console.log(values); //[3,4,5]
  928. * });
  929. *
  930. * comb.async.valuesAt(asyncArr(), 2,3,4).then(function(values){
  931. * console.log(values); //[3,4,5]
  932. * });
  933. *
  934. * ```
  935. *
  936. * @static
  937. * @memberof comb.async
  938. * @name valuesAt
  939. */
  940. 1function asyncValuesAt(arrPromise) {
  941. 3 var args = argsToArray(arguments, 1);
  942. 3 return asyncArray(when(arrPromise).chain(function (result) {
  943. 3 return when(valuesAt.apply(array, [normalizeResult(result)].concat(args)));
  944. }));
  945. }
  946. /**
  947. * Async version of {@link comb.array.pluck}.
  948. *
  949. * ```
  950. * var when = comb.when,
  951. * array = comb.async.array;
  952. * function asyncArr(){
  953. * var ret = new comb.Promise();
  954. * process.nextTick(ret.callback.bind(ret, [
  955. * {name:{first:when("Fred"), last:"Jones"}, age:when(50), roles:["a", "b", "c"]},
  956. * {name:{first:"Bob", last:"Yukon"}, age:40, roles:when(["b", "c"])},
  957. * {name:{first:"Alice", last:"Palace"}, age:when(35), roles:["c"]},
  958. * {name:{first:when("Johnny"), last:"P."}, age:56, roles:when([])}
  959. * ]);
  960. * return ret.promise;
  961. * }
  962. *
  963. * array(asyncArr()).pluck("name.first").then(function(values){
  964. * console.log(values); //["Fred", "Bob", "Alice", "Johnny"]
  965. * });
  966. *
  967. * pluck(asyncArr(), "age").then(function(values){
  968. * console.log(values); //[50, 40, 35, 56]
  969. * });
  970. *
  971. * ```
  972. *
  973. * @static
  974. * @memberof comb.async
  975. * @name pluck
  976. */
  977. 1function asyncPluck(arrPromise, property) {
  978. 5 var args = argsToArray(arguments, 1);
  979. 5 return asyncArray(when(arrPromise).chain(function (result) {
  980. 5 var prop = property.split(".");
  981. 5 result = normalizeResult(result);
  982. 5 return asyncArray(prop).forEach(function (prop) {
  983. 8 var exec = prop.match(/(\w+)\(\)$/);
  984. 8 return asyncArray(result).map(function (item) {
  985. 32 return exec ? item[exec[1]]() : item[prop];
  986. }).chain(function (res) {
  987. 8 result = res;
  988. });
  989. }).chain(function () {
  990. 5 return result;
  991. });
  992. }));
  993. }
  994. /**
  995. *
  996. * Async version of {@link comb.array.invoke}.
  997. *
  998. * ```
  999. * function person(name, age) {
  1000. * return {
  1001. * getName:function () {
  1002. * return when(name);
  1003. * },
  1004. * getOlder:function () {
  1005. * age++;
  1006. * return when(this);
  1007. * },
  1008. * getAge:function () {
  1009. * return when(age);
  1010. * }
  1011. * };
  1012. * }
  1013. *
  1014. * var arr = [when(person("Bob", 40)), when(person("Alice", 35)), when(person("Fred", 50)), when(person("Johnny", 56))];
  1015. * console.log(comb.async.invoke(arr, "getName")); //["Bob", "Alice", "Fred", "Johnny"]
  1016. * console.log(array(arr).invoke("getOlder").pluck("getAge")) //[41, 36, 51, 57]
  1017. * ```
  1018. * @static
  1019. * @memberOf comb.async
  1020. * @name invoke
  1021. */
  1022. 1function asyncInvoke(arrPromise) {
  1023. 2 var args = argsToArray(arguments, 1);
  1024. 2 return asyncArray(when(arrPromise).chain(function (result) {
  1025. 2 return when(invoke.apply(array, [normalizeResult(result)].concat(args)));
  1026. }));
  1027. }
  1028. 1var asyncExports = (exports.async = {
  1029. array:asyncArray,
  1030. forEach:asyncForEach,
  1031. map:asyncMap,
  1032. filter:asyncFilter,
  1033. every:asyncEvery,
  1034. some:asyncSome,
  1035. zip:asyncZip,
  1036. sum:asyncSum,
  1037. avg:asyncAvg,
  1038. sort:asyncSort,
  1039. min:asyncMin,
  1040. max:asyncMax,
  1041. difference:asyncDifference,
  1042. removeDuplicates:asyncRemoveDuplicates,
  1043. unique:asyncUnique,
  1044. rotate:asyncRotate,
  1045. permutations:asyncPermutations,
  1046. transpose:asyncTranspose,
  1047. valuesAt:asyncValuesAt,
  1048. union:asyncUnion,
  1049. intersect:asyncIntersect,
  1050. powerSet:asyncPowerSet,
  1051. cartesian:asyncCartesian,
  1052. compact:asyncCompact,
  1053. multiply:asyncMultiply,
  1054. flatten:asyncFlatten,
  1055. pluck:asyncPluck,
  1056. invoke:asyncInvoke
  1057. });
  1058. 1var methods = ["forEach", "map", "filter", "some", "every", "zip", "sum", "avg", "sort", "min", "max", "difference", "removeDuplicates", "unique", "rotate",
  1059. "permutations", "transpose", "valuesAt", "union", "intersect", "powerSet", "cartesian", "compact",
  1060. "multiply", "flatten", "pluck", "invoke"];
  1061. /**
  1062. * Exposes array methods on a {@link comb.Promise}.
  1063. *
  1064. * The methods added are.
  1065. *
  1066. * * forEach : See {@link comb.async.forEach}.
  1067. * * map : See {@link comb.async.map}.
  1068. * * filter : See {@link comb.async.filter}.
  1069. * * some : See {@link comb.async.some}.
  1070. * * every : See {@link comb.async.every}.
  1071. * * zip : See {@link comb.async.zip}.
  1072. * * sum : See {@link comb.async.sum}.
  1073. * * avg : See {@link comb.async.avg}.
  1074. * * sort : See {@link comb.async.sort}.
  1075. * * min : See {@link comb.async.min}.
  1076. * * max : See {@link comb.async.max}.
  1077. * * difference : See {@link comb.async.difference}.
  1078. * * removeDuplicates : See {@link comb.async.removeDuplicates}.
  1079. * * unique : See {@link comb.async.unique}.
  1080. * * rotate : See {@link comb.async.rotate}.
  1081. * * permutations : See {@link comb.async.permutations}.
  1082. * * transpose : See {@link comb.async.transpose}.
  1083. * * valuesAt : See {@link comb.async.valuesAt}.
  1084. * * union : See {@link comb.async.union}.
  1085. * * intersect : See {@link comb.async.intersect}.
  1086. * * powerSet : See {@link comb.async.powerSet}.
  1087. * * cartesian : See {@link comb.async.cartesian}.
  1088. * * compact : See {@link comb.async.compact}.
  1089. * * multiply : See {@link comb.async.multiply}.
  1090. * * flatten : See {@link comb.async.flatten}.
  1091. * * pluck : See {@link comb.async.pluck}.
  1092. * * invoke : See {@link comb.async.invoke}.
  1093. *
  1094. * When using this method each of the methods are chainable so you can combine actions.
  1095. *
  1096. * ```
  1097. * var array = comb.async.array;
  1098. * function asyncArr(){
  1099. * var ret = new comb.Promise();
  1100. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  1101. * return ret.promise;
  1102. * }
  1103. *
  1104. * array(asyncArr())
  1105. * .map(function (num, i) {
  1106. * return num * (i + 1);
  1107. * }).filter(function (num) {
  1108. * return num % 2;
  1109. * }).avg().then(function(avg){
  1110. * console.log(avg); //11.666666666666666
  1111. * });
  1112. * ```
  1113. * @param {comb.Promise|[]} p the promise or array to use.
  1114. *
  1115. * @static
  1116. * @memberof comb.async
  1117. * @name array
  1118. */
  1119. 1function asyncArray(p) {
  1120. 626 var ret;
  1121. 626 if (!p || !p.__isArrayAsync__) {
  1122. 410 ret = merge(when(p), {
  1123. promise:function () {
  1124. 216 return asyncArray(this)
  1125. }
  1126. });
  1127. 410 forEach(methods, function (m) {
  1128. 11070 var func = asyncExports[m];
  1129. 11070 ret[m] = function () {
  1130. 143 var args = argsToArray(arguments), mRet = new Promise();
  1131. 143 process.nextTick(function () {
  1132. 143 func.apply(null, [ret].concat(args)).then(mRet);
  1133. });
  1134. 143 return asyncArray(mRet);
  1135. };
  1136. });
  1137. 410 ret.__isArrayAsync__ = true;
  1138. } else {
  1139. 216 ret = p;
  1140. }
  1141. 626 return ret;
  1142. }
base/array.js
Coverage100.00 SLOC1092 LOC358 Missed0
  1. 1var obj = require("./object"),
  2. merge = obj.merge,
  3. misc = require("./misc"),
  4. argsToArray = misc.argsToArray,
  5. string = require("./string"),
  6. isString = string.isString,
  7. number = require("./number.js"),
  8. floor = Math.floor,
  9. abs = Math.abs,
  10. mathMax = Math.max,
  11. mathMin = Math.min;
  12. 1var isArray = exports.isArray = function isArray(obj) {
  13. 15301 return Object.prototype.toString.call(obj) === "[object Array]";
  14. };
  15. 1function isDate(obj) {
  16. 126 var undef;
  17. 126 return (obj !== undef && typeof obj === "object" && obj instanceof Date);
  18. }
  19. 1function cross(num, cros) {
  20. 21 var ret = reduceRight(cros, function (a, b) {
  21. 57 if (!isArray(b)) {
  22. 57 b = [b];
  23. }
  24. 57 b.unshift(num);
  25. 57 a.unshift(b);
  26. 57 return a;
  27. }, []);
  28. 21 return ret;
  29. }
  30. 1function permute(num, cross, length) {
  31. 18 var ret = [];
  32. 18 for (var i = 0; i < cross.length; i++) {
  33. 36 ret.push([num].concat(rotate(cross, i)).slice(0, length));
  34. }
  35. 18 return ret;
  36. }
  37. 1function intersection(a, b) {
  38. 135 var ret = [];
  39. 135 if (isArray(a) && isArray(b)) {
  40. 135 if (a.length && b.length) {
  41. 105 var aOne = a[0];
  42. 105 if (indexOf(b, aOne) !== -1) {
  43. 84 ret = [aOne].concat(intersection(a.slice(1), b));
  44. } else {
  45. 21 ret = intersection(a.slice(1), b);
  46. }
  47. }
  48. }
  49. 135 return ret;
  50. }
  51. 1var _sort = (function () {
  52. 1 var isAll = function (arr, test) {
  53. 63 return every(arr, test);
  54. };
  55. 1 var defaultCmp = function (a, b) {
  56. 90 return a - b;
  57. };
  58. 1 var dateSort = function (a, b) {
  59. 27 return a.getTime() - b.getTime();
  60. };
  61. 1 return function _sort(arr, property) {
  62. 66 var ret = [];
  63. 66 if (isArray(arr)) {
  64. 66 ret = arr.slice();
  65. 66 if (property) {
  66. 30 if (typeof property === "function") {
  67. 3 ret.sort(property);
  68. } else {
  69. 27 ret.sort(function (a, b) {
  70. 81 var aProp = a[property], bProp = b[property];
  71. 81 if (isString(aProp) && isString(bProp)) {
  72. 27 return aProp > bProp ? 1 : aProp < bProp ? -1 : 0;
  73. 54 } else if (isDate(aProp) && isDate(bProp)) {
  74. 27 return aProp.getTime() - bProp.getTime()
  75. } else {
  76. 27 return aProp - bProp;
  77. }
  78. });
  79. }
  80. } else {
  81. 36 if (isAll(ret, isString)) {
  82. 9 ret.sort();
  83. 27 } else if (isAll(ret, isDate)) {
  84. 9 ret.sort(dateSort);
  85. } else {
  86. 18 ret.sort(defaultCmp);
  87. }
  88. }
  89. }
  90. 66 return ret;
  91. };
  92. })();
  93. 1function indexOf(arr, searchElement, fromIndex) {
  94. 299 if (!isArray(arr)) {
  95. 1 throw new TypeError();
  96. }
  97. 298 var t = Object(arr);
  98. 298 var len = t.length >>> 0;
  99. 298 if (len === 0) {
  100. 33 return -1;
  101. }
  102. 265 var n = 0;
  103. 265 if (arguments.length > 2) {
  104. 8 n = Number(arguments[2]);
  105. 8 if (n !== n) { // shortcut for verifying if it's NaN
  106. 2 n = 0;
  107. 6 } else if (n !== 0 && n !== Infinity && n !== -Infinity) {
  108. 2 n = (n > 0 || -1) * floor(abs(n));
  109. }
  110. }
  111. 265 if (n >= len) {
  112. 2 return -1;
  113. }
  114. 263 var k = n >= 0 ? n : mathMax(len - abs(n), 0);
  115. 263 for (; k < len; k++) {
  116. 568 if (k in t && t[k] === searchElement) {
  117. 158 return k;
  118. }
  119. }
  120. 105 return -1;
  121. }
  122. 1function lastIndexOf(arr, searchElement, fromIndex) {
  123. 12 if (!isArray(arr)) {
  124. 1 throw new TypeError();
  125. }
  126. 11 var t = Object(arr);
  127. 11 var len = t.length >>> 0;
  128. 11 if (len === 0) {
  129. 1 return -1;
  130. }
  131. 10 var n = len;
  132. 10 if (arguments.length > 2) {
  133. 8 n = Number(arguments[2]);
  134. 8 if (n !== n) {
  135. 2 n = 0;
  136. 6 } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
  137. 2 n = (n > 0 || -1) * floor(abs(n));
  138. }
  139. }
  140. 10 var k = n >= 0 ? mathMin(n, len - 1) : len - abs(n);
  141. 10 for (; k >= 0; k--) {
  142. 18 if (k in t && t[k] === searchElement) {
  143. 6 return k;
  144. }
  145. }
  146. 4 return -1;
  147. }
  148. 1function filter(arr, iterator, scope) {
  149. 27 if (!isArray(arr) || typeof iterator !== "function") {
  150. 1 throw new TypeError();
  151. }
  152. 26 var t = Object(arr);
  153. 26 var len = t.length >>> 0;
  154. 26 var res = [];
  155. 26 for (var i = 0; i < len; i++) {
  156. 88 if (i in t) {
  157. 88 var val = t[i]; // in case fun mutates this
  158. 88 if (iterator.call(scope, val, i, t)) {
  159. 48 res.push(val);
  160. }
  161. }
  162. }
  163. 26 return res;
  164. }
  165. 1function forEach(arr, iterator, scope) {
  166. 11431 if (!isArray(arr) || typeof iterator !== "function") {
  167. 1 throw new TypeError();
  168. }
  169. 11430 for (var i = 0, len = arr.length; i < len; ++i) {
  170. 83362 iterator.call(scope || arr, arr[i], i, arr);
  171. }
  172. 11430 return arr;
  173. }
  174. 1function every(arr, iterator, scope) {
  175. 331 if (!isArray(arr) || typeof iterator !== "function") {
  176. 1 throw new TypeError();
  177. }
  178. 330 var t = Object(arr);
  179. 330 var len = t.length >>> 0;
  180. 330 for (var i = 0; i < len; i++) {
  181. 383 if (i in t && !iterator.call(scope, t[i], i, t)) {
  182. 300 return false;
  183. }
  184. }
  185. 30 return true;
  186. }
  187. 1function some(arr, iterator, scope) {
  188. 7 if (!isArray(arr) || typeof iterator !== "function") {
  189. 1 throw new TypeError();
  190. }
  191. 6 var t = Object(arr);
  192. 6 var len = t.length >>> 0;
  193. 6 for (var i = 0; i < len; i++) {
  194. 30 if (i in t && iterator.call(scope, t[i], i, t)) {
  195. 2 return true;
  196. }
  197. }
  198. 4 return false;
  199. }
  200. 1function map(arr, iterator, scope) {
  201. 105 if (!isArray(arr) || typeof iterator !== "function") {
  202. 1 throw new TypeError();
  203. }
  204. 104 var t = Object(arr);
  205. 104 var len = t.length >>> 0;
  206. 104 var res = [];
  207. 104 for (var i = 0; i < len; i++) {
  208. 388 if (i in t) {
  209. 388 res.push(iterator.call(scope, t[i], i, t));
  210. }
  211. }
  212. 104 return res;
  213. }
  214. 1function reduce(arr, accumulator, curr) {
  215. 139 if (!isArray(arr) || typeof accumulator !== "function") {
  216. 1 throw new TypeError();
  217. }
  218. 138 var i = 0, l = arr.length >> 0;
  219. 138 if (arguments.length < 3) {
  220. 24 if (l === 0) {
  221. 1 throw new TypeError("Array length is 0 and no second argument");
  222. }
  223. 23 curr = arr[0];
  224. 23 i = 1; // start accumulating at the second element
  225. } else {
  226. 114 curr = arguments[2];
  227. }
  228. 137 while (i < l) {
  229. 393 if (i in arr) {
  230. 393 curr = accumulator.call(undefined, curr, arr[i], i, arr);
  231. }
  232. 393 ++i;
  233. }
  234. 137 return curr;
  235. }
  236. 1function reduceRight(arr, accumulator, curr) {
  237. 27 if (!isArray(arr) || typeof accumulator !== "function") {
  238. 1 throw new TypeError();
  239. }
  240. 26 var t = Object(arr);
  241. 26 var len = t.length >>> 0;
  242. // no value to return if no initial value, empty array
  243. 26 if (len === 0 && arguments.length === 2) {
  244. 1 throw new TypeError();
  245. }
  246. 25 var k = len - 1;
  247. 25 if (arguments.length >= 3) {
  248. 23 curr = arguments[2];
  249. } else {
  250. 2 do {
  251. 2 if (k in arr) {
  252. 2 curr = arr[k--];
  253. 2 break;
  254. }
  255. }
  256. while (true);
  257. }
  258. 25 while (k >= 0) {
  259. 75 if (k in t) {
  260. 75 curr = accumulator.call(undefined, curr, t[k], k, t);
  261. }
  262. 75 k--;
  263. }
  264. 25 return curr;
  265. }
  266. /**
  267. * converts anything to an array
  268. *
  269. * @example
  270. * comb.array.toArray({a : "b", b : "c"}) => [["a","b"], ["b","c"]];
  271. * comb.array.toArray("a") => ["a"]
  272. * comb.array.toArray(["a"]) => ["a"];
  273. * comb.array.toArray() => [];
  274. * comb.array.toArray("a", {a : "b"}) => ["a", ["a", "b"]];
  275. *
  276. * @static
  277. * @memberOf comb.array
  278. */
  279. 1function toArray(o) {
  280. 49 var ret = [];
  281. 49 if (o != null) {
  282. 48 var args = argsToArray(arguments);
  283. 48 if (args.length === 1) {
  284. 46 if (isArray(o)) {
  285. 33 ret = o;
  286. 13 } else if (obj.isHash(o)) {
  287. 4 for (var i in o) {
  288. 6 if (o.hasOwnProperty(i)) {
  289. 6 ret.push([i, o[i]]);
  290. }
  291. }
  292. } else {
  293. 9 ret.push(o);
  294. }
  295. } else {
  296. 2 forEach(args, function (a) {
  297. 4 ret = ret.concat(toArray(a));
  298. });
  299. }
  300. }
  301. 49 return ret;
  302. }
  303. /**
  304. * Sums all items in an array
  305. *
  306. * @example
  307. *
  308. * comb.array.sum([1,2,3]) => 6
  309. * comb.array.sum(["A","B","C"]) => "ABC";
  310. * var d1 = new Date(1999), d2 = new Date(2000), d3 = new Date(3000);
  311. * comb.array.sum([d1,d2,d3]) => "Wed Dec 31 1969 18:00:01 GMT-0600 (CST)"
  312. * + "Wed Dec 31 1969" 18:00:02 GMT-0600 (CST)"
  313. * + "Wed Dec 31 1969 18:00:03 GMT-0600 (CST)"
  314. * comb.array.sum([{},{},{}]) => "[object Object][object Object][object Object]";
  315. *
  316. * @param {Number[]} array the array of numbers to sum
  317. * @static
  318. * @memberOf comb.array
  319. */
  320. 1function sum(array) {
  321. 27 array = array || [];
  322. 27 if (array.length) {
  323. 21 return reduce(array, function (a, b) {
  324. 44 return a + b;
  325. });
  326. } else {
  327. 6 return 0;
  328. }
  329. }
  330. /**
  331. * Averages an array of numbers.
  332. * @example
  333. *
  334. * comb.array.avg([1,2,3]); //2
  335. *
  336. * @param {Number[]} array - an array of numbers
  337. * @return {Number} the average of all the numbers in the array.
  338. * @throws {Error} if the array is not all numbers.
  339. * @static
  340. * @memberOf comb.array
  341. */
  342. 1function avg(arr) {
  343. 13 arr = arr || [];
  344. 13 if (arr.length) {
  345. 8 var total = sum(arr);
  346. 8 if (number.isNumber(total)) {
  347. 4 return total / arr.length;
  348. } else {
  349. 4 throw new Error("Cannot average an array of non numbers.");
  350. }
  351. } else {
  352. 5 return 0;
  353. }
  354. }
  355. /**
  356. * Allows the sorting of an array based on a property name instead. This can also
  357. * act as a sort that does not change the original array.
  358. *
  359. * <b>NOTE:</b> this does not change the original array!
  360. *
  361. * @example
  362. * comb.array.sort([{a : 1}, {a : 2}, {a : -2}], "a"); //[{a : -2}, {a : 1}, {a : 2}];
  363. * @param {Array} arr the array to sort
  364. * @param {String|Function} cmp the property to sort on. Or a function used to compare.
  365. * @return {Array} a copy of the original array that is sorted.
  366. *
  367. * @static
  368. * @memberOf comb.array
  369. */
  370. 1function sort(arr, cmp) {
  371. 24 return _sort(arr, cmp);
  372. }
  373. /**
  374. * Finds that min value of an array. If a second argument is provided and it is a function
  375. * it will be used as a comparator function. If the second argument is a string then it will be used
  376. * as a property look up on each item.
  377. *
  378. * @example
  379. * comb.array.min([{a : 1}, {a : 2}, {a : -2}], "a"); //{a : -2}
  380. * comb.array.min([{a : 1}, {a : 2}, {a : -2}], function(a,b){
  381. * return a.a - b.a
  382. * }); //{a : -2}
  383. *
  384. * @param {Array} arr the array to find the min value on
  385. * @param {String|Function} cmp the property to sort on. Or a function used to compare.
  386. * @return {*}
  387. *
  388. * @static
  389. * @memberOf comb.array
  390. */
  391. 1function min(arr, cmp) {
  392. 21 return _sort(arr, cmp)[0];
  393. }
  394. /**
  395. * Finds that max value of an array. If a second argument is provided and it is a function
  396. * it will be used as a comparator function. If the second argument is a string then it will be used
  397. * as a property look up on each item.
  398. *
  399. * @example
  400. * comb.array.max([{a : 1}, {a : 2}, {a : -2}], "a"); //{a : 2}
  401. * comb.array.max([{a : 1}, {a : 2}, {a : -2}], function(a,b){
  402. * return a.a - b.a
  403. * }); //{a : 2}
  404. *
  405. * @param arr the array to find the max value on
  406. * @param {String|Function} cmp the property to sort on. Or a function used to compare.
  407. * @return {*} the maximum value of the array based on the provided cmp.
  408. *
  409. * @static
  410. * @memberOf comb.array
  411. */
  412. 1function max(arr, cmp) {
  413. 21 return _sort(arr, cmp)[arr.length - 1];
  414. }
  415. /**
  416. * Finds the difference of the two arrays.
  417. *
  418. * @example
  419. *
  420. * comb.array.difference([1,2,3], [2,3]); //[1]
  421. * comb.array.difference(["a","b",3], [3]); //["a","b"]
  422. *
  423. * @param {Array} arr1 the array we are subtracting from
  424. * @param {Array} arr2 the array we are subtracting from arr1
  425. * @return {*} the difference of the arrays.
  426. *
  427. * @static
  428. * @memberOf comb.array
  429. */
  430. 1function difference(arr1, arr2) {
  431. 15 var ret = arr1, args = flatten(argsToArray(arguments, 1));
  432. 15 if (isArray(arr1)) {
  433. 15 ret = filter(arr1, function (a) {
  434. 42 return indexOf(args, a) === -1;
  435. });
  436. }
  437. 15 return ret;
  438. }
  439. /**
  440. * Removes duplicates from an array
  441. *
  442. * @example
  443. *
  444. * comb.array.removeDuplicates([1,1,1]) => [1]
  445. * comb.array.removeDuplicates([1,2,3,2]) => [1,2,3]
  446. *
  447. * @param {Aray} array the array of elements to remove duplicates from
  448. *
  449. * @static
  450. * @memberOf comb.array
  451. */
  452. 1function removeDuplicates(arr) {
  453. 33 if (isArray(arr)) {
  454. 33 return reduce(arr, function (a, b) {
  455. 141 if (indexOf(a, b) === -1) {
  456. 96 return a.concat(b);
  457. } else {
  458. 45 return a;
  459. }
  460. }, []);
  461. }
  462. }
  463. /**
  464. *
  465. * See {@link comb.array.removeDuplicates}
  466. *
  467. * @static
  468. * @memberOf comb.array
  469. */
  470. 1function unique(arr) {
  471. 4 return removeDuplicates(arr);
  472. }
  473. /**
  474. * Rotates an array the number of specified positions
  475. *
  476. * @example
  477. * var arr = ["a", "b", "c", "d"];
  478. * comb.array.rotate(arr) => ["b", "c", "d", "a"]
  479. * comb.array.rotate(arr, 2) => ["c", "d", "a", "b"]);
  480. * comb.array.rotate(arr, 3) => ["d", "a", "b", "c"]);
  481. * comb.array.rotate(arr, 4) => ["a", "b", "c", "d"]);
  482. * comb.array.rotate(arr, -1) => ["d", "a", "b", "c"]);
  483. * comb.array.rotate(arr, -2) => ["c", "d", "a", "b"]);
  484. * comb.array.rotate(arr, -3) => ["b", "c", "d", "a"]);
  485. * comb.array.rotate(arr, -4) => ["a", "b", "c", "d"]);
  486. *
  487. * @param {Array} array the array of elements to remove duplicates from
  488. * @param {Number} numberOfTimes the number of times to rotate the array
  489. *
  490. * @static
  491. * @memberOf comb.array
  492. */
  493. 1function rotate(arr, numberOfTimes) {
  494. 174 var ret = arr.slice();
  495. 174 if (typeof numberOfTimes !== "number") {
  496. 3 numberOfTimes = 1;
  497. }
  498. 174 if (numberOfTimes && isArray(arr)) {
  499. 96 if (numberOfTimes > 0) {
  500. 66 ret.push(ret.shift());
  501. 66 numberOfTimes--;
  502. } else {
  503. 30 ret.unshift(ret.pop());
  504. 30 numberOfTimes++;
  505. }
  506. 96 return rotate(ret, numberOfTimes);
  507. } else {
  508. 78 return ret;
  509. }
  510. }
  511. /**
  512. * Finds all permutations of an array
  513. *
  514. * @example
  515. * var arr = [1,2,3];
  516. * comb.array.permutations(arr) => [[ 1, 2, 3 ],[ 1, 3, 2 ],[ 2, 3, 1 ],
  517. * [ 2, 1, 3 ],[ 3, 1, 2 ],[ 3, 2, 1 ]]
  518. * comb.array.permutations(arr, 2) => [[ 1, 2],[ 1, 3],[ 2, 3],[ 2, 1],[ 3, 1],[ 3, 2]]
  519. * comb.array.permutations(arr, 1) => [[1],[2],[3]]
  520. * comb.array.permutations(arr, 0) => [[]]
  521. * comb.array.permutations(arr, 4) => []
  522. *
  523. * @param {Array} arr the array to permute.
  524. * @param {Number} length the number of elements to permute.
  525. * @static
  526. * @memberOf comb.array
  527. */
  528. 1function permutations(arr, length) {
  529. 15 var ret = [];
  530. 15 if (isArray(arr)) {
  531. 15 var copy = arr.slice(0);
  532. 15 if (typeof length !== "number") {
  533. 3 length = arr.length;
  534. }
  535. 15 if (!length) {
  536. 3 ret = [
  537. []
  538. ];
  539. 12 } else if (length <= arr.length) {
  540. 9 ret = reduce(arr, function (a, b, i) {
  541. 27 var ret;
  542. 27 if (length > 1) {
  543. 18 ret = permute(b, rotate(copy, i).slice(1), length);
  544. } else {
  545. 9 ret = [
  546. [b]
  547. ];
  548. }
  549. 27 return a.concat(ret);
  550. }, []);
  551. }
  552. }
  553. 15 return ret;
  554. }
  555. /**
  556. * Zips to arrays together
  557. *
  558. * @example
  559. * var a = [ 4, 5, 6 ], b = [ 7, 8, 9 ]
  560. * comb.array.zip([1], [2], [3]) => [[ 1, 2, 3 ]]);
  561. * comb.array.zip([1,2], [2], [3]) => [[ 1, 2, 3 ],[2, null, null]]
  562. * comb.array.zip([1,2,3], a, b) => [[1, 4, 7],[2, 5, 8],[3, 6, 9]]
  563. * comb.array.zip([1,2], a, b) => [[1, 4, 7],[2, 5, 8]]
  564. * comb.array.zip(a, [1,2], [8]) => [[4,1,8],[5,2,null],[6,null,null]]
  565. *
  566. * @param arrays variable number of arrays to zip together
  567. *
  568. * @static
  569. * @memberOf comb.array
  570. */
  571. 1function zip() {
  572. 11 var ret = [];
  573. 11 var arrs = argsToArray(arguments);
  574. 11 if (arrs.length > 1) {
  575. 11 var arr1 = arrs.shift();
  576. 11 if (isArray(arr1)) {
  577. 11 ret = reduce(arr1, function (a, b, i) {
  578. 27 var curr = [b];
  579. 27 for (var j = 0; j < arrs.length; j++) {
  580. 54 var currArr = arrs[j];
  581. 54 if (isArray(currArr) && !misc.isUndefined(currArr[i])) {
  582. 44 curr.push(currArr[i]);
  583. } else {
  584. 10 curr.push(null);
  585. }
  586. }
  587. 27 a.push(curr);
  588. 27 return a;
  589. }, []);
  590. }
  591. }
  592. 11 return ret;
  593. }
  594. /**
  595. * Transposes an array of arrays
  596. * @example
  597. *
  598. * comb.array.transpose([[1,2,3], [4,5,6]]) => [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
  599. * comb.array.transpose([[1,2], [3,4], [5,6]]) => [ [ 1, 3, 5 ], [ 2, 4, 6 ] ]
  600. * comb.array.transpose([[1], [3,4], [5,6]]) => [[1]])
  601. *
  602. * @param [Array[Array[]]] arr Array of arrays
  603. *
  604. * @static
  605. * @memberOf comb.array
  606. */
  607. 1function transpose(arr) {
  608. 9 var ret = [];
  609. 9 if (isArray(arr) && arr.length) {
  610. 9 var last;
  611. 9 forEach(arr, function (a) {
  612. 24 if (isArray(a) && (!last || a.length === last.length)) {
  613. 18 forEach(a, function (b, i) {
  614. 39 if (!ret[i]) {
  615. 18 ret[i] = [];
  616. }
  617. 39 ret[i].push(b);
  618. });
  619. 18 last = a;
  620. }
  621. });
  622. }
  623. 9 return ret;
  624. }
  625. /**
  626. * Retrieves values at specified indexes in the array
  627. *
  628. * @example
  629. *
  630. * var arr =["a", "b", "c", "d"]
  631. * comb.array.valuesAt(arr, 1,2,3) => ["b", "c", "d"];
  632. * comb.array.valuesAt(arr, 1,2,3, 4) => ["b", "c", "d", null];
  633. * comb.array.valuesAt(arr, 0,3) => ["a", "d"];
  634. *
  635. * @param {Array} arr the array to retrieve values from
  636. * @param {Number} indexes variable number of indexes to retrieve
  637. *
  638. * @static
  639. * @memberOf comb.array
  640. */
  641. 1function valuesAt(arr, indexes) {
  642. 9 var ret = [];
  643. 9 indexes = argsToArray(arguments);
  644. 9 arr = indexes.shift();
  645. 9 var l = arr.length;
  646. 9 if (isArray(arr) && indexes.length) {
  647. 9 for (var i = 0; i < indexes.length; i++) {
  648. 27 ret.push(arr[indexes[i]] || null);
  649. }
  650. }
  651. 9 return ret;
  652. }
  653. /**
  654. * Union a variable number of arrays together
  655. *
  656. * @example
  657. *
  658. * comb.array.union(['a','b','c'], ['b','c', 'd']) => ["a", "b", "c", "d"]
  659. * comb.array.union(["a"], ["b"], ["c"], ["d"], ["c"]) => ["a", "b", "c", "d"]
  660. *
  661. * @param arrs variable number of arrays to union
  662. *
  663. * @static
  664. * @memberOf comb.array
  665. */
  666. 1function union() {
  667. 6 var ret = [];
  668. 6 var arrs = argsToArray(arguments);
  669. 6 if (arrs.length > 1) {
  670. 6 ret = removeDuplicates(reduce(arrs, function (a, b) {
  671. 21 return a.concat(b);
  672. }, []));
  673. }
  674. 6 return ret;
  675. }
  676. /**
  677. * Finds the intersection of arrays
  678. * NOTE : this function accepts an arbitrary number of arrays
  679. *
  680. * @example
  681. * comb.array.intersect([1,2], [2,3], [2,3,5]) => [2]
  682. * comb.array.intersect([1,2,3], [2,3,4,5], [2,3,5]) => [2,3]
  683. * comb.array.intersect([1,2,3,4], [2,3,4,5], [2,3,4,5]) => [2,3,4]
  684. * comb.array.intersect([1,2,3,4,5], [1,2,3,4,5], [1,2,3]) => [1,2,3]
  685. * comb.array.intersect([[1,2,3,4,5],[1,2,3,4,5],[1,2,3]]) => [1,2,3]
  686. *
  687. * @param {Array} a
  688. * @param {Array} b
  689. *
  690. * @static
  691. * @memberOf comb.array
  692. */
  693. 1function intersect(a, b) {
  694. 15 var collect = [], set;
  695. 15 var args = argsToArray(arguments);
  696. 15 if (args.length > 1) {
  697. //assume we are intersections all the lists in the array
  698. 13 set = args;
  699. } else {
  700. 2 set = args[0];
  701. }
  702. 15 if (isArray(set)) {
  703. 15 var x = set.shift();
  704. 15 collect = reduce(set, function (a, b) {
  705. 30 return intersection(a, b);
  706. }, x);
  707. }
  708. 15 return removeDuplicates(collect);
  709. }
  710. /**
  711. * Finds the powerset of an array
  712. *
  713. * @example
  714. *
  715. * comb.array.powerSet([1,2]) => [
  716. * [],
  717. * [ 1 ],
  718. * [ 2 ],
  719. * [ 1, 2 ]
  720. * ]
  721. * comb.array.powerSet([1,2,3]) => [
  722. * [],
  723. * [ 1 ],
  724. * [ 2 ],
  725. * [ 1, 2 ],
  726. * [ 3 ],
  727. * [ 1, 3 ],
  728. * [ 2, 3 ],
  729. * [ 1, 2, 3 ]
  730. * ]
  731. * comb.array.powerSet([1,2,3,4]) => [
  732. * [],
  733. * [ 1 ],
  734. * [ 2 ],
  735. * [ 1, 2 ],
  736. * [ 3 ],
  737. * [ 1, 3 ],
  738. * [ 2, 3 ],
  739. * [ 1, 2, 3 ],
  740. * [ 4 ],
  741. * [ 1, 4 ],
  742. * [ 2, 4 ],
  743. * [ 1, 2, 4 ],
  744. * [ 3, 4 ],
  745. * [ 1, 3, 4 ],
  746. * [ 2, 3, 4 ],
  747. * [ 1, 2, 3, 4 ]
  748. * ]
  749. *
  750. * @param {Array} arr the array to find the powerset of
  751. *
  752. * @static
  753. * @memberOf comb.array
  754. */
  755. 1function powerSet(arr) {
  756. 9 var ret = [];
  757. 9 if (isArray(arr) && arr.length) {
  758. 9 ret = reduce(arr, function (a, b) {
  759. 27 var ret = map(a, function (c) {
  760. 75 return c.concat(b);
  761. });
  762. 27 return a.concat(ret);
  763. }, [
  764. []
  765. ]);
  766. }
  767. 9 return ret;
  768. }
  769. /**
  770. * Find the cartesian product of two arrays
  771. *
  772. * @example
  773. *
  774. * comb.array.cartesian([1,2], [2,3]) => [
  775. * [1,2],
  776. * [1,3],
  777. * [2,2],
  778. * [2,3]
  779. * ]
  780. * comb.array.cartesian([1,2], [2,3,4]) => [
  781. * [1,2],
  782. * [1,3],
  783. * [1,4] ,
  784. * [2,2],
  785. * [2,3],
  786. * [2,4]
  787. * ]
  788. * comb.array.cartesian([1,2,3], [2,3,4]) => [
  789. * [1,2],
  790. * [1,3],
  791. * [1,4] ,
  792. * [2,2],
  793. * [2,3],
  794. * [2,4] ,
  795. * [3,2],
  796. * [3,3],
  797. * [3,4]
  798. * ]
  799. *
  800. * @param {Array} a
  801. * @param {Array} b
  802. *
  803. * @static
  804. * @memberOf comb.array
  805. */
  806. 1function cartesian(a, b) {
  807. 30 var ret = [];
  808. 30 if (isArray(a) && isArray(b) && a.length && b.length) {
  809. 21 ret = cross(a[0], b).concat(cartesian(a.slice(1), b));
  810. }
  811. 30 return ret;
  812. }
  813. /**
  814. * Compacts an array removing null or undefined objects from the array.
  815. *
  816. * @example
  817. *
  818. * var x;
  819. * comb.array.compact([1,null,null,x,2]) => [1,2]
  820. * comb.array.compact([1,2]) => [1,2]
  821. *
  822. * @param {Array} arr
  823. * @static
  824. * @memberOf comb.array
  825. */
  826. 1function compact(arr) {
  827. 6 var ret = [];
  828. 6 if (isArray(arr) && arr.length) {
  829. 6 ret = filter(arr, function (item) {
  830. 21 return !misc.isUndefinedOrNull(item);
  831. });
  832. }
  833. 6 return ret;
  834. }
  835. /**
  836. * Creates a new array that is the result of the array concated the number of
  837. * times. If times is not specified or it equals 0 then it defaults to 1.
  838. * @param {Array} arr the array to multiply.
  839. * @param {Number} [times=1] the number of times to multiple the array.
  840. * @return {Array} a new array that is the result of the array multiplied the number of times specified.
  841. *
  842. * @static
  843. * @memberOf comb.array
  844. */
  845. 1function multiply(arr, times) {
  846. 12 times = number.isNumber(times) ? times : 1;
  847. 12 if (!times) {
  848. //make sure times is greater than zero if it is zero then dont multiply it
  849. 3 times = 1;
  850. }
  851. 12 arr = toArray(arr || []);
  852. 12 var ret = [], i = 0;
  853. 12 while (++i <= times) {
  854. 15 ret = ret.concat(arr);
  855. }
  856. 12 return ret;
  857. }
  858. /**
  859. * Flatten multiple arrays into a single array
  860. *
  861. * @example
  862. *
  863. * comb.array.flatten([1,2], [2,3], [3,4]) => [1,2,2,3,3,4]
  864. * comb.array.flatten([1,"A"], [2,"B"], [3,"C"]) => [1,"A",2,"B",3,"C"]
  865. *
  866. * @param array
  867. *
  868. * @static
  869. * @memberOf comb.array
  870. */
  871. 1function flatten(arr) {
  872. 29 var set;
  873. 29 var args = argsToArray(arguments);
  874. 29 if (args.length > 1) {
  875. //assume we are intersections all the lists in the array
  876. 10 set = args;
  877. } else {
  878. 19 set = toArray(arr);
  879. }
  880. 29 return reduce(set, function (a, b) {
  881. 58 return a.concat(b);
  882. }, []);
  883. }
  884. /**
  885. * Plucks values from an array. Effectevily the same as using a array.map implementation. However the porperty specified supports
  886. * a "dot" notation to access nested properties.
  887. *
  888. * @example
  889. *
  890. * var arr = [
  891. * {name:{first:"Fred", last:"Jones"}, age:50, roles:["a", "b", "c"]},
  892. * {name:{first:"Bob", last:"Yukon"}, age:40, roles:["b", "c"]},
  893. * {name:{first:"Alice", last:"Palace"}, age:35, roles:["c"]},
  894. * {name:{first:"Johnny", last:"P."}, age:56, roles:[]}
  895. * ];
  896. * console.log(comb.array.pluck(arr, "name.first")); //["Fred", "Bob", "Alice", "Johnny"]
  897. * console.log(comb.array.pluck(arr, "age")); //[50, 40, 35, 56]
  898. * console.log(comb.array.pluck(arr, "roles.length")); //[3, 2, 1, 0]
  899. * console.log(comb.array.pluck(arr, "roles.0")); //["a", "b", "c", undefined]
  900. *
  901. * @param {Array} arr the array to pluck values from
  902. * @param {String} prop the property to retrieve from each item in the array
  903. * @return {Array} an array containing the plucked properties.
  904. *
  905. * @static
  906. * @memberOf comb.array
  907. */
  908. 1function pluck(arr, prop) {
  909. 7 prop = prop.split(".");
  910. 7 var result = arr.slice(0);
  911. 7 forEach(prop, function (prop) {
  912. 13 var exec = prop.match(/(\w+)\(\)$/);
  913. 13 result = map(result, function (item) {
  914. 52 return exec ? item[exec[1]]() : item[prop];
  915. });
  916. });
  917. 7 return result;
  918. }
  919. /**
  920. * Invokes the method specified in the scope of each item in the array. If you supply addional arguments they will
  921. * be applied to the function.
  922. *
  923. * ```
  924. *
  925. * function person(name, age){
  926. * return {
  927. * getName : function(){
  928. * return name;
  929. * },
  930. * setName : function(newName){
  931. * name = newName;
  932. * },
  933. *
  934. * getOlder : function(){
  935. * age++;
  936. * return this;
  937. * },
  938. *
  939. * getAge : function(){
  940. * return age;
  941. * }
  942. * };
  943. * }
  944. *
  945. * var arr = [person("Bob", 40), person("Alice", 35), person("Fred", 50), person("Johnny", 56)];
  946. *
  947. * console.log(comb.array.invoke(arr, "getName")); //["Bob", "Alice", "Fred", "Johnny"]
  948. *
  949. * console.log(comb.array(arr).invoke("getOlder").invoke("getAge")); //[41, 36, 51, 57];
  950. *
  951. * ```
  952. *
  953. * @param {Array} arr the array to iterate and invoke the functions on
  954. * @param {String|Function} func the function to invoke, if it is a string then the function will be looked up on the
  955. * each item in the array and invoked
  956. * @param [args=null] variable number of arguments to apply to the function
  957. * @return {Array} the return values of the functions invoked.
  958. *
  959. * @static
  960. * @memberOf comb.array
  961. */
  962. 1function invoke(arr, func, args) {
  963. 8 args = argsToArray(arguments, 2);
  964. 8 return map(arr, function (item) {
  965. 32 var exec = isString(func) ? item[func] : func;
  966. 32 return exec.apply(item, args);
  967. });
  968. }
  969. 1var comb = exports;
  970. /**
  971. * @namespace Utilities for working with arrays.
  972. *
  973. * The `comb.array` namespace can be used to decorate arrays with additional chainable funcitonality.
  974. *
  975. * ```
  976. *
  977. * var arr = comb.array([1,3,2,5,4,6]);
  978. * console.log(arr.sum()) //21
  979. * console.log(arr.sort()) //[1,2,3,4,5,6]
  980. * console.log(arr.min()) //[1]
  981. * console.log(arr.max()) [6]
  982. *
  983. * ```
  984. *
  985. * @function
  986. * @ignoreCode
  987. */
  988. 1comb.array = {
  989. toArray:toArray,
  990. sum:sum,
  991. avg:avg,
  992. sort:sort,
  993. min:min,
  994. max:max,
  995. difference:difference,
  996. removeDuplicates:removeDuplicates,
  997. unique:unique,
  998. rotate:rotate,
  999. permutations:permutations,
  1000. zip:zip,
  1001. transpose:transpose,
  1002. valuesAt:valuesAt,
  1003. union:union,
  1004. intersect:intersect,
  1005. powerSet:powerSet,
  1006. cartesian:cartesian,
  1007. compact:compact,
  1008. multiply:multiply,
  1009. flatten:flatten,
  1010. pluck:pluck,
  1011. invoke:invoke,
  1012. forEach:forEach,
  1013. map:map,
  1014. filter:filter,
  1015. reduce:reduce,
  1016. reduceRight:reduceRight,
  1017. some:some,
  1018. every:every,
  1019. indexOf:indexOf,
  1020. lastIndexOf:lastIndexOf
  1021. };
base/broadcast.js
Coverage100.00 SLOC171 LOC63 Missed0
  1. 1var func = require("./functions"),
  2. obj = require("./object");
  3. 1var comb = exports;
  4. 1var wrapper = function() {
  5. 8 return function() {
  6. 31 var c = arguments.callee, listeners = c.__listeners, func = c.func, r;
  7. 31 if (func) {
  8. 31 r = func.apply(this, arguments);
  9. }
  10. 31 for (var i = 0; i < listeners.length; i++) {
  11. 39 var lis = listeners[i];
  12. 39 if (lis) {
  13. 30 lis.apply(this, arguments);
  14. }
  15. }
  16. 31 return r;
  17. }
  18. };
  19. 1var listeners = {};
  20. 1obj.merge(comb, {
  21. /**@lends comb*/
  22. /**
  23. * Disconnects a listener to a function
  24. * @param {handle} A handle returned from comb.connect
  25. */
  26. disconnect : function(handle) {
  27. 10 if (handle && handle.length == 3) {
  28. 9 var obj = handle[0], method = handle[1], cb = handle[2];
  29. 9 if (typeof method != "string") throw "comb.disconnect : When calling disconnect the method must be string";
  30. 9 var scope = obj || global, ls;
  31. 9 if (typeof scope[method] == "function") {
  32. 8 ls = scope[method].__listeners;
  33. 8 if (ls && cb-- > 0) {
  34. //we dont want to splice it because our indexing will get off
  35. 8 ls[cb] = null;
  36. }
  37. } else {
  38. 1 throw new Error("unknown method " + method + " in object " + obj);
  39. }
  40. } else {
  41. 1 throw "comb.disconnect : invalid handle"
  42. }
  43. },
  44. /**
  45. * Function to listen when other functions are called
  46. *
  47. * @example
  48. *
  49. * comb.connect(obj, "event", myfunc);
  50. * comb.connect(obj, "event", "log", console);
  51. *
  52. * @param {Object} obj the object in which the method you are connecting to resides
  53. * @param {String} method the name of the method to connect to
  54. * @param {Function} cb the function to callback
  55. * @param {Object} [scope] the scope to call the specified cb in
  56. *
  57. * @returns {Array} handle to pass to {@link comb.disconnect}
  58. */
  59. connect : function(obj, method, cb, scope) {
  60. 14 var index;
  61. 14 if (typeof method != "string") throw new Error("When calling connect the method must be string");
  62. 14 if (!func.isFunction(cb)) throw new Error("When calling connect callback must be a string");
  63. 14 var scope = obj || global, listeners, newMethod;
  64. 14 if (typeof scope[method] == "function") {
  65. 13 listeners = scope[method].__listeners;
  66. 13 if (!listeners) {
  67. 8 newMethod = wrapper();
  68. 8 newMethod.func = obj[method];
  69. 8 listeners = (newMethod.__listeners = []);
  70. 8 scope[method] = newMethod;
  71. }
  72. 13 index = listeners.push(cb);
  73. } else {
  74. 1 throw new Error("unknow method " + method + " in object " + obj);
  75. }
  76. 13 return [obj, method, index];
  77. },
  78. /**
  79. * Broadcasts an event to all listeners
  80. * NOTE : the function takes a variable number of arguments
  81. * i.e. all arguments after the topic will be passed to the listeners
  82. *
  83. * @example
  84. *
  85. *
  86. * comb.broadcast("hello", "hello world");
  87. * //the args "hello" and "world" will be passed to any listener of the topic
  88. * //"hello"
  89. * comb.broadcast("hello", "hello", "world");
  90. *
  91. * @param {String} topic the topic to brodcast
  92. * @param params the information to bradcast
  93. */
  94. broadcast : function() {
  95. 4 var args = Array.prototype.slice.call(arguments);
  96. 4 var topic = args.splice(0, 1)[0];
  97. 4 if (topic) {
  98. 4 var list = listeners[topic];
  99. 4 if (list) {
  100. 4 for (var i = list.length - 1; i >= 0; i--) {
  101. 4 var han = list[i], cb = han.cb;
  102. 4 if (cb) {
  103. 4 cb.apply(this, args);
  104. }
  105. }
  106. }
  107. }
  108. },
  109. /**
  110. * Listen for the broadcast of certain events
  111. *
  112. * @example
  113. * comb.listen("hello", function(arg1, arg2){
  114. * console.log(arg1);
  115. * console.log(arg2);
  116. * });
  117. *
  118. * @param {String} topic the topic to listen for
  119. * @param {Function} callback the funciton to call when the topic is published
  120. *
  121. * @returns a handle to pass to {@link comb.unListen}
  122. */
  123. listen : function(topic, callback) {
  124. 3 if (!func.isFunction(callback)) throw new Error("callback must be a function");
  125. 3 var handle = {
  126. topic : topic,
  127. cb : callback,
  128. pos : null
  129. };
  130. 3 var list = listeners[topic];
  131. 3 if (!list) {
  132. 3 list = (listeners[topic] = []);
  133. }
  134. 3 list.push(handle);
  135. 3 handle.pos = list.length - 1;
  136. 3 return handle;
  137. },
  138. /**
  139. * Disconnects a listener
  140. *
  141. * @param handle a handle returned from {@link comb.listen}
  142. */
  143. unListen : function(handle) {
  144. 2 if (handle) {
  145. 2 var topic = handle.topic, list = listeners[topic];
  146. 2 if (list) {
  147. 2 for (var i = list.length - 1; i >= 0; i--) {
  148. 2 if (list[i] == handle) {
  149. 2 list.splice(i, 1);
  150. }
  151. }
  152. 2 if (!list.length) {
  153. 2 delete listeners[topic];
  154. }
  155. }
  156. }
  157. }
  158. });
base/date.js
Coverage100.00 SLOC1106 LOC356 Missed0
  1. 1var string = require("./string").string;
  2. /**
  3. * @ignore
  4. * Based on DOJO Date Implementation
  5. *
  6. * Dojo is available under *either* the terms of the modified BSD license *or* the
  7. * Academic Free License version 2.1. As a recipient of Dojo, you may choose which
  8. * license to receive this code under (except as noted in per-module LICENSE
  9. * files). Some modules may not be the copyright of the Dojo Foundation. These
  10. * modules contain explicit declarations of copyright in both the LICENSE files in
  11. * the directories in which they reside and in the code itself. No external
  12. * contributions are allowed under licenses which are fundamentally incompatible
  13. * with the AFL or BSD licenses that Dojo is distributed under.
  14. *
  15. */
  16. 1var floor = Math.floor, round = Math.round, min = Math.min, pow = Math.pow, ceil = Math.ceil, abs = Math.abs;
  17. 1var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  18. 1var monthAbbr = ["Jan.", "Feb.", "Mar.", "Apr.", "May.", "Jun.", "Jul.", "Aug.", "Sep.", "Oct.", "Nov.", "Dec."];
  19. 1var monthLetter = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
  20. 1var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  21. 1var dayAbbr = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  22. 1var dayLetter = ["S", "M", "T", "W", "T", "F", "S"];
  23. 1var eraNames = ["Before Christ", "Anno Domini"];
  24. 1var eraAbbr = ["BC", "AD"];
  25. 1var eraLetter = ["B", "A"];
  26. 1var comb = exports, date;
  27. /**
  28. * Determines if obj is a Date
  29. *
  30. * @param {Anything} obj the thing to test if it is a Date
  31. * @memberOf comb
  32. * @returns {Boolean} true if it is a Date false otherwise
  33. */
  34. 1comb.isDate = function (obj) {
  35. 1233 var undef;
  36. 1233 return (obj !== undef && typeof obj === "object" && obj instanceof Date);
  37. };
  38. 1function getDayOfYear(/*Date*/dateObject, utc) {
  39. // summary: gets the day of the year as represented by dateObject
  40. 6 return date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject, null, utc) + 1; // Number
  41. }
  42. 1function getWeekOfYear(/*Date*/dateObject, /*Number*/firstDayOfWeek, utc) {
  43. 4 firstDayOfWeek = firstDayOfWeek || 0;
  44. 4 var fullYear = dateObject[utc ? "getUTCFullYear" : "getFullYear"]();
  45. 4 var firstDayOfYear = new Date(fullYear, 0, 1).getDay(),
  46. adj = (firstDayOfYear - firstDayOfWeek + 7) % 7,
  47. week = floor((getDayOfYear(dateObject) + adj - 1) / 7);
  48. // if year starts on the specified day, start counting weeks at 1
  49. 4 if (firstDayOfYear == firstDayOfWeek) {
  50. 4 week++;
  51. }
  52. 4 return week; // Number
  53. }
  54. 1function buildDateEXP(pattern, tokens) {
  55. 55 return pattern.replace(/([a-z])\1*/ig,
  56. function (match) {
  57. // Build a simple regexp. Avoid captures, which would ruin the tokens list
  58. 197 var s,
  59. c = match.charAt(0),
  60. l = match.length,
  61. p2 = '', p3 = '';
  62. 197 p2 = '0?';
  63. 197 p3 = '0{0,2}';
  64. 197 switch (c) {
  65. case 'y':
  66. 33 s = '\\d{2,4}';
  67. 33 break;
  68. case 'M':
  69. 33 s = (l > 2) ? '\\S+?' : '1[0-2]|' + p2 + '[1-9]';
  70. 33 break;
  71. case 'D':
  72. 4 s = '[12][0-9][0-9]|3[0-5][0-9]|36[0-6]|' + p3 + '[1-9][0-9]|' + p2 + '[1-9]';
  73. 4 break;
  74. case 'd':
  75. 31 s = '3[01]|[12]\\d|' + p2 + '[1-9]';
  76. 31 break;
  77. case 'w':
  78. 4 s = '[1-4][0-9]|5[0-3]|' + p2 + '[1-9]';
  79. 4 break;
  80. case 'E':
  81. 12 s = '\\S+';
  82. 12 break;
  83. case 'h': //hour (1-12)
  84. 10 s = '1[0-2]|' + p2 + '[1-9]';
  85. 10 break;
  86. case 'K': //hour (1-24)
  87. 4 s = '1[01]|' + p2 + '\\d';
  88. 4 break;
  89. case 'H': //hour (0-23)
  90. 2 s = '1\\d|2[0-3]|' + p2 + '\\d';
  91. 2 break;
  92. case 'k': //hour (1-24)
  93. 2 s = '1\\d|2[0-4]|' + p2 + '[1-9]';
  94. 2 break;
  95. case 'm':
  96. case 's':
  97. 30 s = '[0-5]\\d';
  98. 30 break;
  99. case 'S':
  100. 10 s = '\\d{' + l + '}';
  101. 10 break;
  102. case 'a':
  103. 6 var am = 'AM', pm = 'PM';
  104. 6 s = am + '|' + pm;
  105. 6 if (am != am.toLowerCase()) {
  106. 6 s += '|' + am.toLowerCase();
  107. }
  108. 6 if (pm != pm.toLowerCase()) {
  109. 6 s += '|' + pm.toLowerCase();
  110. }
  111. 6 s = s.replace(/\./g, "\\.");
  112. 6 break;
  113. case 'v':
  114. case 'z':
  115. case 'Z':
  116. case 'G':
  117. case 'q':
  118. case 'Q':
  119. 12 s = ".*";
  120. 12 break;
  121. default:
  122. 4 s = c == " " ? "\\s*" : c + "*";
  123. // console.log("parse of date format, pattern=" + pattern);
  124. }
  125. 197 if (tokens) {
  126. 197 tokens.push(match);
  127. }
  128. 197 return "(" + s + ")"; // add capture
  129. }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE.
  130. }
  131. /**
  132. * @namespace Utilities for Dates
  133. */
  134. 1comb.date = {
  135. /**@lends comb.date*/
  136. /**
  137. * Returns the number of days in the month of a date
  138. *
  139. * @example
  140. *
  141. * comb.date.getDaysInMonth(new Date(2006, 1, 1)); //28
  142. * comb.date.getDaysInMonth(new Date(2004, 1, 1)); //29
  143. * comb.date.getDaysInMonth(new Date(2006, 2, 1)); //31
  144. * comb.date.getDaysInMonth(new Date(2006, 3, 1)); //30
  145. * comb.date.getDaysInMonth(new Date(2006, 4, 1)); //31
  146. * comb.date.getDaysInMonth(new Date(2006, 5, 1)); //30
  147. * comb.date.getDaysInMonth(new Date(2006, 6, 1)); //31
  148. * @param {Date} dateObject the date containing the month
  149. * @return {Number} the number of days in the month
  150. */
  151. getDaysInMonth:function (/*Date*/dateObject) {
  152. // summary:
  153. // Returns the number of days in the month used by dateObject
  154. 36 var month = dateObject.getMonth();
  155. 36 var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  156. 36 if (month == 1 && date.isLeapYear(dateObject)) {
  157. 6 return 29;
  158. } // Number
  159. 30 return days[month]; // Number
  160. },
  161. /**
  162. * Determines if a date is a leap year
  163. *
  164. * @example
  165. *
  166. * comb.date.isLeapYear(new Date(1600, 0, 1)); //true
  167. * comb.date.isLeapYear(new Date(2004, 0, 1)); //true
  168. * comb.date.isLeapYear(new Date(2000, 0, 1)); //true
  169. * comb.date.isLeapYear(new Date(2006, 0, 1)); //false
  170. * comb.date.isLeapYear(new Date(1900, 0, 1)); //false
  171. * comb.date.isLeapYear(new Date(1800, 0, 1)); //false
  172. * comb.date.isLeapYear(new Date(1700, 0, 1)); //false
  173. *
  174. * @param {Date} dateObject
  175. * @returns {Boolean} true if it is a leap year false otherwise
  176. */
  177. isLeapYear:function (/*Date*/dateObject, utc) {
  178. 28 var year = dateObject[utc ? "getUTCFullYear" : "getFullYear"]();
  179. 28 return !(year % 400) || (!(year % 4) && !!(year % 100)); // Boolean
  180. },
  181. /**
  182. * Determines if a date is on a weekend
  183. *
  184. * @example
  185. *
  186. * var thursday = new Date(2006, 8, 21);
  187. * var saturday = new Date(2006, 8, 23);
  188. * var sunday = new Date(2006, 8, 24);
  189. * var monday = new Date(2006, 8, 25);
  190. * comb.date.isWeekend(thursday)); //false
  191. * comb.date.isWeekend(saturday); //true
  192. * comb.date.isWeekend(sunday); //true
  193. * comb.date.isWeekend(monday)); //false
  194. *
  195. * @param {Date} dateObject the date to test
  196. *
  197. * @returns {Boolean} true if the date is a weekend
  198. */
  199. isWeekend:function (/*Date?*/dateObject, utc) {
  200. // summary:
  201. // Determines if the date falls on a weekend, according to local custom.
  202. 4 var day = (dateObject || new Date())[utc ? "getUTCDay" : "getDay"]();
  203. 4 return day == 0 || day == 6;
  204. },
  205. /**
  206. * Get the timezone of a date
  207. *
  208. * @example
  209. * //just setting the strLocal to simulate the toString() of a date
  210. * dt.str = 'Sun Sep 17 2006 22:25:51 GMT-0500 (CDT)';
  211. * //just setting the strLocal to simulate the locale
  212. * dt.strLocale = 'Sun 17 Sep 2006 10:25:51 PM CDT';
  213. * comb.date.getTimezoneName(dt); //'CDT'
  214. * dt.str = 'Sun Sep 17 2006 22:57:18 GMT-0500 (CDT)';
  215. * dt.strLocale = 'Sun Sep 17 22:57:18 2006';
  216. * comb.date.getTimezoneName(dt); //'CDT'
  217. * @param dateObject the date to get the timezone from
  218. *
  219. * @returns {String} the timezone of the date
  220. */
  221. getTimezoneName:function (/*Date*/dateObject) {
  222. 35 var str = dateObject.toString();
  223. 35 var tz = '';
  224. 35 var pos = str.indexOf('(');
  225. 35 if (pos > -1) {
  226. 31 tz = str.substring(++pos, str.indexOf(')'));
  227. }
  228. 35 return tz; // String
  229. },
  230. /**
  231. * Compares two dates
  232. *
  233. * @example
  234. *
  235. * var d1 = new Date();
  236. * d1.setHours(0);
  237. * comb.date.compare(d1, d1); // 0
  238. *
  239. * var d1 = new Date();
  240. * d1.setHours(0);
  241. * var d2 = new Date();
  242. * d2.setFullYear(2005);
  243. * d2.setHours(12);
  244. * comb.date.compare(d1, d2, "date"); // 1
  245. * comb.date.compare(d1, d2, "datetime"); // 1
  246. *
  247. * var d1 = new Date();
  248. * d1.setHours(0);
  249. * var d2 = new Date();
  250. * d2.setFullYear(2005);
  251. * d2.setHours(12);
  252. * comb.date.compare(d2, d1, "date"); // -1
  253. * comb.date.compare(d1, d2, "time"); //-1
  254. *
  255. * @param {Date|String} date1 the date to comapare
  256. * @param {Date|String} [date2=new Date()] the date to compare date1 againse
  257. * @param {"date"|"time"|"datetime"} portion compares the portion specified
  258. *
  259. * @returns -1 if date1 is < date2 0 if date1 === date2 1 if date1 > date2
  260. */
  261. compare:function (/*Date*/date1, /*Date*/date2, /*String*/portion) {
  262. 10 date1 = new Date(date1);
  263. 10 date2 = new Date((date2 || new Date()));
  264. 10 if (portion == "date") {
  265. // Ignore times and compare dates.
  266. 4 date1.setHours(0, 0, 0, 0);
  267. 4 date2.setHours(0, 0, 0, 0);
  268. 6 } else if (portion == "time") {
  269. // Ignore dates and compare times.
  270. 2 date1.setFullYear(0, 0, 0);
  271. 2 date2.setFullYear(0, 0, 0);
  272. }
  273. 10 var ret = 0;
  274. 10 date1 > date2 && (ret = 1);
  275. 10 date1 < date2 && (ret = -1);
  276. 10 return ret; // int
  277. },
  278. /**
  279. * Adds a specified interval and amount to a date
  280. *
  281. * @example
  282. * var dtA = new Date(2005, 11, 27);
  283. * comb.date.add(dtA, "year", 1); //new Date(2006, 11, 27);
  284. * comb.date.add(dtA, "years", 1); //new Date(2006, 11, 27);
  285. *
  286. * dtA = new Date(2000, 0, 1);
  287. * comb.date.add(dtA, "quarter", 1); //new Date(2000, 3, 1);
  288. * comb.date.add(dtA, "quarters", 1); //new Date(2000, 3, 1);
  289. *
  290. * dtA = new Date(2000, 0, 1);
  291. * comb.date.add(dtA, "month", 1); //new Date(2000, 1, 1);
  292. * comb.date.add(dtA, "months", 1); //new Date(2000, 1, 1);
  293. *
  294. * dtA = new Date(2000, 0, 31);
  295. * comb.date.add(dtA, "month", 1); //new Date(2000, 1, 29);
  296. * comb.date.add(dtA, "months", 1); //new Date(2000, 1, 29);
  297. *
  298. * dtA = new Date(2000, 0, 1);
  299. * comb.date.add(dtA, "week", 1); //new Date(2000, 0, 8);
  300. * comb.date.add(dtA, "weeks", 1); //new Date(2000, 0, 8);
  301. *
  302. * dtA = new Date(2000, 0, 1);
  303. * comb.date.add(dtA, "day", 1); //new Date(2000, 0, 2);
  304. *
  305. * dtA = new Date(2000, 0, 1);
  306. * comb.date.add(dtA, "weekday", 1); //new Date(2000, 0, 3);
  307. *
  308. * dtA = new Date(2000, 0, 1, 11);
  309. * comb.date.add(dtA, "hour", 1); //new Date(2000, 0, 1, 12);
  310. *
  311. * dtA = new Date(2000, 11, 31, 23, 59);
  312. * comb.date.add(dtA, "minute", 1); //new Date(2001, 0, 1, 0, 0);
  313. *
  314. * dtA = new Date(2000, 11, 31, 23, 59, 59);
  315. * comb.date.add(dtA, "second", 1); //new Date(2001, 0, 1, 0, 0, 0);
  316. *
  317. * dtA = new Date(2000, 11, 31, 23, 59, 59, 999);
  318. * comb.date.add(dtA, "millisecond", 1); //new Date(2001, 0, 1, 0, 0, 0, 0);
  319. *
  320. * @param {Date} date
  321. * @param {String} interval the interval to add
  322. * <ul>
  323. * <li>day | days</li>
  324. * <li>weekday | weekdays</li>
  325. * <li>year | years</li>
  326. * <li>week | weeks</li>
  327. * <li>quarter | quarters</li>
  328. * <li>months | months</li>
  329. * <li>hour | hours</li>
  330. * <li>minute | minutes</li>
  331. * <li>second | seconds</li>
  332. * <li>millisecond | milliseconds</li>
  333. * </ul>
  334. * @param {Number} [amount=0] the amount to add
  335. */
  336. add:function (/*Date*/date, /*String*/interval, /*int*/amount) {
  337. 216 amount = amount | 0
  338. 216 var sum = new Date(date);
  339. 216 var fixOvershoot = false;
  340. 216 var property = "Date";
  341. //noinspection FallthroughInSwitchStatementJS
  342. 216 switch (interval) {
  343. case "day":
  344. case "days" :
  345. 60 break;
  346. case "weekday":
  347. case "weekdays":
  348. // Divide the increment time span into weekspans plus leftover days
  349. // e.g., 8 days is one 5-day weekspan / and two leftover days
  350. // Can't have zero leftover days, so numbers divisible by 5 get
  351. // a days value of 5, and the remaining days make up the number of weeks
  352. 32 var days, weeks, mod = amount % 5, strt = date.getDay(), adj = 0;
  353. 32 if (!mod) {
  354. 12 days = (amount > 0) ? 5 : -5;
  355. 12 weeks = (amount > 0) ? ((amount - 5) / 5) : ((amount + 5) / 5);
  356. } else {
  357. 20 days = mod;
  358. 20 weeks = parseInt(amount / 5);
  359. }
  360. 32 if (strt == 6 && amount > 0) {
  361. 4 adj = 1;
  362. 28 } else if (strt == 0 && amount < 0) {
  363. // Orig date is Sun / negative increment
  364. // Jump back over Sat
  365. 8 adj = -1;
  366. }
  367. // Get weekday val for the new date
  368. 32 var trgt = strt + days;
  369. // New date is on Sat or Sun
  370. 32 if (trgt == 0 || trgt == 6) {
  371. 4 adj = (amount > 0) ? 2 : -2;
  372. }
  373. // Increment by number of weeks plus leftover days plus
  374. // weekend adjustments
  375. 32 amount = (7 * weeks) + days + adj;
  376. 32 break;
  377. case "year":
  378. case "years":
  379. 34 property = "FullYear";
  380. 34 fixOvershoot = true;
  381. 34 break;
  382. case "week":
  383. case "weeks":
  384. 6 amount *= 7;
  385. 6 break;
  386. case "quarter":
  387. case "quarters" :
  388. // Naive quarter is just three months
  389. 18 amount *= 3;
  390. case "month":
  391. case "months":
  392. // Reset to last day of month if you overshoot
  393. 38 fixOvershoot = true;
  394. 38 property = "Month";
  395. 38 break;
  396. default:
  397. 46 interval = interval.replace(/s$/, "");
  398. 46 property = "UTC" + interval.charAt(0).toUpperCase() + interval.substring(1) + "s";
  399. }
  400. 216 if (property) {
  401. 216 sum["set" + property](sum["get" + property]() + amount);
  402. }
  403. 216 if (fixOvershoot && (sum.getDate() < date.getDate())) {
  404. 26 sum.setDate(0);
  405. }
  406. 216 return sum; // Date
  407. },
  408. /**
  409. * Finds the difference between two dates based on the specified interval
  410. *
  411. * @example
  412. *
  413. * var dtA, dtB;
  414. *
  415. * dtA = new Date(2005, 11, 27);
  416. * dtB = new Date(2006, 11, 27);
  417. * comb.date.difference(dtA, dtB, "year"); //1
  418. *
  419. * dtA = new Date(2000, 1, 29);
  420. * dtB = new Date(2001, 2, 1);
  421. * comb.date.difference(dtA, dtB, "quarter"); //4
  422. * comb.date.difference(dtA, dtB, "month"); //13
  423. *
  424. * dtA = new Date(2000, 1, 1);
  425. * dtB = new Date(2000, 1, 8);
  426. * comb.date.difference(dtA, dtB, "week"); //1
  427. *
  428. * dtA = new Date(2000, 1, 29);
  429. * dtB = new Date(2000, 2, 1);
  430. * comb.date.difference(dtA, dtB, "day"); //1
  431. *
  432. * dtA = new Date(2006, 7, 3);
  433. * dtB = new Date(2006, 7, 11);
  434. * comb.date.difference(dtA, dtB, "weekday"); //6
  435. *
  436. * dtA = new Date(2000, 11, 31, 23);
  437. * dtB = new Date(2001, 0, 1, 0);
  438. * comb.date.difference(dtA, dtB, "hour"); //1
  439. *
  440. * dtA = new Date(2000, 11, 31, 23, 59);
  441. * dtB = new Date(2001, 0, 1, 0, 0);
  442. * comb.date.difference(dtA, dtB, "minute"); //1
  443. *
  444. * dtA = new Date(2000, 11, 31, 23, 59, 59);
  445. * dtB = new Date(2001, 0, 1, 0, 0, 0);
  446. * comb.date.difference(dtA, dtB, "second"); //1
  447. *
  448. * dtA = new Date(2000, 11, 31, 23, 59, 59, 999);
  449. * dtB = new Date(2001, 0, 1, 0, 0, 0, 0);
  450. * comb.date.difference(dtA, dtB, "millisecond"); //1
  451. *
  452. *
  453. * @param {Date} date1
  454. * @param {Date} [date2 = new Date()]
  455. * @param {String} [interval = "day"] the intercal to find the difference of.
  456. * <ul>
  457. * <li>day | days</li>
  458. * <li>weekday | weekdays</li>
  459. * <li>year | years</li>
  460. * <li>week | weeks</li>
  461. * <li>quarter | quarters</li>
  462. * <li>months | months</li>
  463. * <li>hour | hours</li>
  464. * <li>minute | minutes</li>
  465. * <li>second | seconds</li>
  466. * <li>millisecond | milliseconds</li>
  467. * </ul>
  468. */
  469. difference:function (/*Date*/date1, /*Date?*/date2, /*String*/interval, utc) {
  470. 338 date2 = date2 || new Date();
  471. 338 interval = interval || "day";
  472. 338 var yearDiff = date2.getFullYear() - date1.getFullYear();
  473. 338 var delta = 1; // Integer return value
  474. 338 switch (interval) {
  475. case "quarter":
  476. case "quarters":
  477. 10 var m1 = date1[utc ? "getUTCMonth" : "getMonth"]();
  478. 10 var m2 = date2[utc ? "getUTCMonth" : "getMonth"]();
  479. // Figure out which quarter the months are in
  480. 10 var q1 = floor(m1 / 3) + 1;
  481. 10 var q2 = floor(m2 / 3) + 1;
  482. // Add quarters for any year difference between the dates
  483. 10 q2 += (yearDiff * 4);
  484. 10 delta = q2 - q1;
  485. 10 break;
  486. case "weekday":
  487. case "weekdays":
  488. 56 var days = round(date.difference(date1, date2, "day", utc));
  489. 56 var weeks = parseInt(date.difference(date1, date2, "week", utc));
  490. 56 var mod = days % 7;
  491. // Even number of weeks
  492. 56 if (mod == 0) {
  493. 14 days = weeks * 5;
  494. } else {
  495. // Weeks plus spare change (< 7 days)
  496. 42 var adj = 0;
  497. 42 var aDay = date1[utc ? "getUTCDay" : "getDay"]();
  498. 42 var bDay = date2[utc ? "getUTCDay" : "getDay"]();
  499. 42 weeks = parseInt(days / 7);
  500. 42 mod = days % 7;
  501. // Mark the date advanced by the number of
  502. // round weeks (may be zero)
  503. 42 var dtMark = new Date(date1);
  504. 42 dtMark.setDate(dtMark[utc ? "getUTCDate" : "getDate"]() + (weeks * 7));
  505. 42 var dayMark = dtMark[utc ? "getUTCDay" : "getDay"]();
  506. // Spare change days -- 6 or less
  507. 42 if (days > 0) {
  508. 24 switch (true) {
  509. // Range starts on Sat
  510. case (aDay == 6 || bDay == 6):
  511. 6 adj = -1;
  512. 6 break;
  513. // Range starts on Sun
  514. case aDay == 0:
  515. 8 adj = 0;
  516. 8 break;
  517. // Range ends on Sun
  518. case bDay == 0:
  519. 2 adj = -2;
  520. 2 break;
  521. // Range contains weekend
  522. case (dayMark + mod) > 5:
  523. 2 adj = -2;
  524. }
  525. 18 } else if (days < 0) {
  526. 18 switch (true) {
  527. // Range starts on Sat
  528. case aDay == 6:
  529. 2 adj = 0;
  530. 2 break;
  531. // Range starts on Sun
  532. case aDay == 0:
  533. 6 adj = 1;
  534. 6 break;
  535. // Range ends on Sat
  536. case bDay == 6:
  537. 2 adj = 2;
  538. 2 break;
  539. // Range ends on Sun
  540. case bDay == 0:
  541. 2 adj = 1;
  542. 2 break;
  543. // Range contains weekend
  544. case (dayMark + mod) < 0:
  545. 2 adj = 2;
  546. }
  547. }
  548. 42 days += adj;
  549. 42 days -= (weeks * 2);
  550. }
  551. 56 delta = days;
  552. 56 break;
  553. case "year":
  554. case "years":
  555. 16 delta = yearDiff;
  556. 16 break;
  557. case "month":
  558. case "months":
  559. 10 var m1 = date1[utc ? "getUTCMonth" : "getMonth"]();
  560. 10 var m2 = date2[utc ? "getUTCMonth" : "getMonth"]();
  561. 10 delta = (m2 - m1) + (yearDiff * 12);
  562. 10 break;
  563. case "week":
  564. case "weeks":
  565. 64 delta = parseInt(date.difference(date1, date2, "day", utc) / 7);
  566. 64 break;
  567. case "day":
  568. case "days":
  569. 148 delta /= 24;
  570. case "hour":
  571. case "hours":
  572. 160 delta /= 60;
  573. case "minute":
  574. case "minutes":
  575. 168 delta /= 60;
  576. case "second":
  577. case "seconds":
  578. 174 delta /= 1000;
  579. case "millisecond":
  580. case "milliseconds":
  581. 182 delta *= date2.getTime() - date1.getTime();
  582. }
  583. // Round for fractional values and DST leaps
  584. 338 return round(delta); // Number (integer)
  585. },
  586. /**
  587. * Parses a date string into a date object
  588. *
  589. * @example
  590. * var aug_11_2006 = new Date(2006, 7, 11, 0);
  591. * comb.date.parse("08/11/06", "MM/dd/yy"); //aug_11_2006
  592. * comb.date.parse("11Aug2006", 'ddMMMyyyy'); //aug_11_2006
  593. * comb.date.parse("Aug2006", 'MMMyyyy'); //new Date(2006, 7, 1)
  594. * comb.date.parse("Aug 11, 2006", "MMM dd, yyyy"); //aug_11_2006
  595. * comb.date.parse("August 11, 2006", "MMMM dd, yyyy"); //aug_11_2006
  596. * comb.date.parse("Friday, August 11, 2006", "EEEE, MMMM dd, yyyy"); //aug_11_2006
  597. *
  598. * @param {String} dateStr The string to parse
  599. * @param {String} format the format of the date composed of the following options
  600. * <ul>
  601. * <li> G Era designator Text AD</li>
  602. * <li> y Year Year 1996; 96</li>
  603. * <li> M Month in year Month July; Jul; 07</li>
  604. * <li> w Week in year Number 27</li>
  605. * <li> W Week in month Number 2</li>
  606. * <li> D Day in year Number 189</li>
  607. * <li> d Day in month Number 10</li>
  608. * <li> E Day in week Text Tuesday; Tue</li>
  609. * <li> a Am/pm marker Text PM</li>
  610. * <li> H Hour in day (0-23) Number 0</li>
  611. * <li> k Hour in day (1-24) Number 24</li>
  612. * <li> K Hour in am/pm (0-11) Number 0</li>
  613. * <li> h Hour in am/pm (1-12) Number 12</li>
  614. * <li> m Minute in hour Number 30</li>
  615. * <li> s Second in minute Number 55</li>
  616. * <li> S Millisecond Number 978</li>
  617. * <li> z Time zone General time zone Pacific Standard Time; PST; GMT-08:00</li>
  618. * <li> Z Time zone RFC 822 time zone -0800 </li>
  619. * </ul>
  620. *
  621. * @returns {Date} the parsed date
  622. *
  623. *
  624. */
  625. parse:function (dateStr, format) {
  626. 55 if (!format) throw new Error('format required when calling comb.date.parse');
  627. 55 var tokens = [], regexp = buildDateEXP(format, tokens),
  628. re = new RegExp("^" + regexp + "$", "i"),
  629. match = re.exec(dateStr);
  630. 55 if (!match) {
  631. 7 return null;
  632. } // null
  633. 48 var result = [1970, 0, 1, 0, 0, 0, 0], // will get converted to a Date at the end
  634. amPm = "",
  635. valid = match.every(function (v, i) {
  636. 212 if (!i) {
  637. 48 return true;
  638. }
  639. 164 var token = tokens[i - 1];
  640. 164 var l = token.length;
  641. 164 switch (token.charAt(0)) {
  642. case 'y':
  643. 26 if (v < 100) {
  644. 6 v = parseInt(v, 10);
  645. //choose century to apply, according to a sliding window
  646. //of 80 years before and 20 years after present year
  647. 6 var year = '' + new Date().getFullYear(),
  648. century = year.substring(0, 2) * 100,
  649. cutoff = min(year.substring(2, 4) + 20, 99);
  650. 6 result[0] = (v < cutoff) ? century + v : century - 100 + v;
  651. } else {
  652. 20 result[0] = v;
  653. }
  654. 26 break;
  655. case 'M':
  656. 28 if (l > 2) {
  657. 20 var months = monthNames;
  658. 20 if (l === 3) {
  659. 8 months = monthAbbr;
  660. }
  661. //Tolerate abbreviating period in month part
  662. //Case-insensitive comparison
  663. 20 v = v.replace(".", "").toLowerCase();
  664. 20 months = months.map(function (s) {
  665. 240 return s.replace(".", "").toLowerCase();
  666. });
  667. 20 if ((v = months.indexOf(v)) == -1) {
  668. 2 return false;
  669. }
  670. } else {
  671. 8 v--;
  672. }
  673. 26 result[1] = v;
  674. 26 break;
  675. case 'E':
  676. case 'e':
  677. 12 var days = dayNames;
  678. 12 if (l == 3) {
  679. 4 days = dayAbbr;
  680. }
  681. //Case-insensitive comparison
  682. 12 v = v.toLowerCase();
  683. 12 days = days.map(function (d) {
  684. 84 return d.toLowerCase();
  685. });
  686. 12 var d = days.indexOf(v);
  687. 12 if (d == -1) {
  688. 4 v = parseInt(v);
  689. 4 if (isNaN(v) || v > days.length) {
  690. 2 return false;
  691. }
  692. } else {
  693. 8 v = d;
  694. }
  695. 10 break;
  696. case 'D':
  697. 4 result[1] = 0;
  698. case 'd':
  699. 30 result[2] = v;
  700. 30 break;
  701. case 'a': //am/pm
  702. 4 var am = "am";
  703. 4 var pm = "pm";
  704. 4 var period = /\./g;
  705. 4 v = v.replace(period, '').toLowerCase();
  706. // we might not have seen the hours field yet, so store the state and apply hour change later
  707. 4 amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
  708. 4 break;
  709. case 'k': //hour (0-11)
  710. 2 if (v == 24) {
  711. 2 v = 0;
  712. }
  713. // fallthrough...
  714. case 'h': //hour (1-12)
  715. case 'H': //hour (0-23)
  716. case 'K': //hour (0-11)
  717. //in the 12-hour case, adjusting for am/pm requires the 'a' part
  718. //which could come before or after the hour, so we will adjust later
  719. 14 result[3] = v;
  720. 14 break;
  721. case 'm': //minutes
  722. 14 result[4] = v;
  723. 14 break;
  724. case 's': //seconds
  725. 10 result[5] = v;
  726. 10 break;
  727. case 'S': //milliseconds
  728. 8 result[6] = v;
  729. 8 break;
  730. }
  731. 160 return true;
  732. });
  733. 48 if (valid) {
  734. 44 var hours = +result[3];
  735. //account for am/pm
  736. 44 if (amPm === 'p' && hours < 12) {
  737. 2 result[3] = hours + 12; //e.g., 3pm -> 15
  738. 42 } else if (amPm === 'a' && hours == 12) {
  739. 2 result[3] = 0; //12am -> 0
  740. }
  741. 44 var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
  742. 44 var dateToken = (tokens.indexOf('d') != -1),
  743. monthToken = (tokens.indexOf('M') != -1),
  744. month = result[1],
  745. day = result[2],
  746. dateMonth = dateObject.getMonth(),
  747. dateDay = dateObject.getDate();
  748. 44 if ((monthToken && dateMonth > month) || (dateToken && dateDay > day)) {
  749. 2 return null;
  750. }
  751. 42 return dateObject; // Date
  752. } else {
  753. 4 return null;
  754. }
  755. },
  756. /**
  757. * Formats a date to the specidifed format string
  758. *
  759. * @example
  760. *
  761. * var date = new Date(2006, 7, 11, 0, 55, 12, 345);
  762. * comb.date.format(date, "EEEE, MMMM dd, yyyy"); //"Friday, August 11, 2006"
  763. * comb.date.format(date, "M/dd/yy"); //"8/11/06"
  764. * comb.date.format(date, "E"); //"6"
  765. * comb.date.format(date, "h:m a"); //"12:55 AM"
  766. * comb.date.format(date, 'h:m:s'); //"12:55:12"
  767. * comb.date.format(date, 'h:m:s.SS'); //"12:55:12.35"
  768. * comb.date.format(date, 'k:m:s.SS'); //"24:55:12.35"
  769. * comb.date.format(date, 'H:m:s.SS'); //"0:55:12.35"
  770. * comb.date.format(date, "ddMMyyyy"); //"11082006"
  771. *
  772. * @param date the date to format
  773. * @param {String} format the format of the date composed of the following options
  774. * <ul>
  775. * <li> G Era designator Text AD</li>
  776. * <li> y Year Year 1996; 96</li>
  777. * <li> M Month in year Month July; Jul; 07</li>
  778. * <li> w Week in year Number 27</li>
  779. * <li> W Week in month Number 2</li>
  780. * <li> D Day in year Number 189</li>
  781. * <li> d Day in month Number 10</li>
  782. * <li> E Day in week Text Tuesday; Tue</li>
  783. * <li> a Am/pm marker Text PM</li>
  784. * <li> H Hour in day (0-23) Number 0</li>
  785. * <li> k Hour in day (1-24) Number 24</li>
  786. * <li> K Hour in am/pm (0-11) Number 0</li>
  787. * <li> h Hour in am/pm (1-12) Number 12</li>
  788. * <li> m Minute in hour Number 30</li>
  789. * <li> s Second in minute Number 55</li>
  790. * <li> S Millisecond Number 978</li>
  791. * <li> z Time zone General time zone Pacific Standard Time; PST; GMT-08:00</li>
  792. * <li> Z Time zone RFC 822 time zone -0800 </li>
  793. * </ul>
  794. */
  795. format:function (date, format, utc) {
  796. 79 utc = utc || false;
  797. 79 var fullYear, month, day, d, hour, minute, second, millisecond;
  798. 79 if (utc) {
  799. 26 fullYear = date.getUTCFullYear(),
  800. month = date.getUTCMonth(),
  801. day = date.getUTCDay(),
  802. d = date.getUTCDate(),
  803. hour = date.getUTCHours(),
  804. minute = date.getUTCMinutes(),
  805. second = date.getUTCSeconds(),
  806. millisecond = date.getUTCMilliseconds();
  807. } else {
  808. 53 fullYear = date.getFullYear(),
  809. month = date.getMonth(),
  810. d = date.getDate(),
  811. day = date.getDay(),
  812. hour = date.getHours(),
  813. minute = date.getMinutes(),
  814. second = date.getSeconds(),
  815. millisecond = date.getMilliseconds();
  816. }
  817. 79 return format.replace(/([a-z])\1*/ig, function (match, options) {
  818. 381 var s, pad, h,
  819. c = match.charAt(0),
  820. l = match.length;
  821. 381 switch (c) {
  822. case 'd':
  823. 43 s = "" + d;
  824. case 'H':
  825. 74 !s && (s = "" + hour);
  826. case 'm':
  827. 115 !s && (s = "" + minute);
  828. case 's':
  829. 154 !s && (s = "" + second);
  830. 154 pad = true;
  831. 154 break;
  832. case 'G':
  833. 4 s = ((l < 4) ? eraAbbr : eraNames)[fullYear < 0 ? 0 : 1];
  834. 4 break;
  835. case 'y':
  836. 49 s = fullYear;
  837. 49 if (l > 1) {
  838. 49 l == 2 ? s = string.truncate("" + s, 2, true) : pad = true;
  839. }
  840. 49 break;
  841. case 'Q':
  842. case 'q':
  843. 6 s = ceil((month + 1) / 3);
  844. 6 pad = true;
  845. 6 break;
  846. case 'M':
  847. 43 if (l < 3) {
  848. 33 s = month + 1;
  849. 33 pad = true;
  850. } else {
  851. 10 s = (l == 3 ? monthAbbr : monthNames)[month];
  852. }
  853. 43 break;
  854. case 'w':
  855. 4 s = getWeekOfYear(date, 0, utc), pad = true;
  856. 4 break;
  857. case 'D':
  858. 2 s = getDayOfYear(date, utc), pad = true;
  859. 2 break;
  860. case 'E':
  861. 12 if (l < 3) {
  862. 2 s = day + 1;
  863. 2 pad = true;
  864. } else {
  865. 10 s = (l == 3 ? dayAbbr : dayNames)[day];
  866. }
  867. 12 break;
  868. case 'a':
  869. 2 s = (hour < 12) ? 'AM' : 'PM';
  870. 2 break;
  871. case 'h':
  872. 6 s = (hour % 12) || 12, pad = true;
  873. 6 break;
  874. case 'K':
  875. 2 s = (hour % 12), pad = true;
  876. 2 break;
  877. case 'k':
  878. 2 s = hour || 24, pad = true;
  879. 2 break;
  880. case 'S':
  881. 37 s = round(millisecond * pow(10, l - 3)), pad = true;
  882. 37 break;
  883. case 'v':
  884. case 'z':
  885. 31 s = comb.date.getTimezoneName(date);
  886. 31 if (s) {
  887. 27 break;
  888. }
  889. 4 l = 4;
  890. // fallthrough... use GMT if tz not available
  891. case 'Z':
  892. 4 var offset = date.getTimezoneOffset();
  893. 4 var tz = [
  894. (offset >= 0 ? "-" : "+"),
  895. string.pad(floor(abs(offset) / 60), 2, "0"),
  896. string.pad(abs(offset) % 60, 2, "0")
  897. ];
  898. 4 if (l == 4) {
  899. 4 tz.splice(0, 0, "GMT");
  900. 4 tz.splice(3, 0, ":");
  901. }
  902. 4 s = tz.join("");
  903. 4 break;
  904. // case 'Y': case 'u': case 'W': case 'F': case 'g': case 'A': case 'e':
  905. // console.log(match+" modifier unimplemented");
  906. default:
  907. 27 s = match;
  908. // throw new Error("comb.date.format: invalid pattern char: " + match);
  909. }
  910. 381 if (pad) {
  911. 291 s = string.pad(s, l, '0');
  912. }
  913. 381 return s;
  914. });
  915. }
  916. };
  917. 1date = comb.date;
  918. /**
  919. * Adds the specified year/s to the current date.
  920. *
  921. * @example
  922. *
  923. * //assuming that current year is 2012
  924. * comb.yearsFromNow(1); //2013-mm-dd hh:MM:ss
  925. *
  926. * @param {Number} val the number of years to add
  927. *
  928. * @return {Date} a date with the number of years added
  929. */
  930. 1comb.yearsFromNow = function (val) {
  931. 1 return date.add(new Date(), "years", val);
  932. };
  933. /**
  934. * Subtracts the specified year/s from the current date.
  935. *
  936. * @param {Number} val the number of years to subtract
  937. *
  938. * @return {Date} a date with the number of years subtracted
  939. */
  940. 1comb.yearsAgo = function (val) {
  941. 1 return date.add(new Date(), "years", -val);
  942. };
  943. /**
  944. * Adds the specified month/s to the current date.
  945. *
  946. * @example
  947. *
  948. * //assuming that current month is february
  949. * comb.yearsFromNow(2); //yyyy-04-dd hh:MM:ss
  950. *
  951. * @param {Number} val the number of months to add
  952. *
  953. * @return {Date} a date with the number of years added
  954. */
  955. 1comb.monthsFromNow = function (val) {
  956. 1 return date.add(new Date(), "months", val);
  957. };
  958. /**
  959. * Subtracts the specified month/s from the current date.
  960. *
  961. * @param {Number} val the number of months to subtract
  962. *
  963. * @return {Date} a date with the number of months subtracted
  964. */
  965. 1comb.monthsAgo = function (val) {
  966. 1 return date.add(new Date(), "months", -val);
  967. };
  968. /**
  969. * Adds the specified day/s to the current date.
  970. *
  971. * @param {Number} val the number of days to add
  972. *
  973. * @return {Date} a date with the number of days added
  974. */
  975. 1comb.daysFromNow = function (val) {
  976. 27 return date.add(new Date(), "days", val);
  977. };
  978. /**
  979. * Subtracts the specified day/s from the current date.
  980. *
  981. * @param {Number} val the number of days to subtract
  982. *
  983. * @return {Date} a date with the number of days subtracted
  984. */
  985. 1comb.daysAgo = function (val) {
  986. 1 return date.add(new Date(), "days", -val);
  987. };
  988. /**
  989. * Adds the specified hour/s to the current date.
  990. *
  991. * @param {Number} val the number of hours to add
  992. *
  993. * @return {Date} a date with the number of hours added
  994. */
  995. 1comb.hoursFromNow = function (val) {
  996. 1 return date.add(new Date(), "hours", val);
  997. };
  998. /**
  999. * Subtracts the specified hour/s from the current date.
  1000. *
  1001. * @param {Number} val the number of hours to subtract
  1002. *
  1003. * @return {Date} a date with the number of hours subtracted
  1004. */
  1005. 1comb.hoursAgo = function (val) {
  1006. 1 return date.add(new Date(), "hours", -val);
  1007. };
  1008. /**
  1009. * Adds the specified minute/s to the current date.
  1010. *
  1011. * @param {Number} val the number of minutes to add
  1012. *
  1013. * @return {Date} a date with the number of minutes added
  1014. */
  1015. 1comb.minutesFromNow = function (val) {
  1016. 1 return date.add(new Date(), "minutes", val);
  1017. };
  1018. /**
  1019. * Subtracts the specified minute/s from the current date.
  1020. *
  1021. * @param {Number} val the number of minutes to subtract
  1022. *
  1023. * @return {Date} a date with the number of minutes subtracted
  1024. */
  1025. 1comb.minutesAgo = function (val) {
  1026. 1 return date.add(new Date(), "minutes", -val);
  1027. };
  1028. /**
  1029. * Adds the specified second/s to the current date.
  1030. *
  1031. * @param {Number} val the number of seconds to add
  1032. *
  1033. * @return {Date} a date with the number of seconds added
  1034. */
  1035. 1comb.secondsFromNow = function (val) {
  1036. 1 return date.add(new Date(), "seconds", val);
  1037. };
  1038. /**
  1039. * Subtracts the specified second/s from the current date.
  1040. *
  1041. * @param {Number} val the number of seconds to subtract
  1042. *
  1043. * @return {Date} a date with the number of seconds subtracted
  1044. */
  1045. 1comb.secondsAgo = function (val) {
  1046. 1 return date.add(new Date(), "seconds", -val);
  1047. };
base/functions.js
Coverage100.00 SLOC254 LOC90 Missed0
  1. 1var string = require("./string"),
  2. isString = string.isString;
  3. /**
  4. * Determines if something is a function
  5. * @param {Anything} obj the thing to test if it is a function
  6. *
  7. * @returns {Boolean} true if the obj is a function false otherwise
  8. * @static
  9. * @memberOf comb
  10. */
  11. 1function isFunction(obj) {
  12. 22776 return typeof obj === "function";
  13. }
  14. /**
  15. * Binds a method to a particular scope
  16. *
  17. * @static
  18. * @memberOf comb
  19. *
  20. * @param {Object} scope the scope to bind the callback to
  21. * @param {String|Function} method the method to callback
  22. * @param [args] optional args to pass to the callback
  23. *
  24. * @returns {Function} the hitched function
  25. */
  26. 1function hitch(scope, method, args) {
  27. 5162 args = Array.prototype.slice.call(arguments).slice(2);
  28. 5162 if ((isString(method) && !(method in scope))) {
  29. 2 throw new Error(method + " property not defined in scope");
  30. 5160 } else if (!isString(method) && !isFunction(method)) {
  31. 1 throw new Error(method + " is not a function");
  32. }
  33. 5159 if (isString(method)) {
  34. 2618 return function () {
  35. 1302 var func = scope[method];
  36. 1302 if (isFunction(func)) {
  37. 1299 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  38. 1299 return func.apply(scope, scopeArgs);
  39. } else {
  40. 3 return func;
  41. }
  42. };
  43. } else {
  44. 2541 return function () {
  45. 2376 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  46. 2376 return method.apply(scope, scopeArgs);
  47. };
  48. }
  49. }
  50. /**
  51. * Binds a method to the scope of the first argument.
  52. *
  53. * This is useful if you have async actions and you just want to run a method or retrieve a property on the object.
  54. *
  55. * ```
  56. * var arr = [], push = comb.applyFirst("push"), length = comb.applyFirst("length");
  57. * push(arr, 1, 2,3,4);
  58. * console.log(length(arr)); //4
  59. * console.log(arr); //1,2,3,4
  60. *
  61. * ```
  62. * @static
  63. * @memberOf comb
  64. * @param {String|Function} method the method to invoke in the scope of the first arument.
  65. * @param [args] optional args to pass to the callback
  66. *
  67. * @returns {Function} a function that will execute the method in the scope of the first argument.
  68. */
  69. 1function applyFirst(method, args) {
  70. 10 args = Array.prototype.slice.call(arguments).slice(1);
  71. 10 if (!isString(method) && !isFunction(method)) {
  72. 2 throw new Error(method + " must be the name of a property or function to execute");
  73. }
  74. 8 if (isString(method)) {
  75. 4 return function () {
  76. 12 var scopeArgs = Array.prototype.slice.call(arguments), scope = scopeArgs.shift();
  77. 12 var func = scope[method];
  78. 12 if (isFunction(func)) {
  79. 6 scopeArgs = args.concat(scopeArgs);
  80. 6 return func.apply(scope, scopeArgs);
  81. } else {
  82. 6 return func;
  83. }
  84. };
  85. } else {
  86. 4 return function () {
  87. 4 var scopeArgs = Array.prototype.slice.call(arguments), scope = scopeArgs.shift();
  88. 4 scopeArgs = args.concat(scopeArgs);
  89. 4 return method.apply(scope, scopeArgs);
  90. };
  91. }
  92. }
  93. 1;
  94. /**
  95. * @function
  96. * Binds a method to a particular scope
  97. * @static
  98. * @memberOf comb
  99. * @param {Object} scope the scope to bind the callback to
  100. * @param {String|Function} method the method to callback
  101. * @param [args] optional args to pass to the callback
  102. *
  103. * @returns {Function} the hitched function
  104. */
  105. 1exports.bind = hitch;
  106. /**
  107. * Binds a method to a particular scope ignoring any new arguments passed
  108. * into the function. This is useful if you want to force particular arguments and
  109. * ignore any new ones
  110. * @static
  111. * @memberOf comb
  112. * @param {Object} scope the scope to bind the callback to
  113. * @param {String|Function} method the method to callback
  114. * @param [args] optional args to pass to the callback
  115. *
  116. * @returns {Function} the hitched function
  117. */
  118. 1function hitchIgnore(scope, method, args) {
  119. 268 args = Array.prototype.slice.call(arguments).slice(2);
  120. 268 if ((isString(method) && !(method in scope))) {
  121. 2 throw new Error(method + " property not defined in scope");
  122. 266 } else if (!isString(method) && !isFunction(method)) {
  123. 1 throw new Error(method + " is not a function");
  124. }
  125. 265 if (isString(method)) {
  126. 15 return function () {
  127. 15 var func = scope[method];
  128. 15 if (isFunction(func)) {
  129. 12 return func.apply(scope, args);
  130. } else {
  131. 3 return func;
  132. }
  133. };
  134. } else {
  135. 250 return function () {
  136. 239 return method.apply(scope, args);
  137. };
  138. }
  139. }
  140. 1;
  141. /**
  142. * @function
  143. * Binds a method to a particular scope ignoring any new arguments passed
  144. * into the function. This is useful if you want to force particular arguments and
  145. * ignore any new ones
  146. * @static
  147. * @memberOf comb
  148. * @param {Object} scope the scope to bind the callback to
  149. * @param {String|Function} method the method to callback
  150. * @param [args] optional args to pass to the callback
  151. *
  152. * @returns {Function} the hitched function
  153. */
  154. 1exports.bindIgnore = hitchIgnore;
  155. /**
  156. * Allows the passing of additional arguments to a function when it is called
  157. * especially useful for callbacks that you want to provide additional parameters to
  158. *
  159. * @static
  160. * @memberOf comb
  161. * @param {String|Function} method the method to callback
  162. * @param {Anything} [args] variable number of arguments to pass
  163. *
  164. * @returns {Function} partially hitched function
  165. */
  166. 1function partial(method, args) {
  167. 8416 args = Array.prototype.slice.call(arguments).slice(1);
  168. 8416 if (!isString(method) && !isFunction(method)) {
  169. 2 throw new Error(method + " must be the name of a property or function to execute");
  170. }
  171. 8414 if (isString(method)) {
  172. 4 return function () {
  173. 4 var func = this[method];
  174. 4 if (isFunction(func)) {
  175. 2 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  176. 2 return func.apply(this, scopeArgs);
  177. } else {
  178. 2 return func;
  179. }
  180. };
  181. } else {
  182. 8410 return function () {
  183. 1697 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  184. 1697 return method.apply(this, scopeArgs);
  185. };
  186. }
  187. }
  188. 1;
  189. 1function curryFunc(f, execute) {
  190. 28 return function (arg) {
  191. 28 var args = Array.prototype.slice.call(arguments);
  192. 28 return execute ? f.apply(this, arguments) : function (arg) {
  193. 21 return f.apply(this, args.concat(Array.prototype.slice.call(arguments)));
  194. };
  195. };
  196. }
  197. /**
  198. * Curries a function
  199. * @example
  200. * var curried = comb.curry(4, function(a,b,c,d){
  201. * return [a,b,c,d].join(",");
  202. * }
  203. * curried("a");
  204. * curried("b");
  205. * curried("c");
  206. * curried("d") => "a,b,c,d"
  207. *
  208. * //OR
  209. *
  210. * curried("a")("b")("c")("d") => "a,b,c,d"
  211. *
  212. * @static
  213. * @memberOf comb
  214. * @param {Number} depth the number of args you expect
  215. * @param {Function} cb the function to call once all args are gathered
  216. * @param {Object} [scope] what scope to call the function in
  217. *
  218. * @returns {Function} the curried version of the function
  219. * */
  220. 1function curry(depth, cb, scope) {
  221. 7 var f;
  222. 7 if (scope) {
  223. 5 f = hitch(scope, cb);
  224. } else {
  225. 2 f = cb;
  226. }
  227. 7 if (depth) {
  228. 7 var len = depth - 1;
  229. 7 for (var i = len; i >= 0; i--) {
  230. 28 f = curryFunc(f, i === len);
  231. }
  232. }
  233. 7 return f;
  234. }
  235. 1exports.isFunction = isFunction;
  236. 1exports.hitch = hitch;
  237. 1exports.hitchIgnore = hitchIgnore;
  238. 1exports.partial = partial;
  239. 1exports.applyFirst = applyFirst;
  240. 1exports.bindFirst = applyFirst;
  241. 1exports.curry = curry;
base/index.js
Coverage100.00 SLOC12 LOC2 Missed0
  1. 1var objectBase = require("./object");
  2. 1objectBase.merge(exports, objectBase,
  3. require("./broadcast"),
  4. require("./functions"),
  5. require("./string"),
  6. require("./number"),
  7. require("./misc"),
  8. require("./date"),
  9. require("./array"),
  10. require("./regexp"),
  11. require("./inflections"));
base/inflections.js
Coverage100.00 SLOC215 LOC93 Missed0
  1. /*
  2. * A port of the Rails/Sequel inflections class
  3. * http://sequel.rubyforge.org/rdoc/classes/Sequel/Inflections.html
  4. */
  5. 1var array = require("./array").array, misc = require("./misc");
  6. 1var comb = exports;
  7. 1var CAMELIZE_CONVERT_REGEXP = /_(.)/g;
  8. 1var DASH = '-';
  9. 1var UNDERSCORE = '_';
  10. 1var UNDERSCORE_CONVERT_REGEXP1 = /([A-Z]+)(\d+|[A-Z][a-z])/g;
  11. 1var UNDERSCORE_CONVERT_REGEXP2 = /(\d+|[a-z])(\d+|[A-Z])/g;
  12. 1var UNDERSCORE_CONVERT_REPLACE = '$1_$2';
  13. 1var PLURALS = [], SINGULARS = [], UNCOUNTABLES = [];
  14. 1var _plural = function (rule, replacement) {
  15. 20 PLURALS.unshift([rule, replacement])
  16. };
  17. 1var _singular = function (rule, replacement) {
  18. 23 SINGULARS.unshift([rule, replacement])
  19. };
  20. /**
  21. * Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
  22. # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
  23. #
  24. # Examples:
  25. # irregular 'octopus', 'octopi'
  26. # irregular 'person', 'people'
  27. * @param singular the singular version
  28. * @param plural the plural version
  29. */
  30. 1var _irregular = function (singular, plural) {
  31. 7 _plural(new RegExp("(" + singular.substr(0, 1) + ")" + singular.substr(1) + "$"), "$1" + plural.substr(1));
  32. 7 _singular(new RegExp("(" + plural.substr(0, 1) + ")" + plural.substr(1) + "$"), "$1" + singular.substr(1));
  33. };
  34. 1var _uncountable = function (words) {
  35. 1 UNCOUNTABLES.push(misc.argsToArray(arguments))
  36. 1 UNCOUNTABLES = array.flatten(UNCOUNTABLES);
  37. };
  38. 1_plural(/$/, 's');
  39. 1_plural(/s$/i, 's');
  40. 1_plural(/(alias|(?:stat|octop|vir|b)us)$/i, '$1es');
  41. 1_plural(/(buffal|tomat)o$/i, '$1oes');
  42. 1_plural(/([ti])um$/i, '$1a');
  43. 1_plural(/sis$/i, 'ses');
  44. 1_plural(/(?:([^f])fe|([lr])f)$/i, '$1$2ves');
  45. 1_plural(/(hive)$/i, '$1s');
  46. 1_plural(/([^aeiouy]|qu)y$/i, '$1ies');
  47. 1_plural(/(x|ch|ss|sh)$/i, '$1es');
  48. 1_plural(/(matr|vert|ind)ix|ex$/i, '$1ices');
  49. 1_plural(/([m|l])ouse$/i, '$1ice');
  50. 1_plural(/^(ox)$/i, "$1en");
  51. 1_singular(/s$/i, '');
  52. 1_singular(/([ti])a$/i, '$1um');
  53. 1_singular(/(analy|ba|cri|diagno|parenthe|progno|synop|the)ses$/i, '$1sis');
  54. 1_singular(/([^f])ves$/i, '$1fe');
  55. 1_singular(/([h|t]ive)s$/i, '$1');
  56. 1_singular(/([lr])ves$/i, '$1f');
  57. 1_singular(/([^aeiouy]|qu)ies$/i, '$1y');
  58. 1_singular(/(m)ovies$/i, '$1ovie');
  59. 1_singular(/(x|ch|ss|sh)es$/i, '$1');
  60. 1_singular(/([m|l])ice$/i, '$1ouse');
  61. 1_singular(/buses$/i, 'bus');
  62. 1_singular(/oes$/i, 'o');
  63. 1_singular(/shoes$/i, 'shoe');
  64. 1_singular(/(alias|(?:stat|octop|vir|b)us)es$/i, '$1');
  65. 1_singular(/(vert|ind)ices$/i, '$1ex');
  66. 1_singular(/matrices$/i, 'matrix');
  67. 1_irregular('person', 'people');
  68. 1_irregular('man', 'men');
  69. 1_irregular('child', 'children');
  70. 1_irregular('sex', 'sexes');
  71. 1_irregular('move', 'moves');
  72. 1_irregular('quiz', 'quizzes');
  73. 1_irregular('testis', 'testes');
  74. 1_uncountable("equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "news");
  75. 1exports.singular = _singular;
  76. 1exports.plural = _plural;
  77. 1exports.uncountable = _uncountable;
  78. /**
  79. * Converts a string to camelcase
  80. *
  81. * @example
  82. * comb.camelize('hello_world') => helloWorld
  83. * comb.camelize('column_name') => columnName
  84. * comb.camelize('columnName') => columnName
  85. * comb.camelize(null) => null
  86. * comb.camelize() => undefined
  87. *
  88. * @param {String} str the string to camelize
  89. * @memberOf comb
  90. * @returns {String} the camelized version of the string
  91. */
  92. 1comb.camelize = function (str) {
  93. 14 var ret = str;
  94. 14 if (!misc.isUndefinedOrNull(str)) {
  95. 12 ret = str.replace(CAMELIZE_CONVERT_REGEXP, function (a, b) {
  96. 8 return b.toUpperCase();
  97. });
  98. }
  99. 14 return ret;
  100. };
  101. /**
  102. * The reverse of camelize. Makes an underscored form from the expression in the string.
  103. *
  104. * @example
  105. * comb.underscore('helloWorld') => hello_world
  106. * comb.underscore('column_name') => column_name
  107. * comb.underscore('columnName') => column_name
  108. * comb.underscore(null) => null
  109. * comb.underscore() => undefined
  110. * @param {String} str The string to underscore
  111. * @memberOf comb
  112. * @returns {String} the underscored version of the string
  113. * */
  114. 1comb.underscore = function (str) {
  115. 12 var ret = str;
  116. 12 if (!misc.isUndefinedOrNull(str)) {
  117. 10 ret = str.replace(UNDERSCORE_CONVERT_REGEXP1, UNDERSCORE_CONVERT_REPLACE)
  118. .replace(UNDERSCORE_CONVERT_REGEXP2, UNDERSCORE_CONVERT_REPLACE)
  119. .replace(DASH, UNDERSCORE).toLowerCase();
  120. }
  121. 12 return ret;
  122. };
  123. /**
  124. * Singularizes and camelizes the string. Also strips out all characters preceding
  125. * and including a period (".").
  126. *
  127. * @example
  128. * comb.classify('egg_and_hams') => "eggAndHam"
  129. * comb.classify('post') => "post"
  130. * comb.classify('schema.post') => "post"
  131. *
  132. * @param {String} str the string to classify
  133. * @memberOf comb
  134. * @returns {String} the classified version of the string
  135. **/
  136. 1comb.classify = function (str) {
  137. 8 var ret = str;
  138. 8 if (!misc.isUndefinedOrNull(str)) {
  139. 6 ret = comb.camelize(comb.singularize(str.replace(/.*\./g, '')));
  140. }
  141. 8 return ret;
  142. };
  143. /**
  144. * Returns the plural form of the word in the string.
  145. *
  146. * @example
  147. * comb.pluralize("post") => "posts"
  148. * comb.pluralize("octopus") => "octopi"
  149. * comb.pluralize("sheep") => "sheep"
  150. * comb.pluralize("words") => "words"
  151. * comb.pluralize("the blue mailman") => "the blue mailmen"
  152. * comb.pluralize("CamelOctopus") => "CamelOctopi"
  153. *
  154. * @param {String} str the string to pluralize
  155. * @memberOf comb
  156. * @returns {String} the pluralized version of the string
  157. **/
  158. 1comb.pluralize = function (str) {
  159. 100 var ret = str;
  160. 100 if (!misc.isUndefinedOrNull(str)) {
  161. 98 if (UNCOUNTABLES.indexOf(str) == -1) {
  162. 96 for (var i in PLURALS) {
  163. 1282 var s = PLURALS[i], rule = s[0], replacement = s[1];
  164. 1282 if ((ret = ret.replace(rule, replacement)) != str) {
  165. 96 break;
  166. }
  167. }
  168. }
  169. }
  170. 100 return ret;
  171. };
  172. /**
  173. * The reverse of pluralize, returns the singular form of a word in a string.
  174. *
  175. * @example
  176. * comb.singularize("posts") => "post"
  177. * comb.singularize("octopi")=> "octopus"
  178. * comb.singularize("sheep") => "sheep"
  179. * comb.singularize("word") => "word"
  180. * comb.singularize("the blue mailmen") => "the blue mailman"
  181. * comb.singularize("CamelOctopi") => "CamelOctopus"
  182. *
  183. * @param {String} str the string to singularize
  184. * @memberOf comb
  185. * @returns {String} the singularized version of the string
  186. * */
  187. 1comb.singularize = function (str) {
  188. 106 var ret = str;
  189. 106 if (!misc.isUndefinedOrNull(str)) {
  190. 104 if (UNCOUNTABLES.indexOf(str) == -1) {
  191. 102 for (var i in SINGULARS) {
  192. 1570 var s = SINGULARS[i], rule = s[0], replacement = s[1];
  193. 1570 if ((ret = ret.replace(rule, replacement)) != str) {
  194. 98 break;
  195. }
  196. }
  197. }
  198. }
  199. 106 return ret;
  200. };
base/misc.js
Coverage100.00 SLOC187 LOC51 Missed0
  1. 1var comb = exports,
  2. arraySlice = Array.prototype.slice;
  3. /**
  4. *
  5. * Converts an arguments object to an array
  6. *
  7. * @example
  8. *
  9. * function test(){
  10. * return comb.argsToArray(arguments);
  11. * }
  12. *
  13. * function testSlice(){
  14. * return comb.argsToArray(arguments, 3);
  15. * }
  16. *
  17. * console.log(test(1,2,3)); //[1,2,3]
  18. * console.log(test(1,2,3,4,5,6)); //[4,5,6]
  19. *
  20. * @function
  21. * @param {Arguments} args the arguments object to convert
  22. * @param {Number} [slice=0] the number of arguments to slice.
  23. * @memberOf comb
  24. * @static
  25. * @returns {Array} array version of the arguments object
  26. */
  27. 1function argsToArray(args, slice) {
  28. 21727 slice = slice || 0;
  29. 21727 return arraySlice.call(args, slice);
  30. }
  31. /**
  32. * Determines if obj is a boolean
  33. *
  34. * @param {Anything} obj the thing to test if it is a boolean
  35. *
  36. * @returns {Boolean} true if it is a boolean false otherwise
  37. * @memberOf comb
  38. * @static
  39. */
  40. 1function isBoolean(obj) {
  41. 11999 var undef, type = typeof obj;
  42. 11999 return obj != undef && type == "boolean" || type == "Boolean";
  43. }
  44. /**
  45. * Determines if obj is undefined
  46. *
  47. * @param {Anything} obj the thing to test if it is undefined
  48. * @returns {Boolean} true if it is undefined false otherwise
  49. * @memberOf comb
  50. * @static
  51. */
  52. 1function isUndefined(obj) {
  53. 10200 var undef;
  54. 10200 return obj !== null && obj === undef;
  55. }
  56. /**
  57. * Determins if the obj is not undefined
  58. *
  59. * @param obj the thing to test if it is not undefined
  60. *
  61. * @return {Boolean} true if it is defined false otherwise
  62. * @memberOf comb
  63. * @static
  64. */
  65. 1function isDefined(obj) {
  66. 12 return !isUndefined(obj);
  67. }
  68. /**
  69. * Determines if obj is undefined or null
  70. *
  71. * @param {Anything} obj the thing to test if it is undefined or null
  72. * @returns {Boolean} true if it is undefined or null false otherwise
  73. * @memberOf comb
  74. * @static
  75. */
  76. 1function isUndefinedOrNull(obj) {
  77. 9908 return isUndefined(obj) || isNull(obj);
  78. }
  79. /**
  80. * Determines if obj is null
  81. *
  82. * @param {Anything} obj the thing to test if it is null
  83. *
  84. * @returns {Boolean} true if it is null false otherwise
  85. * @memberOf comb
  86. * @static
  87. */
  88. 1function isNull(obj) {
  89. 9724 var undef;
  90. 9724 return obj !== undef && obj == null;
  91. }
  92. /**
  93. * Determines if obj is an Arguments object;
  94. *
  95. * @param {Anything} obj the thing to test if it is null
  96. *
  97. * @returns {Boolean} true if it is an Arguments Object false otherwise
  98. * @memberOf comb
  99. * @static
  100. */
  101. 1function isArguments(object) {
  102. 1237 return !isUndefinedOrNull(object) && Object.prototype.toString.call(object) == '[object Arguments]';
  103. }
  104. 1function isInstance(obj, clazz) {
  105. 7978 if (typeof clazz == "function") {
  106. 7977 return obj instanceof clazz;
  107. } else {
  108. 1 return false;
  109. }
  110. }
  111. /**
  112. * Determines if obj is an instance of a particular class
  113. *
  114. * @param {Anything} obj the thing to test if it and instance of a class
  115. * @param {Object} Clazz used to determine if the object is an instance of
  116. *
  117. * @returns {Boolean} true if it is an instance of the clazz false otherwise
  118. * @memberOf comb
  119. * @static
  120. */
  121. 1function isInstanceOf(obj, clazz) {
  122. 7978 return argsToArray(arguments, 1).some(function (c) {
  123. 7978 return isInstance(obj, c);
  124. });
  125. }
  126. 1(function () {
  127. 1 var listeners = [];
  128. 1 var setup = false;
  129. 1 function setupListener() {
  130. 13 if (!setup) {
  131. 1 var orig = process.emit;
  132. 1 process.emit = function (event) {
  133. 836 try {
  134. 836 if (event === 'exit') {
  135. 1 listeners.forEach(function (cb) {
  136. 13 cb();
  137. });
  138. }
  139. } finally {
  140. 836 orig.apply(this, arguments);
  141. }
  142. };
  143. 1 setup = true;
  144. }
  145. }
  146. /**
  147. * Adds listeners to process.exit without having to change setMaxListeners useful if you
  148. * are writing a library and do not want to change core setting.
  149. *
  150. * @param {Funciton} cb funciton to call when process is exiting
  151. * @memberOf comb
  152. * @static
  153. */
  154. 1 function listenForExit(cb) {
  155. 13 setupListener();
  156. 13 listeners.push(cb);
  157. }
  158. 1 comb.listenForExit = listenForExit;
  159. })();
  160. 1comb.argsToArray = argsToArray;
  161. 1comb.isBoolean = isBoolean;
  162. 1comb.isUndefined = isUndefined;
  163. 1comb.isDefined = isDefined;
  164. 1comb.isUndefinedOrNull = isUndefinedOrNull;
  165. 1comb.isNull = isNull;
  166. 1comb.isArguments = isArguments;
  167. 1comb.isInstanceOf = isInstanceOf;
base/number.js
Coverage100.00 SLOC66 LOC10 Missed0
  1. 1var comb = exports;
  2. /**
  3. * Determines if obj is a number
  4. *
  5. * @param {Anything} obj the thing to test if it is a Number
  6. *
  7. * @returns {Boolean} true if it is a number false otherwise
  8. */
  9. 1comb.isNumber = function(obj) {
  10. 2610 var undef;
  11. 2610 return obj !== undef && obj != null && (typeof obj == "number" || obj instanceof Number);
  12. };
  13. /**
  14. * @private
  15. */
  16. 1var round = Math.round, pow = Math.pow;
  17. /**
  18. * @namespace Utilities for numbers
  19. */
  20. 1comb.number = {
  21. /**@lends comb.number*/
  22. /**
  23. * Rounds a number to the specified places.
  24. *
  25. * @example
  26. *
  27. * comb.number.round(10.000009, 2); //10
  28. * comb.number.round(10.000009, 5); //10.00001
  29. * comb.number.round(10.0009, 3); //10.001
  30. * comb.number.round(10.0009, 2); //10
  31. * comb.number.round(10.0009, 3); //10.001
  32. *
  33. * @param {Number} num the number to round.
  34. * @param {Number} places the number of places to round to.
  35. */
  36. round : function(number, places, increment) {
  37. 12 increment = increment || 1e-20;
  38. 12 var factor = 10 / (10 * (increment || 10));
  39. 12 return (Math.ceil(factor * +number) / factor).toFixed(places) * 1; // Number
  40. },
  41. /**
  42. * Rounds a number to the specified places, rounding up.
  43. *
  44. * @example
  45. *
  46. * comb.number.roundCeil(10.000001, 2); //10.01
  47. * comb.number.roundCeil(10.000002, 5); //10.00001
  48. * comb.number.roundCeil(10.0003, 3); //10.001
  49. * comb.number.roundCeil(10.0004, 2); //10.01
  50. * comb.number.roundCeil(10.0005, 3); //10.001
  51. * comb.number.roundCeil(10.0002, 2); //10.01
  52. *
  53. * @param {Number} num the number to round.
  54. * @param {Number} places the number of places to round to.
  55. */
  56. roundCeil : function(number, places){
  57. 12 return Math.ceil(number * Math.pow(10, places))/Math.pow(10, places);
  58. }
  59. };
base/object.js
Coverage100.00 SLOC485 LOC139 Missed0
  1. 1var comb = exports,
  2. misc = require("./misc.js"),
  3. isUndefinedOrNull = misc.isUndefined,
  4. isArguments = misc.isArguments,
  5. pSlice = Array.prototype.slice;
  6. //taken from node js assert.js
  7. //https://github.com/joyent/node/blob/master/lib/assert.js
  8. 1function _deepEqual(actual, expected) {
  9. // 7.1. All identical values are equivalent, as determined by ===.
  10. 79 if (actual === expected) {
  11. 6 return true;
  12. 73 } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
  13. 2 if (actual.length != expected.length) return false;
  14. 2 for (var i = 0; i < actual.length; i++) {
  15. 6 if (actual[i] !== expected[i]) return false;
  16. }
  17. 2 return true;
  18. // 7.2. If the expected value is a Date object, the actual value is
  19. // equivalent if it is also a Date object that refers to the same time.
  20. 71 } else if (actual instanceof Date && expected instanceof Date) {
  21. 4 return actual.getTime() === expected.getTime();
  22. // 7.3 If the expected value is a RegExp object, the actual value is
  23. // equivalent if it is also a RegExp object with the same source and
  24. // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
  25. 67 } else if (actual instanceof RegExp && expected instanceof RegExp) {
  26. 4 return actual.source === expected.source &&
  27. actual.global === expected.global &&
  28. actual.multiline === expected.multiline &&
  29. actual.lastIndex === expected.lastIndex &&
  30. actual.ignoreCase === expected.ignoreCase;
  31. // 7.4. Other pairs that do not both pass typeof value == 'object',
  32. // equivalence is determined by ==.
  33. 63 } else if (typeof actual != 'object' && typeof expected != 'object') {
  34. 23 return actual == expected;
  35. // 7.5 For all other Object pairs, including Array objects, equivalence is
  36. // determined by having the same number of owned properties (as verified
  37. // with Object.prototype.hasOwnProperty.call), the same set of keys
  38. // (although not necessarily the same order), equivalent values for every
  39. // corresponding key, and an identical 'prototype' property. Note: this
  40. // accounts for both named and indexed properties on Arrays.
  41. } else {
  42. 40 return objEquiv(actual, expected);
  43. }
  44. }
  45. 1function objEquiv(a, b) {
  46. 40 if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
  47. 10 return false;
  48. // an identical 'prototype' property.
  49. 30 if (a.prototype !== b.prototype) return false;
  50. //~~~I've managed to break Object.keys through screwy arguments passing.
  51. // Converting to array solves the problem.
  52. 30 if (isArguments(a)) {
  53. 4 if (!isArguments(b)) {
  54. 2 return false;
  55. }
  56. 2 a = pSlice.call(a);
  57. 2 b = pSlice.call(b);
  58. 2 return _deepEqual(a, b);
  59. }
  60. 26 try {
  61. 26 var ka = Object.keys(a),
  62. kb = Object.keys(b),
  63. key, i;
  64. } catch (e) {//happens when one is a string literal and the other isn't
  65. 2 return false;
  66. }
  67. // having the same number of owned properties (keys incorporates
  68. // hasOwnProperty)
  69. 24 if (ka.length != kb.length)
  70. 4 return false;
  71. //the same set of keys (although not necessarily the same order),
  72. 20 ka.sort();
  73. 20 kb.sort();
  74. //~~~cheap key test
  75. 20 for (i = ka.length - 1; i >= 0; i--) {
  76. 20 if (ka[i] != kb[i])
  77. 4 return false;
  78. }
  79. //equivalent values for every corresponding key, and
  80. //~~~possibly expensive deep test
  81. 16 for (i = ka.length - 1; i >= 0; i--) {
  82. 16 key = ka[i];
  83. 22 if (!_deepEqual(a[key], b[key])) return false;
  84. }
  85. 10 return true;
  86. }
  87. 1function merge(target, source) {
  88. 467 var name, s;
  89. 467 for (name in source) {
  90. 638 s = source[name];
  91. 638 if (!(name in target) || (target[name] !== s)) {
  92. 636 target[name] = s;
  93. }
  94. }
  95. 467 return target;
  96. }
  97. 1function deepMerge(target, source) {
  98. 27 var name, s, t;
  99. 27 for (name in source) {
  100. 37 s = source[name], t = target[name];
  101. 37 if (!_deepEqual(t, s)) {
  102. 37 if (comb.isHash(t) && comb.isHash(s)) {
  103. 8 target[name] = deepMerge(t, s);
  104. 29 } else if (comb.isHash(s)) {
  105. 8 target[name] = deepMerge({}, s);
  106. } else {
  107. 21 target[name] = s;
  108. }
  109. }
  110. }
  111. 27 return target;
  112. }
  113. /**
  114. * Determines if an object is just a hash and not a qualified Object such as Number
  115. *
  116. * @example
  117. * comb.isHash({}) => true
  118. * comb.isHash({1 : 2, a : "b"}) => true
  119. * comb.isHash(new Date()) => false
  120. * comb.isHash(new String()) => false
  121. * comb.isHash(new Number()) => false
  122. * comb.isHash(new Boolean()) => false
  123. * comb.isHash() => false
  124. * comb.isHash("") => false
  125. * comb.isHash(1) => false
  126. * comb.isHash(false) => false
  127. * comb.isHash(true) => false
  128. * @param {Anything} obj the thing to test if it is a hash
  129. *
  130. * @returns {Boolean} true if it is a hash false otherwise
  131. * @memberOf comb
  132. */
  133. 1function isHash(obj) {
  134. 210 var ret = comb.isObject(obj);
  135. 210 return ret && obj.constructor === Object;
  136. }
  137. /**
  138. * Merges objects together
  139. * NOTE: this function takes a variable number of objects to merge
  140. *
  141. * @example
  142. *
  143. * var myObj = {};
  144. * comb.merge(myObj, {test : true});
  145. *
  146. * myObj.test => true
  147. *
  148. * comb.merge(myObj, {test : false}, {test2 : false}, {test3 : "hello", test4 : "world"});
  149. * myObj.test => false
  150. * myObj.test2 => false
  151. * myObj.test3 => "hello"
  152. * myObj.test4 => "world"
  153. *
  154. *
  155. * @param {Object} obj the object to merge into
  156. * @param {Object} props variable number of objects to merge into the obj
  157. *
  158. * @returns {Object} the merged object
  159. * @memberOf comb
  160. * @name merge
  161. */
  162. 1function combMerge(obj, props) {
  163. 442 if (!obj) {
  164. 1 obj = {};
  165. }
  166. 442 for (var i = 1, l = arguments.length; i < l; i++) {
  167. 463 merge(obj, arguments[i]);
  168. }
  169. 442 return obj; // Object
  170. }
  171. 1;
  172. /**
  173. * Merges objects together only overriding properties that are different.
  174. * NOTE: this function takes a variable number of objects to merge
  175. *
  176. * @example
  177. *
  178. * var myObj = {my : {cool : {property1 : 1, property2 : 2}}};
  179. * comb.deepMerge(myObj, {my : {cool : {property3 : 3}}});
  180. *
  181. * myObj.my.cool.property1 => 1
  182. * myObj.my.cool.property2 => 2
  183. * myObj.my.cool.property3 => 3
  184. *
  185. *
  186. * @param {Object} obj the object to merge into
  187. * @param {Object} props variable number of objects to merge into the obj
  188. *
  189. * @returns {Object} the merged object
  190. * @memberOf comb
  191. * @name deepMerge
  192. */
  193. 1function combDeepMerge(obj, props) {
  194. 5 if (!obj) {
  195. 1 obj = {};
  196. }
  197. 5 for (var i = 1, l = arguments.length; i < l; i++) {
  198. 11 deepMerge(obj, arguments[i]);
  199. }
  200. 5 return obj; // Object
  201. }
  202. 1;
  203. /**
  204. * Extends the prototype of an object if it exists otherwise it extends the object.
  205. *
  206. * @example
  207. *
  208. * var MyObj = function(){};
  209. * MyObj.prototype.test = true;
  210. * comb.extend(MyObj, {test2 : false, test3 : "hello", test4 : "world"});
  211. *
  212. * var myObj = new MyObj();
  213. *
  214. * myObj.test => true
  215. * myObj.test2 => false
  216. * myObj.test3 => "hello"
  217. * myObj.test4 => "world"
  218. *
  219. * var myObj2 = {};
  220. * myObj2.test = true;
  221. * comb.extend(myObj2, {test2 : false, test3 : "hello", test4 : "world"});
  222. *
  223. * myObj2.test => true
  224. * myObj2.test2 => false
  225. * myObj2.test3 => "hello"
  226. * myObj2.test4 => "world"
  227. *
  228. *
  229. * @param {Object} parent the parent object to extend
  230. * @param {Object} extend the extension object to mixin to the parent
  231. *
  232. * @returns {Object} returns the extended object
  233. * @memberOf comb
  234. */
  235. 1function extend(parent, extend) {
  236. 4 var proto = parent.prototype || parent;
  237. 4 merge(proto, extend);
  238. 4 return parent;
  239. }
  240. /**
  241. * Determines if obj is an object
  242. *
  243. * @param {Anything} obj the thing to test if it is an object
  244. *
  245. * @returns {Boolean} true if it is an object false otherwise
  246. * @memberOf comb
  247. */
  248. 1function isObject(obj) {
  249. 1432 var undef;
  250. 1432 return obj !== null && obj !== undef && typeof obj === "object";
  251. }
  252. /**
  253. * Determines if an object is empty
  254. *
  255. * @example
  256. *
  257. * comb.isEmpty({}) => true
  258. * comb.isEmpty({a : 1}) => false
  259. *
  260. * @param object the object to test
  261. * @returns {Boolean} true if the object is empty;
  262. * @memberOf comb
  263. */
  264. 1function isEmpty(object) {
  265. 9 if (comb.isObject(object)) {
  266. 8 for (var i in object) {
  267. 4 if (object.hasOwnProperty(i)) {
  268. 4 return false;
  269. }
  270. }
  271. }
  272. 5 return true;
  273. }
  274. /**
  275. * Determines if two things are deep equal.
  276. *
  277. * @example
  278. *
  279. * comb.deepEqual({a : 1, b : 2}, {a : 1, b : 2}) => true
  280. * comb.deepEqual({a : 1}, {a : 1, b : 2}) => false
  281. *
  282. * @param o1 the first thing to compare
  283. * @param o3 the second thing to compare
  284. * @return {Boolean}
  285. * @memberOf comb
  286. */
  287. 1function deepEqual(o1, o2) {
  288. 24 return _deepEqual(o1, o2);
  289. }
  290. 1comb.isHash = isHash;
  291. 1comb.isEmpty = isEmpty;
  292. 1comb.deepEqual = deepEqual;
  293. 1comb.isObject = isObject;
  294. 1comb.extend = extend;
  295. 1comb.deepMerge = combDeepMerge;
  296. 1comb.merge = combMerge;
  297. /**
  298. * Loops through each k/v in a hash.
  299. *
  300. * ```
  301. * var obj = {a : "b", c : "d", e : "f"};
  302. * comb(obj).forEach(function(value, key){
  303. * console.log(value, key);
  304. * });
  305. *
  306. * comb.hash.forEach(obj, function(){
  307. * console.log(value, key);
  308. * });
  309. *
  310. * ```
  311. * @param {Object} hash the hash to iterate
  312. * @param {Function} iterator the interator function. Called with (key, value, hash).
  313. * @param {Object} [scope=hash] the scope to invoke the interator in.
  314. * @return {Object} the original hash.
  315. * @memberOf comb.hash
  316. */
  317. 1function forEach(hash, iterator, scope) {
  318. 6 if (!isHash(hash) || typeof iterator !== "function") {
  319. 4 throw new TypeError();
  320. }
  321. 2 var keys = Object.keys(hash), key;
  322. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  323. 6 key = keys[i];
  324. 6 iterator.call(scope || hash, hash[key], key, hash);
  325. }
  326. 2 return hash;
  327. }
  328. /**
  329. * Filters out key/value pairs in an object. Filters out key/value pairs that return a falsey value from the iterator.
  330. *
  331. * ```
  332. * var obj = {a : "b", c : "d", e : "f"};
  333. * comb(obj).filter(function(value, key){
  334. * return value == "b" || key === "e";
  335. * }); //{a : "b", e : "f"};
  336. *
  337. * comb.hash.filter(obj, function(){
  338. * return value == "b" || key === "e";
  339. * }); //{a : "b", e : "f"};
  340. *
  341. * ```
  342. * @param {Object} hash the hash to filter.
  343. * @param {Function} iterator the interator function. Called with (key, value, hash).
  344. * @param {Object} [scope=hash] the scope to invoke the interator in.
  345. * @return {Object} a new object with the values that returned true..
  346. * @memberOf comb.hash
  347. */
  348. 1function filter(hash, iterator, scope) {
  349. 6 if (!isHash(hash) || typeof iterator !== "function") {
  350. 4 throw new TypeError();
  351. }
  352. 2 var keys = Object.keys(hash), key, value, ret = {};
  353. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  354. 6 key = keys[i];
  355. 6 value = hash[key];
  356. 6 if (iterator.call(scope || hash, value, key, hash)) {
  357. 4 ret[key] = value;
  358. }
  359. }
  360. 2 return ret;
  361. }
  362. /**
  363. * Returns the values of a hash.
  364. *
  365. * ```
  366. * var obj = {a : "b", c : "d", e : "f"};
  367. * comb(obj).values(); //["b", "d", "f"]
  368. *
  369. * comb.hash.values(obj); //["b", "d", "f"]
  370. *
  371. * ```
  372. *
  373. * @param {Object} hash the object to retrieve the values of.
  374. * @return {Array} array of values.
  375. * @memberOf comb.hash
  376. */
  377. 1function values(hash) {
  378. 5 if (!isHash(hash)) {
  379. 3 throw new TypeError();
  380. }
  381. 2 var keys = Object.keys(hash), ret = [];
  382. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  383. 6 ret.push(hash[keys[i]]);
  384. }
  385. 2 return ret;
  386. }
  387. /**
  388. * Returns a new hash that is the invert of the hash.
  389. *
  390. * ```
  391. * var obj = {a : "b", c : "d", e : "f"};
  392. * comb(obj).invert(); //{b : "a", d : "c", f : "e"}
  393. *
  394. * comb.hash.invert(obj); //{b : "a", d : "c", f : "e"}
  395. * ```
  396. *
  397. * @param {Object} hash the hash to invert.
  398. * @return {Object} A new hash that is the invert of hash.
  399. * @memberOf comb.hash
  400. */
  401. 1function invert(hash) {
  402. 5 if (!isHash(hash)) {
  403. 3 throw new TypeError();
  404. }
  405. 2 var keys = Object.keys(hash), key, ret = {};
  406. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  407. 6 key = keys[i];
  408. 6 ret[hash[key]] = key;
  409. }
  410. 2 return ret;
  411. }
  412. /**
  413. * Converts a hash to an array.
  414. *
  415. * ```
  416. * var obj = {a : "b", c : "d", e : "f"};
  417. * comb(obj).toArray(); //[["a", "b"], ["c", "d"], ["e", "f"]]
  418. *
  419. * comb.hash.toArray(obj); //[["a", "b"], ["c", "d"], ["e", "f"]]
  420. * ```
  421. *
  422. * @param {Object} hash the hash to convert to an array.
  423. * @return {Array} a two dimensional array representing the hash.
  424. * @memberOf comb.hash
  425. */
  426. 1function toArray(hash) {
  427. 5 if (!isHash(hash)) {
  428. 3 throw new TypeError();
  429. }
  430. 2 var keys = Object.keys(hash), key, ret = [];
  431. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  432. 6 key = keys[i];
  433. 6 ret.push([key, hash[key]]);
  434. }
  435. 2 return ret;
  436. }
  437. /**
  438. * @namespace utilities for working with hases i.e. {}
  439. * @ignoreCode
  440. */
  441. 1comb.hash = {
  442. forEach:forEach,
  443. filter:filter,
  444. invert:invert,
  445. values:values,
  446. toArray:toArray
  447. };
base/regexp.js
Coverage100.00 SLOC48 LOC11 Missed0
  1. 1var comb = exports;
  2. /**
  3. * Tests if something is a regular expression.
  4. *
  5. * @example
  6. *
  7. * comb.isRegExp(/hello/); //true
  8. * comb.isRegExp("hello"); //false
  9. *
  10. * @param obj the thing to test.
  11. * @return {Boolean}
  12. * @static
  13. * @memberOf comb
  14. *
  15. */
  16. 1function isRegExp(obj) {
  17. 13 var undef;
  18. 13 return obj !== undef && obj != null && (obj instanceof RegExp);
  19. }
  20. 1comb.isRexExp = isRegExp;
  21. 1comb.isRegExp = isRegExp;
  22. /**
  23. * @namespace Regeular expression utilities
  24. *
  25. */
  26. 1comb.regexp = {
  27. /**@lends comb.regexp*/
  28. /**
  29. * Escapes a string
  30. *
  31. * @param {String} str the string to escape
  32. * @param {String} [except] characters to ignore
  33. *
  34. * @returns {String} the escaped string
  35. */
  36. escapeString:function (str, except) {
  37. 46 return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function (ch) {
  38. 46 if (except && except.indexOf(ch) != -1) {
  39. 15 return ch;
  40. }
  41. 31 return "\\" + ch;
  42. }); // String
  43. }
  44. };
base/string.js
Coverage100.00 SLOC1187 LOC131 Missed0
  1. 1var comb = exports, date,
  2. misc = require("./misc"),
  3. object = require("./object"),
  4. isHash = object.isHash;
  5. /**
  6. * Tests if something is a string.
  7. *
  8. * @example
  9. *
  10. * comb.isString("true") //true
  11. * comb.isString(true) //false
  12. *
  13. * @param obj the thing to test
  14. * @return {Boolean} returns true if the argument is a string.
  15. * @static
  16. * @memberOf comb
  17. */
  18. 1function isString(obj) {
  19. 35924 var undef;
  20. 35924 return obj != undef && (typeof obj == "string" || obj instanceof String);
  21. }
  22. 1comb.isString = isString;
  23. 1var FORMAT_REGEX = /%((?:-?\+?.?\d*)?|(?:\[[^\[|\]]*\]))?([sjdDZ])/g;
  24. 1var INTERP_REGEX = /{(?:\[([^\[|\]]*)\])?(\w+)}/g;
  25. 1var STR_FORMAT = /(-?)(\+?)([A-Z|a-z|\W]?)([1-9][0-9]*)?$/;
  26. 1var OBJECT_FORMAT = /([1-9][0-9]*)$/g;
  27. 1var formatString = function (string, format) {
  28. 66 var match = format.match(STR_FORMAT), ret = string, cString = comb.string;
  29. 66 if (match) {
  30. 66 var isLeftJustified = match[1], padChar = match[3], width = match[4];
  31. 66 if (width) {
  32. 66 width = parseInt(width);
  33. 66 if (ret.length < width) {
  34. 36 ret = cString.pad(ret, width, padChar, isLeftJustified);
  35. } else {
  36. 30 ret = cString.truncate(ret, width);
  37. }
  38. }
  39. }
  40. 66 return ret;
  41. };
  42. 1var formatNumber = function (number, format) {
  43. 16 if (typeof number == "number") {
  44. 14 var cString = comb.string, ret = "" + number;
  45. 14 var match = format.match(STR_FORMAT);
  46. 14 if (match) {
  47. 14 var isLeftJustified = match[1], signed = match[2], padChar = match[3], width = match[4];
  48. 14 if (signed) {
  49. 6 ret = (number > 0 ? "+" : "") + ret;
  50. }
  51. 14 if (width) {
  52. 10 width = parseInt(width);
  53. 10 if (ret.length < width) {
  54. 8 ret = cString.pad(ret, width, padChar || "0", isLeftJustified);
  55. } else {
  56. 2 ret = cString.truncate(ret, width);
  57. }
  58. }
  59. }
  60. } else {
  61. 2 throw new Error("comb.string.format : when using %d the parameter must be a number!");
  62. }
  63. 14 return ret;
  64. };
  65. 1var formatObject = function (object, format) {
  66. 8 var ret, match = format.match(OBJECT_FORMAT), spacing = 0;
  67. 8 if (match) {
  68. 8 spacing = parseInt(match[0]);
  69. 8 if (isNaN(spacing)) spacing = 0;
  70. }
  71. 8 try {
  72. 8 ret = JSON.stringify(object, null, spacing);
  73. } catch (e) {
  74. 2 throw new Error("comb.string.format : Unable to parse json from ", object);
  75. }
  76. 6 return ret;
  77. };
  78. 1var styles = {
  79. //styles
  80. bold:1,
  81. bright:1,
  82. italic:3,
  83. underline:4,
  84. blink:5,
  85. inverse:7,
  86. crossedOut:9,
  87. red:31,
  88. green:32,
  89. yellow:33,
  90. blue:34,
  91. magenta:35,
  92. cyan:36,
  93. white:37,
  94. redBackground:41,
  95. greenBackground:42,
  96. yellowBackground:43,
  97. blueBackground:44,
  98. magentaBackground:45,
  99. cyanBackground:46,
  100. whiteBackground:47,
  101. encircled:52,
  102. overlined:53,
  103. grey:90,
  104. black:90
  105. };
  106. /**@namespace comb characters*/
  107. 1comb.characters = {
  108. /**@lends comb.characters*/
  109. /**
  110. * ☺
  111. */
  112. SMILEY:"☺",
  113. /**
  114. * ☻
  115. */
  116. SOLID_SMILEY:"☻",
  117. /**
  118. * ♥
  119. */
  120. HEART:"♥",
  121. /**
  122. * ♦
  123. */
  124. DIAMOND:"♦",
  125. /**
  126. * ♣
  127. */
  128. CLOVE:"♣",
  129. /**
  130. * â™ 
  131. */
  132. SPADE:"â™ ",
  133. /**
  134. * •
  135. */
  136. DOT:"•",
  137. /**
  138. * â—˜
  139. */
  140. SQUARE_CIRCLE:"â—˜",
  141. /**
  142. * â—‹
  143. */
  144. CIRCLE:"â—‹",
  145. /**
  146. * â—™
  147. */
  148. FILLED_SQUARE_CIRCLE:"â—™",
  149. /**
  150. * ♂
  151. */
  152. MALE:"♂",
  153. /**
  154. * ♀
  155. */
  156. FEMALE:"♀",
  157. /**
  158. * ♪
  159. */
  160. EIGHT_NOTE:"♪",
  161. /**
  162. * ♫
  163. */
  164. DOUBLE_EIGHT_NOTE:"♫",
  165. /**
  166. * ☼
  167. */
  168. SUN:"☼",
  169. /**
  170. * â–º
  171. */
  172. PLAY:"â–º",
  173. /**
  174. * â—„
  175. */
  176. REWIND:"â—„",
  177. /**
  178. * ↕
  179. */
  180. UP_DOWN:"↕",
  181. /**
  182. * ¶
  183. */
  184. PILCROW:"¶",
  185. /**
  186. * §
  187. */
  188. SECTION:"§",
  189. /**
  190. * â–¬
  191. */
  192. THICK_MINUS:"â–¬",
  193. /**
  194. * ↨
  195. */
  196. SMALL_UP_DOWN:"↨",
  197. /**
  198. * ↑
  199. */
  200. UP_ARROW:"↑",
  201. /**
  202. * ↓
  203. */
  204. DOWN_ARROW:"↓",
  205. /**
  206. * →
  207. */
  208. RIGHT_ARROW:"→",
  209. /**
  210. * ←
  211. */
  212. LEFT_ARROW:"←",
  213. /**
  214. * ∟
  215. */
  216. RIGHT_ANGLE:"∟",
  217. /**
  218. * ↔
  219. */
  220. LEFT_RIGHT_ARROW:"↔",
  221. /**
  222. * â–²
  223. */
  224. TRIANGLE:"â–²",
  225. /**
  226. * â–¼
  227. */
  228. DOWN_TRIANGLE:"â–¼",
  229. /**
  230. * ⌂
  231. */
  232. HOUSE:"⌂",
  233. /**
  234. * Ç
  235. */
  236. C_CEDILLA:"Ç",
  237. /**
  238. * ü
  239. */
  240. U_UMLAUT:"ü",
  241. /**
  242. * é
  243. */
  244. E_ACCENT:"é",
  245. /**
  246. * â
  247. */
  248. A_LOWER_CIRCUMFLEX:"â",
  249. /**
  250. * ä
  251. */
  252. A_LOWER_UMLAUT:"ä",
  253. /**
  254. * à
  255. */
  256. A_LOWER_GRAVE_ACCENT:"à",
  257. /**
  258. * å
  259. */
  260. A_LOWER_CIRCLE_OVER:"Ã¥",
  261. /**
  262. * ç
  263. */
  264. C_LOWER_CIRCUMFLEX:"ç",
  265. /**
  266. * ê
  267. */
  268. E_LOWER_CIRCUMFLEX:"ê",
  269. /**
  270. * ë
  271. */
  272. E_LOWER_UMLAUT:"ë",
  273. /**
  274. * è
  275. */
  276. E_LOWER_GRAVE_ACCENT:"è",
  277. /**
  278. * ï
  279. */
  280. I_LOWER_UMLAUT:"ï",
  281. /**
  282. * î
  283. */
  284. I_LOWER_CIRCUMFLEX:"î",
  285. /**
  286. * ì
  287. */
  288. I_LOWER_GRAVE_ACCENT:"ì",
  289. /**
  290. * Ä
  291. */
  292. A_UPPER_UMLAUT:"Ä",
  293. /**
  294. * Ã…
  295. */
  296. A_UPPER_CIRCLE:"Ã…",
  297. /**
  298. * É
  299. */
  300. E_UPPER_ACCENT:"É",
  301. /**
  302. * æ
  303. */
  304. A_E_LOWER:"æ",
  305. /**
  306. * Æ
  307. */
  308. A_E_UPPER:"Æ",
  309. /**
  310. * ô
  311. */
  312. O_LOWER_CIRCUMFLEX:"ô",
  313. /**
  314. * ö
  315. */
  316. O_LOWER_UMLAUT:"ö",
  317. /**
  318. * ò
  319. */
  320. O_LOWER_GRAVE_ACCENT:"ò",
  321. /**
  322. * û
  323. */
  324. U_LOWER_CIRCUMFLEX:"û",
  325. /**
  326. * ù
  327. */
  328. U_LOWER_GRAVE_ACCENT:"ù",
  329. /**
  330. * ÿ
  331. */
  332. Y_LOWER_UMLAUT:"ÿ",
  333. /**
  334. * Ö
  335. */
  336. O_UPPER_UMLAUT:"Ö",
  337. /**
  338. * Ü
  339. */
  340. U_UPPER_UMLAUT:"Ü",
  341. /**
  342. * ¢
  343. */
  344. CENTS:"¢",
  345. /**
  346. * £
  347. */
  348. POUND:"£",
  349. /**
  350. * ¥
  351. */
  352. YEN:"Â¥",
  353. /**
  354. * ¤
  355. */
  356. CURRENCY:"¤",
  357. /**
  358. * â‚§
  359. */
  360. PTS:"â‚§",
  361. /**
  362. * Æ’
  363. */
  364. FUNCTION:"Æ’",
  365. /**
  366. * á
  367. */
  368. A_LOWER_ACCENT:"á",
  369. /**
  370. * í
  371. */
  372. I_LOWER_ACCENT:"í",
  373. /**
  374. * ó
  375. */
  376. O_LOWER_ACCENT:"ó",
  377. /**
  378. * ú
  379. */
  380. U_LOWER_ACCENT:"ú",
  381. /**
  382. * ñ
  383. */
  384. N_LOWER_TILDE:"ñ",
  385. /**
  386. * Ñ
  387. */
  388. N_UPPER_TILDE:"Ñ",
  389. /**
  390. * ª
  391. */
  392. A_SUPER:"ª",
  393. /**
  394. * º
  395. */
  396. O_SUPER:"º",
  397. /**
  398. * ¿
  399. */
  400. UPSIDEDOWN_QUESTION:"¿",
  401. /**
  402. * ⌐
  403. */
  404. SIDEWAYS_L:"⌐",
  405. /**
  406. * ¬
  407. */
  408. NEGATION:"¬",
  409. /**
  410. * ½
  411. */
  412. ONE_HALF:"½",
  413. /**
  414. * ¼
  415. */
  416. ONE_FOURTH:"¼",
  417. /**
  418. * ¡
  419. */
  420. UPSIDEDOWN_EXCLAMATION:"¡",
  421. /**
  422. * «
  423. */
  424. DOUBLE_LEFT:"«",
  425. /**
  426. * »
  427. */
  428. DOUBLE_RIGHT:"»",
  429. /**
  430. * â–‘
  431. */
  432. LIGHT_SHADED_BOX:"â–‘",
  433. /**
  434. * â–’
  435. */
  436. MEDIUM_SHADED_BOX:"â–’",
  437. /**
  438. * â–“
  439. */
  440. DARK_SHADED_BOX:"â–“",
  441. /**
  442. * │
  443. */
  444. VERTICAL_LINE:"│",
  445. /**
  446. * ┤
  447. */
  448. MAZE__SINGLE_RIGHT_T:"┤",
  449. /**
  450. * ┐
  451. */
  452. MAZE_SINGLE_RIGHT_TOP:"┐",
  453. /**
  454. * ┘
  455. */
  456. MAZE_SINGLE_RIGHT_BOTTOM_SMALL:"┘",
  457. /**
  458. * ┌
  459. */
  460. MAZE_SINGLE_LEFT_TOP_SMALL:"┌",
  461. /**
  462. * â””
  463. */
  464. MAZE_SINGLE_LEFT_BOTTOM_SMALL:"â””",
  465. /**
  466. * ├
  467. */
  468. MAZE_SINGLE_LEFT_T:"├",
  469. /**
  470. * â”´
  471. */
  472. MAZE_SINGLE_BOTTOM_T:"â”´",
  473. /**
  474. * ┬
  475. */
  476. MAZE_SINGLE_TOP_T:"┬",
  477. /**
  478. * ┼
  479. */
  480. MAZE_SINGLE_CENTER:"┼",
  481. /**
  482. * ─
  483. */
  484. MAZE_SINGLE_HORIZONTAL_LINE:"─",
  485. /**
  486. * â•¡
  487. */
  488. MAZE_SINGLE_RIGHT_DOUBLECENTER_T:"â•¡",
  489. /**
  490. * â•›
  491. */
  492. MAZE_SINGLE_RIGHT_DOUBLE_BL:"â•›",
  493. /**
  494. * â•¢
  495. */
  496. MAZE_SINGLE_RIGHT_DOUBLE_T:"â•¢",
  497. /**
  498. * â•–
  499. */
  500. MAZE_SINGLE_RIGHT_DOUBLEBOTTOM_TOP:"â•–",
  501. /**
  502. * â••
  503. */
  504. MAZE_SINGLE_RIGHT_DOUBLELEFT_TOP:"â••",
  505. /**
  506. * ╞
  507. */
  508. MAZE_SINGLE_LEFT_DOUBLE_T:"╞",
  509. /**
  510. * â•§
  511. */
  512. MAZE_SINGLE_BOTTOM_DOUBLE_T:"â•§",
  513. /**
  514. * ╤
  515. */
  516. MAZE_SINGLE_TOP_DOUBLE_T:"╤",
  517. /**
  518. * â•¥
  519. */
  520. MAZE_SINGLE_TOP_DOUBLECENTER_T:"â•¥",
  521. /**
  522. * ╨
  523. */
  524. MAZE_SINGLE_BOTTOM_DOUBLECENTER_T:"╨",
  525. /**
  526. * ╘
  527. */
  528. MAZE_SINGLE_LEFT_DOUBLERIGHT_BOTTOM:"╘",
  529. /**
  530. * â•’
  531. */
  532. MAZE_SINGLE_LEFT_DOUBLERIGHT_TOP:"â•’",
  533. /**
  534. * â•“
  535. */
  536. MAZE_SINGLE_LEFT_DOUBLEBOTTOM_TOP:"â•“",
  537. /**
  538. * â•™
  539. */
  540. MAZE_SINGLE_LEFT_DOUBLETOP_BOTTOM:"â•™",
  541. /**
  542. * Γ
  543. */
  544. MAZE_SINGLE_LEFT_TOP:"Γ",
  545. /**
  546. * ╜
  547. */
  548. MAZE_SINGLE_RIGHT_BOTTOM:"╜",
  549. /**
  550. * ╟
  551. */
  552. MAZE_SINGLE_LEFT_CENTER:"╟",
  553. /**
  554. * â•«
  555. */
  556. MAZE_SINGLE_DOUBLECENTER_CENTER:"â•«",
  557. /**
  558. * ╪
  559. */
  560. MAZE_SINGLE_DOUBLECROSS_CENTER:"╪",
  561. /**
  562. * â•£
  563. */
  564. MAZE_DOUBLE_LEFT_CENTER:"â•£",
  565. /**
  566. * â•‘
  567. */
  568. MAZE_DOUBLE_VERTICAL:"â•‘",
  569. /**
  570. * â•—
  571. */
  572. MAZE_DOUBLE_RIGHT_TOP:"â•—",
  573. /**
  574. * ╝
  575. */
  576. MAZE_DOUBLE_RIGHT_BOTTOM:"╝",
  577. /**
  578. * ╚
  579. */
  580. MAZE_DOUBLE_LEFT_BOTTOM:"╚",
  581. /**
  582. * â•”
  583. */
  584. MAZE_DOUBLE_LEFT_TOP:"â•”",
  585. /**
  586. * â•©
  587. */
  588. MAZE_DOUBLE_BOTTOM_T:"â•©",
  589. /**
  590. * ╦
  591. */
  592. MAZE_DOUBLE_TOP_T:"╦",
  593. /**
  594. * â• 
  595. */
  596. MAZE_DOUBLE_LEFT_T:"â• ",
  597. /**
  598. * ═
  599. */
  600. MAZE_DOUBLE_HORIZONTAL:"═",
  601. /**
  602. * ╬
  603. */
  604. MAZE_DOUBLE_CROSS:"╬",
  605. /**
  606. * â–ˆ
  607. */
  608. SOLID_RECTANGLE:"â–ˆ",
  609. /**
  610. * ▌
  611. */
  612. THICK_LEFT_VERTICAL:"▌",
  613. /**
  614. * ▐
  615. */
  616. THICK_RIGHT_VERTICAL:"▐",
  617. /**
  618. * â–„
  619. */
  620. SOLID_SMALL_RECTANGLE_BOTTOM:"â–„",
  621. /**
  622. * â–€
  623. */
  624. SOLID_SMALL_RECTANGLE_TOP:"â–€",
  625. /**
  626. * Φ
  627. */
  628. PHI_UPPER:"Φ",
  629. /**
  630. * ∞
  631. */
  632. INFINITY:"∞",
  633. /**
  634. * ∩
  635. */
  636. INTERSECTION:"∩",
  637. /**
  638. * ≡
  639. */
  640. DEFINITION:"≡",
  641. /**
  642. * ±
  643. */
  644. PLUS_MINUS:"±",
  645. /**
  646. * ≥
  647. */
  648. GT_EQ:"≥",
  649. /**
  650. * ≤
  651. */
  652. LT_EQ:"≤",
  653. /**
  654. * ⌠
  655. */
  656. THEREFORE:"⌠",
  657. /**
  658. * ∵
  659. */
  660. SINCE:"∵",
  661. /**
  662. * ∄
  663. */
  664. DOESNOT_EXIST:"∄",
  665. /**
  666. * ∃
  667. */
  668. EXISTS:"∃",
  669. /**
  670. * ∀
  671. */
  672. FOR_ALL:"∀",
  673. /**
  674. * ⊕
  675. */
  676. EXCLUSIVE_OR:"⊕",
  677. /**
  678. * ⌡
  679. */
  680. BECAUSE:"⌡",
  681. /**
  682. * ÷
  683. */
  684. DIVIDE:"÷",
  685. /**
  686. * ≈
  687. */
  688. APPROX:"≈",
  689. /**
  690. * °
  691. */
  692. DEGREE:"°",
  693. /**
  694. * ∙
  695. */
  696. BOLD_DOT:"∙",
  697. /**
  698. * ·
  699. */
  700. DOT_SMALL:"·",
  701. /**
  702. * √
  703. */
  704. CHECK:"√",
  705. /**
  706. * ✗
  707. */
  708. ITALIC_X:"✗",
  709. /**
  710. * ⁿ
  711. */
  712. SUPER_N:"ⁿ",
  713. /**
  714. * ²
  715. */
  716. SQUARED:"²",
  717. /**
  718. * ³
  719. */
  720. CUBED:"³",
  721. /**
  722. * â– 
  723. */
  724. SOLID_BOX:"â– ",
  725. /**
  726. * ‰
  727. */
  728. PERMILE:"‰",
  729. /**
  730. * ®
  731. */
  732. REGISTERED_TM:"®",
  733. /**
  734. * ©
  735. */
  736. COPYRIGHT:"©",
  737. /**
  738. * â„¢
  739. */
  740. TRADEMARK:"â„¢",
  741. /**
  742. * β
  743. */
  744. BETA:"β",
  745. /**
  746. * γ
  747. */
  748. GAMMA:"γ",
  749. /**
  750. * ζ
  751. */
  752. ZETA:"ζ",
  753. /**
  754. * η
  755. */
  756. ETA:"η",
  757. /**
  758. * ι
  759. */
  760. IOTA:"ι",
  761. /**
  762. * κ
  763. */
  764. KAPPA:"κ",
  765. /**
  766. * λ
  767. */
  768. LAMBDA:"λ",
  769. /**
  770. * ν
  771. */
  772. NU:"ν",
  773. /**
  774. * ξ
  775. */
  776. XI:"ξ",
  777. /**
  778. * ο
  779. */
  780. OMICRON:"ο",
  781. /**
  782. * ρ
  783. */
  784. RHO:"ρ",
  785. /**
  786. * Ï…
  787. */
  788. UPSILON:"Ï…",
  789. /**
  790. * φ
  791. */
  792. CHI_LOWER:"φ",
  793. /**
  794. * χ
  795. */
  796. CHI_UPPER:"χ",
  797. /**
  798. * ψ
  799. */
  800. PSI:"ψ",
  801. /**
  802. * α
  803. */
  804. ALPHA:"α",
  805. /**
  806. * ß
  807. */
  808. ESZETT:"ß",
  809. /**
  810. * π
  811. */
  812. PI:"Ï€",
  813. /**
  814. * Σ
  815. */
  816. SIGMA_UPPER:"Σ",
  817. /**
  818. * σ
  819. */
  820. SIGMA_LOWER:"σ",
  821. /**
  822. * µ
  823. */
  824. MU:"µ",
  825. /**
  826. * Ï„
  827. */
  828. TAU:"Ï„",
  829. /**
  830. * Θ
  831. */
  832. THETA:"Θ",
  833. /**
  834. * Ω
  835. */
  836. OMEGA:"Ω",
  837. /**
  838. * δ
  839. */
  840. DELTA:"δ",
  841. /**
  842. * φ
  843. */
  844. PHI_LOWER:"φ",
  845. /**
  846. * ε
  847. */
  848. EPSILON:"ε"
  849. };
  850. /**@namespace String utilities*/
  851. 1comb.string = {
  852. /**@lends comb.string*/
  853. /**
  854. * Pads a string
  855. *
  856. * @example
  857. *
  858. * comb.string.pad("STR", 5, " ", true) => "STR "
  859. * comb.string.pad("STR", 5, "$") => "$$STR"
  860. *
  861. * @param {String} string the string to pad
  862. * @param {Number} length the length of the string when padded
  863. * @param {String} [ch= " "] character to pad the string with
  864. * @param {Boolean} [end=false] if true then the padding is added to the end
  865. *
  866. * @returns {String} the padded string
  867. */
  868. pad:function (string, length, ch, end) {
  869. 351 string = "" + string; //check for numbers
  870. 351 ch = ch || " ";
  871. 351 var strLen = string.length;
  872. 351 while (strLen < length) {
  873. 271 if (end) {
  874. 157 string += ch;
  875. } else {
  876. 114 string = ch + string;
  877. }
  878. 271 strLen++;
  879. }
  880. 351 return string;
  881. },
  882. /**
  883. * Truncates a string to the specified length.
  884. * @example
  885. *
  886. * //from the beginning
  887. * comb.string.truncate("abcdefg", 3) => "abc";
  888. * //from the end
  889. * comb.string.truncate("abcdefg", 3,true) => "efg"
  890. * //omit the length
  891. * comb.string.truncate("abcdefg") => "abcdefg"
  892. *
  893. * @param {String} string the string to truncate
  894. * @param {Number} [length = -1] the max length of the string, if the string is
  895. * shorter than the length then the string is returned.
  896. * @param {Boolean} [end=false] truncate starting at the end of the string
  897. *
  898. * @return {String} the truncated string.
  899. */
  900. truncate:function (string, length, end) {
  901. 46 var ret = string;
  902. 46 if (comb.isString(ret)) {
  903. 44 if (string.length > length) {
  904. 27 if (end) {
  905. 8 var l = string.length;
  906. 8 ret = string.substring(l - length, l);
  907. } else {
  908. 19 ret = string.substring(0, length);
  909. }
  910. }
  911. } else {
  912. 2 ret = comb.string.truncate("" + ret, length);
  913. }
  914. 46 return ret;
  915. },
  916. /**
  917. * Formats a string with the specified format
  918. *
  919. * @example
  920. *
  921. * var format = comb.string.format;
  922. *
  923. * format("%s, %s", ["Hello", "World"]) => "Hello, World";
  924. * format("%[ 10]s, %[- 10]s", ["Hello", "World"])
  925. * => " Hello, World ";
  926. * format("%-!10s, %#10s, %10s and %-10s",
  927. * "apple", "orange", "bananas", "watermelons")
  928. * => "apple!!!!!, ####orange, bananas and watermelon"
  929. * format("%+d, %+d, %10d, %-10d, %-+#10d, %10d",
  930. * 1,-2, 1, 2, 3, 100000000000)
  931. * => "+1, -2, 0000000001, 2000000000, +3########, 1000000000"
  932. * format("%[h:mm a]D", [date]) => 7:32 PM - local -
  933. * format("%[h:mm a]Z", [date]) => 12:32 PM - UTC
  934. * //When using object formats they must be in an array otherwise
  935. * //format will try to interpolate the properties into the string.
  936. * format("%j", [{a : "b"}])
  937. * => '{"a":"b"}'
  938. * format("%1j, %4j", [{a : "b"}, {a : "b"}])
  939. * => '{\n "a": "b"\n},\n{\n "a": "b"\n}'
  940. * format("{hello}, {world}", {hello : "Hello", world : "World")
  941. * => "Hello, World";
  942. * format({[-s10]apple}, {[%#10]orange}, {[10]banana} and {[-10]watermelons}",
  943. * {
  944. * apple : "apple",
  945. * orange : "orange",
  946. * banana : "bananas",
  947. * watermelons : "watermelons"
  948. * });
  949. * => applesssss, ####orange, bananas and watermelon
  950. *
  951. * @param {String} str the string to format, if you want to use a spacing character as padding (other than \\s) then put your format in brackets.
  952. * <ol>
  953. * <li>String Formats %[options]s</li>
  954. * <ul>
  955. * <li>- : left justified</li>
  956. * <li>Char : padding character <b>Excludes d,j,s</b></li>
  957. * <li>Number : width</li>
  958. * </ul>
  959. * </li>
  960. * <li>Number Formats %[options]d</li>
  961. * <ul>
  962. * <li>- : left justified</li>
  963. * <li>+ : signed number</li>
  964. * <li>Char : padding character <b>Excludes d,j,s</b></li>
  965. * <li>Number : width</li>
  966. * </ul>
  967. * </li>
  968. * <li>Object Formats %[options]j</li>
  969. * <ul>
  970. * <li>Number : spacing for object properties.</li>
  971. * </ul>
  972. * </li>
  973. * </ol>
  974. *
  975. *
  976. * @param {Object|Array|Arguments...} obj the parameters to replace in the string
  977. * if an array is passed then the array is used sequentially
  978. * if an object is passed then the object keys are used
  979. * if a variable number of args are passed then they are used like an array
  980. *
  981. * @returns {String} the formatted string
  982. */
  983. format:function (str, obj) {
  984. 113 !date && (date = require("./date"));
  985. 113 if (obj instanceof Array) {
  986. 48 var i = 0, len = obj.length;
  987. //find the matches
  988. 48 return str.replace(FORMAT_REGEX, function (m, format, type) {
  989. 88 var replacer, ret;
  990. 88 if (i < len) {
  991. 86 replacer = obj[i++];
  992. } else {
  993. //we are out of things to replace with so
  994. //just return the match?
  995. 2 return m;
  996. }
  997. 86 if (m == "%s" || m == "%d" || m == "%D") {
  998. //fast path!
  999. 36 ret = replacer + "";
  1000. 50 } else if (m == "%Z") {
  1001. 2 ret = replacer.toUTCString();
  1002. 48 } else if (m == "%j") {
  1003. 4 try {
  1004. 4 ret = JSON.stringify(replacer);
  1005. } catch (e) {
  1006. 2 throw new Error("comb.string.format : Unable to parse json from ", replacer);
  1007. }
  1008. } else {
  1009. 44 format = format.replace(/^\[|\]$/g, "");
  1010. 44 switch (type) {
  1011. case "s":
  1012. 8 ret = formatString(replacer, format);
  1013. 8 break;
  1014. case "d":
  1015. 14 ret = formatNumber(replacer, format);
  1016. 12 break;
  1017. case "j":
  1018. 6 ret = formatObject(replacer, format);
  1019. 4 break;
  1020. case "D":
  1021. 8 ret = date.date.format(replacer, format);
  1022. 8 break;
  1023. case "Z":
  1024. 8 ret = date.date.format(replacer, format, true);
  1025. 8 break;
  1026. }
  1027. }
  1028. 80 return ret;
  1029. });
  1030. 65 } else if (isHash(obj)) {
  1031. 37 return str.replace(INTERP_REGEX, function (m, format, value) {
  1032. 146 value = obj[value];
  1033. 146 if (!misc.isUndefined(value)) {
  1034. 144 if (format) {
  1035. 89 if (comb.isString(value)) {
  1036. 58 return formatString(value, format);
  1037. 31 } else if (typeof value == "number") {
  1038. 2 return formatNumber(value, format);
  1039. 29 } else if (date.isDate(value)) {
  1040. 27 return date.date.format(value, format);
  1041. 2 } else if (typeof value == "object") {
  1042. 2 return formatObject(value, format);
  1043. }
  1044. } else {
  1045. 55 return "" + value;
  1046. }
  1047. }
  1048. 2 return m;
  1049. });
  1050. } else {
  1051. 28 var args = Array.prototype.slice.call(arguments).slice(1);
  1052. 28 return exports.string.format(str, args);
  1053. }
  1054. },
  1055. /**
  1056. * Converts a string to an array
  1057. *
  1058. * @example
  1059. *
  1060. * comb.string.toArray("a|b|c|d", "|") => ["a","b","c","d"]
  1061. * comb.string.toArray("a", "|") => ["a"]
  1062. * comb.string.toArray("", "|") => []
  1063. *
  1064. * @param {String} str the string to parse
  1065. * @param {String} delimeter the delimeter to use
  1066. */
  1067. toArray:function (testStr, delim) {
  1068. 6 var ret = [];
  1069. 6 if (testStr) {
  1070. 6 if (testStr.indexOf(delim) > 0) return testStr.replace(/\s+/g, "").split(delim);
  1071. 2 else return [testStr];
  1072. }
  1073. 2 return ret;
  1074. },
  1075. /**
  1076. * Returns a string duplicated n times;
  1077. *
  1078. * @example
  1079. *
  1080. * comb.string.multiply("HELLO", 5) => "HELLOHELLOHELLOHELLOHELLO"
  1081. *
  1082. *
  1083. */
  1084. multiply:function (str, times) {
  1085. 156 var ret = [];
  1086. 156 if (times) {
  1087. 150 for (var i = 0; i < times; i++) {
  1088. 568 ret.push(str);
  1089. }
  1090. }
  1091. 156 return ret.join("");
  1092. },
  1093. /**
  1094. * Styles a string according to the specified styles.
  1095. *
  1096. * @example
  1097. * //style a string red
  1098. * comb.string.style('myStr', 'red');
  1099. * //style a string red and bold
  1100. * comb.string.style('myStr', ['red', bold]);
  1101. *
  1102. * @param {String} str The string to style.
  1103. * @param {String|Array} styles the style or styles to apply to a string.
  1104. * options include :
  1105. * <ul>
  1106. * <li>bold</li>
  1107. * <li>bright</li>
  1108. * <li>italic</li>
  1109. * <li>underline</li>
  1110. * <li>inverse</li>
  1111. * <li>crossedOut</li>
  1112. * <li>blink</li>
  1113. * <li>red</li>
  1114. * <li>green</li>
  1115. * <li>yellow</li>
  1116. * <li>blue</li>
  1117. * <li>magenta</li>
  1118. * <li>cyan</li>
  1119. * <li>white</li>
  1120. * <li>redBackground</li>
  1121. * <li>greenBackground</li>
  1122. * <li>yellowBackground</li>
  1123. * <li>blueBackground</li>
  1124. * <li>magentaBackground</li>
  1125. * <li>cyanBackground</li>
  1126. * <li>whiteBackground</li>
  1127. * <li>grey</li>
  1128. * <li>black</li>
  1129. *
  1130. * </ul>
  1131. */
  1132. style:function (str, options) {
  1133. 90 var ret = str;
  1134. 90 if (options) {
  1135. 90 if (ret instanceof Array) {
  1136. 2 ret = ret.map(function (s) {
  1137. 6 return comb.string.style(s, options);
  1138. })
  1139. 88 } else if (options instanceof Array) {
  1140. 8 options.forEach(function (option) {
  1141. 24 ret = comb.string.style(ret, option);
  1142. });
  1143. 80 } else if (options in styles) {
  1144. 80 ret = '\x1B[' + styles[options] + 'm' + str + '\x1B[0m';
  1145. }
  1146. }
  1147. 90 return ret;
  1148. }
  1149. };
collections/AVLTree.js
Coverage100.00 SLOC203 LOC100 Missed0
  1. 1var define = require("../define").define,
  2. Tree = require("./Tree"),
  3. base = require("../base"),
  4. multiply = base.string.multiply;
  5. 1var abs = Math.abs;
  6. 1var makeNode = function(data) {
  7. 122 return {
  8. data : data,
  9. balance : 0,
  10. left : null,
  11. right : null
  12. }
  13. };
  14. 1var rotateSingle = function(root, dir, otherDir) {
  15. 80 var save = root[otherDir];
  16. 80 root[otherDir] = save[dir];
  17. 80 save[dir] = root;
  18. 80 return save;
  19. };
  20. 1var rotateDouble = function(root, dir, otherDir) {
  21. 15 root[otherDir] = rotateSingle(root[otherDir], otherDir, dir);
  22. 15 return rotateSingle(root, dir, otherDir);
  23. };
  24. 1var adjustBalance = function(root, dir, bal) {
  25. 15 var otherDir = dir == "left" ? "right" : "left";
  26. 15 var n = root[dir], nn = n[otherDir];
  27. 15 if (nn.balance == 0)
  28. 11 root.balance = n.balance = 0;
  29. 4 else if (nn.balance == bal) {
  30. 2 root.balance = -bal;
  31. 2 n.balance = 0;
  32. }
  33. else { /* nn.balance == -bal */
  34. 2 root.balance = 0;
  35. 2 n.balance = bal;
  36. }
  37. 15 nn.balance = 0;
  38. };
  39. 1var insertAdjustBalance = function(root, dir) {
  40. 60 var otherDir = dir == "left" ? "right" : "left";
  41. 60 var n = root[dir];
  42. 60 var bal = dir == "left" ? -1 : +1;
  43. 60 if (n.balance == bal) {
  44. 46 root.balance = n.balance = 0;
  45. 46 root = rotateSingle(root, otherDir, dir);
  46. }
  47. else {
  48. 14 adjustBalance(root, dir, bal);
  49. 14 root = rotateDouble(root, otherDir, dir);
  50. }
  51. 60 return root;
  52. };
  53. 1var removeAdjustBalance = function(root, dir, done) {
  54. 5 var otherDir = dir == "left" ? "right" : "left";
  55. 5 var n = root[otherDir];
  56. 5 var bal = dir == "left" ? -1 : 1;
  57. 5 if (n.balance == -bal) {
  58. 1 root.balance = n.balance = 0;
  59. 1 root = rotateSingle(root, dir, otherDir);
  60. }
  61. 4 else if (n.balance == bal) {
  62. 1 adjustBalance(root, otherDir, -bal);
  63. 1 root = rotateDouble(root, dir, otherDir);
  64. }
  65. else { /* n.balance == 0 */
  66. 3 root.balance = -bal;
  67. 3 n.balance = bal;
  68. 3 root = rotateSingle(root, dir, otherDir);
  69. 3 done.done = true;
  70. }
  71. 5 return root;
  72. };
  73. 1var insert = function(root, data, done, compare) {
  74. 472 if (root == null || root == undefined)
  75. 122 root = makeNode(data);
  76. else {
  77. 350 var dir = compare(data, root.data) == -1 ? "left" : "right";
  78. 350 root[dir] = insert(root[dir], data, done, compare);
  79. 350 if (!done.done) {
  80. /* Update balance factors */
  81. 255 root.balance += dir == "left" ? -1 : 1;
  82. /* Rebalance as necessary and terminate */
  83. 255 if (root.balance == 0)
  84. 21 done.done = true;
  85. 234 else if (abs(root.balance) > 1) {
  86. 60 root = insertAdjustBalance(root, dir);
  87. 60 done.done = true;
  88. }
  89. }
  90. }
  91. 472 return root;
  92. };
  93. 1var remove = function(root, data, done, compare) {
  94. 57 var dir, cmp, save, b;
  95. 57 if (root) {
  96. //Remove node
  97. 57 cmp = compare(data, root.data);
  98. 57 if (cmp === 0) {
  99. // Unlink and fix parent
  100. 26 var l = root.left, r = root.right;
  101. 26 if (!l || !r) {
  102. 18 dir = !l ? "right" : "left";
  103. 18 save = root[dir];
  104. 18 return save;
  105. }
  106. else {
  107. 8 var heir = l, r;
  108. 8 while ((r = heir.right) != null) {
  109. 3 heir = r;
  110. }
  111. 8 root.data = heir.data;
  112. //reset and start searching
  113. 8 data = heir.data;
  114. }
  115. }
  116. 39 dir = compare(root.data, data) == -1 ? "right" : "left";
  117. 39 root[dir] = remove(root[dir], data, done, compare);
  118. 39 if (!done.done) {
  119. /* Update balance factors */
  120. 24 b = (root.balance += (dir == "left" ? 1 : -1));
  121. /* Terminate or rebalance as necessary */
  122. 24 var a = abs(b);
  123. 24 if (a === 1)
  124. 10 done.done = true;
  125. 14 else if (a > 1)
  126. 5 root = removeAdjustBalance(root, dir, done);
  127. }
  128. }
  129. 39 return root;
  130. };
  131. /**
  132. * @ignoreCode
  133. * @class <p>An AVL tree is a self-balancing binary search tree.
  134. * In an AVL tree, the heights of the two child subtrees of any node differ by at most one.
  135. * Lookup, insertion, and deletion all take O(log n) time in both the average and worst cases,
  136. * where n is the number of nodes in the tree prior to the operation.
  137. * Insertions and deletions may require the tree to be rebalanced by one or more tree rotations.</p>
  138. * <p>AVL trees are more rigidly balanced than red-black trees, leading to slower insertion and removal but faster retrieval</p>
  139. *
  140. * <b>Performance</b>
  141. * <table>
  142. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  143. * <tr><td>Space</td><td>O(n)</td><td>O(n)</td></tr>
  144. * <tr><td>Search</td><td>O(log n)</td><td>O(log n)</td></tr>
  145. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  146. * <tr><td>Delete</td><td>O(log n)</td><td>O(log n)</td></tr>
  147. * <table>
  148. * @name AVLTree
  149. * @augments comb.collections.Tree
  150. * @memberOf comb.collections
  151. */
  152. 1module.exports = exports = define(Tree, {
  153. instance : {
  154. /**@lends comb.collections.AVLTree.prototype*/
  155. insert : function(data) {
  156. 122 var done = {done : false};
  157. 122 this.__root = insert(this.__root, data, done, this.compare);
  158. },
  159. remove : function(data) {
  160. 18 this.__root = remove(this.__root, data, {done : false}, this.compare);
  161. },
  162. __printNode : function(node, level) {
  163. 37 var str = [];
  164. 37 if (node == null) {
  165. 19 str.push(multiply('\t', level));
  166. 19 str.push("~");
  167. 19 console.log(str.join(""));
  168. } else {
  169. 18 this.__printNode(node.right, level + 1);
  170. 18 str.push(multiply('\t', level));
  171. 18 str.push(node.data + ":" + node.balance + "\n");
  172. 18 console.log(str.join(""));
  173. 18 this.__printNode(node.left, level + 1);
  174. }
  175. }
  176. }
  177. });
collections/AnderssonTree.js
Coverage100.00 SLOC165 LOC76 Missed0
  1. 1var define = require("../define").define,
  2. Tree = require("./Tree"),
  3. base = require("../base"),
  4. multiply = base.string.multiply;
  5. 1var RED = "red", BLACK = "black";
  6. 1var nil = {level:0, data:null};
  7. 1var makeNode = function (data, level) {
  8. 122 return {
  9. data:data,
  10. level:level,
  11. left:nil,
  12. right:nil
  13. }
  14. };
  15. 1var skew = function (root) {
  16. 384 if (root.level != 0 && root.left.level == root.level) {
  17. 66 var save = root.left;
  18. 66 root.left = save.right;
  19. 66 save.right = root;
  20. 66 root = save;
  21. }
  22. 384 return root;
  23. };
  24. 1var split = function (root) {
  25. 384 if (root.level != 0 && root.right.right.level == root.level) {
  26. 79 var save = root.right;
  27. 79 root.right = save.left;
  28. 79 save.left = root;
  29. 79 root = save;
  30. 79 ++root.level;
  31. }
  32. 384 return root;
  33. };
  34. 1var insert = function (root, data, compare) {
  35. 492 if (root == nil) {
  36. 122 root = makeNode(data, 1);
  37. }
  38. else {
  39. 370 var dir = compare(data, root.data) == -1 ? "left" : "right";
  40. 370 root[dir] = insert(root[dir], data, compare);
  41. 370 root = skew(root);
  42. 370 root = split(root);
  43. }
  44. 492 return root;
  45. };
  46. 1var remove = function (root, data, compare) {
  47. 59 var rLeft, rRight;
  48. 59 if (root != nil) {
  49. 59 var cmp = compare(data, root.data);
  50. 59 if (cmp == 0) {
  51. 26 rLeft = root.left, rRight = root.right;
  52. 26 if (rLeft != nil && rRight != nil) {
  53. 8 var heir = rLeft;
  54. 8 while (heir.right != nil)
  55. 3 heir = heir.right;
  56. 8 root.data = heir.data;
  57. 8 root.left = remove(rLeft, heir.data, compare);
  58. } else {
  59. 18 root = root[rLeft == nil ? "right" : "left"];
  60. }
  61. } else {
  62. 33 var dir = cmp == -1 ? "left" : "right";
  63. 33 root[dir] = remove(root[dir], data, compare);
  64. }
  65. }
  66. 59 if (root != nil) {
  67. 42 var rLevel = root.level;
  68. 42 var rLeftLevel = root.left.level, rRightLevel = root.right.level;
  69. 42 if (rLeftLevel < rLevel - 1 || rRightLevel < rLevel - 1) {
  70. 14 if (rRightLevel > --root.level)
  71. 2 root.right.level = root.level;
  72. 14 root = skew(root);
  73. 14 root = split(root);
  74. }
  75. }
  76. 59 return root;
  77. };
  78. /**
  79. *
  80. * @ignoreCode
  81. * @class <p>Andersson Trees are a version of a balanced Binary tree, while similar to RedBlack Trees the balancing is not as strict.</p>
  82. *
  83. * <b>Performance</b>
  84. * <table>
  85. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  86. * <tr><td>space</td><td>O(n)</td><td>O(n)</td></tr>
  87. * <tr><td>Search</td><td>O(log n)</td><td>O(log n)</td></tr>
  88. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  89. * <tr><td>Delete</td><td>O(log n)</td><td>O(log n)</td></tr>
  90. * <table>
  91. * @name AnderssonTree
  92. * @augments comb.collections.Tree
  93. * @memberOf comb.collections
  94. */
  95. 1module.exports = exports = define(Tree, {
  96. instance:{
  97. /**@lends comb.collections.AnderssonTree.prototype*/
  98. isEmpty:function () {
  99. 4 return this.__root == nil || this._super(arguments);
  100. },
  101. insert:function (data) {
  102. 130 if (this.__root == null) this.__root = nil;
  103. 122 this.__root = insert(this.__root, data, this.compare);
  104. },
  105. remove:function (data) {
  106. 18 this.__root = remove(this.__root, data, this.compare);
  107. },
  108. traverseWithCondition:function (node, order, callback) {
  109. 614 var cont = true;
  110. 614 if (node != nil) {
  111. 317 return this._super(arguments);
  112. }
  113. 297 return cont;
  114. },
  115. traverse:function (node, order, callback) {
  116. 989 if (node != nil) {
  117. 482 this._super(arguments);
  118. }
  119. },
  120. contains:function (value) {
  121. 44 if (this.__root != nil) {
  122. 26 return this._super(arguments);
  123. }
  124. 18 return false;
  125. },
  126. __printNode:function (node, level) {
  127. 37 var str = [];
  128. 37 if (node.data == null || node == null) {
  129. 19 str.push(multiply('\t', level));
  130. 19 str.push("~");
  131. 19 console.log(str.join(""));
  132. } else {
  133. 18 this.__printNode(node.right, level + 1);
  134. 18 str.push(multiply('\t', level));
  135. 18 str.push(node.data + ":" + node.level + "\n");
  136. 18 console.log(str.join(""));
  137. 18 this.__printNode(node.left, level + 1);
  138. }
  139. }
  140. }
  141. });
collections/BinaryTree.js
Coverage100.00 SLOC83 LOC30 Missed0
  1. 1var define = require("../define").define,
  2. Tree = require("./Tree"),
  3. base = require("../base");
  4. /**
  5. *
  6. * @ignoreCode
  7. * @class <p>A Search tree that maintains the following properties</p>
  8. * <ul>
  9. * <li>The left subtree of a node contains only nodes with keys less than the node's key.
  10. * <li>The right subtree of a node contains only nodes with keys greater than the node's key.
  11. * <li>Both the left and right subtrees must also be binary search trees.
  12. * </ul>
  13. *
  14. * <b>Performance</b>
  15. * <table>
  16. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  17. * <tr><td>Space</td><td>O(n)</td><td>O(n)</td></tr>
  18. * <tr><td>Search</td><td>O(log n)</td><td>O(n)</td></tr>
  19. * <tr><td>Insert</td><td>O(log n)</td><td>O(n)</td></tr>
  20. * <tr><td>Delete</td><td>O(log n)</td><td>O(n)</td></tr>
  21. * <table>
  22. * @name BinaryTree
  23. * @augments comb.collections.Tree
  24. * @memberOf comb.collections
  25. */
  26. 1module.exports = exports = define(Tree, {
  27. instance : {
  28. /**@lends comb.collections.BinaryTree.prototype*/
  29. insert : function(data) {
  30. 124 if (this.__root == null) {
  31. 9 return (this.__root = {
  32. data : data,
  33. parent : null,
  34. left : null,
  35. right : null
  36. });
  37. }
  38. 115 var compare = this.compare;
  39. 115 var root = this.__root;
  40. 115 while (root != null) {
  41. 505 var cmp = compare(data, root.data);
  42. 505 if (cmp) {
  43. 503 var leaf = (cmp == -1) ? "left" : "right";
  44. 503 var next = root[leaf];
  45. 503 if (next == null) {
  46. 113 return (root[leaf] = {data : data, parent : root, left : null, right : null});
  47. } else {
  48. 390 root = next;
  49. }
  50. } else {
  51. 2 return;
  52. }
  53. }
  54. },
  55. remove : function(data) {
  56. 19 if (this.__root != null) {
  57. 19 var head = {right : this.__root}, it = head;
  58. 19 var p, f = null;
  59. 19 var dir = "right";
  60. 19 while (it[dir] != null) {
  61. 51 p = it;
  62. 51 it = it[dir];
  63. 51 var cmp = this.compare(data, it.data);
  64. 51 if (!cmp) {
  65. 18 f = it;
  66. }
  67. 51 dir = (cmp == -1 ? "left" : "right");
  68. }
  69. 19 if (f != null) {
  70. 18 f.data = it.data;
  71. 18 p[p.right == it ? "right" : "left"] = it[it.left == null ? "right" : "left"];
  72. }
  73. 19 this.__root = head.right;
  74. }
  75. }
  76. }
  77. });
collections/Collection.js
Coverage100.00 SLOC56 LOC8 Missed0
  1. 1var define = require("../define").define,
  2. base = require("../base");
  3. /**
  4. * @ignoreCode
  5. * @class Base class for all collections
  6. * @name Collection
  7. * @memberOf comb.collections
  8. */
  9. 1define(null, {
  10. instance:{
  11. /**@lends comb.collections.Collection.prototype*/
  12. /**
  13. * Concats two collections
  14. */
  15. concat:function () {
  16. 1 throw new Error("Not Implemented");
  17. },
  18. /**
  19. * Joins two collections
  20. */
  21. join:function () {
  22. 1 throw new Error("Not Implemented");
  23. },
  24. /**
  25. * Slice a portion from a collection
  26. */
  27. slice:function () {
  28. 1 throw new Error("Not Implemented");
  29. },
  30. /**
  31. * Convert a collection to a string
  32. */
  33. toString:function () {
  34. 1 throw new Error("Not Implemented");
  35. },
  36. /**
  37. * Find the index of an item in a collection
  38. */
  39. indexOf:function () {
  40. 1 throw new Error("Not Implemented");
  41. },
  42. /**
  43. * Find the last index of an item in a collection
  44. */
  45. lastIndexOf:function () {
  46. 1 throw new Error("Not Implemented");
  47. }
  48. }
  49. }).as(module);
collections/HashTable.js
Coverage100.00 SLOC378 LOC126 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. Iterable = require("./Iterable"),
  4. base = require("../base");
  5. 1var hashFunction = function (key) {
  6. 100 if (typeof key == "string") {
  7. 26 return key;
  8. 74 } else if (typeof key == "object") {
  9. 44 return key.hashCode ? key.hashCode() : "" + key;
  10. } else {
  11. 30 return "" + key;
  12. }
  13. };
  14. 1var Bucket = define(null, {
  15. instance:{
  16. constructor:function () {
  17. 32 this.__entries = [];
  18. },
  19. pushValue:function (key, value) {
  20. 36 this.__entries.push({key:key, value:value});
  21. 36 return value;
  22. },
  23. remove:function (key) {
  24. 14 var ret = null, map = this.__entries, val;
  25. 14 var i = map.length - 1;
  26. 14 for (; i >= 0; i--) {
  27. 16 if ((val = map[i]) != null && val.key === key) {
  28. 12 map[i] = null;
  29. 12 return val.value;
  30. }
  31. }
  32. 2 return ret;
  33. },
  34. "set":function (key, value) {
  35. 14 var ret = null, map = this.__entries;
  36. 14 var i = map.length - 1;
  37. 14 for (; i >= 0; i--) {
  38. 18 var val = map[i];
  39. 18 if (val && key === val.key) {
  40. 6 val.value = value;
  41. 6 ret = value;
  42. 6 break;
  43. }
  44. }
  45. 14 if (!ret) {
  46. 8 map.push({key:key, value:value});
  47. }
  48. 14 return ret;
  49. },
  50. find:function (key) {
  51. 34 var ret = null, map = this.__entries, val;
  52. 34 var i = map.length - 1;
  53. 34 for (; i >= 0; i--) {
  54. 43 val = map[i];
  55. 43 if (val && key === val.key) {
  56. 26 ret = val.value;
  57. 26 break;
  58. }
  59. }
  60. 34 return ret;
  61. },
  62. getEntrySet:function (arr) {
  63. 78 var map = this.__entries, l = map.length;
  64. 78 if (l) {
  65. 78 for (var i = 0; i < l; i++) {
  66. 88 var e = map[i];
  67. 88 if (e) {
  68. 80 arr.push(e);
  69. }
  70. }
  71. }
  72. },
  73. getKeys:function (arr) {
  74. 21 var map = this.__entries, l = map.length;
  75. 21 if (l) {
  76. 21 for (var i = 0; i < l; i++) {
  77. 24 var e = map[i];
  78. 24 if (e) {
  79. 8 arr.push(e.key);
  80. }
  81. }
  82. }
  83. 21 return arr;
  84. },
  85. getValues:function (arr) {
  86. 14 var map = this.__entries, l = map.length;
  87. 14 if (l) {
  88. 14 for (var i = 0; i < l; i++) {
  89. 16 var e = map[i];
  90. 16 if (e) {
  91. 8 arr.push(e.value);
  92. }
  93. }
  94. }
  95. 14 return arr;
  96. }
  97. }
  98. });
  99. /**
  100. * @ignoreCode
  101. * @class <p>Implementation of a HashTable for javascript.
  102. * This HashTable implementation allows one to use anything as a key.
  103. * </p>
  104. * <b>NOTE: THIS IS ~ 3 times slower than javascript native objects</b>
  105. *
  106. * <p> A use case for this collection is when one needs to store items in which the key will not be a string, or number</p>
  107. *
  108. * @name HashTable
  109. * @augments comb.collections.Collection
  110. * @memberOf comb.collections
  111. *
  112. * @property {Array} keys all keys contained in the table
  113. * @property {Array} values all values contained in the table
  114. * @property {Array} entrySet an array of objects. Each object contains a key, and value property.
  115. */
  116. 1define([Collection, Iterable], {
  117. instance:{
  118. /**@lends comb.collections.HashTable.prototype*/
  119. constructor:function () {
  120. 13 this.__map = {};
  121. },
  122. __entrySet:function () {
  123. 14 var ret = [], es = [];
  124. 14 for (var i in this.__map) {
  125. 78 this.__map[i].getEntrySet(ret);
  126. }
  127. 14 return ret;
  128. },
  129. /**
  130. * Put a key, value pair into the table
  131. *
  132. * <b>NOTE :</b> the collection will not check if the key previously existed.
  133. *
  134. * @param {Anything} key the key to look up the object.
  135. * @param {Anything} value the value that corresponds to the key.
  136. *
  137. * @returns the value
  138. */
  139. put:function (key, value) {
  140. 29 var hash = hashFunction(key);
  141. 29 var bucket = null;
  142. 29 if ((bucket = this.__map[hash]) == null) {
  143. 25 bucket = (this.__map[hash] = new Bucket());
  144. }
  145. 29 bucket.pushValue(key, value);
  146. 29 return value;
  147. },
  148. /**
  149. * Remove a key value pair from the table.
  150. *
  151. * @param key the key of the key value pair to remove.
  152. *
  153. * @returns the removed value.
  154. */
  155. remove:function (key) {
  156. 16 var hash = hashFunction(key), ret = null;
  157. 16 var bucket = this.__map[hash];
  158. 16 if (bucket) {
  159. 14 ret = bucket.remove(key);
  160. }
  161. 16 return ret;
  162. },
  163. /**
  164. * Get the value corresponding to the key.
  165. *
  166. * @param key the key used to look up the value
  167. *
  168. * @returns null if not found, or the value.
  169. */
  170. "get":function (key) {
  171. 26 var hash = hashFunction(key), ret = null;
  172. 26 var bucket = null;
  173. 26 if ((bucket = this.__map[hash]) != null) {
  174. 26 ret = bucket.find(key);
  175. }
  176. 26 return ret;
  177. },
  178. /**
  179. * Set the value of a previously existing key,value pair or create a new entry.
  180. *
  181. * @param key the key to be be used
  182. * @param value the value to be set
  183. *
  184. * @returns the value.
  185. */
  186. "set":function (key, value) {
  187. 21 var hash = hashFunction(key), ret = null, bucket = null, map = this.__map;
  188. 21 if ((bucket = map[hash]) != null) {
  189. 14 ret = bucket.set(key, value);
  190. } else {
  191. 7 ret = (map[hash] = new Bucket()).pushValue(key, value);
  192. }
  193. 21 return ret;
  194. },
  195. /**
  196. * Tests if the table contains a particular key
  197. * @param key the key to test
  198. *
  199. * @returns {Boolean} true if it exitsts false otherwise.
  200. */
  201. contains:function (key) {
  202. 8 var hash = hashFunction(key), ret = false;
  203. 8 var bucket = null;
  204. 8 if ((bucket = this.__map[hash]) != null) {
  205. 8 ret = bucket.find(key) != null;
  206. }
  207. 8 return ret;
  208. },
  209. /**
  210. * Returns a new HashTable containing the values of this HashTable, and the other table.
  211. * </br>
  212. * <b> DOES NOT CHANGE THE ORIGINAL!</b>
  213. * @param {comb.collections.HashTable} hashTable the hash table to concat with this.
  214. *
  215. * @returns {comb.collections.HashTable} a new HashTable containing all values from both tables.
  216. */
  217. concat:function (hashTable) {
  218. 2 if (hashTable instanceof this._static) {
  219. 1 var ret = new this._static();
  220. 1 var otherEntrySet = hashTable.entrySet.concat(this.entrySet);
  221. 1 for (var i = otherEntrySet.length - 1; i >= 0; i--) {
  222. 4 var e = otherEntrySet[i];
  223. 4 ret.put(e.key, e.value);
  224. }
  225. 1 return ret;
  226. } else {
  227. 1 throw new TypeError("When joining hashtables the joining arg must be a HashTable");
  228. }
  229. },
  230. /**
  231. * Creates a new HashTable containg values that passed the filtering function.
  232. *
  233. * @param {Function} cb Function to callback with each item, the first aruguments is an object containing a key and value field
  234. * @param {Object} scope the scope to call the function.
  235. *
  236. * @returns {comb.collections.HashTable} the HashTable containing the values that passed the filter.
  237. */
  238. filter:function (cb, scope) {
  239. 1 var es = this.__entrySet(), ret = new this._static();
  240. 1 es = es.filter.apply(es, arguments);
  241. 1 for (var i = es.length - 1; i >= 0; i--) {
  242. 4 var e = es[i];
  243. 4 ret.put(e.key, e.value);
  244. }
  245. 1 return ret;
  246. },
  247. /**
  248. * Loop through each value in the hashtable
  249. *
  250. * @param {Function} cb the function to call with an object containing a key and value field
  251. * @param {Object} scope the scope to call the funciton in
  252. */
  253. forEach:function (cb, scope) {
  254. 1 var es = this.__entrySet(), l = es.length, f = cb.bind(scope || this);
  255. 1 es.forEach.apply(es, arguments);
  256. },
  257. /**
  258. * Determines if every item meets the condition returned by the callback.
  259. *
  260. * @param {Function} cb Function to callback with each item, the first aruguments is an object containing a key and value field
  261. * @param {Object} [scope=this] scope to call the function in
  262. *
  263. * @returns {Boolean} True if every item passed false otherwise
  264. */
  265. every:function () {
  266. 2 var es = this.__entrySet();
  267. 2 return es.every.apply(es, arguments);
  268. },
  269. /**
  270. * Loop through each value in the hashtable, collecting the value returned by the callback function.
  271. * @param {Function} cb Function to callback with each item, the first aruguments is an object containing a key and value field
  272. * @param {Object} [scope=this] scope to call the function in
  273. *
  274. * @returns {Array} an array containing the mapped values.
  275. */
  276. map:function () {
  277. 1 var es = this.__entrySet(), ret = new this._static();
  278. 1 return es.map.apply(es, arguments);
  279. },
  280. /**
  281. * Determines if some items meet the condition returned by the callback.
  282. *
  283. * @param {Function} cb Function to callback with each item, the first aruguments is an object containing a key and value field
  284. * @param {Object} [scope=this] scope to call the function in
  285. *
  286. * @returns {Boolean} True if some items passed false otherwise
  287. */
  288. some:function () {
  289. 2 var es = this.__entrySet();
  290. 2 return es.some.apply(es, arguments);
  291. },
  292. /**
  293. * Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value.
  294. *
  295. * @param {Function} callback Function to execute on each value in the array.
  296. * @param initialValue Value to use as the first argument to the first call of the callback..
  297. */
  298. reduce:function () {
  299. 1 var es = this.__entrySet();
  300. 1 return es.reduce.apply(es, arguments);
  301. },
  302. /**
  303. * Apply a function against an accumulator and each value of the array (from right-to-left) as to reduce it to a single value.
  304. *
  305. * @param {Function} callback Function to execute on each value in the array.
  306. * @param initialValue Value to use as the first argument to the first call of the callback..
  307. */
  308. reduceRight:function () {
  309. 1 var es = this.__entrySet();
  310. 1 return es.reduceRight.apply(es, arguments);
  311. },
  312. /**
  313. * Clears out all items from the table.
  314. */
  315. clear:function () {
  316. 1 this.__map = {};
  317. },
  318. getters:{
  319. keys:function () {
  320. 8 var ret = [], es = [];
  321. 8 for (var i in this.__map) {
  322. 21 this.__map[i].getKeys(ret);
  323. }
  324. 8 return ret;
  325. },
  326. values:function () {
  327. 6 var ret = [], es = [];
  328. 6 for (var i in this.__map) {
  329. 14 this.__map[i].getValues(ret);
  330. }
  331. 6 return ret;
  332. },
  333. entrySet:function () {
  334. 5 return this.__entrySet();
  335. },
  336. isEmpty:function () {
  337. 2 return this.keys.length == 0;
  338. }
  339. }
  340. }
  341. }).as(module);
collections/Heap.js
Coverage100.00 SLOC218 LOC62 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. base = require("../base");
  4. 1var padding = function(char, numSpaces) {
  5. 9 var ret = [];
  6. 9 for (var i = 0; i < numSpaces; i++)
  7. 16 ret.push(char);
  8. 9 return ret.join("");
  9. };
  10. 1module.exports = exports = define(Collection, {
  11. instance : {
  12. /**@lends comb.collections.Heap.prototype*/
  13. __getParentIndex : function(index) {
  14. 192 return Math.floor((index - 1) / 2);
  15. },
  16. __getLeftChildIndex : function(index) {
  17. 28 return (index * 2) + 1;
  18. },
  19. __getRightChildIndex : function(index) {
  20. 28 return (index * 2) + 2;
  21. },
  22. __makeNode : function(key, value) {
  23. 137 return {key : key, value : value};
  24. },
  25. /**
  26. * Base class for Heap Implementations.
  27. *
  28. *
  29. * @constructs
  30. * @augments comb.collections.Collection
  31. * @memberOf comb.collections
  32. *
  33. * @property {Number} count the current number of elements.
  34. * @property {Array} keys the keys of all items in the heap.
  35. * @property {Array} values the values contained in the heap.
  36. * @property {Boolean} isEmpty true if the Heap is empty.
  37. */
  38. constructor : function() {
  39. 32 this.__heap = [];
  40. },
  41. /**
  42. * Insert a key value into the key
  43. * @param key
  44. * @param value
  45. */
  46. insert : function(key, value) {
  47. 138 if (!base.isString(key)) {
  48. 137 var l = this.__heap.push(this.__makeNode(key, value));
  49. 137 this.__upHeap(l - 1);
  50. } else {
  51. 1 throw TypeError("Invalid key");
  52. }
  53. },
  54. /**
  55. * Removes the root from the heap
  56. *
  57. * @returns the value of the root
  58. */
  59. remove : function() {
  60. 48 var ret = undefined, heap = this.__heap, l = heap.length;
  61. 48 if (l) {
  62. 48 ret = heap[0];
  63. 48 if (l == 1) {
  64. 12 heap.length = 0;
  65. } else {
  66. 36 heap[0] = heap.pop();
  67. 36 this.__downHeap(0);
  68. }
  69. }
  70. 48 return ret ? ret.value : ret;
  71. },
  72. /**
  73. * Gets he value of the root node with out removing it.
  74. *
  75. * @returns the value of the root
  76. */
  77. peek : function() {
  78. 33 var ret = undefined, heap = this.__heap, l = heap.length;
  79. 33 if (l) {
  80. 30 ret = heap[0];
  81. }
  82. 33 return ret ? ret.value : ret;
  83. },
  84. /**
  85. * Gets the key of the root node without removing it.
  86. *
  87. * @returns the key of the root
  88. */
  89. peekKey : function() {
  90. 6 var ret = undefined, heap = this.__heap, l = heap.length;
  91. 6 if (l) {
  92. 4 ret = heap[0];
  93. }
  94. 6 return ret ? ret.key : ret;
  95. },
  96. /**
  97. * Perform the heapify operation after the an
  98. * item as been added to the bottom of the heap.
  99. *
  100. * @param index the index in which the new item was added
  101. */
  102. __upHeap : function(index) {
  103. 1 throw Error("NOT IMPLEMENTED");
  104. },
  105. /**
  106. * Heapify the heap after the root has been removed
  107. *
  108. * @param index the index of the root
  109. */
  110. __downHeap : function(index) {
  111. 1 throw Error("NOT IMPLEMENTED");
  112. },
  113. /**
  114. *
  115. * Determine if the heap contains a particular key.
  116. *
  117. * @param key key to test.
  118. *
  119. * @returns {Boolean} true if the key is contained in this heap.
  120. */
  121. containsKey : function(key) {
  122. 15 var heap = this.__heap;
  123. 15 for (var i = heap.length - 1; i >= 0; i--) {
  124. 42 if (heap[i].key == key) {
  125. 12 return true;
  126. }
  127. }
  128. 3 return false;
  129. },
  130. /**
  131. *
  132. * Determine if the heap contains a particular value.
  133. *
  134. * @param value value to test.
  135. *
  136. * @returns {Boolean} true if the value is contained in this heap.
  137. */
  138. containsValue : function(value) {
  139. 15 var heap = this.__heap;
  140. 15 for (var i = heap.length - 1; i >= 0; i--) {
  141. 42 if (heap[i].value == value) {
  142. 12 return true;
  143. }
  144. }
  145. 3 return false;
  146. },
  147. /**
  148. * Empty the heap.
  149. */
  150. clear : function() {
  151. 9 this.__heap.length = 0;
  152. },
  153. __printNode : function(index, level) {
  154. //console.log(level);
  155. 9 var str = [], node = this.__heap[index];
  156. 9 if (node == null || node == undefined) {
  157. 5 str.push(padding('\t', level));
  158. 5 str.push("~");
  159. 5 console.log(str.join(""));
  160. } else {
  161. 4 this.__printNode(this.__getRightChildIndex(index), level + 1);
  162. 4 str.push(padding('\t', level));
  163. 4 str.push(node.key + " : " + node.value + "\n");
  164. 4 console.log(str.join(""));
  165. 4 this.__printNode(this.__getLeftChildIndex(index), level + 1);
  166. }
  167. },
  168. /**
  169. * Print the heap.
  170. */
  171. print : function() {
  172. 1 this.__printNode(0, 0);
  173. },
  174. getters : {
  175. count : function() {
  176. 12 return this.__heap.length;
  177. },
  178. keys : function() {
  179. 3 return this.__heap.map(function(n) {
  180. 12 return n.key;
  181. });
  182. },
  183. values : function() {
  184. 3 return this.__heap.map(function(n) {
  185. 12 return n.value;
  186. });
  187. },
  188. isEmpty : function() {
  189. 18 return this.__heap.length == 0;
  190. }
  191. }
  192. }
  193. });
collections/Iterable.js
Coverage100.00 SLOC63 LOC9 Missed0
  1. 1var define = require("../define").define,
  2. base = require("../base");
  3. /**
  4. * @ignoreCode
  5. * @class Base class for all collections
  6. * @name Iterable
  7. * @memberOf comb.collections
  8. */
  9. 1define(null, {
  10. instance:{
  11. /**@lends comb.collections.Iterable.prototype*/
  12. /**
  13. * Filter items from a collection
  14. */
  15. filter:function () {
  16. 1 throw new Error("Not Implemented");
  17. },
  18. /**
  19. * Loop through the items in a collection
  20. */
  21. forEach:function () {
  22. 1 throw new Error("Not Implemented");
  23. },
  24. /**
  25. * Determine if every item in a collection meets the criteria
  26. */
  27. every:function () {
  28. 1 throw new Error("Not Implemented");
  29. },
  30. /**
  31. * Map every item in a collection
  32. */
  33. map:function () {
  34. 1 throw new Error("Not Implemented");
  35. },
  36. /**
  37. * Determing if some items in a colleciton meet the criteria
  38. */
  39. some:function () {
  40. 1 throw new Error("Not Implemented");
  41. },
  42. /**
  43. * Reduce a collection
  44. */
  45. reduce:function () {
  46. 1 throw new Error("Not Implemented");
  47. },
  48. /**
  49. * Reduce a collection starting from the right most position
  50. */
  51. reduceRight:function () {
  52. 1 throw new Error("Not Implemented");
  53. }
  54. }
  55. }).as(module);
collections/MaxHeap.js
Coverage100.00 SLOC66 LOC25 Missed0
  1. 1var define = require("../define").define,
  2. Heap = require("./Heap"),
  3. base = require("../base");
  4. /**
  5. * @ignoreCode
  6. *
  7. * @class <p> Max Heap implementation, lowest value in heap is always at the root.</p>
  8. * </br>
  9. * <b>Performance</b>
  10. * <table>
  11. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  12. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  13. * <tr><td>Remove</td><td>O(log n)</td><td>O(log n)</td></tr>
  14. * <tr><td>Peek</td><td>O(1)</td><td>O(1)</td></tr>
  15. * <tr><td>Contains</td><td>O(n)</td><td>O(n)</td></tr>
  16. * <table>
  17. * @name MaxHeap
  18. * @augments comb.collections.Heap
  19. * @memberOf comb.collections
  20. */
  21. 1exports = module.exports = define(Heap, {
  22. instance : {
  23. __upHeap : function(index) {
  24. 48 var heap = this.__heap;
  25. 48 var node = heap[index];
  26. 48 while (index >= 0) {
  27. 84 var parentIndex = this.__getParentIndex(index), parent = heap[parentIndex];
  28. 84 if (parent && parent.key < node.key) {
  29. 36 heap[index] = parent;
  30. 36 index = parentIndex;
  31. } else {
  32. 48 break;
  33. }
  34. }
  35. 48 heap[index] = node;
  36. },
  37. __downHeap : function(index) {
  38. 12 var heap = this.__heap;
  39. 12 var node = heap[index], length = heap.length;
  40. 12 while (index < Math.floor(length / 2)) {
  41. 8 var leftIndex = this.__getLeftChildIndex(index),
  42. rightIndex = this.__getRightChildIndex(index), left = heap[leftIndex], right = heap[rightIndex], child, childIndex;
  43. 8 if (rightIndex < length && right.key < left.key) {
  44. 4 childIndex = leftIndex;
  45. 4 child = left
  46. } else {
  47. 4 childIndex = leftIndex;
  48. 4 child = heap[leftIndex];
  49. }
  50. 8 if(child.key > node.key){
  51. 5 heap[index] = child;
  52. 5 index = childIndex;
  53. }else{
  54. 3 break;
  55. }
  56. }
  57. 12 heap[index] = node;
  58. }
  59. }
  60. });
collections/MinHeap.js
Coverage100.00 SLOC65 LOC26 Missed0
  1. 1var define = require("../define").define,
  2. Heap = require("./Heap"),
  3. base = require("../base");
  4. 1var floor = Math.floor, MinHeap;
  5. /**
  6. * @class <p> Min Heap implementation, lowest value in heap is always at the root.</p>
  7. * </br>
  8. * <b>Performance</b>
  9. * <table>
  10. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  11. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  12. * <tr><td>Remove</td><td>O(log n)</td><td>O(log n)</td></tr>
  13. * <tr><td>Peek</td><td>O(1)</td><td>O(1)</td></tr>
  14. * <tr><td>Contains</td><td>O(n)</td><td>O(n)</td></tr>
  15. * <table>
  16. * @name MinHeap
  17. * @augments comb.collections.Heap
  18. * @memberOf comb.collections
  19. * @ignoreCode
  20. */
  21. 1module.exports = exports = define(Heap, {
  22. instance : {
  23. __upHeap : function(index) {
  24. 88 var heap = this.__heap;
  25. 88 var node = heap[index], key = node.key, gpi = this.__getParentIndex;
  26. 88 while (index >= 0) {
  27. 108 var parentIndex = gpi(index), parent = heap[parentIndex];
  28. 108 if (parent && parent.key > key) {
  29. 20 heap[index] = parent;
  30. 20 index = parentIndex;
  31. } else {
  32. 88 break;
  33. }
  34. }
  35. 88 heap[index] = node;
  36. },
  37. __downHeap : function(index) {
  38. 24 var heap = this.__heap;
  39. 24 var node = heap[index], key = node.key, length = heap.length, max = floor(length / 2), glci = this.__getLeftChildIndex, grci = this.__getRightChildIndex;
  40. 24 while (index < max) {
  41. 16 var leftIndex = glci(index),
  42. rightIndex = grci(index), left = heap[leftIndex], right = heap[rightIndex], child, childIndex;
  43. 16 if (rightIndex < length && right.key < left.key) {
  44. 2 childIndex = rightIndex;
  45. 2 child = right;
  46. } else {
  47. 14 childIndex = leftIndex;
  48. 14 child = left;
  49. }
  50. 16 if (child.key < key) {
  51. 10 heap[index] = child;
  52. 10 index = childIndex;
  53. } else {
  54. 6 break;
  55. }
  56. }
  57. 24 heap[index] = node;
  58. }
  59. }
  60. });
collections/Pool.js
Coverage100.00 SLOC162 LOC51 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. Queue = require("./Queue"),
  4. base = require("../base");
  5. /**
  6. * @class Base class for a pool.
  7. *
  8. * @name Pool
  9. * @memberOf comb.collections
  10. *
  11. * @property {Number} count the total number of objects in the pool, including free and in use objects.
  12. * @property {Number} freeCount the number of free objects in this pool.
  13. * @property {Number} inUseCount the number of objects in use in this pool.
  14. * @property {Number} [minObjects=0] the minimum number of objects this pool should contain.
  15. * @property {Number} [maxObjects=1] the maximum number of objects this pool should contain
  16. * @ignoreCode
  17. */
  18. 1exports = module.exports = define(null, {
  19. instance : {
  20. /**@lends comb.collections.Pool.prototype*/
  21. __minObjects : 0,
  22. __maxObjects : 1,
  23. constructor : function(options) {
  24. 5 options = options || {};
  25. 5 this.__freeObjects = new Queue();
  26. 5 this.__inUseObjects = [];
  27. 5 this.__minObjects = options.minObjects || 0;
  28. 5 this.__maxObjects = options.maxObjects || 1;
  29. 5 this.minObjects = this.__minObjects;
  30. 4 this.maxObjects = this.__maxObjects;
  31. },
  32. /**
  33. * Retrieves an object from this pool.
  34. * `
  35. * @return {*} an object to contained in this pool
  36. */
  37. getObject : function() {
  38. 19 var ret = undefined;
  39. 19 if (this.freeCount > 0) {
  40. 2 ret = this.__freeObjects.dequeue();
  41. 2 this.__inUseObjects.push(ret);
  42. 17 } else if (this.__maxObjects > this.count) {
  43. 12 ret = this.createObject();
  44. 12 this.__inUseObjects.push(ret);
  45. }
  46. 19 return ret;
  47. },
  48. /**
  49. * Returns an object to this pool. The object is validated before it is returned to the pool,
  50. * if the validation fails then it is removed from the pool;
  51. * @param {*} obj the object to return to the pool
  52. */
  53. returnObject : function(obj) {
  54. 8 if (this.validate(obj) && this.count <= this.__maxObjects) {
  55. 7 this.__freeObjects.enqueue(obj);
  56. 7 var index;
  57. 7 if ((index = this.__inUseObjects.indexOf(obj)) > -1)
  58. 7 this.__inUseObjects.splice(index, 1);
  59. } else {
  60. 1 this.removeObject(obj);
  61. }
  62. },
  63. /**
  64. * Removes an object from the pool, this can be overriden to provide any
  65. * teardown of objects that needs to take place.
  66. *
  67. * @param {*} obj the object that needs to be removed.
  68. *
  69. * @return {*} the object removed.
  70. */
  71. removeObject : function(obj) {
  72. 2 var index;
  73. 2 if (this.__freeObjects.contains(obj)) {
  74. 1 this.__freeObjects.remove(obj);
  75. 1 } else if ((index = this.__inUseObjects.indexOf(obj)) > -1) {
  76. 1 this.__inUseObjects.splice(index, 1);
  77. }
  78. //otherwise its not contained in this pool;
  79. 2 return obj;
  80. },
  81. /**
  82. * Validates an object in this pool.
  83. * </br>
  84. * <b>THIS SHOULD BE OVERRIDDEN TO VALIDATE</b>
  85. *
  86. * @param {*} obj the object to validate.
  87. */
  88. validate : function(obj) {
  89. 8 return true;
  90. },
  91. /**
  92. * Creates a new object for this pool.
  93. * * </br>
  94. * <b>THIS SHOULD BE OVERRIDDEN TO ADD THE CORRECT TYPE OF OBJECT</b>
  95. *
  96. * @return {Object} be default just creates an object.
  97. */
  98. createObject : function() {
  99. 16 return {};
  100. },
  101. setters : {
  102. minObjects : function(l) {
  103. 6 if (l <= this.__maxObjects) {
  104. 5 this.__minObjects = l;
  105. 5 var i;
  106. 5 if ((i = this.count) < l) {
  107. 1 while (i++ < l) {
  108. 4 this.__freeObjects.enqueue(this.createObject());
  109. }
  110. }
  111. } else {
  112. 1 throw "comb.collections.Pool : minObjects cannot be greater than maxObjects.";
  113. }
  114. },
  115. maxObjects : function(l) {
  116. 9 if (l >= this.__minObjects) {
  117. 8 this.__maxObjects = l;
  118. 8 var i = this.count, j = this.freeCount, fo = this.__freeObjects;
  119. 8 while (i > l && j >= 0) {
  120. 1 this.removeObject(fo.dequeue());
  121. 1 j--;
  122. 1 i--;
  123. }
  124. } else {
  125. 1 throw "comb.collections.Pool : maxObjects cannot be less than maxObjects.";
  126. }
  127. }
  128. },
  129. getters : {
  130. freeCount : function() {
  131. 41 return this.__freeObjects.count;
  132. },
  133. inUseCount : function() {
  134. 14 return this.__inUseObjects.length;
  135. },
  136. count : function() {
  137. 52 return this.__freeObjects.count + this.__inUseObjects.length;
  138. },
  139. minObjects : function() {
  140. 2 return this.__minObjects;
  141. },
  142. maxObjects : function() {
  143. 1 return this.__maxObjects;
  144. }
  145. }
  146. }
  147. })
collections/PriorityQueue.js
Coverage100.00 SLOC41 LOC6 Missed0
  1. 1var define = require("../define").define,
  2. MinHeap = require("./MinHeap"),
  3. base = require("../base");
  4. 1var PriorityQueue;
  5. /**
  6. * @class PriorityQueue Implementation where the value with the highest priority moves to the front
  7. * Priority starts at 0, and the greatest value being the lowest priority;
  8. * @name PriorityQueue
  9. * @augments comb.collections.MinHeap
  10. * @memberOf comb.collections
  11. * @ignoreCode
  12. */
  13. 1PriorityQueue = define(MinHeap, {
  14. instance : {
  15. /**@lends comb.collections.PriorityQueue.prototype*/
  16. /**
  17. * Adds the value with the specified priority to the queue
  18. *
  19. * @param {Number} priority the priority of the item
  20. * </br>
  21. * <b>0 = Highest, n = lowest</b>
  22. * @param value
  23. */
  24. enqueue : function(priority, value) {
  25. 44 return this.insert(priority, value);
  26. },
  27. /**
  28. * Removes the item with the highest priority from the queue
  29. *
  30. * @returns the value of the item
  31. */
  32. dequeue : function() {
  33. 16 return this.remove();
  34. }
  35. }
  36. });
  37. 1module.exports = exports = PriorityQueue;
collections/Queue.js
Coverage100.00 SLOC119 LOC32 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. base = require("../base");
  4. /**
  5. * @class <p>FIFO Data structure</p>
  6. * @name Queue
  7. * @augments comb.collections.Collection
  8. * @memberOf comb.collections
  9. *
  10. * @property {Number} count the current number of elements in this queue
  11. * @property {Boolean} isEmpty true if this queue is empty
  12. * @property {Array} values a copy of the values contained in this queue
  13. * @ignoreCode
  14. */
  15. 1module.exports = exports = define(Collection, {
  16. instance : {
  17. /**@lends comb.collections.Queue.prototype*/
  18. constructor : function() {
  19. 6 this.__queue = [];
  20. 6 this.__next = 0;
  21. 6 this.__last = 0;
  22. },
  23. /**
  24. * Add data to this queue
  25. * @param {*} data element to add
  26. */
  27. enqueue : function(data) {
  28. 27 this.__queue[this.__last++] = data;
  29. },
  30. /**
  31. * Removes first item from the head of the queue
  32. *
  33. * @return {*} The element removed from this queue. Returns undefined if the queue is empty.
  34. */
  35. dequeue : function() {
  36. 7 var ret = undefined,next = this.__next, queue;
  37. 7 if (next != this.__last) {
  38. 5 queue = this.__queue;
  39. 5 ret = queue[next];
  40. 5 queue[this.__next++] = undefined;
  41. }
  42. 7 return ret;
  43. },
  44. /**
  45. * Retrieves the item at the head of the queue without removing it
  46. *
  47. * @return {*} The element at the head of the queue. Returns undefined if the queue is empty.
  48. */
  49. peek : function() {
  50. 1 var ret = undefined, next = this.__next;
  51. 1 if (next != this.__last) {
  52. 1 ret = this.__queue[next];
  53. }
  54. 1 return ret;
  55. },
  56. /**
  57. * Removes all items from this queue
  58. */
  59. clear : function() {
  60. 1 this.__queue.length = 0;
  61. 1 this.__next = 0;
  62. 1 this.__last = 0;
  63. },
  64. /**
  65. * Determine if this queue contains the element
  66. * @param {*} obj the object to find
  67. *
  68. * @return {Boolean} true if this queue contains the element
  69. */
  70. contains : function(obj) {
  71. 5 return this.__queue.indexOf(obj) != -1;
  72. },
  73. /**
  74. * Removes an element from this queue.
  75. * @param {*} obj the data to remove.
  76. *
  77. * @return {Boolean} true if the element was removed, false otherwise.
  78. */
  79. remove : function(obj) {
  80. 8 var index = this.__queue.indexOf(obj), ret = false;
  81. 8 if (index != -1) {
  82. 8 if (index == this.__next) {
  83. 1 this.dequeue();
  84. } else {
  85. 7 this.__queue.splice(index, 1);
  86. 7 this.__last--;
  87. }
  88. 8 ret = true;
  89. }
  90. 8 return ret;
  91. },
  92. toString : function(){
  93. 3 return this.__queue.toString();
  94. },
  95. getters : {
  96. count : function() {
  97. 95 return this.__last - this.__next;
  98. },
  99. isEmpty : function() {
  100. 5 return this.__last - this.__next == 0;
  101. },
  102. values : function() {
  103. 1 return this.__queue.slice(this.__next, this.__last);
  104. }
  105. }
  106. }
  107. });
collections/RedBlackTree.js
Coverage100.00 SLOC188 LOC101 Missed0
  1. 1var define = require("../define").define,
  2. Tree = require("./Tree"),
  3. base = require("../base"),
  4. multiply = base.string.multiply;
  5. 1var RED = "red", BLACK = "black";
  6. 1var isRed = function(node) {
  7. 1156 return node != null && node.red;
  8. };
  9. 1var makeNode = function(data, parent) {
  10. 122 return {
  11. data : data,
  12. red : true,
  13. left : null,
  14. right : null
  15. }
  16. };
  17. 1var insert = function(root, data, compare) {
  18. 503 if (root == null) {
  19. 122 return makeNode(data, null);
  20. } else {
  21. 381 var cmp = compare(data, root.data);
  22. 381 if (cmp) {
  23. 381 var dir = cmp == -1 ? "left" : "right";
  24. 381 var otherDir = dir == "left" ? "right" : "left";
  25. 381 root[dir] = insert(root[dir], data, compare);
  26. 381 var node = root[dir];
  27. 381 if (isRed(node)) {
  28. 294 var sibling = root[otherDir];
  29. 294 if (isRed(sibling)) {
  30. /* Case 1 */
  31. 68 root.red = true;
  32. 68 node.red = false;
  33. 68 sibling.red = false;
  34. } else {
  35. 226 if (isRed(node[dir])) {
  36. 43 root = rotateSingle(root, otherDir);
  37. 183 } else if (isRed(node[otherDir])) {
  38. 11 root = rotateDouble(root, otherDir);
  39. }
  40. }
  41. }
  42. }
  43. }
  44. 381 return root;
  45. };
  46. 1var rotateSingle = function(root, dir) {
  47. 70 var otherDir = dir == "left" ? "right" : "left";
  48. 70 var save = root[otherDir];
  49. 70 root[otherDir] = save[dir];
  50. 70 save[dir] = root;
  51. 70 root.red = true;
  52. 70 save.red = false;
  53. 70 return save;
  54. };
  55. 1var rotateDouble = function(root, dir) {
  56. 12 var otherDir = dir == "left" ? "right" : "left";
  57. 12 root[otherDir] = rotateSingle(root[otherDir], otherDir);
  58. 12 return rotateSingle(root, dir);
  59. };
  60. 1var remove = function (root, data, done, compare) {
  61. 52 if (root == null) {
  62. 2 done.done = true;
  63. } else {
  64. 50 var dir;
  65. 50 if (compare(data, root.data) == 0) {
  66. 22 if (root.left == null || root.right == null) {
  67. 16 var save = root[root.left == null ? "right" : "left"];
  68. /* Case 0 */
  69. 16 if (isRed(root)) {
  70. 2 done.done = true;
  71. 14 } else if (isRed(save)) {
  72. 5 save.red = false;
  73. 5 done.done = true;
  74. }
  75. 16 return save;
  76. }
  77. else {
  78. 6 var heir = root.right, p;
  79. 6 while (heir.left != null) {
  80. 2 p = heir;
  81. 2 heir = heir.left;
  82. }
  83. 6 p && (p.left = null);
  84. 6 root.data = heir.data;
  85. 6 data = heir.data;
  86. }
  87. }
  88. 34 dir = compare(data, root.data) == -1 ? "left" : "right";
  89. 34 root[dir] = remove(root[dir], data, done, compare);
  90. 34 !done.done && (root = removeBalance(root, dir, done));
  91. }
  92. 36 return root;
  93. };
  94. 1var removeBalance = function(root, dir, done) {
  95. 11 var notDir = dir == "left" ? "right" : "left";
  96. 11 var p = root, s = p[notDir];
  97. 11 if (isRed(s)) {
  98. 2 root = rotateSingle(root, dir);
  99. 2 s = p[notDir];
  100. }
  101. 11 if (s != null) {
  102. 11 if (!isRed(s.left) && !isRed(s.right)) {
  103. 9 isRed(p) && (done.done = true);
  104. 9 p.red = 0;
  105. 9 s.red = 1;
  106. } else {
  107. 2 var save = p.red, newRoot = ( root === p );
  108. 2 p = (isRed(s[notDir]) ? rotateSingle : rotateDouble)(p, dir);
  109. 2 p.red = save;
  110. 2 p.left.red = p.right.red = 0;
  111. 2 if (newRoot) {
  112. 1 root = p;
  113. } else {
  114. 1 root[dir] = p;
  115. }
  116. 2 done.done = true;
  117. }
  118. }
  119. 11 return root;
  120. };
  121. 1var RedBlackTree;
  122. /**
  123. * @class <p>A RedBlack tree is a form of a self balancing binary tree.</p>
  124. *
  125. * <b>Performance</b>
  126. * <table>
  127. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  128. * <tr><td>Space</td><td>O(n)</td><td>O(n)</td></tr>
  129. * <tr><td>Search</td><td>O(log n)</td><td>O(log n)</td></tr>
  130. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  131. * <tr><td>Delete</td><td>O(log n)</td><td>O(log n)</td></tr>
  132. * <table>
  133. * @name RedBlackTree
  134. * @augments comb.collections.Tree
  135. * @memberOf comb.collections
  136. * @ignoreCode
  137. */
  138. 1module.exports = exports = define(Tree, {
  139. instance : {
  140. /**@lends comb.collections.RedBlackTree.prototype*/
  141. insert : function(data) {
  142. 122 this.__root = insert(this.__root, data, this.compare);
  143. 122 this.__root.red = false;
  144. },
  145. remove : function(data) {
  146. 18 var done = {done : false};
  147. 18 var root = remove(this.__root, data, done, this.compare);
  148. 18 if (root != null)
  149. 17 root.red = 0;
  150. 18 this.__root = root;
  151. },
  152. __printNode : function(node, level) {
  153. 37 var str = [];
  154. 37 if (node == null || node == undefined) {
  155. 19 str.push(multiply('\t', level));
  156. 19 str.push("~");
  157. 19 console.log(str.join(""));
  158. } else {
  159. 18 this.__printNode(node.right, level + 1);
  160. 18 str.push(multiply('\t', level));
  161. 18 str.push((node.red ? "RED" : "BLACK") + ":" + node.data + "\n");
  162. 18 console.log(str.join(""));
  163. 18 this.__printNode(node.left, level + 1);
  164. }
  165. }
  166. }
  167. });
collections/Stack.js
Coverage100.00 SLOC117 LOC30 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. base = require("../base");
  4. /**
  5. * @class <p>LIFO Data structure</p>
  6. * @name Stack
  7. * @augments comb.collections.Collection
  8. * @memberOf comb.collections
  9. *
  10. * @property {Number} count the current number of elements in this queue
  11. * @property {Boolean} isEmpty true if this queue is empty
  12. * @property {Array} values a copy of the values contained in this queue
  13. * @ignoreCode
  14. */
  15. 1module.exports = exports = define(Collection, {
  16. instance : {
  17. /**@lends comb.collections.Stack.prototype*/
  18. constructor : function() {
  19. 1 this.__stack = [];
  20. 1 this.__next = -1;
  21. },
  22. /**
  23. * Add an item to the tail of this stack
  24. * @param {*} data item to qppend to this stack
  25. *
  26. */
  27. push : function(data) {
  28. 23 this.__stack[++this.__next] = data;
  29. },
  30. /**
  31. * Removes the tail of this static
  32. * @return {*} the data at the tail of this stack
  33. */
  34. pop : function() {
  35. 11 var ret = undefined, stack, next = this.__next;
  36. 11 if (next >= 0) {
  37. 10 stack = this.__stack;
  38. 10 ret = stack[next];
  39. 10 stack[this.__next--] = undefined;
  40. }
  41. 11 return ret;
  42. },
  43. /**
  44. * Retrieves the item at the tail of the stack without removing it
  45. *
  46. * @return {*} The element at the tail of the stack. Returns undefined if the stack is empty.
  47. */
  48. peek : function() {
  49. 1 var ret = undefined,next = this.__next;
  50. 1 if (next >= 0) {
  51. 1 ret = this.__stack[next];
  52. }
  53. 1 return ret;
  54. },
  55. /**
  56. * Removes all items from this stack.
  57. */
  58. clear : function() {
  59. 1 this.__stack.length = 0;
  60. 1 this.__next = -1;
  61. },
  62. /**
  63. * Determine if this stack contains the element
  64. * @param {*} obj the object to find
  65. *
  66. * @return {Boolean} true if this stack contains the element
  67. */
  68. contains : function(obj) {
  69. 3 return this.__stack.indexOf(obj) != -1;
  70. },
  71. /**
  72. * Removes an element from this stack.
  73. * @param {*} obj the data to remove.
  74. *
  75. * @return {Boolean} true if the element was removed, false otherwise.
  76. */
  77. remove : function(obj) {
  78. 7 var index = this.__stack.indexOf(obj), ret = false;
  79. 7 if (index != -1) {
  80. 7 if (index == this.__next) {
  81. 1 this.pop();
  82. } else {
  83. 6 this.__stack.splice(index, 1);
  84. 6 this.__next--;
  85. }
  86. 7 ret = true;
  87. }
  88. 7 return ret;
  89. },
  90. toString : function(){
  91. 3 return this.__stack.toString();
  92. },
  93. getters : {
  94. count : function() {
  95. 3 return this.__next + 1;
  96. },
  97. isEmpty : function() {
  98. 5 return this.__next < 0;
  99. },
  100. values : function() {
  101. 1 return this.__stack.slice(0, this.__next + 1).reverse();
  102. }
  103. }
  104. }
  105. });
collections/Tree.js
Coverage100.00 SLOC457 LOC160 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. Iterable = require("./Iterable"),
  4. base = require("../base"),
  5. multiply = base.string.multiply;
  6. 1var compare = function(a, b) {
  7. 2757 var ret = 0;
  8. 2757 if (a > b) {
  9. 1571 return 1;
  10. 1186 } else if (a < b) {
  11. 936 return -1;
  12. 250 }else if(!b){
  13. 1 return 1;
  14. }
  15. 249 return ret;
  16. };
  17. 1var Tree = define([Collection, Iterable], {
  18. instance : {
  19. /**@lends comb.collections.Tree.prototype*/
  20. /**
  21. * Prints a node
  22. * @param node node to print
  23. * @param level the current level the node is at, Used for formatting
  24. */
  25. __printNode : function(node, level) {
  26. //console.log(level);
  27. 37 var str = [];
  28. 37 if (node == null || node == undefined) {
  29. 19 str.push(multiply('\t', level));
  30. 19 str.push("~");
  31. 19 console.log(str.join(""));
  32. } else {
  33. 18 this.__printNode(node.right, level + 1);
  34. 18 str.push(multiply('\t', level));
  35. 18 str.push(node.data + "\n");
  36. 18 console.log(str.join(""));
  37. 18 this.__printNode(node.left, level + 1);
  38. }
  39. },
  40. /**
  41. * Base Class for all tree implementations
  42. * @constructs
  43. * @augments comb.collections.Collection
  44. * @augments comb.collections.Iterable
  45. * @memberOf comb.collections
  46. *
  47. * @param {Object} options options to initialize the tree
  48. * @param {Function} options.compare function used to compare items in a tree must return an integer
  49. * <ul>
  50. * </li>-1 for less than</li>
  51. * </li>0 for equal</li>
  52. * </li>1 for greater than</li>
  53. * </ul>
  54. *
  55. */
  56. constructor : function(options) {
  57. 45 options = options || {};
  58. 45 this.compare = options.compare || compare;
  59. 45 this.__root = null;
  60. },
  61. /**
  62. * Inserts an item into the tree
  63. * @param {Anything} data the item to insert
  64. */
  65. insert : function(data) {
  66. 1 throw new Error("Not Implemented");
  67. },
  68. /**
  69. * Removes an item from the tree
  70. * @param {Anything} data the item to insert
  71. */
  72. remove : function(data) {
  73. 1 throw new Error("Not Implemented");
  74. },
  75. /**
  76. * Clear all items from a tree
  77. */
  78. clear : function() {
  79. 4 this.__root = null;
  80. },
  81. /**
  82. * Test if a tree is empty
  83. *
  84. * @return {Boolean} true if empty false otherwise
  85. */
  86. isEmpty : function() {
  87. 15 return this.__root == null;
  88. },
  89. /**
  90. * Traverse a tree until the callback function returns false
  91. *
  92. * <p><b>Not typically used directly</b></p>
  93. *
  94. * @param {Object} node the node to start at
  95. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  96. * @param {Function} callback called for each item, traversal continues until the function returns false
  97. *
  98. */
  99. traverseWithCondition : function(node, order, callback) {
  100. 2172 var cont = true;
  101. 2172 if (node) {
  102. 1270 order = order || Tree.PRE_ORDER;
  103. 1270 if (order === Tree.PRE_ORDER) {
  104. 152 cont = callback(node.data);
  105. 152 if (cont) {
  106. 144 cont = this.traverseWithCondition(node.left, order, callback);
  107. 144 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  108. }
  109. 1118 } else if (order === Tree.IN_ORDER) {
  110. 510 cont = this.traverseWithCondition(node.left, order, callback);
  111. 510 if (cont) {
  112. 444 cont = callback(node.data);
  113. 444 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  114. }
  115. 608 } else if (order === Tree.POST_ORDER) {
  116. 178 cont = this.traverseWithCondition(node.left, order, callback);
  117. 178 if (cont) {
  118. 158 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  119. 158 cont && (cont = callback(node.data));
  120. }
  121. 430 } else if (order === Tree.REVERSE_ORDER) {
  122. 430 cont = this.traverseWithCondition(node.right, order, callback);
  123. 430 if (cont) {
  124. 384 cont = callback(node.data);
  125. 384 cont && (cont = this.traverseWithCondition(node.left, order, callback));
  126. }
  127. }
  128. }
  129. 2172 return cont;
  130. },
  131. /**
  132. * Traverse a tree
  133. *
  134. * <p><b>Not typically used directly</b></p>
  135. *
  136. * @param {Object} node the node to start at
  137. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  138. * @param {Function} callback called for each item
  139. *
  140. */
  141. traverse : function(node, order, callback) {
  142. 3449 if (node) {
  143. 1928 order = order || Tree.PRE_ORDER;
  144. 1928 if (order === Tree.PRE_ORDER) {
  145. 632 callback(node.data);
  146. 632 this.traverse(node.left, order, callback);
  147. 632 this.traverse(node.right, order, callback);
  148. 1296 } else if (order === Tree.IN_ORDER) {
  149. 632 this.traverse(node.left, order, callback);
  150. 632 callback(node.data);
  151. 632 this.traverse(node.right, order, callback);
  152. 664 } else if (order === Tree.POST_ORDER) {
  153. 632 this.traverse(node.left, order, callback);
  154. 632 this.traverse(node.right, order, callback);
  155. 632 callback(node.data);
  156. 32 } else if (order === Tree.REVERSE_ORDER) {
  157. 32 this.traverseWithCondition(node.right, order, callback);
  158. 32 callback(node.data);
  159. 32 this.traverseWithCondition(node.left, order, callback);
  160. }
  161. }
  162. },
  163. /**
  164. * Loop through each item in the tree
  165. * @param {Function} cb called for each item in the tree
  166. * @param {Object} [scope=this] scope to call the function in
  167. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  168. */
  169. forEach : function(cb, scope, order) {
  170. 28 if (typeof cb !== "function")
  171. 4 throw new TypeError();
  172. 24 order = order || Tree.IN_ORDER;
  173. 24 scope = scope || this;
  174. 24 this.traverse(this.__root, order, function(node) {
  175. 312 cb.call(scope, node, this);
  176. });
  177. },
  178. /**
  179. * Loop through each item in the tree, collecting the value returned by the callback funciton.
  180. * @param {Function} cb called for each item in the tree.
  181. * Whatever the function returns is inserted into the return tree
  182. * @param {Object} [scope=this] scope to call the function in
  183. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  184. *
  185. * @return {comb.collections.Tree} the tree with the mapped items
  186. */
  187. map : function(cb, scope, order) {
  188. 28 if (typeof cb !== "function")
  189. 4 throw new TypeError();
  190. 24 order = order || Tree.IN_ORDER;
  191. 24 scope = scope || this;
  192. 24 var construct = this._static;
  193. 24 var ret = new this._static();
  194. 24 this.traverse(this.__root, order, function(node) {
  195. 312 ret.insert(cb.call(scope, node, this));
  196. });
  197. 24 return ret;
  198. },
  199. /**
  200. * Filters a tree, only returning items that result in true being returned from the callback
  201. *
  202. * @param {Function} cb called for each item in the tree
  203. * @param {Object} [scope=this] scope to call the function in
  204. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  205. *
  206. * @return {comb.collections.Tree} the tree with items that resulted in true being returned from the callback
  207. */
  208. filter : function(cb, scope, order) {
  209. 16 if (typeof cb !== "function")
  210. 4 throw new TypeError();
  211. 12 order = order || Tree.IN_ORDER;
  212. 12 scope = scope || this;
  213. 12 var ret = new this._static();
  214. 12 this.traverse(this.__root, order, function(node) {
  215. 216 var include = cb.call(scope, node, this);
  216. 216 include && ret.insert(node);
  217. });
  218. 12 return ret;
  219. },
  220. /**
  221. * Reduces a tree
  222. *
  223. * @param {Function} fun called for each item in the tree
  224. * @param [accumulator=First item in tree(Order dependant)] scope to call the function in
  225. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  226. *
  227. * @return the result of the reduce function
  228. */
  229. reduce : function(fun, accumulator, order) {
  230. 36 var arr = this.toArray(order);
  231. 36 var args = [fun];
  232. 36 !base.isUndefinedOrNull(accumulator) && args.push(accumulator)
  233. 36 return arr.reduce.apply(arr, args);
  234. },
  235. /**
  236. * Reduces from right to left
  237. *
  238. * @param {Function} fun called for each item in the tree
  239. * @param [accumulator=First item in tree(Order dependant)] scope to call the function in
  240. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  241. *
  242. * @return the result of the reduce function
  243. */
  244. reduceRight : function(fun, accumulator, order) {
  245. 12 var arr = this.toArray(order);
  246. 12 var args = [fun];
  247. 12 !base.isUndefinedOrNull(accumulator) && args.push(accumulator)
  248. 12 return arr.reduceRight.apply(arr, args);
  249. },
  250. /**
  251. * Determines if every item meets the condition returned by the callback.
  252. *
  253. * @param {Function} cb called for each item in the tree
  254. * @param {Object} [scope=this] scope to call the function in
  255. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  256. *
  257. * @return {Boolean} True if every item passed false otherwise
  258. */
  259. every : function(cb, scope, order) {
  260. 36 if (typeof cb !== "function")
  261. 4 throw new TypeError();
  262. 32 order = order || Tree.IN_ORDER;
  263. 32 scope = scope || this;
  264. 32 var ret = false;
  265. 32 this.traverseWithCondition(this.__root, order, function(node) {
  266. 264 return (ret = cb.call(scope, node, this));
  267. });
  268. 32 return ret;
  269. },
  270. /**
  271. * Determines if some item meet the condition returned by the callback. Traversal ends the first time true is found.
  272. *
  273. * @param {Function} cb called for each item in the tree
  274. * @param {Object} [scope=this] scope to call the function in
  275. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  276. *
  277. * @return {Boolean} True if every item passed false otherwise
  278. */
  279. some : function(cb, scope, order) {
  280. 36 if (typeof cb !== "function")
  281. 4 throw new TypeError();
  282. 32 order = order || Tree.IN_ORDER;
  283. 32 scope = scope || this;
  284. 32 var ret;
  285. 32 this.traverseWithCondition(this.__root, order, function(node) {
  286. 284 ret = cb.call(scope, node, this);
  287. 284 return !ret;
  288. });
  289. 32 return ret;
  290. },
  291. /**
  292. * Converts a tree into an array based on the specified order
  293. *
  294. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  295. *
  296. * @return {Array} array of all items in the order specified.
  297. */
  298. toArray : function(order) {
  299. 72 order = order || Tree.IN_ORDER;
  300. 72 var arr = [];
  301. 72 this.traverse(this.__root, order, function(node) {
  302. 1056 arr.push(node);
  303. });
  304. 72 return arr;
  305. },
  306. /**
  307. * Determines if a value is contained in the tree
  308. * @param {*} value the value to find
  309. *
  310. * @return {Boolean} true if the tree contains the item false otherwise.
  311. */
  312. contains : function(value) {
  313. 160 var ret = false;
  314. 160 var root = this.__root;
  315. 160 while (root != null) {
  316. 376 var cmp = this.compare(value, root.data);
  317. 376 if (cmp) {
  318. 271 root = root[(cmp == -1) ? "left" : "right"];
  319. } else {
  320. 105 ret = true;
  321. 105 root = null;
  322. }
  323. }
  324. 160 return ret;
  325. },
  326. /**
  327. * Finds a value in the tree
  328. * @param {*} value the value to find
  329. *
  330. * @return the value of the node that matched
  331. */
  332. find : function(value) {
  333. 8 var ret;
  334. 8 var root = this.__root;
  335. 8 while (root != null) {
  336. 35 var cmp = this.compare(value, root.data);
  337. 35 if (cmp) {
  338. 31 root = root[(cmp == -1) ? "left" : "right"];
  339. } else {
  340. 4 ret = root.data;
  341. 4 break;
  342. }
  343. }
  344. 8 return ret;
  345. },
  346. /**
  347. * Find all values less than a value
  348. * @param {*} value the value to find nodes less than
  349. * @param {Boolean} [exclusive=false] if true the value will NOT be included in the return array
  350. *
  351. * @return {Array} the array containing all values less than
  352. */
  353. findLessThan : function(value, exclusive) {
  354. //find a better way!!!!
  355. 32 var ret = [], compare = this.compare;
  356. 32 this.traverseWithCondition(this.__root, exports.IN_ORDER, function(v) {
  357. 200 var cmp = compare(value, v);
  358. 200 if ((!exclusive && cmp == 0) || cmp == 1) {
  359. 168 ret.push(v);
  360. 168 return true;
  361. } else {
  362. 32 return false;
  363. }
  364. });
  365. 32 return ret;
  366. },
  367. /**
  368. * Find all greater than a value
  369. * @param {*} value the value to find nodes greater than
  370. * @param {Boolean} [exclusive=false] if true the value will NOT be included in the return array
  371. *
  372. * @return {Array} the array containing all values greater than
  373. */
  374. findGreaterThan : function(value, exclusive) {
  375. //find a better way!!!!
  376. 32 var ret = [], compare = this.compare;
  377. 32 this.traverse(this.__root, exports.REVERSE_ORDER, function(v) {
  378. 416 var cmp = compare(value, v);
  379. 416 if ((!exclusive && cmp == 0) || cmp == -1) {
  380. 372 ret.push(v);
  381. 372 return true;
  382. } else {
  383. 44 return false;
  384. }
  385. });
  386. 32 return ret;
  387. },
  388. /**
  389. * Prints a tree to the console.
  390. */
  391. print : function() {
  392. 4 this.__printNode(this.__root, 0);
  393. }
  394. },
  395. static : {
  396. /** @lends comb.collections.Tree */
  397. /**
  398. * Pre Order
  399. */
  400. PRE_ORDER : "pre_order",
  401. /**
  402. * In Order
  403. */
  404. IN_ORDER : "in_order",
  405. /**
  406. * Post Order
  407. */
  408. POST_ORDER:"post_order",
  409. /**
  410. * Reverse Order
  411. */
  412. REVERSE_ORDER : "reverse_order"
  413. }
  414. });
  415. /**@ignore*/
  416. 1module.exports = exports = Tree;
collections/index.js
Coverage100.00 SLOC21 LOC2 Missed0
  1. 1var comb = exports;
  2. /**
  3. * @ignore
  4. * @namespace Various collections*/
  5. 1comb.collections = {
  6. Collection : require("./Collection"),
  7. Iterable : require("./Iterable"),
  8. Tree : require("./Tree"),
  9. BinaryTree : require("./BinaryTree"),
  10. RedBlackTree : require("./RedBlackTree"),
  11. AnderssonTree : require("./AnderssonTree"),
  12. AVLTree : require("./AVLTree"),
  13. HashTable : require("./HashTable"),
  14. Queue : require("./Queue"),
  15. Stack : require("./Stack"),
  16. Heap : require("./Heap"),
  17. MinHeap : require("./MinHeap"),
  18. MaxHeap : require("./MaxHeap"),
  19. PriorityQueue : require("./PriorityQueue"),
  20. Pool : require("./Pool")
  21. };
define.js
Coverage100.00 SLOC505 LOC138 Missed0
  1. /**
  2. * Used to keep track of classes and to create unique ids
  3. * @ignore
  4. */
  5. 1var classCounter = 0;
  6. 1var callSuper = function (args, a) {
  7. 1164 var meta = this.__meta,
  8. supers = meta.supers,
  9. l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos;
  10. 1164 if (l > pos) {
  11. 1019 a && (args = a);
  12. 1019 var name = superMeta.name, f = superMeta.f, m;
  13. 1019 do {
  14. 1019 m = supers[pos][name];
  15. 1019 if ("function" === typeof m && (m = m._f || m) !== f) {
  16. 1019 superMeta.pos = 1 + pos;
  17. 1019 return m.apply(this, args);
  18. }
  19. } while (l > ++pos);
  20. }
  21. 145 return null;
  22. };
  23. 1var defaultFunction = function () {
  24. 140 var meta = this.__meta || {},
  25. supers = meta.supers,
  26. l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos;
  27. 140 if (l > pos) {
  28. 123 var name = superMeta.name, f = superMeta.f, m;
  29. 123 do {
  30. 173 m = supers[pos][name];
  31. 173 if ("function" === typeof m && (m = m._f || m) !== f) {
  32. 94 superMeta.pos = 1 + pos;
  33. 94 return m.apply(this, arguments);
  34. }
  35. } while (l > ++pos);
  36. }
  37. 46 return null;
  38. };
  39. 1var functionWrapper = function (f, name) {
  40. 306 var wrapper = function () {
  41. 29019 var ret, meta = this.__meta || {};
  42. 29019 var orig = meta.superMeta;
  43. 29019 meta.superMeta = {f:f, pos:0, name:name};
  44. 29019 ret = f.apply(this, arguments);
  45. 28970 meta.superMeta = orig;
  46. 28970 return ret;
  47. };
  48. 306 wrapper._f = f;
  49. 306 return wrapper;
  50. };
  51. /**
  52. * @ignore
  53. */
  54. 1var defineMixinProps = function (child, proto) {
  55. 34 var operations = proto.setters || {};
  56. 34 for (var i in operations) {
  57. 15 if (!child.__lookupSetter__(i)) { //make sure that the setter isnt already there
  58. 15 child.__defineSetter__(i, operations[i]);
  59. }
  60. }
  61. 34 operations = proto.getters || {};
  62. 34 for (i in operations) {
  63. 15 if (!child.__lookupGetter__(i)) {
  64. //define the getter if the child does not already have it
  65. 15 child.__defineGetter__(i, operations[i]);
  66. }
  67. }
  68. 34 for (var j in proto) {
  69. 103 if (j != "getters" && j != "setters") {
  70. 83 var p = proto[j];
  71. 83 if ("function" === typeof p) {
  72. 78 if (!child.hasOwnProperty(j)) {
  73. 10 child[j] = functionWrapper(defaultFunction, j);
  74. }
  75. } else {
  76. 5 child[j] = p;
  77. }
  78. }
  79. }
  80. };
  81. /**
  82. * @ignore
  83. */
  84. 1var mixin = function () {
  85. 7 var args = Array.prototype.slice.call(arguments), l = args.length;
  86. 7 var child = this.prototype, childMeta = child.__meta, thisMeta = this.__meta, bases = child.__meta.bases, staticBases = bases.slice(),
  87. staticSupers = thisMeta.supers || [], supers = childMeta.supers|| [];
  88. 7 for (var i = 0; i < l; i++) {
  89. 17 var m = args[i], mProto = m.prototype;
  90. 17 var protoMeta = mProto.__meta, meta = m.__meta;
  91. 17 !protoMeta && (protoMeta = (mProto.__meta = {proto :mProto || {}}));
  92. 17 !meta && (meta = (m.__meta = {proto :m.__proto__ || {}}));
  93. 17 defineMixinProps(child, protoMeta.proto || {});
  94. 17 defineMixinProps(this, meta.proto || {});
  95. //copy the bases for static,
  96. 17 mixinSupers(m.prototype, supers, bases);
  97. 17 mixinSupers(m, staticSupers, staticBases);
  98. }
  99. 7 return this;
  100. };
  101. /**
  102. * @ignore
  103. */
  104. 1var mixinSupers = function (sup, arr, bases) {
  105. 214 var meta = sup.__meta;
  106. 214 !meta && (meta = (sup.__meta = {}));
  107. 214 var unique = sup.__meta.unique;
  108. 214 !unique && (meta.unique = "define" + ++classCounter);
  109. //check it we already have this super mixed into our prototype chain
  110. //if true then we have already looped their supers!
  111. 214 if (bases.indexOf(unique) == -1) {
  112. //add their id to our bases
  113. 138 bases.push(unique);
  114. 138 var supers = sup.__meta.supers || [], i = supers.length - 1 || 0;
  115. 138 while (i >= 0) {
  116. 118 mixinSupers(supers[i--], arr, bases);
  117. }
  118. 138 arr.unshift(sup);
  119. }
  120. };
  121. /**
  122. * @ignore
  123. */
  124. 1var defineProps = function (child, proto) {
  125. 88 var operations = proto.setters;
  126. 88 if (operations) {
  127. 8 for (var i in operations) {
  128. 13 child.__defineSetter__(i, operations[i]);
  129. }
  130. }
  131. 88 operations = proto.getters || {};
  132. 88 if (operations) {
  133. 88 for (i in operations) {
  134. 47 child.__defineGetter__(i, operations[i]);
  135. }
  136. }
  137. 88 for (i in proto) {
  138. 347 if (i != "getters" && i != "setters") {
  139. 326 var f = proto[i];
  140. 326 if ("function" === typeof f) {
  141. 293 var meta = f.__meta || {};
  142. 293 if (!meta.isConstructor) {
  143. 292 child[i] = functionWrapper(f, i);
  144. } else {
  145. 1 child[i] = f;
  146. }
  147. } else {
  148. 33 child[i] = f;
  149. }
  150. }
  151. }
  152. };
  153. 1var _export = function (obj, name) {
  154. 11 if (obj && name) {
  155. 2 obj[name] = this;
  156. } else {
  157. 9 obj.exports = obj = this;
  158. }
  159. 11 return this;
  160. };
  161. /**
  162. * @ignore
  163. */
  164. 1var __define = function (child, sup, proto) {
  165. 46 var childProto = child.prototype, supers = [];
  166. 46 var unique = "define" + ++classCounter, bases = [], staticBases = [];
  167. 46 var instanceSupers = [], staticSupers = [];
  168. 46 var meta = childProto.__meta = {
  169. supers:instanceSupers,
  170. unique:unique,
  171. bases:bases,
  172. superMeta:{
  173. f:null,
  174. pos:0,
  175. name:null
  176. }
  177. };
  178. 46 var childMeta = child.__meta = {
  179. supers:staticSupers,
  180. unique:unique,
  181. bases:staticBases,
  182. isConstructor:true,
  183. superMeta:{
  184. f:null,
  185. pos:0,
  186. name:null
  187. }
  188. };
  189. 46 if (sup) {
  190. 31 supers = Array.isArray(sup) ? sup : [sup];
  191. 31 sup = supers.shift();
  192. 31 child.__proto__ = sup;
  193. 31 childProto.__proto__ = sup.prototype;
  194. 31 mixinSupers(sup.prototype, instanceSupers, bases),
  195. mixinSupers(sup, staticSupers, staticBases);
  196. }
  197. 46 if (proto) {
  198. 44 var instance = meta.proto = proto.instance || {};
  199. 44 !instance.hasOwnProperty("constructor") && (instance.constructor = defaultFunction);
  200. 44 var stat = childMeta.proto = proto.static || {};
  201. 44 stat.init = stat.init || defaultFunction;
  202. 44 defineProps(childProto, instance, false);
  203. 44 defineProps(child, stat, true);
  204. } else {
  205. 2 meta.proto = {};
  206. 2 childMeta.proto = {};
  207. 2 child.init = functionWrapper(defaultFunction, "init");
  208. 2 childProto.constructor = functionWrapper(defaultFunction, "constructor");
  209. }
  210. 46 if (supers.length) {
  211. 7 mixin.apply(child, supers);
  212. }
  213. 46 child.mixin = mixin;
  214. 46 childProto._super = child._super = callSuper;
  215. 46 child.as = _export;
  216. 46 childProto._static = child;
  217. };
  218. /**
  219. * Defines a new class to be used
  220. *
  221. * <p>
  222. * Class methods
  223. * <ul>
  224. * <li>as(module | object, name): exports the object to module or the object with the name</li>
  225. * <li>mixin(mixin) : mixes in an object</li>
  226. * </ul>
  227. * </br>
  228. * Instance methods
  229. * <ul>
  230. * <li>_super(argumnents, [?newargs]): calls the super of the current method</li>
  231. * </ul>
  232. *
  233. * </br>
  234. * Instance properties
  235. * <ul>
  236. * <li>_static: use to reference class properties and methods</li>
  237. * </ul>
  238. *
  239. * </p>
  240. *
  241. *
  242. * @example
  243. * //Class without a super class
  244. * var Mammal = comb.define(null, {
  245. * instance : {
  246. *
  247. * constructor: function(options) {
  248. * options = options || {};
  249. * this._super(arguments);
  250. * this._type = options.type || "mammal";
  251. * },
  252. *
  253. * speak : function() {
  254. * return "A mammal of type " + this._type + " sounds like";
  255. * },
  256. *
  257. * //Define your getters
  258. * getters : {
  259. * type : function() {
  260. * return this._type;
  261. * }
  262. * },
  263. *
  264. * //Define your setters
  265. * setters : {
  266. * type : function(t) {
  267. * this._type = t;
  268. * }
  269. * }
  270. * },
  271. *
  272. * //Define your static methods
  273. * static : {
  274. * soundOff : function() {
  275. * return "Im a mammal!!";
  276. * }
  277. * }
  278. * });
  279. *
  280. * //Show singular inheritance
  281. *var Wolf = comb.define(Mammal, {
  282. * instance: {
  283. * constructor: function(options) {
  284. * options = options || {};
  285. * //You can call your super constructor, or you may not
  286. * //call it to prevent the super initializing parameters
  287. * this._super(arguments);
  288. * this._sound = "growl";
  289. * this._color = options.color || "grey";
  290. * },
  291. *
  292. * speak : function() {
  293. * //override my super classes speak
  294. * //Should return "A mammal of type mammal sounds like a growl"
  295. * return this._super(arguments) + " a " + this._sound;
  296. * },
  297. *
  298. * //add new getters for sound and color
  299. * getters : {
  300. *
  301. * color : function() {
  302. * return this._color;
  303. * },
  304. *
  305. * sound : function() {
  306. * return this._sound;
  307. * }
  308. * },
  309. *
  310. * setters : {
  311. *
  312. * //NOTE color is read only except on initialization
  313. *
  314. * sound : function(s) {
  315. * this._sound = s;
  316. * }
  317. * }
  318. *
  319. * },
  320. *
  321. * static : {
  322. * //override my satic soundOff
  323. * soundOff : function() {
  324. * //You can even call super in your statics!!!
  325. * //should return "I'm a mammal!! that growls"
  326. * return this._super(arguments) + " that growls";
  327. * }
  328. * }
  329. *});
  330. *
  331. *
  332. * //Typical hierarchical inheritance
  333. * // Mammal->Wolf->Dog
  334. * var Dog = comb.define(Wolf, {
  335. * instance: {
  336. * constructor: function(options) {
  337. * options = options || {};
  338. * this._super(arguments);
  339. * //override Wolfs initialization of sound to woof.
  340. * this._sound = "woof";
  341. *
  342. * },
  343. *
  344. * speak : function() {
  345. * //Should return "A mammal of type mammal sounds like a growl thats domesticated"
  346. * return this._super(arguments) + " thats domesticated";
  347. * }
  348. * },
  349. *
  350. * static : {
  351. * soundOff : function() {
  352. * //should return "I'm a mammal!! that growls but now barks"
  353. * return this._super(arguments) + " but now barks";
  354. * }
  355. * }
  356. *});
  357. *
  358. *
  359. *
  360. * dog instanceof Wolf => true
  361. * dog instanceof Mammal => true
  362. * dog.speak() => "A mammal of type mammal sounds like a woof thats domesticated"
  363. * dog.type => "mammal"
  364. * dog.color => "gold"
  365. * dog.sound => "woof"
  366. * Dog.soundOff() => "Im a mammal!! that growls but now barks"
  367. *
  368. * // Mammal->Wolf->Dog->Breed
  369. *var Breed = comb.define(Dog, {
  370. * instance: {
  371. *
  372. * //initialize outside of constructor
  373. * _pitch : "high",
  374. *
  375. * constructor: function(options) {
  376. * options = options || {};
  377. * this._super(arguments);
  378. * this.breed = options.breed || "lab";
  379. * },
  380. *
  381. * speak : function() {
  382. * //Should return "A mammal of type mammal sounds like a
  383. * //growl thats domesticated with a high pitch!"
  384. * return this._super(arguments) + " with a " + this._pitch + " pitch!";
  385. * },
  386. *
  387. * getters : {
  388. * pitch : function() {
  389. * return this._pitch;
  390. * }
  391. * }
  392. * },
  393. *
  394. * static : {
  395. * soundOff : function() {
  396. * //should return "I'M A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  397. * return this._super(arguments).toUpperCase() + "!";
  398. * }
  399. * }
  400. * });
  401. *
  402. *
  403. * var breed = new Breed({color : "gold", type : "lab"}),
  404. *
  405. *
  406. *
  407. * breed instanceof Dog => true
  408. * breed instanceof Wolf => true
  409. * breed instanceof Mammal => true
  410. * breed.speak() => "A mammal of type lab sounds like a woof "
  411. * + "thats domesticated with a high pitch!"
  412. * breed.type => "lab"
  413. * breed.color => "gold"
  414. * breed.sound => "woof"
  415. * breed.soundOff() => "IM A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  416. *
  417. *
  418. * //Example of multiple inheritance
  419. * //NOTE proto is optional
  420. *
  421. * //Mammal is super class
  422. * //Wolf Dog and Breed inject functionality into the prototype
  423. * var Lab = comb.define([Mammal, Wolf, Dog, Breed]);
  424. *
  425. * var lab = new Lab();
  426. * lab instanceof Wolf => false
  427. * lab instanceof Dog => false
  428. * lab instanceof Breed => false
  429. * lab instanceof Mammal => true
  430. * lab.speak() => "A mammal of type mammal sounds like a"
  431. * + " woof thats domesticated with a high pitch!"
  432. * Lab.soundOff() => "IM A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  433. *
  434. * @name define
  435. * @memberOf comb
  436. *
  437. * @param {Array|Class} super the supers of this class
  438. * @param {Object} [proto] the object used to define this class
  439. * @param {Object} [proto.instance] the instance methods of the class
  440. * @param {Object} [proto.instance.getters] the getters for the class
  441. * @param {Object} [proto.instance.setters] the setters for the class
  442. * @param {Object} [proto.static] the Class level methods of this class
  443. * @param {Object} [proto.static.getters] static getters for the object
  444. * @param {Object} [proto.static.setters] static setters for the object
  445. *
  446. * @returns {Object} the constructor of the class to be used with new keyword
  447. */
  448. 1exports.define = function (sup, proto) {
  449. 43 var child = function () {
  450. 2278 this.constructor.apply(this, arguments);
  451. };
  452. 43 __define(child, sup, proto);
  453. 43 return child.init() || child;
  454. };
  455. /**
  456. * Defines a singleton instance of a Class. See {@link define}
  457. * @example
  458. * var MyLab = comb.singleton([Mammal, Wolf, Dog, Breed]);
  459. * var myLab1 = new MyLab();
  460. * myLab1.type = "collie"
  461. * var myLab2 = new MyLab();
  462. * myLab1 === myLab2 => true
  463. * myLab1.type => "collie"
  464. * myLab2.type => "collie"
  465. *
  466. *
  467. * @name singleton
  468. * @memberOf comb
  469. */
  470. 1exports.singleton = function (sup, proto) {
  471. 3 var retInstance;
  472. 3 var child = function () {
  473. 6 if (!retInstance) {
  474. 2 this.constructor.apply(this, arguments);
  475. 2 retInstance = this;
  476. }
  477. 6 return retInstance;
  478. };
  479. 3 __define(child, sup, proto);
  480. 3 return child.init() || child;
  481. };
extensions/arguments.js
Coverage100.00 SLOC14 LOC4 Missed0
  1. 1var base = require("../base"),
  2. utils = require("./utils"),
  3. extend = utils.extend,
  4. array = base.array;
  5. 1var methods = [
  6. ["argsToArray", "toArray"]
  7. ];
  8. 1module.exports = function (o) {
  9. 4 return extend(o, methods, base);
  10. };
extensions/array.js
Coverage100.00 SLOC21 LOC7 Missed0
  1. 1var base = require("../base"),
  2. array = base.array,
  3. string = base.string,
  4. utils = require("./utils"),
  5. extend = utils.extend;
  6. 1var stringMethods = ["style"];
  7. 1var methods = ["forEach", "map", "filter", "reduce", "reduceRight", "some", "every", "indexOf", "lastIndexOf",
  8. "zip", "sum", "avg", "sort", "min", "max", "difference", "removeDuplicates", "unique", "rotate",
  9. "permutations", "transpose", "valuesAt", "union", "intersect", "powerSet", "cartesian", "compact",
  10. "multiply", "flatten", "pluck", "invoke"];
  11. 1module.exports = function (o) {
  12. 183 extend(o, methods, array);
  13. 183 extend(o, stringMethods, string);
  14. 183 return o;
  15. };
extensions/cast.js
Coverage100.00 SLOC45 LOC13 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend,
  5. arrayExtension = require("./array"),
  6. argumentsExtension = require("./arguments"),
  7. dateExtension = require("./date"),
  8. functionExtension = require("./function"),
  9. numberExtension = require("./number"),
  10. objectExtension = require("./object"),
  11. stringExtension = require("./string");
  12. 1var methods = {
  13. array:function () {
  14. 183 return arrayExtension(this);
  15. },
  16. date:function () {
  17. 277 return dateExtension(this);
  18. },
  19. args:function () {
  20. 4 return argumentsExtension(this);
  21. },
  22. func:function () {
  23. 42 return functionExtension(this);
  24. },
  25. number:function () {
  26. 159 return numberExtension(this);
  27. },
  28. string:function () {
  29. 485 return stringExtension(this);
  30. },
  31. object:function () {
  32. 516 return objectExtension(this);
  33. }
  34. };
  35. 1module.exports = function (o) {
  36. 1194 extend(o, Object.keys(methods), methods, function (name, func) {
  37. 8358 return base.partial(func);
  38. });
  39. 1194 return o;
  40. };
extensions/date.js
Coverage100.00 SLOC26 LOC5 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend,
  5. date = base.date,
  6. string = base.string;
  7. 1var methods = [
  8. "add",
  9. "compare",
  10. "difference",
  11. "format",
  12. "getDaysInMonth",
  13. "getTimezoneName",
  14. "isLeapYear",
  15. "isWeekend"
  16. ];
  17. 1module.exports = function (o) {
  18. 277 extend(o, methods, date);
  19. 277 return o;
  20. };
extensions/function.js
Coverage100.00 SLOC42 LOC16 Missed0
  1. 1var base = require("../base"),
  2. argsToArray = base.argsToArray,
  3. utils = require("./utils"),
  4. array = base.array,
  5. extend = utils.extend,
  6. comb;
  7. 1var methods = [
  8. "hitch",
  9. "bind",
  10. "hitchIgnore",
  11. "bindIgnore",
  12. "partial",
  13. "applyFirst",
  14. "bindFirst",
  15. "curry",
  16. "extend"
  17. ];
  18. 1var baseMethods = ["extend"];
  19. 1module.exports = function (o) {
  20. 42 comb = comb || (require("../index"));
  21. 42 extend(o, methods, base, function (name, func) {
  22. 378 var ret;
  23. 378 if (name !== 'partial' && name !== "applyFirst") {
  24. 294 ret = function (arg1) {
  25. 7 var args = argsToArray(arguments, 1);
  26. 7 return comb(func.apply(null, [arg1, this].concat(args)));
  27. };
  28. } else {
  29. 84 ret = function (arg1) {
  30. 3 return comb(func.apply(null, [this].concat(argsToArray(arguments))));
  31. };
  32. }
  33. 378 return ret;
  34. });
  35. 42 extend(o, baseMethods, base);
  36. 42 return o;
  37. };
extensions/index.js
Coverage100.00 SLOC47 LOC24 Missed0
  1. 1var base = require("../base"),
  2. stringExtension = require("./string"),
  3. functionExtension = require("./function"),
  4. objectExtension = require("./object"),
  5. argumentsExtension = require("./arguments"),
  6. dateExtension = require("./date"),
  7. isExtension = require("./is"),
  8. castExtension = require("./cast.js"),
  9. numberExtension = require("./number"),
  10. arrayExtension = require("./array");
  11. 1var TRUE = isExtension(true),
  12. FALSE = isExtension(false);
  13. 1function createExtension(obj) {
  14. 1268 if (base.isBoolean(obj)) {
  15. 17 return obj ? TRUE : FALSE;
  16. }
  17. 1251 if (!base.isUndefinedOrNull(obj) && !obj.__isExtended__) {
  18. 1194 obj = isExtension(obj);
  19. 1194 obj = castExtension(obj);
  20. 1194 if (obj.isObject()) {
  21. 516 obj.object();
  22. }
  23. 1194 if (obj.isArguments()) {
  24. 4 obj.args();
  25. }
  26. 1194 if (obj.isArray()) {
  27. 183 obj.array();
  28. }
  29. 1194 if (obj.isFunction()) {
  30. 42 obj.func();
  31. }
  32. 1194 if (obj.isString()) {
  33. 485 obj.string();
  34. }
  35. 1194 if (obj.isDate()) {
  36. 275 obj.date();
  37. }
  38. 1194 if (obj.isNumber()) {
  39. 159 obj.number();
  40. }
  41. }
  42. 1251 return obj;
  43. }
  44. 1exports.createExtension = createExtension;
extensions/is.js
Coverage100.00 SLOC87 LOC28 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend;
  5. 1var methods = [
  6. "isDefined",
  7. "isUndefined",
  8. "isNull",
  9. "isUndefinedOrNull",
  10. "isArguments",
  11. "isObject",
  12. "isHash",
  13. "isBoolean",
  14. "isDate",
  15. "isEmpty",
  16. "isArray",
  17. "isFunction",
  18. "isInstanceOf",
  19. "isNumber",
  20. "isPromiseLike",
  21. "isRegExp",
  22. "isString",
  23. "deepEqual"
  24. ];
  25. 1module.exports = function (o) {
  26. 1196 var valueOf = false, val;
  27. 1196 if (!base.isInstanceOf(o, Object)) {
  28. 638 if (base.isBoolean(o)) {
  29. 2 val = new Boolean(o);
  30. 2 valueOf = true;
  31. 636 } else if (base.isNumber(o)) {
  32. 155 val = new Number(o);
  33. 155 valueOf = true;
  34. 481 } else if (base.isString(o)) {
  35. 481 val = new String(o);
  36. 481 valueOf = true;
  37. }
  38. } else {
  39. 558 val = o;
  40. }
  41. 1196 var ret = extend(val, methods, base, null, valueOf);
  42. 1196 if (valueOf) {
  43. 638 Object.defineProperty(ret, "valueOf", {
  44. value:function () {
  45. 5094 return o;
  46. },
  47. writable:false,
  48. enumerable:false,
  49. configurable:true
  50. });
  51. }
  52. 1196 Object.defineProperty(ret, "eq", {
  53. value:function (other) {
  54. 6 return o === other;
  55. },
  56. writable:false,
  57. enumerable:false,
  58. configurable:true
  59. });
  60. 1196 Object.defineProperty(ret, "neq", {
  61. value:function (other) {
  62. 6 return o !== other;
  63. },
  64. writable:false,
  65. enumerable:false,
  66. configurable:true
  67. });
  68. 1196 Object.defineProperty(ret, "print", {
  69. value:function () {
  70. 3 console.log(o);
  71. 3 return val;
  72. },
  73. writable:false,
  74. enumerable:false,
  75. configurable:true
  76. });
  77. 1196 Object.defineProperty(ret, "__isExtended__", {
  78. value:true,
  79. writable:false,
  80. enumerable:false,
  81. configurable:true
  82. });
  83. 1196 return ret;
  84. };
extensions/number.js
Coverage100.00 SLOC17 LOC5 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend,
  5. number = base.number,
  6. string = base.string;
  7. 1var methods = [
  8. "round",
  9. "roundCeil"
  10. ];
  11. 1module.exports = function (o) {
  12. 159 extend(o, methods, number, null, true);
  13. 159 return o;
  14. };
extensions/object.js
Coverage100.00 SLOC38 LOC12 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend,
  5. hash = base.hash,
  6. argsToArray = base.argsToArray,
  7. comb;
  8. 1var methods = [
  9. "hitch",
  10. "hitchIgnore",
  11. "bind",
  12. "bindIgnore",
  13. "merge",
  14. "extend",
  15. "deepMerge"
  16. ];
  17. 1var lastMethod = ["curry"];
  18. 1var hashMethods = [
  19. "forEach",
  20. "filter",
  21. "invert",
  22. "values",
  23. "toArray"
  24. ];
  25. 1module.exports = function (o) {
  26. 516 comb = comb || (require("../index"));
  27. 516 extend(o, methods, base);
  28. 516 extend(o, hashMethods, hash);
  29. 516 extend(o, lastMethod, base, function (name, func) {
  30. 516 return function () {
  31. 1 return comb(func.apply(null, argsToArray(arguments).concat([this])));
  32. };
  33. });
  34. 516 return o;
  35. };
extensions/string.js
Coverage100.00 SLOC45 LOC21 Missed0
  1. 1var base = require("../base"),
  2. string = base.string,
  3. date = base.date,
  4. utils = require("./utils"),
  5. extend = utils.extend,
  6. regexp = base.regexp,
  7. array = base.array,
  8. argsToArray = base.argsToArray,
  9. comb;
  10. 1var methods = ["style", "multiply", "toArray", "format", "truncate", "pad"];
  11. 1var baseMethods = ["camelize", "underscore", "classify", "pluralize", "singularize", "applyFirst", "bindFirst", "partial"];
  12. 1var functionMethods = ["hitch", "bind", "hitchIgnore", "curry"];
  13. 1var arrayMethods = ["pluck", "invoke"];
  14. 1var dateMethods = [
  15. ["parse", "parseDate"]
  16. ];
  17. 1var regexpMethods = [
  18. ["escapeString", "escape"]
  19. ];
  20. 1module.exports = function (o) {
  21. 485 comb = comb || (require("../index"));
  22. 485 extend(o, methods, string, null, true);
  23. 485 extend(o, baseMethods, base, null, true);
  24. 485 extend(o, dateMethods, date, null, true);
  25. 485 extend(o, regexpMethods, regexp, null, true);
  26. 485 extend(o, arrayMethods, array, function (name, func) {
  27. 970 return function () {
  28. 7 return comb(func.apply(null, argsToArray(arguments).concat([this.valueOf()])));
  29. };
  30. }, true);
  31. 485 extend(o, functionMethods, base, function (name, func) {
  32. 1940 return function (arg1) {
  33. 3 var args = argsToArray(arguments, 1);
  34. 3 return comb(func.apply(null, [arg1, this.valueOf()].concat(args)));
  35. };
  36. });
  37. 485 return o;
  38. };
extensions/utils.js
Coverage100.00 SLOC45 LOC19 Missed0
  1. 1var define = require("../define").define,
  2. base = require("../base"),
  3. array = base.array,
  4. isBoolean = base.isBoolean,
  5. argsToArray = base.argsToArray,
  6. comb;
  7. 1function proxyFunc(name, func, valueOf, scope) {
  8. 43916 return function proxy() {
  9. 9055 comb || (comb = require("../index"));
  10. 9055 var ret = func.apply(scope, [valueOf ? this.valueOf() : this].concat(argsToArray(arguments)));
  11. 9052 if (ret && !base.isBoolean(ret) && ret !== this && !ret.__isExtended__) {
  12. 569 ret = comb(ret);
  13. }
  14. 9052 return ret;
  15. };
  16. }
  17. 1function extend(obj, methods, base, creator, valueOf) {
  18. 7738 valueOf = isBoolean(valueOf) ? valueOf : false;
  19. 7738 array.forEach(methods, function (method) {
  20. 56078 var newFunc, func, m, name;
  21. 56078 if (Array.isArray(method) && method.length === 2) {
  22. 974 m = method[0];
  23. 974 name = method[1];
  24. } else {
  25. 55104 m = name = method;
  26. }
  27. 56078 Object.defineProperty(obj, name, {
  28. value:(creator || proxyFunc)(name, base[m], valueOf, base),
  29. writable:false,
  30. enumerable:false,
  31. configurable:true
  32. });
  33. });
  34. 7738 return obj;
  35. }
  36. 1module.exports = {
  37. proxyFunc:proxyFunc,
  38. extend:extend
  39. };
index.js
Coverage100.00 SLOC107 LOC8 Missed0
  1. 1var base = require("./base"),
  2. extension = require("./extensions"),
  3. createExtension = extension.createExtension;
  4. /**
  5. * @projectName comb
  6. *
  7. * @github https://github.com/C2FO/comb
  8. *
  9. * @includeDoc [Getting Started] ../docs-md/introduction.md
  10. * @includeDoc [OO] ../docs-md/define.md
  11. * @includeDoc [Promises] ../docs-md/promise.md
  12. * @includeDoc [Logging] ../docs-md/logging.md
  13. * @includeDoc [Utilities] ../docs-md/utilities.md
  14. * @includeDoc [Test Coverage] [../docs-md/coverage.html]
  15. *
  16. *
  17. * @header
  18. *
  19. * #Comb
  20. *
  21. * ##Overview
  22. *
  23. * Framework for node that provides a one stop shop for frequently needed utilities, including:
  24. *
  25. * * [OO utilties](./define.html)
  26. * * Collections
  27. * * [Logging](./logging.html)
  28. * * [String &amp; date formatting](./utilities)
  29. * * [Flow control](./promise.html)
  30. *
  31. *
  32. * ##Installation
  33. *
  34. * `npm install comb`
  35. *
  36. * ##Highlights
  37. *
  38. * * 100% test coverage!
  39. * * comb([define](./comb.html#.define)|[singleton](./comb.html#.singleton))
  40. * * The backbone of comb.
  41. * * Options for classical inheritance models as well as mixins(pseudo multi-inheritance)
  42. * * You can call this._super from any method. Including statically defined ones!
  43. * * Access to your class level properties within an instance
  44. * * Logging
  45. * * Logger inheritance through name spaces
  46. * * Predefined [level](./comb_logging_Level.html) level definition along with the ability to define your own.
  47. * * Multiple appenders including
  48. * * [FileAppender](./comb_logging_appenders_FileAppender.html) - log it to a file
  49. * * [RollingFileAppender](./comb_logging_appenders_RollingFileAppender.html) - log it to a file up to a customizable size then create a new one.
  50. * * [JSONAppender](./comb_logging_appenders_JSONAppender.html) - write it out as JSON to a file.
  51. * * [ConsoleAppender](./comb_logging_appenders_ConsoleAppender.html)- log it to the console
  52. * * Configurable with [files OR programatically](./comb_logger.html#.configure)
  53. * * Collections
  54. * * [RedBlackTree](./comb_collections_RedBlackTree.html)
  55. * * [AVLTree](./comb_collections_AVLTree.html)
  56. * * [AnderssonTree](./comb_collections_AnderssonTree.html)
  57. * * [BinaryTree](./comb_collections_BinaryTree.html)
  58. * * [HashTable](./comb_collections_HashTable.html)
  59. * * [MaxHeap](./comb_collections_MaxHeap.html)
  60. * * [MinHeap](./comb_collections_MinHeap.html)
  61. * * [Pool](./comb_collections_Pool.html)
  62. * * [PriorityQueue](./comb_collections_PriorityQueue.html)
  63. * * [Queue](./comb_collections_Queue.html)
  64. * * [Stack](./comb_collections_Stack.html)
  65. *
  66. * * [Flow control](./promise.html)
  67. * * [Promises](./comb_Promise.html)
  68. * * [PromiseList](./comb_PromiseList.html)
  69. * * [comb.when](./comb.html#.when)
  70. * * [comb.serial](./comb.html#.serial)
  71. *
  72. * @footer
  73. * ##License
  74. *
  75. * MIT <https://github.com/C2FO/comb/raw/master/LICENSE>
  76. *
  77. * ##Meta
  78. * * Code: `git clone git://github.com/C2FO/comb.git`
  79. * * Website: <http://c2fo.com>
  80. * * Twitter: [http://twitter.com/c2fo](http://twitter.com/c2fo) - 877.465.4045
  81. */
  82. /**
  83. * Utilities for javascript, optimized for the server environment.
  84. *
  85. *
  86. * @namespace
  87. * @ignoreCode
  88. */
  89. 1var comb = createExtension;
  90. 1base.merge(comb, base, require("./define"), require("./promise"), require("./async"), require("./plugins"), require("./collections"), require("./logging"));
  91. 1comb.definePlugin = function (obj) {
  92. 1 if (comb.isHash(obj)) {
  93. 1 comb.deepMerge(comb, obj);
  94. }
  95. 1 return comb;
  96. };
  97. 1module.exports = comb;
logging/appenders/appender.js
Coverage100.00 SLOC167 LOC30 Missed0
  1. 1var define = require("../../define.js").define, base = require("../../base"), Level = require("../level");
  2. 1var APPENDER_TYPES = {};
  3. /**
  4. * @class Base class for all appenders
  5. *
  6. * @name Appender
  7. * @memberOf comb.logging.appenders
  8. *
  9. * @param {Object} [options] options to assign to this Appender
  10. * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender
  11. * on a logger it must have a different name.
  12. * @param {String} [options.pattern="[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}"]
  13. * <p>Available Options for formatting see {@link comb.string.format} for formatting options</p>
  14. * <ul>
  15. * <li>timeStamp - the timestamp of the event being logged</li>
  16. * <li>level - the {@link comb.logging.Level} of the event</li>
  17. * <li>levelName - the name of the level being logged</li>
  18. * <li>name - the name of the logger logging the event</li>
  19. * <li>message - the message being logged</li>
  20. * </ul>
  21. * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender
  22. * <p><b>Note:</b> the level can be different from the logger in the case that you want a particular logger
  23. * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the
  24. * appenders level is lower than the logger is will not receive any messages.</p>
  25. *
  26. * @property {String} name the name of this Appender.
  27. * @property {String} pattern the pattern for this Appender.
  28. * @property {comb.logging.Level} level the level of this Appender.
  29. * @ignoreCode
  30. */
  31. 1define(null, {
  32. instance:{
  33. /**@lends comb.logging.appenders.Appender.prototype*/
  34. constructor:function (options) {
  35. 21 options = options || {};
  36. 21 this.name = options.name || "appender";
  37. 21 this.pattern = options.pattern || "[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}";
  38. 21 var level = options.level;
  39. 21 if (options.level && (level = Level.toLevel(level))) {
  40. 3 this.__level = level;
  41. }
  42. },
  43. /**
  44. * Appends a message to a log.
  45. * <b>This method is abstract and must be implemented in subclasses</b>
  46. * @param {Object} event the logging event to log.
  47. * @param {Date} event.timeStamp the timeStamp of the event.
  48. * @param {comb.logging.Level} level the level of the event.
  49. * @param {String} name the name of the logger the event was emitted from.
  50. * @param {String} message the message that is being logged.
  51. *
  52. */
  53. append:function (event) {
  54. 1 throw new Error("abstract method");
  55. },
  56. _canAppend:function (event) {
  57. 76 return !base.isUndefinedOrNull(this.__level) && event.level.isGreaterOrEqualToo(this.__level);
  58. },
  59. /**@ignore*/
  60. setters:{
  61. /**@ignore*/
  62. level:function (level) {
  63. 59 if (level && level instanceof Level) {
  64. 52 this.__level = level;
  65. } else {
  66. //try to get the level
  67. 7 level = Level.toLevel(level);
  68. 7 if (level) {
  69. 4 this.__level = level;
  70. }
  71. }
  72. },
  73. pattern:function (patt) {
  74. 22 if (base.isString(patt)) {
  75. 22 this.__pattern = patt;
  76. }
  77. },
  78. name:function (name) {
  79. 27 if (base.isString(name)) {
  80. 27 this.__name = name;
  81. }
  82. }
  83. },
  84. /**@ignore*/
  85. getters:{
  86. /**@ignore*/
  87. level:function () {
  88. 92 return this.__level;
  89. },
  90. name:function () {
  91. 112 return this.__name;
  92. },
  93. pattern:function () {
  94. 3 return this.__pattern;
  95. }
  96. }
  97. },
  98. "static":{
  99. /**@lends comb.logging.appenders.Appender*/
  100. /**
  101. * Register an appender so it can be used with {@link comb.logging.PropertyConfigurator}
  102. *
  103. * @example
  104. *
  105. * var Appender = comb.logging.appenders.Appender;
  106. * comb.define(Appender, {
  107. * instance : {
  108. * append : function(event){
  109. * //log the message
  110. * }
  111. * }
  112. * }).registerType("MyAppender").as(module);
  113. *
  114. * @param {String} type the identifier for your appender type.
  115. * @return returns the Appender class for chaining.
  116. */
  117. registerType:function (type) {
  118. 5 if (base.isString(type)) {
  119. 5 APPENDER_TYPES[type.toLowerCase()] = this;
  120. }
  121. 5 return this;
  122. },
  123. /**
  124. * Acts as a factory for appenders.
  125. *
  126. * @example
  127. *
  128. * var logging = comb.logging,
  129. * Logger = logging.Logger,
  130. * Appender = logging.appenders.Appender;
  131. *
  132. * var logger = comb.logging.Logger.getLogger("my.logger");
  133. * logger.addAppender(Appender.createAppender("consoleAppender"));
  134. *
  135. * @param {String} type the type of appender to create.
  136. * @param {Object} [options={}] additional options to pass to the appender.
  137. * @return {comb.logging.appenders.Appender} an appender to add to a logger.
  138. */
  139. createAppender:function (type, options) {
  140. 14 var caseType = type.toLowerCase();
  141. 14 if (caseType in APPENDER_TYPES) {
  142. 13 return new APPENDER_TYPES[caseType](options);
  143. } else {
  144. 1 throw new Error(type + " appender is not registered!");
  145. }
  146. }
  147. }
  148. }).as(module);
logging/appenders/consoleAppender.js
Coverage100.00 SLOC37 LOC17 Missed0
  1. 1var define = require("../../define.js").define, base = require("../../base"), string = base.string, style = string.style, format = string.format, Appender = require("./appender"), Level = require("../level");
  2. /**
  3. * @class Appends messages to the console.
  4. *
  5. * @name ConsoleAppender
  6. * @augments comb.logging.appenders.Appender
  7. * @memberOf comb.logging.appenders
  8. */
  9. 1define(Appender, {
  10. instance:{
  11. constructor:function (options) {
  12. 9 options = options || {};
  13. 9 !options.name && (options.name = "consoleAppender");
  14. 9 this._super(arguments, [options]);
  15. },
  16. append:function (event) {
  17. 7 if (this._canAppend(event)) {
  18. 7 var message = format(this.__pattern, event);
  19. 7 var level = event.level;
  20. 7 if (Level.ERROR.equals(level) || Level.FATAL.equals(level)) {
  21. 3 console.log(style(message, "red"));
  22. 4 } else if (Level.WARN.equals(level)) {
  23. 1 console.log(style(message, "yellow"));
  24. 3 } else if (Level.DEBUG.equals(level)) {
  25. 1 console.log(style(message, "magenta"));
  26. 2 } else if (Level.TRACE.equals(level)) {
  27. 1 console.log(style(message, "cyan"));
  28. } else {
  29. 1 console.log(message);
  30. }
  31. }
  32. }
  33. }
  34. }).registerType("ConsoleAppender").as(module);
logging/appenders/fileAppender.js
Coverage100.00 SLOC83 LOC22 Missed0
  1. 1var define = require("../../define.js").define,
  2. base = require("../../base"),
  3. promise = require("../../promise"),
  4. string = base.string,
  5. Promise = promise.Promise,
  6. PromiseList = promise.PromiseList,
  7. style = string.style,
  8. format = string.format,
  9. Appender = require("./appender"),
  10. Level = require("../level"),
  11. fs = require("fs");
  12. /**
  13. * @class Appends messages to a file.
  14. *
  15. * @example
  16. * var fileAppender = new comb.logging.appenders.FileAppender({
  17. * file : "/var/log/myLog.log"
  18. * });
  19. *
  20. *
  21. * @name FileAppender
  22. * @augments comb.logging.appenders.Appender
  23. * @memberOf comb.logging.appenders
  24. *
  25. * @param {Object} [options] options to assign to this Appender
  26. * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender
  27. * on a logger it must have a different name.
  28. * @param {String} [options.pattern="[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}"]
  29. * <p>Available Options for formatting see {@link comb.string.format} for formatting options</p>
  30. * <ul>
  31. * <li>timeStamp - the timestamp of the event being logged</li>
  32. * <li>level - the {@link comb.logging.Level} of the event</li>
  33. * <li>levelName - the name of the level being logged</li>
  34. * <li>name - the name of the logger logging the event</li>
  35. * <li>message - the message being logged</li>
  36. * </ul>
  37. * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender
  38. * <p><b>Note:</b> the level can be different from the logger in the case that you want a particular logger
  39. * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the
  40. * appenders level is lower than the logger is will not recieve any messages.</p>
  41. *
  42. * @param {String} [options.file="./log.log"] the file to log events to.
  43. * @param {String} [options.encoding="utf8"] the encoding of the file.
  44. * @param {Boolean} [options.overwrite=false] if true the log file is overwritten otherwise it is appended to.
  45. * @ignoreCode
  46. */
  47. 1define(Appender, {
  48. instance:{
  49. constructor:function (options) {
  50. 8 options = options || {};
  51. 8 !options.name && (options.name = "fileAppender");
  52. 8 this.__file = options.file || "./log.log";
  53. 8 this.__encoding = options.encoding || "utf8";
  54. 8 this.__overwrite = options.overwrite || false;
  55. 8 this.__writeStream = options.writeStream || fs.createWriteStream(this.__file, { flags:this.__overwrite ? "w" : 'a', encoding:this.__encoding});
  56. 8 this._super([options]);
  57. 8 this.__pattern += "\n";
  58. 8 base.listenForExit(base.hitch(this, "__onExit"));
  59. },
  60. __onExit:function () {
  61. 14 var ret = new Promise();
  62. 14 var ws = this.__writeStream;
  63. 14 this.__writeStream = null;
  64. 14 ws.on("close", base.hitch(ret, "callback"));
  65. 14 ws.destroySoon();
  66. 14 return ret.promise();
  67. },
  68. append:function (event) {
  69. 15 var ws = this.__writeStream;
  70. 15 if (this._canAppend(event) && ws && ws.writable) {
  71. 15 var message = format(this.__pattern, event);
  72. 15 var level = event.level;
  73. 15 ws.write(message);
  74. }
  75. }
  76. }
  77. }).registerType("FileAppender").as(module);
logging/appenders/index.js
Coverage100.00 SLOC11 LOC5 Missed0
  1. /**@ignore*/
  2. 1exports.Appender = require("./appender");
  3. /**@ignore*/
  4. 1exports.ConsoleAppender = require("./consoleAppender");
  5. /**@ignore*/
  6. 1exports.FileAppender = require("./fileAppender");
  7. /**@ignore*/
  8. 1exports.JSONAppender = require("./jsonAppender");
  9. /**@ignore*/
  10. 1exports.RollingFileAppender = require("./rollingFileAppender");
logging/appenders/jsonAppender.js
Coverage100.00 SLOC85 LOC19 Missed0
  1. 1var define = require("../../define.js").define,
  2. base = require("../../base"),
  3. string = base.string,
  4. escape = base.regexp.escapeString,
  5. FileAppender = require("./fileAppender"),
  6. format = string.format,
  7. Level = require("../level"),
  8. fs = require("fs");
  9. /**
  10. * @class Appends messages to a file in JSON format. The messages are logged to an array in a JSON file
  11. * <b>The file is always overwritten</b>
  12. *
  13. * @example
  14. * //example log.json
  15. * [
  16. * {
  17. * "timestamp" : "Wed Jun 08 2011 11:16:20 GMT-0500 (CDT)",
  18. * "level" : "INFO",
  19. * "name" : "comb",
  20. * "message" : "INFO MESSAGE!!!!"
  21. * }
  22. * ]
  23. *
  24. *
  25. * @name JSONAppender
  26. * @augments comb.logging.appenders.FileAppender
  27. * @memberOf comb.logging.appenders
  28. *
  29. * @param {Object} [options] options to assign to this Appender
  30. * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender
  31. * on a logger it must have a different name.
  32. * @param {String} [options.pattern="{"timestamp" : "{timeStamp}", "level" : "{levelName}", "name" : "{name}", "message" : "{message}"}"]
  33. * <p>Available Options for formatting see {@link comb.string.format} for formatting options</p>
  34. * <ul>
  35. * <li>timeStamp - the timestamp of the event being logged</li>
  36. * <li>level - the {@link comb.logging.Level} of the event</li>
  37. * <li>levelName - the name of the level being logged</li>
  38. * <li>name - the name of the logger logging the event</li>
  39. * <li>message - the message being logged</li>
  40. * </ul>
  41. * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender
  42. * <p><b>Note:</b> the level can be different from the logger in the case that you want a particular logger
  43. * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the
  44. * appenders level is lower than the logger is will not recieve any messages.</p>
  45. *
  46. * @param {String} [options.file="./log.json"] the file to log events to.
  47. * @param {String} [options.encoding="utf8"] the encoding of the file.
  48. *
  49. * @ignoreCode
  50. */
  51. 1define(FileAppender, {
  52. instance:{
  53. constructor:function (options) {
  54. 5 options = options || {};
  55. 5 this.name = options.name || "JSONAppender";
  56. 5 this.__count = 0;
  57. 5 this.__file = options.file || "./log.json";
  58. 5 this.__encoding = options.encoding || "utf8";
  59. 5 this.__writeStream = options.writeStream || fs.createWriteStream(this.__file, { flags:"w", encoding:this.__encoding});
  60. 5 this.__writeStream.write("[\n");
  61. 5 this.level = options.level;
  62. //explicit overwrite of patter
  63. 5 this.__pattern = '{"timestamp" : "{timeStamp}", "level" : "{levelName}", "name" : "{name}", "message" : "{message}"}';
  64. 5 base.listenForExit(base.hitch(this, "__onExit"));
  65. },
  66. append:function (event) {
  67. 6 if (this._canAppend(event)) {
  68. 6 event.message = event.message.replace(/\n+/g, "\\n");
  69. 6 var message = (this.__count ? ",\n" : "\n") + format(this.__pattern, event);
  70. 6 this.__writeStream.write(message);
  71. 6 this.__count++;
  72. }
  73. },
  74. __onExit:function () {
  75. 5 this.__writeStream.write("]");
  76. 5 this._super(arguments);
  77. }
  78. }
  79. }).registerType("JSONAppender").as(module);
logging/appenders/rollingFileAppender.js
Coverage100.00 SLOC211 LOC79 Missed0
  1. 1var define = require("../../define.js").define,
  2. promise = require("../../promise"),
  3. Promise = promise.Promise,
  4. PromiseList = promise.PromiseList,
  5. base = require("../../base"),
  6. hitch = base.hitch,
  7. string = base.string,
  8. escape = base.regexp.escapeString,
  9. style = string.style,
  10. format = string.format,
  11. FileAppender = require("./fileAppender"),
  12. Level = require("../level"),
  13. fs = require("fs"),
  14. path = require("path");
  15. 1var conversion = {
  16. MB:1048576,
  17. KB:1024,
  18. GB:1073741824
  19. };
  20. 1var DEFAULT_SIZE = "10MB";
  21. 1var convertToBytes = function (str) {
  22. 3 var ret = DEFAULT_SIZE;
  23. 3 var match = str.match(/(\d+)(MB|KB|GB)$/);
  24. 3 if (match && match.length == 3) {
  25. 3 var size = parseInt(match[1], 10);
  26. 3 ret = size * conversion[match[2]];
  27. }
  28. 3 return ret;
  29. }
  30. /**
  31. * @class Appends messages to a file. Rolls files over when a size limit has been reached. Once the max file size has
  32. * been reached it is rolled over to a file called &lt;logName&gt;.log.n where n is a number.
  33. * </br></br>
  34. * <p>Example. RollingFileAppender is current writing to myLog.log, the log reaches is max size to it is
  35. * renamed to myLog.log.1 and a new myLog.log is created.</p>
  36. * </br>
  37. * If maxBackupIndex is reached then the log at that index is deleted. If maxBackupIndex is set to 0 then no log is
  38. * rolled over.</p>
  39. *
  40. * @name RollingFileAppender
  41. * @augments comb.logging.appenders.FileAppender
  42. * @memberOf comb.logging.appenders
  43. *
  44. * @param {Object} [options] options to assign to this Appender
  45. * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender
  46. * on a logger it must have a different name.
  47. * @param {String} [options.pattern="[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}"]
  48. * <p>Available Options for formatting see {@link comb.string.format} for formatting options</p>
  49. * <ul>
  50. * <li>timeStamp - the timestamp of the event being logged</li>
  51. * <li>level - the {@link comb.logging.Level} of the event</li>
  52. * <li>levelName - the name of the level being logged</li>
  53. * <li>name - the name of the logger logging the event</li>
  54. * <li>message - the message being logged</li>
  55. * </ul>
  56. * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender
  57. * <p><b>Note:</b> the level can be different from the logger in the case that you want a particular logger
  58. * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the
  59. * appenders level is lower than the logger is will not recieve any messages.</p>
  60. *
  61. * @param {String} [options.file="./log.log"] the file to log events to.
  62. * @param {String} [options.encoding="utf8"] the encoding of the file.
  63. * @param {Boolean} [options.overwrite=false] if true the log file is overwritten otherwise it is appended to.
  64. * @param {String} [options.maxSize="10MB"] the maxSize of a file. Valid options include "KB", "MB", or "GB"
  65. *
  66. * <pre class="code">
  67. * maxSize = "100MB"
  68. * //or
  69. * maxSize = "100KB"
  70. * //or
  71. * maxSize = "1GB"
  72. * </pre>
  73. *
  74. * @param {Number} [options.maxBackupIndex=10] the maximum number of files to rollOver.
  75. */
  76. 1define(FileAppender, {
  77. instance:{
  78. __watching:false,
  79. constructor:function (options) {
  80. 3 options = options || {};
  81. 3 this.maxSize = options.maxSize || DEFAULT_SIZE;
  82. 3 !options.name && (options.name = "rollingFileAppender");
  83. 3 this.maxBackupIndex = options.maxBackupIndex || 10;
  84. 3 this.__queue = [];
  85. 3 this.__inRollover = false;
  86. 3 this._super([options]);
  87. },
  88. __startCheck:function () {
  89. 1 if (!this.__watching) {
  90. 1 this.__watching = true;
  91. 1 fs.watchFile(this.__file, hitch(this, "__checkFile"));
  92. 1 fs.stat(this.__file, hitch(this, function (err, stat) {
  93. 1 this.__checkFile(stat);
  94. }));
  95. }
  96. },
  97. __checkFile:function (stats) {
  98. 5 var ret = new Promise();
  99. 5 if (!this.__inRollover) {
  100. 3 if (stats.size >= this.maxSize) {
  101. 2 if (this.maxBackupIndex > 0) {
  102. 1 this.__inRollover = true;
  103. 1 this.__onExit().chain(hitch(this, "__rollover")).then(hitch(this, function () {
  104. 1 var ws = fs.createWriteStream(this.__file, { flags:"w", encoding:this.__encoding});
  105. 1 ws.on("open", hitch(this, function () {
  106. 1 this.__writeStream = ws;
  107. 1 this.__inRollover = false;
  108. 1 this.__checkQueue();
  109. 1 ret.callback();
  110. }));
  111. }), hitch(ret, "errback", new Error("comb.logging.appenders.RollingFileAppender : error rolling over files")));
  112. } else {
  113. 1 this.__writeStream = fs.createWriteStream(this.__file, { flags:"w", encoding:this.__encoding});
  114. 1 ret.callback();
  115. }
  116. } else {
  117. 1 ret.callback();
  118. }
  119. } else {
  120. 2 ret.callback();
  121. }
  122. 5 return ret.promise();
  123. },
  124. append:function (event) {
  125. 12 if (this._canAppend(event)) {
  126. 12 !this.__watching && this.__startCheck();
  127. 12 var ws = this.__writeStream;
  128. 12 if (!this.__inRollover && ws && ws.writable) {
  129. 9 this._super(arguments);
  130. } else {
  131. 3 this.__queue.push(event);
  132. }
  133. }
  134. },
  135. __checkQueue:function () {
  136. 1 this.__queue.forEach(this.append, this);
  137. 1 this.__queue.length = 0;
  138. },
  139. __rollover:function () {
  140. 1 var ret = new Promise(), file = this.__file;
  141. 1 var dir = path.dirname(file), baseName = new RegExp("(" + escape(path.basename(path.basename(file))) + ")(?:\\.(\\d*))*");
  142. 1 fs.readdir(dir, hitch(this, function (err, files) {
  143. 1 files = files.filter(
  144. function (f) {
  145. 5 var match = f.match(baseName);
  146. 5 if (match) {
  147. 4 return true;
  148. } else {
  149. 1 return false;
  150. }
  151. });
  152. 1 files = files.sort(function (a, b) {
  153. 6 var ret = 0;
  154. 6 if (a > b) {
  155. 3 ret = 0;
  156. 3 } else if (a < b) {
  157. 3 ret = 1;
  158. }
  159. 6 return ret;
  160. });
  161. 1 var count = files.length, i = 0;
  162. 1 var checkFile = hitch(this, function () {
  163. 5 if (count > 0) {
  164. 4 var f = dir + "/" + files[i++];
  165. 4 if (count > this.maxBackupIndex) {
  166. //drop the file;
  167. 1 count--;
  168. 1 fs.unlink(f, function (err) {
  169. 1 err ? ret.errback(err) : checkFile();
  170. });
  171. } else {
  172. //rename the file
  173. 3 var rn = this.__file + "." + count--;
  174. 3 fs.rename(f, rn, function (err) {
  175. 3 err ? ret.errback(err) : checkFile();
  176. });
  177. }
  178. } else {
  179. 1 ret.callback();
  180. }
  181. });
  182. 1 checkFile();
  183. }));
  184. 1 return ret.promise();
  185. },
  186. getters:{
  187. maxSize:function () {
  188. 3 return this.__maxSize;
  189. }
  190. },
  191. setters:{
  192. maxSize:function (size) {
  193. 3 this.__maxSize = size ? convertToBytes(size) : DEFAULT_SIZE;
  194. }
  195. }
  196. }
  197. }).registerType("RollingFileAppender").as(module);
logging/config.js
Coverage100.00 SLOC213 LOC50 Missed0
  1. 1var define = require("../define.js").define, base = require("../base"), fs = require('fs'), Appender = require("./appenders/appender.js");
  2. 1var logging, Logger, Level, appenders;
  3. 1var checkProcessUncaughtException = (function () {
  4. 1 var isProcessUnCaaughtEception = false;
  5. 1 return function _checkProcessUncaughtException() {
  6. //if (!isProcessUnCaaughtEception) {
  7. 10 var rootLogger = Logger.getRootLogger();
  8. 10 if (!isProcessUnCaaughtEception) {
  9. 1 process.on("uncaughtException", function (err) {
  10. 2 if (rootLogger.appenders.length) {
  11. 1 rootLogger.error(err);
  12. } else {
  13. 1 console.error(err.stack);
  14. }
  15. });
  16. 1 isProcessUnCaaughtEception = true;
  17. }
  18. //}
  19. };
  20. })();
  21. 1var parseProperties = function (properties) {
  22. 4 for (var i in properties) {
  23. 4 var logger = Logger.getLogger(i);
  24. 4 var props = properties[i], level = props.level, appenderArr = props.appenders;
  25. 4 if (level) {
  26. 4 level = Level.toLevel(level);
  27. 4 if (level) {
  28. 4 logger.level = level;
  29. }
  30. }
  31. 4 if (appenderArr && base.isArray(appenderArr)) {
  32. 4 for (var j = appenderArr.length - 1; j >= 0; j--) {
  33. 14 var appenderProps = base.merge({}, appenderArr[j]), type = appenderProps.type;
  34. 14 appenderProps.type = null;
  35. 14 if (type) {
  36. 12 logger.addAppender(type, appenderProps);
  37. }
  38. }
  39. }
  40. }
  41. 4 checkProcessUncaughtException();
  42. };
  43. /**
  44. * @class default configurator for logging
  45. *
  46. * @name BasicConfigurator
  47. * @memberOf comb.logging
  48. * @ignoreCode
  49. *
  50. */
  51. 1var BasicConfigurator = (exports.BasicConfigurator = define(null, {
  52. instance:{
  53. /**@lends comb.logging.BasicConfigurator.prototype*/
  54. constructor:function () {
  55. 6 if (!Logger) {
  56. 1 logging = require("./index").logging;
  57. 1 Logger = logging.Logger;
  58. 1 Level = logging.Level;
  59. 1 appenders = logging.appenders;
  60. }
  61. },
  62. /**
  63. * Configure logging.
  64. *
  65. * @param {comb.logging.Appender} [appender=null] appender to add to the root logger, by default a console logger is added.
  66. */
  67. configure:function (appender) {
  68. 6 var rootLogger = Logger.getRootLogger();
  69. 6 rootLogger.removeAllAppenders();
  70. 6 if (base.isInstanceOf(appender, appenders.Appender)) {
  71. 2 rootLogger.addAppender(appender);
  72. } else {
  73. 4 rootLogger.addAppender(new appenders.ConsoleAppender());
  74. }
  75. 6 checkProcessUncaughtException();
  76. }
  77. }
  78. }));
  79. /**
  80. * @class Configures comb.Logger with the properties or properties contained within a file
  81. *
  82. * @example
  83. *
  84. * var propertyConfigurator = new comb.logging.PropertyConfigurator();
  85. *
  86. * propertyConfigurator.configure("/location/of/combLogger.json");
  87. *
  88. * //or
  89. *
  90. * var config = {
  91. * "my.logger" : {
  92. * level : "INFO",
  93. * appenders : [
  94. * {
  95. * //default file appender
  96. * type : "FileAppender",
  97. * file : "/var/log/myApp.log",
  98. * },
  99. * {
  100. * //default JSON appender
  101. * type : "JSONAppender",
  102. * file : "/var/log/myApp.JSON",
  103. * },
  104. * {
  105. * type : "FileAppender",
  106. * //override default patter
  107. * pattern : "{[EEEE, MMMM dd, yyyy h:m a]timeStamp} {[5]level}"
  108. * + " {[- 5]levelName} {[-20]name} : {message}",
  109. * //location of my log file
  110. * file : "/var/log/myApp-errors.log",
  111. * //override name so it will get added to the log
  112. * name : "errorFileAppender",
  113. * //overwrite each time
  114. * overwrite : true,
  115. * //explicity set the appender to only accept errors
  116. * level : "ERROR"
  117. * },
  118. * {
  119. * type : "JSONAppender",
  120. * file : "/var/log/myApp-error.json",
  121. * //explicity set the appender to only accept errors
  122. * level : "ERROR"
  123. * }
  124. * ]
  125. * }
  126. * //repeat for more loggers
  127. *
  128. * propertyConfigurator.configure(config);
  129. * }
  130. *
  131. * @name PropertyConfigurator
  132. * @augments comb.logging.BasicConfigurator
  133. * @memberOf comb.logging
  134. * @ignoreCode
  135. *
  136. */
  137. 1exports.PropertyConfigurator = define(BasicConfigurator, {
  138. instance:{
  139. /**@lends comb.logging.PropertyConfigurator.prototype*/
  140. /**
  141. * Call to configure logging
  142. *
  143. * @example
  144. *
  145. * //Example configuration
  146. * {
  147. * "my.logger" : {
  148. * level : "INFO",
  149. * appenders : [
  150. * {
  151. * //default file appender
  152. * type : "FileAppender",
  153. * file : "/var/log/myApp.log",
  154. * },
  155. * {
  156. * //default JSON appender
  157. * type : "JSONAppender",
  158. * file : "/var/log/myApp.JSON",
  159. * },
  160. * {
  161. * type : "FileAppender",
  162. * //override default patter
  163. * pattern : "{[EEEE, MMMM dd, yyyy h:m a]timeStamp} {[5]level}"
  164. * + " {[- 5]levelName} {[-20]name} : {message}",
  165. * //location of my log file
  166. * file : "/var/log/myApp-errors.log",
  167. * //override name so it will get added to the log
  168. * name : "errorFileAppender",
  169. * //overwrite each time
  170. * overwrite : true,
  171. * //explicity set the appender to only accept errors
  172. * level : "ERROR"
  173. * },
  174. * {
  175. * type : "JSONAppender",
  176. * file : "/var/log/myApp-error.json",
  177. * //explicity set the appender to only accept errors
  178. * level : "ERROR"
  179. * }
  180. * ]
  181. * }
  182. *
  183. * @param {Object|String} properties Object containing configuration or string containing a file name with the configuration.
  184. */
  185. configure:function (properties) {
  186. 6 var rootLogger = Logger.getRootLogger();
  187. 6 rootLogger.removeAllAppenders();
  188. 6 if (base.isHash(properties)) {
  189. 2 parseProperties(base.deepMerge({}, properties));
  190. } else {
  191. 4 fs.readFile(properties, function (err, res) {
  192. 4 if (err) {
  193. 1 throw err;
  194. } else {
  195. 3 try {
  196. 3 parseProperties(JSON.parse(res));
  197. } catch (e) {
  198. 1 throw e;
  199. }
  200. }
  201. });
  202. }
  203. }
  204. }
  205. });
logging/index.js
Coverage100.00 SLOC662 LOC162 Missed0
  1. 1var define = require("../define.js"),
  2. base = require("../base"),
  3. isString = base.isString,
  4. merge = base.merge,
  5. isUndefinedOrNull = base.isUndefinedOrNull,
  6. isHash = base.isHash,
  7. isInstanceOf = base.isInstanceOf,
  8. argsToArray = base.argsToArray,
  9. format = base.string.format,
  10. Level = require("./level"),
  11. appenders = require("./appenders"),
  12. Appender = appenders.Appender,
  13. configurators = require("./config");
  14. 1var rootTree;
  15. 1var LoggerTree = define.define(null, {
  16. instance:{
  17. constructor:function (root) {
  18. 14 this.__root = root;
  19. 14 this.__name = root.name;
  20. 14 this.__level = root.level;
  21. 14 this.__parent = root._parent;
  22. 14 this.__map = {};
  23. },
  24. __getSubLoggers:function () {
  25. 6 var map = this.__map, ret = [], n;
  26. 6 for (var i in map) {
  27. 2 n = map[i];
  28. 2 if (n) {
  29. 2 ret = ret.concat(n.tree.getCurrentLoggers());
  30. }
  31. }
  32. 6 return ret;
  33. },
  34. __getLoggers:function () {
  35. 4 return [this.__root].concat(this.__getSubLoggers())
  36. },
  37. getCurrentLoggers:function () {
  38. 4 return this.__getLoggers();
  39. },
  40. getSubLoggers:function () {
  41. 2 return this.__getSubLoggers();
  42. },
  43. getLogger:function (name) {
  44. 31 var ret;
  45. 31 if (name) {
  46. 29 var parts = name.split(".");
  47. 29 if (parts.length) {
  48. 29 var category = parts.shift();
  49. 29 var lNode = this.__map[category];
  50. 29 if (!lNode) {
  51. 13 lNode = this.__map[category] = new Logger(category, this);
  52. 13 lNode.addAppenders(this.__root.appenders);
  53. }
  54. 29 ret = lNode;
  55. 29 if (parts.length) {
  56. //keep searching
  57. 10 name = parts.join(".");
  58. 10 ret = lNode.tree.getLogger(name);
  59. }
  60. }
  61. } else {
  62. 2 ret = this.__root;
  63. }
  64. 31 return ret;
  65. },
  66. getRootLogger:function () {
  67. 23 return this.__root;
  68. },
  69. isDisabled:function (level) {
  70. },
  71. resetConfiguration:function () {
  72. },
  73. /**
  74. * level = string|Level
  75. */
  76. addAppender:function (appender) {
  77. 85 var map = this.__map;
  78. 85 for (var i in map) {
  79. 61 map[i].addAppender(appender);
  80. }
  81. },
  82. removeAppender:function (name) {
  83. 68 var map = this.__map;
  84. 68 for (var i in map) {
  85. 59 map[i].removeAppender(name);
  86. }
  87. },
  88. setters:{
  89. level:function (level) {
  90. 45 this.__level = level;
  91. 45 if (level && level instanceof Level) {
  92. 45 var map = this.__map;
  93. 45 for (var i in map) {
  94. 2 map[i].level = level;
  95. }
  96. }
  97. }
  98. },
  99. getters:{
  100. categories:function () {
  101. 2 return this.getCurrentLoggers().map(function (l) {
  102. 3 return l.fullName;
  103. });
  104. },
  105. name:function () {
  106. 35 var ret = this.__name;
  107. 35 if (this.__parent) {
  108. 21 var pName = this.__parent.name;
  109. 21 if (pName) {
  110. 8 ret = pName + "." + ret;
  111. }
  112. }
  113. 35 return ret;
  114. },
  115. level:function () {
  116. 13 return this.__level;
  117. },
  118. additive:function () {
  119. 13 return this.__root.additive;
  120. }
  121. }
  122. }
  123. });
  124. 1var comb = exports;
  125. /**
  126. * @ignore
  127. * @namespace logging package*/
  128. 1comb.logging = merge({
  129. Level:Level
  130. }, configurators);
  131. /**
  132. * @ignore
  133. * @namespace appenders for logging*/
  134. 1comb.logging.appenders = appenders;
  135. 1var logging = comb.logging;
  136. /**
  137. * @class This class is the entry point for all logging actions in comb.
  138. * <p><b>Logger should be retrieved by calling Logger.getLogger() NOT through the new keyword</b><p>
  139. * <p>
  140. * All loggers in comb follow a heirarchy of inheritance based on a dot notation.
  141. * <pre class="code">
  142. * rootLogger - ""
  143. * / \
  144. * "my" "myOther"
  145. * / \
  146. * "my.logger" "myOther.logger"
  147. * / \
  148. * "my.logger.Log" "myOther.logger.Log"
  149. *
  150. * </pre>
  151. * In the above Tree the rootLogger is the base for all logger. my and myOther inherit from rootLogger
  152. * my.logger inherits from my, and myOther.logger inherits from myOther. The logs do not have to be retrieved in
  153. * order. If I set rootLogger to ERROR level and added a console appender to it the appender and level will be
  154. * added to all logs. However if I set my to INFO level and add a fileAppender to it the level and appender will
  155. * only be added to logs in "my" subtree. If you set my.logger to not be additive then levels, and appenders will not
  156. * propogate down to the rest of the tree.
  157. *
  158. * </p>
  159. *
  160. * <p>For information on levels see {@link comb.logging.Level}.</p>
  161. * <p>For information on appenders see
  162. * <ul>
  163. * <li>{@link comb.logging.appenders.Appender}</li>
  164. * <li>{@link comb.logging.appenders.ConsoleAppender}</li>
  165. * <li>{@link comb.logging.appenders.FileAppender}</li>
  166. * <li>{@link comb.logging.appenders.JSONAppender}</li>
  167. * <li>{@link comb.logging.appenders.RollingFileAppender}</li>
  168. * </ul>
  169. * </p>
  170. * <p>For information on configurators see {@link comb.logging.BasicConfigurator} or {@link comb.logging.PropertyConfigurator}.</p>
  171. *
  172. * @example
  173. *
  174. * var logger = comb.logger;
  175. *
  176. * //configure you logging environement
  177. * logger.configure();
  178. *
  179. * //add a file appender to all loggers
  180. * logger.configure(logger.appender("FileAppender", {file : "/var/log/myLog.log"});
  181. *
  182. * //Retreiving a logger.
  183. * var combLogger = logger("comb");
  184. * var combCollectionLogger = logger("comb.collections");
  185. * var treeLogger = logger("comb.collections.Tree")
  186. * //add a JSON appender to tree logger just for fun!
  187. * .addAppender("JSONAppender", {file : "/var/log/myTreeLogger.json"})
  188. *
  189. * //set my treeLogger to DEBUG Level
  190. * treeLogger.level = "DEBUG";
  191. *
  192. *
  193. * @name Logger
  194. * @memberOf comb.logging
  195. *
  196. * @property {Array<comb.logging.Logger>} subLoggers all loggers this logger is the parent of.
  197. * @property {comb.logging.Level} level the level of this Logger
  198. * @property {Boolean} additive set to false to prevent changes to this logger from propogating down.
  199. * @property {Boolean} isDebug true if this Loggers level is DEBUG
  200. * @property {Boolean} isTrace true if this Loggers level is TRACE
  201. * @property {Boolean} isInfo true if this Loggers level is INFO
  202. * @property {Boolean} isWarn true if this Loggers level is WARN
  203. * @property {Boolean} isError true if this Loggers level is ERROR
  204. * @property {Boolean} isFatal true if this Loggers level is FATAL
  205. * @property {Boolean} isOff true if this Loggers level is OFF
  206. * @property {String} name the name of this logger this <b>does not</b> include the dot notated name
  207. * @property {String} fullName the full path name of this Logger.
  208. * @property {comb.logging.appenders.Appender} appenders list of appenders this logger currently contains.
  209. * @ignoreCode
  210. */
  211. 1var Logger = (logging.Logger = define.define(null, {
  212. instance:{
  213. /**@lends comb.logging.Logger.prototype*/
  214. constructor:function (name, parent) {
  215. 14 this.__additive = true;
  216. 14 this.__name = name;
  217. 14 this._parent = parent;
  218. 14 this._tree = new LoggerTree(this);
  219. 14 this.fullName = this._tree.name;
  220. 14 if (!parent || !parent.additive) {
  221. 1 this.level = Level.ALL;
  222. } else {
  223. 13 this.level = parent.level;
  224. }
  225. 14 this.__appenders = {};
  226. },
  227. /**
  228. * Log an info level message
  229. *
  230. * @param {String} message the message to log.
  231. *
  232. * @return {comb.logging.Logger} for chaining.
  233. */
  234. info:function (message) {
  235. 17 return this.log.apply(this, [Level.INFO].concat(argsToArray(arguments)));
  236. },
  237. /**
  238. * Log an debug level message
  239. *
  240. * @param {String} message the message to log.
  241. *
  242. * @return {comb.logging.Logger} for chaining.
  243. */
  244. debug:function (message) {
  245. 14 return this.log.apply(this, [Level.DEBUG].concat(argsToArray(arguments)));
  246. },
  247. /**
  248. * Log an error level message
  249. *
  250. * @param {String} message the message to log.
  251. *
  252. * @return {comb.logging.Logger} for chaining.
  253. */
  254. error:function (message) {
  255. 15 return this.log.apply(this, [Level.ERROR].concat(argsToArray(arguments)));
  256. },
  257. /**
  258. * Log an warn level message
  259. *
  260. * @param {String} message the message to log.
  261. *
  262. * @return {comb.logging.Logger} for chaining.
  263. */
  264. warn:function (message) {
  265. 14 return this.log.apply(this, [Level.WARN].concat(argsToArray(arguments)));
  266. },
  267. /**
  268. * Log an trace level message
  269. *
  270. * @param {String} message the message to log.
  271. *
  272. * @return {comb.logging.Logger} for chaining.
  273. */
  274. trace:function (message) {
  275. 14 return this.log.apply(this, [Level.TRACE].concat(argsToArray(arguments)));
  276. },
  277. /**
  278. * Log an fatal level message
  279. *
  280. * @param {String} message the message to log.
  281. *
  282. * @return {comb.logging.Logger} for chaining.
  283. */
  284. fatal:function (message) {
  285. 14 return this.log.apply(this, [Level.FATAL].concat(argsToArray(arguments)));
  286. },
  287. /**
  288. * Log a message
  289. *
  290. * @param {comb.logging.Level} level the level the message is
  291. * @param {String} message the message to log.
  292. *
  293. * @return {comb.logging.Logger} for chaining.
  294. */
  295. log:function (level, message) {
  296. 94 var args = argsToArray(arguments, 1);
  297. 94 level = Level.toLevel(level);
  298. 94 if (args.length > 1) {
  299. 18 message = format.apply(null, args);
  300. }
  301. 94 if (level.isGreaterOrEqualToo(this.level)) {
  302. 64 if (Level.TRACE.equals(level)) {
  303. 10 var err = new Error;
  304. 10 err.name = "Trace";
  305. 10 err.message = message || '';
  306. 10 Error.captureStackTrace(err, arguments.callee);
  307. 10 message = err.stack;
  308. 54 } else if (Level.ERROR.equals(level) && isInstanceOf(message, Error)) {
  309. 1 message = message.stack;
  310. }
  311. 64 var type = level.name.toLowerCase(), appenders = this.__appenders;
  312. 64 var event = {
  313. level:level,
  314. levelName:level.name,
  315. message:message,
  316. timeStamp:new Date(),
  317. name:this.fullName
  318. };
  319. 64 Object.keys(appenders).forEach(function (i) {
  320. 64 appenders[i].append(event);
  321. });
  322. }
  323. 94 return this;
  324. },
  325. /**
  326. * Add an appender to this logger. If this is additive then the appender is added to all subloggers.
  327. *
  328. * @example
  329. * comb.logger("my.logger")
  330. * .addAppender("ConsoleAppender")
  331. * .addAppender("FileAppender", {file:'/var/log/my.log'})
  332. * .addAppender("RollingFileAppender", {file:'/var/log/myRolling.log'})
  333. * .addAppender("JSONAppender", {file:'/var/log/myJson.log'});
  334. *
  335. * @param {comb.logging.Appender|String} If the appender is an {@link comb.logging.appenders.Appender} then it is added.
  336. * If the appender is a string then {@link comb.logging.appenders.Appender.createAppender} will be called to create it.
  337. *
  338. * @param {Object} [opts = null] If the first argument is a string then the opts will be used as constructor arguments for
  339. * creating the appender.
  340. *
  341. * @return {comb.logging.Logger} for chaining.
  342. */
  343. addAppender:function (appender, opts) {
  344. 104 var args = argsToArray(arguments);
  345. 104 if (isString(appender)) {
  346. 12 this.addAppender(Appender.createAppender(appender, opts));
  347. } else {
  348. 92 if (!isUndefinedOrNull(appender)) {
  349. 92 var name = appender.name;
  350. 92 if (!(name in this.__appenders)) {
  351. 85 this.__appenders[name] = appender;
  352. 85 if (!appender.level) {
  353. 17 appender.level = this.level;
  354. }
  355. 85 this._tree.addAppender(appender);
  356. }
  357. }
  358. }
  359. 104 return this;
  360. },
  361. /**
  362. * Short cut to add a list of appenders to this Logger
  363. * @param {Array<comb.logging.Appender>} appenders
  364. *
  365. * @return {comb.logging.Logger} for chaining.
  366. */
  367. addAppenders:function (appenders) {
  368. 14 appenders.forEach(this.addAppender.bind(this));
  369. 14 return this;
  370. },
  371. /**
  372. * Removes and appender from this logger.
  373. * @param {String} name the name of the appender
  374. * @return {comb.logging.Logger} for chaining.
  375. */
  376. removeAppender:function (name) {
  377. 68 if (name in this.__appenders) {
  378. 68 delete this.__appenders[name];
  379. 68 this._tree.removeAppender(name);
  380. }
  381. 68 return this;
  382. },
  383. /**
  384. * Removes a list of appenders from this logger.
  385. *
  386. * @param {String[]} appenders a list of names of appenders to remove
  387. * @return {comb.logging.Logger} for chaining.
  388. */
  389. removeAppenders:function (appenders) {
  390. 1 appenders.forEach(this.removeAppender, this);
  391. 1 return this;
  392. },
  393. /**
  394. * Removes all appenders from this logger and sub loggers if this Logger is additive.
  395. *
  396. * @return {comb.logging.Logger} for chaining.
  397. */
  398. removeAllAppenders:function () {
  399. 16 Object.keys(this.__appenders).forEach(this.removeAppender.bind(this));
  400. 16 return this;
  401. },
  402. /**
  403. * Determines if an appender is attached.
  404. *
  405. * @param {String} name the name of the appender.
  406. */
  407. isAppenderAttached:function (name) {
  408. 38 return (name in this.__appenders);
  409. },
  410. /**
  411. * Gets an appender from this logger
  412. *
  413. * @param {String} name the name of the appender.
  414. *
  415. * @return {comb.logging.Appender|undefined} returns the appender with the specified name or
  416. * undefined if it is not found.
  417. */
  418. getAppender:function (name) {
  419. 8 var ret;
  420. 8 if (name in this.__appenders) {
  421. 8 ret = this.__appenders[name];
  422. }
  423. 8 return ret;
  424. },
  425. /**
  426. * @ignore
  427. * */
  428. setters:{
  429. level:function (level) {
  430. 46 level = Level.toLevel(level);
  431. 46 if (this.__additive) {
  432. 45 this.__level = level;
  433. 45 var appenders = this.__appenders;
  434. 45 for (var i in appenders) {
  435. 35 appenders[i].level = level;
  436. }
  437. 45 this._tree.level = level;
  438. } else {
  439. 1 this.__level = level;
  440. }
  441. },
  442. additive:function (additive) {
  443. 2 this.__additive = additive;
  444. }
  445. },
  446. /**@ignore*/
  447. getters:{
  448. /**@ignore*/
  449. /**@ignore*/
  450. subLoggers:function () {
  451. 2 return this._tree.getSubLoggers();
  452. },
  453. /**@ignore*/
  454. level:function () {
  455. 202 return this.__level;
  456. },
  457. /**@ignore*/
  458. additive:function () {
  459. 15 return this.__additive;
  460. },
  461. isAll:function () {
  462. 8 return Level.ALL.isGreaterOrEqualToo(this.level);
  463. },
  464. /**@ignore*/
  465. isDebug:function () {
  466. 8 return Level.DEBUG.isGreaterOrEqualToo(this.level);
  467. },
  468. /**@ignore*/
  469. isTrace:function () {
  470. 8 return Level.TRACE.isGreaterOrEqualToo(this.level);
  471. },
  472. /**@ignore*/
  473. isInfo:function () {
  474. 8 return Level.INFO.isGreaterOrEqualToo(this.level);
  475. },
  476. /**@ignore*/
  477. isWarn:function () {
  478. 8 return Level.WARN.isGreaterOrEqualToo(this.level);
  479. },
  480. /**@ignore*/
  481. isError:function () {
  482. 8 return Level.ERROR.isGreaterOrEqualToo(this.level);
  483. },
  484. /**@ignore*/
  485. isFatal:function () {
  486. 8 return Level.FATAL.isGreaterOrEqualToo(this.level);
  487. },
  488. /**@ignore*/
  489. isOff:function () {
  490. 9 return Level.OFF.equals(this.level);
  491. },
  492. /**@ignore*/
  493. name:function () {
  494. 14 return this.__name;
  495. },
  496. /**@ignore*/
  497. tree:function () {
  498. 13 return this._tree;
  499. },
  500. /**@ignore*/
  501. appenders:function () {
  502. 20 var ret = [];
  503. 20 for (var i in this.__appenders) {
  504. 9 ret.push(this.__appenders[i]);
  505. }
  506. 20 return ret;
  507. },
  508. categories:function () {
  509. 2 return this._tree.categories;
  510. }
  511. }
  512. },
  513. static:{
  514. /**@lends comb.logging.Logger*/
  515. /**
  516. * Return the root of all loggers
  517. */
  518. getRootLogger:function () {
  519. 23 return rootTree.getRootLogger();
  520. },
  521. /**
  522. * Retrieves/Creates a logger based on the name passed in
  523. *
  524. * @param {String} name the name of the logger
  525. */
  526. getLogger:function (name) {
  527. 21 return rootTree.getLogger(name);
  528. }
  529. }
  530. }));
  531. /**
  532. *
  533. * @function
  534. * @description Alias to {@link comb.logging.Logger.getLogger}. See {@link comb.logging.Logger} for more information.
  535. *
  536. * @example
  537. *
  538. * comb.logger("my.logger");
  539. *
  540. * @memberOf comb
  541. * @namespace
  542. */
  543. 1exports.logger = Logger.getLogger.bind(Logger);
  544. /**
  545. * @function
  546. * @description Shortcut to configure loggers.
  547. *
  548. * @example
  549. *
  550. * //same as new comb.logging.BasicConfigurator().configure();
  551. * comb.logger.configure();
  552. *
  553. * //new comb.logging.PropertyConfigurator().configure("/location/to/logger/configuration.json");
  554. * comb.logger.configure("/location/to/logger/configuration.json");
  555. *
  556. * @memberOf comb.logger
  557. */
  558. 1exports.logger.configure = function configure() {
  559. 4 var args = argsToArray(arguments), configurator;
  560. 4 if (!args.length) {
  561. 1 configurator = new configurators.BasicConfigurator();
  562. } else {
  563. 3 var first = args[0];
  564. 3 if (isHash(first) || isString(first)) {
  565. 2 configurator = new configurators.PropertyConfigurator();
  566. } else {
  567. 1 configurator = new configurators.BasicConfigurator();
  568. }
  569. }
  570. 4 configurator.configure.apply(configurator, args);
  571. 4 return this;
  572. };
  573. /**
  574. * @function
  575. * @description Factory method for creating appenders. See {@link comb.logging.appenders.Appender.createAppender} for
  576. * arguments.
  577. *
  578. * @example
  579. *
  580. * var appender = comb.logger.appender("ConsoleAppender");
  581. * comb.logger("my.logger").addAppender(appender);
  582. *
  583. * @memberOf comb.logger
  584. *
  585. */
  586. 1exports.logger.appender = Appender.createAppender.bind(Appender);
  587. 1var rootLogger = new Logger("");
  588. 1rootTree = rootLogger._tree;
  589. /**
  590. * The root for all loggers.
  591. * @memberOf comb.logger
  592. * @type comb.logging.Logger
  593. */
  594. 1exports.logger.rootLogger = rootLogger;
logging/level.js
Coverage100.00 SLOC188 LOC33 Missed0
  1. 1var define = require("../define.js").define, base = require("../base");
  2. 1var LEVELS = {
  3. ALL:-100000,
  4. DEBUG:1,
  5. TRACE:2,
  6. INFO:3,
  7. WARN:4,
  8. ERROR:5,
  9. FATAL:6,
  10. OFF:100000
  11. };
  12. 1var LEVELS_REVERSE = {
  13. "-100000":"ALL",
  14. "1":"DEBUG",
  15. "2":"TRACE",
  16. "3":"INFO",
  17. "4":"WARN",
  18. "5":"ERROR",
  19. "6":"FATAL",
  20. "100000":"OFF"
  21. };
  22. /**
  23. * @class Level class used to describe logging levels. The levels determine what types of events are logged to the appenders
  24. * for example the if Level.ALL is used then all event will be logged, however if Level.INFO was used then <b>ONLY</b>
  25. * INFO, WARN, ERROR, and FATAL events will be logged. To turn off logging for a logger use Level.OFF.
  26. *
  27. * <p><b>Not typically instantiated directly, but through staticly defined levels</b></p>
  28. * @example
  29. * //Levels in ascending order
  30. * comb.logging.Level.ALL
  31. * comb.logging.Level.DEBUG
  32. * comb.logging.Level.TRACE
  33. * comb.logging.Level.INFO
  34. * comb.logging.Level.WARN
  35. * comb.logging.Level.ERROR
  36. * comb.logging.Level.FATAL
  37. * comb.logging.Level.OFF
  38. *
  39. * //or
  40. * Level.getLevel("INFO");
  41. *
  42. * @name Level
  43. * @memberOf comb.logging
  44. *
  45. * @property {Number} level the numerical representation of this level.
  46. * @property {String} name the name of level.
  47. * @ignoreCode
  48. */
  49. 1var Level = (exports = module.exports = define(null, {
  50. instance:{
  51. /**@lends comb.logging.Level.prototype*/
  52. constructor:function (level, name) {
  53. 9 this.level = level;
  54. 9 this.name = name;
  55. },
  56. /**
  57. * Determing if this level is >= another level
  58. * @param {comb.logging.Level} level the level to test against
  59. *
  60. * @returns {Boolean} true if this is >= false otherwise.
  61. */
  62. isGreaterOrEqualToo:function (level) {
  63. 290 var ret = false;
  64. 290 if (level && base.isNumber(level.level)) {
  65. 290 if (this.level >= level.level) {
  66. 189 ret = true;
  67. }
  68. }
  69. 290 return ret;
  70. },
  71. /**
  72. * Determing if this level is equal to another level based off of the numerical rank.
  73. *
  74. * @param {comb.logging.Level} level the level to compare
  75. *
  76. * @returns {Boolean} true if this is equal to that false otherwise.
  77. */
  78. equals:function (level) {
  79. 148 return level.level == this.level;
  80. }
  81. },
  82. static:{
  83. /**@lends comb.logging.Level*/
  84. /**
  85. * Converts a numerical or string representation of a level, if a default level is provided,
  86. * then if a level cannot be determined then the default level is used.
  87. *
  88. * @param {Number|String|comb.logging.Level} level the level to try to convert
  89. * @param {comb.logging.Level} [defaultLevel] default level to use if one cannot be determined,
  90. *
  91. * @returns {comb.logging.Level|null} returns a level if one can be determined null otherwise.
  92. */
  93. toLevel:function (level, defaultLevel) {
  94. 194 var ret = null;
  95. 194 var args = base.argsToArray(arguments);
  96. 194 if (args.length === 1) {
  97. 192 var level = args[0];
  98. 192 if (base.isNumber(level)) {
  99. 11 var strLevel = LEVELS_REVERSE[level];
  100. 11 ret = Level[strLevel];
  101. 181 } else if (base.isString(level)) {
  102. 48 ret = Level[level.toUpperCase()];
  103. } else {
  104. 133 ret = level;
  105. }
  106. } else {
  107. 2 ret = (Level.toLevel(args[0]) || args[1]);
  108. }
  109. 194 return ret;
  110. },
  111. /**
  112. * Adds a new level to the Level object.
  113. *
  114. * @example
  115. *
  116. * logger = Logger.getLogger("my.logger");
  117. *
  118. * //create the custom level
  119. * Level.addLevel("custom_Level", 20);
  120. *
  121. * //now set the level on a logger
  122. * logger.level = Level.CUSTOM_LEVEL;
  123. *
  124. * @param {string} label the label of the level, <b>Note:</b> the label will be coverted to uppercase.
  125. * @param {number} level the level of the level
  126. *
  127. * @return {undefined|comb.logging.Level} the level that was created.
  128. *
  129. */
  130. addLevel:function (label, level) {
  131. 1 var ret;
  132. 1 if (base.isString(label) && base.isNumber(level)) {
  133. 1 label = label.toUpperCase();
  134. 1 LEVELS_REVERSE[level] = label;
  135. 1 LEVELS[label] = level;
  136. 1 ret = (this[label] = new Level(level, label));
  137. }
  138. 1 return ret;
  139. },
  140. /**
  141. * Level to allow logging of all events.
  142. */
  143. ALL:null,
  144. /**
  145. * Logs only events debug or greater.
  146. */
  147. DEBUG:null,
  148. /**
  149. * Like debug but provides a finer level of detail
  150. */
  151. TRACE:null,
  152. /**
  153. * Only info, or error related events
  154. */
  155. INFO:null,
  156. /**
  157. * Only warn or error related events
  158. */
  159. WARN:null,
  160. /**
  161. * Error or fatal events
  162. */
  163. ERROR:null,
  164. /**
  165. * Only fatal events
  166. */
  167. FATAL:null,
  168. /**
  169. * No events will be logged.
  170. */
  171. OFF:null
  172. }
  173. }));
  174. 1for (var i in LEVELS_REVERSE) {
  175. 8 Level[LEVELS_REVERSE[i]] = new Level(parseInt(i, 10), LEVELS_REVERSE[i]);
  176. }
plugins/Broadcaster.js
Coverage100.00 SLOC113 LOC26 Missed0
  1. 1var func = require("../base/functions"),
  2. define = require("../define").define;
  3. 1var Broadcaster = define(null, {
  4. instance : {
  5. /** @lends comb.plugins.Broadcaster.prototype */
  6. /**
  7. * Plugin to allow a class to easily broadcast events
  8. *
  9. * @example
  10. *
  11. * var Mammal = define(comb.plugins.Broadcaster, {
  12. * instance : {
  13. *
  14. * constructor: function(options) {
  15. * options = options || {};
  16. * this._super(arguments);
  17. * this._type = options.type || "mammal";
  18. * },
  19. *
  20. * speak : function() {
  21. * var str = "A mammal of type " + this._type + " sounds like";
  22. * this.broadcast("speak", str);
  23. * this.onSpeak(str);
  24. * return str;
  25. * },
  26. *
  27. * onSpeak : function(){}
  28. * }
  29. * });
  30. *
  31. *
  32. * var m = new Mammal({color : "gold"});
  33. * m.listen("speak", function(str){
  34. * //called back from the broadcast event
  35. * console.log(str);
  36. * });
  37. * m.speak();
  38. *
  39. * @constructs
  40. */
  41. constructor : function() {
  42. 2 this.__listeners = {};
  43. },
  44. /**
  45. * Broadcasts an event from an object
  46. *
  47. * @param name the name of the event to broadcast
  48. * @param {Object|String|Function|Date|Number} [args] variable number of arguments to pass to listeners, can be anything
  49. */
  50. broadcast : function(topic, args) {
  51. 2 var args = Array.prototype.slice.call(arguments, 0), topic = args.shift();
  52. 2 if (topic && topic in this.__listeners) {
  53. 2 var list = this.__listeners[topic], i = list.length - 1;
  54. 2 while (i >= 0) {
  55. 2 list[i--].cb.apply(this, args);
  56. }
  57. }
  58. },
  59. /**
  60. * Listens to a broadcasted event
  61. * Simimlar to {@link comb.listen}
  62. *
  63. * @param {String} topic the topic to listen to
  64. * @param {Function} callback the function to callback on event publish
  65. *
  66. * @returns {Array} handle to disconnect a topic
  67. */
  68. listen : function(topic, callback) {
  69. 3 if (!func.isFunction(callback)) throw new Error("callback must be a function");
  70. 3 var handle = {
  71. topic : topic,
  72. cb : callback
  73. };
  74. 3 var list = this.__listeners[topic];
  75. 3 if (!list) {
  76. 2 list = (this.__listeners[topic] = [handle]);
  77. 2 handle.pos = 0;
  78. } else {
  79. 1 handle.pos = list.push(handle);
  80. }
  81. 3 return handle;
  82. },
  83. /**
  84. * Disconnects a listener
  85. * Similar to {@link comb.unListen}
  86. *
  87. * @param handle disconnect a handle returned from Broadcaster.listen
  88. */
  89. unListen : function(handle) {
  90. 1 if (handle) {
  91. 1 var topic = handle.topic;
  92. 1 if (topic in this.__listeners) {
  93. 1 var listeners = this.__listeners, list = listeners[topic];
  94. 1 if (list) {
  95. 1 for (var i = list.length - 1; i >= 0; i--) {
  96. 1 if (list[i] == handle) {
  97. 1 list.splice(i, 1);
  98. 1 break;
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }
  105. }
  106. });
  107. 1exports = module.exports = Broadcaster;
plugins/Middleware.js
Coverage100.00 SLOC212 LOC40 Missed0
  1. 1var func = require("../base/functions"),
  2. obj = require("../base/object"),
  3. Promise = require("../promise").Promise,
  4. define = require("../define").define;
  5. 1var Middleware = define(null, {
  6. instance:{
  7. /** @lends comb.plugins.Middleware.prototype */
  8. __hooks:{pre:{}, post:{}},
  9. /**
  10. * @class Plugin to enable middleware on a class
  11. *
  12. * @example
  13. *
  14. * var Mammal = define(comb.plugins.Middleware, {
  15. * instance : {
  16. *
  17. * constructor: function(options) {
  18. * options = options || {};
  19. * this.super(arguments);
  20. * this._type = options.type || "mammal";
  21. * },
  22. *
  23. * speak : function() {
  24. * var ret = new comb.Promise();
  25. * this._hook("pre", "speak")
  26. * .then(comb.hitch(this, "_hook", "post", "speak"), hitch(ret, "errback"))
  27. * .then(comb.hitch(ret, "callback"), comb.hitch(ret, "errback"));
  28. * return ret;
  29. * }
  30. * }
  31. *});
  32. *
  33. * Mammal.pre('speak', function(next){
  34. * //do something meaningful
  35. * next();
  36. * });
  37. * var m = new Mammal({color : "gold"});
  38. * m.speak();
  39. *
  40. * @constructs
  41. */
  42. constructor:function () {
  43. 6 this.__hooks = obj.merge({}, this.__hooks);
  44. 6 this._super(arguments);
  45. },
  46. /**
  47. * <p>Protected!</p>
  48. *
  49. * <p>Call to initiate middleware for the topic</p>
  50. * <p><b>NOTE:</b> this function takes a variable number of arguments
  51. * whatever comes after the op param will be passed into
  52. * the listening function, with the last argument to the listenting
  53. * function being the next function</p>
  54. *
  55. *
  56. * @public
  57. * @param {"pre"|"post"} state the state in which the hook should be called
  58. * @param {String} op the operation that is being acted upong
  59. * @param args arguments to be passed into the listening functions.
  60. * @returns {comb.Promise} a promise to use after middleware chain completes
  61. *
  62. */
  63. _hook:function (state, op, args) {
  64. 11 args = args || [];
  65. 11 var promise = new Promise();
  66. 11 var funcs, length;
  67. 11 if (this.__hooks[state] && (funcs = this.__hooks[state][op]) != null && (length = funcs.length) > 0) {
  68. 7 var count = 0;
  69. 7 var next = func.hitch(this, function (err) {
  70. 15 if (err) {
  71. 1 promise.errback(err);
  72. } else {
  73. 14 process.nextTick(func.hitch(this, function () {
  74. //if Ive looped through all of them callback
  75. 14 if (count == length) {
  76. 6 promise.callback();
  77. } else {
  78. //call next
  79. 8 var nextArgs = args.slice(0);
  80. 8 nextArgs.unshift(next);
  81. 8 funcs[count++].apply(this, nextArgs);
  82. }
  83. }));
  84. }
  85. });
  86. 7 next();
  87. } else {
  88. 4 promise.callback();
  89. }
  90. 11 return promise.promise();
  91. },
  92. /**
  93. * Use to listen to before an event occurred i.e. pre save
  94. *
  95. * <b>NOTE:</b></br>
  96. * <ul>
  97. * <li>You must call next in order for the middleware chain to complete</li>
  98. * <li>This connects to events on the instance of an object, not all instances!</li>
  99. * <li>Hooks are called in the order they are received!</li>
  100. * <li> When connecting your callback will be called in the scope of the class</br>if you want a certain scope use {@link comb.hitch}</li>
  101. * </ul>
  102. *
  103. * @example
  104. * instance.pre("save", function(args,...., next){
  105. * //do something...
  106. * //you have to call next!!!!!
  107. * next();
  108. * });
  109. *
  110. * */
  111. pre:function (fun, callback) {
  112. 1 var hook = this.__hooks.pre[fun];
  113. 1 if (!hook) {
  114. 1 hook = this.__hooks.pre[fun] = [];
  115. }
  116. 1 hook.push(callback);
  117. },
  118. /**
  119. * <p>Use to listen to after an event has occurred i.e. post save</p>
  120. * <b>NOTE:</b></br>
  121. * <ul>
  122. * <li>You must call next in order for the middleware chain to complete</li>
  123. * <li>This connects to events on the instance of an object, NOT all instances!</li>
  124. * <li>Hooks are called in the order they are received!</li>
  125. * <li>When connecting your callback will be called in the scope of the class</br>if you want a certain scope use {@link comb.hitch}</li>
  126. * </ul>
  127. * @example
  128. *
  129. * instance.post("save", function(next){
  130. * //do something...
  131. * //you have to call next!!!!!
  132. * next();
  133. * });
  134. * */
  135. post:function (fun, callback) {
  136. 1 var hook = this.__hooks.post[fun];
  137. //if I havent initialized it create it;
  138. 1 if (hook == undefined) {
  139. 1 hook = this.__hooks.post[fun] = [];
  140. }
  141. 1 hook.push(callback);
  142. }
  143. },
  144. static:{
  145. /** @lends comb.plugins.Middleware */
  146. /**
  147. *<p> Use to listen to after an event has occurred i.e. post save</p>
  148. *
  149. * <b>NOTE:</b></br>
  150. * <ul>
  151. * <li>You must call next in order for the middleware chain to complete</li>
  152. * <li>This connects to events on ALL instances of an object</li>
  153. * <li>Hooks are called in the order they are received!</li>
  154. * <li>When connecting your callback will be called in the scope of the class</br>if you want a certain scope use {@link comb.hitch}</li>
  155. * </ul>
  156. *
  157. * @example
  158. * Class.pre("save", function(next){
  159. * ...
  160. * //you must call next
  161. * });
  162. * */
  163. pre:function (name, cb) {
  164. 2 var hooks = this.prototype.__hooks;
  165. 2 var hook = hooks.pre[name];
  166. 2 if (!hook) {
  167. 1 hook = hooks.pre[name] = [];
  168. }
  169. 2 hook.push(cb);
  170. },
  171. /**
  172. *<p>Use to listen to after an event has occurred i.e. post save</p>
  173. *
  174. *<b>NOTE:</b></br>
  175. * <ul>
  176. * <li>You must call next in order for the middleware chain to complete</li>
  177. * <li>This connects to events on ALL instances of an object</li>
  178. * <li>Hooks are called in the order they are received!</li>
  179. * <li>When connecting your callback will be called in the scope of the class</br>if you want a certain scope use {@link comb.hitch}</li>
  180. * </ul>
  181. *
  182. * @example
  183. * Class.post("save", function(next){
  184. * ...
  185. * //you must call next
  186. * });
  187. * */
  188. post:function (name, cb) {
  189. 1 var hooks = this.prototype.__hooks;
  190. 1 var hook = hooks.post[name];
  191. 1 if (!hook) {
  192. 1 hook = hooks.post[name] = [];
  193. }
  194. 1 hook.push(cb);
  195. }
  196. }
  197. });
  198. 1module.exports = exports = Middleware;
plugins/index.js
Coverage100.00 SLOC6 LOC2 Missed0
  1. 1var comb = exports;
  2. /**@namespace plugins for classes using {@link comb.define}*/
  3. 1comb.plugins = {
  4. Broadcaster : require("./Broadcaster"),
  5. Middleware : require("./Middleware")
  6. };
promise.js
Coverage100.00 SLOC776 LOC192 Missed0
  1. 1var hitch = require("./base/functions").hitch,
  2. define = require("./define").define,
  3. base = require("./base"),
  4. argsToArray = base.argsToArray,
  5. array = base.array,
  6. forEach = array.forEach,
  7. zip = array.zip,
  8. flatten = array.flatten,
  9. isUndefinedOrNull = base.isUndefinedOrNull,
  10. isArray = base.isArray,
  11. isFunction = base.isFunction,
  12. bindIgnore = base.bindIgnore,
  13. isInstanceOf = base.isInstanceOf;
  14. 1var Promise = define(null, {
  15. instance:{
  16. /** @lends comb.Promise.prototype */
  17. __fired:false,
  18. __results:null,
  19. __error:null,
  20. __errorCbs:null,
  21. __cbs:null,
  22. /**
  23. * Promise object used to provide seperation of success and error resolution paths for async operations.
  24. *
  25. * @example
  26. * var myFunc = function(){
  27. * var promise = new Promise();
  28. * //callback the promise after 10 Secs
  29. * setTimeout(hitch(promise, "callback"), 10000);
  30. * return promise.promise();
  31. * }
  32. * var myFunc2 = function(){
  33. * var promises =[];
  34. * for(var i = 0; i < 10; i++){
  35. * promises.push(myFunc);
  36. * }
  37. * //create a new promise list with all 10 promises
  38. * return new PromiseList(promises).promise();
  39. * }
  40. *
  41. * myFunc.then(function(success){}, function(error){})
  42. * //chain promise operations
  43. * myFunc.chain(myfunc).then(function(success){}, function(error){})
  44. *
  45. * myFunc2.then(function(success){}, function(error){})
  46. * //chain promise operations
  47. * myFunc2.chain(myfunc).then(function(success){}, function(error){})
  48. * @constructs
  49. */
  50. constructor:function () {
  51. 1928 this.__errorCbs = [];
  52. 1928 this.__cbs = [];
  53. },
  54. /**
  55. * @private
  56. */
  57. __resolve:function () {
  58. 1855 if (!this.__fired) {
  59. 1855 this.__fired = true;
  60. 1855 var cbs = this.__error ? this.__errorCbs : this.__cbs,
  61. len = cbs.length, i,
  62. results = this.__error || this.__results;
  63. 1855 for (i = 0; i < len; i++) {
  64. 1248 this.__callNextTick(cbs[i], results);
  65. }
  66. }
  67. },
  68. __callNextTick:function (cb, results) {
  69. 2068 process.nextTick(hitch(this, function () {
  70. 2068 cb.apply(this, results);
  71. }));
  72. },
  73. /**
  74. * Add a callback to the callback chain of the promise
  75. *
  76. *
  77. * @param {Function|comb.Promise} cb the function or promise to callback when the promise is resolved.
  78. *
  79. * @return {comb.Promise} this promise for chaining
  80. */
  81. addCallback:function (cb) {
  82. 2089 if (cb) {
  83. 2089 if (exports.isPromiseLike(cb)) {
  84. 856 cb = hitch(cb, "callback");
  85. }
  86. 2089 if (this.__fired && this.__results) {
  87. 806 this.__callNextTick(cb, this.__results);
  88. } else {
  89. 1283 this.__cbs.push(cb);
  90. }
  91. }
  92. 2089 return this;
  93. },
  94. /**
  95. * Add a callback to the errback chain of the promise
  96. *
  97. * @param {Function|comb.Promise} cb the function or promise to callback when the promise errors
  98. *
  99. * @return {comb.Promise} this promise for chaining
  100. */
  101. addErrback:function (cb) {
  102. 1839 if (cb) {
  103. 1831 if (exports.isPromiseLike(cb)) {
  104. 1572 cb = hitch(cb, "errback");
  105. }
  106. 1831 if (this.__fired && this.__error) {
  107. 14 this.__callNextTick(cb, this.__error);
  108. } else {
  109. 1817 this.__errorCbs.push(cb);
  110. }
  111. }
  112. 1839 return this;
  113. },
  114. /**
  115. *
  116. * Adds a callback or promise to be resolved for both success
  117. * and error.
  118. *
  119. * @param {Function|comb.Promise} cb callback or promise to be resolved for both success
  120. * and error.
  121. * @return {comb.Promise} this promise for chaining
  122. */
  123. both:function (cb) {
  124. 6 this.addCallback(cb);
  125. 6 if (exports.isPromiseLike(cb)) {
  126. 2 this.addErrback(hitch(cb, "callback"));
  127. } else {
  128. 4 this.addErrback(cb);
  129. }
  130. 6 return this;
  131. },
  132. /**
  133. * When called all functions registered as callbacks are called with the passed in results.
  134. *
  135. * @param {*} args variable number of results to pass back to listeners of the promise
  136. */
  137. callback:function (args) {
  138. 1724 args = argsToArray(arguments);
  139. 1724 if (this.__fired) {
  140. 1 throw new Error("Already fired!");
  141. }
  142. 1723 this.__results = args;
  143. 1723 this.__resolve();
  144. 1723 return this.promise();
  145. },
  146. /**
  147. * When called all functions registered as errbacks are called with the passed in error(s)
  148. *
  149. * @param {*} args number of errors to pass back to listeners of the promise
  150. */
  151. errback:function (args) {
  152. 133 if (this.__fired) {
  153. 1 throw args || new Error("Already fired");
  154. }
  155. 132 this.__error = argsToArray(arguments);
  156. 132 this.__resolve();
  157. 132 return this.promise();
  158. },
  159. /**
  160. * Resolved a promise using the node style callback.
  161. *
  162. * @example
  163. *
  164. * var promise = new Promise();
  165. * fs.readFile("file.txt", "utf8", promise.resolve.bind(promise));
  166. * promise.then(function(file){
  167. * console.log(file);
  168. * });
  169. *
  170. * @param {Error} [err=null] If specified then the promise will error out
  171. * @param {...} [args] if err is null then the aruments will be used to resolve the promise.
  172. *
  173. * @return {comb.Promise} for chaining.
  174. */
  175. resolve:function (err, args) {
  176. 2 if (err) {
  177. 1 this.errback(err);
  178. } else {
  179. 1 this.callback.apply(this, argsToArray(arguments, 1));
  180. }
  181. 2 return this;
  182. },
  183. /**
  184. * Call to specify action to take after promise completes or errors
  185. *
  186. * @param {Function} [callback=null] function to call after the promise completes successfully
  187. * @param {Function} [errback=null] function to call if the promise errors
  188. *
  189. * @return {comb.Promise} this promise for chaining
  190. */
  191. then:function (callback, errback) {
  192. 937 if (exports.isPromiseLike(callback)) {
  193. 878 this.addCallback(callback);
  194. 878 this.addErrback(callback);
  195. } else {
  196. 59 this.addCallback(callback);
  197. 59 this.addErrback(errback);
  198. }
  199. 937 return this;
  200. },
  201. /**
  202. * Call this function as a classic node callback where the first argument
  203. * will be an error, or null if no error occured. The other arugments will
  204. * be the result from the promise.
  205. *
  206. * @example
  207. *
  208. * promise.classic(function(err, res){
  209. * if(err){
  210. * console.log(err);
  211. * }else{
  212. * console.log(res);
  213. * }
  214. * });
  215. *
  216. * @param cb callback where the first argument
  217. * will be an error, or null if no error occured. The other arugments will
  218. * be the result from the promise.
  219. * @return {comb.Promise} the promise to chain
  220. */
  221. classic:function (cb) {
  222. 5 if ("function" === typeof cb) {
  223. 5 this.addErrback(function (err) {
  224. 1 cb(err);
  225. });
  226. 5 this.addCallback(function () {
  227. 4 cb.apply(this, [null].concat(argsToArray(arguments)));
  228. });
  229. }
  230. 5 return this;
  231. },
  232. /**
  233. * Call to chaining of promises
  234. *
  235. * ```
  236. * new Promise()
  237. * .callback("hello")
  238. * .chain(function(previousPromiseResults){
  239. * return previousPromiseResults + " world";
  240. * }, errorHandler)
  241. * .chain(function(previousPromiseResults){
  242. * return when(dbCall());
  243. * }).classic(function(err, results){
  244. * //all promises are done
  245. * });
  246. *
  247. * ```
  248. *
  249. * You can also use static values
  250. *
  251. * ```
  252. * new Promise().callback()
  253. * .chain("hello")
  254. * .chain(function(prev){
  255. * return prev + " world!"
  256. * }).then(function(str){
  257. * console.log(str); //"hello world!"
  258. * });
  259. * ```
  260. *
  261. * If you do not provide an `errback` for each chain then it will be propogated to the final promise
  262. *
  263. *
  264. * ```
  265. * new Promise()
  266. * .chain(function(){
  267. * return new comb.Promise().errback(new Error("error"));
  268. * })
  269. * .chain(function(){
  270. * return prev + " world!"
  271. * })
  272. * .classic(function(err, str){
  273. * console.log(err.message); //"error"
  274. * });
  275. * ```
  276. *
  277. *
  278. * @param callback method to call this one completes. If you return a promise the execution will delay until the returned promise has resolved.
  279. * @param [errback=null] method to call if this promise errors. If errback is not specified then the returned promises
  280. * errback method will be used.
  281. *
  282. * @return {comb.Promise} A new that wraps the promise for chaining
  283. */
  284. chain:function (callback, errback) {
  285. 745 var promise = new Promise();
  286. 745 this.addCallback(function () {
  287. 688 var args = arguments;
  288. 688 process.nextTick(function () {
  289. 688 try {
  290. 688 when(isFunction(callback) ? callback.apply(this, args) : callback).then(promise);
  291. } catch (e) {
  292. 9 promise.errback(e);
  293. }
  294. });
  295. });
  296. // If no errback passed, then invoke our promise's errback to pass
  297. // on to next link in the chain.
  298. 745 this.addErrback(errback || promise);
  299. 745 return promise.promise();
  300. },
  301. /**
  302. * Applies the same function that returns a promise to both the callback and errback.
  303. *
  304. * @param {Function} callback function to call. This function must return a Promise
  305. *
  306. * @return {comb.Promise} a promise to continue chaining or to resolve with.
  307. *
  308. */
  309. chainBoth:function (callback) {
  310. 10 var promise = new Promise();
  311. 10 this.addCallback(function () {
  312. 5 try {
  313. 5 when(isFunction(callback) ? callback.apply(this, arguments) : callback).then(promise);
  314. } catch (e) {
  315. 1 promise.errback(e);
  316. }
  317. });
  318. 10 this.addErrback(function () {
  319. 5 try {
  320. 5 when(isFunction(callback) ? callback.apply(this, arguments) : callback).then(promise);
  321. } catch (e) {
  322. 1 promise.errback(e);
  323. }
  324. });
  325. 10 return promise.promise();
  326. },
  327. /**
  328. * Creates an object to that contains methods to listen to resolution but not the "callback" or "errback" methods.
  329. *
  330. * @example
  331. *
  332. * var asyncMethod = function(){
  333. * var ret = new comb.Promise();
  334. * process.nextTick(ret.callback.bind(ret, "hello"));
  335. * return ret.promise();
  336. * }
  337. *
  338. * asyncMethod().callback() //throws error
  339. *
  340. * @return {Object} an object containing "chain", "chainBoth", "promise", "addCallback", "addErrback", "then", "both".
  341. */
  342. promise:function () {
  343. 3130 var ret = {
  344. chain:this.chain.bind(this),
  345. chainBoth:this.chainBoth.bind(this),
  346. promise:function () {
  347. 1098 return ret;
  348. }
  349. };
  350. 3130 forEach(["addCallback", "addErrback", "then", "both", "classic"], function (action) {
  351. 15650 ret[action] = function () {
  352. 1486 this[action].apply(this, arguments);
  353. 1486 return ret;
  354. }.bind(this);
  355. }, this);
  356. 3130 return ret;
  357. }
  358. }
  359. });
  360. 1var PromiseList = define(Promise, {
  361. instance:{
  362. /** @lends comb.PromiseList.prototype */
  363. /*@private*/
  364. __results:null,
  365. /*@private*/
  366. __errors:null,
  367. /*@private*/
  368. __promiseLength:0,
  369. /*@private*/
  370. __defLength:0,
  371. /*@private*/
  372. __firedLength:0,
  373. normalizeResults:false,
  374. /**
  375. * PromiseList object used for handling a list of Promises
  376. *
  377. * @example
  378. * var myFunc = function(){
  379. * var promise = new Promise();
  380. * //callback the promise after 10 Secs
  381. * setTimeout(hitch(promise, "callback"), 10000);
  382. * return promise.promise();
  383. * }
  384. * var myFunc2 = function(){
  385. * var promises =[];
  386. * for(var i = 0; i < 10; i++){
  387. * promises.push(myFunc);
  388. * }
  389. * //create a new promise list with all 10 promises
  390. * return new PromiseList(promises).promise();
  391. * }
  392. *
  393. * myFunc.then(function(success){}, function(error){})
  394. * //chain promise operations
  395. * myFunc.chain(myfunc).then(function(success){}, function(error){})
  396. *
  397. * myFunc2.then(function(success){}, function(error){})
  398. * //chain promise operations
  399. * myFunc2.chain(myfunc).then(function(success){}, function(error){})
  400. *
  401. * @param {comb.Promise[]} [defs=[]] the list of promises
  402. * @constructs
  403. * @augments comb.Promise
  404. * @memberOf comb
  405. * */
  406. constructor:function (defs, normalizeResults) {
  407. 61 this.__errors = [];
  408. 61 this.__results = [];
  409. 61 this.normalizeResults = base.isBoolean(normalizeResults) ? normalizeResults : false;
  410. 61 this._super(arguments);
  411. 61 if (defs && defs.length) {
  412. 54 this.__defLength = defs.length;
  413. 54 forEach(defs, this.__addPromise, this);
  414. } else {
  415. 7 this.__resolve();
  416. }
  417. },
  418. /**
  419. * Add a promise to our chain
  420. * @private
  421. * @param promise the promise to add to our chain
  422. * @param i the index of the promise in our chain
  423. */
  424. __addPromise:function (promise, i) {
  425. 181 promise.addCallback(hitch(this, function () {
  426. 176 var args = argsToArray(arguments);
  427. 176 args.unshift(i);
  428. 176 this.callback.apply(this, args);
  429. }));
  430. 181 promise.addErrback(hitch(this, function () {
  431. 5 var args = argsToArray(arguments);
  432. 5 args.unshift(i);
  433. 5 this.errback.apply(this, args);
  434. }));
  435. },
  436. /**
  437. * Resolves the promise
  438. * @private
  439. */
  440. __resolve:function () {
  441. 61 if (!this.__fired) {
  442. 61 this.__fired = true;
  443. 61 var cbs = this.__errors.length ? this.__errorCbs : this.__cbs,
  444. len = cbs.length, i,
  445. results = this.__errors.length ? this.__errors : this.__results;
  446. 61 for (i = 0; i < len; i++) {
  447. 50 this.__callNextTick(cbs[i], results);
  448. }
  449. }
  450. },
  451. __callNextTick:function (cb, results) {
  452. 60 process.nextTick(hitch(this, function () {
  453. 60 cb.apply(this, [results]);
  454. }));
  455. },
  456. addCallback:function (cb) {
  457. 60 if (cb) {
  458. 60 if (exports.isPromiseLike(cb)) {
  459. 25 cb = hitch(cb, "callback");
  460. }
  461. 60 if (this.__fired && !this.__errors.length) {
  462. 9 this.__callNextTick(cb, this.__results);
  463. } else {
  464. 51 this.__cbs.push(cb);
  465. }
  466. }
  467. 60 return this;
  468. },
  469. addErrback:function (cb) {
  470. 60 if (cb) {
  471. 60 if (exports.isPromiseLike(cb)) {
  472. 46 cb = hitch(cb, "errback");
  473. }
  474. 60 if (this.__fired && this.__errors.length) {
  475. 1 this.__callNextTick(cb, this.__errors);
  476. } else {
  477. 59 this.__errorCbs.push(cb);
  478. }
  479. }
  480. 60 return this;
  481. },
  482. callback:function (i) {
  483. 177 if (this.__fired) {
  484. 1 throw new Error("Already fired!");
  485. }
  486. 176 var args = argsToArray(arguments);
  487. 176 if (this.normalizeResults) {
  488. 159 args = args.slice(1);
  489. 159 args = args.length == 1 ? args.pop() : args;
  490. }
  491. 176 this.__results[i] = args;
  492. 176 this.__firedLength++;
  493. 176 if (this.__firedLength == this.__defLength) {
  494. 49 this.__resolve();
  495. }
  496. 176 return this.promise();
  497. },
  498. errback:function (i) {
  499. 6 if (this.__fired) {
  500. 1 throw new Error("Already fired!");
  501. }
  502. 5 var args = argsToArray(arguments);
  503. 5 if (this.normalizeResults) {
  504. 4 args = args.slice(1);
  505. 4 args = args.length == 1 ? args.pop() : args;
  506. }
  507. 5 this.__errors[i] = args;
  508. 5 this.__firedLength++;
  509. 5 if (this.__firedLength == this.__defLength) {
  510. 5 this.__resolve();
  511. }
  512. 5 return this.promise();
  513. }
  514. }
  515. });
  516. /**
  517. * Creates the promise chain
  518. * @ignore
  519. * @private
  520. */
  521. 1function callNext(list, results, propogate) {
  522. 59 var ret = new Promise().callback();
  523. 59 forEach(list, function (listItem) {
  524. 288 ret = ret.chain(propogate ? listItem : bindIgnore(null, listItem));
  525. 288 if (!propogate) {
  526. 244 ret.addCallback(function (res) {
  527. 221 results.push(res);
  528. });
  529. }
  530. });
  531. 59 return propogate ? ret.promise() : ret.chain(results);
  532. }
  533. /**
  534. * Tests if an object is like a promise (i.e. it contains then, addCallback, addErrback)
  535. * @param obj object to test
  536. * @function
  537. * @static
  538. * @memberOf comb
  539. */
  540. 1function isPromiseLike(obj) {
  541. 6943 return !isUndefinedOrNull(obj) && (isInstanceOf(obj, Promise) || (isFunction(obj.then)
  542. && isFunction(obj.addCallback) && isFunction(obj.addErrback)));
  543. }
  544. /**
  545. * Waits for promise and non promise values to resolve and fires callback or errback appropriately. If you pass in an array of promises
  546. * then it will wait for all promises in the list to resolve.
  547. *
  548. * @example
  549. * var a = "hello";
  550. * var b = new comb.Promise().callback(world);
  551. * comb.when(a, b) => called back with ["hello", "world"];
  552. *
  553. * @param {Anything...} args variable number of arguments to wait for.
  554. * @param {Function} cb the callback function
  555. * @param {Function} eb the errback function
  556. * @returns {comb.Promise} a promise that is fired when all values have resolved
  557. * @static
  558. * @memberOf comb
  559. */
  560. 1function when(args, cb, eb) {
  561. 1598 var args = argsToArray(arguments), p;
  562. 1598 eb = base.isFunction(args[args.length - 1]) ? args.pop() : null;
  563. 1598 cb = base.isFunction(args[args.length - 1]) ? args.pop() : null;
  564. 1598 if (eb && !cb) {
  565. 9 cb = eb;
  566. 9 eb = null;
  567. }
  568. 1598 if (!args.length) {
  569. 7 p = new Promise().callback(args);
  570. 1591 } else if (args.length == 1) {
  571. 1551 args = args.pop();
  572. 1551 if (isPromiseLike(args)) {
  573. 885 p = args;
  574. 666 } else if (isArray(args) && array.every(args, isPromiseLike)) {
  575. 10 p = new PromiseList(args, true);
  576. } else {
  577. 656 p = new Promise().callback(args);
  578. }
  579. } else {
  580. 40 p = new PromiseList(args.map(function (a) {
  581. 135 return exports.isPromiseLike(a) ? a : new Promise().callback(a);
  582. }), true);
  583. }
  584. 1598 if (cb) {
  585. 15 p.addCallback(cb);
  586. }
  587. 1598 if (eb) {
  588. 6 p.addErrback(eb);
  589. }
  590. 1598 return p.promise();
  591. }
  592. /**
  593. * Wraps traditional node style functions with a promise.
  594. * @example
  595. *
  596. * var fs = require("fs");
  597. * var readFile = comb.wrap(fs.readFile, fs);
  598. * readFile(__dirname + "/test.json").then(
  599. * function(buffer){
  600. * console.log(contents);
  601. * },
  602. * funciton(err){
  603. *
  604. * } console.error(err);
  605. * );
  606. *
  607. *
  608. * @param {Function} fn function to wrap
  609. * @param {Object} scope scope to call the function in
  610. *
  611. * @return {Funciton} a wrapped function
  612. * @static
  613. * @memberOf comb
  614. */
  615. 1function wrap(fn, scope) {
  616. 2 return function () {
  617. 2 var ret = new Promise();
  618. 2 var args = argsToArray(arguments);
  619. 2 args.push(ret.resolve.bind(ret));
  620. 2 fn.apply(scope || this, args);
  621. 2 return ret.promise();
  622. }
  623. }
  624. /**
  625. * Executes a list of items in a serial manner. If the list contains promises then each promise
  626. * will be executed in a serial manner, if the list contains non async items then the next item in the list
  627. * is called.
  628. *
  629. * @example
  630. *
  631. * var asyncAction = function(item, timeout){
  632. * var ret = new comb.Promise();
  633. * setTimeout(comb.hitchIgnore(ret, "callback", item), timeout);
  634. * return ret.promise();
  635. * };
  636. *
  637. * comb.serial([
  638. * comb.partial(asyncAction, 1, 1000),
  639. * comb.partial(asyncAction, 2, 900),
  640. * comb.partial(asyncAction, 3, 800),
  641. * comb.partial(asyncAction, 4, 700),
  642. * comb.partial(asyncAction, 5, 600),
  643. * comb.partial(asyncAction, 6, 500)
  644. * ]).then(function(results){
  645. * console.log(results); // [1,2,3,4,5,6];
  646. * });
  647. *
  648. *
  649. *
  650. * @param list
  651. * @param callback
  652. * @param errback
  653. * @static
  654. * @memberOf comb
  655. */
  656. 1function serial(list, callback, errback) {
  657. 56 if (base.isArray(list)) {
  658. 55 return callNext(list, [], false);
  659. } else {
  660. 1 throw new Error("When calling comb.serial the first argument must be an array");
  661. }
  662. }
  663. /**
  664. * Works just like {@link comb.Promise#chain} method, allowing you to propogate results from one funciton to another.
  665. * This is different than {@link comb.serial} in that it propogates results from one promise to the next, where
  666. * {@link comb.serial} does not.
  667. *
  668. * @example
  669. *
  670. * function asyncAction(add, timeout) {
  671. * return function (num) {
  672. * num = num || 0;
  673. * var ret = new comb.Promise();
  674. * setTimeout(function () {
  675. * ret.callback(num + add);
  676. * }, timeout);
  677. * return ret;
  678. * }
  679. * }
  680. *
  681. * comb.chain([
  682. * asyncAction(1, 100),
  683. * asyncAction(2, 100),
  684. * asyncAction(3, 100),
  685. * asyncAction(4, 100),
  686. * asyncAction(5, 100),
  687. * ]).then(function(results){
  688. * console.log(results); //15
  689. * });
  690. *
  691. * @param {function[]} list an array of function to call.
  692. * @return {comb.Promise} a promise that will resolve with the results of the last function in the list.
  693. * @static
  694. * @memberOf comb
  695. */
  696. 1function chain(list) {
  697. 5 if (base.isArray(list)) {
  698. 4 return callNext(list, [], true);
  699. } else {
  700. 1 throw new Error("When calling comb.serial the first argument must be an array");
  701. }
  702. }
  703. 1base.merge(exports, {
  704. isPromiseLike:isPromiseLike,
  705. when:when,
  706. wrap:wrap,
  707. serial:serial,
  708. chain:chain,
  709. Promise:Promise,
  710. PromiseList:PromiseList
  711. });