Overview
Coverage40.68 SLOC12330 LOC3363 Missed1995

collections/Tree.js
Coverage2.50 SLOC457 LOC160 Missed156
  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. 0 var ret = 0;
  8. 0 if (a > b) {
  9. 0 return 1;
  10. 0 } else if (a < b) {
  11. 0 return -1;
  12. 0 }else if(!b){
  13. 0 return 1;
  14. }
  15. 0 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. 0 var str = [];
  28. 0 if (node == null || node == undefined) {
  29. 0 str.push(multiply('\t', level));
  30. 0 str.push("~");
  31. 0 console.log(str.join(""));
  32. } else {
  33. 0 this.__printNode(node.right, level + 1);
  34. 0 str.push(multiply('\t', level));
  35. 0 str.push(node.data + "\n");
  36. 0 console.log(str.join(""));
  37. 0 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. 0 options = options || {};
  58. 0 this.compare = options.compare || compare;
  59. 0 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. 0 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. 0 throw new Error("Not Implemented");
  74. },
  75. /**
  76. * Clear all items from a tree
  77. */
  78. clear : function() {
  79. 0 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. 0 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. 0 var cont = true;
  101. 0 if (node) {
  102. 0 order = order || Tree.PRE_ORDER;
  103. 0 if (order === Tree.PRE_ORDER) {
  104. 0 cont = callback(node.data);
  105. 0 if (cont) {
  106. 0 cont = this.traverseWithCondition(node.left, order, callback);
  107. 0 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  108. }
  109. 0 } else if (order === Tree.IN_ORDER) {
  110. 0 cont = this.traverseWithCondition(node.left, order, callback);
  111. 0 if (cont) {
  112. 0 cont = callback(node.data);
  113. 0 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  114. }
  115. 0 } else if (order === Tree.POST_ORDER) {
  116. 0 cont = this.traverseWithCondition(node.left, order, callback);
  117. 0 if (cont) {
  118. 0 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  119. 0 cont && (cont = callback(node.data));
  120. }
  121. 0 } else if (order === Tree.REVERSE_ORDER) {
  122. 0 cont = this.traverseWithCondition(node.right, order, callback);
  123. 0 if (cont) {
  124. 0 cont = callback(node.data);
  125. 0 cont && (cont = this.traverseWithCondition(node.left, order, callback));
  126. }
  127. }
  128. }
  129. 0 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. 0 if (node) {
  143. 0 order = order || Tree.PRE_ORDER;
  144. 0 if (order === Tree.PRE_ORDER) {
  145. 0 callback(node.data);
  146. 0 this.traverse(node.left, order, callback);
  147. 0 this.traverse(node.right, order, callback);
  148. 0 } else if (order === Tree.IN_ORDER) {
  149. 0 this.traverse(node.left, order, callback);
  150. 0 callback(node.data);
  151. 0 this.traverse(node.right, order, callback);
  152. 0 } else if (order === Tree.POST_ORDER) {
  153. 0 this.traverse(node.left, order, callback);
  154. 0 this.traverse(node.right, order, callback);
  155. 0 callback(node.data);
  156. 0 } else if (order === Tree.REVERSE_ORDER) {
  157. 0 this.traverseWithCondition(node.right, order, callback);
  158. 0 callback(node.data);
  159. 0 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. 0 if (typeof cb !== "function")
  171. 0 throw new TypeError();
  172. 0 order = order || Tree.IN_ORDER;
  173. 0 scope = scope || this;
  174. 0 this.traverse(this.__root, order, function(node) {
  175. 0 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. 0 if (typeof cb !== "function")
  189. 0 throw new TypeError();
  190. 0 order = order || Tree.IN_ORDER;
  191. 0 scope = scope || this;
  192. 0 var construct = this._static;
  193. 0 var ret = new this._static();
  194. 0 this.traverse(this.__root, order, function(node) {
  195. 0 ret.insert(cb.call(scope, node, this));
  196. });
  197. 0 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. 0 if (typeof cb !== "function")
  210. 0 throw new TypeError();
  211. 0 order = order || Tree.IN_ORDER;
  212. 0 scope = scope || this;
  213. 0 var ret = new this._static();
  214. 0 this.traverse(this.__root, order, function(node) {
  215. 0 var include = cb.call(scope, node, this);
  216. 0 include && ret.insert(node);
  217. });
  218. 0 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. 0 var arr = this.toArray(order);
  231. 0 var args = [fun];
  232. 0 !base.isUndefinedOrNull(accumulator) && args.push(accumulator)
  233. 0 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. 0 var arr = this.toArray(order);
  246. 0 var args = [fun];
  247. 0 !base.isUndefinedOrNull(accumulator) && args.push(accumulator)
  248. 0 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. 0 if (typeof cb !== "function")
  261. 0 throw new TypeError();
  262. 0 order = order || Tree.IN_ORDER;
  263. 0 scope = scope || this;
  264. 0 var ret = false;
  265. 0 this.traverseWithCondition(this.__root, order, function(node) {
  266. 0 return (ret = cb.call(scope, node, this));
  267. });
  268. 0 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. 0 if (typeof cb !== "function")
  281. 0 throw new TypeError();
  282. 0 order = order || Tree.IN_ORDER;
  283. 0 scope = scope || this;
  284. 0 var ret;
  285. 0 this.traverseWithCondition(this.__root, order, function(node) {
  286. 0 ret = cb.call(scope, node, this);
  287. 0 return !ret;
  288. });
  289. 0 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. 0 order = order || Tree.IN_ORDER;
  300. 0 var arr = [];
  301. 0 this.traverse(this.__root, order, function(node) {
  302. 0 arr.push(node);
  303. });
  304. 0 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. 0 var ret = false;
  314. 0 var root = this.__root;
  315. 0 while (root != null) {
  316. 0 var cmp = this.compare(value, root.data);
  317. 0 if (cmp) {
  318. 0 root = root[(cmp == -1) ? "left" : "right"];
  319. } else {
  320. 0 ret = true;
  321. 0 root = null;
  322. }
  323. }
  324. 0 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. 0 var ret;
  334. 0 var root = this.__root;
  335. 0 while (root != null) {
  336. 0 var cmp = this.compare(value, root.data);
  337. 0 if (cmp) {
  338. 0 root = root[(cmp == -1) ? "left" : "right"];
  339. } else {
  340. 0 ret = root.data;
  341. 0 break;
  342. }
  343. }
  344. 0 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. 0 var ret = [], compare = this.compare;
  356. 0 this.traverseWithCondition(this.__root, exports.IN_ORDER, function(v) {
  357. 0 var cmp = compare(value, v);
  358. 0 if ((!exclusive && cmp == 0) || cmp == 1) {
  359. 0 ret.push(v);
  360. 0 return true;
  361. } else {
  362. 0 return false;
  363. }
  364. });
  365. 0 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. 0 var ret = [], compare = this.compare;
  377. 0 this.traverse(this.__root, exports.REVERSE_ORDER, function(v) {
  378. 0 var cmp = compare(value, v);
  379. 0 if ((!exclusive && cmp == 0) || cmp == -1) {
  380. 0 ret.push(v);
  381. 0 return true;
  382. } else {
  383. 0 return false;
  384. }
  385. });
  386. 0 return ret;
  387. },
  388. /**
  389. * Prints a tree to the console.
  390. */
  391. print : function() {
  392. 0 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/HashTable.js
Coverage3.17 SLOC378 LOC126 Missed122
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. Iterable = require("./Iterable"),
  4. base = require("../base");
  5. 1var hashFunction = function (key) {
  6. 0 if (typeof key == "string") {
  7. 0 return key;
  8. 0 } else if (typeof key == "object") {
  9. 0 return key.hashCode ? key.hashCode() : "" + key;
  10. } else {
  11. 0 return "" + key;
  12. }
  13. };
  14. 1var Bucket = define(null, {
  15. instance:{
  16. constructor:function () {
  17. 0 this.__entries = [];
  18. },
  19. pushValue:function (key, value) {
  20. 0 this.__entries.push({key:key, value:value});
  21. 0 return value;
  22. },
  23. remove:function (key) {
  24. 0 var ret = null, map = this.__entries, val;
  25. 0 var i = map.length - 1;
  26. 0 for (; i >= 0; i--) {
  27. 0 if ((val = map[i]) != null && val.key === key) {
  28. 0 map[i] = null;
  29. 0 return val.value;
  30. }
  31. }
  32. 0 return ret;
  33. },
  34. "set":function (key, value) {
  35. 0 var ret = null, map = this.__entries;
  36. 0 var i = map.length - 1;
  37. 0 for (; i >= 0; i--) {
  38. 0 var val = map[i];
  39. 0 if (val && key === val.key) {
  40. 0 val.value = value;
  41. 0 ret = value;
  42. 0 break;
  43. }
  44. }
  45. 0 if (!ret) {
  46. 0 map.push({key:key, value:value});
  47. }
  48. 0 return ret;
  49. },
  50. find:function (key) {
  51. 0 var ret = null, map = this.__entries, val;
  52. 0 var i = map.length - 1;
  53. 0 for (; i >= 0; i--) {
  54. 0 val = map[i];
  55. 0 if (val && key === val.key) {
  56. 0 ret = val.value;
  57. 0 break;
  58. }
  59. }
  60. 0 return ret;
  61. },
  62. getEntrySet:function (arr) {
  63. 0 var map = this.__entries, l = map.length;
  64. 0 if (l) {
  65. 0 for (var i = 0; i < l; i++) {
  66. 0 var e = map[i];
  67. 0 if (e) {
  68. 0 arr.push(e);
  69. }
  70. }
  71. }
  72. },
  73. getKeys:function (arr) {
  74. 0 var map = this.__entries, l = map.length;
  75. 0 if (l) {
  76. 0 for (var i = 0; i < l; i++) {
  77. 0 var e = map[i];
  78. 0 if (e) {
  79. 0 arr.push(e.key);
  80. }
  81. }
  82. }
  83. 0 return arr;
  84. },
  85. getValues:function (arr) {
  86. 0 var map = this.__entries, l = map.length;
  87. 0 if (l) {
  88. 0 for (var i = 0; i < l; i++) {
  89. 0 var e = map[i];
  90. 0 if (e) {
  91. 0 arr.push(e.value);
  92. }
  93. }
  94. }
  95. 0 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. 0 this.__map = {};
  121. },
  122. __entrySet:function () {
  123. 0 var ret = [], es = [];
  124. 0 for (var i in this.__map) {
  125. 0 this.__map[i].getEntrySet(ret);
  126. }
  127. 0 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. 0 var hash = hashFunction(key);
  141. 0 var bucket = null;
  142. 0 if ((bucket = this.__map[hash]) == null) {
  143. 0 bucket = (this.__map[hash] = new Bucket());
  144. }
  145. 0 bucket.pushValue(key, value);
  146. 0 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. 0 var hash = hashFunction(key), ret = null;
  157. 0 var bucket = this.__map[hash];
  158. 0 if (bucket) {
  159. 0 ret = bucket.remove(key);
  160. }
  161. 0 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. 0 var hash = hashFunction(key), ret = null;
  172. 0 var bucket = null;
  173. 0 if ((bucket = this.__map[hash]) != null) {
  174. 0 ret = bucket.find(key);
  175. }
  176. 0 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. 0 var hash = hashFunction(key), ret = null, bucket = null, map = this.__map;
  188. 0 if ((bucket = map[hash]) != null) {
  189. 0 ret = bucket.set(key, value);
  190. } else {
  191. 0 ret = (map[hash] = new Bucket()).pushValue(key, value);
  192. }
  193. 0 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. 0 var hash = hashFunction(key), ret = false;
  203. 0 var bucket = null;
  204. 0 if ((bucket = this.__map[hash]) != null) {
  205. 0 ret = bucket.find(key) != null;
  206. }
  207. 0 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. 0 if (hashTable instanceof this._static) {
  219. 0 var ret = new this._static();
  220. 0 var otherEntrySet = hashTable.entrySet.concat(this.entrySet);
  221. 0 for (var i = otherEntrySet.length - 1; i >= 0; i--) {
  222. 0 var e = otherEntrySet[i];
  223. 0 ret.put(e.key, e.value);
  224. }
  225. 0 return ret;
  226. } else {
  227. 0 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. 0 var es = this.__entrySet(), ret = new this._static();
  240. 0 es = es.filter.apply(es, arguments);
  241. 0 for (var i = es.length - 1; i >= 0; i--) {
  242. 0 var e = es[i];
  243. 0 ret.put(e.key, e.value);
  244. }
  245. 0 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. 0 var es = this.__entrySet(), l = es.length, f = cb.bind(scope || this);
  255. 0 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. 0 var es = this.__entrySet();
  267. 0 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. 0 var es = this.__entrySet(), ret = new this._static();
  278. 0 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. 0 var es = this.__entrySet();
  290. 0 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. 0 var es = this.__entrySet();
  300. 0 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. 0 var es = this.__entrySet();
  310. 0 return es.reduceRight.apply(es, arguments);
  311. },
  312. /**
  313. * Clears out all items from the table.
  314. */
  315. clear:function () {
  316. 0 this.__map = {};
  317. },
  318. getters:{
  319. keys:function () {
  320. 0 var ret = [], es = [];
  321. 0 for (var i in this.__map) {
  322. 0 this.__map[i].getKeys(ret);
  323. }
  324. 0 return ret;
  325. },
  326. values:function () {
  327. 0 var ret = [], es = [];
  328. 0 for (var i in this.__map) {
  329. 0 this.__map[i].getValues(ret);
  330. }
  331. 0 return ret;
  332. },
  333. entrySet:function () {
  334. 0 return this.__entrySet();
  335. },
  336. isEmpty:function () {
  337. 0 return this.keys.length == 0;
  338. }
  339. }
  340. }
  341. }).as(module);
collections/Pool.js
Coverage3.92 SLOC162 LOC51 Missed49
  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. 0 options = options || {};
  25. 0 this.__freeObjects = new Queue();
  26. 0 this.__inUseObjects = [];
  27. 0 this.__minObjects = options.minObjects || 0;
  28. 0 this.__maxObjects = options.maxObjects || 1;
  29. 0 this.minObjects = this.__minObjects;
  30. 0 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. 0 var ret = undefined;
  39. 0 if (this.freeCount > 0) {
  40. 0 ret = this.__freeObjects.dequeue();
  41. 0 this.__inUseObjects.push(ret);
  42. 0 } else if (this.__maxObjects > this.count) {
  43. 0 ret = this.createObject();
  44. 0 this.__inUseObjects.push(ret);
  45. }
  46. 0 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. 0 if (this.validate(obj) && this.count <= this.__maxObjects) {
  55. 0 this.__freeObjects.enqueue(obj);
  56. 0 var index;
  57. 0 if ((index = this.__inUseObjects.indexOf(obj)) > -1)
  58. 0 this.__inUseObjects.splice(index, 1);
  59. } else {
  60. 0 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. 0 var index;
  73. 0 if (this.__freeObjects.contains(obj)) {
  74. 0 this.__freeObjects.remove(obj);
  75. 0 } else if ((index = this.__inUseObjects.indexOf(obj)) > -1) {
  76. 0 this.__inUseObjects.splice(index, 1);
  77. }
  78. //otherwise its not contained in this pool;
  79. 0 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. 0 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. 0 return {};
  100. },
  101. setters : {
  102. minObjects : function(l) {
  103. 0 if (l <= this.__maxObjects) {
  104. 0 this.__minObjects = l;
  105. 0 var i;
  106. 0 if ((i = this.count) < l) {
  107. 0 while (i++ < l) {
  108. 0 this.__freeObjects.enqueue(this.createObject());
  109. }
  110. }
  111. } else {
  112. 0 throw "comb.collections.Pool : minObjects cannot be greater than maxObjects.";
  113. }
  114. },
  115. maxObjects : function(l) {
  116. 0 if (l >= this.__minObjects) {
  117. 0 this.__maxObjects = l;
  118. 0 var i = this.count, j = this.freeCount, fo = this.__freeObjects;
  119. 0 while (i > l && j >= 0) {
  120. 0 this.removeObject(fo.dequeue());
  121. 0 j--;
  122. 0 i--;
  123. }
  124. } else {
  125. 0 throw "comb.collections.Pool : maxObjects cannot be less than maxObjects.";
  126. }
  127. }
  128. },
  129. getters : {
  130. freeCount : function() {
  131. 0 return this.__freeObjects.count;
  132. },
  133. inUseCount : function() {
  134. 0 return this.__inUseObjects.length;
  135. },
  136. count : function() {
  137. 0 return this.__freeObjects.count + this.__inUseObjects.length;
  138. },
  139. minObjects : function() {
  140. 0 return this.__minObjects;
  141. },
  142. maxObjects : function() {
  143. 0 return this.__maxObjects;
  144. }
  145. }
  146. }
  147. })
collections/Heap.js
Coverage4.84 SLOC218 LOC62 Missed59
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. base = require("../base");
  4. 1var padding = function(char, numSpaces) {
  5. 0 var ret = [];
  6. 0 for (var i = 0; i < numSpaces; i++)
  7. 0 ret.push(char);
  8. 0 return ret.join("");
  9. };
  10. 1module.exports = exports = define(Collection, {
  11. instance : {
  12. /**@lends comb.collections.Heap.prototype*/
  13. __getParentIndex : function(index) {
  14. 0 return Math.floor((index - 1) / 2);
  15. },
  16. __getLeftChildIndex : function(index) {
  17. 0 return (index * 2) + 1;
  18. },
  19. __getRightChildIndex : function(index) {
  20. 0 return (index * 2) + 2;
  21. },
  22. __makeNode : function(key, value) {
  23. 0 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. 0 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. 0 if (!base.isString(key)) {
  48. 0 var l = this.__heap.push(this.__makeNode(key, value));
  49. 0 this.__upHeap(l - 1);
  50. } else {
  51. 0 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. 0 var ret = undefined, heap = this.__heap, l = heap.length;
  61. 0 if (l) {
  62. 0 ret = heap[0];
  63. 0 if (l == 1) {
  64. 0 heap.length = 0;
  65. } else {
  66. 0 heap[0] = heap.pop();
  67. 0 this.__downHeap(0);
  68. }
  69. }
  70. 0 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. 0 var ret = undefined, heap = this.__heap, l = heap.length;
  79. 0 if (l) {
  80. 0 ret = heap[0];
  81. }
  82. 0 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. 0 var ret = undefined, heap = this.__heap, l = heap.length;
  91. 0 if (l) {
  92. 0 ret = heap[0];
  93. }
  94. 0 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. 0 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. 0 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. 0 var heap = this.__heap;
  123. 0 for (var i = heap.length - 1; i >= 0; i--) {
  124. 0 if (heap[i].key == key) {
  125. 0 return true;
  126. }
  127. }
  128. 0 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. 0 var heap = this.__heap;
  140. 0 for (var i = heap.length - 1; i >= 0; i--) {
  141. 0 if (heap[i].value == value) {
  142. 0 return true;
  143. }
  144. }
  145. 0 return false;
  146. },
  147. /**
  148. * Empty the heap.
  149. */
  150. clear : function() {
  151. 0 this.__heap.length = 0;
  152. },
  153. __printNode : function(index, level) {
  154. //console.log(level);
  155. 0 var str = [], node = this.__heap[index];
  156. 0 if (node == null || node == undefined) {
  157. 0 str.push(padding('\t', level));
  158. 0 str.push("~");
  159. 0 console.log(str.join(""));
  160. } else {
  161. 0 this.__printNode(this.__getRightChildIndex(index), level + 1);
  162. 0 str.push(padding('\t', level));
  163. 0 str.push(node.key + " : " + node.value + "\n");
  164. 0 console.log(str.join(""));
  165. 0 this.__printNode(this.__getLeftChildIndex(index), level + 1);
  166. }
  167. },
  168. /**
  169. * Print the heap.
  170. */
  171. print : function() {
  172. 0 this.__printNode(0, 0);
  173. },
  174. getters : {
  175. count : function() {
  176. 0 return this.__heap.length;
  177. },
  178. keys : function() {
  179. 0 return this.__heap.map(function(n) {
  180. 0 return n.key;
  181. });
  182. },
  183. values : function() {
  184. 0 return this.__heap.map(function(n) {
  185. 0 return n.value;
  186. });
  187. },
  188. isEmpty : function() {
  189. 0 return this.__heap.length == 0;
  190. }
  191. }
  192. }
  193. });
collections/Queue.js
Coverage6.25 SLOC119 LOC32 Missed30
  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. 0 this.__queue = [];
  20. 0 this.__next = 0;
  21. 0 this.__last = 0;
  22. },
  23. /**
  24. * Add data to this queue
  25. * @param {*} data element to add
  26. */
  27. enqueue : function(data) {
  28. 0 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. 0 var ret = undefined,next = this.__next, queue;
  37. 0 if (next != this.__last) {
  38. 0 queue = this.__queue;
  39. 0 ret = queue[next];
  40. 0 queue[this.__next++] = undefined;
  41. }
  42. 0 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. 0 var ret = undefined, next = this.__next;
  51. 0 if (next != this.__last) {
  52. 0 ret = this.__queue[next];
  53. }
  54. 0 return ret;
  55. },
  56. /**
  57. * Removes all items from this queue
  58. */
  59. clear : function() {
  60. 0 this.__queue.length = 0;
  61. 0 this.__next = 0;
  62. 0 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. 0 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. 0 var index = this.__queue.indexOf(obj), ret = false;
  81. 0 if (index != -1) {
  82. 0 if (index == this.__next) {
  83. 0 this.dequeue();
  84. } else {
  85. 0 this.__queue.splice(index, 1);
  86. 0 this.__last--;
  87. }
  88. 0 ret = true;
  89. }
  90. 0 return ret;
  91. },
  92. toString : function(){
  93. 0 return this.__queue.toString();
  94. },
  95. getters : {
  96. count : function() {
  97. 0 return this.__last - this.__next;
  98. },
  99. isEmpty : function() {
  100. 0 return this.__last - this.__next == 0;
  101. },
  102. values : function() {
  103. 0 return this.__queue.slice(this.__next, this.__last);
  104. }
  105. }
  106. }
  107. });
logging/appenders/rollingFileAppender.js
Coverage6.33 SLOC211 LOC79 Missed74
  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. 0 var ret = DEFAULT_SIZE;
  23. 0 var match = str.match(/(\d+)(MB|KB|GB)$/);
  24. 0 if (match && match.length == 3) {
  25. 0 var size = parseInt(match[1], 10);
  26. 0 ret = size * conversion[match[2]];
  27. }
  28. 0 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. 0 options = options || {};
  81. 0 this.maxSize = options.maxSize || DEFAULT_SIZE;
  82. 0 !options.name && (options.name = "rollingFileAppender");
  83. 0 this.maxBackupIndex = options.maxBackupIndex || 10;
  84. 0 this.__queue = [];
  85. 0 this.__inRollover = false;
  86. 0 this._super([options]);
  87. },
  88. __startCheck:function () {
  89. 0 if (!this.__watching) {
  90. 0 this.__watching = true;
  91. 0 fs.watchFile(this.__file, hitch(this, "__checkFile"));
  92. 0 fs.stat(this.__file, hitch(this, function (err, stat) {
  93. 0 this.__checkFile(stat);
  94. }));
  95. }
  96. },
  97. __checkFile:function (stats) {
  98. 0 var ret = new Promise();
  99. 0 if (!this.__inRollover) {
  100. 0 if (stats.size >= this.maxSize) {
  101. 0 if (this.maxBackupIndex > 0) {
  102. 0 this.__inRollover = true;
  103. 0 this.__onExit().chain(hitch(this, "__rollover")).then(hitch(this, function () {
  104. 0 var ws = fs.createWriteStream(this.__file, { flags:"w", encoding:this.__encoding});
  105. 0 ws.on("open", hitch(this, function () {
  106. 0 this.__writeStream = ws;
  107. 0 this.__inRollover = false;
  108. 0 this.__checkQueue();
  109. 0 ret.callback();
  110. }));
  111. }), hitch(ret, "errback", new Error("comb.logging.appenders.RollingFileAppender : error rolling over files")));
  112. } else {
  113. 0 this.__writeStream = fs.createWriteStream(this.__file, { flags:"w", encoding:this.__encoding});
  114. 0 ret.callback();
  115. }
  116. } else {
  117. 0 ret.callback();
  118. }
  119. } else {
  120. 0 ret.callback();
  121. }
  122. 0 return ret.promise();
  123. },
  124. append:function (event) {
  125. 0 if (this._canAppend(event)) {
  126. 0 !this.__watching && this.__startCheck();
  127. 0 var ws = this.__writeStream;
  128. 0 if (!this.__inRollover && ws && ws.writable) {
  129. 0 this._super(arguments);
  130. } else {
  131. 0 this.__queue.push(event);
  132. }
  133. }
  134. },
  135. __checkQueue:function () {
  136. 0 this.__queue.forEach(this.append, this);
  137. 0 this.__queue.length = 0;
  138. },
  139. __rollover:function () {
  140. 0 var ret = new Promise(), file = this.__file;
  141. 0 var dir = path.dirname(file), baseName = new RegExp("(" + escape(path.basename(path.basename(file))) + ")(?:\\.(\\d*))*");
  142. 0 fs.readdir(dir, hitch(this, function (err, files) {
  143. 0 files = files.filter(
  144. function (f) {
  145. 0 var match = f.match(baseName);
  146. 0 if (match) {
  147. 0 return true;
  148. } else {
  149. 0 return false;
  150. }
  151. });
  152. 0 files = files.sort(function (a, b) {
  153. 0 var ret = 0;
  154. 0 if (a > b) {
  155. 0 ret = 0;
  156. 0 } else if (a < b) {
  157. 0 ret = 1;
  158. }
  159. 0 return ret;
  160. });
  161. 0 var count = files.length, i = 0;
  162. 0 var checkFile = hitch(this, function () {
  163. 0 if (count > 0) {
  164. 0 var f = dir + "/" + files[i++];
  165. 0 if (count > this.maxBackupIndex) {
  166. //drop the file;
  167. 0 count--;
  168. 0 fs.unlink(f, function (err) {
  169. 0 err ? ret.errback(err) : checkFile();
  170. });
  171. } else {
  172. //rename the file
  173. 0 var rn = this.__file + "." + count--;
  174. 0 fs.rename(f, rn, function (err) {
  175. 0 err ? ret.errback(err) : checkFile();
  176. });
  177. }
  178. } else {
  179. 0 ret.callback();
  180. }
  181. });
  182. 0 checkFile();
  183. }));
  184. 0 return ret.promise();
  185. },
  186. getters:{
  187. maxSize:function () {
  188. 0 return this.__maxSize;
  189. }
  190. },
  191. setters:{
  192. maxSize:function (size) {
  193. 0 this.__maxSize = size ? convertToBytes(size) : DEFAULT_SIZE;
  194. }
  195. }
  196. }
  197. }).registerType("RollingFileAppender").as(module);
collections/BinaryTree.js
Coverage6.67 SLOC83 LOC30 Missed28
  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. 0 if (this.__root == null) {
  31. 0 return (this.__root = {
  32. data : data,
  33. parent : null,
  34. left : null,
  35. right : null
  36. });
  37. }
  38. 0 var compare = this.compare;
  39. 0 var root = this.__root;
  40. 0 while (root != null) {
  41. 0 var cmp = compare(data, root.data);
  42. 0 if (cmp) {
  43. 0 var leaf = (cmp == -1) ? "left" : "right";
  44. 0 var next = root[leaf];
  45. 0 if (next == null) {
  46. 0 return (root[leaf] = {data : data, parent : root, left : null, right : null});
  47. } else {
  48. 0 root = next;
  49. }
  50. } else {
  51. 0 return;
  52. }
  53. }
  54. },
  55. remove : function(data) {
  56. 0 if (this.__root != null) {
  57. 0 var head = {right : this.__root}, it = head;
  58. 0 var p, f = null;
  59. 0 var dir = "right";
  60. 0 while (it[dir] != null) {
  61. 0 p = it;
  62. 0 it = it[dir];
  63. 0 var cmp = this.compare(data, it.data);
  64. 0 if (!cmp) {
  65. 0 f = it;
  66. }
  67. 0 dir = (cmp == -1 ? "left" : "right");
  68. }
  69. 0 if (f != null) {
  70. 0 f.data = it.data;
  71. 0 p[p.right == it ? "right" : "left"] = it[it.left == null ? "right" : "left"];
  72. }
  73. 0 this.__root = head.right;
  74. }
  75. }
  76. }
  77. });
collections/Stack.js
Coverage6.67 SLOC117 LOC30 Missed28
  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. 0 this.__stack = [];
  20. 0 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. 0 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. 0 var ret = undefined, stack, next = this.__next;
  36. 0 if (next >= 0) {
  37. 0 stack = this.__stack;
  38. 0 ret = stack[next];
  39. 0 stack[this.__next--] = undefined;
  40. }
  41. 0 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. 0 var ret = undefined,next = this.__next;
  50. 0 if (next >= 0) {
  51. 0 ret = this.__stack[next];
  52. }
  53. 0 return ret;
  54. },
  55. /**
  56. * Removes all items from this stack.
  57. */
  58. clear : function() {
  59. 0 this.__stack.length = 0;
  60. 0 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. 0 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. 0 var index = this.__stack.indexOf(obj), ret = false;
  79. 0 if (index != -1) {
  80. 0 if (index == this.__next) {
  81. 0 this.pop();
  82. } else {
  83. 0 this.__stack.splice(index, 1);
  84. 0 this.__next--;
  85. }
  86. 0 ret = true;
  87. }
  88. 0 return ret;
  89. },
  90. toString : function(){
  91. 0 return this.__stack.toString();
  92. },
  93. getters : {
  94. count : function() {
  95. 0 return this.__next + 1;
  96. },
  97. isEmpty : function() {
  98. 0 return this.__next < 0;
  99. },
  100. values : function() {
  101. 0 return this.__stack.slice(0, this.__next + 1).reverse();
  102. }
  103. }
  104. }
  105. });
plugins/Middleware.js
Coverage7.50 SLOC212 LOC40 Missed37
  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. 0 this.__hooks = obj.merge({}, this.__hooks);
  44. 0 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. 0 args = args || [];
  65. 0 var promise = new Promise();
  66. 0 var funcs, length;
  67. 0 if (this.__hooks[state] && (funcs = this.__hooks[state][op]) != null && (length = funcs.length) > 0) {
  68. 0 var count = 0;
  69. 0 var next = func.hitch(this, function (err) {
  70. 0 if (err) {
  71. 0 promise.errback(err);
  72. } else {
  73. 0 process.nextTick(func.hitch(this, function () {
  74. //if Ive looped through all of them callback
  75. 0 if (count == length) {
  76. 0 promise.callback();
  77. } else {
  78. //call next
  79. 0 var nextArgs = args.slice(0);
  80. 0 nextArgs.unshift(next);
  81. 0 funcs[count++].apply(this, nextArgs);
  82. }
  83. }));
  84. }
  85. });
  86. 0 next();
  87. } else {
  88. 0 promise.callback();
  89. }
  90. 0 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. 0 var hook = this.__hooks.pre[fun];
  113. 0 if (!hook) {
  114. 0 hook = this.__hooks.pre[fun] = [];
  115. }
  116. 0 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. 0 var hook = this.__hooks.post[fun];
  137. //if I havent initialized it create it;
  138. 0 if (hook == undefined) {
  139. 0 hook = this.__hooks.post[fun] = [];
  140. }
  141. 0 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. 0 var hooks = this.prototype.__hooks;
  165. 0 var hook = hooks.pre[name];
  166. 0 if (!hook) {
  167. 0 hook = hooks.pre[name] = [];
  168. }
  169. 0 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. 0 var hooks = this.prototype.__hooks;
  190. 0 var hook = hooks.post[name];
  191. 0 if (!hook) {
  192. 0 hook = hooks.post[name] = [];
  193. }
  194. 0 hook.push(cb);
  195. }
  196. }
  197. });
  198. 1module.exports = exports = Middleware;
base/broadcast.js
Coverage7.94 SLOC171 LOC63 Missed58
  1. 1var func = require("./functions"),
  2. obj = require("./object");
  3. 1var comb = exports;
  4. 1var wrapper = function() {
  5. 0 return function() {
  6. 0 var c = arguments.callee, listeners = c.__listeners, func = c.func, r;
  7. 0 if (func) {
  8. 0 r = func.apply(this, arguments);
  9. }
  10. 0 for (var i = 0; i < listeners.length; i++) {
  11. 0 var lis = listeners[i];
  12. 0 if (lis) {
  13. 0 lis.apply(this, arguments);
  14. }
  15. }
  16. 0 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. 0 if (handle && handle.length == 3) {
  28. 0 var obj = handle[0], method = handle[1], cb = handle[2];
  29. 0 if (typeof method != "string") throw "comb.disconnect : When calling disconnect the method must be string";
  30. 0 var scope = obj || global, ls;
  31. 0 if (typeof scope[method] == "function") {
  32. 0 ls = scope[method].__listeners;
  33. 0 if (ls && cb-- > 0) {
  34. //we dont want to splice it because our indexing will get off
  35. 0 ls[cb] = null;
  36. }
  37. } else {
  38. 0 throw new Error("unknown method " + method + " in object " + obj);
  39. }
  40. } else {
  41. 0 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. 0 var index;
  61. 0 if (typeof method != "string") throw new Error("When calling connect the method must be string");
  62. 0 if (!func.isFunction(cb)) throw new Error("When calling connect callback must be a string");
  63. 0 var scope = obj || global, listeners, newMethod;
  64. 0 if (typeof scope[method] == "function") {
  65. 0 listeners = scope[method].__listeners;
  66. 0 if (!listeners) {
  67. 0 newMethod = wrapper();
  68. 0 newMethod.func = obj[method];
  69. 0 listeners = (newMethod.__listeners = []);
  70. 0 scope[method] = newMethod;
  71. }
  72. 0 index = listeners.push(cb);
  73. } else {
  74. 0 throw new Error("unknow method " + method + " in object " + obj);
  75. }
  76. 0 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. 0 var args = Array.prototype.slice.call(arguments);
  96. 0 var topic = args.splice(0, 1)[0];
  97. 0 if (topic) {
  98. 0 var list = listeners[topic];
  99. 0 if (list) {
  100. 0 for (var i = list.length - 1; i >= 0; i--) {
  101. 0 var han = list[i], cb = han.cb;
  102. 0 if (cb) {
  103. 0 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. 0 if (!func.isFunction(callback)) throw new Error("callback must be a function");
  125. 0 var handle = {
  126. topic : topic,
  127. cb : callback,
  128. pos : null
  129. };
  130. 0 var list = listeners[topic];
  131. 0 if (!list) {
  132. 0 list = (listeners[topic] = []);
  133. }
  134. 0 list.push(handle);
  135. 0 handle.pos = list.length - 1;
  136. 0 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. 0 if (handle) {
  145. 0 var topic = handle.topic, list = listeners[topic];
  146. 0 if (list) {
  147. 0 for (var i = list.length - 1; i >= 0; i--) {
  148. 0 if (list[i] == handle) {
  149. 0 list.splice(i, 1);
  150. }
  151. }
  152. 0 if (!list.length) {
  153. 0 delete listeners[topic];
  154. }
  155. }
  156. }
  157. }
  158. });
collections/MaxHeap.js
Coverage8.00 SLOC66 LOC25 Missed23
  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. 0 var heap = this.__heap;
  25. 0 var node = heap[index];
  26. 0 while (index >= 0) {
  27. 0 var parentIndex = this.__getParentIndex(index), parent = heap[parentIndex];
  28. 0 if (parent && parent.key < node.key) {
  29. 0 heap[index] = parent;
  30. 0 index = parentIndex;
  31. } else {
  32. 0 break;
  33. }
  34. }
  35. 0 heap[index] = node;
  36. },
  37. __downHeap : function(index) {
  38. 0 var heap = this.__heap;
  39. 0 var node = heap[index], length = heap.length;
  40. 0 while (index < Math.floor(length / 2)) {
  41. 0 var leftIndex = this.__getLeftChildIndex(index),
  42. rightIndex = this.__getRightChildIndex(index), left = heap[leftIndex], right = heap[rightIndex], child, childIndex;
  43. 0 if (rightIndex < length && right.key < left.key) {
  44. 0 childIndex = leftIndex;
  45. 0 child = left
  46. } else {
  47. 0 childIndex = leftIndex;
  48. 0 child = heap[leftIndex];
  49. }
  50. 0 if(child.key > node.key){
  51. 0 heap[index] = child;
  52. 0 index = childIndex;
  53. }else{
  54. 0 break;
  55. }
  56. }
  57. 0 heap[index] = node;
  58. }
  59. }
  60. });
logging/appenders/fileAppender.js
Coverage9.09 SLOC83 LOC22 Missed20
  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. 0 options = options || {};
  51. 0 !options.name && (options.name = "fileAppender");
  52. 0 this.__file = options.file || "./log.log";
  53. 0 this.__encoding = options.encoding || "utf8";
  54. 0 this.__overwrite = options.overwrite || false;
  55. 0 this.__writeStream = options.writeStream || fs.createWriteStream(this.__file, { flags:this.__overwrite ? "w" : 'a', encoding:this.__encoding});
  56. 0 this._super([options]);
  57. 0 this.__pattern += "\n";
  58. 0 base.listenForExit(base.hitch(this, "__onExit"));
  59. },
  60. __onExit:function () {
  61. 0 var ret = new Promise();
  62. 0 var ws = this.__writeStream;
  63. 0 this.__writeStream = null;
  64. 0 ws.on("close", base.hitch(ret, "callback"));
  65. 0 ws.destroySoon();
  66. 0 return ret.promise();
  67. },
  68. append:function (event) {
  69. 0 var ws = this.__writeStream;
  70. 0 if (this._canAppend(event) && ws && ws.writable) {
  71. 0 var message = format(this.__pattern, event);
  72. 0 var level = event.level;
  73. 0 ws.write(message);
  74. }
  75. }
  76. }
  77. }).registerType("FileAppender").as(module);
logging/appenders/jsonAppender.js
Coverage10.53 SLOC85 LOC19 Missed17
  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. 0 options = options || {};
  55. 0 this.name = options.name || "JSONAppender";
  56. 0 this.__count = 0;
  57. 0 this.__file = options.file || "./log.json";
  58. 0 this.__encoding = options.encoding || "utf8";
  59. 0 this.__writeStream = options.writeStream || fs.createWriteStream(this.__file, { flags:"w", encoding:this.__encoding});
  60. 0 this.__writeStream.write("[\n");
  61. 0 this.level = options.level;
  62. //explicit overwrite of patter
  63. 0 this.__pattern = '{"timestamp" : "{timeStamp}", "level" : "{levelName}", "name" : "{name}", "message" : "{message}"}';
  64. 0 base.listenForExit(base.hitch(this, "__onExit"));
  65. },
  66. append:function (event) {
  67. 0 if (this._canAppend(event)) {
  68. 0 event.message = event.message.replace(/\n+/g, "\\n");
  69. 0 var message = (this.__count ? ",\n" : "\n") + format(this.__pattern, event);
  70. 0 this.__writeStream.write(message);
  71. 0 this.__count++;
  72. }
  73. },
  74. __onExit:function () {
  75. 0 this.__writeStream.write("]");
  76. 0 this._super(arguments);
  77. }
  78. }
  79. }).registerType("JSONAppender").as(module);
collections/RedBlackTree.js
Coverage10.89 SLOC188 LOC101 Missed90
  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. 0 return node != null && node.red;
  8. };
  9. 1var makeNode = function(data, parent) {
  10. 0 return {
  11. data : data,
  12. red : true,
  13. left : null,
  14. right : null
  15. }
  16. };
  17. 1var insert = function(root, data, compare) {
  18. 0 if (root == null) {
  19. 0 return makeNode(data, null);
  20. } else {
  21. 0 var cmp = compare(data, root.data);
  22. 0 if (cmp) {
  23. 0 var dir = cmp == -1 ? "left" : "right";
  24. 0 var otherDir = dir == "left" ? "right" : "left";
  25. 0 root[dir] = insert(root[dir], data, compare);
  26. 0 var node = root[dir];
  27. 0 if (isRed(node)) {
  28. 0 var sibling = root[otherDir];
  29. 0 if (isRed(sibling)) {
  30. /* Case 1 */
  31. 0 root.red = true;
  32. 0 node.red = false;
  33. 0 sibling.red = false;
  34. } else {
  35. 0 if (isRed(node[dir])) {
  36. 0 root = rotateSingle(root, otherDir);
  37. 0 } else if (isRed(node[otherDir])) {
  38. 0 root = rotateDouble(root, otherDir);
  39. }
  40. }
  41. }
  42. }
  43. }
  44. 0 return root;
  45. };
  46. 1var rotateSingle = function(root, dir) {
  47. 0 var otherDir = dir == "left" ? "right" : "left";
  48. 0 var save = root[otherDir];
  49. 0 root[otherDir] = save[dir];
  50. 0 save[dir] = root;
  51. 0 root.red = true;
  52. 0 save.red = false;
  53. 0 return save;
  54. };
  55. 1var rotateDouble = function(root, dir) {
  56. 0 var otherDir = dir == "left" ? "right" : "left";
  57. 0 root[otherDir] = rotateSingle(root[otherDir], otherDir);
  58. 0 return rotateSingle(root, dir);
  59. };
  60. 1var remove = function (root, data, done, compare) {
  61. 0 if (root == null) {
  62. 0 done.done = true;
  63. } else {
  64. 0 var dir;
  65. 0 if (compare(data, root.data) == 0) {
  66. 0 if (root.left == null || root.right == null) {
  67. 0 var save = root[root.left == null ? "right" : "left"];
  68. /* Case 0 */
  69. 0 if (isRed(root)) {
  70. 0 done.done = true;
  71. 0 } else if (isRed(save)) {
  72. 0 save.red = false;
  73. 0 done.done = true;
  74. }
  75. 0 return save;
  76. }
  77. else {
  78. 0 var heir = root.right, p;
  79. 0 while (heir.left != null) {
  80. 0 p = heir;
  81. 0 heir = heir.left;
  82. }
  83. 0 p && (p.left = null);
  84. 0 root.data = heir.data;
  85. 0 data = heir.data;
  86. }
  87. }
  88. 0 dir = compare(data, root.data) == -1 ? "left" : "right";
  89. 0 root[dir] = remove(root[dir], data, done, compare);
  90. 0 !done.done && (root = removeBalance(root, dir, done));
  91. }
  92. 0 return root;
  93. };
  94. 1var removeBalance = function(root, dir, done) {
  95. 0 var notDir = dir == "left" ? "right" : "left";
  96. 0 var p = root, s = p[notDir];
  97. 0 if (isRed(s)) {
  98. 0 root = rotateSingle(root, dir);
  99. 0 s = p[notDir];
  100. }
  101. 0 if (s != null) {
  102. 0 if (!isRed(s.left) && !isRed(s.right)) {
  103. 0 isRed(p) && (done.done = true);
  104. 0 p.red = 0;
  105. 0 s.red = 1;
  106. } else {
  107. 0 var save = p.red, newRoot = ( root === p );
  108. 0 p = (isRed(s[notDir]) ? rotateSingle : rotateDouble)(p, dir);
  109. 0 p.red = save;
  110. 0 p.left.red = p.right.red = 0;
  111. 0 if (newRoot) {
  112. 0 root = p;
  113. } else {
  114. 0 root[dir] = p;
  115. }
  116. 0 done.done = true;
  117. }
  118. }
  119. 0 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. 0 this.__root = insert(this.__root, data, this.compare);
  143. 0 this.__root.red = false;
  144. },
  145. remove : function(data) {
  146. 0 var done = {done : false};
  147. 0 var root = remove(this.__root, data, done, this.compare);
  148. 0 if (root != null)
  149. 0 root.red = 0;
  150. 0 this.__root = root;
  151. },
  152. __printNode : function(node, level) {
  153. 0 var str = [];
  154. 0 if (node == null || node == undefined) {
  155. 0 str.push(multiply('\t', level));
  156. 0 str.push("~");
  157. 0 console.log(str.join(""));
  158. } else {
  159. 0 this.__printNode(node.right, level + 1);
  160. 0 str.push(multiply('\t', level));
  161. 0 str.push((node.red ? "RED" : "BLACK") + ":" + node.data + "\n");
  162. 0 console.log(str.join(""));
  163. 0 this.__printNode(node.left, level + 1);
  164. }
  165. }
  166. }
  167. });
collections/AVLTree.js
Coverage11.00 SLOC203 LOC100 Missed89
  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. 0 return {
  8. data : data,
  9. balance : 0,
  10. left : null,
  11. right : null
  12. }
  13. };
  14. 1var rotateSingle = function(root, dir, otherDir) {
  15. 0 var save = root[otherDir];
  16. 0 root[otherDir] = save[dir];
  17. 0 save[dir] = root;
  18. 0 return save;
  19. };
  20. 1var rotateDouble = function(root, dir, otherDir) {
  21. 0 root[otherDir] = rotateSingle(root[otherDir], otherDir, dir);
  22. 0 return rotateSingle(root, dir, otherDir);
  23. };
  24. 1var adjustBalance = function(root, dir, bal) {
  25. 0 var otherDir = dir == "left" ? "right" : "left";
  26. 0 var n = root[dir], nn = n[otherDir];
  27. 0 if (nn.balance == 0)
  28. 0 root.balance = n.balance = 0;
  29. 0 else if (nn.balance == bal) {
  30. 0 root.balance = -bal;
  31. 0 n.balance = 0;
  32. }
  33. else { /* nn.balance == -bal */
  34. 0 root.balance = 0;
  35. 0 n.balance = bal;
  36. }
  37. 0 nn.balance = 0;
  38. };
  39. 1var insertAdjustBalance = function(root, dir) {
  40. 0 var otherDir = dir == "left" ? "right" : "left";
  41. 0 var n = root[dir];
  42. 0 var bal = dir == "left" ? -1 : +1;
  43. 0 if (n.balance == bal) {
  44. 0 root.balance = n.balance = 0;
  45. 0 root = rotateSingle(root, otherDir, dir);
  46. }
  47. else {
  48. 0 adjustBalance(root, dir, bal);
  49. 0 root = rotateDouble(root, otherDir, dir);
  50. }
  51. 0 return root;
  52. };
  53. 1var removeAdjustBalance = function(root, dir, done) {
  54. 0 var otherDir = dir == "left" ? "right" : "left";
  55. 0 var n = root[otherDir];
  56. 0 var bal = dir == "left" ? -1 : 1;
  57. 0 if (n.balance == -bal) {
  58. 0 root.balance = n.balance = 0;
  59. 0 root = rotateSingle(root, dir, otherDir);
  60. }
  61. 0 else if (n.balance == bal) {
  62. 0 adjustBalance(root, otherDir, -bal);
  63. 0 root = rotateDouble(root, dir, otherDir);
  64. }
  65. else { /* n.balance == 0 */
  66. 0 root.balance = -bal;
  67. 0 n.balance = bal;
  68. 0 root = rotateSingle(root, dir, otherDir);
  69. 0 done.done = true;
  70. }
  71. 0 return root;
  72. };
  73. 1var insert = function(root, data, done, compare) {
  74. 0 if (root == null || root == undefined)
  75. 0 root = makeNode(data);
  76. else {
  77. 0 var dir = compare(data, root.data) == -1 ? "left" : "right";
  78. 0 root[dir] = insert(root[dir], data, done, compare);
  79. 0 if (!done.done) {
  80. /* Update balance factors */
  81. 0 root.balance += dir == "left" ? -1 : 1;
  82. /* Rebalance as necessary and terminate */
  83. 0 if (root.balance == 0)
  84. 0 done.done = true;
  85. 0 else if (abs(root.balance) > 1) {
  86. 0 root = insertAdjustBalance(root, dir);
  87. 0 done.done = true;
  88. }
  89. }
  90. }
  91. 0 return root;
  92. };
  93. 1var remove = function(root, data, done, compare) {
  94. 0 var dir, cmp, save, b;
  95. 0 if (root) {
  96. //Remove node
  97. 0 cmp = compare(data, root.data);
  98. 0 if (cmp === 0) {
  99. // Unlink and fix parent
  100. 0 var l = root.left, r = root.right;
  101. 0 if (!l || !r) {
  102. 0 dir = !l ? "right" : "left";
  103. 0 save = root[dir];
  104. 0 return save;
  105. }
  106. else {
  107. 0 var heir = l, r;
  108. 0 while ((r = heir.right) != null) {
  109. 0 heir = r;
  110. }
  111. 0 root.data = heir.data;
  112. //reset and start searching
  113. 0 data = heir.data;
  114. }
  115. }
  116. 0 dir = compare(root.data, data) == -1 ? "right" : "left";
  117. 0 root[dir] = remove(root[dir], data, done, compare);
  118. 0 if (!done.done) {
  119. /* Update balance factors */
  120. 0 b = (root.balance += (dir == "left" ? 1 : -1));
  121. /* Terminate or rebalance as necessary */
  122. 0 var a = abs(b);
  123. 0 if (a === 1)
  124. 0 done.done = true;
  125. 0 else if (a > 1)
  126. 0 root = removeAdjustBalance(root, dir, done);
  127. }
  128. }
  129. 0 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. 0 var done = {done : false};
  157. 0 this.__root = insert(this.__root, data, done, this.compare);
  158. },
  159. remove : function(data) {
  160. 0 this.__root = remove(this.__root, data, {done : false}, this.compare);
  161. },
  162. __printNode : function(node, level) {
  163. 0 var str = [];
  164. 0 if (node == null) {
  165. 0 str.push(multiply('\t', level));
  166. 0 str.push("~");
  167. 0 console.log(str.join(""));
  168. } else {
  169. 0 this.__printNode(node.right, level + 1);
  170. 0 str.push(multiply('\t', level));
  171. 0 str.push(node.data + ":" + node.balance + "\n");
  172. 0 console.log(str.join(""));
  173. 0 this.__printNode(node.left, level + 1);
  174. }
  175. }
  176. }
  177. });
base/string.js
Coverage11.45 SLOC1187 LOC131 Missed116
  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. 11699 var undef;
  20. 11699 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. 0 var match = format.match(STR_FORMAT), ret = string, cString = comb.string;
  29. 0 if (match) {
  30. 0 var isLeftJustified = match[1], padChar = match[3], width = match[4];
  31. 0 if (width) {
  32. 0 width = parseInt(width);
  33. 0 if (ret.length < width) {
  34. 0 ret = cString.pad(ret, width, padChar, isLeftJustified);
  35. } else {
  36. 0 ret = cString.truncate(ret, width);
  37. }
  38. }
  39. }
  40. 0 return ret;
  41. };
  42. 1var formatNumber = function (number, format) {
  43. 0 if (typeof number == "number") {
  44. 0 var cString = comb.string, ret = "" + number;
  45. 0 var match = format.match(STR_FORMAT);
  46. 0 if (match) {
  47. 0 var isLeftJustified = match[1], signed = match[2], padChar = match[3], width = match[4];
  48. 0 if (signed) {
  49. 0 ret = (number > 0 ? "+" : "") + ret;
  50. }
  51. 0 if (width) {
  52. 0 width = parseInt(width);
  53. 0 if (ret.length < width) {
  54. 0 ret = cString.pad(ret, width, padChar || "0", isLeftJustified);
  55. } else {
  56. 0 ret = cString.truncate(ret, width);
  57. }
  58. }
  59. }
  60. } else {
  61. 0 throw new Error("comb.string.format : when using %d the parameter must be a number!");
  62. }
  63. 0 return ret;
  64. };
  65. 1var formatObject = function (object, format) {
  66. 0 var ret, match = format.match(OBJECT_FORMAT), spacing = 0;
  67. 0 if (match) {
  68. 0 spacing = parseInt(match[0]);
  69. 0 if (isNaN(spacing)) spacing = 0;
  70. }
  71. 0 try {
  72. 0 ret = JSON.stringify(object, null, spacing);
  73. } catch (e) {
  74. 0 throw new Error("comb.string.format : Unable to parse json from ", object);
  75. }
  76. 0 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. 0 string = "" + string; //check for numbers
  870. 0 ch = ch || " ";
  871. 0 var strLen = string.length;
  872. 0 while (strLen < length) {
  873. 0 if (end) {
  874. 0 string += ch;
  875. } else {
  876. 0 string = ch + string;
  877. }
  878. 0 strLen++;
  879. }
  880. 0 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. 0 var ret = string;
  902. 0 if (comb.isString(ret)) {
  903. 0 if (string.length > length) {
  904. 0 if (end) {
  905. 0 var l = string.length;
  906. 0 ret = string.substring(l - length, l);
  907. } else {
  908. 0 ret = string.substring(0, length);
  909. }
  910. }
  911. } else {
  912. 0 ret = comb.string.truncate("" + ret, length);
  913. }
  914. 0 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. 0 !date && (date = require("./date"));
  985. 0 if (obj instanceof Array) {
  986. 0 var i = 0, len = obj.length;
  987. //find the matches
  988. 0 return str.replace(FORMAT_REGEX, function (m, format, type) {
  989. 0 var replacer, ret;
  990. 0 if (i < len) {
  991. 0 replacer = obj[i++];
  992. } else {
  993. //we are out of things to replace with so
  994. //just return the match?
  995. 0 return m;
  996. }
  997. 0 if (m == "%s" || m == "%d" || m == "%D") {
  998. //fast path!
  999. 0 ret = replacer + "";
  1000. 0 } else if (m == "%Z") {
  1001. 0 ret = replacer.toUTCString();
  1002. 0 } else if (m == "%j") {
  1003. 0 try {
  1004. 0 ret = JSON.stringify(replacer);
  1005. } catch (e) {
  1006. 0 throw new Error("comb.string.format : Unable to parse json from ", replacer);
  1007. }
  1008. } else {
  1009. 0 format = format.replace(/^\[|\]$/g, "");
  1010. 0 switch (type) {
  1011. case "s":
  1012. 0 ret = formatString(replacer, format);
  1013. 0 break;
  1014. case "d":
  1015. 0 ret = formatNumber(replacer, format);
  1016. 0 break;
  1017. case "j":
  1018. 0 ret = formatObject(replacer, format);
  1019. 0 break;
  1020. case "D":
  1021. 0 ret = date.date.format(replacer, format);
  1022. 0 break;
  1023. case "Z":
  1024. 0 ret = date.date.format(replacer, format, true);
  1025. 0 break;
  1026. }
  1027. }
  1028. 0 return ret;
  1029. });
  1030. 0 } else if (isHash(obj)) {
  1031. 0 return str.replace(INTERP_REGEX, function (m, format, value) {
  1032. 0 value = obj[value];
  1033. 0 if (!misc.isUndefined(value)) {
  1034. 0 if (format) {
  1035. 0 if (comb.isString(value)) {
  1036. 0 return formatString(value, format);
  1037. 0 } else if (typeof value == "number") {
  1038. 0 return formatNumber(value, format);
  1039. 0 } else if (date.isDate(value)) {
  1040. 0 return date.date.format(value, format);
  1041. 0 } else if (typeof value == "object") {
  1042. 0 return formatObject(value, format);
  1043. }
  1044. } else {
  1045. 0 return "" + value;
  1046. }
  1047. }
  1048. 0 return m;
  1049. });
  1050. } else {
  1051. 0 var args = Array.prototype.slice.call(arguments).slice(1);
  1052. 0 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. 0 var ret = [];
  1069. 0 if (testStr) {
  1070. 0 if (testStr.indexOf(delim) > 0) return testStr.replace(/\s+/g, "").split(delim);
  1071. 0 else return [testStr];
  1072. }
  1073. 0 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. 0 var ret = [];
  1086. 0 if (times) {
  1087. 0 for (var i = 0; i < times; i++) {
  1088. 0 ret.push(str);
  1089. }
  1090. }
  1091. 0 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. 0 var ret = str;
  1134. 0 if (options) {
  1135. 0 if (ret instanceof Array) {
  1136. 0 ret = ret.map(function (s) {
  1137. 0 return comb.string.style(s, options);
  1138. })
  1139. 0 } else if (options instanceof Array) {
  1140. 0 options.forEach(function (option) {
  1141. 0 ret = comb.string.style(ret, option);
  1142. });
  1143. 0 } else if (options in styles) {
  1144. 0 ret = '\x1B[' + styles[options] + 'm' + str + '\x1B[0m';
  1145. }
  1146. }
  1147. 0 return ret;
  1148. }
  1149. };
collections/MinHeap.js
Coverage11.54 SLOC65 LOC26 Missed23
  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. 0 var heap = this.__heap;
  25. 0 var node = heap[index], key = node.key, gpi = this.__getParentIndex;
  26. 0 while (index >= 0) {
  27. 0 var parentIndex = gpi(index), parent = heap[parentIndex];
  28. 0 if (parent && parent.key > key) {
  29. 0 heap[index] = parent;
  30. 0 index = parentIndex;
  31. } else {
  32. 0 break;
  33. }
  34. }
  35. 0 heap[index] = node;
  36. },
  37. __downHeap : function(index) {
  38. 0 var heap = this.__heap;
  39. 0 var node = heap[index], key = node.key, length = heap.length, max = floor(length / 2), glci = this.__getLeftChildIndex, grci = this.__getRightChildIndex;
  40. 0 while (index < max) {
  41. 0 var leftIndex = glci(index),
  42. rightIndex = grci(index), left = heap[leftIndex], right = heap[rightIndex], child, childIndex;
  43. 0 if (rightIndex < length && right.key < left.key) {
  44. 0 childIndex = rightIndex;
  45. 0 child = right;
  46. } else {
  47. 0 childIndex = leftIndex;
  48. 0 child = left;
  49. }
  50. 0 if (child.key < key) {
  51. 0 heap[index] = child;
  52. 0 index = childIndex;
  53. } else {
  54. 0 break;
  55. }
  56. }
  57. 0 heap[index] = node;
  58. }
  59. }
  60. });
plugins/Broadcaster.js
Coverage11.54 SLOC113 LOC26 Missed23
  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. 0 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. 0 var args = Array.prototype.slice.call(arguments, 0), topic = args.shift();
  52. 0 if (topic && topic in this.__listeners) {
  53. 0 var list = this.__listeners[topic], i = list.length - 1;
  54. 0 while (i >= 0) {
  55. 0 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. 0 if (!func.isFunction(callback)) throw new Error("callback must be a function");
  70. 0 var handle = {
  71. topic : topic,
  72. cb : callback
  73. };
  74. 0 var list = this.__listeners[topic];
  75. 0 if (!list) {
  76. 0 list = (this.__listeners[topic] = [handle]);
  77. 0 handle.pos = 0;
  78. } else {
  79. 0 handle.pos = list.push(handle);
  80. }
  81. 0 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. 0 if (handle) {
  91. 0 var topic = handle.topic;
  92. 0 if (topic in this.__listeners) {
  93. 0 var listeners = this.__listeners, list = listeners[topic];
  94. 0 if (list) {
  95. 0 for (var i = list.length - 1; i >= 0; i--) {
  96. 0 if (list[i] == handle) {
  97. 0 list.splice(i, 1);
  98. 0 break;
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }
  105. }
  106. });
  107. 1exports = module.exports = Broadcaster;
logging/appenders/consoleAppender.js
Coverage11.76 SLOC37 LOC17 Missed15
  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. 0 options = options || {};
  13. 0 !options.name && (options.name = "consoleAppender");
  14. 0 this._super(arguments, [options]);
  15. },
  16. append:function (event) {
  17. 0 if (this._canAppend(event)) {
  18. 0 var message = format(this.__pattern, event);
  19. 0 var level = event.level;
  20. 0 if (Level.ERROR.equals(level) || Level.FATAL.equals(level)) {
  21. 0 console.log(style(message, "red"));
  22. 0 } else if (Level.WARN.equals(level)) {
  23. 0 console.log(style(message, "yellow"));
  24. 0 } else if (Level.DEBUG.equals(level)) {
  25. 0 console.log(style(message, "magenta"));
  26. 0 } else if (Level.TRACE.equals(level)) {
  27. 0 console.log(style(message, "cyan"));
  28. } else {
  29. 0 console.log(message);
  30. }
  31. }
  32. }
  33. }
  34. }).registerType("ConsoleAppender").as(module);
collections/AnderssonTree.js
Coverage11.84 SLOC165 LOC76 Missed67
  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. 0 return {
  9. data:data,
  10. level:level,
  11. left:nil,
  12. right:nil
  13. }
  14. };
  15. 1var skew = function (root) {
  16. 0 if (root.level != 0 && root.left.level == root.level) {
  17. 0 var save = root.left;
  18. 0 root.left = save.right;
  19. 0 save.right = root;
  20. 0 root = save;
  21. }
  22. 0 return root;
  23. };
  24. 1var split = function (root) {
  25. 0 if (root.level != 0 && root.right.right.level == root.level) {
  26. 0 var save = root.right;
  27. 0 root.right = save.left;
  28. 0 save.left = root;
  29. 0 root = save;
  30. 0 ++root.level;
  31. }
  32. 0 return root;
  33. };
  34. 1var insert = function (root, data, compare) {
  35. 0 if (root == nil) {
  36. 0 root = makeNode(data, 1);
  37. }
  38. else {
  39. 0 var dir = compare(data, root.data) == -1 ? "left" : "right";
  40. 0 root[dir] = insert(root[dir], data, compare);
  41. 0 root = skew(root);
  42. 0 root = split(root);
  43. }
  44. 0 return root;
  45. };
  46. 1var remove = function (root, data, compare) {
  47. 0 var rLeft, rRight;
  48. 0 if (root != nil) {
  49. 0 var cmp = compare(data, root.data);
  50. 0 if (cmp == 0) {
  51. 0 rLeft = root.left, rRight = root.right;
  52. 0 if (rLeft != nil && rRight != nil) {
  53. 0 var heir = rLeft;
  54. 0 while (heir.right != nil)
  55. 0 heir = heir.right;
  56. 0 root.data = heir.data;
  57. 0 root.left = remove(rLeft, heir.data, compare);
  58. } else {
  59. 0 root = root[rLeft == nil ? "right" : "left"];
  60. }
  61. } else {
  62. 0 var dir = cmp == -1 ? "left" : "right";
  63. 0 root[dir] = remove(root[dir], data, compare);
  64. }
  65. }
  66. 0 if (root != nil) {
  67. 0 var rLevel = root.level;
  68. 0 var rLeftLevel = root.left.level, rRightLevel = root.right.level;
  69. 0 if (rLeftLevel < rLevel - 1 || rRightLevel < rLevel - 1) {
  70. 0 if (rRightLevel > --root.level)
  71. 0 root.right.level = root.level;
  72. 0 root = skew(root);
  73. 0 root = split(root);
  74. }
  75. }
  76. 0 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. 0 return this.__root == nil || this._super(arguments);
  100. },
  101. insert:function (data) {
  102. 0 if (this.__root == null) this.__root = nil;
  103. 0 this.__root = insert(this.__root, data, this.compare);
  104. },
  105. remove:function (data) {
  106. 0 this.__root = remove(this.__root, data, this.compare);
  107. },
  108. traverseWithCondition:function (node, order, callback) {
  109. 0 var cont = true;
  110. 0 if (node != nil) {
  111. 0 return this._super(arguments);
  112. }
  113. 0 return cont;
  114. },
  115. traverse:function (node, order, callback) {
  116. 0 if (node != nil) {
  117. 0 this._super(arguments);
  118. }
  119. },
  120. contains:function (value) {
  121. 0 if (this.__root != nil) {
  122. 0 return this._super(arguments);
  123. }
  124. 0 return false;
  125. },
  126. __printNode:function (node, level) {
  127. 0 var str = [];
  128. 0 if (node.data == null || node == null) {
  129. 0 str.push(multiply('\t', level));
  130. 0 str.push("~");
  131. 0 console.log(str.join(""));
  132. } else {
  133. 0 this.__printNode(node.right, level + 1);
  134. 0 str.push(multiply('\t', level));
  135. 0 str.push(node.data + ":" + node.level + "\n");
  136. 0 console.log(str.join(""));
  137. 0 this.__printNode(node.left, level + 1);
  138. }
  139. }
  140. }
  141. });
base/date/index.js
Coverage13.95 SLOC885 LOC294 Missed253
  1. 1var string = require("./../string").string,
  2. transforms = require("./transforms.js"),
  3. addTransform = transforms.addTransform,
  4. differenceTransform = transforms.differenceTransform;
  5. /**
  6. * @ignore
  7. * Based on DOJO Date Implementation
  8. *
  9. * Dojo is available under *either* the terms of the modified BSD license *or* the
  10. * Academic Free License version 2.1. As a recipient of Dojo, you may choose which
  11. * license to receive this code under (except as noted in per-module LICENSE
  12. * files). Some modules may not be the copyright of the Dojo Foundation. These
  13. * modules contain explicit declarations of copyright in both the LICENSE files in
  14. * the directories in which they reside and in the code itself. No external
  15. * contributions are allowed under licenses which are fundamentally incompatible
  16. * with the AFL or BSD licenses that Dojo is distributed under.
  17. *
  18. */
  19. 1var floor = Math.floor, round = Math.round, min = Math.min, pow = Math.pow, ceil = Math.ceil, abs = Math.abs;
  20. 1var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  21. 1var monthAbbr = ["Jan.", "Feb.", "Mar.", "Apr.", "May.", "Jun.", "Jul.", "Aug.", "Sep.", "Oct.", "Nov.", "Dec."];
  22. 1var monthLetter = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
  23. 1var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  24. 1var dayAbbr = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  25. 1var dayLetter = ["S", "M", "T", "W", "T", "F", "S"];
  26. 1var eraNames = ["Before Christ", "Anno Domini"];
  27. 1var eraAbbr = ["BC", "AD"];
  28. 1var eraLetter = ["B", "A"];
  29. 1var comb = exports, date;
  30. /**
  31. * Determines if obj is a Date
  32. *
  33. * @param {Anything} obj the thing to test if it is a Date
  34. * @memberOf comb
  35. * @returns {Boolean} true if it is a Date false otherwise
  36. */
  37. 1comb.isDate = function (obj) {
  38. 0 var undef;
  39. 0 return (obj !== undef && typeof obj === "object" && obj instanceof Date);
  40. };
  41. 1function getDayOfYear(/*Date*/dateObject, utc) {
  42. // summary: gets the day of the year as represented by dateObject
  43. 0 return date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject, null, utc) + 1; // Number
  44. }
  45. 1function getWeekOfYear(/*Date*/dateObject, /*Number*/firstDayOfWeek, utc) {
  46. 0 firstDayOfWeek = firstDayOfWeek || 0;
  47. 0 var fullYear = dateObject[utc ? "getUTCFullYear" : "getFullYear"]();
  48. 0 var firstDayOfYear = new Date(fullYear, 0, 1).getDay(),
  49. adj = (firstDayOfYear - firstDayOfWeek + 7) % 7,
  50. week = floor((getDayOfYear(dateObject) + adj - 1) / 7);
  51. // if year starts on the specified day, start counting weeks at 1
  52. 0 if (firstDayOfYear === firstDayOfWeek) {
  53. 0 week++;
  54. }
  55. 0 return week; // Number
  56. }
  57. 1function getTimezoneName(/*Date*/dateObject) {
  58. 0 var str = dateObject.toString();
  59. 0 var tz = '';
  60. 0 var pos = str.indexOf('(');
  61. 0 if (pos > -1) {
  62. 0 tz = str.substring(++pos, str.indexOf(')'));
  63. }
  64. 0 return tz; // String
  65. }
  66. 1function buildDateEXP(pattern, tokens) {
  67. 0 return pattern.replace(/([a-z])\1*/ig,function (match) {
  68. // Build a simple regexp. Avoid captures, which would ruin the tokens list
  69. 0 var s,
  70. c = match.charAt(0),
  71. l = match.length,
  72. p2 = '', p3 = '';
  73. 0 p2 = '0?';
  74. 0 p3 = '0{0,2}';
  75. 0 if (c === 'y') {
  76. 0 s = '\\d{2,4}';
  77. 0 } else if (c === "M") {
  78. 0 s = (l > 2) ? '\\S+?' : '1[0-2]|' + p2 + '[1-9]';
  79. 0 } else if (c === "D") {
  80. 0 s = '[12][0-9][0-9]|3[0-5][0-9]|36[0-6]|' + p3 + '[1-9][0-9]|' + p2 + '[1-9]';
  81. 0 } else if (c === "d") {
  82. 0 s = '3[01]|[12]\\d|' + p2 + '[1-9]';
  83. 0 } else if (c === "w") {
  84. 0 s = '[1-4][0-9]|5[0-3]|' + p2 + '[1-9]';
  85. 0 } else if (c === "E") {
  86. 0 s = '\\S+';
  87. 0 } else if (c === "h") {
  88. 0 s = '1[0-2]|' + p2 + '[1-9]';
  89. 0 } else if (c === "K") {
  90. 0 s = '1[01]|' + p2 + '\\d';
  91. 0 } else if (c === "H") {
  92. 0 s = '1\\d|2[0-3]|' + p2 + '\\d';
  93. 0 } else if (c === "k") {
  94. 0 s = '1\\d|2[0-4]|' + p2 + '[1-9]';
  95. 0 } else if (c === "m" || c === "s") {
  96. 0 s = '[0-5]\\d';
  97. 0 } else if (c === "S") {
  98. 0 s = '\\d{' + l + '}';
  99. 0 } else if (c === "a") {
  100. 0 var am = 'AM', pm = 'PM';
  101. 0 s = am + '|' + pm;
  102. 0 if (am !== am.toLowerCase()) {
  103. 0 s += '|' + am.toLowerCase();
  104. }
  105. 0 if (pm !== pm.toLowerCase()) {
  106. 0 s += '|' + pm.toLowerCase();
  107. }
  108. 0 s = s.replace(/\./g, "\\.");
  109. 0 } else if (c === 'v' || c === 'z' || c === 'Z' || c === 'G' || c === 'q' || c === 'Q') {
  110. 0 s = ".*";
  111. } else {
  112. 0 s = c === " " ? "\\s*" : c + "*";
  113. }
  114. 0 if (tokens) {
  115. 0 tokens.push(match);
  116. }
  117. 0 return "(" + s + ")"; // add capture
  118. }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE.
  119. }
  120. /**
  121. * @namespace Utilities for Dates
  122. */
  123. 1comb.date = {
  124. /**@lends comb.date*/
  125. /**
  126. * Returns the number of days in the month of a date
  127. *
  128. * @example
  129. *
  130. * comb.date.getDaysInMonth(new Date(2006, 1, 1)); //28
  131. * comb.date.getDaysInMonth(new Date(2004, 1, 1)); //29
  132. * comb.date.getDaysInMonth(new Date(2006, 2, 1)); //31
  133. * comb.date.getDaysInMonth(new Date(2006, 3, 1)); //30
  134. * comb.date.getDaysInMonth(new Date(2006, 4, 1)); //31
  135. * comb.date.getDaysInMonth(new Date(2006, 5, 1)); //30
  136. * comb.date.getDaysInMonth(new Date(2006, 6, 1)); //31
  137. * @param {Date} dateObject the date containing the month
  138. * @return {Number} the number of days in the month
  139. */
  140. getDaysInMonth:function (/*Date*/dateObject) {
  141. // summary:
  142. // Returns the number of days in the month used by dateObject
  143. 0 var month = dateObject.getMonth();
  144. 0 var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  145. 0 if (month === 1 && date.isLeapYear(dateObject)) {
  146. 0 return 29;
  147. } // Number
  148. 0 return days[month]; // Number
  149. },
  150. /**
  151. * Determines if a date is a leap year
  152. *
  153. * @example
  154. *
  155. * comb.date.isLeapYear(new Date(1600, 0, 1)); //true
  156. * comb.date.isLeapYear(new Date(2004, 0, 1)); //true
  157. * comb.date.isLeapYear(new Date(2000, 0, 1)); //true
  158. * comb.date.isLeapYear(new Date(2006, 0, 1)); //false
  159. * comb.date.isLeapYear(new Date(1900, 0, 1)); //false
  160. * comb.date.isLeapYear(new Date(1800, 0, 1)); //false
  161. * comb.date.isLeapYear(new Date(1700, 0, 1)); //false
  162. *
  163. * @param {Date} dateObject
  164. * @returns {Boolean} true if it is a leap year false otherwise
  165. */
  166. isLeapYear:function (/*Date*/dateObject, utc) {
  167. 0 var year = dateObject[utc ? "getUTCFullYear" : "getFullYear"]();
  168. 0 return (year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0);
  169. },
  170. /**
  171. * Determines if a date is on a weekend
  172. *
  173. * @example
  174. *
  175. * var thursday = new Date(2006, 8, 21);
  176. * var saturday = new Date(2006, 8, 23);
  177. * var sunday = new Date(2006, 8, 24);
  178. * var monday = new Date(2006, 8, 25);
  179. * comb.date.isWeekend(thursday)); //false
  180. * comb.date.isWeekend(saturday); //true
  181. * comb.date.isWeekend(sunday); //true
  182. * comb.date.isWeekend(monday)); //false
  183. *
  184. * @param {Date} dateObject the date to test
  185. *
  186. * @returns {Boolean} true if the date is a weekend
  187. */
  188. isWeekend:function (/*Date?*/dateObject, utc) {
  189. // summary:
  190. // Determines if the date falls on a weekend, according to local custom.
  191. 0 var day = (dateObject || new Date())[utc ? "getUTCDay" : "getDay"]();
  192. 0 return day === 0 || day === 6;
  193. },
  194. /**
  195. * Get the timezone of a date
  196. *
  197. * @example
  198. * //just setting the strLocal to simulate the toString() of a date
  199. * dt.str = 'Sun Sep 17 2006 22:25:51 GMT-0500 (CDT)';
  200. * //just setting the strLocal to simulate the locale
  201. * dt.strLocale = 'Sun 17 Sep 2006 10:25:51 PM CDT';
  202. * comb.date.getTimezoneName(dt); //'CDT'
  203. * dt.str = 'Sun Sep 17 2006 22:57:18 GMT-0500 (CDT)';
  204. * dt.strLocale = 'Sun Sep 17 22:57:18 2006';
  205. * comb.date.getTimezoneName(dt); //'CDT'
  206. * @param dateObject the date to get the timezone from
  207. *
  208. * @returns {String} the timezone of the date
  209. */
  210. getTimezoneName:getTimezoneName,
  211. /**
  212. * Compares two dates
  213. *
  214. * @example
  215. *
  216. * var d1 = new Date();
  217. * d1.setHours(0);
  218. * comb.date.compare(d1, d1); // 0
  219. *
  220. * var d1 = new Date();
  221. * d1.setHours(0);
  222. * var d2 = new Date();
  223. * d2.setFullYear(2005);
  224. * d2.setHours(12);
  225. * comb.date.compare(d1, d2, "date"); // 1
  226. * comb.date.compare(d1, d2, "datetime"); // 1
  227. *
  228. * var d1 = new Date();
  229. * d1.setHours(0);
  230. * var d2 = new Date();
  231. * d2.setFullYear(2005);
  232. * d2.setHours(12);
  233. * comb.date.compare(d2, d1, "date"); // -1
  234. * comb.date.compare(d1, d2, "time"); //-1
  235. *
  236. * @param {Date|String} date1 the date to comapare
  237. * @param {Date|String} [date2=new Date()] the date to compare date1 againse
  238. * @param {"date"|"time"|"datetime"} portion compares the portion specified
  239. *
  240. * @returns -1 if date1 is < date2 0 if date1 === date2 1 if date1 > date2
  241. */
  242. compare:function (/*Date*/date1, /*Date*/date2, /*String*/portion) {
  243. 0 date1 = new Date(date1);
  244. 0 date2 = new Date((date2 || new Date()));
  245. 0 if (portion === "date") {
  246. // Ignore times and compare dates.
  247. 0 date1.setHours(0, 0, 0, 0);
  248. 0 date2.setHours(0, 0, 0, 0);
  249. 0 } else if (portion === "time") {
  250. // Ignore dates and compare times.
  251. 0 date1.setFullYear(0, 0, 0);
  252. 0 date2.setFullYear(0, 0, 0);
  253. }
  254. 0 return date1 > date2 ? 1 : date1 < date2 ? -1 : 0;
  255. },
  256. /**
  257. * Adds a specified interval and amount to a date
  258. *
  259. * @example
  260. * var dtA = new Date(2005, 11, 27);
  261. * comb.date.add(dtA, "year", 1); //new Date(2006, 11, 27);
  262. * comb.date.add(dtA, "years", 1); //new Date(2006, 11, 27);
  263. *
  264. * dtA = new Date(2000, 0, 1);
  265. * comb.date.add(dtA, "quarter", 1); //new Date(2000, 3, 1);
  266. * comb.date.add(dtA, "quarters", 1); //new Date(2000, 3, 1);
  267. *
  268. * dtA = new Date(2000, 0, 1);
  269. * comb.date.add(dtA, "month", 1); //new Date(2000, 1, 1);
  270. * comb.date.add(dtA, "months", 1); //new Date(2000, 1, 1);
  271. *
  272. * dtA = new Date(2000, 0, 31);
  273. * comb.date.add(dtA, "month", 1); //new Date(2000, 1, 29);
  274. * comb.date.add(dtA, "months", 1); //new Date(2000, 1, 29);
  275. *
  276. * dtA = new Date(2000, 0, 1);
  277. * comb.date.add(dtA, "week", 1); //new Date(2000, 0, 8);
  278. * comb.date.add(dtA, "weeks", 1); //new Date(2000, 0, 8);
  279. *
  280. * dtA = new Date(2000, 0, 1);
  281. * comb.date.add(dtA, "day", 1); //new Date(2000, 0, 2);
  282. *
  283. * dtA = new Date(2000, 0, 1);
  284. * comb.date.add(dtA, "weekday", 1); //new Date(2000, 0, 3);
  285. *
  286. * dtA = new Date(2000, 0, 1, 11);
  287. * comb.date.add(dtA, "hour", 1); //new Date(2000, 0, 1, 12);
  288. *
  289. * dtA = new Date(2000, 11, 31, 23, 59);
  290. * comb.date.add(dtA, "minute", 1); //new Date(2001, 0, 1, 0, 0);
  291. *
  292. * dtA = new Date(2000, 11, 31, 23, 59, 59);
  293. * comb.date.add(dtA, "second", 1); //new Date(2001, 0, 1, 0, 0, 0);
  294. *
  295. * dtA = new Date(2000, 11, 31, 23, 59, 59, 999);
  296. * comb.date.add(dtA, "millisecond", 1); //new Date(2001, 0, 1, 0, 0, 0, 0);
  297. *
  298. * @param {Date} date
  299. * @param {String} interval the interval to add
  300. * <ul>
  301. * <li>day | days</li>
  302. * <li>weekday | weekdays</li>
  303. * <li>year | years</li>
  304. * <li>week | weeks</li>
  305. * <li>quarter | quarters</li>
  306. * <li>months | months</li>
  307. * <li>hour | hours</li>
  308. * <li>minute | minutes</li>
  309. * <li>second | seconds</li>
  310. * <li>millisecond | milliseconds</li>
  311. * </ul>
  312. * @param {Number} [amount=0] the amount to add
  313. */
  314. add:function (/*Date*/date, /*String*/interval, /*int*/amount) {
  315. 13 var res = addTransform(interval, date, amount || 0);
  316. 13 amount = res[0];
  317. 13 var property = res[1];
  318. 13 var sum = new Date(date);
  319. 13 var fixOvershoot = res[2];
  320. 13 if (property) {
  321. 13 sum["set" + property](sum["get" + property]() + amount);
  322. }
  323. 13 if (fixOvershoot && (sum.getDate() < date.getDate())) {
  324. 0 sum.setDate(0);
  325. }
  326. 13 return sum; // Date
  327. },
  328. /**
  329. * Finds the difference between two dates based on the specified interval
  330. *
  331. * @example
  332. *
  333. * var dtA, dtB;
  334. *
  335. * dtA = new Date(2005, 11, 27);
  336. * dtB = new Date(2006, 11, 27);
  337. * comb.date.difference(dtA, dtB, "year"); //1
  338. *
  339. * dtA = new Date(2000, 1, 29);
  340. * dtB = new Date(2001, 2, 1);
  341. * comb.date.difference(dtA, dtB, "quarter"); //4
  342. * comb.date.difference(dtA, dtB, "month"); //13
  343. *
  344. * dtA = new Date(2000, 1, 1);
  345. * dtB = new Date(2000, 1, 8);
  346. * comb.date.difference(dtA, dtB, "week"); //1
  347. *
  348. * dtA = new Date(2000, 1, 29);
  349. * dtB = new Date(2000, 2, 1);
  350. * comb.date.difference(dtA, dtB, "day"); //1
  351. *
  352. * dtA = new Date(2006, 7, 3);
  353. * dtB = new Date(2006, 7, 11);
  354. * comb.date.difference(dtA, dtB, "weekday"); //6
  355. *
  356. * dtA = new Date(2000, 11, 31, 23);
  357. * dtB = new Date(2001, 0, 1, 0);
  358. * comb.date.difference(dtA, dtB, "hour"); //1
  359. *
  360. * dtA = new Date(2000, 11, 31, 23, 59);
  361. * dtB = new Date(2001, 0, 1, 0, 0);
  362. * comb.date.difference(dtA, dtB, "minute"); //1
  363. *
  364. * dtA = new Date(2000, 11, 31, 23, 59, 59);
  365. * dtB = new Date(2001, 0, 1, 0, 0, 0);
  366. * comb.date.difference(dtA, dtB, "second"); //1
  367. *
  368. * dtA = new Date(2000, 11, 31, 23, 59, 59, 999);
  369. * dtB = new Date(2001, 0, 1, 0, 0, 0, 0);
  370. * comb.date.difference(dtA, dtB, "millisecond"); //1
  371. *
  372. *
  373. * @param {Date} date1
  374. * @param {Date} [date2 = new Date()]
  375. * @param {String} [interval = "day"] the intercal to find the difference of.
  376. * <ul>
  377. * <li>day | days</li>
  378. * <li>weekday | weekdays</li>
  379. * <li>year | years</li>
  380. * <li>week | weeks</li>
  381. * <li>quarter | quarters</li>
  382. * <li>months | months</li>
  383. * <li>hour | hours</li>
  384. * <li>minute | minutes</li>
  385. * <li>second | seconds</li>
  386. * <li>millisecond | milliseconds</li>
  387. * </ul>
  388. */
  389. difference:function (/*Date*/date1, /*Date?*/date2, /*String*/interval, utc) {
  390. 0 date2 = date2 || new Date();
  391. 0 interval = interval || "day";
  392. 0 return differenceTransform(interval, date1, date2, utc);
  393. },
  394. /**
  395. * Parses a date string into a date object
  396. *
  397. * @example
  398. * var aug_11_2006 = new Date(2006, 7, 11, 0);
  399. * comb.date.parse("08/11/06", "MM/dd/yy"); //aug_11_2006
  400. * comb.date.parse("11Aug2006", 'ddMMMyyyy'); //aug_11_2006
  401. * comb.date.parse("Aug2006", 'MMMyyyy'); //new Date(2006, 7, 1)
  402. * comb.date.parse("Aug 11, 2006", "MMM dd, yyyy"); //aug_11_2006
  403. * comb.date.parse("August 11, 2006", "MMMM dd, yyyy"); //aug_11_2006
  404. * comb.date.parse("Friday, August 11, 2006", "EEEE, MMMM dd, yyyy"); //aug_11_2006
  405. *
  406. * @param {String} dateStr The string to parse
  407. * @param {String} format the format of the date composed of the following options
  408. * <ul>
  409. * <li> G Era designator Text AD</li>
  410. * <li> y Year Year 1996; 96</li>
  411. * <li> M Month in year Month July; Jul; 07</li>
  412. * <li> w Week in year Number 27</li>
  413. * <li> W Week in month Number 2</li>
  414. * <li> D Day in year Number 189</li>
  415. * <li> d Day in month Number 10</li>
  416. * <li> E Day in week Text Tuesday; Tue</li>
  417. * <li> a Am/pm marker Text PM</li>
  418. * <li> H Hour in day (0-23) Number 0</li>
  419. * <li> k Hour in day (1-24) Number 24</li>
  420. * <li> K Hour in am/pm (0-11) Number 0</li>
  421. * <li> h Hour in am/pm (1-12) Number 12</li>
  422. * <li> m Minute in hour Number 30</li>
  423. * <li> s Second in minute Number 55</li>
  424. * <li> S Millisecond Number 978</li>
  425. * <li> z Time zone General time zone Pacific Standard Time; PST; GMT-08:00</li>
  426. * <li> Z Time zone RFC 822 time zone -0800 </li>
  427. * </ul>
  428. *
  429. * @returns {Date} the parsed date
  430. *
  431. *
  432. */
  433. parse:function (dateStr, format) {
  434. 0 if (!format) {
  435. 0 throw new Error('format required when calling comb.date.parse');
  436. }
  437. 0 var tokens = [], regexp = buildDateEXP(format, tokens),
  438. re = new RegExp("^" + regexp + "$", "i"),
  439. match = re.exec(dateStr);
  440. 0 if (!match) {
  441. 0 return null;
  442. } // null
  443. 0 var result = [1970, 0, 1, 0, 0, 0, 0], // will get converted to a Date at the end
  444. amPm = "",
  445. valid = match.every(function (v, i) {
  446. 0 if (i) {
  447. 0 var token = tokens[i - 1];
  448. 0 var l = token.length, type = token.charAt(0);
  449. 0 if (type === 'y') {
  450. 0 if (v < 100) {
  451. 0 v = parseInt(v, 10);
  452. //choose century to apply, according to a sliding window
  453. //of 80 years before and 20 years after present year
  454. 0 var year = '' + new Date().getFullYear(),
  455. century = year.substring(0, 2) * 100,
  456. cutoff = min(year.substring(2, 4) + 20, 99);
  457. 0 result[0] = (v < cutoff) ? century + v : century - 100 + v;
  458. } else {
  459. 0 result[0] = v;
  460. }
  461. 0 } else if (type === "M") {
  462. 0 if (l > 2) {
  463. 0 var months = monthNames;
  464. 0 if (l === 3) {
  465. 0 months = monthAbbr;
  466. }
  467. //Tolerate abbreviating period in month part
  468. //Case-insensitive comparison
  469. 0 v = v.replace(".", "").toLowerCase();
  470. 0 months = months.map(function (s) {
  471. 0 return s.replace(".", "").toLowerCase();
  472. });
  473. 0 if ((v = months.indexOf(v)) === -1) {
  474. 0 return false;
  475. }
  476. } else {
  477. 0 v--;
  478. }
  479. 0 result[1] = v;
  480. 0 } else if (type === "E" || type === "e") {
  481. 0 var days = dayNames;
  482. 0 if (l === 3) {
  483. 0 days = dayAbbr;
  484. }
  485. //Case-insensitive comparison
  486. 0 v = v.toLowerCase();
  487. 0 days = days.map(function (d) {
  488. 0 return d.toLowerCase();
  489. });
  490. 0 var d = days.indexOf(v);
  491. 0 if (d === -1) {
  492. 0 v = parseInt(v, 10);
  493. 0 if (isNaN(v) || v > days.length) {
  494. 0 return false;
  495. }
  496. } else {
  497. 0 v = d;
  498. }
  499. 0 } else if (type === 'D' || type === "d") {
  500. 0 if (type === "D") {
  501. 0 result[1] = 0;
  502. }
  503. 0 result[2] = v;
  504. 0 } else if (type === "a") {
  505. 0 var am = "am";
  506. 0 var pm = "pm";
  507. 0 var period = /\./g;
  508. 0 v = v.replace(period, '').toLowerCase();
  509. // we might not have seen the hours field yet, so store the state and apply hour change later
  510. 0 amPm = (v === pm) ? 'p' : (v === am) ? 'a' : '';
  511. 0 } else if (type === "k" || type === "h" || type === "H" || type === "K") {
  512. 0 if (type === "k" && (+v) === 24) {
  513. 0 v = 0;
  514. }
  515. 0 result[3] = v;
  516. 0 } else if (type === "m") {
  517. 0 result[4] = v;
  518. 0 } else if (type === "s") {
  519. 0 result[5] = v;
  520. 0 } else if (type === "S") {
  521. 0 result[6] = v;
  522. }
  523. }
  524. 0 return true;
  525. });
  526. 0 if (valid) {
  527. 0 var hours = +result[3];
  528. //account for am/pm
  529. 0 if (amPm === 'p' && hours < 12) {
  530. 0 result[3] = hours + 12; //e.g., 3pm -> 15
  531. 0 } else if (amPm === 'a' && hours === 12) {
  532. 0 result[3] = 0; //12am -> 0
  533. }
  534. 0 var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
  535. 0 var dateToken = (tokens.indexOf('d') !== -1),
  536. monthToken = (tokens.indexOf('M') !== -1),
  537. month = result[1],
  538. day = result[2],
  539. dateMonth = dateObject.getMonth(),
  540. dateDay = dateObject.getDate();
  541. 0 if ((monthToken && dateMonth > month) || (dateToken && dateDay > day)) {
  542. 0 return null;
  543. }
  544. 0 return dateObject; // Date
  545. } else {
  546. 0 return null;
  547. }
  548. },
  549. /**
  550. * Formats a date to the specidifed format string
  551. *
  552. * @example
  553. *
  554. * var date = new Date(2006, 7, 11, 0, 55, 12, 345);
  555. * comb.date.format(date, "EEEE, MMMM dd, yyyy"); //"Friday, August 11, 2006"
  556. * comb.date.format(date, "M/dd/yy"); //"8/11/06"
  557. * comb.date.format(date, "E"); //"6"
  558. * comb.date.format(date, "h:m a"); //"12:55 AM"
  559. * comb.date.format(date, 'h:m:s'); //"12:55:12"
  560. * comb.date.format(date, 'h:m:s.SS'); //"12:55:12.35"
  561. * comb.date.format(date, 'k:m:s.SS'); //"24:55:12.35"
  562. * comb.date.format(date, 'H:m:s.SS'); //"0:55:12.35"
  563. * comb.date.format(date, "ddMMyyyy"); //"11082006"
  564. *
  565. * @param date the date to format
  566. * @param {String} format the format of the date composed of the following options
  567. * <ul>
  568. * <li> G Era designator Text AD</li>
  569. * <li> y Year Year 1996; 96</li>
  570. * <li> M Month in year Month July; Jul; 07</li>
  571. * <li> w Week in year Number 27</li>
  572. * <li> W Week in month Number 2</li>
  573. * <li> D Day in year Number 189</li>
  574. * <li> d Day in month Number 10</li>
  575. * <li> E Day in week Text Tuesday; Tue</li>
  576. * <li> a Am/pm marker Text PM</li>
  577. * <li> H Hour in day (0-23) Number 0</li>
  578. * <li> k Hour in day (1-24) Number 24</li>
  579. * <li> K Hour in am/pm (0-11) Number 0</li>
  580. * <li> h Hour in am/pm (1-12) Number 12</li>
  581. * <li> m Minute in hour Number 30</li>
  582. * <li> s Second in minute Number 55</li>
  583. * <li> S Millisecond Number 978</li>
  584. * <li> z Time zone General time zone Pacific Standard Time; PST; GMT-08:00</li>
  585. * <li> Z Time zone RFC 822 time zone -0800 </li>
  586. * </ul>
  587. */
  588. format:function (date, format, utc) {
  589. 0 utc = utc || false;
  590. 0 var fullYear, month, day, d, hour, minute, second, millisecond;
  591. 0 if (utc) {
  592. 0 fullYear = date.getUTCFullYear();
  593. 0 month = date.getUTCMonth();
  594. 0 day = date.getUTCDay();
  595. 0 d = date.getUTCDate();
  596. 0 hour = date.getUTCHours();
  597. 0 minute = date.getUTCMinutes();
  598. 0 second = date.getUTCSeconds();
  599. 0 millisecond = date.getUTCMilliseconds();
  600. } else {
  601. 0 fullYear = date.getFullYear();
  602. 0 month = date.getMonth();
  603. 0 d = date.getDate();
  604. 0 day = date.getDay();
  605. 0 hour = date.getHours();
  606. 0 minute = date.getMinutes();
  607. 0 second = date.getSeconds();
  608. 0 millisecond = date.getMilliseconds();
  609. }
  610. 0 return format.replace(/([A-Za-z])\1*/g, function (match, options) {
  611. 0 var s, pad, h,
  612. c = match.charAt(0),
  613. l = match.length;
  614. 0 if (c === 'd') {
  615. 0 s = "" + d;
  616. 0 pad = true;
  617. 0 } else if (c === "H" && !s) {
  618. 0 s = "" + hour;
  619. 0 pad = true;
  620. 0 } else if (c === 'm' && !s) {
  621. 0 s = "" + minute;
  622. 0 pad = true;
  623. 0 } else if (c === 's') {
  624. 0 if (!s) {
  625. 0 s = "" + second;
  626. }
  627. 0 pad = true;
  628. 0 } else if (c === "G") {
  629. 0 s = ((l < 4) ? eraAbbr : eraNames)[fullYear < 0 ? 0 : 1];
  630. 0 } else if (c === "y") {
  631. 0 s = fullYear;
  632. 0 if (l > 1) {
  633. 0 if (l === 2) {
  634. 0 s = string.truncate("" + s, 2, true);
  635. } else {
  636. 0 pad = true;
  637. }
  638. }
  639. 0 } else if (c.toUpperCase() === "Q") {
  640. 0 s = ceil((month + 1) / 3);
  641. 0 pad = true;
  642. 0 } else if (c === "M") {
  643. 0 if (l < 3) {
  644. 0 s = month + 1;
  645. 0 pad = true;
  646. } else {
  647. 0 s = (l === 3 ? monthAbbr : monthNames)[month];
  648. }
  649. 0 } else if (c === "w") {
  650. 0 s = getWeekOfYear(date, 0, utc);
  651. 0 pad = true;
  652. 0 } else if (c === "D") {
  653. 0 s = getDayOfYear(date, utc);
  654. 0 pad = true;
  655. 0 } else if (c === "E") {
  656. 0 if (l < 3) {
  657. 0 s = day + 1;
  658. 0 pad = true;
  659. } else {
  660. 0 s = (l === -3 ? dayAbbr : dayNames)[day];
  661. }
  662. 0 } else if (c === 'a') {
  663. 0 s = (hour < 12) ? 'AM' : 'PM';
  664. 0 } else if (c === "h") {
  665. 0 s = (hour % 12) || 12;
  666. 0 pad = true;
  667. 0 } else if (c === "K") {
  668. 0 s = (hour % 12);
  669. 0 pad = true;
  670. 0 } else if (c === "k") {
  671. 0 s = hour || 24;
  672. 0 pad = true;
  673. 0 } else if (c === "S") {
  674. 0 s = round(millisecond * pow(10, l - 3));
  675. 0 pad = true;
  676. 0 } else if (c === "z" || c === "v" || c === "Z") {
  677. 0 s = getTimezoneName(date);
  678. 0 if ((c === "z" || c === "v") && !s) {
  679. 0 l = 4;
  680. }
  681. 0 if (!s || c === "Z") {
  682. 0 var offset = date.getTimezoneOffset();
  683. 0 var tz = [
  684. (offset >= 0 ? "-" : "+"),
  685. string.pad(floor(abs(offset) / 60), 2, "0"),
  686. string.pad(abs(offset) % 60, 2, "0")
  687. ];
  688. 0 if (l === 4) {
  689. 0 tz.splice(0, 0, "GMT");
  690. 0 tz.splice(3, 0, ":");
  691. }
  692. 0 s = tz.join("");
  693. }
  694. } else {
  695. 0 s = match;
  696. }
  697. 0 if (pad) {
  698. 0 s = string.pad(s, l, '0');
  699. }
  700. 0 return s;
  701. });
  702. }
  703. };
  704. 1date = comb.date;
  705. /**
  706. * Adds the specified year/s to the current date.
  707. *
  708. * @example
  709. *
  710. * //assuming that current year is 2012
  711. * comb.yearsFromNow(1); //2013-mm-dd hh:MM:ss
  712. *
  713. * @param {Number} val the number of years to add
  714. *
  715. * @return {Date} a date with the number of years added
  716. */
  717. 1comb.yearsFromNow = function (val) {
  718. 0 return date.add(new Date(), "years", val);
  719. };
  720. /**
  721. * Subtracts the specified year/s from the current date.
  722. *
  723. * @param {Number} val the number of years to subtract
  724. *
  725. * @return {Date} a date with the number of years subtracted
  726. */
  727. 1comb.yearsAgo = function (val) {
  728. 0 return date.add(new Date(), "years", -val);
  729. };
  730. /**
  731. * Adds the specified month/s to the current date.
  732. *
  733. * @example
  734. *
  735. * //assuming that current month is february
  736. * comb.yearsFromNow(2); //yyyy-04-dd hh:MM:ss
  737. *
  738. * @param {Number} val the number of months to add
  739. *
  740. * @return {Date} a date with the number of years added
  741. */
  742. 1comb.monthsFromNow = function (val) {
  743. 0 return date.add(new Date(), "months", val);
  744. };
  745. /**
  746. * Subtracts the specified month/s from the current date.
  747. *
  748. * @param {Number} val the number of months to subtract
  749. *
  750. * @return {Date} a date with the number of months subtracted
  751. */
  752. 1comb.monthsAgo = function (val) {
  753. 0 return date.add(new Date(), "months", -val);
  754. };
  755. /**
  756. * Adds the specified day/s to the current date.
  757. *
  758. * @param {Number} val the number of days to add
  759. *
  760. * @return {Date} a date with the number of days added
  761. */
  762. 1comb.daysFromNow = function (val) {
  763. 13 return date.add(new Date(), "days", val);
  764. };
  765. /**
  766. * Subtracts the specified day/s from the current date.
  767. *
  768. * @param {Number} val the number of days to subtract
  769. *
  770. * @return {Date} a date with the number of days subtracted
  771. */
  772. 1comb.daysAgo = function (val) {
  773. 0 return date.add(new Date(), "days", -val);
  774. };
  775. /**
  776. * Adds the specified hour/s to the current date.
  777. *
  778. * @param {Number} val the number of hours to add
  779. *
  780. * @return {Date} a date with the number of hours added
  781. */
  782. 1comb.hoursFromNow = function (val) {
  783. 0 return date.add(new Date(), "hours", val);
  784. };
  785. /**
  786. * Subtracts the specified hour/s from the current date.
  787. *
  788. * @param {Number} val the number of hours to subtract
  789. *
  790. * @return {Date} a date with the number of hours subtracted
  791. */
  792. 1comb.hoursAgo = function (val) {
  793. 0 return date.add(new Date(), "hours", -val);
  794. };
  795. /**
  796. * Adds the specified minute/s to the current date.
  797. *
  798. * @param {Number} val the number of minutes to add
  799. *
  800. * @return {Date} a date with the number of minutes added
  801. */
  802. 1comb.minutesFromNow = function (val) {
  803. 0 return date.add(new Date(), "minutes", val);
  804. };
  805. /**
  806. * Subtracts the specified minute/s from the current date.
  807. *
  808. * @param {Number} val the number of minutes to subtract
  809. *
  810. * @return {Date} a date with the number of minutes subtracted
  811. */
  812. 1comb.minutesAgo = function (val) {
  813. 0 return date.add(new Date(), "minutes", -val);
  814. };
  815. /**
  816. * Adds the specified second/s to the current date.
  817. *
  818. * @param {Number} val the number of seconds to add
  819. *
  820. * @return {Date} a date with the number of seconds added
  821. */
  822. 1comb.secondsFromNow = function (val) {
  823. 0 return date.add(new Date(), "seconds", val);
  824. };
  825. /**
  826. * Subtracts the specified second/s from the current date.
  827. *
  828. * @param {Number} val the number of seconds to subtract
  829. *
  830. * @return {Date} a date with the number of seconds subtracted
  831. */
  832. 1comb.secondsAgo = function (val) {
  833. 0 return date.add(new Date(), "seconds", -val);
  834. };
base/date/transforms.js
Coverage14.67 SLOC148 LOC75 Missed64
  1. 1var floor = Math.floor, round = Math.round, min = Math.min, pow = Math.pow, ceil = Math.ceil, abs = Math.abs;
  2. 1var addMap = {
  3. day:function addDay(date, amount) {
  4. 13 return [amount, "Date", false];
  5. },
  6. weekday:function addWeekday(date, amount) {
  7. // Divide the increment time span into weekspans plus leftover days
  8. // e.g., 8 days is one 5-day weekspan / and two leftover days
  9. // Can't have zero leftover days, so numbers divisible by 5 get
  10. // a days value of 5, and the remaining days make up the number of weeks
  11. 0 var days, weeks, mod = amount % 5, strt = date.getDay(), adj = 0;
  12. 0 if (!mod) {
  13. 0 days = (amount > 0) ? 5 : -5;
  14. 0 weeks = (amount > 0) ? ((amount - 5) / 5) : ((amount + 5) / 5);
  15. } else {
  16. 0 days = mod;
  17. 0 weeks = parseInt(amount / 5, 10);
  18. }
  19. 0 if (strt === 6 && amount > 0) {
  20. 0 adj = 1;
  21. 0 } else if (strt === 0 && amount < 0) {
  22. // Orig date is Sun / negative increment
  23. // Jump back over Sat
  24. 0 adj = -1;
  25. }
  26. // Get weekday val for the new date
  27. 0 var trgt = strt + days;
  28. // New date is on Sat or Sun
  29. 0 if (trgt === 0 || trgt === 6) {
  30. 0 adj = (amount > 0) ? 2 : -2;
  31. }
  32. // Increment by number of weeks plus leftover days plus
  33. // weekend adjustments
  34. 0 return [(7 * weeks) + days + adj, "Date", false];
  35. },
  36. year:function addYear(date, amount) {
  37. 0 return [amount, "FullYear", true];
  38. },
  39. week:function addWeek(date, amount) {
  40. 0 return [amount * 7, "Date", false];
  41. },
  42. quarter:function addYear(date, amount) {
  43. 0 return [amount * 3, "Month", true];
  44. },
  45. month:function addYear(date, amount) {
  46. 0 return [amount, "Month", true];
  47. }
  48. };
  49. 1function addTransform(interval, date, amount) {
  50. 13 interval = interval.replace(/s$/, "");
  51. 13 if (addMap.hasOwnProperty(interval)) {
  52. 13 return addMap[interval](date, amount);
  53. }
  54. 0 return [amount, "UTC" + interval.charAt(0).toUpperCase() + interval.substring(1) + "s", false];
  55. }
  56. 1var differenceMap = {
  57. "quarter":function quarterDifference(date1, date2, utc) {
  58. 0 var yearDiff = date2.getFullYear() - date1.getFullYear();
  59. 0 var m1 = date1[utc ? "getUTCMonth" : "getMonth"]();
  60. 0 var m2 = date2[utc ? "getUTCMonth" : "getMonth"]();
  61. // Figure out which quarter the months are in
  62. 0 var q1 = floor(m1 / 3) + 1;
  63. 0 var q2 = floor(m2 / 3) + 1;
  64. // Add quarters for any year difference between the dates
  65. 0 q2 += (yearDiff * 4);
  66. 0 return q2 - q1;
  67. },
  68. "weekday":function weekdayDifference(date1, date2, utc) {
  69. 0 var days = differenceTransform("day", date1, date2, utc), weeks;
  70. 0 var mod = days % 7;
  71. // Even number of weeks
  72. 0 if (mod === 0) {
  73. 0 days = differenceTransform("week", date1, date2, utc) * 5;
  74. } else {
  75. // Weeks plus spare change (< 7 days)
  76. 0 var adj = 0, aDay = date1[utc ? "getUTCDay" : "getDay"](), bDay = date2[utc ? "getUTCDay" : "getDay"]();
  77. 0 weeks = parseInt(days / 7, 10);
  78. // Mark the date advanced by the number of
  79. // round weeks (may be zero)
  80. 0 var dtMark = new Date(date1);
  81. 0 dtMark.setDate(dtMark[utc ? "getUTCDate" : "getDate"]() + (weeks * 7));
  82. 0 var dayMark = dtMark[utc ? "getUTCDay" : "getDay"]();
  83. // Spare change days -- 6 or less
  84. 0 if (days > 0) {
  85. 0 if (aDay === 6 || bDay === 6) {
  86. 0 adj = -1;
  87. 0 } else if (aDay === 0) {
  88. 0 adj = 0;
  89. 0 } else if (bDay === 0 || (dayMark + mod) > 5) {
  90. 0 adj = -2;
  91. }
  92. 0 } else if (days < 0) {
  93. 0 if (aDay === 6) {
  94. 0 adj = 0;
  95. 0 } else if (aDay === 0 || bDay === 0) {
  96. 0 adj = 1;
  97. 0 } else if (bDay === 6 || (dayMark + mod) < 0) {
  98. 0 adj = 2;
  99. }
  100. }
  101. 0 days += adj;
  102. 0 days -= (weeks * 2);
  103. }
  104. 0 return days;
  105. },
  106. year:function (date1, date2) {
  107. 0 return date2.getFullYear() - date1.getFullYear();
  108. },
  109. month:function (date1, date2, utc) {
  110. 0 var m1 = date1[utc ? "getUTCMonth" : "getMonth"]();
  111. 0 var m2 = date2[utc ? "getUTCMonth" : "getMonth"]();
  112. 0 return (m2 - m1) + ((date2.getFullYear() - date1.getFullYear()) * 12);
  113. },
  114. week:function (date1, date2, utc) {
  115. 0 return round(differenceTransform("day", date1, date2, utc) / 7);
  116. },
  117. day:function (date1, date2) {
  118. 0 return 1.1574074074074074e-8 * (date2.getTime() - date1.getTime());
  119. },
  120. hour:function (date1, date2) {
  121. 0 return 2.7777777777777776e-7 * (date2.getTime() - date1.getTime());
  122. },
  123. minute:function (date1, date2) {
  124. 0 return 0.000016666666666666667 * (date2.getTime() - date1.getTime());
  125. },
  126. second:function (date1, date2) {
  127. 0 return 0.001 * (date2.getTime() - date1.getTime());
  128. },
  129. millisecond:function (date1, date2) {
  130. 0 return date2.getTime() - date1.getTime();
  131. }
  132. };
  133. 1function differenceTransform(interval, date1, date2, utc) {
  134. 0 interval = interval.replace(/s$/, "");
  135. 0 return round(differenceMap[interval](date1, date2, utc));
  136. }
  137. 1exports.addTransform = addTransform;
  138. 1exports.differenceTransform = differenceTransform;
logging/config.js
Coverage16.00 SLOC213 LOC50 Missed42
  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. 0 var rootLogger = Logger.getRootLogger();
  8. 0 if (!isProcessUnCaaughtEception) {
  9. 0 process.on("uncaughtException", function (err) {
  10. 0 if (rootLogger.appenders.length) {
  11. 0 rootLogger.error(err);
  12. } else {
  13. 0 console.error(err.stack);
  14. }
  15. });
  16. 0 isProcessUnCaaughtEception = true;
  17. }
  18. //}
  19. };
  20. })();
  21. 1var parseProperties = function (properties) {
  22. 0 for (var i in properties) {
  23. 0 var logger = Logger.getLogger(i);
  24. 0 var props = properties[i], level = props.level, appenderArr = props.appenders;
  25. 0 if (level) {
  26. 0 level = Level.toLevel(level);
  27. 0 if (level) {
  28. 0 logger.level = level;
  29. }
  30. }
  31. 0 if (appenderArr && base.isArray(appenderArr)) {
  32. 0 for (var j = appenderArr.length - 1; j >= 0; j--) {
  33. 0 var appenderProps = base.merge({}, appenderArr[j]), type = appenderProps.type;
  34. 0 appenderProps.type = null;
  35. 0 if (type) {
  36. 0 logger.addAppender(type, appenderProps);
  37. }
  38. }
  39. }
  40. }
  41. 0 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. 0 if (!Logger) {
  56. 0 logging = require("./index").logging;
  57. 0 Logger = logging.Logger;
  58. 0 Level = logging.Level;
  59. 0 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. 0 var rootLogger = Logger.getRootLogger();
  69. 0 rootLogger.removeAllAppenders();
  70. 0 if (base.isInstanceOf(appender, appenders.Appender)) {
  71. 0 rootLogger.addAppender(appender);
  72. } else {
  73. 0 rootLogger.addAppender(new appenders.ConsoleAppender());
  74. }
  75. 0 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. 0 var rootLogger = Logger.getRootLogger();
  187. 0 rootLogger.removeAllAppenders();
  188. 0 if (base.isHash(properties)) {
  189. 0 parseProperties(base.deepMerge({}, properties));
  190. } else {
  191. 0 fs.readFile(properties, function (err, res) {
  192. 0 if (err) {
  193. 0 throw err;
  194. } else {
  195. 0 try {
  196. 0 parseProperties(JSON.parse(res));
  197. } catch (e) {
  198. 0 throw e;
  199. }
  200. }
  201. });
  202. }
  203. }
  204. }
  205. });
extensions/index.js
Coverage16.67 SLOC47 LOC24 Missed20
  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. 0 if (base.isBoolean(obj)) {
  15. 0 return obj ? TRUE : FALSE;
  16. }
  17. 0 if (!base.isUndefinedOrNull(obj) && !obj.__isExtended__) {
  18. 0 obj = isExtension(obj);
  19. 0 obj = castExtension(obj);
  20. 0 if (obj.isObject()) {
  21. 0 obj.object();
  22. }
  23. 0 if (obj.isArguments()) {
  24. 0 obj.args();
  25. }
  26. 0 if (obj.isArray()) {
  27. 0 obj.array();
  28. }
  29. 0 if (obj.isFunction()) {
  30. 0 obj.func();
  31. }
  32. 0 if (obj.isString()) {
  33. 0 obj.string();
  34. }
  35. 0 if (obj.isDate()) {
  36. 0 obj.date();
  37. }
  38. 0 if (obj.isNumber()) {
  39. 0 obj.number();
  40. }
  41. }
  42. 0 return obj;
  43. }
  44. 1exports.createExtension = createExtension;
logging/appenders/appender.js
Coverage20.00 SLOC167 LOC30 Missed24
  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. 0 options = options || {};
  36. 0 this.name = options.name || "appender";
  37. 0 this.pattern = options.pattern || "[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}";
  38. 0 var level = options.level;
  39. 0 if (options.level && (level = Level.toLevel(level))) {
  40. 0 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. 0 throw new Error("abstract method");
  55. },
  56. _canAppend:function (event) {
  57. 0 return !base.isUndefinedOrNull(this.__level) && event.level.isGreaterOrEqualToo(this.__level);
  58. },
  59. /**@ignore*/
  60. setters:{
  61. /**@ignore*/
  62. level:function (level) {
  63. 0 if (level && level instanceof Level) {
  64. 0 this.__level = level;
  65. } else {
  66. //try to get the level
  67. 0 level = Level.toLevel(level);
  68. 0 if (level) {
  69. 0 this.__level = level;
  70. }
  71. }
  72. },
  73. pattern:function (patt) {
  74. 0 if (base.isString(patt)) {
  75. 0 this.__pattern = patt;
  76. }
  77. },
  78. name:function (name) {
  79. 0 if (base.isString(name)) {
  80. 0 this.__name = name;
  81. }
  82. }
  83. },
  84. /**@ignore*/
  85. getters:{
  86. /**@ignore*/
  87. level:function () {
  88. 0 return this.__level;
  89. },
  90. name:function () {
  91. 0 return this.__name;
  92. },
  93. pattern:function () {
  94. 0 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. 4 if (base.isString(type)) {
  119. 4 APPENDER_TYPES[type.toLowerCase()] = this;
  120. }
  121. 4 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. 0 var caseType = type.toLowerCase();
  141. 0 if (caseType in APPENDER_TYPES) {
  142. 0 return new APPENDER_TYPES[caseType](options);
  143. } else {
  144. 0 throw new Error(type + " appender is not registered!");
  145. }
  146. }
  147. }
  148. }).as(module);
collections/Iterable.js
Coverage22.22 SLOC63 LOC9 Missed7
  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. 0 throw new Error("Not Implemented");
  17. },
  18. /**
  19. * Loop through the items in a collection
  20. */
  21. forEach:function () {
  22. 0 throw new Error("Not Implemented");
  23. },
  24. /**
  25. * Determine if every item in a collection meets the criteria
  26. */
  27. every:function () {
  28. 0 throw new Error("Not Implemented");
  29. },
  30. /**
  31. * Map every item in a collection
  32. */
  33. map:function () {
  34. 0 throw new Error("Not Implemented");
  35. },
  36. /**
  37. * Determing if some items in a colleciton meet the criteria
  38. */
  39. some:function () {
  40. 0 throw new Error("Not Implemented");
  41. },
  42. /**
  43. * Reduce a collection
  44. */
  45. reduce:function () {
  46. 0 throw new Error("Not Implemented");
  47. },
  48. /**
  49. * Reduce a collection starting from the right most position
  50. */
  51. reduceRight:function () {
  52. 0 throw new Error("Not Implemented");
  53. }
  54. }
  55. }).as(module);
extensions/cast.js
Coverage23.08 SLOC45 LOC13 Missed10
  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. 0 return arrayExtension(this);
  15. },
  16. date:function () {
  17. 0 return dateExtension(this);
  18. },
  19. args:function () {
  20. 0 return argumentsExtension(this);
  21. },
  22. func:function () {
  23. 0 return functionExtension(this);
  24. },
  25. number:function () {
  26. 0 return numberExtension(this);
  27. },
  28. string:function () {
  29. 0 return stringExtension(this);
  30. },
  31. object:function () {
  32. 0 return objectExtension(this);
  33. }
  34. };
  35. 1module.exports = function (o) {
  36. 0 extend(o, Object.keys(methods), methods, function (name, func) {
  37. 0 return base.partial(func);
  38. });
  39. 0 return o;
  40. };
collections/Collection.js
Coverage25.00 SLOC56 LOC8 Missed6
  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. 0 throw new Error("Not Implemented");
  17. },
  18. /**
  19. * Joins two collections
  20. */
  21. join:function () {
  22. 0 throw new Error("Not Implemented");
  23. },
  24. /**
  25. * Slice a portion from a collection
  26. */
  27. slice:function () {
  28. 0 throw new Error("Not Implemented");
  29. },
  30. /**
  31. * Convert a collection to a string
  32. */
  33. toString:function () {
  34. 0 throw new Error("Not Implemented");
  35. },
  36. /**
  37. * Find the index of an item in a collection
  38. */
  39. indexOf:function () {
  40. 0 throw new Error("Not Implemented");
  41. },
  42. /**
  43. * Find the last index of an item in a collection
  44. */
  45. lastIndexOf:function () {
  46. 0 throw new Error("Not Implemented");
  47. }
  48. }
  49. }).as(module);
extensions/function.js
Coverage25.00 SLOC42 LOC16 Missed12
  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. 0 comb = comb || (require("../index"));
  21. 0 extend(o, methods, base, function (name, func) {
  22. 0 var ret;
  23. 0 if (name !== 'partial' && name !== "applyFirst") {
  24. 0 ret = function (arg1) {
  25. 0 var args = argsToArray(arguments, 1);
  26. 0 return comb(func.apply(null, [arg1, this].concat(args)));
  27. };
  28. } else {
  29. 0 ret = function (arg1) {
  30. 0 return comb(func.apply(null, [this].concat(argsToArray(arguments))));
  31. };
  32. }
  33. 0 return ret;
  34. });
  35. 0 extend(o, baseMethods, base);
  36. 0 return o;
  37. };
logging/index.js
Coverage25.77 SLOC678 LOC163 Missed121
  1. 1var os = require("os"),
  2. define = require("../define.js"),
  3. base = require("../base"),
  4. isString = base.isString,
  5. merge = base.merge,
  6. isUndefinedOrNull = base.isUndefinedOrNull,
  7. isHash = base.isHash,
  8. isInstanceOf = base.isInstanceOf,
  9. argsToArray = base.argsToArray,
  10. format = base.string.format,
  11. Level = require("./level"),
  12. appenders = require("./appenders"),
  13. Appender = appenders.Appender,
  14. configurators = require("./config");
  15. 1var rootTree;
  16. 1var LoggerTree = define.define(null, {
  17. instance: {
  18. constructor: function (root) {
  19. 1 this.__root = root;
  20. 1 this.__name = root.name;
  21. 1 this.__level = root.level;
  22. 1 this.__parent = root._parent;
  23. 1 this.__map = {};
  24. },
  25. __getSubLoggers: function () {
  26. 0 var map = this.__map, ret = [], n;
  27. 0 for (var i in map) {
  28. 0 n = map[i];
  29. 0 if (n) {
  30. 0 ret = ret.concat(n.tree.getCurrentLoggers());
  31. }
  32. }
  33. 0 return ret;
  34. },
  35. __getLoggers: function () {
  36. 0 return [this.__root].concat(this.__getSubLoggers())
  37. },
  38. getCurrentLoggers: function () {
  39. 0 return this.__getLoggers();
  40. },
  41. getSubLoggers: function () {
  42. 0 return this.__getSubLoggers();
  43. },
  44. getLogger: function (name) {
  45. 0 var ret;
  46. 0 if (name) {
  47. 0 var parts = name.split(".");
  48. 0 if (parts.length) {
  49. 0 var category = parts.shift();
  50. 0 var lNode = this.__map[category];
  51. 0 if (!lNode) {
  52. 0 lNode = this.__map[category] = new Logger(category, this);
  53. 0 lNode.addAppenders(this.__root.appenders);
  54. }
  55. 0 ret = lNode;
  56. 0 if (parts.length) {
  57. //keep searching
  58. 0 name = parts.join(".");
  59. 0 ret = lNode.tree.getLogger(name);
  60. }
  61. }
  62. } else {
  63. 0 ret = this.__root;
  64. }
  65. 0 return ret;
  66. },
  67. getRootLogger: function () {
  68. 0 return this.__root;
  69. },
  70. isDisabled: function (level) {
  71. },
  72. resetConfiguration: function () {
  73. },
  74. /**
  75. * level = string|Level
  76. */
  77. addAppender: function (appender) {
  78. 0 var map = this.__map;
  79. 0 for (var i in map) {
  80. 0 map[i].addAppender(appender);
  81. }
  82. },
  83. removeAppender: function (name) {
  84. 0 var map = this.__map;
  85. 0 for (var i in map) {
  86. 0 map[i].removeAppender(name);
  87. }
  88. },
  89. setters: {
  90. level: function (level) {
  91. 1 this.__level = level;
  92. 1 if (level && level instanceof Level) {
  93. 1 var map = this.__map;
  94. 1 for (var i in map) {
  95. 0 map[i].level = level;
  96. }
  97. }
  98. }
  99. },
  100. getters: {
  101. categories: function () {
  102. 0 return this.getCurrentLoggers().map(function (l) {
  103. 0 return l.fullName;
  104. });
  105. },
  106. name: function () {
  107. 1 var ret = this.__name;
  108. 1 if (this.__parent) {
  109. 0 var pName = this.__parent.name;
  110. 0 if (pName) {
  111. 0 ret = pName + "." + ret;
  112. }
  113. }
  114. 1 return ret;
  115. },
  116. level: function () {
  117. 0 return this.__level;
  118. },
  119. additive: function () {
  120. 0 return this.__root.additive;
  121. }
  122. }
  123. }
  124. });
  125. 1var comb = exports;
  126. /**
  127. * @ignore
  128. * @namespace logging package*/
  129. 1comb.logging = merge({
  130. Level: Level
  131. }, configurators);
  132. /**
  133. * @ignore
  134. * @namespace appenders for logging*/
  135. 1comb.logging.appenders = appenders;
  136. 1var logging = comb.logging, hasGetGid = process.hasOwnProperty("getgid");
  137. /**
  138. * @class This class is the entry point for all logging actions in comb.
  139. * <p><b>Logger should be retrieved by calling Logger.getLogger() NOT through the new keyword</b><p>
  140. * <p>
  141. * All loggers in comb follow a heirarchy of inheritance based on a dot notation.
  142. * <pre class="code">
  143. * rootLogger - ""
  144. * / \
  145. * "my" "myOther"
  146. * / \
  147. * "my.logger" "myOther.logger"
  148. * / \
  149. * "my.logger.Log" "myOther.logger.Log"
  150. *
  151. * </pre>
  152. * In the above Tree the rootLogger is the base for all logger. my and myOther inherit from rootLogger
  153. * my.logger inherits from my, and myOther.logger inherits from myOther. The logs do not have to be retrieved in
  154. * order. If I set rootLogger to ERROR level and added a console appender to it the appender and level will be
  155. * added to all logs. However if I set my to INFO level and add a fileAppender to it the level and appender will
  156. * only be added to logs in "my" subtree. If you set my.logger to not be additive then levels, and appenders will not
  157. * propogate down to the rest of the tree.
  158. *
  159. * </p>
  160. *
  161. * <p>For information on levels see {@link comb.logging.Level}.</p>
  162. * <p>For information on appenders see
  163. * <ul>
  164. * <li>{@link comb.logging.appenders.Appender}</li>
  165. * <li>{@link comb.logging.appenders.ConsoleAppender}</li>
  166. * <li>{@link comb.logging.appenders.FileAppender}</li>
  167. * <li>{@link comb.logging.appenders.JSONAppender}</li>
  168. * <li>{@link comb.logging.appenders.RollingFileAppender}</li>
  169. * </ul>
  170. * </p>
  171. * <p>For information on configurators see {@link comb.logging.BasicConfigurator} or {@link comb.logging.PropertyConfigurator}.</p>
  172. *
  173. * @example
  174. *
  175. * var logger = comb.logger;
  176. *
  177. * //configure you logging environement
  178. * logger.configure();
  179. *
  180. * //add a file appender to all loggers
  181. * logger.configure(logger.appender("FileAppender", {file : "/var/log/myLog.log"});
  182. *
  183. * //Retreiving a logger.
  184. * var combLogger = logger("comb");
  185. * var combCollectionLogger = logger("comb.collections");
  186. * var treeLogger = logger("comb.collections.Tree")
  187. * //add a JSON appender to tree logger just for fun!
  188. * .addAppender("JSONAppender", {file : "/var/log/myTreeLogger.json"})
  189. *
  190. * //set my treeLogger to DEBUG Level
  191. * treeLogger.level = "DEBUG";
  192. *
  193. *
  194. * @name Logger
  195. * @memberOf comb.logging
  196. *
  197. * @property {Array<comb.logging.Logger>} subLoggers all loggers this logger is the parent of.
  198. * @property {comb.logging.Level} level the level of this Logger
  199. * @property {Boolean} additive set to false to prevent changes to this logger from propogating down.
  200. * @property {Boolean} isDebug true if this Loggers level is DEBUG
  201. * @property {Boolean} isTrace true if this Loggers level is TRACE
  202. * @property {Boolean} isInfo true if this Loggers level is INFO
  203. * @property {Boolean} isWarn true if this Loggers level is WARN
  204. * @property {Boolean} isError true if this Loggers level is ERROR
  205. * @property {Boolean} isFatal true if this Loggers level is FATAL
  206. * @property {Boolean} isOff true if this Loggers level is OFF
  207. * @property {String} name the name of this logger this <b>does not</b> include the dot notated name
  208. * @property {String} fullName the full path name of this Logger.
  209. * @property {comb.logging.appenders.Appender} appenders list of appenders this logger currently contains.
  210. * @ignoreCode
  211. */
  212. 1var Logger = (logging.Logger = define.define(null, {
  213. instance: {
  214. /**@lends comb.logging.Logger.prototype*/
  215. constructor: function (name, parent) {
  216. 1 this.__additive = true;
  217. 1 this.__name = name;
  218. 1 this._parent = parent;
  219. 1 this._tree = new LoggerTree(this);
  220. 1 this.fullName = this._tree.name;
  221. 1 if (!parent || !parent.additive) {
  222. 1 this.level = Level.ALL;
  223. } else {
  224. 0 this.level = parent.level;
  225. }
  226. 1 this.__appenders = {};
  227. },
  228. /**
  229. * Log an info level message
  230. *
  231. * @param {String} message the message to log.
  232. *
  233. * @return {comb.logging.Logger} for chaining.
  234. */
  235. info: function (message) {
  236. 0 return this.log.apply(this, [Level.INFO].concat(argsToArray(arguments)));
  237. },
  238. /**
  239. * Log an debug level message
  240. *
  241. * @param {String} message the message to log.
  242. *
  243. * @return {comb.logging.Logger} for chaining.
  244. */
  245. debug: function (message) {
  246. 0 return this.log.apply(this, [Level.DEBUG].concat(argsToArray(arguments)));
  247. },
  248. /**
  249. * Log an error level message
  250. *
  251. * @param {String} message the message to log.
  252. *
  253. * @return {comb.logging.Logger} for chaining.
  254. */
  255. error: function (message) {
  256. 0 return this.log.apply(this, [Level.ERROR].concat(argsToArray(arguments)));
  257. },
  258. /**
  259. * Log an warn level message
  260. *
  261. * @param {String} message the message to log.
  262. *
  263. * @return {comb.logging.Logger} for chaining.
  264. */
  265. warn: function (message) {
  266. 0 return this.log.apply(this, [Level.WARN].concat(argsToArray(arguments)));
  267. },
  268. /**
  269. * Log an trace level message
  270. *
  271. * @param {String} message the message to log.
  272. *
  273. * @return {comb.logging.Logger} for chaining.
  274. */
  275. trace: function (message) {
  276. 0 return this.log.apply(this, [Level.TRACE].concat(argsToArray(arguments)));
  277. },
  278. /**
  279. * Log an fatal level message
  280. *
  281. * @param {String} message the message to log.
  282. *
  283. * @return {comb.logging.Logger} for chaining.
  284. */
  285. fatal: function (message) {
  286. 0 return this.log.apply(this, [Level.FATAL].concat(argsToArray(arguments)));
  287. },
  288. /**
  289. * Creates a log event to be passed to appenders
  290. *
  291. * @param {comb.logging.Level} level the level of the logging event
  292. * @param {String} message the message to be logged
  293. * @return {Object} the logging event
  294. */
  295. getLogEvent: function getLogEvent(level, message) {
  296. 0 return {
  297. hostname: os.hostname(),
  298. pid: process.pid,
  299. gid: hasGetGid ? process.getgid() : null,
  300. processTitle: process.title,
  301. level: level,
  302. levelName: level.name,
  303. message: message,
  304. timeStamp: new Date(),
  305. name: this.fullName
  306. };
  307. },
  308. /**
  309. * Log a message
  310. *
  311. * @param {comb.logging.Level} level the level the message is
  312. * @param {String} message the message to log.
  313. *
  314. * @return {comb.logging.Logger} for chaining.
  315. */
  316. log: function (level, message) {
  317. 0 var args = argsToArray(arguments, 1);
  318. 0 level = Level.toLevel(level);
  319. 0 if (args.length > 1) {
  320. 0 message = format.apply(null, args);
  321. }
  322. 0 if (level.isGreaterOrEqualToo(this.level)) {
  323. 0 if (Level.TRACE.equals(level)) {
  324. 0 var err = new Error;
  325. 0 err.name = "Trace";
  326. 0 err.message = message || '';
  327. 0 Error.captureStackTrace(err, arguments.callee);
  328. 0 message = err.stack;
  329. 0 } else if (Level.ERROR.equals(level) && isInstanceOf(message, Error)) {
  330. 0 message = message.stack;
  331. }
  332. 0 var type = level.name.toLowerCase(), appenders = this.__appenders;
  333. 0 var event = this.getLogEvent(level, message);
  334. 0 Object.keys(appenders).forEach(function (i) {
  335. 0 appenders[i].append(event);
  336. });
  337. }
  338. 0 return this;
  339. },
  340. /**
  341. * Add an appender to this logger. If this is additive then the appender is added to all subloggers.
  342. *
  343. * @example
  344. * comb.logger("my.logger")
  345. * .addAppender("ConsoleAppender")
  346. * .addAppender("FileAppender", {file:'/var/log/my.log'})
  347. * .addAppender("RollingFileAppender", {file:'/var/log/myRolling.log'})
  348. * .addAppender("JSONAppender", {file:'/var/log/myJson.log'});
  349. *
  350. * @param {comb.logging.Appender|String} If the appender is an {@link comb.logging.appenders.Appender} then it is added.
  351. * If the appender is a string then {@link comb.logging.appenders.Appender.createAppender} will be called to create it.
  352. *
  353. * @param {Object} [opts = null] If the first argument is a string then the opts will be used as constructor arguments for
  354. * creating the appender.
  355. *
  356. * @return {comb.logging.Logger} for chaining.
  357. */
  358. addAppender: function (appender, opts) {
  359. 0 var args = argsToArray(arguments);
  360. 0 if (isString(appender)) {
  361. 0 this.addAppender(Appender.createAppender(appender, opts));
  362. } else {
  363. 0 if (!isUndefinedOrNull(appender)) {
  364. 0 var name = appender.name;
  365. 0 if (!(name in this.__appenders)) {
  366. 0 this.__appenders[name] = appender;
  367. 0 if (!appender.level) {
  368. 0 appender.level = this.level;
  369. }
  370. 0 this._tree.addAppender(appender);
  371. }
  372. }
  373. }
  374. 0 return this;
  375. },
  376. /**
  377. * Short cut to add a list of appenders to this Logger
  378. * @param {Array<comb.logging.Appender>} appenders
  379. *
  380. * @return {comb.logging.Logger} for chaining.
  381. */
  382. addAppenders: function (appenders) {
  383. 0 appenders.forEach(this.addAppender.bind(this));
  384. 0 return this;
  385. },
  386. /**
  387. * Removes and appender from this logger.
  388. * @param {String} name the name of the appender
  389. * @return {comb.logging.Logger} for chaining.
  390. */
  391. removeAppender: function (name) {
  392. 0 if (name in this.__appenders) {
  393. 0 delete this.__appenders[name];
  394. 0 this._tree.removeAppender(name);
  395. }
  396. 0 return this;
  397. },
  398. /**
  399. * Removes a list of appenders from this logger.
  400. *
  401. * @param {String[]} appenders a list of names of appenders to remove
  402. * @return {comb.logging.Logger} for chaining.
  403. */
  404. removeAppenders: function (appenders) {
  405. 0 appenders.forEach(this.removeAppender, this);
  406. 0 return this;
  407. },
  408. /**
  409. * Removes all appenders from this logger and sub loggers if this Logger is additive.
  410. *
  411. * @return {comb.logging.Logger} for chaining.
  412. */
  413. removeAllAppenders: function () {
  414. 0 Object.keys(this.__appenders).forEach(this.removeAppender.bind(this));
  415. 0 return this;
  416. },
  417. /**
  418. * Determines if an appender is attached.
  419. *
  420. * @param {String} name the name of the appender.
  421. */
  422. isAppenderAttached: function (name) {
  423. 0 return (name in this.__appenders);
  424. },
  425. /**
  426. * Gets an appender from this logger
  427. *
  428. * @param {String} name the name of the appender.
  429. *
  430. * @return {comb.logging.Appender|undefined} returns the appender with the specified name or
  431. * undefined if it is not found.
  432. */
  433. getAppender: function (name) {
  434. 0 var ret;
  435. 0 if (name in this.__appenders) {
  436. 0 ret = this.__appenders[name];
  437. }
  438. 0 return ret;
  439. },
  440. /**
  441. * @ignore
  442. * */
  443. setters: {
  444. level: function (level) {
  445. 1 level = Level.toLevel(level);
  446. 1 if (this.__additive) {
  447. 1 this.__level = level;
  448. 1 var appenders = this.__appenders;
  449. 1 for (var i in appenders) {
  450. 0 appenders[i].level = level;
  451. }
  452. 1 this._tree.level = level;
  453. } else {
  454. 0 this.__level = level;
  455. }
  456. },
  457. additive: function (additive) {
  458. 0 this.__additive = additive;
  459. }
  460. },
  461. /**@ignore*/
  462. getters: {
  463. /**@ignore*/
  464. /**@ignore*/
  465. subLoggers: function () {
  466. 0 return this._tree.getSubLoggers();
  467. },
  468. /**@ignore*/
  469. level: function () {
  470. 1 return this.__level;
  471. },
  472. /**@ignore*/
  473. additive: function () {
  474. 0 return this.__additive;
  475. },
  476. isAll: function () {
  477. 0 return Level.ALL.isGreaterOrEqualToo(this.level);
  478. },
  479. /**@ignore*/
  480. isDebug: function () {
  481. 0 return Level.DEBUG.isGreaterOrEqualToo(this.level);
  482. },
  483. /**@ignore*/
  484. isTrace: function () {
  485. 0 return Level.TRACE.isGreaterOrEqualToo(this.level);
  486. },
  487. /**@ignore*/
  488. isInfo: function () {
  489. 0 return Level.INFO.isGreaterOrEqualToo(this.level);
  490. },
  491. /**@ignore*/
  492. isWarn: function () {
  493. 0 return Level.WARN.isGreaterOrEqualToo(this.level);
  494. },
  495. /**@ignore*/
  496. isError: function () {
  497. 0 return Level.ERROR.isGreaterOrEqualToo(this.level);
  498. },
  499. /**@ignore*/
  500. isFatal: function () {
  501. 0 return Level.FATAL.isGreaterOrEqualToo(this.level);
  502. },
  503. /**@ignore*/
  504. isOff: function () {
  505. 0 return Level.OFF.equals(this.level);
  506. },
  507. /**@ignore*/
  508. name: function () {
  509. 1 return this.__name;
  510. },
  511. /**@ignore*/
  512. tree: function () {
  513. 0 return this._tree;
  514. },
  515. /**@ignore*/
  516. appenders: function () {
  517. 0 var ret = [];
  518. 0 for (var i in this.__appenders) {
  519. 0 ret.push(this.__appenders[i]);
  520. }
  521. 0 return ret;
  522. },
  523. categories: function () {
  524. 0 return this._tree.categories;
  525. }
  526. }
  527. },
  528. static: {
  529. /**@lends comb.logging.Logger*/
  530. /**
  531. * Return the root of all loggers
  532. */
  533. getRootLogger: function () {
  534. 0 return rootTree.getRootLogger();
  535. },
  536. /**
  537. * Retrieves/Creates a logger based on the name passed in
  538. *
  539. * @param {String} name the name of the logger
  540. */
  541. getLogger: function (name) {
  542. 0 return rootTree.getLogger(name);
  543. }
  544. }
  545. }));
  546. /**
  547. *
  548. * @function
  549. * @description Alias to {@link comb.logging.Logger.getLogger}. See {@link comb.logging.Logger} for more information.
  550. *
  551. * @example
  552. *
  553. * comb.logger("my.logger");
  554. *
  555. * @memberOf comb
  556. * @namespace
  557. */
  558. 1exports.logger = Logger.getLogger.bind(Logger);
  559. /**
  560. * @function
  561. * @description Shortcut to configure loggers.
  562. *
  563. * @example
  564. *
  565. * //same as new comb.logging.BasicConfigurator().configure();
  566. * comb.logger.configure();
  567. *
  568. * //new comb.logging.PropertyConfigurator().configure("/location/to/logger/configuration.json");
  569. * comb.logger.configure("/location/to/logger/configuration.json");
  570. *
  571. * @memberOf comb.logger
  572. */
  573. 1exports.logger.configure = function configure() {
  574. 0 var args = argsToArray(arguments), configurator;
  575. 0 if (!args.length) {
  576. 0 configurator = new configurators.BasicConfigurator();
  577. } else {
  578. 0 var first = args[0];
  579. 0 if (isHash(first) || isString(first)) {
  580. 0 configurator = new configurators.PropertyConfigurator();
  581. } else {
  582. 0 configurator = new configurators.BasicConfigurator();
  583. }
  584. }
  585. 0 configurator.configure.apply(configurator, args);
  586. 0 return this;
  587. };
  588. /**
  589. * @function
  590. * @description Factory method for creating appenders. See {@link comb.logging.appenders.Appender.createAppender} for
  591. * arguments.
  592. *
  593. * @example
  594. *
  595. * var appender = comb.logger.appender("ConsoleAppender");
  596. * comb.logger("my.logger").addAppender(appender);
  597. *
  598. * @memberOf comb.logger
  599. *
  600. */
  601. 1exports.logger.appender = Appender.createAppender.bind(Appender);
  602. 1var rootLogger = new Logger("");
  603. 1rootTree = rootLogger._tree;
  604. /**
  605. * The root for all loggers.
  606. * @memberOf comb.logger
  607. * @type comb.logging.Logger
  608. */
  609. 1exports.logger.rootLogger = rootLogger;
extensions/string.js
Coverage38.10 SLOC45 LOC21 Missed13
  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", "bindIgnore","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. 0 comb = comb || (require("../index"));
  22. 0 extend(o, methods, string, null, true);
  23. 0 extend(o, baseMethods, base, null, true);
  24. 0 extend(o, dateMethods, date, null, true);
  25. 0 extend(o, regexpMethods, regexp, null, true);
  26. 0 extend(o, arrayMethods, array, function (name, func) {
  27. 0 return function () {
  28. 0 return comb(func.apply(null, argsToArray(arguments).concat([this.valueOf()])));
  29. };
  30. }, true);
  31. 0 extend(o, functionMethods, base, function (name, func) {
  32. 0 return function (arg1) {
  33. 0 var args = argsToArray(arguments, 1);
  34. 0 return comb(func.apply(null, [arg1, this.valueOf()].concat(args)));
  35. };
  36. });
  37. 0 return o;
  38. };
extensions/object.js
Coverage41.67 SLOC38 LOC12 Missed7
  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. 0 comb = comb || (require("../index"));
  27. 0 extend(o, methods, base);
  28. 0 extend(o, hashMethods, hash);
  29. 0 extend(o, lastMethod, base, function (name, func) {
  30. 0 return function () {
  31. 0 return comb(func.apply(null, argsToArray(arguments).concat([this])));
  32. };
  33. });
  34. 0 return o;
  35. };
base/object.js
Coverage42.45 SLOC485 LOC139 Missed80
  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. 1 if (actual === expected) {
  11. 0 return true;
  12. 1 } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
  13. 0 if (actual.length != expected.length) return false;
  14. 0 for (var i = 0; i < actual.length; i++) {
  15. 0 if (actual[i] !== expected[i]) return false;
  16. }
  17. 0 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. 1 } else if (actual instanceof Date && expected instanceof Date) {
  21. 0 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. 1 } else if (actual instanceof RegExp && expected instanceof RegExp) {
  26. 0 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. 1 } else if (typeof actual != 'object' && typeof expected != 'object') {
  34. 1 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. 0 return objEquiv(actual, expected);
  43. }
  44. }
  45. 1function objEquiv(a, b) {
  46. 0 if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
  47. 0 return false;
  48. // an identical 'prototype' property.
  49. 0 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. 0 if (isArguments(a)) {
  53. 0 if (!isArguments(b)) {
  54. 0 return false;
  55. }
  56. 0 a = pSlice.call(a);
  57. 0 b = pSlice.call(b);
  58. 0 return _deepEqual(a, b);
  59. }
  60. 0 try {
  61. 0 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. 0 return false;
  66. }
  67. // having the same number of owned properties (keys incorporates
  68. // hasOwnProperty)
  69. 0 if (ka.length != kb.length)
  70. 0 return false;
  71. //the same set of keys (although not necessarily the same order),
  72. 0 ka.sort();
  73. 0 kb.sort();
  74. //~~~cheap key test
  75. 0 for (i = ka.length - 1; i >= 0; i--) {
  76. 0 if (ka[i] != kb[i])
  77. 0 return false;
  78. }
  79. //equivalent values for every corresponding key, and
  80. //~~~possibly expensive deep test
  81. 0 for (i = ka.length - 1; i >= 0; i--) {
  82. 0 key = ka[i];
  83. 0 if (!_deepEqual(a[key], b[key])) return false;
  84. }
  85. 0 return true;
  86. }
  87. 1function merge(target, source) {
  88. 430 var name, s;
  89. 430 for (name in source) {
  90. 570 s = source[name];
  91. 570 if (!(name in target) || (target[name] !== s)) {
  92. 570 target[name] = s;
  93. }
  94. }
  95. 430 return target;
  96. }
  97. 1function deepMerge(target, source) {
  98. 1 var name, s, t;
  99. 1 for (name in source) {
  100. 1 s = source[name], t = target[name];
  101. 1 if (!_deepEqual(t, s)) {
  102. 1 if (comb.isHash(t) && comb.isHash(s)) {
  103. 0 target[name] = deepMerge(t, s);
  104. 1 } else if (comb.isHash(s)) {
  105. 0 target[name] = deepMerge({}, s);
  106. } else {
  107. 1 target[name] = s;
  108. }
  109. }
  110. }
  111. 1 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. 57 var ret = comb.isObject(obj);
  135. 57 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. 415 if (!obj) {
  164. 0 obj = {};
  165. }
  166. 415 for (var i = 1, l = arguments.length; i < l; i++) {
  167. 430 merge(obj, arguments[i]);
  168. }
  169. 415 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. 1 if (!obj) {
  195. 0 obj = {};
  196. }
  197. 1 for (var i = 1, l = arguments.length; i < l; i++) {
  198. 1 deepMerge(obj, arguments[i]);
  199. }
  200. 1 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. 0 var proto = parent.prototype || parent;
  237. 0 merge(proto, extend);
  238. 0 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. 57 var undef;
  250. 57 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. 0 if (comb.isObject(object)) {
  266. 0 for (var i in object) {
  267. 0 if (object.hasOwnProperty(i)) {
  268. 0 return false;
  269. }
  270. }
  271. }
  272. 0 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. 0 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. 0 if (!isHash(hash) || typeof iterator !== "function") {
  319. 0 throw new TypeError();
  320. }
  321. 0 var keys = Object.keys(hash), key;
  322. 0 for (var i = 0, len = keys.length; i < len; ++i) {
  323. 0 key = keys[i];
  324. 0 iterator.call(scope || hash, hash[key], key, hash);
  325. }
  326. 0 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. 0 if (!isHash(hash) || typeof iterator !== "function") {
  350. 0 throw new TypeError();
  351. }
  352. 0 var keys = Object.keys(hash), key, value, ret = {};
  353. 0 for (var i = 0, len = keys.length; i < len; ++i) {
  354. 0 key = keys[i];
  355. 0 value = hash[key];
  356. 0 if (iterator.call(scope || hash, value, key, hash)) {
  357. 0 ret[key] = value;
  358. }
  359. }
  360. 0 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. 0 if (!isHash(hash)) {
  379. 0 throw new TypeError();
  380. }
  381. 0 var keys = Object.keys(hash), ret = [];
  382. 0 for (var i = 0, len = keys.length; i < len; ++i) {
  383. 0 ret.push(hash[keys[i]]);
  384. }
  385. 0 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. 0 if (!isHash(hash)) {
  403. 0 throw new TypeError();
  404. }
  405. 0 var keys = Object.keys(hash), key, ret = {};
  406. 0 for (var i = 0, len = keys.length; i < len; ++i) {
  407. 0 key = keys[i];
  408. 0 ret[hash[key]] = key;
  409. }
  410. 0 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. 0 if (!isHash(hash)) {
  428. 0 throw new TypeError();
  429. }
  430. 0 var keys = Object.keys(hash), key, ret = [];
  431. 0 for (var i = 0, len = keys.length; i < len; ++i) {
  432. 0 key = keys[i];
  433. 0 ret.push([key, hash[key]]);
  434. }
  435. 0 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
Coverage45.45 SLOC48 LOC11 Missed6
  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. 0 var undef;
  18. 0 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. 0 return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function (ch) {
  38. 0 if (except && except.indexOf(ch) != -1) {
  39. 0 return ch;
  40. }
  41. 0 return "\\" + ch;
  42. }); // String
  43. }
  44. };
logging/level.js
Coverage48.48 SLOC188 LOC33 Missed17
  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. 8 this.level = level;
  54. 8 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. 0 var ret = false;
  64. 0 if (level && base.isNumber(level.level)) {
  65. 0 if (this.level >= level.level) {
  66. 0 ret = true;
  67. }
  68. }
  69. 0 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. 0 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. 1 var ret = null;
  95. 1 var args = base.argsToArray(arguments);
  96. 1 if (args.length === 1) {
  97. 1 var level = args[0];
  98. 1 if (base.isNumber(level)) {
  99. 0 var strLevel = LEVELS_REVERSE[level];
  100. 0 ret = Level[strLevel];
  101. 1 } else if (base.isString(level)) {
  102. 0 ret = Level[level.toUpperCase()];
  103. } else {
  104. 1 ret = level;
  105. }
  106. } else {
  107. 0 ret = (Level.toLevel(args[0]) || args[1]);
  108. }
  109. 1 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. 0 var ret;
  132. 0 if (base.isString(label) && base.isNumber(level)) {
  133. 0 label = label.toUpperCase();
  134. 0 LEVELS_REVERSE[level] = label;
  135. 0 LEVELS[label] = level;
  136. 0 ret = (this[label] = new Level(level, label));
  137. }
  138. 0 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. }
base/functions.js
Coverage49.51 SLOC279 LOC103 Missed52
  1. 1var string = require("./string"),
  2. object = require("./object"),
  3. isArray = Array.isArray,
  4. isObject = object.isObject,
  5. isString = string.isString;
  6. /**
  7. * Determines if something is a function
  8. * @param {Anything} obj the thing to test if it is a function
  9. *
  10. * @returns {Boolean} true if the obj is a function false otherwise
  11. * @static
  12. * @memberOf comb
  13. */
  14. 1function isFunction(obj) {
  15. 13760 return typeof obj === "function";
  16. }
  17. /**
  18. * Binds a method to a particular scope
  19. *
  20. * @static
  21. * @memberOf comb
  22. *
  23. * @param {Object} scope the scope to bind the callback to
  24. * @param {String|Function} method the method to callback
  25. * @param [args] optional args to pass to the callback
  26. *
  27. * @returns {Function} the hitched function
  28. */
  29. 1function hitch(scope, method, args) {
  30. 3710 args = Array.prototype.slice.call(arguments).slice(2);
  31. 3710 if ((isString(method) && !(method in scope))) {
  32. 0 throw new Error(method + " property not defined in scope");
  33. 3710 } else if (!isString(method) && !isFunction(method)) {
  34. 0 throw new Error(method + " is not a function");
  35. }
  36. 3710 if (isString(method)) {
  37. 802 return function () {
  38. 437 var func = scope[method];
  39. 437 if (isFunction(func)) {
  40. 437 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  41. 437 return func.apply(scope, scopeArgs);
  42. } else {
  43. 0 return func;
  44. }
  45. };
  46. } else {
  47. 2908 return function () {
  48. 2514 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  49. 2514 return method.apply(scope, scopeArgs);
  50. };
  51. }
  52. }
  53. /**
  54. * Binds a method to the scope of the first argument.
  55. *
  56. * This is useful if you have async actions and you just want to run a method or retrieve a property on the object.
  57. *
  58. * ```
  59. * var arr = [], push = comb.applyFirst("push"), length = comb.applyFirst("length");
  60. * push(arr, 1, 2,3,4);
  61. * console.log(length(arr)); //4
  62. * console.log(arr); //1,2,3,4
  63. *
  64. * ```
  65. * @static
  66. * @memberOf comb
  67. * @param {String|Function} method the method to invoke in the scope of the first arument.
  68. * @param [args] optional args to pass to the callback
  69. *
  70. * @returns {Function} a function that will execute the method in the scope of the first argument.
  71. */
  72. 1function applyFirst(method, args) {
  73. 0 args = Array.prototype.slice.call(arguments).slice(1);
  74. 0 if (!isString(method) && !isFunction(method)) {
  75. 0 throw new Error(method + " must be the name of a property or function to execute");
  76. }
  77. 0 if (isString(method)) {
  78. 0 return function () {
  79. 0 var scopeArgs = Array.prototype.slice.call(arguments), scope = scopeArgs.shift();
  80. 0 var func = scope[method];
  81. 0 if (isFunction(func)) {
  82. 0 scopeArgs = args.concat(scopeArgs);
  83. 0 return func.apply(scope, scopeArgs);
  84. } else {
  85. 0 return func;
  86. }
  87. };
  88. } else {
  89. 0 return function () {
  90. 0 var scopeArgs = Array.prototype.slice.call(arguments), scope = scopeArgs.shift();
  91. 0 scopeArgs = args.concat(scopeArgs);
  92. 0 return method.apply(scope, scopeArgs);
  93. };
  94. }
  95. }
  96. 1;
  97. /**
  98. * @function
  99. * Binds a method to a particular scope
  100. * @static
  101. * @memberOf comb
  102. * @param {Object} scope the scope to bind the callback to
  103. * @param {String|Function} method the method to callback
  104. * @param [args] optional args to pass to the callback
  105. *
  106. * @returns {Function} the hitched function
  107. */
  108. 1exports.bind = hitch;
  109. /**
  110. * Binds a method to a particular scope ignoring any new arguments passed
  111. * into the function. This is useful if you want to force particular arguments and
  112. * ignore any new ones
  113. * @static
  114. * @memberOf comb
  115. * @param {Object} scope the scope to bind the callback to
  116. * @param {String|Function} method the method to callback
  117. * @param [args] optional args to pass to the callback
  118. *
  119. * @returns {Function} the hitched function
  120. */
  121. 1function hitchIgnore(scope, method, args) {
  122. 124 args = Array.prototype.slice.call(arguments).slice(2);
  123. 124 if ((isString(method) && !(method in scope))) {
  124. 0 throw new Error(method + " property not defined in scope");
  125. 124 } else if (!isString(method) && !isFunction(method)) {
  126. 0 throw new Error(method + " is not a function");
  127. }
  128. 124 if (isString(method)) {
  129. 12 return function () {
  130. 12 var func = scope[method];
  131. 12 if (isFunction(func)) {
  132. 12 return func.apply(scope, args);
  133. } else {
  134. 0 return func;
  135. }
  136. };
  137. } else {
  138. 112 return function () {
  139. 101 return method.apply(scope, args);
  140. };
  141. }
  142. }
  143. /**
  144. * @function
  145. * Binds a method to a particular scope ignoring any new arguments passed
  146. * into the function. This is useful if you want to force particular arguments and
  147. * ignore any new ones
  148. * @static
  149. * @memberOf comb
  150. * @param {Object} scope the scope to bind the callback to
  151. * @param {String|Function} method the method to callback
  152. * @param [args] optional args to pass to the callback
  153. *
  154. * @returns {Function} the hitched function
  155. */
  156. 1exports.bindIgnore = hitchIgnore;
  157. 1function hitchAll(scope, methods) {
  158. 0 var funcs = Array.prototype.slice.call(arguments).slice(1);
  159. 0 if (!isObject(scope)) {
  160. 0 throw new TypeError("scope must be an object");
  161. }
  162. 0 if (funcs.length === 1 && isArray(funcs[0])) {
  163. 0 funcs = funcs[0];
  164. }
  165. 0 if (!funcs.length) {
  166. 0 funcs = Object.keys(scope).filter(function (k) {
  167. 0 return isFunction(scope[k]);
  168. });
  169. }
  170. 0 for (var i = 0, l = funcs.length; i < l; i++) {
  171. 0 scope[funcs[i]] = hitch(scope, scope[funcs[i]]);
  172. }
  173. 0 return object;
  174. }
  175. 1exports.hitchAll = hitchAll;
  176. 1exports.bindAll = hitchAll;
  177. /**
  178. * Allows the passing of additional arguments to a function when it is called
  179. * especially useful for callbacks that you want to provide additional parameters to
  180. *
  181. * @static
  182. * @memberOf comb
  183. * @param {String|Function} method the method to callback
  184. * @param {Anything} [args] variable number of arguments to pass
  185. *
  186. * @returns {Function} partially hitched function
  187. */
  188. 1function partial(method, args) {
  189. 47 args = Array.prototype.slice.call(arguments).slice(1);
  190. 47 if (!isString(method) && !isFunction(method)) {
  191. 0 throw new Error(method + " must be the name of a property or function to execute");
  192. }
  193. 47 if (isString(method)) {
  194. 0 return function () {
  195. 0 var func = this[method];
  196. 0 if (isFunction(func)) {
  197. 0 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  198. 0 return func.apply(this, scopeArgs);
  199. } else {
  200. 0 return func;
  201. }
  202. };
  203. } else {
  204. 47 return function () {
  205. 25 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  206. 25 return method.apply(this, scopeArgs);
  207. };
  208. }
  209. }
  210. 1;
  211. 1function curryFunc(f, execute) {
  212. 0 return function (arg) {
  213. 0 var args = Array.prototype.slice.call(arguments);
  214. 0 return execute ? f.apply(this, arguments) : function (arg) {
  215. 0 return f.apply(this, args.concat(Array.prototype.slice.call(arguments)));
  216. };
  217. };
  218. }
  219. /**
  220. * Curries a function
  221. * @example
  222. * var curried = comb.curry(4, function(a,b,c,d){
  223. * return [a,b,c,d].join(",");
  224. * }
  225. * curried("a");
  226. * curried("b");
  227. * curried("c");
  228. * curried("d") => "a,b,c,d"
  229. *
  230. * //OR
  231. *
  232. * curried("a")("b")("c")("d") => "a,b,c,d"
  233. *
  234. * @static
  235. * @memberOf comb
  236. * @param {Number} depth the number of args you expect
  237. * @param {Function} cb the function to call once all args are gathered
  238. * @param {Object} [scope] what scope to call the function in
  239. *
  240. * @returns {Function} the curried version of the function
  241. * */
  242. 1function curry(depth, cb, scope) {
  243. 0 var f;
  244. 0 if (scope) {
  245. 0 f = hitch(scope, cb);
  246. } else {
  247. 0 f = cb;
  248. }
  249. 0 if (depth) {
  250. 0 var len = depth - 1;
  251. 0 for (var i = len; i >= 0; i--) {
  252. 0 f = curryFunc(f, i === len);
  253. }
  254. }
  255. 0 return f;
  256. }
  257. 1exports.isFunction = isFunction;
  258. 1exports.hitch = hitch;
  259. 1exports.hitchIgnore = hitchIgnore;
  260. 1exports.partial = partial;
  261. 1exports.applyFirst = applyFirst;
  262. 1exports.bindFirst = applyFirst;
  263. 1exports.curry = curry;
extensions/array.js
Coverage57.14 SLOC21 LOC7 Missed3
  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. 0 extend(o, methods, array);
  13. 0 extend(o, stringMethods, string);
  14. 0 return o;
  15. };
extensions/is.js
Coverage57.14 SLOC87 LOC28 Missed12
  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. 2 var valueOf = false, val;
  27. 2 if (!base.isInstanceOf(o, Object)) {
  28. 2 if (base.isBoolean(o)) {
  29. 2 val = new Boolean(o);
  30. 2 valueOf = true;
  31. 0 } else if (base.isNumber(o)) {
  32. 0 val = new Number(o);
  33. 0 valueOf = true;
  34. 0 } else if (base.isString(o)) {
  35. 0 val = new String(o);
  36. 0 valueOf = true;
  37. }
  38. } else {
  39. 0 val = o;
  40. }
  41. 2 var ret = extend(val, methods, base, null, valueOf);
  42. 2 if (valueOf) {
  43. 2 Object.defineProperty(ret, "valueOf", {
  44. value:function () {
  45. 0 return o;
  46. },
  47. writable:false,
  48. enumerable:false,
  49. configurable:true
  50. });
  51. }
  52. 2 Object.defineProperty(ret, "eq", {
  53. value:function (other) {
  54. 0 return o === other;
  55. },
  56. writable:false,
  57. enumerable:false,
  58. configurable:true
  59. });
  60. 2 Object.defineProperty(ret, "neq", {
  61. value:function (other) {
  62. 0 return o !== other;
  63. },
  64. writable:false,
  65. enumerable:false,
  66. configurable:true
  67. });
  68. 2 Object.defineProperty(ret, "print", {
  69. value:function () {
  70. 0 console.log(o);
  71. 0 return val;
  72. },
  73. writable:false,
  74. enumerable:false,
  75. configurable:true
  76. });
  77. 2 Object.defineProperty(ret, "__isExtended__", {
  78. value:true,
  79. writable:false,
  80. enumerable:false,
  81. configurable:true
  82. });
  83. 2 return ret;
  84. };
base/number.js
Coverage60.00 SLOC66 LOC10 Missed4
  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. 93 var undef;
  11. 93 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. 0 increment = increment || 1e-20;
  38. 0 var factor = 10 / (10 * (increment || 10));
  39. 0 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. 0 return Math.ceil(number * Math.pow(10, places))/Math.pow(10, places);
  58. }
  59. };
extensions/date.js
Coverage60.00 SLOC26 LOC5 Missed2
  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. 0 extend(o, methods, date);
  19. 0 return o;
  20. };
extensions/number.js
Coverage60.00 SLOC17 LOC5 Missed2
  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. 0 extend(o, methods, number, null, true);
  13. 0 return o;
  14. };
extensions/utils.js
Coverage63.16 SLOC45 LOC19 Missed7
  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. 36 return function proxy() {
  9. 0 comb || (comb = require("../index"));
  10. 0 var ret = func.apply(scope, [valueOf ? this.valueOf() : this].concat(argsToArray(arguments)));
  11. 0 if (ret && !base.isBoolean(ret) && ret !== this && !ret.__isExtended__) {
  12. 0 ret = comb(ret);
  13. }
  14. 0 return ret;
  15. };
  16. }
  17. 1function extend(obj, methods, base, creator, valueOf) {
  18. 2 valueOf = isBoolean(valueOf) ? valueOf : false;
  19. 2 array.forEach(methods, function (method) {
  20. 36 var newFunc, func, m, name;
  21. 36 if (Array.isArray(method) && method.length === 2) {
  22. 0 m = method[0];
  23. 0 name = method[1];
  24. } else {
  25. 36 m = name = method;
  26. }
  27. 36 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. 2 return obj;
  35. }
  36. 1module.exports = {
  37. proxyFunc:proxyFunc,
  38. extend:extend
  39. };
collections/PriorityQueue.js
Coverage66.67 SLOC41 LOC6 Missed2
  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. 0 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. 0 return this.remove();
  34. }
  35. }
  36. });
  37. 1module.exports = exports = PriorityQueue;
base/inflections.js
Coverage68.82 SLOC215 LOC93 Missed29
  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. 0 var ret = str;
  94. 0 if (!misc.isUndefinedOrNull(str)) {
  95. 0 ret = str.replace(CAMELIZE_CONVERT_REGEXP, function (a, b) {
  96. 0 return b.toUpperCase();
  97. });
  98. }
  99. 0 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. 0 var ret = str;
  116. 0 if (!misc.isUndefinedOrNull(str)) {
  117. 0 ret = str.replace(UNDERSCORE_CONVERT_REGEXP1, UNDERSCORE_CONVERT_REPLACE)
  118. .replace(UNDERSCORE_CONVERT_REGEXP2, UNDERSCORE_CONVERT_REPLACE)
  119. .replace(DASH, UNDERSCORE).toLowerCase();
  120. }
  121. 0 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. 0 var ret = str;
  138. 0 if (!misc.isUndefinedOrNull(str)) {
  139. 0 ret = comb.camelize(comb.singularize(str.replace(/.*\./g, '')));
  140. }
  141. 0 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. 0 var ret = str;
  160. 0 if (!misc.isUndefinedOrNull(str)) {
  161. 0 if (UNCOUNTABLES.indexOf(str) == -1) {
  162. 0 for (var i in PLURALS) {
  163. 0 var s = PLURALS[i], rule = s[0], replacement = s[1];
  164. 0 if ((ret = ret.replace(rule, replacement)) != str) {
  165. 0 break;
  166. }
  167. }
  168. }
  169. }
  170. 0 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. 0 var ret = str;
  189. 0 if (!misc.isUndefinedOrNull(str)) {
  190. 0 if (UNCOUNTABLES.indexOf(str) == -1) {
  191. 0 for (var i in SINGULARS) {
  192. 0 var s = SINGULARS[i], rule = s[0], replacement = s[1];
  193. 0 if ((ret = ret.replace(rule, replacement)) != str) {
  194. 0 break;
  195. }
  196. }
  197. }
  198. }
  199. 0 return ret;
  200. };
base/misc.js
Coverage74.51 SLOC187 LOC51 Missed13
  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. 11657 slice = slice || 0;
  29. 11657 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. 146 var undef, type = typeof obj;
  42. 146 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. 7316 var undef;
  54. 7316 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. 2 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. 7304 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. 7122 var undef;
  90. 7122 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. 0 return !isUndefinedOrNull(object) && Object.prototype.toString.call(object) == '[object Arguments]';
  103. }
  104. 1function isInstance(obj, clazz) {
  105. 7108 if (typeof clazz == "function") {
  106. 7108 return obj instanceof clazz;
  107. } else {
  108. 0 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. 7108 return argsToArray(arguments, 1).some(function (c) {
  123. 7108 return isInstance(obj, c);
  124. });
  125. }
  126. 1(function () {
  127. 1 var listeners = [];
  128. 1 var setup = false;
  129. 1 function setupListener() {
  130. 0 if (!setup) {
  131. 0 var orig = process.emit;
  132. 0 process.emit = function (event) {
  133. 0 try {
  134. 0 if (event === 'exit') {
  135. 0 listeners.forEach(function (cb) {
  136. 0 cb();
  137. });
  138. }
  139. } finally {
  140. 0 orig.apply(this, arguments);
  141. }
  142. };
  143. 0 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. 0 setupListener();
  156. 0 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;
extensions/arguments.js
Coverage75.00 SLOC14 LOC4 Missed1
  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. 0 return extend(o, methods, base);
  10. };
base/array.js
Coverage82.63 SLOC1089 LOC357 Missed62
  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. 5762 return Object.prototype.toString.call(obj) === "[object Array]";
  14. };
  15. 1function isDate(obj) {
  16. 42 var undef;
  17. 42 return (obj !== undef && typeof obj === "object" && obj instanceof Date);
  18. }
  19. 1function cross(num, cros) {
  20. 7 var ret = reduceRight(cros, function (a, b) {
  21. 19 if (!isArray(b)) {
  22. 19 b = [b];
  23. }
  24. 19 b.unshift(num);
  25. 19 a.unshift(b);
  26. 19 return a;
  27. }, []);
  28. 7 return ret;
  29. }
  30. 1function permute(num, cross, length) {
  31. 6 var ret = [];
  32. 6 for (var i = 0; i < cross.length; i++) {
  33. 12 ret.push([num].concat(rotate(cross, i)).slice(0, length));
  34. }
  35. 6 return ret;
  36. }
  37. 1function intersection(a, b) {
  38. 10 var ret = [], aOne;
  39. 10 if (isArray(a) && isArray(b) && a.length && b.length) {
  40. 10 for (var i = 0, l = a.length; i < l; i++) {
  41. 35 aOne = a[i];
  42. 35 if (indexOf(b, aOne) !== -1) {
  43. 28 ret.push(aOne);
  44. }
  45. }
  46. }
  47. 10 return ret;
  48. }
  49. 1var _sort = (function () {
  50. 1 var isAll = function (arr, test) {
  51. 21 return every(arr, test);
  52. };
  53. 1 var defaultCmp = function (a, b) {
  54. 30 return a - b;
  55. };
  56. 1 var dateSort = function (a, b) {
  57. 9 return a.getTime() - b.getTime();
  58. };
  59. 1 return function _sort(arr, property) {
  60. 22 var ret = [];
  61. 22 if (isArray(arr)) {
  62. 22 ret = arr.slice();
  63. 22 if (property) {
  64. 10 if (typeof property === "function") {
  65. 1 ret.sort(property);
  66. } else {
  67. 9 ret.sort(function (a, b) {
  68. 27 var aProp = a[property], bProp = b[property];
  69. 27 if (isString(aProp) && isString(bProp)) {
  70. 9 return aProp > bProp ? 1 : aProp < bProp ? -1 : 0;
  71. 18 } else if (isDate(aProp) && isDate(bProp)) {
  72. 9 return aProp.getTime() - bProp.getTime()
  73. } else {
  74. 9 return aProp - bProp;
  75. }
  76. });
  77. }
  78. } else {
  79. 12 if (isAll(ret, isString)) {
  80. 3 ret.sort();
  81. 9 } else if (isAll(ret, isDate)) {
  82. 3 ret.sort(dateSort);
  83. } else {
  84. 6 ret.sort(defaultCmp);
  85. }
  86. }
  87. }
  88. 22 return ret;
  89. };
  90. })();
  91. 1function indexOf(arr, searchElement, fromIndex) {
  92. 96 if (!isArray(arr)) {
  93. 0 throw new TypeError();
  94. }
  95. 96 var t = Object(arr);
  96. 96 var len = t.length >>> 0;
  97. 96 if (len === 0) {
  98. 11 return -1;
  99. }
  100. 85 var n = 0;
  101. 85 if (arguments.length > 2) {
  102. 0 n = Number(arguments[2]);
  103. 0 if (n !== n) { // shortcut for verifying if it's NaN
  104. 0 n = 0;
  105. 0 } else if (n !== 0 && n !== Infinity && n !== -Infinity) {
  106. 0 n = (n > 0 || -1) * floor(abs(n));
  107. }
  108. }
  109. 85 if (n >= len) {
  110. 0 return -1;
  111. }
  112. 85 var k = n >= 0 ? n : mathMax(len - abs(n), 0);
  113. 85 for (; k < len; k++) {
  114. 182 if (k in t && t[k] === searchElement) {
  115. 50 return k;
  116. }
  117. }
  118. 35 return -1;
  119. }
  120. 1function lastIndexOf(arr, searchElement, fromIndex) {
  121. 0 if (!isArray(arr)) {
  122. 0 throw new TypeError();
  123. }
  124. 0 var t = Object(arr);
  125. 0 var len = t.length >>> 0;
  126. 0 if (len === 0) {
  127. 0 return -1;
  128. }
  129. 0 var n = len;
  130. 0 if (arguments.length > 2) {
  131. 0 n = Number(arguments[2]);
  132. 0 if (n !== n) {
  133. 0 n = 0;
  134. 0 } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
  135. 0 n = (n > 0 || -1) * floor(abs(n));
  136. }
  137. }
  138. 0 var k = n >= 0 ? mathMin(n, len - 1) : len - abs(n);
  139. 0 for (; k >= 0; k--) {
  140. 0 if (k in t && t[k] === searchElement) {
  141. 0 return k;
  142. }
  143. }
  144. 0 return -1;
  145. }
  146. 1function filter(arr, iterator, scope) {
  147. 17 if (!isArray(arr) || typeof iterator !== "function") {
  148. 0 throw new TypeError();
  149. }
  150. 17 var t = Object(arr);
  151. 17 var len = t.length >>> 0;
  152. 17 var res = [];
  153. 17 for (var i = 0; i < len; i++) {
  154. 71 if (i in t) {
  155. 31 var val = t[i]; // in case fun mutates this
  156. 31 if (iterator.call(scope, val, i, t)) {
  157. 21 res.push(val);
  158. }
  159. }
  160. }
  161. 17 return res;
  162. }
  163. 1function forEach(arr, iterator, scope) {
  164. 3931 if (!isArray(arr) || typeof iterator !== "function") {
  165. 0 throw new TypeError();
  166. }
  167. 3931 for (var i = 0, len = arr.length; i < len; ++i) {
  168. 28257 iterator.call(scope || arr, arr[i], i, arr);
  169. }
  170. 3931 return arr;
  171. }
  172. 1function every(arr, iterator, scope) {
  173. 367 if (!isArray(arr) || typeof iterator !== "function") {
  174. 0 throw new TypeError();
  175. }
  176. 367 var t = Object(arr);
  177. 367 var len = t.length >>> 0;
  178. 367 for (var i = 0; i < len; i++) {
  179. 520 if (i in t && !iterator.call(scope, t[i], i, t)) {
  180. 270 return false;
  181. }
  182. }
  183. 97 return true;
  184. }
  185. 1function some(arr, iterator, scope) {
  186. 0 if (!isArray(arr) || typeof iterator !== "function") {
  187. 0 throw new TypeError();
  188. }
  189. 0 var t = Object(arr);
  190. 0 var len = t.length >>> 0;
  191. 0 for (var i = 0; i < len; i++) {
  192. 0 if (i in t && iterator.call(scope, t[i], i, t)) {
  193. 0 return true;
  194. }
  195. }
  196. 0 return false;
  197. }
  198. 1function map(arr, iterator, scope) {
  199. 11 if (!isArray(arr) || typeof iterator !== "function") {
  200. 0 throw new TypeError();
  201. }
  202. 11 var t = Object(arr);
  203. 11 var len = t.length >>> 0;
  204. 11 var res = [];
  205. 11 for (var i = 0; i < len; i++) {
  206. 33 if (i in t) {
  207. 33 res.push(iterator.call(scope, t[i], i, t));
  208. }
  209. }
  210. 11 return res;
  211. }
  212. 1function reduce(arr, accumulator, curr) {
  213. 84 if (!isArray(arr) || typeof accumulator !== "function") {
  214. 0 throw new TypeError();
  215. }
  216. 84 var i = 0, l = arr.length >> 0;
  217. 84 if (arguments.length < 3) {
  218. 7 if (l === 0) {
  219. 0 throw new TypeError("Array length is 0 and no second argument");
  220. }
  221. 7 curr = arr[0];
  222. 7 i = 1; // start accumulating at the second element
  223. } else {
  224. 77 curr = arguments[2];
  225. }
  226. 84 while (i < l) {
  227. 189 if (i in arr) {
  228. 189 curr = accumulator.call(undefined, curr, arr[i], i, arr);
  229. }
  230. 189 ++i;
  231. }
  232. 84 return curr;
  233. }
  234. 1function reduceRight(arr, accumulator, curr) {
  235. 7 if (!isArray(arr) || typeof accumulator !== "function") {
  236. 0 throw new TypeError();
  237. }
  238. 7 var t = Object(arr);
  239. 7 var len = t.length >>> 0;
  240. // no value to return if no initial value, empty array
  241. 7 if (len === 0 && arguments.length === 2) {
  242. 0 throw new TypeError();
  243. }
  244. 7 var k = len - 1;
  245. 7 if (arguments.length >= 3) {
  246. 7 curr = arguments[2];
  247. } else {
  248. 0 do {
  249. 0 if (k in arr) {
  250. 0 curr = arr[k--];
  251. 0 break;
  252. }
  253. }
  254. while (true);
  255. }
  256. 7 while (k >= 0) {
  257. 19 if (k in t) {
  258. 19 curr = accumulator.call(undefined, curr, t[k], k, t);
  259. }
  260. 19 k--;
  261. }
  262. 7 return curr;
  263. }
  264. /**
  265. * converts anything to an array
  266. *
  267. * @example
  268. * comb.array.toArray({a : "b", b : "c"}) => [["a","b"], ["b","c"]];
  269. * comb.array.toArray("a") => ["a"]
  270. * comb.array.toArray(["a"]) => ["a"];
  271. * comb.array.toArray() => [];
  272. * comb.array.toArray("a", {a : "b"}) => ["a", ["a", "b"]];
  273. *
  274. * @static
  275. * @memberOf comb.array
  276. */
  277. 1function toArray(o) {
  278. 52 var ret = [];
  279. 52 if (o != null) {
  280. 52 var args = argsToArray(arguments);
  281. 52 if (args.length === 1) {
  282. 52 if (isArray(o)) {
  283. 52 ret = o;
  284. 0 } else if (obj.isHash(o)) {
  285. 0 for (var i in o) {
  286. 0 if (o.hasOwnProperty(i)) {
  287. 0 ret.push([i, o[i]]);
  288. }
  289. }
  290. } else {
  291. 0 ret.push(o);
  292. }
  293. } else {
  294. 0 forEach(args, function (a) {
  295. 0 ret = ret.concat(toArray(a));
  296. });
  297. }
  298. }
  299. 52 return ret;
  300. }
  301. /**
  302. * Sums all items in an array
  303. *
  304. * @example
  305. *
  306. * comb.array.sum([1,2,3]) => 6
  307. * comb.array.sum(["A","B","C"]) => "ABC";
  308. * var d1 = new Date(1999), d2 = new Date(2000), d3 = new Date(3000);
  309. * comb.array.sum([d1,d2,d3]) => "Wed Dec 31 1969 18:00:01 GMT-0600 (CST)"
  310. * + "Wed Dec 31 1969" 18:00:02 GMT-0600 (CST)"
  311. * + "Wed Dec 31 1969 18:00:03 GMT-0600 (CST)"
  312. * comb.array.sum([{},{},{}]) => "[object Object][object Object][object Object]";
  313. *
  314. * @param {Number[]} array the array of numbers to sum
  315. * @static
  316. * @memberOf comb.array
  317. */
  318. 1function sum(array) {
  319. 9 array = array || [];
  320. 9 if (array.length) {
  321. 7 return reduce(array, function (a, b) {
  322. 14 return a + b;
  323. });
  324. } else {
  325. 2 return 0;
  326. }
  327. }
  328. /**
  329. * Averages an array of numbers.
  330. * @example
  331. *
  332. * comb.array.avg([1,2,3]); //2
  333. *
  334. * @param {Number[]} array - an array of numbers
  335. * @return {Number} the average of all the numbers in the array.
  336. * @throws {Error} if the array is not all numbers.
  337. * @static
  338. * @memberOf comb.array
  339. */
  340. 1function avg(arr) {
  341. 5 arr = arr || [];
  342. 5 if (arr.length) {
  343. 3 var total = sum(arr);
  344. 3 if (number.isNumber(total)) {
  345. 2 return total / arr.length;
  346. } else {
  347. 1 throw new Error("Cannot average an array of non numbers.");
  348. }
  349. } else {
  350. 2 return 0;
  351. }
  352. }
  353. /**
  354. * Allows the sorting of an array based on a property name instead. This can also
  355. * act as a sort that does not change the original array.
  356. *
  357. * <b>NOTE:</b> this does not change the original array!
  358. *
  359. * @example
  360. * comb.array.sort([{a : 1}, {a : 2}, {a : -2}], "a"); //[{a : -2}, {a : 1}, {a : 2}];
  361. * @param {Array} arr the array to sort
  362. * @param {String|Function} cmp the property to sort on. Or a function used to compare.
  363. * @return {Array} a copy of the original array that is sorted.
  364. *
  365. * @static
  366. * @memberOf comb.array
  367. */
  368. 1function sort(arr, cmp) {
  369. 8 return _sort(arr, cmp);
  370. }
  371. /**
  372. * Finds that min value of an array. If a second argument is provided and it is a function
  373. * it will be used as a comparator function. If the second argument is a string then it will be used
  374. * as a property look up on each item.
  375. *
  376. * @example
  377. * comb.array.min([{a : 1}, {a : 2}, {a : -2}], "a"); //{a : -2}
  378. * comb.array.min([{a : 1}, {a : 2}, {a : -2}], function(a,b){
  379. * return a.a - b.a
  380. * }); //{a : -2}
  381. *
  382. * @param {Array} arr the array to find the min value on
  383. * @param {String|Function} cmp the property to sort on. Or a function used to compare.
  384. * @return {*}
  385. *
  386. * @static
  387. * @memberOf comb.array
  388. */
  389. 1function min(arr, cmp) {
  390. 7 return _sort(arr, cmp)[0];
  391. }
  392. /**
  393. * Finds that max value of an array. If a second argument is provided and it is a function
  394. * it will be used as a comparator function. If the second argument is a string then it will be used
  395. * as a property look up on each item.
  396. *
  397. * @example
  398. * comb.array.max([{a : 1}, {a : 2}, {a : -2}], "a"); //{a : 2}
  399. * comb.array.max([{a : 1}, {a : 2}, {a : -2}], function(a,b){
  400. * return a.a - b.a
  401. * }); //{a : 2}
  402. *
  403. * @param arr the array to find the max value on
  404. * @param {String|Function} cmp the property to sort on. Or a function used to compare.
  405. * @return {*} the maximum value of the array based on the provided cmp.
  406. *
  407. * @static
  408. * @memberOf comb.array
  409. */
  410. 1function max(arr, cmp) {
  411. 7 return _sort(arr, cmp)[arr.length - 1];
  412. }
  413. /**
  414. * Finds the difference of the two arrays.
  415. *
  416. * @example
  417. *
  418. * comb.array.difference([1,2,3], [2,3]); //[1]
  419. * comb.array.difference(["a","b",3], [3]); //["a","b"]
  420. *
  421. * @param {Array} arr1 the array we are subtracting from
  422. * @param {Array} arr2 the array we are subtracting from arr1
  423. * @return {*} the difference of the arrays.
  424. *
  425. * @static
  426. * @memberOf comb.array
  427. */
  428. 1function difference(arr1, arr2) {
  429. 5 var ret = arr1, args = flatten(argsToArray(arguments, 1));
  430. 5 if (isArray(arr1)) {
  431. 5 ret = filter(arr1, function (a) {
  432. 14 return indexOf(args, a) === -1;
  433. });
  434. }
  435. 5 return ret;
  436. }
  437. /**
  438. * Removes duplicates from an array
  439. *
  440. * @example
  441. *
  442. * comb.array.removeDuplicates([1,1,1]) => [1]
  443. * comb.array.removeDuplicates([1,2,3,2]) => [1,2,3]
  444. *
  445. * @param {Aray} array the array of elements to remove duplicates from
  446. *
  447. * @static
  448. * @memberOf comb.array
  449. */
  450. 1function removeDuplicates(arr) {
  451. 11 if (isArray(arr)) {
  452. 11 return reduce(arr, function (a, b) {
  453. 47 if (indexOf(a, b) === -1) {
  454. 32 return a.concat(b);
  455. } else {
  456. 15 return a;
  457. }
  458. }, []);
  459. }
  460. }
  461. /**
  462. *
  463. * See {@link comb.array.removeDuplicates}
  464. *
  465. * @static
  466. * @memberOf comb.array
  467. */
  468. 1function unique(arr) {
  469. 0 return removeDuplicates(arr);
  470. }
  471. /**
  472. * Rotates an array the number of specified positions
  473. *
  474. * @example
  475. * var arr = ["a", "b", "c", "d"];
  476. * comb.array.rotate(arr) => ["b", "c", "d", "a"]
  477. * comb.array.rotate(arr, 2) => ["c", "d", "a", "b"]);
  478. * comb.array.rotate(arr, 3) => ["d", "a", "b", "c"]);
  479. * comb.array.rotate(arr, 4) => ["a", "b", "c", "d"]);
  480. * comb.array.rotate(arr, -1) => ["d", "a", "b", "c"]);
  481. * comb.array.rotate(arr, -2) => ["c", "d", "a", "b"]);
  482. * comb.array.rotate(arr, -3) => ["b", "c", "d", "a"]);
  483. * comb.array.rotate(arr, -4) => ["a", "b", "c", "d"]);
  484. *
  485. * @param {Array} array the array of elements to remove duplicates from
  486. * @param {Number} numberOfTimes the number of times to rotate the array
  487. *
  488. * @static
  489. * @memberOf comb.array
  490. */
  491. 1function rotate(arr, numberOfTimes) {
  492. 58 var ret = arr.slice();
  493. 58 if (typeof numberOfTimes !== "number") {
  494. 1 numberOfTimes = 1;
  495. }
  496. 58 if (numberOfTimes && isArray(arr)) {
  497. 32 if (numberOfTimes > 0) {
  498. 22 ret.push(ret.shift());
  499. 22 numberOfTimes--;
  500. } else {
  501. 10 ret.unshift(ret.pop());
  502. 10 numberOfTimes++;
  503. }
  504. 32 return rotate(ret, numberOfTimes);
  505. } else {
  506. 26 return ret;
  507. }
  508. }
  509. /**
  510. * Finds all permutations of an array
  511. *
  512. * @example
  513. * var arr = [1,2,3];
  514. * comb.array.permutations(arr) => [[ 1, 2, 3 ],[ 1, 3, 2 ],[ 2, 3, 1 ],
  515. * [ 2, 1, 3 ],[ 3, 1, 2 ],[ 3, 2, 1 ]]
  516. * comb.array.permutations(arr, 2) => [[ 1, 2],[ 1, 3],[ 2, 3],[ 2, 1],[ 3, 1],[ 3, 2]]
  517. * comb.array.permutations(arr, 1) => [[1],[2],[3]]
  518. * comb.array.permutations(arr, 0) => [[]]
  519. * comb.array.permutations(arr, 4) => []
  520. *
  521. * @param {Array} arr the array to permute.
  522. * @param {Number} length the number of elements to permute.
  523. * @static
  524. * @memberOf comb.array
  525. */
  526. 1function permutations(arr, length) {
  527. 5 var ret = [];
  528. 5 if (isArray(arr)) {
  529. 5 var copy = arr.slice(0);
  530. 5 if (typeof length !== "number") {
  531. 1 length = arr.length;
  532. }
  533. 5 if (!length) {
  534. 1 ret = [
  535. []
  536. ];
  537. 4 } else if (length <= arr.length) {
  538. 3 ret = reduce(arr, function (a, b, i) {
  539. 9 var ret;
  540. 9 if (length > 1) {
  541. 6 ret = permute(b, rotate(copy, i).slice(1), length);
  542. } else {
  543. 3 ret = [
  544. [b]
  545. ];
  546. }
  547. 9 return a.concat(ret);
  548. }, []);
  549. }
  550. }
  551. 5 return ret;
  552. }
  553. /**
  554. * Zips to arrays together
  555. *
  556. * @example
  557. * var a = [ 4, 5, 6 ], b = [ 7, 8, 9 ]
  558. * comb.array.zip([1], [2], [3]) => [[ 1, 2, 3 ]]);
  559. * comb.array.zip([1,2], [2], [3]) => [[ 1, 2, 3 ],[2, null, null]]
  560. * comb.array.zip([1,2,3], a, b) => [[1, 4, 7],[2, 5, 8],[3, 6, 9]]
  561. * comb.array.zip([1,2], a, b) => [[1, 4, 7],[2, 5, 8]]
  562. * comb.array.zip(a, [1,2], [8]) => [[4,1,8],[5,2,null],[6,null,null]]
  563. *
  564. * @param arrays variable number of arrays to zip together
  565. *
  566. * @static
  567. * @memberOf comb.array
  568. */
  569. 1function zip() {
  570. 1 var ret = [];
  571. 1 var arrs = argsToArray(arguments);
  572. 1 if (arrs.length > 1) {
  573. 1 var arr1 = arrs.shift();
  574. 1 if (isArray(arr1)) {
  575. 1 ret = reduce(arr1, function (a, b, i) {
  576. 5 var curr = [b];
  577. 5 for (var j = 0; j < arrs.length; j++) {
  578. 10 var currArr = arrs[j];
  579. 10 if (isArray(currArr) && !misc.isUndefined(currArr[i])) {
  580. 10 curr.push(currArr[i]);
  581. } else {
  582. 0 curr.push(null);
  583. }
  584. }
  585. 5 a.push(curr);
  586. 5 return a;
  587. }, []);
  588. }
  589. }
  590. 1 return ret;
  591. }
  592. /**
  593. * Transposes an array of arrays
  594. * @example
  595. *
  596. * comb.array.transpose([[1,2,3], [4,5,6]]) => [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
  597. * comb.array.transpose([[1,2], [3,4], [5,6]]) => [ [ 1, 3, 5 ], [ 2, 4, 6 ] ]
  598. * comb.array.transpose([[1], [3,4], [5,6]]) => [[1]])
  599. *
  600. * @param [Array[Array[]]] arr Array of arrays
  601. *
  602. * @static
  603. * @memberOf comb.array
  604. */
  605. 1function transpose(arr) {
  606. 3 var ret = [];
  607. 3 if (isArray(arr) && arr.length) {
  608. 3 var last;
  609. 3 forEach(arr, function (a) {
  610. 8 if (isArray(a) && (!last || a.length === last.length)) {
  611. 6 forEach(a, function (b, i) {
  612. 13 if (!ret[i]) {
  613. 6 ret[i] = [];
  614. }
  615. 13 ret[i].push(b);
  616. });
  617. 6 last = a;
  618. }
  619. });
  620. }
  621. 3 return ret;
  622. }
  623. /**
  624. * Retrieves values at specified indexes in the array
  625. *
  626. * @example
  627. *
  628. * var arr =["a", "b", "c", "d"]
  629. * comb.array.valuesAt(arr, 1,2,3) => ["b", "c", "d"];
  630. * comb.array.valuesAt(arr, 1,2,3, 4) => ["b", "c", "d", null];
  631. * comb.array.valuesAt(arr, 0,3) => ["a", "d"];
  632. *
  633. * @param {Array} arr the array to retrieve values from
  634. * @param {Number} indexes variable number of indexes to retrieve
  635. *
  636. * @static
  637. * @memberOf comb.array
  638. */
  639. 1function valuesAt(arr, indexes) {
  640. 3 var ret = [];
  641. 3 indexes = argsToArray(arguments);
  642. 3 arr = indexes.shift();
  643. 3 var l = arr.length;
  644. 3 if (isArray(arr) && indexes.length) {
  645. 3 for (var i = 0; i < indexes.length; i++) {
  646. 9 ret.push(arr[indexes[i]] || null);
  647. }
  648. }
  649. 3 return ret;
  650. }
  651. /**
  652. * Union a variable number of arrays together
  653. *
  654. * @example
  655. *
  656. * comb.array.union(['a','b','c'], ['b','c', 'd']) => ["a", "b", "c", "d"]
  657. * comb.array.union(["a"], ["b"], ["c"], ["d"], ["c"]) => ["a", "b", "c", "d"]
  658. *
  659. * @param arrs variable number of arrays to union
  660. *
  661. * @static
  662. * @memberOf comb.array
  663. */
  664. 1function union() {
  665. 2 var ret = [];
  666. 2 var arrs = argsToArray(arguments);
  667. 2 if (arrs.length > 1) {
  668. 2 ret = removeDuplicates(reduce(arrs, function (a, b) {
  669. 7 return a.concat(b);
  670. }, []));
  671. }
  672. 2 return ret;
  673. }
  674. /**
  675. * Finds the intersection of arrays
  676. * NOTE : this function accepts an arbitrary number of arrays
  677. *
  678. * @example
  679. * comb.array.intersect([1,2], [2,3], [2,3,5]) => [2]
  680. * comb.array.intersect([1,2,3], [2,3,4,5], [2,3,5]) => [2,3]
  681. * comb.array.intersect([1,2,3,4], [2,3,4,5], [2,3,4,5]) => [2,3,4]
  682. * comb.array.intersect([1,2,3,4,5], [1,2,3,4,5], [1,2,3]) => [1,2,3]
  683. * comb.array.intersect([[1,2,3,4,5],[1,2,3,4,5],[1,2,3]]) => [1,2,3]
  684. *
  685. * @param {Array} a
  686. * @param {Array} b
  687. *
  688. * @static
  689. * @memberOf comb.array
  690. */
  691. 1function intersect(a, b) {
  692. 5 var collect = [], set;
  693. 5 var args = argsToArray(arguments);
  694. 5 if (args.length > 1) {
  695. //assume we are intersections all the lists in the array
  696. 5 set = args;
  697. } else {
  698. 0 set = args[0];
  699. }
  700. 5 if (isArray(set)) {
  701. 5 var x = set.shift();
  702. 5 collect = reduce(set, function (a, b) {
  703. 10 return intersection(a, b);
  704. }, x);
  705. }
  706. 5 return removeDuplicates(collect);
  707. }
  708. /**
  709. * Finds the powerset of an array
  710. *
  711. * @example
  712. *
  713. * comb.array.powerSet([1,2]) => [
  714. * [],
  715. * [ 1 ],
  716. * [ 2 ],
  717. * [ 1, 2 ]
  718. * ]
  719. * comb.array.powerSet([1,2,3]) => [
  720. * [],
  721. * [ 1 ],
  722. * [ 2 ],
  723. * [ 1, 2 ],
  724. * [ 3 ],
  725. * [ 1, 3 ],
  726. * [ 2, 3 ],
  727. * [ 1, 2, 3 ]
  728. * ]
  729. * comb.array.powerSet([1,2,3,4]) => [
  730. * [],
  731. * [ 1 ],
  732. * [ 2 ],
  733. * [ 1, 2 ],
  734. * [ 3 ],
  735. * [ 1, 3 ],
  736. * [ 2, 3 ],
  737. * [ 1, 2, 3 ],
  738. * [ 4 ],
  739. * [ 1, 4 ],
  740. * [ 2, 4 ],
  741. * [ 1, 2, 4 ],
  742. * [ 3, 4 ],
  743. * [ 1, 3, 4 ],
  744. * [ 2, 3, 4 ],
  745. * [ 1, 2, 3, 4 ]
  746. * ]
  747. *
  748. * @param {Array} arr the array to find the powerset of
  749. *
  750. * @static
  751. * @memberOf comb.array
  752. */
  753. 1function powerSet(arr) {
  754. 3 var ret = [];
  755. 3 if (isArray(arr) && arr.length) {
  756. 3 ret = reduce(arr, function (a, b) {
  757. 9 var ret = map(a, function (c) {
  758. 25 return c.concat(b);
  759. });
  760. 9 return a.concat(ret);
  761. }, [
  762. []
  763. ]);
  764. }
  765. 3 return ret;
  766. }
  767. /**
  768. * Find the cartesian product of two arrays
  769. *
  770. * @example
  771. *
  772. * comb.array.cartesian([1,2], [2,3]) => [
  773. * [1,2],
  774. * [1,3],
  775. * [2,2],
  776. * [2,3]
  777. * ]
  778. * comb.array.cartesian([1,2], [2,3,4]) => [
  779. * [1,2],
  780. * [1,3],
  781. * [1,4] ,
  782. * [2,2],
  783. * [2,3],
  784. * [2,4]
  785. * ]
  786. * comb.array.cartesian([1,2,3], [2,3,4]) => [
  787. * [1,2],
  788. * [1,3],
  789. * [1,4] ,
  790. * [2,2],
  791. * [2,3],
  792. * [2,4] ,
  793. * [3,2],
  794. * [3,3],
  795. * [3,4]
  796. * ]
  797. *
  798. * @param {Array} a
  799. * @param {Array} b
  800. *
  801. * @static
  802. * @memberOf comb.array
  803. */
  804. 1function cartesian(a, b) {
  805. 10 var ret = [];
  806. 10 if (isArray(a) && isArray(b) && a.length && b.length) {
  807. 7 ret = cross(a[0], b).concat(cartesian(a.slice(1), b));
  808. }
  809. 10 return ret;
  810. }
  811. /**
  812. * Compacts an array removing null or undefined objects from the array.
  813. *
  814. * @example
  815. *
  816. * var x;
  817. * comb.array.compact([1,null,null,x,2]) => [1,2]
  818. * comb.array.compact([1,2]) => [1,2]
  819. *
  820. * @param {Array} arr
  821. * @static
  822. * @memberOf comb.array
  823. */
  824. 1function compact(arr) {
  825. 12 var ret = [];
  826. 12 if (isArray(arr) && arr.length) {
  827. 12 ret = filter(arr, function (item) {
  828. 17 return !misc.isUndefinedOrNull(item);
  829. });
  830. }
  831. 12 return ret;
  832. }
  833. /**
  834. * Creates a new array that is the result of the array concated the number of
  835. * times. If times is not specified or it equals 0 then it defaults to 1.
  836. * @param {Array} arr the array to multiply.
  837. * @param {Number} [times=1] the number of times to multiple the array.
  838. * @return {Array} a new array that is the result of the array multiplied the number of times specified.
  839. *
  840. * @static
  841. * @memberOf comb.array
  842. */
  843. 1function multiply(arr, times) {
  844. 4 times = number.isNumber(times) ? times : 1;
  845. 4 if (!times) {
  846. //make sure times is greater than zero if it is zero then dont multiply it
  847. 1 times = 1;
  848. }
  849. 4 arr = toArray(arr || []);
  850. 4 var ret = [], i = 0;
  851. 4 while (++i <= times) {
  852. 5 ret = ret.concat(arr);
  853. }
  854. 4 return ret;
  855. }
  856. /**
  857. * Flatten multiple arrays into a single array
  858. *
  859. * @example
  860. *
  861. * comb.array.flatten([1,2], [2,3], [3,4]) => [1,2,2,3,3,4]
  862. * comb.array.flatten([1,"A"], [2,"B"], [3,"C"]) => [1,"A",2,"B",3,"C"]
  863. *
  864. * @param array
  865. *
  866. * @static
  867. * @memberOf comb.array
  868. */
  869. 1function flatten(arr) {
  870. 52 var set;
  871. 52 var args = argsToArray(arguments);
  872. 52 if (args.length > 1) {
  873. //assume we are intersections all the lists in the array
  874. 4 set = args;
  875. } else {
  876. 48 set = toArray(arr);
  877. }
  878. 52 return reduce(set, function (a, b) {
  879. 88 return a.concat(b);
  880. }, []);
  881. }
  882. /**
  883. * Plucks values from an array. Effectevily the same as using a array.map implementation. However the porperty specified supports
  884. * a "dot" notation to access nested properties.
  885. *
  886. * @example
  887. *
  888. * var arr = [
  889. * {name:{first:"Fred", last:"Jones"}, age:50, roles:["a", "b", "c"]},
  890. * {name:{first:"Bob", last:"Yukon"}, age:40, roles:["b", "c"]},
  891. * {name:{first:"Alice", last:"Palace"}, age:35, roles:["c"]},
  892. * {name:{first:"Johnny", last:"P."}, age:56, roles:[]}
  893. * ];
  894. * console.log(comb.array.pluck(arr, "name.first")); //["Fred", "Bob", "Alice", "Johnny"]
  895. * console.log(comb.array.pluck(arr, "age")); //[50, 40, 35, 56]
  896. * console.log(comb.array.pluck(arr, "roles.length")); //[3, 2, 1, 0]
  897. * console.log(comb.array.pluck(arr, "roles.0")); //["a", "b", "c", undefined]
  898. *
  899. * @param {Array} arr the array to pluck values from
  900. * @param {String} prop the property to retrieve from each item in the array
  901. * @return {Array} an array containing the plucked properties.
  902. *
  903. * @static
  904. * @memberOf comb.array
  905. */
  906. 1function pluck(arr, prop) {
  907. 0 prop = prop.split(".");
  908. 0 var result = arr.slice(0);
  909. 0 forEach(prop, function (prop) {
  910. 0 var exec = prop.match(/(\w+)\(\)$/);
  911. 0 result = map(result, function (item) {
  912. 0 return exec ? item[exec[1]]() : item[prop];
  913. });
  914. });
  915. 0 return result;
  916. }
  917. /**
  918. * Invokes the method specified in the scope of each item in the array. If you supply addional arguments they will
  919. * be applied to the function.
  920. *
  921. * ```
  922. *
  923. * function person(name, age){
  924. * return {
  925. * getName : function(){
  926. * return name;
  927. * },
  928. * setName : function(newName){
  929. * name = newName;
  930. * },
  931. *
  932. * getOlder : function(){
  933. * age++;
  934. * return this;
  935. * },
  936. *
  937. * getAge : function(){
  938. * return age;
  939. * }
  940. * };
  941. * }
  942. *
  943. * var arr = [person("Bob", 40), person("Alice", 35), person("Fred", 50), person("Johnny", 56)];
  944. *
  945. * console.log(comb.array.invoke(arr, "getName")); //["Bob", "Alice", "Fred", "Johnny"]
  946. *
  947. * console.log(comb.array(arr).invoke("getOlder").invoke("getAge")); //[41, 36, 51, 57];
  948. *
  949. * ```
  950. *
  951. * @param {Array} arr the array to iterate and invoke the functions on
  952. * @param {String|Function} func the function to invoke, if it is a string then the function will be looked up on the
  953. * each item in the array and invoked
  954. * @param [args=null] variable number of arguments to apply to the function
  955. * @return {Array} the return values of the functions invoked.
  956. *
  957. * @static
  958. * @memberOf comb.array
  959. */
  960. 1function invoke(arr, func, args) {
  961. 2 args = argsToArray(arguments, 2);
  962. 2 return map(arr, function (item) {
  963. 8 var exec = isString(func) ? item[func] : func;
  964. 8 return exec.apply(item, args);
  965. });
  966. }
  967. 1var comb = exports;
  968. /**
  969. * @namespace Utilities for working with arrays.
  970. *
  971. * The `comb.array` namespace can be used to decorate arrays with additional chainable funcitonality.
  972. *
  973. * ```
  974. *
  975. * var arr = comb.array([1,3,2,5,4,6]);
  976. * console.log(arr.sum()) //21
  977. * console.log(arr.sort()) //[1,2,3,4,5,6]
  978. * console.log(arr.min()) //[1]
  979. * console.log(arr.max()) [6]
  980. *
  981. * ```
  982. *
  983. * @function
  984. * @ignoreCode
  985. */
  986. 1comb.array = {
  987. toArray:toArray,
  988. sum:sum,
  989. avg:avg,
  990. sort:sort,
  991. min:min,
  992. max:max,
  993. difference:difference,
  994. removeDuplicates:removeDuplicates,
  995. unique:unique,
  996. rotate:rotate,
  997. permutations:permutations,
  998. zip:zip,
  999. transpose:transpose,
  1000. valuesAt:valuesAt,
  1001. union:union,
  1002. intersect:intersect,
  1003. powerSet:powerSet,
  1004. cartesian:cartesian,
  1005. compact:compact,
  1006. multiply:multiply,
  1007. flatten:flatten,
  1008. pluck:pluck,
  1009. invoke:invoke,
  1010. forEach:forEach,
  1011. map:map,
  1012. filter:filter,
  1013. reduce:reduce,
  1014. reduceRight:reduceRight,
  1015. some:some,
  1016. every:every,
  1017. indexOf:indexOf,
  1018. lastIndexOf:lastIndexOf
  1019. };
async.js
Coverage100.00 SLOC1222 LOC169 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 _loopResults(cb, scope, results, index, offset, limit) {
  45. 79 return function () {
  46. 79 return when(results.slice(offset, limit + offset).map(function (r, i) {
  47. 211 var ret = new Promise();
  48. 211 process.nextTick(function () {
  49. 211 try {
  50. 211 when(cb.apply(scope || results, [r, i + offset, results])).then(ret);
  51. } catch (e) {
  52. 5 ret.errback(e);
  53. }
  54. });
  55. 211 return ret;
  56. }));
  57. };
  58. }
  59. 1function asyncLoop(promise, cb, scope, limit) {
  60. 52 if (isNumber(scope)) {
  61. 13 limit = scope;
  62. 13 scope = null;
  63. }
  64. 52 return when(promise).chain(function (results) {
  65. 52 var loopResults = (isArray(results) ? results : [results]);
  66. 52 limit = limit || loopResults.length;
  67. 52 var list = [];
  68. 52 for (var offset = 0, i = 0, l = loopResults.length; offset < l; offset += limit, i++) {
  69. 79 list.push(_loopResults(cb, scope, loopResults, i, offset, limit));
  70. }
  71. 52 var ret = new Promise();
  72. 52 serial(list).then(function (loopResults) {
  73. 42 ret.callback({loopResults: flatten(loopResults) || [], arr: results});
  74. }, function (error) {
  75. 10 error = compact(error);
  76. 10 ret.errback(error.length === 1 ? error[0] : error);
  77. });
  78. 52 return ret;
  79. });
  80. }
  81. 1function normalizeResult(result) {
  82. 92 return isArray(result) ? result : isDefined(result) ? [result] : result;
  83. }
  84. /**
  85. * Loops through the results of an promise. The promise can return an array or just a single item.
  86. *
  87. * ```
  88. * function asyncArr(){
  89. * var ret = new comb.Promise();
  90. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  91. * return ret.promise;
  92. * }
  93. *
  94. * comb.async.forEach(asyncArr(), function(){
  95. * //do something with it
  96. * }).then(function(arr){
  97. * console.log(arr); //[1,2,3,4,5];
  98. * });
  99. *
  100. * ```
  101. *
  102. * You may also return a promise from the iterator block.
  103. *
  104. * ```
  105. * var myNewArr = [];
  106. *
  107. * comb.async.forEach(asyncArr(), function(item, index){
  108. * var ret = new comb.Promise();
  109. * process.nextTick(function(){
  110. * myNewArr.push([item, index]);
  111. * ret.callback();
  112. * });
  113. * return ret.promise();
  114. * }).then(function(){
  115. * console.log(myNewArr) //[[1,0], [2,1], [3,2], [4,3], [5,4]]
  116. * });
  117. * ```
  118. *
  119. *
  120. * @param {comb.Promise|Array} promise the promise or array to loop through
  121. * @param {Function} iterator a function to invoke for each item
  122. * @param [scope] optional scope to execute the function in.
  123. * @return {comb.Promise} a promise that is resolved with the original array.
  124. * @static
  125. * @memberof comb.async
  126. * @name forEach
  127. */
  128. 1function asyncForEach(promise, iterator, scope, limit) {
  129. 11 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  130. 9 return results.arr;
  131. }));
  132. }
  133. /**
  134. * Loops through the results of an promise resolving with the return value of the iterator function.
  135. * The promise can return an array or just a single item.
  136. *
  137. * ```
  138. * function asyncArr(){
  139. * var ret = new comb.Promise();
  140. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  141. * return ret.promise;
  142. * }
  143. *
  144. * comb.async.map(asyncArr(), function(item){
  145. * return item * 2;
  146. * }).then(function(arr){
  147. * console.log(arr); //[2,4,6,8,10];
  148. * });
  149. *
  150. * ```
  151. *
  152. * You may also return a promise from the iterator block.
  153. *
  154. * ```
  155. * comb.async.map(asyncArr(), function(item, index){
  156. * var ret = new comb.Promise();
  157. * process.nextTick(function(){
  158. * ret.callback(item * 2);
  159. * });
  160. * return ret.promise();
  161. * }).then(function(){
  162. * console.log(myNewArr) //[2,4,6,8,10];
  163. * });
  164. * ```
  165. *
  166. *
  167. * @param {comb.Promise|Array} promise the promise or array to loop through
  168. * @param {Function} iterator a function to invoke for each item
  169. * @param [scope] optional scope to execute the function in.
  170. * @return {comb.Promise} a promise that is resolved with the mapped array.
  171. * @static
  172. * @memberof comb.async
  173. * @name map
  174. */
  175. 1function asyncMap(promise, iterator, scope, limit) {
  176. 15 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  177. 13 return results.loopResults;
  178. }));
  179. }
  180. /**
  181. * Loops through the results of an promise resolving with the filtered array.
  182. * The promise can return an array or just a single item.
  183. *
  184. * ```
  185. * function asyncArr(){
  186. * var ret = new comb.Promise();
  187. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  188. * return ret.promise;
  189. * }
  190. *
  191. * comb.async.filter(asyncArr(), function(item){
  192. * return item % 2;
  193. * }).then(function(arr){
  194. * console.log(arr); //[1,3,5];
  195. * });
  196. *
  197. * ```
  198. *
  199. * You may also return a promise from the iterator block.
  200. *
  201. * ```
  202. * comb.async.filter(asyncArr(), function(item, index){
  203. * var ret = new comb.Promise();
  204. * process.nextTick(function(){
  205. * ret.callback(item % 2);
  206. * });
  207. * return ret.promise();
  208. * }).then(function(){
  209. * console.log(myNewArr) //[1,3,5];
  210. * })
  211. * ```
  212. *
  213. *
  214. * @param {comb.Promise|Array} promise the promise or array to loop through
  215. * @param {Function} iterator a function to invoke for each item
  216. * @param [scope] optional scope to execute the function in.
  217. * @return {comb.Promise} a promise that is resolved with the filtered array.
  218. * @static
  219. * @memberof comb.async
  220. * @name filter
  221. */
  222. 1function asyncFilter(promise, iterator, scope, limit) {
  223. 7 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  224. 5 var loopResults = results.loopResults, resultArr = results.arr;
  225. 5 return (isArray(resultArr) ? resultArr : [resultArr]).filter(function (res, i) {
  226. 21 return loopResults[i];
  227. });
  228. }));
  229. }
  230. /**
  231. * Loops through the results of an promise resolving with true if every item passed, false otherwise.
  232. * The promise can return an array or just a single item.
  233. *
  234. * ```
  235. * function asyncArr(){
  236. * var ret = new comb.Promise();
  237. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  238. * return ret.promise;
  239. * }
  240. *
  241. * comb.async.every(asyncArr(), function(item){
  242. * return item <= 5;
  243. * }).then(function(every){
  244. * console.log(every); //true
  245. * });
  246. *
  247. * ```
  248. *
  249. * You may also return a promise from the iterator block.
  250. *
  251. * ```
  252. * comb.async.every(asyncArr(), function(item, index){
  253. * var ret = new comb.Promise();
  254. * process.nextTick(function(){
  255. * ret.callback(item == 1);
  256. * });
  257. * return ret.promise();
  258. * }).then(function(){
  259. * console.log(myNewArr) //false;
  260. * })
  261. * ```
  262. *
  263. *
  264. * @param {comb.Promise|Array} promise the promise or array to loop through
  265. * @param {Function} iterator a function to invoke for each item
  266. * @param [scope] optional scope to execute the function in.
  267. * @return {comb.Promise} a promise that is resolved true if every item passed false otherwise.
  268. * @static
  269. * @memberof comb.async
  270. * @name every
  271. */
  272. 1function asyncEvery(promise, iterator, scope, limit) {
  273. 9 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  274. 7 return results.loopResults.every(function (res) {
  275. 19 return !!res;
  276. });
  277. }));
  278. }
  279. /**
  280. * Loops through the results of an promise resolving with true if some items passed, false otherwise.
  281. * The promise can return an array or just a single item.
  282. *
  283. * ```
  284. * function asyncArr(){
  285. * var ret = new comb.Promise();
  286. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  287. * return ret.promise;
  288. * }
  289. *
  290. * comb.async.some(asyncArr(), function(item){
  291. * return item == 1;
  292. * }).then(function(every){
  293. * console.log(every); //true
  294. * });
  295. *
  296. * ```
  297. *
  298. * You may also return a promise from the iterator block.
  299. *
  300. * ```
  301. * comb.async.some(asyncArr(), function(item, index){
  302. * var ret = new comb.Promise();
  303. * process.nextTick(function(){
  304. * ret.callback(item > 5);
  305. * });
  306. * return ret.promise();
  307. * }).then(function(){
  308. * console.log(myNewArr) //false;
  309. * })
  310. * ```
  311. *
  312. *
  313. * @param {comb.Promise|Array} promise the promise or array to loop through
  314. * @param {Function} iterator a function to invoke for each item
  315. * @param [scope] optional scope to execute the function in.
  316. * @return {comb.Promise} a promise that is resolved with true if some items passed false otherwise.
  317. * @static
  318. * @memberof comb.async
  319. * @name some
  320. */
  321. 1function asyncSome(promise, iterator, scope, limit) {
  322. 10 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  323. 8 return results.loopResults.some(function (res) {
  324. 20 return !!res;
  325. });
  326. }));
  327. }
  328. /**
  329. * Zips results from promises into an array.
  330. *
  331. * ```
  332. * function asyncArr(){
  333. * var ret = new comb.Promise();
  334. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  335. * return ret.promise;
  336. * }
  337. *
  338. * comb.async.zip(asyncArr(), asyncArr()).then(function(zipped){
  339. * console.log(zipped); //[[1,1],[2,2],[3,3], [4,4], [5,5]]
  340. * });
  341. *
  342. * comb.async.array(asyncArr()).zip(asyncArr()).then(function(zipped){
  343. * console.log(zipped); //[[1,1],[2,2],[3,3], [4,4], [5,5]]
  344. * });
  345. *
  346. * ```
  347. *
  348. *
  349. * @return {comb.Promise} an array with all the arrays zipped together.
  350. * @static
  351. * @memberof comb.async
  352. * @name zip
  353. */
  354. 1function asyncZip() {
  355. 1 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  356. 1 return zip.apply(array, normalizeResult(result).map(function (arg) {
  357. 3 return isArray(arg) ? arg : [arg];
  358. }));
  359. }));
  360. }
  361. /**
  362. * Async version of {@link comb.array.avg}.
  363. *
  364. *
  365. * ```
  366. * function asyncArr(){
  367. * var ret = new comb.Promise();
  368. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  369. * return ret.promise;
  370. * }
  371. *
  372. * comb.async.array(asyncArr()).avg().then(function(avg){
  373. * console.log(avg); //3
  374. * })
  375. *
  376. * comb.async.avg(asyncArr()).then(function(avg){
  377. * console.log(avg); //3
  378. * })
  379. *
  380. * ```
  381. *
  382. * @static
  383. * @memberof comb.async
  384. * @name avg
  385. */
  386. 1function asyncAvg(avgPromise) {
  387. 5 return when(avgPromise).chain(function (result) {
  388. 5 return avg.call(array, normalizeResult(result));
  389. });
  390. }
  391. /**
  392. * Async version of {@link comb.array.cartesian}.
  393. *
  394. * ```
  395. * function asyncArr(){
  396. * var ret = new comb.Promise();
  397. * process.nextTick(ret.callback.bind(ret, [1,2]);
  398. * return ret.promise;
  399. * }
  400. *
  401. * comb.async.array(asyncArr()).cartesian([1,2,3]).then(function(avg){
  402. * console.log(avg); //[ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 2 ], [ 2, 3 ] ]
  403. * })
  404. *
  405. * comb.async.cartesian(asyncArr(), [1,2,3]).then(function(avg){
  406. * console.log(avg); //[ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 2 ], [ 2, 3 ] ]
  407. * })
  408. *
  409. * ```
  410. *
  411. * @static
  412. * @memberof comb.async
  413. * @name cartesian
  414. */
  415. 1function asyncCartesian() {
  416. 3 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  417. 3 return cartesian.apply(array, normalizeResult(result).map(function (arg) {
  418. 6 return isArray(arg) ? arg : [arg];
  419. }));
  420. }));
  421. }
  422. /**
  423. * Async version of {@link comb.array.compact}.
  424. *
  425. * ```
  426. * function asyncArr(){
  427. * var ret = new comb.Promise();
  428. * process.nextTick(ret.callback.bind(ret, [1,null,2,null,3,null,4,null,5]);
  429. * return ret.promise;
  430. * }
  431. *
  432. * comb.async.array(asyncArr()).compact().then(function(compacted){
  433. * console.log(compacted); //[1,2,3,4,5]
  434. * })
  435. *
  436. * comb.async.compact(asyncArr()).then(function(compacted){
  437. * console.log(compacted); //[1,2,3,4,5]
  438. * })
  439. *
  440. * ```
  441. *
  442. * @static
  443. * @memberof comb.async
  444. * @name compact
  445. */
  446. 1function asyncCompact(arr) {
  447. 2 return asyncArray(when(arr).chain(function (result) {
  448. 2 return compact.call(array, normalizeResult(result));
  449. }));
  450. }
  451. /**
  452. * Async version of {@link comb.array.difference}.
  453. *
  454. * ```
  455. * function asyncArr(){
  456. * var ret = new comb.Promise();
  457. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  458. * return ret.promise;
  459. * }
  460. *
  461. * comb.async.array(asyncArr()).difference([3,4,5]).then(function(diff){
  462. * console.log(diff); //[1,2]
  463. * })
  464. *
  465. * comb.async.difference(asyncArr(), [3,4,5]).then(function(diff){
  466. * console.log(diff); //[1,2]
  467. * })
  468. *
  469. * ```
  470. *
  471. * @static
  472. * @memberof comb.async
  473. * @name difference
  474. */
  475. 1function asyncDifference() {
  476. 5 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  477. 5 return difference.apply(array, normalizeResult(result).map(function (arg) {
  478. 11 return isArray(arg) ? arg : [arg];
  479. }));
  480. }));
  481. }
  482. /**
  483. * Async version of {@link comb.array.flatten}.
  484. *
  485. * ```
  486. * function asyncArr(){
  487. * var ret = new comb.Promise();
  488. * process.nextTick(ret.callback.bind(ret, [[1],[2],[3],[4],[5]]);
  489. * return ret.promise;
  490. * }
  491. *
  492. * comb.async.array(asyncArr()).flatten().then(function(flat){
  493. * console.log(flat); //[1,2,3,4,5]
  494. * });
  495. *
  496. * comb.async.flatten(asyncArr()).then(function(flat){
  497. * console.log(flat); //[1,2,3,4,5]
  498. * });
  499. *
  500. * ```
  501. *
  502. * @static
  503. * @memberof comb.async
  504. * @name flatten
  505. */
  506. 1function asyncFlatten() {
  507. 4 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  508. 4 return flatten.apply(array, normalizeResult(result).map(function (arg) {
  509. 12 return isArray(arg) ? arg : [arg];
  510. }));
  511. }));
  512. }
  513. /**
  514. * Async version of {@link comb.array.intersect}.
  515. *
  516. * ```
  517. * function asyncArr(){
  518. * var ret = new comb.Promise();
  519. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  520. * return ret.promise;
  521. * }
  522. *
  523. * comb.async.array(asyncArr()).intersect([3,4], [3]).then(function(intersection){
  524. * console.log(intersection); //[3]
  525. * });
  526. *
  527. * comb.async.intersect(asyncArr(), [3,4]).then(function(intersection){
  528. * console.log(intersection); //[3,4]
  529. * });
  530. *
  531. * ```
  532. *
  533. * @static
  534. * @memberof comb.async
  535. * @name intersect
  536. */
  537. 1function asyncIntersect() {
  538. 5 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  539. 5 return intersect.apply(array, normalizeResult(result).map(function (arg) {
  540. 15 return isArray(arg) ? arg : [arg];
  541. }));
  542. }));
  543. }
  544. /**
  545. * Async version of {@link comb.array.max}.
  546. *
  547. * ```
  548. * function asyncArr(){
  549. * var ret = new comb.Promise();
  550. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  551. * return ret.promise;
  552. * }
  553. *
  554. * comb.async.array(asyncArr()).max().then(function(max){
  555. * console.log(max); //5
  556. * })
  557. *
  558. * comb.async.max(asyncArr()).then(function(max){
  559. * console.log(max); //5
  560. * })
  561. *
  562. * ```
  563. *
  564. * @static
  565. * @memberof comb.async
  566. * @name max
  567. */
  568. 1function asyncMax() {
  569. 7 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  570. 7 if (isFunction(last) || isString(last)) {
  571. 3 cmp = last;
  572. } else {
  573. 4 args.push(last);
  574. }
  575. 7 return when.apply(null, args).chain(function (result) {
  576. 7 return max.call(array, normalizeResult(result), cmp);
  577. });
  578. }
  579. /**
  580. * Async version of {@link comb.array.min}.
  581. *
  582. * ```
  583. * function asyncArr(){
  584. * var ret = new comb.Promise();
  585. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  586. * return ret.promise;
  587. * }
  588. *
  589. * comb.async.array(asyncArr()).min().then(function(min){
  590. * console.log(min) //3
  591. * });
  592. *
  593. * comb.async.min(asyncArr()).then(function(min){
  594. * console.log(min) //3
  595. * });
  596. *
  597. * ```
  598. *
  599. * @static
  600. * @memberof comb.async
  601. * @name min
  602. */
  603. 1function asyncMin() {
  604. 7 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  605. 7 if (isFunction(last) || isString(last)) {
  606. 3 cmp = last;
  607. } else {
  608. 4 args.push(last);
  609. }
  610. 7 return when.apply(null, args).chain(function (result) {
  611. 7 return min.call(array, normalizeResult(result), cmp);
  612. });
  613. }
  614. /**
  615. * Async version of {@link comb.array.sort}.
  616. *
  617. * ```
  618. * function asyncArr(){
  619. * var ret = new comb.Promise();
  620. * process.nextTick(ret.callback.bind(ret, [1,3,2,5,4]);
  621. * return ret.promise;
  622. * }
  623. *
  624. * comb.async.array(asyncArr()).sort().then(function(sorted){
  625. * console.log(sorted); //[1,2,3,4,5]
  626. * });
  627. *
  628. * comb.async.sort(asyncArr()).then(function(sorted){
  629. * console.log(sorted); //[1,2,3,4,5]
  630. * });
  631. *
  632. * ```
  633. *
  634. * @static
  635. * @memberof comb.async
  636. * @name sort
  637. */
  638. 1function asyncSort() {
  639. 8 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  640. 8 if (isFunction(last) || isString(last)) {
  641. 4 cmp = last;
  642. } else {
  643. 4 args.push(last);
  644. }
  645. 8 return asyncArray(when.apply(null, args).chain(function (result) {
  646. 8 return sort.call(array, normalizeResult(result), cmp);
  647. }));
  648. }
  649. /**
  650. * Async version of {@link comb.array.multiply}.
  651. *
  652. * ```
  653. * function asyncArr(){
  654. * var ret = new comb.Promise();
  655. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  656. * return ret.promise;
  657. * }
  658. *
  659. * comb.async.array(asyncArr()).multiply(2).then(function(mult){
  660. * console.log(mult); //[1,2,3,4,5,1,2,3,4,5]
  661. * });
  662. *
  663. * comb.async.multiply(asyncArr(),2 ).then(function(mult){
  664. * console.log(mult); //[1,2,3,4,5,1,2,3,4,5]
  665. * });
  666. *
  667. * ```
  668. *
  669. * @static
  670. * @memberof comb.async
  671. * @name multiply
  672. */
  673. 1function asyncMultiply() {
  674. 4 var args = argsToArray(arguments), last = args.pop(), times = null;
  675. 4 if (isNumber(last)) {
  676. 3 times = last;
  677. } else {
  678. 1 args.push(last);
  679. }
  680. 4 return asyncArray(when.apply(null, args).chain(function (result) {
  681. 4 return multiply.call(array, normalizeResult(result), times);
  682. }));
  683. }
  684. /**
  685. * Async version of {@link comb.array.permutations}.
  686. *
  687. * ```
  688. * function asyncArr(){
  689. * var ret = new comb.Promise();
  690. * process.nextTick(ret.callback.bind(ret, [1,2,3]);
  691. * return ret.promise;
  692. * }
  693. *
  694. * comb.async.array(asyncArr()).permutations().then(function(permutations){
  695. * console.log(permutations) //[ [ 1, 2, 3 ],
  696. * // [ 1, 3, 2 ],
  697. * // [ 2, 3, 1 ],
  698. * // [ 2, 1, 3 ],
  699. * // [ 3, 1, 2 ],
  700. * // [ 3, 2, 1 ] ]
  701. * });
  702. *
  703. * comb.async.permutations(asyncArr()).then(function(permutations){
  704. * console.log(permutations) //[ [ 1, 2, 3 ],
  705. * // [ 1, 3, 2 ],
  706. * // [ 2, 3, 1 ],
  707. * // [ 2, 1, 3 ],
  708. * // [ 3, 1, 2 ],
  709. * // [ 3, 2, 1 ] ]
  710. * });
  711. *
  712. * ```
  713. *
  714. * @static
  715. * @memberof comb.async
  716. * @name permutations
  717. */
  718. 1function asyncPermutations() {
  719. 5 var args = argsToArray(arguments), last = args.pop(), times = null;
  720. 5 if (isNumber(last)) {
  721. 4 times = last;
  722. } else {
  723. 1 args.push(last);
  724. }
  725. 5 return asyncArray(when.apply(null, args).chain(function (result) {
  726. 5 return permutations.call(array, normalizeResult(result), times);
  727. }));
  728. }
  729. /**
  730. * Async version of {@link comb.array.powerSet}.
  731. *
  732. * ```
  733. * function asyncArr(){
  734. * var ret = new comb.Promise();
  735. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  736. * return ret.promise;
  737. * }
  738. *
  739. * comb.async.array(asyncArr()).powerSet().then(function(set){
  740. * console.log(set); //[ [], [ 1 ], [ 2 ], [ 1, 2 ], [ 3 ], [ 1, 3 ], [ 2, 3 ], [ 1, 2, 3 ] ]
  741. * });
  742. *
  743. * comb.async.powerSet(asyncArr()).then(function(set){
  744. * console.log(set); //[ [], [ 1 ], [ 2 ], [ 1, 2 ], [ 3 ], [ 1, 3 ], [ 2, 3 ], [ 1, 2, 3 ] ]
  745. * });
  746. *
  747. * ```
  748. *
  749. * @static
  750. * @memberof comb.async
  751. * @name powerSet
  752. */
  753. 1function asyncPowerSet(arr) {
  754. 3 return asyncArray(when(arr).chain(function (result) {
  755. 3 return powerSet.call(array, normalizeResult(result));
  756. }));
  757. }
  758. /**
  759. * Async version of {@link comb.array.removeDuplicates}.
  760. *
  761. * ```
  762. * function asyncArr(){
  763. * var ret = new comb.Promise();
  764. * process.nextTick(ret.callback.bind(ret, [1,2,2,3,3,3,4,4,4]);
  765. * return ret.promise;
  766. * }
  767. *
  768. * comb.async.array(asyncArr()).removeDuplicates().then(function(unique){
  769. * console.log(unique); // [1, 2, 3, 4]
  770. * });
  771. *
  772. * comb.async.removeDuplicates(asyncArray()).then(function(unique){
  773. * console.log(unique); // [1, 2, 3, 4]
  774. * });
  775. * ```
  776. *
  777. * @static
  778. * @memberof comb.async
  779. * @name removeDuplicates
  780. */
  781. 1function asyncRemoveDuplicates(arr) {
  782. 4 return asyncArray(when(arr).chain(function (result) {
  783. 4 return removeDuplicates.call(array, normalizeResult(result));
  784. }));
  785. }
  786. /**
  787. * Async version of {@link comb.array.rotate}.
  788. *
  789. * ```
  790. * function asyncArr(){
  791. * var ret = new comb.Promise();
  792. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  793. * return ret.promise;
  794. * }
  795. *
  796. * comb.async.array(asyncArr()).rotate(2).then(function(rotated){
  797. * console.log(rotated); // [ 3, 4, 5, 1, 2 ]
  798. * });
  799. *
  800. * comb.async.rotate(asyncArr(), 2).then(function(rotated){
  801. * console.log(rotated); // [ 3, 4, 5, 1, 2 ]
  802. * });
  803. *
  804. * ```
  805. *
  806. * @static
  807. * @memberof comb.async
  808. * @name rotate
  809. */
  810. 1function asyncRotate() {
  811. 8 var args = argsToArray(arguments), last = args.pop(), times = null;
  812. 8 if (isNumber(last)) {
  813. 7 times = last;
  814. } else {
  815. 1 args.push(last);
  816. }
  817. 8 return asyncArray(when.apply(null, args).chain(function (result) {
  818. 8 return rotate.call(array, normalizeResult(result), times);
  819. }));
  820. }
  821. /**
  822. * Async version of {@link comb.array.sum}.
  823. *
  824. * ```
  825. * function asyncArr(){
  826. * var ret = new comb.Promise();
  827. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  828. * return ret.promise;
  829. * }
  830. *
  831. * comb.async.array(asyncArr()).sum().then(function(sum){
  832. * console.log(sum) //15
  833. * })
  834. *
  835. * comb.async.sum(asyncArr()).then(function(sum){
  836. * console.log(sum) //15
  837. * })
  838. *
  839. * ```
  840. *
  841. * @static
  842. * @memberof comb.async
  843. * @name sum
  844. */
  845. 1function asyncSum(arr) {
  846. 6 return when(arr).chain(function (result) {
  847. 6 return sum.call(array, normalizeResult(result));
  848. });
  849. }
  850. /**
  851. * Async version of {@link comb.array.transpose}.
  852. *
  853. * ```
  854. * function asyncArr(){
  855. * var ret = new comb.Promise();
  856. * process.nextTick(ret.callback.bind(ret, [[1, 2, 3], [4, 5, 6]]);
  857. * return ret.promise;
  858. * }
  859. *
  860. * comb.async.array(asyncArr()).transpose().then(function(transposed){
  861. * console.log(transposed); //[ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
  862. * });
  863. *
  864. * comb.async.transpose(asyncArr()).then(function(transposed){
  865. * console.log(transposed); //[ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
  866. * });
  867. *
  868. * ```
  869. *
  870. * @static
  871. * @memberof comb.async
  872. * @name transpose
  873. */
  874. 1function asyncTranspose(arr) {
  875. 3 return asyncArray(when(arr).chain(function (result) {
  876. 3 return transpose.call(array, normalizeResult(result));
  877. }));
  878. }
  879. /**
  880. * Async version of {@link comb.array.union}.
  881. *
  882. * ```
  883. * function asyncArr(){
  884. * var ret = new comb.Promise();
  885. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  886. * return ret.promise;
  887. * }
  888. *
  889. * comb.async.array(asyncArr()).union([3],[7], [9,10]).then(function(union){
  890. * console.log(union); //[1,2,3,4,5,7,9,10]
  891. * });
  892. *
  893. * comb.async.union(asyncArr(), [3],[7], [9,10]).then(function(union){
  894. * console.log(union); //[1,2,3,4,5,7,9,10]
  895. * });
  896. *
  897. * ```
  898. *
  899. * @static
  900. * @memberof comb.async
  901. * @name union
  902. */
  903. 1function asyncUnion() {
  904. 2 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  905. 2 return union.apply(array, (normalizeResult(result)).map(function (arg) {
  906. 7 return isArray(arg) ? arg : [arg];
  907. }));
  908. }));
  909. }
  910. /**
  911. * Async version of {@link comb.array.unique}.
  912. *
  913. * ```
  914. * function asyncArr(){
  915. * var ret = new comb.Promise();
  916. * process.nextTick(ret.callback.bind(ret, [1,2,2,3,3,4,4,5]);
  917. * return ret.promise;
  918. * }
  919. *
  920. * comb.async.array(asyncArr()).unique().then(function(unique){
  921. * console.log(unique); //[1,2,3,4,5]
  922. * });
  923. *
  924. * comb.async.unique(asyncArr()).then(function(unique){
  925. * console.log(unique); //[1,2,3,4,5]
  926. * });
  927. *
  928. * ```
  929. *
  930. * @static
  931. * @memberof comb.async
  932. * @name unique
  933. */
  934. 1function asyncUnique() {
  935. 2 return asyncRemoveDuplicates.apply(null, arguments);
  936. }
  937. /**
  938. * Async version of {@link comb.array.valuesAt}.
  939. *
  940. * ```
  941. * function asyncArr(){
  942. * var ret = new comb.Promise();
  943. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  944. * return ret.promise;
  945. * }
  946. *
  947. * comb.async.array(asyncArr()).valuesAt(2,3,4).then(function(values){
  948. * console.log(values); //[3,4,5]
  949. * });
  950. *
  951. * comb.async.valuesAt(asyncArr(), 2,3,4).then(function(values){
  952. * console.log(values); //[3,4,5]
  953. * });
  954. *
  955. * ```
  956. *
  957. * @static
  958. * @memberof comb.async
  959. * @name valuesAt
  960. */
  961. 1function asyncValuesAt(arrPromise) {
  962. 3 var args = argsToArray(arguments, 1);
  963. 3 return asyncArray(when(arrPromise).chain(function (result) {
  964. 3 return when(valuesAt.apply(array, [normalizeResult(result)].concat(args)));
  965. }));
  966. }
  967. /**
  968. * Async version of {@link comb.array.pluck}.
  969. *
  970. * ```
  971. * var when = comb.when,
  972. * array = comb.async.array;
  973. * function asyncArr(){
  974. * var ret = new comb.Promise();
  975. * process.nextTick(ret.callback.bind(ret, [
  976. * {name:{first:when("Fred"), last:"Jones"}, age:when(50), roles:["a", "b", "c"]},
  977. * {name:{first:"Bob", last:"Yukon"}, age:40, roles:when(["b", "c"])},
  978. * {name:{first:"Alice", last:"Palace"}, age:when(35), roles:["c"]},
  979. * {name:{first:when("Johnny"), last:"P."}, age:56, roles:when([])}
  980. * ]);
  981. * return ret.promise;
  982. * }
  983. *
  984. * array(asyncArr()).pluck("name.first").then(function(values){
  985. * console.log(values); //["Fred", "Bob", "Alice", "Johnny"]
  986. * });
  987. *
  988. * pluck(asyncArr(), "age").then(function(values){
  989. * console.log(values); //[50, 40, 35, 56]
  990. * });
  991. *
  992. * ```
  993. *
  994. * @static
  995. * @memberof comb.async
  996. * @name pluck
  997. */
  998. 1function asyncPluck(arrPromise, property) {
  999. 5 var args = argsToArray(arguments, 1);
  1000. 5 return asyncArray(when(arrPromise).chain(function (result) {
  1001. 5 var prop = property.split(".");
  1002. 5 result = normalizeResult(result);
  1003. 5 return asyncArray(prop).forEach(function (prop) {
  1004. 8 var exec = prop.match(/(\w+)\(\)$/);
  1005. 8 return asyncArray(result).map(function (item) {
  1006. 32 return exec ? item[exec[1]]() : item[prop];
  1007. }, 1).chain(function (res) {
  1008. 8 result = res;
  1009. });
  1010. }, 1).chain(function () {
  1011. 5 return result;
  1012. });
  1013. }));
  1014. }
  1015. /**
  1016. *
  1017. * Async version of {@link comb.array.invoke}.
  1018. *
  1019. * ```
  1020. * function person(name, age) {
  1021. * return {
  1022. * getName:function () {
  1023. * return when(name);
  1024. * },
  1025. * getOlder:function () {
  1026. * age++;
  1027. * return when(this);
  1028. * },
  1029. * getAge:function () {
  1030. * return when(age);
  1031. * }
  1032. * };
  1033. * }
  1034. *
  1035. * var arr = [when(person("Bob", 40)), when(person("Alice", 35)), when(person("Fred", 50)), when(person("Johnny", 56))];
  1036. * console.log(comb.async.invoke(arr, "getName")); //["Bob", "Alice", "Fred", "Johnny"]
  1037. * console.log(array(arr).invoke("getOlder").pluck("getAge")) //[41, 36, 51, 57]
  1038. * ```
  1039. * @static
  1040. * @memberOf comb.async
  1041. * @name invoke
  1042. */
  1043. 1function asyncInvoke(arrPromise) {
  1044. 2 var args = argsToArray(arguments, 1);
  1045. 2 return asyncArray(when(arrPromise).chain(function (result) {
  1046. 2 return when(invoke.apply(array, [normalizeResult(result)].concat(args)));
  1047. }));
  1048. }
  1049. 1var asyncExports = (exports.async = {
  1050. array: asyncArray,
  1051. forEach: asyncForEach,
  1052. map: asyncMap,
  1053. filter: asyncFilter,
  1054. every: asyncEvery,
  1055. some: asyncSome,
  1056. zip: asyncZip,
  1057. sum: asyncSum,
  1058. avg: asyncAvg,
  1059. sort: asyncSort,
  1060. min: asyncMin,
  1061. max: asyncMax,
  1062. difference: asyncDifference,
  1063. removeDuplicates: asyncRemoveDuplicates,
  1064. unique: asyncUnique,
  1065. rotate: asyncRotate,
  1066. permutations: asyncPermutations,
  1067. transpose: asyncTranspose,
  1068. valuesAt: asyncValuesAt,
  1069. union: asyncUnion,
  1070. intersect: asyncIntersect,
  1071. powerSet: asyncPowerSet,
  1072. cartesian: asyncCartesian,
  1073. compact: asyncCompact,
  1074. multiply: asyncMultiply,
  1075. flatten: asyncFlatten,
  1076. pluck: asyncPluck,
  1077. invoke: asyncInvoke
  1078. });
  1079. 1var methods = ["forEach", "map", "filter", "some", "every", "zip", "sum", "avg", "sort", "min", "max", "difference", "removeDuplicates", "unique", "rotate",
  1080. "permutations", "transpose", "valuesAt", "union", "intersect", "powerSet", "cartesian", "compact",
  1081. "multiply", "flatten", "pluck", "invoke"];
  1082. /**
  1083. * Exposes array methods on a {@link comb.Promise}.
  1084. *
  1085. * The methods added are.
  1086. *
  1087. * * forEach : See {@link comb.async.forEach}.
  1088. * * map : See {@link comb.async.map}.
  1089. * * filter : See {@link comb.async.filter}.
  1090. * * some : See {@link comb.async.some}.
  1091. * * every : See {@link comb.async.every}.
  1092. * * zip : See {@link comb.async.zip}.
  1093. * * sum : See {@link comb.async.sum}.
  1094. * * avg : See {@link comb.async.avg}.
  1095. * * sort : See {@link comb.async.sort}.
  1096. * * min : See {@link comb.async.min}.
  1097. * * max : See {@link comb.async.max}.
  1098. * * difference : See {@link comb.async.difference}.
  1099. * * removeDuplicates : See {@link comb.async.removeDuplicates}.
  1100. * * unique : See {@link comb.async.unique}.
  1101. * * rotate : See {@link comb.async.rotate}.
  1102. * * permutations : See {@link comb.async.permutations}.
  1103. * * transpose : See {@link comb.async.transpose}.
  1104. * * valuesAt : See {@link comb.async.valuesAt}.
  1105. * * union : See {@link comb.async.union}.
  1106. * * intersect : See {@link comb.async.intersect}.
  1107. * * powerSet : See {@link comb.async.powerSet}.
  1108. * * cartesian : See {@link comb.async.cartesian}.
  1109. * * compact : See {@link comb.async.compact}.
  1110. * * multiply : See {@link comb.async.multiply}.
  1111. * * flatten : See {@link comb.async.flatten}.
  1112. * * pluck : See {@link comb.async.pluck}.
  1113. * * invoke : See {@link comb.async.invoke}.
  1114. *
  1115. * When using this method each of the methods are chainable so you can combine actions.
  1116. *
  1117. * ```
  1118. * var array = comb.async.array;
  1119. * function asyncArr(){
  1120. * var ret = new comb.Promise();
  1121. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  1122. * return ret.promise;
  1123. * }
  1124. *
  1125. * array(asyncArr())
  1126. * .map(function (num, i) {
  1127. * return num * (i + 1);
  1128. * }).filter(function (num) {
  1129. * return num % 2;
  1130. * }).avg().then(function(avg){
  1131. * console.log(avg); //11.666666666666666
  1132. * });
  1133. * ```
  1134. * @param {comb.Promise|[]} p the promise or array to use.
  1135. *
  1136. * @static
  1137. * @memberof comb.async
  1138. * @name array
  1139. */
  1140. 1function asyncArray(p) {
  1141. 626 var ret;
  1142. 626 if (!p || !p.__isArrayAsync__) {
  1143. 410 ret = merge(when(p), {
  1144. promise: function () {
  1145. 216 return asyncArray(this)
  1146. }
  1147. });
  1148. 410 forEach(methods, function (m) {
  1149. 11070 var func = asyncExports[m];
  1150. 11070 ret[m] = function () {
  1151. 143 var args = argsToArray(arguments), mRet = new Promise();
  1152. 143 process.nextTick(function () {
  1153. 143 func.apply(null, [ret].concat(args)).then(mRet);
  1154. });
  1155. 143 return asyncArray(mRet);
  1156. };
  1157. });
  1158. 410 ret.__isArrayAsync__ = true;
  1159. } else {
  1160. 216 ret = p;
  1161. }
  1162. 626 return ret;
  1163. }
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"));
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 SLOC558 LOC162 Missed0
  1. /**
  2. * Used to keep track of classes and to create unique ids
  3. * @ignore
  4. */
  5. 1var classCounter = 0, Base,
  6. _base = require("./base"),
  7. isHash = _base.isHash,
  8. isArray = _base.isArray;
  9. 1function callSuper(args, a) {
  10. 238 var meta = this.__meta,
  11. supers = meta.supers,
  12. l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos;
  13. 238 if (l > pos) {
  14. 238 a && (args = a);
  15. 238 var name = superMeta.name, f = superMeta.f, m;
  16. 238 do {
  17. 238 m = supers[pos][name];
  18. 238 if ("function" === typeof m && (m = m._f || m) !== f) {
  19. 234 superMeta.pos = 1 + pos;
  20. 234 return m.apply(this, args);
  21. }
  22. } while (l > ++pos);
  23. }
  24. 4 return null;
  25. }
  26. 1function getSuper() {
  27. 2 var meta = this.__meta,
  28. supers = meta.supers,
  29. l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos;
  30. 2 if (l > pos) {
  31. 2 var name = superMeta.name, f = superMeta.f, m;
  32. 2 do {
  33. 3 m = supers[pos][name];
  34. 3 if ("function" === typeof m && (m = m._f || m) !== f) {
  35. 1 superMeta.pos = 1 + pos;
  36. 1 return m.bind(this);
  37. }
  38. } while (l > ++pos);
  39. }
  40. 1 return null;
  41. }
  42. 1function defaultFunction() {
  43. 59 var meta = this.__meta || {},
  44. supers = meta.supers,
  45. l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos;
  46. 59 if (l > pos) {
  47. 54 var name = superMeta.name, f = superMeta.f, m;
  48. 54 do {
  49. 120 m = supers[pos][name];
  50. 120 if ("function" === typeof m && (m = m._f || m) !== f) {
  51. 15 superMeta.pos = 1 + pos;
  52. 15 return m.apply(this, arguments);
  53. }
  54. } while (l > ++pos);
  55. }
  56. 44 return null;
  57. }
  58. 1;
  59. 1function functionWrapper(f, name) {
  60. 292 var wrapper = function () {
  61. 18084 var ret, meta = this.__meta || {};
  62. 18084 var orig = meta.superMeta;
  63. 18084 meta.superMeta = {f: f, pos: 0, name: name};
  64. 18084 ret = f.apply(this, arguments);
  65. 18080 meta.superMeta = orig;
  66. 18080 return ret;
  67. };
  68. 292 wrapper._f = f;
  69. 292 return wrapper;
  70. }
  71. 1;
  72. /**
  73. * @ignore
  74. */
  75. 1function defineMixinProps(child, proto) {
  76. 34 var operations = proto.setters || {};
  77. 34 for (var i in operations) {
  78. 15 if (!child.__lookupSetter__(i)) { //make sure that the setter isnt already there
  79. 15 child.__defineSetter__(i, operations[i]);
  80. }
  81. }
  82. 34 operations = proto.getters || {};
  83. 34 for (i in operations) {
  84. 15 if (!child.__lookupGetter__(i)) {
  85. //define the getter if the child does not already have it
  86. 15 child.__defineGetter__(i, operations[i]);
  87. }
  88. }
  89. 34 for (var j in proto) {
  90. 103 if (j != "getters" && j != "setters") {
  91. 83 var p = proto[j];
  92. 83 if ("function" === typeof p) {
  93. 78 if (!child.hasOwnProperty(j)) {
  94. 10 child[j] = functionWrapper(defaultFunction, j);
  95. }
  96. } else {
  97. 5 child[j] = p;
  98. }
  99. }
  100. }
  101. }
  102. 1;
  103. /**
  104. * @ignore
  105. */
  106. 1function mixin() {
  107. 7 var args = Array.prototype.slice.call(arguments), l = args.length;
  108. 7 var child = this.prototype, childMeta = child.__meta, thisMeta = this.__meta, bases = child.__meta.bases, staticBases = bases.slice(),
  109. staticSupers = thisMeta.supers || [], supers = childMeta.supers || [];
  110. 7 for (var i = 0; i < l; i++) {
  111. 17 var m = args[i], mProto = m.prototype;
  112. 17 var protoMeta = mProto.__meta, meta = m.__meta;
  113. 17 !protoMeta && (protoMeta = (mProto.__meta = {proto: mProto || {}}));
  114. 17 !meta && (meta = (m.__meta = {proto: m.__proto__ || {}}));
  115. 17 defineMixinProps(child, protoMeta.proto || {});
  116. 17 defineMixinProps(this, meta.proto || {});
  117. //copy the bases for static,
  118. 17 mixinSupers(m.prototype, supers, bases);
  119. 17 mixinSupers(m, staticSupers, staticBases);
  120. }
  121. 7 return this;
  122. }
  123. 1;
  124. /**
  125. * @ignore
  126. */
  127. 1function mixinSupers(sup, arr, bases) {
  128. 366 var meta = sup.__meta;
  129. 366 !meta && (meta = (sup.__meta = {}));
  130. 366 var unique = sup.__meta.unique;
  131. 366 !unique && (meta.unique = "define" + ++classCounter);
  132. //check it we already have this super mixed into our prototype chain
  133. //if true then we have already looped their supers!
  134. 366 if (bases.indexOf(unique) == -1) {
  135. //add their id to our bases
  136. 214 bases.push(unique);
  137. 214 var supers = sup.__meta.supers || [], i = supers.length - 1 || 0;
  138. 214 while (i >= 0) {
  139. 250 mixinSupers(supers[i--], arr, bases);
  140. }
  141. 214 arr.unshift(sup);
  142. }
  143. }
  144. 1;
  145. /**
  146. * @ignore
  147. */
  148. 1function defineProps(child, proto) {
  149. 80 var operations = proto.setters;
  150. 80 if (operations) {
  151. 8 for (var i in operations) {
  152. 13 child.__defineSetter__(i, operations[i]);
  153. }
  154. }
  155. 80 operations = proto.getters || {};
  156. 80 if (operations) {
  157. 80 for (i in operations) {
  158. 46 child.__defineGetter__(i, operations[i]);
  159. }
  160. }
  161. 80 for (i in proto) {
  162. 330 if (i != "getters" && i != "setters") {
  163. 310 var f = proto[i];
  164. 310 if ("function" === typeof f) {
  165. 279 var meta = f.__meta || {};
  166. 279 if (!meta.isConstructor) {
  167. 278 child[i] = functionWrapper(f, i);
  168. } else {
  169. 1 child[i] = f;
  170. }
  171. } else {
  172. 31 child[i] = f;
  173. }
  174. }
  175. }
  176. }
  177. 1function _export(obj, name) {
  178. 10 if (obj && name) {
  179. 1 obj[name] = this;
  180. } else {
  181. 9 obj.exports = obj = this;
  182. }
  183. 10 return this;
  184. }
  185. 1function extend(proto) {
  186. 3 return define(this, proto);
  187. }
  188. /**
  189. * @ignore
  190. */
  191. 1function __define(child, sup, proto) {
  192. 42 var childProto = child.prototype, supers = [];
  193. 42 var unique = "define" + ++classCounter, bases = [], staticBases = [];
  194. 42 var instanceSupers = [], staticSupers = [];
  195. 42 var meta = childProto.__meta = {
  196. supers: instanceSupers,
  197. unique: unique,
  198. bases: bases,
  199. superMeta: {
  200. f: null,
  201. pos: 0,
  202. name: null
  203. }
  204. };
  205. 42 var childMeta = child.__meta = {
  206. supers: staticSupers,
  207. unique: unique,
  208. bases: staticBases,
  209. isConstructor: true,
  210. superMeta: {
  211. f: null,
  212. pos: 0,
  213. name: null
  214. }
  215. };
  216. 42 if ((isHash(sup) && !proto)) {
  217. 2 proto = sup;
  218. 2 sup = Base;
  219. 40 } else if ((!sup && isHash(proto))) {
  220. 12 sup = Base;
  221. }
  222. 42 if ("function" === typeof sup || isArray(sup)) {
  223. 41 supers = isArray(sup) ? sup : [sup];
  224. 41 sup = supers.shift();
  225. 41 child.__proto__ = sup;
  226. 41 childProto.__proto__ = sup.prototype;
  227. 41 mixinSupers(sup.prototype, instanceSupers, bases),
  228. mixinSupers(sup, staticSupers, staticBases);
  229. }
  230. 42 if (proto) {
  231. 40 var instance = meta.proto = proto.instance || {};
  232. 40 !instance.hasOwnProperty("constructor") && (instance.constructor = defaultFunction);
  233. 40 var stat = childMeta.proto = proto.static || {};
  234. 40 stat.init = stat.init || defaultFunction;
  235. 40 defineProps(childProto, instance, false);
  236. 40 defineProps(child, stat, true);
  237. } else {
  238. 2 meta.proto = {};
  239. 2 childMeta.proto = {};
  240. 2 child.init = functionWrapper(defaultFunction, "init");
  241. 2 childProto.constructor = functionWrapper(defaultFunction, "constructor");
  242. }
  243. 42 if (supers.length) {
  244. 7 mixin.apply(child, supers);
  245. }
  246. 42 childProto._super = child._super = callSuper;
  247. 42 childProto._getSuper = child._getSuper = getSuper;
  248. 42 childProto._static = child;
  249. }
  250. 1function define(sup, proto) {
  251. 39 function defineConstructor() {
  252. 2053 this.constructor.apply(this, arguments);
  253. }
  254. 39 __define(defineConstructor, sup, proto);
  255. 39 return defineConstructor.init() || defineConstructor;
  256. }
  257. 1function singleton(sup, proto) {
  258. 3 var retInstance;
  259. 3 function singletonConstructor() {
  260. 6 if (!retInstance) {
  261. 2 this.constructor.apply(this, arguments);
  262. 2 retInstance = this;
  263. }
  264. 6 return retInstance;
  265. }
  266. 3 __define(singletonConstructor, sup, proto);
  267. 3 return singletonConstructor.init() || singletonConstructor;
  268. }
  269. 1Base = define({
  270. instance: {},
  271. "static": {
  272. mixin: mixin,
  273. extend: extend,
  274. as: _export
  275. }
  276. });
  277. /**
  278. * Defines a new class to be used
  279. *
  280. * <p>
  281. * Class methods
  282. * <ul>
  283. * <li>as(module | object, name): exports the object to module or the object with the name</li>
  284. * <li>mixin(mixin) : mixes in an object</li>
  285. * </ul>
  286. * </br>
  287. * Instance methods
  288. * <ul>
  289. * <li>_super(argumnents, [?newargs]): calls the super of the current method</li>
  290. * </ul>
  291. *
  292. * </br>
  293. * Instance properties
  294. * <ul>
  295. * <li>_static: use to reference class properties and methods</li>
  296. * </ul>
  297. *
  298. * </p>
  299. *
  300. *
  301. * @example
  302. * //Class without a super class
  303. * var Mammal = comb.define(null, {
  304. * instance : {
  305. *
  306. * constructor: function(options) {
  307. * options = options || {};
  308. * this._super(arguments);
  309. * this._type = options.type || "mammal";
  310. * },
  311. *
  312. * speak : function() {
  313. * return "A mammal of type " + this._type + " sounds like";
  314. * },
  315. *
  316. * //Define your getters
  317. * getters : {
  318. * type : function() {
  319. * return this._type;
  320. * }
  321. * },
  322. *
  323. * //Define your setters
  324. * setters : {
  325. * type : function(t) {
  326. * this._type = t;
  327. * }
  328. * }
  329. * },
  330. *
  331. * //Define your static methods
  332. * static : {
  333. * soundOff : function() {
  334. * return "Im a mammal!!";
  335. * }
  336. * }
  337. * });
  338. *
  339. * //Show singular inheritance
  340. *var Wolf = comb.define(Mammal, {
  341. * instance: {
  342. * constructor: function(options) {
  343. * options = options || {};
  344. * //You can call your super constructor, or you may not
  345. * //call it to prevent the super initializing parameters
  346. * this._super(arguments);
  347. * this._sound = "growl";
  348. * this._color = options.color || "grey";
  349. * },
  350. *
  351. * speak : function() {
  352. * //override my super classes speak
  353. * //Should return "A mammal of type mammal sounds like a growl"
  354. * return this._super(arguments) + " a " + this._sound;
  355. * },
  356. *
  357. * //add new getters for sound and color
  358. * getters : {
  359. *
  360. * color : function() {
  361. * return this._color;
  362. * },
  363. *
  364. * sound : function() {
  365. * return this._sound;
  366. * }
  367. * },
  368. *
  369. * setters : {
  370. *
  371. * //NOTE color is read only except on initialization
  372. *
  373. * sound : function(s) {
  374. * this._sound = s;
  375. * }
  376. * }
  377. *
  378. * },
  379. *
  380. * static : {
  381. * //override my satic soundOff
  382. * soundOff : function() {
  383. * //You can even call super in your statics!!!
  384. * //should return "I'm a mammal!! that growls"
  385. * return this._super(arguments) + " that growls";
  386. * }
  387. * }
  388. *});
  389. *
  390. *
  391. * //Typical hierarchical inheritance
  392. * // Mammal->Wolf->Dog
  393. * var Dog = comb.define(Wolf, {
  394. * instance: {
  395. * constructor: function(options) {
  396. * options = options || {};
  397. * this._super(arguments);
  398. * //override Wolfs initialization of sound to woof.
  399. * this._sound = "woof";
  400. *
  401. * },
  402. *
  403. * speak : function() {
  404. * //Should return "A mammal of type mammal sounds like a growl thats domesticated"
  405. * return this._super(arguments) + " thats domesticated";
  406. * }
  407. * },
  408. *
  409. * static : {
  410. * soundOff : function() {
  411. * //should return "I'm a mammal!! that growls but now barks"
  412. * return this._super(arguments) + " but now barks";
  413. * }
  414. * }
  415. *});
  416. *
  417. *
  418. *
  419. * dog instanceof Wolf => true
  420. * dog instanceof Mammal => true
  421. * dog.speak() => "A mammal of type mammal sounds like a woof thats domesticated"
  422. * dog.type => "mammal"
  423. * dog.color => "gold"
  424. * dog.sound => "woof"
  425. * Dog.soundOff() => "Im a mammal!! that growls but now barks"
  426. *
  427. * // Mammal->Wolf->Dog->Breed
  428. *var Breed = comb.define(Dog, {
  429. * instance: {
  430. *
  431. * //initialize outside of constructor
  432. * _pitch : "high",
  433. *
  434. * constructor: function(options) {
  435. * options = options || {};
  436. * this._super(arguments);
  437. * this.breed = options.breed || "lab";
  438. * },
  439. *
  440. * speak : function() {
  441. * //Should return "A mammal of type mammal sounds like a
  442. * //growl thats domesticated with a high pitch!"
  443. * return this._super(arguments) + " with a " + this._pitch + " pitch!";
  444. * },
  445. *
  446. * getters : {
  447. * pitch : function() {
  448. * return this._pitch;
  449. * }
  450. * }
  451. * },
  452. *
  453. * static : {
  454. * soundOff : function() {
  455. * //should return "I'M A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  456. * return this._super(arguments).toUpperCase() + "!";
  457. * }
  458. * }
  459. * });
  460. *
  461. *
  462. * var breed = new Breed({color : "gold", type : "lab"}),
  463. *
  464. *
  465. *
  466. * breed instanceof Dog => true
  467. * breed instanceof Wolf => true
  468. * breed instanceof Mammal => true
  469. * breed.speak() => "A mammal of type lab sounds like a woof "
  470. * + "thats domesticated with a high pitch!"
  471. * breed.type => "lab"
  472. * breed.color => "gold"
  473. * breed.sound => "woof"
  474. * breed.soundOff() => "IM A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  475. *
  476. *
  477. * //Example of multiple inheritance
  478. * //NOTE proto is optional
  479. *
  480. * //Mammal is super class
  481. * //Wolf Dog and Breed inject functionality into the prototype
  482. * var Lab = comb.define([Mammal, Wolf, Dog, Breed]);
  483. *
  484. * var lab = new Lab();
  485. * lab instanceof Wolf => false
  486. * lab instanceof Dog => false
  487. * lab instanceof Breed => false
  488. * lab instanceof Mammal => true
  489. * lab.speak() => "A mammal of type mammal sounds like a"
  490. * + " woof thats domesticated with a high pitch!"
  491. * Lab.soundOff() => "IM A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  492. *
  493. * @name define
  494. * @memberOf comb
  495. *
  496. * @param {Array|Class} super the supers of this class
  497. * @param {Object} [proto] the object used to define this class
  498. * @param {Object} [proto.instance] the instance methods of the class
  499. * @param {Object} [proto.instance.getters] the getters for the class
  500. * @param {Object} [proto.instance.setters] the setters for the class
  501. * @param {Object} [proto.static] the Class level methods of this class
  502. * @param {Object} [proto.static.getters] static getters for the object
  503. * @param {Object} [proto.static.setters] static setters for the object
  504. *
  505. * @returns {Object} the constructor of the class to be used with new keyword
  506. */
  507. 1exports.define = define;
  508. /**
  509. * Defines a singleton instance of a Class. See {@link define}
  510. * @example
  511. * var MyLab = comb.singleton([Mammal, Wolf, Dog, Breed]);
  512. * var myLab1 = new MyLab();
  513. * myLab1.type = "collie"
  514. * var myLab2 = new MyLab();
  515. * myLab1 === myLab2 => true
  516. * myLab1.type => "collie"
  517. * myLab2.type => "collie"
  518. *
  519. *
  520. * @name singleton
  521. * @memberOf comb
  522. */
  523. 1exports.singleton = singleton;
index.js
Coverage100.00 SLOC110 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 [Change Log] ../History.md
  15. * @includeDoc [Test Coverage] [../docs-md/coverage.html]
  16. *
  17. *
  18. * @header
  19. * [![build status](https://secure.travis-ci.org/C2FO/comb.png)](http://travis-ci.org/C2FO/comb)
  20. * #Comb
  21. *
  22. * ##Overview
  23. *
  24. * Framework for node that provides a one stop shop for frequently needed utilities, including:
  25. *
  26. * * [OO utilties](./define.html)
  27. * * Collections
  28. * * [Logging](./logging.html)
  29. * * [String &amp; date formatting](./utilities)
  30. * * [Flow control](./promise.html)
  31. *
  32. *
  33. * ##Installation
  34. *
  35. * `npm install comb`
  36. *
  37. * ###[Getting Started](./introduction.html)
  38. *
  39. * ##Highlights
  40. *
  41. * * 100% test coverage!
  42. * * comb([define](./comb.html#.define)|[singleton](./comb.html#.singleton))
  43. * * The backbone of comb.
  44. * * Options for classical inheritance models as well as mixins(pseudo multi-inheritance)
  45. * * You can call this._super from any method. Including statically defined ones!
  46. * * Access to your class level properties within an instance
  47. * * Logging
  48. * * Logger inheritance through name spaces
  49. * * Predefined [level](./comb_logging_Level.html) level definition along with the ability to define your own.
  50. * * Multiple appenders including
  51. * * [FileAppender](./comb_logging_appenders_FileAppender.html) - log it to a file
  52. * * [RollingFileAppender](./comb_logging_appenders_RollingFileAppender.html) - log it to a file up to a customizable size then create a new one.
  53. * * [JSONAppender](./comb_logging_appenders_JSONAppender.html) - write it out as JSON to a file.
  54. * * [ConsoleAppender](./comb_logging_appenders_ConsoleAppender.html)- log it to the console
  55. * * Configurable with [files OR programatically](./comb_logger.html#.configure)
  56. * * Collections
  57. * * [RedBlackTree](./comb_collections_RedBlackTree.html)
  58. * * [AVLTree](./comb_collections_AVLTree.html)
  59. * * [AnderssonTree](./comb_collections_AnderssonTree.html)
  60. * * [BinaryTree](./comb_collections_BinaryTree.html)
  61. * * [HashTable](./comb_collections_HashTable.html)
  62. * * [MaxHeap](./comb_collections_MaxHeap.html)
  63. * * [MinHeap](./comb_collections_MinHeap.html)
  64. * * [Pool](./comb_collections_Pool.html)
  65. * * [PriorityQueue](./comb_collections_PriorityQueue.html)
  66. * * [Queue](./comb_collections_Queue.html)
  67. * * [Stack](./comb_collections_Stack.html)
  68. *
  69. * * [Flow control](./promise.html)
  70. * * [Promises](./comb_Promise.html)
  71. * * [PromiseList](./comb_PromiseList.html)
  72. * * [comb.when](./comb.html#.when)
  73. * * [comb.serial](./comb.html#.serial)
  74. *
  75. * @footer
  76. * ##License
  77. *
  78. * MIT <https://github.com/C2FO/comb/raw/master/LICENSE>
  79. *
  80. * ##Meta
  81. * * Code: `git clone git://github.com/C2FO/comb.git`
  82. * * Website: <http://c2fo.com>
  83. * * Twitter: [http://twitter.com/c2fo](http://twitter.com/c2fo) - 877.465.4045
  84. */
  85. /**
  86. * Utilities for javascript, optimized for the server environment.
  87. *
  88. *
  89. * @namespace
  90. * @ignoreCode
  91. */
  92. 1var comb = createExtension;
  93. 1base.merge(comb, base, require("./define"), require("./promise"), require("./async"), require("./plugins"), require("./collections"), require("./logging"));
  94. 1comb.definePlugin = function (obj) {
  95. 1 if (comb.isHash(obj)) {
  96. 1 comb.deepMerge(comb, obj);
  97. }
  98. 1 return comb;
  99. };
  100. 1module.exports = comb;
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");
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 SLOC835 LOC211 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. 2037 this.__errorCbs = [];
  52. 2037 this.__cbs = [];
  53. },
  54. /**
  55. * @private
  56. */
  57. __resolve: function () {
  58. 1895 if (!this.__fired) {
  59. 1895 this.__fired = true;
  60. 1895 var cbs = this.__error ? this.__errorCbs : this.__cbs,
  61. len = cbs.length, i,
  62. results = this.__error || this.__results;
  63. 1895 for (i = 0; i < len; i++) {
  64. 1210 this.__callNextTick(cbs[i], results);
  65. }
  66. }
  67. },
  68. __callNextTick: function (cb, results) {
  69. 1979 process.nextTick(hitch(this, function () {
  70. 1979 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. 2000 if (cb) {
  83. 2000 if (exports.isPromiseLike(cb)) {
  84. 363 cb = hitch(cb, "callback");
  85. }
  86. 2000 if (this.__fired && this.__results) {
  87. 755 this.__callNextTick(cb, this.__results);
  88. } else {
  89. 1245 this.__cbs.push(cb);
  90. }
  91. }
  92. 2000 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. 1882 if (cb) {
  103. 1879 if (exports.isPromiseLike(cb)) {
  104. 361 cb = hitch(cb, "errback");
  105. }
  106. 1879 if (this.__fired && this.__error) {
  107. 14 this.__callNextTick(cb, this.__error);
  108. } else {
  109. 1865 this.__errorCbs.push(cb);
  110. }
  111. }
  112. 1882 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. 1745 args = argsToArray(arguments);
  139. 1745 if (this.__fired) {
  140. 1 throw new Error("Already fired!");
  141. }
  142. 1744 this.__results = args;
  143. 1744 this.__resolve();
  144. 1744 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. 152 if (this.__fired) {
  153. 1 throw args || new Error("Already fired");
  154. }
  155. 151 this.__error = argsToArray(arguments);
  156. 151 this.__resolve();
  157. 151 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. 4 if (err) {
  177. 1 this.errback(err);
  178. } else {
  179. 3 this.callback.apply(this, argsToArray(arguments, 1));
  180. }
  181. 4 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. 1029 if (exports.isPromiseLike(callback)) {
  193. 363 this.addCallback(callback);
  194. 363 this.addErrback(callback);
  195. } else {
  196. 666 this.addCallback(callback);
  197. 666 this.addErrback(errback);
  198. }
  199. 1029 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. 564 var promise = new Promise();
  286. 564 function errorHandler() {
  287. 78 var args = arguments;
  288. 78 if (errback) {
  289. 5 process.nextTick(function () {
  290. 5 try {
  291. 5 when(isFunction(errback) ? errback.apply(this, args) : errback).then(promise);
  292. } catch (e) {
  293. 1 promise.errback(e);
  294. }
  295. });
  296. } else {
  297. 73 promise.errback.apply(promise, args);
  298. }
  299. }
  300. 564 this.addCallback(function () {
  301. 515 var args = arguments;
  302. 515 process.nextTick(function () {
  303. 515 try {
  304. 515 when(isFunction(callback) ? callback.apply(this, args) : callback).then(promise.callback.bind(promise), errorHandler);
  305. } catch (e) {
  306. 4 promise.errback(e);
  307. }
  308. });
  309. });
  310. // If no errback passed, then invoke our promise's errback to pass
  311. // on to next link in the chain.
  312. 564 this.addErrback(errorHandler);
  313. 564 return promise.promise();
  314. },
  315. /**
  316. * Applies the same function that returns a promise to both the callback and errback.
  317. *
  318. * @param {Function} callback function to call. This function must return a Promise
  319. *
  320. * @return {comb.Promise} a promise to continue chaining or to resolve with.
  321. *
  322. */
  323. chainBoth: function (callback) {
  324. 10 var promise = new Promise();
  325. 10 this.addCallback(function () {
  326. 5 try {
  327. 5 when(isFunction(callback) ? callback.apply(this, arguments) : callback).then(promise);
  328. } catch (e) {
  329. 1 promise.errback(e);
  330. }
  331. });
  332. 10 this.addErrback(function () {
  333. 5 try {
  334. 5 when(isFunction(callback) ? callback.apply(this, arguments) : callback).then(promise);
  335. } catch (e) {
  336. 1 promise.errback(e);
  337. }
  338. });
  339. 10 return promise.promise();
  340. },
  341. /**
  342. * Creates an object to that contains methods to listen to resolution but not the "callback" or "errback" methods.
  343. *
  344. * @example
  345. *
  346. * var asyncMethod = function(){
  347. * var ret = new comb.Promise();
  348. * process.nextTick(ret.callback.bind(ret, "hello"));
  349. * return ret.promise();
  350. * }
  351. *
  352. * asyncMethod().callback() //throws error
  353. *
  354. * @return {Object} an object containing "chain", "chainBoth", "promise", "addCallback", "addErrback", "then", "both".
  355. */
  356. promise: function () {
  357. 3316 var ret = {
  358. chain: this.chain.bind(this),
  359. chainBoth: this.chainBoth.bind(this),
  360. promise: function () {
  361. 865 return ret;
  362. }
  363. };
  364. 3316 forEach(["addCallback", "addErrback", "then", "both", "classic"], function (action) {
  365. 16580 ret[action] = function () {
  366. 1446 this[action].apply(this, arguments);
  367. 1446 return ret;
  368. }.bind(this);
  369. }, this);
  370. 3316 return ret;
  371. }
  372. }
  373. });
  374. 1var PromiseList = define(Promise, {
  375. instance: {
  376. /** @lends comb.PromiseList.prototype */
  377. /*@private*/
  378. __results: null,
  379. /*@private*/
  380. __errors: null,
  381. /*@private*/
  382. __promiseLength: 0,
  383. /*@private*/
  384. __defLength: 0,
  385. /*@private*/
  386. __firedLength: 0,
  387. normalizeResults: false,
  388. /**
  389. * PromiseList object used for handling a list of Promises
  390. *
  391. * @example
  392. * var myFunc = function(){
  393. * var promise = new Promise();
  394. * //callback the promise after 10 Secs
  395. * setTimeout(hitch(promise, "callback"), 10000);
  396. * return promise.promise();
  397. * }
  398. * var myFunc2 = function(){
  399. * var promises =[];
  400. * for(var i = 0; i < 10; i++){
  401. * promises.push(myFunc);
  402. * }
  403. * //create a new promise list with all 10 promises
  404. * return new PromiseList(promises).promise();
  405. * }
  406. *
  407. * myFunc.then(function(success){}, function(error){})
  408. * //chain promise operations
  409. * myFunc.chain(myfunc).then(function(success){}, function(error){})
  410. *
  411. * myFunc2.then(function(success){}, function(error){})
  412. * //chain promise operations
  413. * myFunc2.chain(myfunc).then(function(success){}, function(error){})
  414. *
  415. * @param {comb.Promise[]} [defs=[]] the list of promises
  416. * @constructs
  417. * @augments comb.Promise
  418. * @memberOf comb
  419. * */
  420. constructor: function (defs, normalizeResults) {
  421. 142 this.__errors = [];
  422. 142 this.__results = [];
  423. 142 this.normalizeResults = base.isBoolean(normalizeResults) ? normalizeResults : false;
  424. 142 this._super(arguments);
  425. 142 if (defs && defs.length) {
  426. 135 this.__defLength = defs.length;
  427. 135 forEach(defs, this.__addPromise, this);
  428. } else {
  429. 7 this.__resolve();
  430. }
  431. },
  432. /**
  433. * Add a promise to our chain
  434. * @private
  435. * @param promise the promise to add to our chain
  436. * @param i the index of the promise in our chain
  437. */
  438. __addPromise: function (promise, i) {
  439. 394 promise.addCallback(hitch(this, function () {
  440. 379 var args = argsToArray(arguments);
  441. 379 args.unshift(i);
  442. 379 this.callback.apply(this, args);
  443. }));
  444. 394 promise.addErrback(hitch(this, function () {
  445. 15 var args = argsToArray(arguments);
  446. 15 args.unshift(i);
  447. 15 this.errback.apply(this, args);
  448. }));
  449. },
  450. /**
  451. * Resolves the promise
  452. * @private
  453. */
  454. __resolve: function () {
  455. 142 if (!this.__fired) {
  456. 142 this.__fired = true;
  457. 142 var cbs = this.__errors.length ? this.__errorCbs : this.__cbs,
  458. len = cbs.length, i,
  459. results = this.__errors.length ? this.__errors : this.__results;
  460. 142 for (i = 0; i < len; i++) {
  461. 131 this.__callNextTick(cbs[i], results);
  462. }
  463. }
  464. },
  465. __callNextTick: function (cb, results) {
  466. 141 process.nextTick(hitch(this, function () {
  467. 141 cb.apply(this, [results]);
  468. }));
  469. },
  470. addCallback: function (cb) {
  471. 141 if (cb) {
  472. 141 if (exports.isPromiseLike(cb)) {
  473. 3 cb = hitch(cb, "callback");
  474. }
  475. 141 if (this.__fired && !this.__errors.length) {
  476. 9 this.__callNextTick(cb, this.__results);
  477. } else {
  478. 132 this.__cbs.push(cb);
  479. }
  480. }
  481. 141 return this;
  482. },
  483. addErrback: function (cb) {
  484. 141 if (cb) {
  485. 141 if (exports.isPromiseLike(cb)) {
  486. 3 cb = hitch(cb, "errback");
  487. }
  488. 141 if (this.__fired && this.__errors.length) {
  489. 1 this.__callNextTick(cb, this.__errors);
  490. } else {
  491. 140 this.__errorCbs.push(cb);
  492. }
  493. }
  494. 141 return this;
  495. },
  496. callback: function (i) {
  497. 380 if (this.__fired) {
  498. 1 throw new Error("Already fired!");
  499. }
  500. 379 var args = argsToArray(arguments);
  501. 379 if (this.normalizeResults) {
  502. 362 args = args.slice(1);
  503. 362 args = args.length == 1 ? args.pop() : args;
  504. }
  505. 379 this.__results[i] = args;
  506. 379 this.__firedLength++;
  507. 379 if (this.__firedLength == this.__defLength) {
  508. 125 this.__resolve();
  509. }
  510. 379 return this.promise();
  511. },
  512. errback: function (i) {
  513. 16 if (this.__fired) {
  514. 1 throw new Error("Already fired!");
  515. }
  516. 15 var args = argsToArray(arguments);
  517. 15 if (this.normalizeResults) {
  518. 14 args = args.slice(1);
  519. 14 args = args.length == 1 ? args.pop() : args;
  520. }
  521. 15 this.__errors[i] = args;
  522. 15 this.__firedLength++;
  523. 15 if (this.__firedLength == this.__defLength) {
  524. 10 this.__resolve();
  525. }
  526. 15 return this.promise();
  527. }
  528. }
  529. });
  530. /**
  531. * Creates the promise chain
  532. * @ignore
  533. * @private
  534. */
  535. 1function callNext(list, results, propogate) {
  536. 59 var ret = new Promise().callback();
  537. 59 forEach(list, function (listItem) {
  538. 156 ret = ret.chain(propogate ? listItem : bindIgnore(null, listItem));
  539. 156 if (!propogate) {
  540. 112 ret.addCallback(function (res) {
  541. 89 results.push(res);
  542. });
  543. }
  544. });
  545. 59 return propogate ? ret.promise() : ret.chain(results);
  546. }
  547. /**
  548. * Tests if an object is like a promise (i.e. it contains then, addCallback, addErrback)
  549. * @param obj object to test
  550. * @function
  551. * @static
  552. * @memberOf comb
  553. */
  554. 1function isPromiseLike(obj) {
  555. 7287 return !isUndefinedOrNull(obj) && (isInstanceOf(obj, Promise) || (isFunction(obj.then)
  556. && isFunction(obj.addCallback) && isFunction(obj.addErrback)));
  557. }
  558. /**
  559. * Waits for promise and non promise values to resolve and fires callback or errback appropriately. If you pass in an array of promises
  560. * then it will wait for all promises in the list to resolve.
  561. *
  562. * @example
  563. * var a = "hello";
  564. * var b = new comb.Promise().callback(world);
  565. * comb.when(a, b) => called back with ["hello", "world"];
  566. *
  567. * @param {Anything...} args variable number of arguments to wait for.
  568. * @param {Function} cb the callback function
  569. * @param {Function} eb the errback function
  570. * @returns {comb.Promise} a promise that is fired when all values have resolved
  571. * @static
  572. * @memberOf comb
  573. */
  574. 1function when(args, cb, eb) {
  575. 1516 var args = argsToArray(arguments), p;
  576. 1516 eb = base.isFunction(args[args.length - 1]) ? args.pop() : null;
  577. 1516 cb = base.isFunction(args[args.length - 1]) ? args.pop() : null;
  578. 1516 if (eb && !cb) {
  579. 9 cb = eb;
  580. 9 eb = null;
  581. }
  582. 1516 if (!args.length) {
  583. 7 p = new Promise().callback(args);
  584. 1509 } else if (args.length == 1) {
  585. 1469 args = args.pop();
  586. 1469 if (isPromiseLike(args)) {
  587. 764 p = args;
  588. 705 } else if (isArray(args) && array.every(args, isPromiseLike)) {
  589. 91 p = new PromiseList(args, true);
  590. } else {
  591. 614 p = new Promise().callback(args);
  592. }
  593. } else {
  594. 40 p = new PromiseList(args.map(function (a) {
  595. 135 return exports.isPromiseLike(a) ? a : new Promise().callback(a);
  596. }), true);
  597. }
  598. 1516 if (cb) {
  599. 15 p.addCallback(cb);
  600. }
  601. 1516 if (eb) {
  602. 6 p.addErrback(eb);
  603. }
  604. 1516 return p.promise();
  605. }
  606. /**
  607. * Wraps traditional node style functions with a promise.
  608. * @example
  609. *
  610. * var fs = require("fs");
  611. * var readFile = comb.wrap(fs.readFile, fs);
  612. * readFile(__dirname + "/test.json").then(
  613. * function(buffer){
  614. * console.log(contents);
  615. * },
  616. * funciton(err){
  617. *
  618. * } console.error(err);
  619. * );
  620. *
  621. *
  622. * @param {Function} fn function to wrap
  623. * @param {Object} scope scope to call the function in
  624. *
  625. * @return {Funciton} a wrapped function
  626. * @static
  627. * @memberOf comb
  628. */
  629. 1function wrap(fn, scope) {
  630. 2 return function () {
  631. 2 var ret = new Promise();
  632. 2 var args = argsToArray(arguments);
  633. 2 args.push(ret.resolve.bind(ret));
  634. 2 fn.apply(scope || this, args);
  635. 2 return ret.promise();
  636. }
  637. }
  638. /**
  639. * Executes a list of items in a serial manner. If the list contains promises then each promise
  640. * will be executed in a serial manner, if the list contains non async items then the next item in the list
  641. * is called.
  642. *
  643. * @example
  644. *
  645. * var asyncAction = function(item, timeout){
  646. * var ret = new comb.Promise();
  647. * setTimeout(comb.hitchIgnore(ret, "callback", item), timeout);
  648. * return ret.promise();
  649. * };
  650. *
  651. * comb.serial([
  652. * comb.partial(asyncAction, 1, 1000),
  653. * comb.partial(asyncAction, 2, 900),
  654. * comb.partial(asyncAction, 3, 800),
  655. * comb.partial(asyncAction, 4, 700),
  656. * comb.partial(asyncAction, 5, 600),
  657. * comb.partial(asyncAction, 6, 500)
  658. * ]).then(function(results){
  659. * console.log(results); // [1,2,3,4,5,6];
  660. * });
  661. *
  662. *
  663. *
  664. * @param list
  665. * @param callback
  666. * @param errback
  667. * @static
  668. * @memberOf comb
  669. */
  670. 1function serial(list, callback, errback) {
  671. 56 if (base.isArray(list)) {
  672. 55 return callNext(list, [], false);
  673. } else {
  674. 1 throw new Error("When calling comb.serial the first argument must be an array");
  675. }
  676. }
  677. /**
  678. * Works just like {@link comb.Promise#chain} method, allowing you to propogate results from one funciton to another.
  679. * This is different than {@link comb.serial} in that it propogates results from one promise to the next, where
  680. * {@link comb.serial} does not.
  681. *
  682. * @example
  683. *
  684. * function asyncAction(add, timeout) {
  685. * return function (num) {
  686. * num = num || 0;
  687. * var ret = new comb.Promise();
  688. * setTimeout(function () {
  689. * ret.callback(num + add);
  690. * }, timeout);
  691. * return ret;
  692. * }
  693. * }
  694. *
  695. * comb.chain([
  696. * asyncAction(1, 100),
  697. * asyncAction(2, 100),
  698. * asyncAction(3, 100),
  699. * asyncAction(4, 100),
  700. * asyncAction(5, 100),
  701. * ]).then(function(results){
  702. * console.log(results); //15
  703. * });
  704. *
  705. * @param {function[]} list an array of function to call.
  706. * @return {comb.Promise} a promise that will resolve with the results of the last function in the list.
  707. * @static
  708. * @memberOf comb
  709. */
  710. 1function chain(list) {
  711. 5 if (base.isArray(list)) {
  712. 4 return callNext(list, [], true);
  713. } else {
  714. 1 throw new Error("When calling comb.serial the first argument must be an array");
  715. }
  716. }
  717. /**
  718. * Ensures that a promise is resolved before a the function can be run.
  719. *
  720. * For example suppose you have to ensure that you are connected to a database before you execute a function.
  721. *
  722. * ```
  723. * var findUser = comb.wait(connect(), function findUser(id){
  724. * //this wont execute until we are connected
  725. * return User.findById(id);
  726. * });
  727. *
  728. * comb.when(findUser(1), findUser(2)).then(function(users){
  729. * var user1 = users[0], user2 = users[1];
  730. * });
  731. *
  732. * ```
  733. *
  734. * @param args variable number of arguments to wait on. See {@link comb.when}.
  735. * @param {Function} fn function that will wait.
  736. * @return {Function} a function that will wait on the args to resolve.
  737. * @memberOf comb
  738. * @static
  739. */
  740. 1function wait(args, fn) {
  741. 2 var args = argsToArray(arguments), resolved = false;
  742. 2 fn = args.pop();
  743. 2 var p = when(args);
  744. 2 return function waiter() {
  745. 3 if (!resolved) {
  746. 2 args = arguments;
  747. 2 return p.chain(function doneWaiting() {
  748. 2 resolved = true;
  749. 2 return fn.apply(this, args);
  750. }.bind(this));
  751. } else {
  752. 1 return when(fn.apply(this, arguments));
  753. }
  754. }
  755. }
  756. 1base.merge(exports, {
  757. isPromiseLike: isPromiseLike,
  758. when: when,
  759. wrap: wrap,
  760. wait: wait,
  761. serial: serial,
  762. chain: chain,
  763. Promise: Promise,
  764. PromiseList: PromiseList
  765. });