Overview
Coverage98.63 SLOC12999 LOC3567 Missed49

logging/appenders/rollingFileAppender.js
Coverage90.00 SLOC229 LOC90 Missed9
  1. 1var define = require("../../define.js").define,
  2. promise = require("../../promise"),
  3. Promise = promise.Promise,
  4. PromiseList = promise.PromiseList,
  5. base = require("../../base"),
  6. hitch = base.hitch,
  7. string = base.string,
  8. escape = base.regexp.escapeString,
  9. style = string.style,
  10. format = string.format,
  11. FileAppender = require("./fileAppender"),
  12. Level = require("../level"),
  13. fs = require("fs"),
  14. path = require("path");
  15. 1var conversion = {
  16. MB: 1048576,
  17. KB: 1024,
  18. GB: 1073741824
  19. };
  20. 1var DEFAULT_SIZE = "10MB";
  21. 1var convertToBytes = function (str) {
  22. 3 var ret = DEFAULT_SIZE;
  23. 3 var match = str.match(/(\d+)(MB|KB|GB)$/);
  24. 3 if (match && match.length == 3) {
  25. 3 var size = parseInt(match[1], 10);
  26. 3 ret = size * conversion[match[2]];
  27. }
  28. 3 return ret;
  29. }
  30. /**
  31. * @class Appends messages to a file. Rolls files over when a size limit has been reached. Once the max file size has
  32. * been reached it is rolled over to a file called <logName>.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. 1FileAppender.extend({
  77. instance: {
  78. __watching: false,
  79. constructor: function (options) {
  80. 3 options = options || {};
  81. 3 this.maxSize = options.maxSize || DEFAULT_SIZE;
  82. 3 !options.name && (options.name = "rollingFileAppender");
  83. 3 this.maxBackupIndex = options.maxBackupIndex || 10;
  84. 3 this.__queue = [];
  85. 3 this.__inRollover = false;
  86. 3 this._super([options]);
  87. },
  88. __startCheck: function () {
  89. 1 if (!this.__watching) {
  90. 1 this.__watching = true;
  91. 1 fs.watchFile(this.__file, hitch(this, "__checkFile"));
  92. 1 fs.stat(this.__file, hitch(this, function (err, stat) {
  93. 1 this.__checkFile(stat);
  94. }));
  95. }
  96. },
  97. __checkFile: function (stats) {
  98. 5 var ret = new Promise(), self = this;
  99. 5 if (!this.__inRollover) {
  100. 3 if (stats.size >= this.maxSize) {
  101. 2 if (this.maxBackupIndex > 0) {
  102. 1 this.__inRollover = true;
  103. 1 this.__onExit()
  104. .chain(function () {
  105. 1 return self.__rollover();
  106. })
  107. .chain(function () {
  108. 1 var ws = fs.createWriteStream(self.__file, {flags: "w", encoding: self.__encoding});
  109. 1 ws.on("open", function () {
  110. 1 self.__writeStream = ws;
  111. 1 self.__inRollover = false;
  112. 1 self.__checkQueue();
  113. 1 ret.callback();
  114. });
  115. }, function (err) {
  116. 0 process.stderr.write("comb.logging.appenders.RollingFileAppender : error rolling over files resuming writing");
  117. 0 process.stderr.write(err.stack);
  118. 0 var ws = fs.createWriteStream(self.__file, {flags: "w", encoding: self.__encoding});
  119. 0 ws.on("open", function () {
  120. 0 self.__writeStream = ws;
  121. 0 self.__inRollover = false;
  122. 0 self.__checkQueue();
  123. 0 ret.callback();
  124. });
  125. });
  126. } else {
  127. 1 this.__writeStream = fs.createWriteStream(this.__file, {flags: "w", encoding: this.__encoding});
  128. 1 ret.callback();
  129. }
  130. } else {
  131. 1 ret.callback();
  132. }
  133. } else {
  134. 2 ret.callback();
  135. }
  136. 5 return ret.promise();
  137. },
  138. append: function (event) {
  139. 12 if (this._canAppend(event)) {
  140. 12 !this.__watching && this.__startCheck();
  141. 12 var ws = this.__writeStream;
  142. 12 if (!this.__inRollover && ws && ws.writable) {
  143. 9 this._super(arguments);
  144. } else {
  145. 3 this.__queue.push(event);
  146. }
  147. }
  148. },
  149. __checkQueue: function () {
  150. 1 this.__queue.forEach(this.append, this);
  151. 1 this.__queue.length = 0;
  152. },
  153. __rollover: function () {
  154. 1 var ret = new Promise(), file = this.__file, self = this;
  155. 1 var dir = path.dirname(file), baseName = new RegExp("(" + escape(path.basename(path.basename(file))) + ")(?:\\.(\\d*))*");
  156. 1 fs.readdir(dir, function (err, files) {
  157. 1 if (err) {
  158. 0 ret.errback(err);
  159. } else {
  160. 1 files = files.filter(
  161. function (f) {
  162. 5 var match = f.match(baseName);
  163. 5 if (match) {
  164. 4 return true;
  165. } else {
  166. 1 return false;
  167. }
  168. });
  169. 1 files = files.sort(function (a, b) {
  170. 6 var ret = 0;
  171. 6 if (a > b) {
  172. 3 ret = 0;
  173. 3 } else if (a < b) {
  174. 3 ret = 1;
  175. }
  176. 6 return ret;
  177. });
  178. 1 var count = files.length, i = 0;
  179. 1 var checkFile = function () {
  180. 5 if (count > 0) {
  181. 4 var f = dir + "/" + files[i++];
  182. 4 if (count > self.maxBackupIndex) {
  183. //drop the file;
  184. 1 count--;
  185. 1 fs.unlink(f, function (err) {
  186. 1 err ? ret.errback(err) : checkFile();
  187. });
  188. } else {
  189. //rename the file
  190. 3 var rn = self.__file + "." + count--;
  191. 3 fs.rename(f, rn, function (err) {
  192. 3 err ? ret.errback(err) : checkFile();
  193. });
  194. }
  195. } else {
  196. 1 ret.callback();
  197. }
  198. };
  199. 1 checkFile();
  200. }
  201. });
  202. 1 return ret.promise();
  203. },
  204. getters: {
  205. maxSize: function () {
  206. 3 return this.__maxSize;
  207. }
  208. },
  209. setters: {
  210. maxSize: function (size) {
  211. 3 this.__maxSize = size ? convertToBytes(size) : DEFAULT_SIZE;
  212. }
  213. }
  214. }
  215. }).registerType("RollingFileAppender").as(module);
base/functions.js
Coverage90.91 SLOC374 LOC132 Missed12
  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. 18863 return typeof obj === "function";
  16. }
  17. 1function spreadArgs(f, args, scope) {
  18. 18114 var ret;
  19. 18114 switch ((args || []).length) {
  20. case 0:
  21. 4985 ret = f.call(scope);
  22. 4980 break;
  23. case 1:
  24. 7360 ret = f.call(scope, args[0]);
  25. 7348 break;
  26. case 2:
  27. 3032 ret = f.call(scope, args[0], args[1]);
  28. 3031 break;
  29. case 3:
  30. 2725 ret = f.call(scope, args[0], args[1], args[2]);
  31. 2725 break;
  32. default:
  33. 12 ret = f.apply(scope, args);
  34. }
  35. 18096 return ret;
  36. }
  37. /**
  38. * Binds a method to a particular scope
  39. *
  40. * @static
  41. * @memberOf comb
  42. *
  43. * @param {Object} scope the scope to bind the callback to
  44. * @param {String|Function} method the method to callback
  45. * @param [args] optional args to pass to the callback
  46. *
  47. * @returns {Function} the hitched function
  48. */
  49. 1function hitch(scope, method, args) {
  50. 5235 args = Array.prototype.slice.call(arguments).slice(2);
  51. 5235 if ((isString(method) && !(method in scope))) {
  52. 2 throw new Error(method + " property not defined in scope");
  53. 5233 } else if (!isString(method) && !isFunction(method)) {
  54. 1 throw new Error(method + " is not a function");
  55. }
  56. 5232 if (isString(method)) {
  57. 130 return function () {
  58. 379 var func = scope[method];
  59. 379 if (isFunction(func)) {
  60. 376 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  61. 376 return spreadArgs(func, scopeArgs, scope);
  62. } else {
  63. 3 return func;
  64. }
  65. };
  66. } else {
  67. 5102 return function () {
  68. 3200 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  69. 3200 return spreadArgs(method, scopeArgs, scope);
  70. };
  71. }
  72. }
  73. /**
  74. * Binds a method to the scope of the first argument.
  75. *
  76. * This is useful if you have async actions and you just want to run a method or retrieve a property on the object.
  77. *
  78. * ```
  79. * var arr = [], push = comb.applyFirst("push"), length = comb.applyFirst("length");
  80. * push(arr, 1, 2,3,4);
  81. * console.log(length(arr)); //4
  82. * console.log(arr); //1,2,3,4
  83. *
  84. * ```
  85. * @static
  86. * @memberOf comb
  87. * @param {String|Function} method the method to invoke in the scope of the first arument.
  88. * @param [args] optional args to pass to the callback
  89. *
  90. * @returns {Function} a function that will execute the method in the scope of the first argument.
  91. */
  92. 1function applyFirst(method, args) {
  93. 10 args = Array.prototype.slice.call(arguments).slice(1);
  94. 10 if (!isString(method) && !isFunction(method)) {
  95. 2 throw new Error(method + " must be the name of a property or function to execute");
  96. }
  97. 8 if (isString(method)) {
  98. 4 return function () {
  99. 12 var scopeArgs = Array.prototype.slice.call(arguments), scope = scopeArgs.shift();
  100. 12 var func = scope[method];
  101. 12 if (isFunction(func)) {
  102. 6 scopeArgs = args.concat(scopeArgs);
  103. 6 return spreadArgs(func, scopeArgs, scope);
  104. } else {
  105. 6 return func;
  106. }
  107. };
  108. } else {
  109. 4 return function () {
  110. 4 var scopeArgs = Array.prototype.slice.call(arguments), scope = scopeArgs.shift();
  111. 4 scopeArgs = args.concat(scopeArgs);
  112. 4 return spreadArgs(method, scopeArgs, scope);
  113. };
  114. }
  115. }
  116. /**
  117. * @function
  118. * Binds a method to a particular scope
  119. * @static
  120. * @memberOf comb
  121. * @param {Object} scope the scope to bind the callback to
  122. * @param {String|Function} method the method to callback
  123. * @param [args] optional args to pass to the callback
  124. *
  125. * @returns {Function} the hitched function
  126. */
  127. 1exports.bind = hitch;
  128. /**
  129. * Binds a method to a particular scope ignoring any new arguments passed
  130. * into the function. This is useful if you want to force particular arguments and
  131. * ignore any new ones
  132. * @static
  133. * @memberOf comb
  134. * @param {Object} scope the scope to bind the callback to
  135. * @param {String|Function} method the method to callback
  136. * @param [args] optional args to pass to the callback
  137. *
  138. * @returns {Function} the hitched function
  139. */
  140. 1function hitchIgnore(scope, method, args) {
  141. 136 args = Array.prototype.slice.call(arguments).slice(2);
  142. 136 if ((isString(method) && !(method in scope))) {
  143. 2 throw new Error(method + " property not defined in scope");
  144. 134 } else if (!isString(method) && !isFunction(method)) {
  145. 1 throw new Error(method + " is not a function");
  146. }
  147. 133 if (isString(method)) {
  148. 15 return function () {
  149. 15 var func = scope[method];
  150. 15 if (isFunction(func)) {
  151. 12 return spreadArgs(func, args, scope);
  152. } else {
  153. 3 return func;
  154. }
  155. };
  156. } else {
  157. 118 return function () {
  158. 107 return spreadArgs(method, args, scope);
  159. };
  160. }
  161. }
  162. /**
  163. * @function
  164. * Binds a method to a particular scope ignoring any new arguments passed
  165. * into the function. This is useful if you want to force particular arguments and
  166. * ignore any new ones
  167. * @static
  168. * @memberOf comb
  169. * @param {Object} scope the scope to bind the callback to
  170. * @param {String|Function} method the method to callback
  171. * @param [args] optional args to pass to the callback
  172. *
  173. * @returns {Function} the hitched function
  174. */
  175. 1exports.bindIgnore = hitchIgnore;
  176. 1function hitchAll(scope, methods) {
  177. 0 var funcs = Array.prototype.slice.call(arguments).slice(1);
  178. 0 if (!isObject(scope)) {
  179. 0 throw new TypeError("scope must be an object");
  180. }
  181. 0 if (funcs.length === 1 && isArray(funcs[0])) {
  182. 0 funcs = funcs[0];
  183. }
  184. 0 if (!funcs.length) {
  185. 0 funcs = Object.keys(scope).filter(function (k) {
  186. 0 return isFunction(scope[k]);
  187. });
  188. }
  189. 0 for (var i = 0, l = funcs.length; i < l; i++) {
  190. 0 scope[funcs[i]] = hitch(scope, scope[funcs[i]]);
  191. }
  192. 0 return object;
  193. }
  194. 1exports.hitchAll = hitchAll;
  195. 1exports.bindAll = hitchAll;
  196. /**
  197. * Allows the passing of additional arguments to a function when it is called
  198. * especially useful for callbacks that you want to provide additional parameters to
  199. *
  200. * @static
  201. * @memberOf comb
  202. * @param {String|Function} method the method to callback
  203. * @param {Anything} [args] variable number of arguments to pass
  204. *
  205. * @returns {Function} partially hitched function
  206. */
  207. 1function partial(method, args) {
  208. 8561 args = Array.prototype.slice.call(arguments).slice(1);
  209. 8561 if (!isString(method) && !isFunction(method)) {
  210. 2 throw new Error(method + " must be the name of a property or function to execute");
  211. }
  212. 8559 if (isString(method)) {
  213. 4 return function () {
  214. 4 var func = this[method];
  215. 4 if (isFunction(func)) {
  216. 2 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  217. 2 return spreadArgs(func, scopeArgs, this)
  218. } else {
  219. 2 return func;
  220. }
  221. };
  222. } else {
  223. 8555 return function () {
  224. 1728 var scopeArgs = args.concat(Array.prototype.slice.call(arguments));
  225. 1728 return spreadArgs(method, scopeArgs, this)
  226. };
  227. }
  228. }
  229. 1;
  230. 1function curryFunc(f, execute) {
  231. 28 return function (arg) {
  232. 28 var args = Array.prototype.slice.call(arguments);
  233. 28 return execute ? f.apply(this, arguments) : function (arg) {
  234. 21 return spreadArgs(f, args.concat(Array.prototype.slice.call(arguments)), this);
  235. };
  236. };
  237. }
  238. /**
  239. * Curries a function
  240. * @example
  241. * var curried = comb.curry(4, function(a,b,c,d){
  242. * return [a,b,c,d].join(",");
  243. * }
  244. * curried("a");
  245. * curried("b");
  246. * curried("c");
  247. * curried("d") => "a,b,c,d"
  248. *
  249. * //OR
  250. *
  251. * curried("a")("b")("c")("d") => "a,b,c,d"
  252. *
  253. * @static
  254. * @memberOf comb
  255. * @param {Number} depth the number of args you expect
  256. * @param {Function} cb the function to call once all args are gathered
  257. * @param {Object} [scope] what scope to call the function in
  258. *
  259. * @returns {Function} the curried version of the function
  260. * */
  261. 1function curry(depth, cb, scope) {
  262. 7 var f;
  263. 7 if (scope) {
  264. 5 f = hitch(scope, cb);
  265. } else {
  266. 2 f = cb;
  267. }
  268. 7 if (depth) {
  269. 7 var len = depth - 1;
  270. 7 for (var i = len; i >= 0; i--) {
  271. 28 f = curryFunc(f, i === len);
  272. }
  273. }
  274. 7 return f;
  275. }
  276. /**
  277. * Binds all methods or a specified array of function to the scope of the object
  278. * @example
  279. * var f = {
  280. * a: function(){
  281. * return "a";
  282. * },
  283. * b: function(){
  284. * return "b";
  285. * }
  286. * }
  287. *
  288. * comb.hitchAll(f, "a", "b");
  289. *
  290. * //or
  291. *
  292. * comb.hitchAll(f);
  293. *
  294. * @static
  295. * @memberOf comb
  296. * @param {Object} scope the object to bind methods on
  297. * @param {...} funcs varargs of methods to bind
  298. *
  299. * @returns {Object} the originally scoped object.
  300. * */
  301. 1function hitchAll(scope) {
  302. 3 var funcs = Array.prototype.slice.call(arguments, 1);
  303. 3 if (!isObject(scope) && !isFunction(scope)) {
  304. 0 throw new TypeError("scope must be an object");
  305. }
  306. 3 if (funcs.length === 1 && isArray(funcs[0])) {
  307. 1 funcs = funcs[0];
  308. }
  309. 3 if (!funcs.length) {
  310. 1 funcs = [];
  311. 1 for (var k in scope) {
  312. 6 if (scope.hasOwnProperty(k) && isFunction(scope[k])) {
  313. 3 funcs.push(k);
  314. }
  315. }
  316. }
  317. 3 for (var i = 0, l = funcs.length; i < l; i++) {
  318. 6 scope[funcs[i]] = hitch(scope, scope[funcs[i]]);
  319. }
  320. 3 return scope;
  321. }
  322. /**
  323. * Binds all methods or a specified array of function to the scope of the object
  324. * @example
  325. * var f = {
  326. * a: function(){
  327. * return "a";
  328. * },
  329. * b: function(){
  330. * return "b";
  331. * }
  332. * }
  333. *
  334. * comb.bindAll(f, "a", "b");
  335. *
  336. * //or
  337. *
  338. * comb.bindAll(f);
  339. *
  340. * @static
  341. * @memberOf comb
  342. * @param {Object} scope the object to bind methods on
  343. * @param {...} funcs varargs of methods to bind
  344. *
  345. * @returns {Object} the originally scoped object.
  346. * */
  347. 1exports.bindAll = hitchAll;
  348. 1exports.hitchAll = hitchAll;
  349. 1exports.isFunction = isFunction;
  350. 1exports.hitch = hitch;
  351. 1exports.hitchIgnore = hitchIgnore;
  352. 1exports.partial = partial;
  353. 1exports.applyFirst = applyFirst;
  354. 1exports.bindFirst = applyFirst;
  355. 1exports.curry = curry;
  356. 1exports.__spreadArgs = spreadArgs;
plugins/Middleware.js
Coverage95.56 SLOC221 LOC45 Missed2
  1. 1var func = require("../base/functions"),
  2. obj = require("../base/object"),
  3. Promise = require("../promise").Promise,
  4. define = require("../define").define;
  5. 1var nextTick;
  6. 1if (typeof setImmediate === "function") {
  7. // In IE10, or use https://github.com/NobleJS/setImmediate
  8. 1 nextTick = setImmediate;
  9. } else {
  10. 0 nextTick = function (cb) {
  11. 0 process.nextTick(cb);
  12. };
  13. }
  14. 1var Middleware = define(null, {
  15. instance: {
  16. /** @lends comb.plugins.Middleware.prototype */
  17. __hooks: {pre: {}, post: {}},
  18. /**
  19. * @class Plugin to enable middleware on a class
  20. *
  21. * @example
  22. *
  23. * var Mammal = define(comb.plugins.Middleware, {
  24. * instance : {
  25. *
  26. * constructor: function(options) {
  27. * options = options || {};
  28. * this.super(arguments);
  29. * this._type = options.type || "mammal";
  30. * },
  31. *
  32. * speak : function() {
  33. * var ret = new comb.Promise();
  34. * this._hook("pre", "speak")
  35. * .then(comb.hitch(this, "_hook", "post", "speak"), hitch(ret, "errback"))
  36. * .then(comb.hitch(ret, "callback"), comb.hitch(ret, "errback"));
  37. * return ret;
  38. * }
  39. * }
  40. *});
  41. *
  42. * Mammal.pre('speak', function(next){
  43. * //do something meaningful
  44. * next();
  45. * });
  46. * var m = new Mammal({color : "gold"});
  47. * m.speak();
  48. *
  49. * @constructs
  50. */
  51. constructor: function () {
  52. 6 this.__hooks = obj.merge({}, this.__hooks);
  53. 6 this._super(arguments);
  54. },
  55. /**
  56. * <p>Protected!</p>
  57. *
  58. * <p>Call to initiate middleware for the topic</p>
  59. * <p><b>NOTE:</b> this function takes a variable number of arguments
  60. * whatever comes after the op param will be passed into
  61. * the listening function, with the last argument to the listenting
  62. * function being the next function</p>
  63. *
  64. *
  65. * @public
  66. * @param {"pre"|"post"} state the state in which the hook should be called
  67. * @param {String} op the operation that is being acted upong
  68. * @param args arguments to be passed into the listening functions.
  69. * @returns {comb.Promise} a promise to use after middleware chain completes
  70. *
  71. */
  72. _hook: function (state, op, args) {
  73. 11 args = args || [];
  74. 11 var promise = new Promise();
  75. 11 var funcs, length;
  76. 11 if (this.__hooks[state] && (funcs = this.__hooks[state][op]) != null && (length = funcs.length) > 0) {
  77. 7 var count = 0;
  78. 7 var next = func.hitch(this, function (err) {
  79. 15 if (err) {
  80. 1 promise.errback(err);
  81. } else {
  82. 14 nextTick(func.hitch(this, function () {
  83. //if Ive looped through all of them callback
  84. 14 if (count == length) {
  85. 6 promise.callback();
  86. } else {
  87. //call next
  88. 8 var nextArgs = args.slice(0);
  89. 8 nextArgs.unshift(next);
  90. 8 funcs[count++].apply(this, nextArgs);
  91. }
  92. }));
  93. }
  94. });
  95. 7 next();
  96. } else {
  97. 4 promise.callback();
  98. }
  99. 11 return promise.promise();
  100. },
  101. /**
  102. * Use to listen to before an event occurred i.e. pre save
  103. *
  104. * <b>NOTE:</b></br>
  105. * <ul>
  106. * <li>You must call next in order for the middleware chain to complete</li>
  107. * <li>This connects to events on the instance of an object, not all instances!</li>
  108. * <li>Hooks are called in the order they are received!</li>
  109. * <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>
  110. * </ul>
  111. *
  112. * @example
  113. * instance.pre("save", function(args,...., next){
  114. * //do something...
  115. * //you have to call next!!!!!
  116. * next();
  117. * });
  118. *
  119. * */
  120. pre: function (fun, callback) {
  121. 1 var hook = this.__hooks.pre[fun];
  122. 1 if (!hook) {
  123. 1 hook = this.__hooks.pre[fun] = [];
  124. }
  125. 1 hook.push(callback);
  126. },
  127. /**
  128. * <p>Use to listen to after an event has occurred i.e. post save</p>
  129. * <b>NOTE:</b></br>
  130. * <ul>
  131. * <li>You must call next in order for the middleware chain to complete</li>
  132. * <li>This connects to events on the instance of an object, NOT all instances!</li>
  133. * <li>Hooks are called in the order they are received!</li>
  134. * <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>
  135. * </ul>
  136. * @example
  137. *
  138. * instance.post("save", function(next){
  139. * //do something...
  140. * //you have to call next!!!!!
  141. * next();
  142. * });
  143. * */
  144. post: function (fun, callback) {
  145. 1 var hook = this.__hooks.post[fun];
  146. //if I havent initialized it create it;
  147. 1 if (hook == undefined) {
  148. 1 hook = this.__hooks.post[fun] = [];
  149. }
  150. 1 hook.push(callback);
  151. }
  152. },
  153. static: {
  154. /** @lends comb.plugins.Middleware */
  155. /**
  156. *<p> Use to listen to after an event has occurred i.e. post save</p>
  157. *
  158. * <b>NOTE:</b></br>
  159. * <ul>
  160. * <li>You must call next in order for the middleware chain to complete</li>
  161. * <li>This connects to events on ALL instances of an object</li>
  162. * <li>Hooks are called in the order they are received!</li>
  163. * <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>
  164. * </ul>
  165. *
  166. * @example
  167. * Class.pre("save", function(next){
  168. * ...
  169. * //you must call next
  170. * });
  171. * */
  172. pre: function (name, cb) {
  173. 2 var hooks = this.prototype.__hooks;
  174. 2 var hook = hooks.pre[name];
  175. 2 if (!hook) {
  176. 1 hook = hooks.pre[name] = [];
  177. }
  178. 2 hook.push(cb);
  179. },
  180. /**
  181. *<p>Use to listen to after an event has occurred i.e. post save</p>
  182. *
  183. *<b>NOTE:</b></br>
  184. * <ul>
  185. * <li>You must call next in order for the middleware chain to complete</li>
  186. * <li>This connects to events on ALL instances of an object</li>
  187. * <li>Hooks are called in the order they are received!</li>
  188. * <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>
  189. * </ul>
  190. *
  191. * @example
  192. * Class.post("save", function(next){
  193. * ...
  194. * //you must call next
  195. * });
  196. * */
  197. post: function (name, cb) {
  198. 1 var hooks = this.prototype.__hooks;
  199. 1 var hook = hooks.post[name];
  200. 1 if (!hook) {
  201. 1 hook = hooks.post[name] = [];
  202. }
  203. 1 hook.push(cb);
  204. }
  205. }
  206. });
  207. 1module.exports = exports = Middleware;
promise.js
Coverage96.10 SLOC998 LOC282 Missed11
  1. 1var define = require("./define").define,
  2. base = require("./base"),
  3. isObject = base.isObject,
  4. hitch = base.hitch,
  5. hitchAll = base.hitchAll,
  6. partial = base.partial,
  7. argsToArray = base.argsToArray,
  8. array = base.array,
  9. forEach = array.forEach,
  10. zip = array.zip,
  11. flatten = array.flatten,
  12. isUndefinedOrNull = base.isUndefinedOrNull,
  13. isArray = base.isArray,
  14. isFunction = base.isFunction,
  15. bindIgnore = base.bindIgnore,
  16. isInstanceOf = base.isInstanceOf,
  17. spreadArgs = base.__spreadArgs;
  18. 1var nextTick;
  19. 1if (typeof setImmediate === "function") {
  20. // In IE10, or use https://github.com/NobleJS/setImmediate
  21. 1 nextTick = setImmediate;
  22. } else {
  23. 0 nextTick = function (cb) {
  24. 0 process.nextTick(cb);
  25. };
  26. }
  27. 1function reject(e) {
  28. 0 return new Promise().errback(e);
  29. }
  30. 1function promiseWrapper(wrapper, promise, action) {
  31. 0 var m = promise[action];
  32. 0 wrapper[action] = hitch(promise, function () {
  33. 0 spreadArgs(m, arguments, this);
  34. 0 return wrapper;
  35. });
  36. }
  37. 1var Promise = define(null, {
  38. instance: {
  39. /** @lends comb.Promise.prototype */
  40. __fired: false,
  41. __results: null,
  42. __error: null,
  43. __errorCbs: null,
  44. __cbs: null,
  45. /**
  46. * Promise object used to provide seperation of success and error resolution paths for async operations.
  47. *
  48. * @example
  49. * var myFunc = function(){
  50. * var promise = new Promise();
  51. * //callback the promise after 10 Secs
  52. * setTimeout(hitch(promise, "callback"), 10000);
  53. * return promise.promise();
  54. * }
  55. * var myFunc2 = function(){
  56. * var promises =[];
  57. * for(var i = 0; i < 10; i++){
  58. * promises.push(myFunc);
  59. * }
  60. * //create a new promise list with all 10 promises
  61. * return new PromiseList(promises).promise();
  62. * }
  63. *
  64. * myFunc.then(function(success){}, function(error){})
  65. * //chain promise operations
  66. * myFunc.chain(myfunc).then(function(success){}, function(error){})
  67. *
  68. * myFunc2.then(function(success){}, function(error){})
  69. * //chain promise operations
  70. * myFunc2.chain(myfunc).then(function(success){}, function(error){})
  71. * @constructs
  72. */
  73. constructor: function () {
  74. 2526 this.__errorCbs = [];
  75. 2526 this.__cbs = [];
  76. 2526 this.callback = hitch(this, this.callback);
  77. 2526 this.errback = hitch(this, this.errback);
  78. },
  79. /**
  80. * @private
  81. */
  82. __resolve: function () {
  83. 2374 var self = this;
  84. 2374 if (!self.__fired) {
  85. 2374 self.__fired = true;
  86. 2374 nextTick(function () {
  87. 2373 var cbs = self.__error ? self.__errorCbs : self.__cbs,
  88. res = self.__error || self.__results,
  89. i = -1, l = cbs.length;
  90. 2373 while (++i < l) {
  91. 2264 spreadArgs(cbs[i], res);
  92. }
  93. 2373 self.__errorCbs.length = self.__cbs.length = 0;
  94. 2373 self = null;
  95. });
  96. }
  97. },
  98. __callNextTick: function (cb, results) {
  99. 760 nextTick(function () {
  100. 760 spreadArgs(cb, results);
  101. 760 cb = results = null;
  102. });
  103. },
  104. /**
  105. * Add a callback to the callback chain of the promise
  106. *
  107. *
  108. * @param {Function|comb.Promise} cb the function or promise to callback when the promise is resolved.
  109. *
  110. * @return {comb.Promise} this promise for chaining
  111. */
  112. addCallback: function (cb) {
  113. 3045 if (cb) {
  114. 3045 if (isPromise(cb)) {
  115. 2 cb = cb.callback;
  116. }
  117. 3045 if (this.__fired && this.__results) {
  118. 740 this.__callNextTick(cb, this.__results);
  119. } else {
  120. 2305 this.__cbs.push(cb);
  121. }
  122. }
  123. 3045 return this;
  124. },
  125. /**
  126. * Add a callback to the errback chain of the promise
  127. *
  128. * @param {Function|comb.Promise} cb the function or promise to callback when the promise errors
  129. *
  130. * @return {comb.Promise} this promise for chaining
  131. */
  132. addErrback: function (cb) {
  133. 2927 if (cb) {
  134. 2921 if (isPromise(cb)) {
  135. 0 cb = cb.errback;
  136. }
  137. 2921 if (this.__fired && this.__error) {
  138. 20 this.__callNextTick(cb, this.__error);
  139. } else {
  140. 2901 this.__errorCbs.push(cb);
  141. }
  142. }
  143. 2927 return this;
  144. },
  145. /**
  146. *
  147. * Adds a callback or promise to be resolved for both success
  148. * and error.
  149. *
  150. * @param {Function|comb.Promise} cb callback or promise to be resolved for both success
  151. * and error.
  152. * @return {comb.Promise} this promise for chaining
  153. */
  154. both: function (cb) {
  155. 580 this.addCallback(cb);
  156. 580 if (isPromise(cb)) {
  157. 2 this.addErrback(cb.callback);
  158. } else {
  159. 578 this.addErrback(cb);
  160. }
  161. 580 return this;
  162. },
  163. /**
  164. * When called all functions registered as callbacks are called with the passed in results.
  165. *
  166. * @param {*} args variable number of results to pass back to listeners of the promise
  167. */
  168. callback: function (args) {
  169. 2209 args = argsToArray(arguments);
  170. 2209 if (this.__fired) {
  171. 1 throw new Error("Already fired!");
  172. }
  173. 2208 this.__results = args;
  174. 2208 this.__resolve();
  175. 2208 return this.promise();
  176. },
  177. /**
  178. * When called all functions registered as errbacks are called with the passed in error(s)
  179. *
  180. * @param {*} args number of errors to pass back to listeners of the promise
  181. */
  182. errback: function (args) {
  183. 170 if (this.__fired) {
  184. 4 throw args || new Error("Already fired");
  185. }
  186. 166 this.__error = argsToArray(arguments);
  187. 166 this.__resolve();
  188. 166 return this.promise();
  189. },
  190. /**
  191. * Resolved a promise using the node style callback.
  192. *
  193. * @example
  194. *
  195. * var promise = new Promise();
  196. * fs.readFile("file.txt", "utf8", promise.resolve.bind(promise));
  197. * promise.then(function(file){
  198. * console.log(file);
  199. * });
  200. *
  201. * @param {Error} [err=null] If specified then the promise will error out
  202. * @param {...} [args] if err is null then the aruments will be used to resolve the promise.
  203. *
  204. * @return {comb.Promise} for chaining.
  205. */
  206. resolve: function (err, args) {
  207. 4 if (err) {
  208. 1 this.errback(err);
  209. } else {
  210. 3 spreadArgs(this.callback, argsToArray(arguments, 1), this);
  211. }
  212. 4 return this;
  213. },
  214. /**
  215. * Call to specify action to take after promise completes or errors
  216. *
  217. * @param {Function} [callback=null] function to call after the promise completes successfully
  218. * @param {Function} [errback=null] function to call if the promise errors
  219. *
  220. * @return {comb.Promise} this promise for chaining
  221. */
  222. then: function (callback, errback) {
  223. 1858 if (isPromise(callback)) {
  224. 153 errback = callback.errback;
  225. 153 callback = callback.callback;
  226. }
  227. 1858 this.addCallback(callback);
  228. 1858 this.addErrback(errback);
  229. 1858 return this;
  230. },
  231. /**
  232. * Call this function as a classic node callback where the first argument
  233. * will be an error, or null if no error occured. The other arugments will
  234. * be the result from the promise.
  235. *
  236. * @example
  237. *
  238. * promise.classic(function(err, res){
  239. * if(err){
  240. * console.log(err);
  241. * }else{
  242. * console.log(res);
  243. * }
  244. * });
  245. *
  246. * @param cb callback where the first argument
  247. * will be an error, or null if no error occured. The other arugments will
  248. * be the result from the promise.
  249. * @return {comb.Promise} the promise to chain
  250. */
  251. classic: function (cb) {
  252. 5 if ("function" === typeof cb) {
  253. 5 this.addErrback(cb);
  254. 5 this.addCallback(partial(cb, null));
  255. }
  256. 5 return this;
  257. },
  258. /**
  259. * Call to chaining of promises
  260. *
  261. * ```
  262. * new Promise()
  263. * .callback("hello")
  264. * .chain(function(previousPromiseResults){
  265. * return previousPromiseResults + " world";
  266. * }, errorHandler)
  267. * .chain(function(previousPromiseResults){
  268. * return when(dbCall());
  269. * }).classic(function(err, results){
  270. * //all promises are done
  271. * });
  272. *
  273. * ```
  274. *
  275. * You can also use static values
  276. *
  277. * ```
  278. * new Promise().callback()
  279. * .chain("hello")
  280. * .chain(function(prev){
  281. * return prev + " world!"
  282. * }).then(function(str){
  283. * console.log(str); //"hello world!"
  284. * });
  285. * ```
  286. *
  287. * If you do not provide an `errback` for each chain then it will be propogated to the final promise
  288. *
  289. *
  290. * ```
  291. * new Promise()
  292. * .chain(function(){
  293. * return new comb.Promise().errback(new Error("error"));
  294. * })
  295. * .chain(function(){
  296. * return prev + " world!"
  297. * })
  298. * .classic(function(err, str){
  299. * console.log(err.message); //"error"
  300. * });
  301. * ```
  302. *
  303. *
  304. * @param callback method to call this one completes. If you return a promise the execution will delay until the returned promise has resolved.
  305. * @param [errback=null] method to call if this promise errors. If errback is not specified then the returned promises
  306. * errback method will be used.
  307. *
  308. * @return {comb.Promise} A new that wraps the promise for chaining
  309. */
  310. chain: function (callback, errback) {
  311. 600 var promise = new Promise(),
  312. self = this;
  313. 600 function _errback(e) {
  314. 94 if (isFunction(errback)) {
  315. 15 try {
  316. 15 var res = spreadArgs(errback, [e]);
  317. 11 isPromiseLike(res) ? res.then(promise.callback, promise.errback) : promise.callback(res);
  318. } catch (e) {
  319. 4 promise.errback(e);
  320. }
  321. } else {
  322. 79 promise.errback(e);
  323. }
  324. }
  325. 600 function _callback() {
  326. 542 try {
  327. 542 var res = isFunction(callback) ? spreadArgs(callback, arguments) : callback;
  328. 538 if (isPromiseLike(res)) {
  329. 206 res.then(promise.callback, _errback);
  330. } else {
  331. 332 promise.callback(res);
  332. 332 promise = null;
  333. }
  334. 538 callback = res = null;
  335. } catch (e) {
  336. 4 _errback(e);
  337. }
  338. }
  339. 600 self.addCallback(_callback);
  340. 600 self.addErrback(_errback);
  341. 600 return promise.promise();
  342. },
  343. /**
  344. * Applies the same function that returns a promise to both the callback and errback.
  345. *
  346. * @param {Function} callback function to call. This function must return a Promise
  347. *
  348. * @return {comb.Promise} a promise to continue chaining or to resolve with.
  349. *
  350. */
  351. chainBoth: function (callback) {
  352. 10 var promise = new Promise();
  353. 10 this.addCallback(function () {
  354. 5 try {
  355. 5 when(isFunction(callback) ? callback.apply(this, arguments) : callback).then(promise);
  356. } catch (e) {
  357. 1 promise.errback(e);
  358. }
  359. });
  360. 10 this.addErrback(function () {
  361. 5 try {
  362. 5 when(isFunction(callback) ? callback.apply(this, arguments) : callback).then(promise);
  363. } catch (e) {
  364. 1 promise.errback(e);
  365. }
  366. });
  367. 10 return promise.promise();
  368. },
  369. /**
  370. * Creates an object to that contains methods to listen to resolution but not the "callback" or "errback" methods.
  371. *
  372. * @example
  373. *
  374. * var asyncMethod = function(){
  375. * var ret = new comb.Promise();
  376. * process.nextTick(ret.callback.bind(ret, "hello"));
  377. * return ret.promise();
  378. * }
  379. *
  380. * asyncMethod().callback() //throws error
  381. *
  382. * @return {Object} an object containing "chain", "chainBoth", "promise", "addCallback", "addErrback", "then", "both".
  383. */
  384. promise: function () {
  385. 4220 var ret = {
  386. promise: function () {
  387. 765 return ret;
  388. }
  389. };
  390. 4220 var self = this;
  391. 4220 ret["chain"] = function () {
  392. 590 return spreadArgs(self["chain"], arguments, self);
  393. };
  394. 4220 ret["chainBoth"] = function () {
  395. 5 return spreadArgs(self["chainBoth"], arguments, self);
  396. };
  397. 4220 ret["addCallback"] = function () {
  398. 120 spreadArgs(self["addCallback"], arguments, self);
  399. 120 return ret;
  400. };
  401. 4220 ret["addErrback"] = function () {
  402. 7 spreadArgs(self["addErrback"], arguments, self);
  403. 7 return ret;
  404. };
  405. 4220 ret["then"] = function () {
  406. 1143 spreadArgs(self["then"], arguments, self);
  407. 1143 return ret;
  408. };
  409. 4220 ret["both"] = function () {
  410. 0 spreadArgs(self["both"], arguments, self);
  411. 0 return ret;
  412. };
  413. 4220 ret["classic"] = function () {
  414. 3 spreadArgs(self["classic"], arguments, self);
  415. 3 return ret;
  416. };
  417. 4220 return ret;
  418. }
  419. }
  420. });
  421. 1var PromiseList = define(Promise, {
  422. instance: {
  423. /** @lends comb.PromiseList.prototype */
  424. /*@private*/
  425. __results: null,
  426. /*@private*/
  427. __errors: null,
  428. /*@private*/
  429. __promiseLength: 0,
  430. /*@private*/
  431. __defLength: 0,
  432. /*@private*/
  433. __firedLength: 0,
  434. normalizeResults: false,
  435. /**
  436. * PromiseList object used for handling a list of Promises
  437. *
  438. * @example
  439. * var myFunc = function(){
  440. * var promise = new Promise();
  441. * //callback the promise after 10 Secs
  442. * setTimeout(hitch(promise, "callback"), 10000);
  443. * return promise.promise();
  444. * }
  445. * var myFunc2 = function(){
  446. * var promises =[];
  447. * for(var i = 0; i < 10; i++){
  448. * promises.push(myFunc);
  449. * }
  450. * //create a new promise list with all 10 promises
  451. * return new PromiseList(promises).promise();
  452. * }
  453. *
  454. * myFunc.then(function(success){}, function(error){})
  455. * //chain promise operations
  456. * myFunc.chain(myfunc).then(function(success){}, function(error){})
  457. *
  458. * myFunc2.then(function(success){}, function(error){})
  459. * //chain promise operations
  460. * myFunc2.chain(myfunc).then(function(success){}, function(error){})
  461. *
  462. * @param {comb.Promise[]} [defs=[]] the list of promises
  463. * @constructs
  464. * @augments comb.Promise
  465. * @memberOf comb
  466. * */
  467. constructor: function (defs, normalizeResults) {
  468. 140 this.__errors = [];
  469. 140 this.__results = [];
  470. 140 this.normalizeResults = base.isBoolean(normalizeResults) ? normalizeResults : false;
  471. 140 this._super(arguments);
  472. 140 if (defs && defs.length) {
  473. 135 this.__defLength = defs.length;
  474. 135 forEach(defs, this.__addPromise, this);
  475. } else {
  476. 5 this.__resolve();
  477. }
  478. },
  479. /**
  480. * Add a promise to our chain
  481. * @private
  482. * @param promise the promise to add to our chain
  483. * @param i the index of the promise in our chain
  484. */
  485. __addPromise: function (promise, i) {
  486. 757 var self = this;
  487. 757 promise.then(
  488. function () {
  489. 742 var args = argsToArray(arguments);
  490. 742 args.unshift(i);
  491. 742 spreadArgs(self.callback, args, self);
  492. 742 promise = i = self = null;
  493. },
  494. function () {
  495. 15 var args = argsToArray(arguments);
  496. 15 args.unshift(i);
  497. 15 spreadArgs(self.errback, args, self);
  498. 15 promise = i = self = null;
  499. });
  500. },
  501. /**
  502. * Resolves the promise
  503. * @private
  504. */
  505. __resolve: function () {
  506. 140 if (!this.__fired) {
  507. 140 this.__fired = true;
  508. 140 var self = this;
  509. 140 nextTick(function () {
  510. 140 var cbs = self.__errors.length ? self.__errorCbs : self.__cbs,
  511. len = cbs.length, i,
  512. results = self.__errors.length ? self.__errors : self.__results;
  513. 140 for (i = 0; i < len; i++) {
  514. 136 spreadArgs(cbs[i], [results]);
  515. }
  516. });
  517. }
  518. },
  519. __callNextTick: function (cb, results) {
  520. 7 nextTick(function () {
  521. 7 spreadArgs(cb, [results]);
  522. 7 cb = results = null;
  523. });
  524. },
  525. addCallback: function (cb) {
  526. 143 if (cb) {
  527. 143 if (isPromise(cb)) {
  528. 1 cb = cb.callback;
  529. }
  530. 143 if (this.__fired && !this.__errors.length) {
  531. 7 this.__callNextTick(cb, this.__results);
  532. } else {
  533. 136 this.__cbs.push(cb);
  534. }
  535. }
  536. 143 return this;
  537. },
  538. addErrback: function (cb) {
  539. 141 if (cb) {
  540. 141 if (isPromise(cb)) {
  541. 1 cb = cb.errback;
  542. }
  543. 141 if (this.__fired && this.__errors.length) {
  544. 0 this.__callNextTick(cb, this.__errors);
  545. } else {
  546. 141 this.__errorCbs.push(cb);
  547. }
  548. }
  549. 141 return this;
  550. },
  551. callback: function (i) {
  552. 743 if (this.__fired) {
  553. 1 throw new Error("Already fired!");
  554. }
  555. 742 var args = argsToArray(arguments);
  556. 742 if (this.normalizeResults) {
  557. 725 args = args.slice(1);
  558. 725 args = args.length == 1 ? args.pop() : args;
  559. }
  560. 742 this.__results[i] = args;
  561. 742 this.__firedLength++;
  562. 742 if (this.__firedLength == this.__defLength) {
  563. 130 this.__resolve();
  564. }
  565. 742 return this.promise();
  566. },
  567. errback: function (i) {
  568. 16 if (this.__fired) {
  569. 1 throw new Error("Already fired!");
  570. }
  571. 15 var args = argsToArray(arguments);
  572. 15 if (this.normalizeResults) {
  573. 14 args = args.slice(1);
  574. 14 args = args.length == 1 ? args.pop() : args;
  575. }
  576. 15 this.__errors[i] = args;
  577. 15 this.__firedLength++;
  578. 15 if (this.__firedLength == this.__defLength) {
  579. 5 this.__resolve();
  580. }
  581. 15 return this.promise();
  582. }
  583. }
  584. });
  585. /**
  586. * Creates the promise chain
  587. * @ignore
  588. * @private
  589. */
  590. 1function callNext(list, results, propogate) {
  591. 59 var ret = new Promise().callback();
  592. 59 forEach(list, function (listItem) {
  593. 156 ret = ret.chain(propogate ? listItem : bindIgnore(null, listItem));
  594. 156 if (!propogate) {
  595. 112 ret.addCallback(function (res) {
  596. 89 results.push(res);
  597. 89 res = null;
  598. });
  599. }
  600. });
  601. 59 return propogate ? ret.promise() : ret.chain(results);
  602. }
  603. /**
  604. * Tests if an object is like a promise (i.e. it contains then, addCallback, addErrback)
  605. * @param obj object to test
  606. * @function
  607. * @static
  608. * @memberOf comb
  609. */
  610. 1function isPromiseLike(obj) {
  611. 2731 return isObject(obj, Promise) && typeof obj.then === "function";
  612. }
  613. 1function isPromise(obj) {
  614. 8688 return obj instanceof Promise;
  615. }
  616. /**
  617. * Waits for promise and non promise values to resolve and fires callback or errback appropriately. If you pass in an array of promises
  618. * then it will wait for all promises in the list to resolve.
  619. *
  620. * @example
  621. * var a = "hello";
  622. * var b = new comb.Promise().callback(world);
  623. * comb.when(a, b) => called back with ["hello", "world"];
  624. *
  625. * @param {Anything...} args variable number of arguments to wait for.
  626. * @param {Function} cb the callback function
  627. * @param {Function} eb the errback function
  628. * @returns {comb.Promise} a promise that is fired when all values have resolved
  629. * @static
  630. * @memberOf comb
  631. */
  632. 1function when(args, cb, eb) {
  633. 1373 var args = argsToArray(arguments), p;
  634. 1373 eb = base.isFunction(args[args.length - 1]) ? args.pop() : null;
  635. 1373 cb = base.isFunction(args[args.length - 1]) ? args.pop() : null;
  636. 1373 if (eb && !cb) {
  637. 9 cb = eb;
  638. 9 eb = null;
  639. }
  640. 1373 if (!args.length) {
  641. 7 p = new Promise().callback(args);
  642. 1366 } else if (args.length == 1) {
  643. 1326 args = args.pop();
  644. 1326 if (isPromiseLike(args)) {
  645. 651 p = args;
  646. 675 } else if (isArray(args) && array.every(args, isPromiseLike)) {
  647. 89 p = new PromiseList(args, true);
  648. } else {
  649. 586 p = new Promise().callback(args);
  650. }
  651. } else {
  652. 40 p = new PromiseList(args.map(function (a) {
  653. 135 return isPromiseLike(a) ? a : new Promise().callback(a);
  654. }), true);
  655. }
  656. 1373 if (cb) {
  657. 15 p.addCallback(cb);
  658. }
  659. 1373 if (eb) {
  660. 6 p.addErrback(eb);
  661. }
  662. 1373 return p.promise();
  663. }
  664. /**
  665. * Wraps traditional node style functions with a promise.
  666. * @example
  667. *
  668. * var fs = require("fs");
  669. * var readFile = comb.wrap(fs.readFile, fs);
  670. * readFile(__dirname + "/test.json").then(
  671. * function(buffer){
  672. * console.log(contents);
  673. * },
  674. * function(err){
  675. *
  676. * } console.error(err);
  677. * );
  678. *
  679. *
  680. * @param {Function} fn function to wrap
  681. * @param {Object} scope scope to call the function in
  682. *
  683. * @return {Funciton} a wrapped function
  684. * @static
  685. * @memberOf comb
  686. */
  687. 1function wrap(fn, scope) {
  688. 2 return function () {
  689. 2 var ret = new Promise();
  690. 2 var args = argsToArray(arguments);
  691. 2 args.push(ret.resolve.bind(ret));
  692. 2 fn.apply(scope || this, args);
  693. 2 return ret.promise();
  694. }
  695. }
  696. /**
  697. * Executes a list of items in a serial manner. If the list contains promises then each promise
  698. * will be executed in a serial manner, if the list contains non async items then the next item in the list
  699. * is called.
  700. *
  701. * @example
  702. *
  703. * var asyncAction = function(item, timeout){
  704. * var ret = new comb.Promise();
  705. * setTimeout(comb.hitchIgnore(ret, "callback", item), timeout);
  706. * return ret.promise();
  707. * };
  708. *
  709. * comb.serial([
  710. * comb.partial(asyncAction, 1, 1000),
  711. * comb.partial(asyncAction, 2, 900),
  712. * comb.partial(asyncAction, 3, 800),
  713. * comb.partial(asyncAction, 4, 700),
  714. * comb.partial(asyncAction, 5, 600),
  715. * comb.partial(asyncAction, 6, 500)
  716. * ]).then(function(results){
  717. * console.log(results); // [1,2,3,4,5,6];
  718. * });
  719. *
  720. *
  721. *
  722. * @param list
  723. * @param callback
  724. * @param errback
  725. * @static
  726. * @memberOf comb
  727. */
  728. 1function serial(list, callback, errback) {
  729. 56 if (base.isArray(list)) {
  730. 55 return callNext(list, [], false);
  731. } else {
  732. 1 throw new Error("When calling comb.serial the first argument must be an array");
  733. }
  734. }
  735. /**
  736. * Works just like {@link comb.Promise#chain} method, allowing you to propogate results from one funciton to another.
  737. * This is different than {@link comb.serial} in that it propogates results from one promise to the next, where
  738. * {@link comb.serial} does not.
  739. *
  740. * @example
  741. *
  742. * function asyncAction(add, timeout) {
  743. * return function (num) {
  744. * num = num || 0;
  745. * var ret = new comb.Promise();
  746. * setTimeout(function () {
  747. * ret.callback(num + add);
  748. * }, timeout);
  749. * return ret;
  750. * }
  751. * }
  752. *
  753. * comb.chain([
  754. * asyncAction(1, 100),
  755. * asyncAction(2, 100),
  756. * asyncAction(3, 100),
  757. * asyncAction(4, 100),
  758. * asyncAction(5, 100),
  759. * ]).then(function(results){
  760. * console.log(results); //15
  761. * });
  762. *
  763. * @param {function[]} list an array of function to call.
  764. * @return {comb.Promise} a promise that will resolve with the results of the last function in the list.
  765. * @static
  766. * @memberOf comb
  767. */
  768. 1function chain(list) {
  769. 5 if (base.isArray(list)) {
  770. 4 return callNext(list, [], true);
  771. } else {
  772. 1 throw new Error("When calling comb.serial the first argument must be an array");
  773. }
  774. }
  775. /**
  776. * Ensures that a promise is resolved before a the function can be run.
  777. *
  778. * For example suppose you have to ensure that you are connected to a database before you execute a function.
  779. *
  780. * ```
  781. * var findUser = comb.wait(connect(), function findUser(id){
  782. * //this wont execute until we are connected
  783. * return User.findById(id);
  784. * });
  785. *
  786. * comb.when(findUser(1), findUser(2)).then(function(users){
  787. * var user1 = users[0], user2 = users[1];
  788. * });
  789. *
  790. * ```
  791. *
  792. * @param args variable number of arguments to wait on. See {@link comb.when}.
  793. * @param {Function} fn function that will wait.
  794. * @return {Function} a function that will wait on the args to resolve.
  795. * @memberOf comb
  796. * @static
  797. */
  798. 1function wait(args, fn) {
  799. 2 var args = argsToArray(arguments), resolved = false;
  800. 2 fn = args.pop();
  801. 2 var p = when(args);
  802. 2 return function waiter() {
  803. 3 if (!resolved) {
  804. 2 var args = arguments;
  805. 2 return p.chain(function () {
  806. 2 resolved = true;
  807. 2 p = null;
  808. 2 return fn.apply(this, args);
  809. }.bind(this));
  810. } else {
  811. 1 return when(fn.apply(this, arguments));
  812. }
  813. };
  814. }
  815. /**
  816. * Wraps a stream in a promise waiting for either the `"end"` or `"error"` event to be triggered.
  817. *
  818. * ```
  819. * comb.promisfyStream(fs.createdReadStream("my.file")).chain(function(){
  820. * console.log("done reading!");
  821. * });
  822. *
  823. * ```
  824. *
  825. * @param stream stream to wrap
  826. * @return {comb.Promise} a Promise is resolved if `"end"` is triggered before `"error"` or rejected if `"error"` is triggered.
  827. * @memberOf comb
  828. * @static
  829. */
  830. 1function promisfyStream(stream) {
  831. 2 var ret = new Promise(), called;
  832. 2 function errorHandler() {
  833. 1 if (!called) {
  834. 1 called = true;
  835. 1 spreadArgs(ret.errback, arguments, ret);
  836. 1 stream.removeListener("error", endHandler);
  837. 1 stream.removeListener("end", endHandler);
  838. 1 stream = ret = null;
  839. }
  840. }
  841. 2 function endHandler() {
  842. 1 if (!called) {
  843. 1 called = true;
  844. 1 spreadArgs(ret.callback, arguments, ret);
  845. 1 stream.removeListener("error", endHandler);
  846. 1 stream.removeListener("end", endHandler);
  847. 1 stream = ret = null;
  848. }
  849. }
  850. 2 stream.on("error", errorHandler).on("end", endHandler);
  851. 2 return ret.promise();
  852. }
  853. /**
  854. * Creates a new {@link comb.Promise} that is resolved with the given value.
  855. *
  856. * ```
  857. * comb.resolved(1).chain(function(val){
  858. * //val === 1
  859. * });
  860. * ```
  861. *
  862. * If you give it a promise it will wait for the promise to resolve or error.
  863. *
  864. * ```
  865. * comb.resolved(new comb.Promise().callback(1)).chain(function(val){
  866. * //val === 1
  867. * });
  868. * ```
  869. *
  870. * @param val - val to resolve with
  871. * @returns {comb.Promise} a promisfied version of the value.
  872. * @memberOf comb
  873. * @static
  874. */
  875. 1function resolved(val) {
  876. 5 var ret = new Promise();
  877. 5 when(val).chain(ret.callback, ret.errback);
  878. 5 return ret.promise();
  879. }
  880. /**
  881. * Creates a new {@link comb.Promise} that is rejected with the given value.
  882. *
  883. * ```
  884. * comb.resolved(new Error("error!")).chain(null, function(err){
  885. * console.error(err.stack);
  886. * });
  887. * ```
  888. *
  889. * If you give it a promise it will wait for the promise to resolve or error but with reject either way.
  890. *
  891. * ```
  892. * comb.resolved(new comb.Promise().callback(1)).chain(null, function(err){
  893. * //err === 1
  894. * });
  895. * ```
  896. *
  897. * @param val - val to reject with
  898. * @returns {comb.Promise} a rejected promise with the specified value.
  899. * @memberOf comb
  900. * @static
  901. */
  902. 1function rejected(val) {
  903. 4 var ret = new Promise();
  904. 4 when(val).chain(ret.errback, ret.errback);
  905. 4 return ret.promise();
  906. }
  907. 1base.merge(exports, {
  908. isPromiseLike: isPromiseLike,
  909. when: when,
  910. wrap: wrap,
  911. wait: wait,
  912. serial: serial,
  913. chain: chain,
  914. promisfyStream: promisfyStream,
  915. resolved: resolved,
  916. rejected: rejected,
  917. Promise: Promise,
  918. PromiseList: PromiseList
  919. });
define.js
Coverage96.49 SLOC603 LOC171 Missed6
  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. SUPER_REGEXP = /(super)/ig,
  10. invoke = _base.__spreadArgs;
  11. 1function callSuper(args, a) {
  12. 1243 var meta = this.__meta,
  13. supers = meta.supers,
  14. l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos;
  15. 1243 if (l > pos) {
  16. 1243 a && (args = a);
  17. 1243 var name = superMeta.name, f = superMeta.f, m;
  18. 1243 do {
  19. 1243 m = supers[pos][name];
  20. 1243 if ("function" === typeof m && (m = m._f || m) !== f) {
  21. 1239 superMeta.pos = 1 + pos;
  22. 1239 return invoke(m, args, this);
  23. }
  24. } while (l > ++pos);
  25. }
  26. 4 return null;
  27. }
  28. 1function getSuper() {
  29. 2 var meta = this.__meta,
  30. supers = meta.supers,
  31. l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos;
  32. 2 if (l > pos) {
  33. 2 var name = superMeta.name, f = superMeta.f, m;
  34. 2 do {
  35. 3 m = supers[pos][name];
  36. 3 if ("function" === typeof m && (m = m._f || m) !== f) {
  37. 1 superMeta.pos = 1 + pos;
  38. 1 return m.bind(this);
  39. }
  40. } while (l > ++pos);
  41. }
  42. 1 return null;
  43. }
  44. 1function defaultFunction() {
  45. 287 var meta = this.__meta || {},
  46. supers = meta.supers,
  47. l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos;
  48. 287 if (l > pos) {
  49. 143 var name = superMeta.name, f = superMeta.f, m;
  50. 143 do {
  51. 223 m = supers[pos][name];
  52. 223 if ("function" === typeof m && (m = m._f || m) !== f) {
  53. 96 superMeta.pos = 1 + pos;
  54. 96 return invoke(m, arguments, this);
  55. }
  56. } while (l > ++pos);
  57. }
  58. 191 return null;
  59. }
  60. 1function functionWrapper(f, name) {
  61. 318 if (f.toString().match(SUPER_REGEXP)) {
  62. 104 var wrapper = function () {
  63. 2128 var ret, meta = this.__meta || {};
  64. 2128 var orig = meta.superMeta;
  65. 2128 meta.superMeta = {f: f, pos: 0, name: name};
  66. 2128 ret = invoke(f, arguments, this);
  67. 2128 meta.superMeta = orig;
  68. 2128 return ret;
  69. };
  70. 104 wrapper._f = f;
  71. 104 return wrapper;
  72. } else {
  73. 214 f._f = f;
  74. 214 return f;
  75. }
  76. }
  77. /**
  78. * @ignore
  79. */
  80. 1function defineMixinProps(child, proto) {
  81. 34 var operations = proto.setters || {};
  82. 34 for (var i in operations) {
  83. 15 if (!child.__lookupSetter__(i)) { //make sure that the setter isnt already there
  84. 15 child.__defineSetter__(i, operations[i]);
  85. }
  86. }
  87. 34 operations = proto.getters || {};
  88. 34 for (i in operations) {
  89. 15 if (!child.__lookupGetter__(i)) {
  90. //define the getter if the child does not already have it
  91. 15 child.__defineGetter__(i, operations[i]);
  92. }
  93. }
  94. 34 for (var j in proto) {
  95. 103 if (j != "getters" && j != "setters") {
  96. 83 var p = proto[j];
  97. 83 if ("function" === typeof p) {
  98. 78 if (!child.hasOwnProperty(j)) {
  99. 10 child[j] = functionWrapper(defaultFunction, j);
  100. }
  101. } else {
  102. 5 child[j] = p;
  103. }
  104. }
  105. }
  106. }
  107. /**
  108. * @ignore
  109. */
  110. 1function mixin() {
  111. 7 var args = Array.prototype.slice.call(arguments), l = args.length;
  112. 7 var child = this.prototype, childMeta = child.__meta, thisMeta = this.__meta, bases = child.__meta.bases, staticBases = bases.slice(),
  113. staticSupers = thisMeta.supers || [], supers = childMeta.supers || [];
  114. 7 for (var i = 0; i < l; i++) {
  115. 17 var m = args[i], mProto = m.prototype;
  116. 17 var protoMeta = mProto.__meta, meta = m.__meta;
  117. 17 if (!protoMeta) {
  118. 0 protoMeta = {proto: mProto || {}};
  119. 0 Object.defineProperty(mProto, "__meta", {
  120. enumerable: false,
  121. value: protoMeta
  122. });
  123. }
  124. 17 if (!meta) {
  125. 0 meta = {proto: m["__proto__"] || {}};
  126. 0 Object.defineProperty(m, "__meta", {
  127. enumerable: false,
  128. value: protoMeta
  129. });
  130. }
  131. 17 defineMixinProps(child, protoMeta.proto || {});
  132. 17 defineMixinProps(this, meta.proto || {});
  133. //copy the bases for static,
  134. 17 mixinSupers(m.prototype, supers, bases);
  135. 17 mixinSupers(m, staticSupers, staticBases);
  136. }
  137. 7 return this;
  138. }
  139. /**
  140. * @ignore
  141. */
  142. 1function mixinSupers(sup, arr, bases) {
  143. 386 var meta = sup.__meta;
  144. 386 if (!meta) {
  145. 0 meta = {};
  146. 0 Object.defineProperty(sup, "__meta", {
  147. enumerable: false,
  148. value: meta
  149. });
  150. }
  151. 386 var unique = sup.__meta.unique;
  152. 386 !unique && (meta.unique = "define" + ++classCounter);
  153. //check it we already have this super mixed into our prototype chain
  154. //if true then we have already looped their supers!
  155. 386 if (bases.indexOf(unique) == -1) {
  156. //add their id to our bases
  157. 234 bases.push(unique);
  158. 234 var supers = sup.__meta.supers || [], i = supers.length - 1 || 0;
  159. 234 while (i >= 0) {
  160. 258 mixinSupers(supers[i--], arr, bases);
  161. }
  162. 234 arr.unshift(sup);
  163. }
  164. }
  165. /**
  166. * @ignore
  167. */
  168. 1function defineProps(child, proto) {
  169. 92 var operations = proto.setters;
  170. 92 if (operations) {
  171. 8 for (var i in operations) {
  172. 13 child.__defineSetter__(i, operations[i]);
  173. }
  174. }
  175. 92 operations = proto.getters || {};
  176. 92 if (operations) {
  177. 92 for (i in operations) {
  178. 47 child.__defineGetter__(i, operations[i]);
  179. }
  180. }
  181. 92 for (i in proto) {
  182. 359 if (i != "getters" && i != "setters") {
  183. 338 var f = proto[i];
  184. 338 if ("function" === typeof f) {
  185. 305 var meta = f.__meta || {};
  186. 305 if (!meta.isConstructor) {
  187. 304 child[i] = functionWrapper(f, i);
  188. } else {
  189. 1 child[i] = f;
  190. }
  191. } else {
  192. 33 child[i] = f;
  193. }
  194. }
  195. }
  196. }
  197. 1function _export(obj, name) {
  198. 11 if (obj && name) {
  199. 2 obj[name] = this;
  200. } else {
  201. 9 obj.exports = obj = this;
  202. }
  203. 11 return this;
  204. }
  205. 1function extend(proto) {
  206. 7 return define(this, proto);
  207. }
  208. /**
  209. * @ignore
  210. */
  211. 1function __define(child, sup, proto) {
  212. 48 var childProto = child.prototype, supers = [];
  213. 48 var unique = "define" + ++classCounter, bases = [], staticBases = [];
  214. 48 var instanceSupers = [], staticSupers = [];
  215. 48 var meta = {
  216. supers: instanceSupers,
  217. unique: unique,
  218. bases: bases,
  219. superMeta: {
  220. f: null,
  221. pos: 0,
  222. name: null
  223. }
  224. };
  225. 48 Object.defineProperty(childProto, "__meta", {
  226. enumerable: false,
  227. value: meta
  228. });
  229. 48 var childMeta = {
  230. supers: staticSupers,
  231. unique: unique,
  232. bases: staticBases,
  233. isConstructor: true,
  234. superMeta: {
  235. f: null,
  236. pos: 0,
  237. name: null
  238. }
  239. };
  240. 48 Object.defineProperty(child, "__meta", {
  241. enumerable: false,
  242. value: childMeta
  243. });
  244. 48 if ((isHash(sup) && !proto)) {
  245. 2 proto = sup;
  246. 2 sup = Base;
  247. 46 } else if ((!sup && isHash(proto))) {
  248. 14 sup = Base;
  249. }
  250. 48 if ("function" === typeof sup || isArray(sup)) {
  251. 47 supers = isArray(sup) ? sup : [sup];
  252. 47 sup = supers.shift();
  253. 47 child.__proto__ = sup;
  254. 47 childProto.__proto__ = sup.prototype;
  255. 47 mixinSupers(sup.prototype, instanceSupers, bases),
  256. mixinSupers(sup, staticSupers, staticBases);
  257. }
  258. 48 if (proto) {
  259. 46 var instance = meta.proto = proto.instance || {};
  260. 46 !instance.hasOwnProperty("constructor") && (instance.constructor = defaultFunction);
  261. 46 var stat = childMeta.proto = proto.static || {};
  262. 46 stat.init = stat.init || defaultFunction;
  263. 46 defineProps(childProto, instance, false);
  264. 46 defineProps(child, stat, true);
  265. } else {
  266. 2 meta.proto = {};
  267. 2 childMeta.proto = {};
  268. 2 child.init = functionWrapper(defaultFunction, "init");
  269. 2 childProto.constructor = functionWrapper(defaultFunction, "constructor");
  270. }
  271. 48 if (supers.length) {
  272. 7 invoke(mixin, supers, child);
  273. }
  274. 48 Object.defineProperty(childProto, "_super", {
  275. enumerable: false,
  276. value: callSuper
  277. });
  278. 48 Object.defineProperty(child, "_super", {
  279. enumerable: false,
  280. value: callSuper
  281. });
  282. 48 Object.defineProperty(child, "_getSuper", {
  283. enumerable: false,
  284. value: getSuper
  285. });
  286. 48 Object.defineProperty(childProto, "_getSuper", {
  287. enumerable: false,
  288. value: getSuper
  289. });
  290. 48 Object.defineProperty(childProto, "_static", {
  291. enumerable: false,
  292. value: child
  293. });
  294. }
  295. 1function define(sup, proto) {
  296. 45 function defineConstructor() {
  297. 2876 invoke(this.constructor, arguments, this);
  298. 2875 sup = proto = null;
  299. }
  300. 45 __define(defineConstructor, sup, proto);
  301. 45 return defineConstructor.init() || defineConstructor;
  302. }
  303. 1function singleton(sup, proto) {
  304. 3 var retInstance;
  305. 3 function singletonConstructor() {
  306. 6 if (!retInstance) {
  307. 2 invoke(this.constructor, arguments, this);
  308. 2 retInstance = this;
  309. }
  310. 6 return retInstance;
  311. }
  312. 3 __define(singletonConstructor, sup, proto);
  313. 3 return singletonConstructor.init() || singletonConstructor;
  314. }
  315. 1Base = define({
  316. instance: {},
  317. "static": {
  318. mixin: mixin,
  319. extend: extend,
  320. as: _export
  321. }
  322. });
  323. /**
  324. * Defines a new class to be used
  325. *
  326. * <p>
  327. * Class methods
  328. * <ul>
  329. * <li>as(module | object, name): exports the object to module or the object with the name</li>
  330. * <li>mixin(mixin) : mixes in an object</li>
  331. * </ul>
  332. * </br>
  333. * Instance methods
  334. * <ul>
  335. * <li>_super(argumnents, [?newargs]): calls the super of the current method</li>
  336. * </ul>
  337. *
  338. * </br>
  339. * Instance properties
  340. * <ul>
  341. * <li>_static: use to reference class properties and methods</li>
  342. * </ul>
  343. *
  344. * </p>
  345. *
  346. *
  347. * @example
  348. * //Class without a super class
  349. * var Mammal = comb.define(null, {
  350. * instance : {
  351. *
  352. * constructor: function(options) {
  353. * options = options || {};
  354. * this._super(arguments);
  355. * this._type = options.type || "mammal";
  356. * },
  357. *
  358. * speak : function() {
  359. * return "A mammal of type " + this._type + " sounds like";
  360. * },
  361. *
  362. * //Define your getters
  363. * getters : {
  364. * type : function() {
  365. * return this._type;
  366. * }
  367. * },
  368. *
  369. * //Define your setters
  370. * setters : {
  371. * type : function(t) {
  372. * this._type = t;
  373. * }
  374. * }
  375. * },
  376. *
  377. * //Define your static methods
  378. * static : {
  379. * soundOff : function() {
  380. * return "Im a mammal!!";
  381. * }
  382. * }
  383. * });
  384. *
  385. * //Show singular inheritance
  386. *var Wolf = comb.define(Mammal, {
  387. * instance: {
  388. * constructor: function(options) {
  389. * options = options || {};
  390. * //You can call your super constructor, or you may not
  391. * //call it to prevent the super initializing parameters
  392. * this._super(arguments);
  393. * this._sound = "growl";
  394. * this._color = options.color || "grey";
  395. * },
  396. *
  397. * speak : function() {
  398. * //override my super classes speak
  399. * //Should return "A mammal of type mammal sounds like a growl"
  400. * return this._super(arguments) + " a " + this._sound;
  401. * },
  402. *
  403. * //add new getters for sound and color
  404. * getters : {
  405. *
  406. * color : function() {
  407. * return this._color;
  408. * },
  409. *
  410. * sound : function() {
  411. * return this._sound;
  412. * }
  413. * },
  414. *
  415. * setters : {
  416. *
  417. * //NOTE color is read only except on initialization
  418. *
  419. * sound : function(s) {
  420. * this._sound = s;
  421. * }
  422. * }
  423. *
  424. * },
  425. *
  426. * static : {
  427. * //override my satic soundOff
  428. * soundOff : function() {
  429. * //You can even call super in your statics!!!
  430. * //should return "I'm a mammal!! that growls"
  431. * return this._super(arguments) + " that growls";
  432. * }
  433. * }
  434. *});
  435. *
  436. *
  437. * //Typical hierarchical inheritance
  438. * // Mammal->Wolf->Dog
  439. * var Dog = comb.define(Wolf, {
  440. * instance: {
  441. * constructor: function(options) {
  442. * options = options || {};
  443. * this._super(arguments);
  444. * //override Wolfs initialization of sound to woof.
  445. * this._sound = "woof";
  446. *
  447. * },
  448. *
  449. * speak : function() {
  450. * //Should return "A mammal of type mammal sounds like a growl thats domesticated"
  451. * return this._super(arguments) + " thats domesticated";
  452. * }
  453. * },
  454. *
  455. * static : {
  456. * soundOff : function() {
  457. * //should return "I'm a mammal!! that growls but now barks"
  458. * return this._super(arguments) + " but now barks";
  459. * }
  460. * }
  461. *});
  462. *
  463. *
  464. *
  465. * dog instanceof Wolf => true
  466. * dog instanceof Mammal => true
  467. * dog.speak() => "A mammal of type mammal sounds like a woof thats domesticated"
  468. * dog.type => "mammal"
  469. * dog.color => "gold"
  470. * dog.sound => "woof"
  471. * Dog.soundOff() => "Im a mammal!! that growls but now barks"
  472. *
  473. * // Mammal->Wolf->Dog->Breed
  474. *var Breed = comb.define(Dog, {
  475. * instance: {
  476. *
  477. * //initialize outside of constructor
  478. * _pitch : "high",
  479. *
  480. * constructor: function(options) {
  481. * options = options || {};
  482. * this._super(arguments);
  483. * this.breed = options.breed || "lab";
  484. * },
  485. *
  486. * speak : function() {
  487. * //Should return "A mammal of type mammal sounds like a
  488. * //growl thats domesticated with a high pitch!"
  489. * return this._super(arguments) + " with a " + this._pitch + " pitch!";
  490. * },
  491. *
  492. * getters : {
  493. * pitch : function() {
  494. * return this._pitch;
  495. * }
  496. * }
  497. * },
  498. *
  499. * static : {
  500. * soundOff : function() {
  501. * //should return "I'M A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  502. * return this._super(arguments).toUpperCase() + "!";
  503. * }
  504. * }
  505. * });
  506. *
  507. *
  508. * var breed = new Breed({color : "gold", type : "lab"}),
  509. *
  510. *
  511. *
  512. * breed instanceof Dog => true
  513. * breed instanceof Wolf => true
  514. * breed instanceof Mammal => true
  515. * breed.speak() => "A mammal of type lab sounds like a woof "
  516. * + "thats domesticated with a high pitch!"
  517. * breed.type => "lab"
  518. * breed.color => "gold"
  519. * breed.sound => "woof"
  520. * breed.soundOff() => "IM A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  521. *
  522. *
  523. * //Example of multiple inheritance
  524. * //NOTE proto is optional
  525. *
  526. * //Mammal is super class
  527. * //Wolf Dog and Breed inject functionality into the prototype
  528. * var Lab = comb.define([Mammal, Wolf, Dog, Breed]);
  529. *
  530. * var lab = new Lab();
  531. * lab instanceof Wolf => false
  532. * lab instanceof Dog => false
  533. * lab instanceof Breed => false
  534. * lab instanceof Mammal => true
  535. * lab.speak() => "A mammal of type mammal sounds like a"
  536. * + " woof thats domesticated with a high pitch!"
  537. * Lab.soundOff() => "IM A MAMMAL!! THAT GROWLS BUT NOW BARKS!"
  538. *
  539. * @name define
  540. * @memberOf comb
  541. *
  542. * @param {Array|Class} super the supers of this class
  543. * @param {Object} [proto] the object used to define this class
  544. * @param {Object} [proto.instance] the instance methods of the class
  545. * @param {Object} [proto.instance.getters] the getters for the class
  546. * @param {Object} [proto.instance.setters] the setters for the class
  547. * @param {Object} [proto.static] the Class level methods of this class
  548. * @param {Object} [proto.static.getters] static getters for the object
  549. * @param {Object} [proto.static.setters] static setters for the object
  550. *
  551. * @returns {Object} the constructor of the class to be used with new keyword
  552. */
  553. 1exports.define = define;
  554. /**
  555. * Defines a singleton instance of a Class. See {@link define}
  556. * @example
  557. * var MyLab = comb.singleton([Mammal, Wolf, Dog, Breed]);
  558. * var myLab1 = new MyLab();
  559. * myLab1.type = "collie"
  560. * var myLab2 = new MyLab();
  561. * myLab1 === myLab2 => true
  562. * myLab1.type => "collie"
  563. * myLab2.type => "collie"
  564. *
  565. *
  566. * @name singleton
  567. * @memberOf comb
  568. */
  569. 1exports.singleton = singleton;
base/string.js
Coverage98.06 SLOC546 LOC155 Missed3
  1. 1var comb = exports, date,
  2. misc = require("./misc"),
  3. object = require("./object"),
  4. round = require("./number").number.round,
  5. isHash = object.isHash,
  6. pSlice = Array.prototype.slice,
  7. toStr = Object.prototype.toString,
  8. abs = Math.abs;
  9. 1function getDate() {
  10. 70 return (date || (date = require("./date")));
  11. }
  12. /**
  13. * Truncates a string to the specified length.
  14. * @example
  15. *
  16. * //from the beginning
  17. * comb.string.truncate("abcdefg", 3) => "abc";
  18. * //from the end
  19. * comb.string.truncate("abcdefg", 3,true) => "efg"
  20. * //omit the length
  21. * comb.string.truncate("abcdefg") => "abcdefg"
  22. *
  23. * @param {String} string the string to truncate
  24. * @param {Number} [length = -1] the max length of the string, if the string is
  25. * shorter than the length then the string is returned.
  26. * @param {Boolean} [end=false] truncate starting at the end of the string
  27. *
  28. * @return {String} the truncated string.
  29. * @memberOf comb.string
  30. * @static
  31. */
  32. 1function truncate(string, length, end) {
  33. 46 var ret = string;
  34. 46 if (comb.isString(ret)) {
  35. 44 if (string.length > length) {
  36. 27 if (end) {
  37. 8 var l = string.length;
  38. 8 ret = string.substring(l - length, l);
  39. } else {
  40. 19 ret = string.substring(0, length);
  41. }
  42. }
  43. } else {
  44. 2 ret = truncate("" + ret, length);
  45. }
  46. 46 return ret;
  47. }
  48. /**
  49. * Escapes an HTML string by replacing <>&" characters.
  50. *
  51. * @example
  52. * comb.string.escapeHtml('<script>alert("test")</script>'); // &lt;script&gt;alert("test")&lt;/script&gt;
  53. *
  54. * @param {String} str The string to escape.
  55. * @memberOf comb.string
  56. * @static
  57. */
  58. 1function escapeHtml(str) {
  59. 1 return String(str)
  60. .replace(/&/g, '&amp;')
  61. .replace(/</g, '&lt;')
  62. .replace(/>/g, '&gt;')
  63. .replace(/"/g, '&quot;')
  64. .replace(/'/g, '&#39;');
  65. }
  66. /**
  67. * Pads a string
  68. *
  69. * @example
  70. *
  71. * comb.string.pad("STR", 5, " ", true) => "STR "
  72. * comb.string.pad("STR", 5, "$") => "$$STR"
  73. *
  74. * @param {String} string the string to pad
  75. * @param {Number} length the length of the string when padded
  76. * @param {String} [ch= " "] character to pad the string with
  77. * @param {Boolean} [end=false] if true then the padding is added to the end
  78. *
  79. * @returns {String} the padded string
  80. * @memberOf comb.string
  81. * @static
  82. */
  83. 1function pad(string, length, ch, end) {
  84. 376 string = "" + string; //check for numbers
  85. 376 ch = ch || " ";
  86. 376 var strLen = string.length;
  87. 376 while (strLen < length) {
  88. 306 if (end) {
  89. 158 string += ch;
  90. } else {
  91. 148 string = ch + string;
  92. }
  93. 306 strLen++;
  94. }
  95. 376 return string;
  96. }
  97. /**
  98. * Tests if something is a string.
  99. *
  100. * @example
  101. *
  102. * comb.isString("true") //true
  103. * comb.isString(true) //false
  104. *
  105. * @param obj the thing to test
  106. * @return {Boolean} returns true if the argument is a string.
  107. * @static
  108. * @memberOf comb
  109. */
  110. 1function isString(obj) {
  111. 36117 return toStr.call(obj) === '[object String]';
  112. }
  113. 1comb.isString = isString;
  114. 1var FORMAT_REGEX = /%((?:-?[\+| ]?.?\d*(?:\.\d+)*)?|(?:\[[^\[|\]]*\]))?([sjdDZ])/g;
  115. 1var INTERP_REGEX = /{(?:\[([^\[|\]]*)\])?(\w+)}/g;
  116. 1var STR_FORMAT = /(-?)(\+?)([A-Z|a-z|\W]?)([1-9][0-9]*)?$/;
  117. 1var NUMBER_FORMAT = /(-?)([\+| ]?)([^\.1-9])?([1-9][0-9]*)?(\.[1-9][0-9]*)?$/;
  118. 1var OBJECT_FORMAT = /([1-9][0-9]*)$/g;
  119. 1function formatString(string, format) {
  120. 64 var match = format.match(STR_FORMAT), ret = string;
  121. 64 if (match) {
  122. 64 var isLeftJustified = match[1], padChar = match[3], width = match[4];
  123. 64 if (width) {
  124. 64 width = parseInt(width, 10);
  125. 64 if (ret.length < width) {
  126. 35 ret = pad(ret, width, padChar, isLeftJustified);
  127. } else {
  128. 29 ret = truncate(ret, width);
  129. }
  130. }
  131. }
  132. 64 return ret;
  133. }
  134. 1function formatNumber(number, format) {
  135. 37 var ret = "";
  136. 37 if (typeof number === "number") {
  137. 35 ret = "" + abs(number);
  138. 35 var match = format.match(NUMBER_FORMAT);
  139. 35 if (match) {
  140. 35 var isLeftJustified = match[1], signed = (number < 0 ? '-' : match[2]), padChar = match[3] || ' ', width = match[4], precision = match[5];
  141. 35 if (precision) {
  142. 12 precision = parseInt(precision.replace(/^\./, ""), 10);
  143. 12 ret = round(abs(number), precision) + "";
  144. //check and ensure we have padding zeros
  145. 12 var split = ret.split(".");
  146. 12 if (split.length === 1) {
  147. 12 ret = [split[0], pad("0", precision, "0", true)].join(".");
  148. 0 } else if (split[1].length < precision) {
  149. 0 ret = [split[0], pad(split[1], precision, "0", true)].join(".");
  150. }
  151. }
  152. 35 if (padChar === ' ') {
  153. 27 ret = signed + ret;
  154. 27 signed = false;
  155. }
  156. 35 if (width) {
  157. 26 width = parseInt(width, 10);
  158. 26 if (signed) {
  159. 6 width--;
  160. }
  161. 26 if (ret.length < width) {
  162. 23 ret = pad(ret, width, padChar || " ", isLeftJustified);
  163. } else {
  164. 3 ret = truncate(ret, width);
  165. }
  166. }
  167. 35 if (signed) {
  168. 6 ret = signed + ret;
  169. }
  170. }
  171. } else {
  172. 2 throw new Error("comb.string.format : when using %d the parameter must be a number!");
  173. }
  174. 35 return ret;
  175. }
  176. 1function formatObject(object, format) {
  177. 8 var ret, match = format.match(OBJECT_FORMAT), spacing = 0;
  178. 8 if (match) {
  179. 8 spacing = parseInt(match[0], 10);
  180. 8 if (isNaN(spacing)) {
  181. 0 spacing = 0;
  182. }
  183. }
  184. 8 try {
  185. 8 ret = JSON.stringify(object, null, spacing);
  186. } catch (e) {
  187. 2 throw new Error("comb.string.format : Unable to parse json from ", object);
  188. }
  189. 6 return ret;
  190. }
  191. /**
  192. * Formats a string with the specified format
  193. *
  194. * Format `String`s
  195. *
  196. * ```
  197. * comb.string.format("%s", "Hello"); // "Hello"
  198. * comb.string.format("%10s", "Hello"); // " Hello"
  199. * comb.string.format("%-10s", "Hello"); // ""Hello ""
  200. * comb.string.format('%.10s', "Hello"); //".....Hello"
  201. * comb.string.format('%-!10s', "Hello"); //"Hello!!!!!"
  202. * comb.string.format("%-.10s%s!", "Hello", "World"); //"Hello.....World!"
  203. * ```
  204. *
  205. * Formatting Numbers
  206. *
  207. * ```
  208. * comb.string.format('%d', 10); //"10"
  209. *
  210. * //setting precision
  211. * comb.string.format('%.2d', 10); //"10.00"
  212. *
  213. * //specifying width
  214. * comb.string.format('%5d', 10); //" 10"
  215. *
  216. * //Signed
  217. * comb.string.format('%+d', 10); //"+10"
  218. * comb.string.format('%+d', -10); //"-10"
  219. *
  220. * comb.string.format('% d', 10); //" 10"
  221. * comb.string.format('% d', -10); //"-10"
  222. *
  223. * //width
  224. * comb.string.format('%5d', 10); //" 10"
  225. *
  226. * //width and precision
  227. * comb.string.format('%6.2d', 10); //" 10.00"
  228. *
  229. * //signed, width and precision
  230. * comb.string.format('%+ 7.2d', 10); //" +10.00"
  231. * comb.string.format('%+ 7.2d', -10); //" -10.00"
  232. * comb.string.format('%+07.2d', 10); //"+010.00"
  233. * comb.string.format('%+07.2d', -10); //"-010.00"
  234. * comb.string.format('% 7.2d', 10); //" 10.00"
  235. * comb.string.format('% 7.2d', -10); //" -10.00"
  236. * comb.string.format('% 7.2d', 10); //" 10.00"
  237. * comb.string.format('% 7.2d', -10); //" -10.00"
  238. *
  239. * //use a 0 as padding
  240. * comb.string.format('%010d', 10); //"0000000010"
  241. *
  242. * //use an ! as padding
  243. * comb.string.format('%!10d', 10); //"!!!!!!!!10"
  244. *
  245. * //left justify signed ! as padding and a width of 10
  246. * comb.string.format('%-+!10d', 10); //"+10!!!!!!!"
  247. *
  248. * ```
  249. *
  250. * Formatting dates
  251. *
  252. * ```
  253. * comb.string.format("%[h:mm a]D", new Date(2014, 05, 04, 7,6,1)); // 7:06 AM - local -
  254. * comb.string.format("%[h:mm a]Z", new Date(2014, 05, 04, 7,6,1)); //12:06 PM - UTC
  255. * comb.string.format("%[yyyy-MM-dd]D", new Date(2014, 05, 04, 7,6,1)); // 2014-06-04 - local
  256. * comb.string.format("%[yyyy-MM-dd]Z", new Date(2014, 05, 04, 7,6,1)); // 2014-06-04 - UTC
  257. * ```
  258. *
  259. * Formatting Objects
  260. *
  261. * ```
  262. * //When using object formats they must be in an array otherwise
  263. * //format will try to interpolate the properties into the string.
  264. * comb.string.format("%j", [{a : "b"}]) // '{"a":"b"}'
  265. *
  266. * //Specifying spacing
  267. * comb.string.format("%4j", [{a : "b"}]) // '{\n "a": "b"\n}'
  268. *
  269. * ```
  270. *
  271. * String interpolation
  272. *
  273. * ```
  274. * comb.string.format("{hello}, {world}", {hello : "Hello", world : "World"); //"Hello, World";
  275. * comb.string.format("{[.2]min}...{[.2]max}", {min: 1, max: 10}); //"1.00...10.00"
  276. * ```
  277. *
  278. * @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.
  279. * <ol>
  280. * <li>String Formats %[options]s</li>
  281. * <ul>
  282. * <li>- : left justified</li>
  283. * <li>Char : padding character <b>Excludes d,j,s</b></li>
  284. * <li>Number : width</li>
  285. * </ul>
  286. * </li>
  287. * <li>Number Formats %[options]d</li>
  288. * <ul>
  289. * <li>`-` : left justified</li>
  290. * <li>`+` or `<space>` : signed number if space is used the number will use a extra `<space>` instead of a `+`</li>
  291. * <li>`<Char>` : padding character <b>Excludes d,j,s</b></li>
  292. * <li>`Number` : width</li>
  293. * <li>`.Number`: specify the precision of the number</li>
  294. * </ul>
  295. * </li>
  296. * <li>Object Formats %[options]j</li>
  297. * <ul>
  298. * <li>Number : spacing for object properties.</li>
  299. * </ul>
  300. * </li>
  301. * </ol>
  302. *
  303. *
  304. * @param {Object|Array|Arguments...} obj the parameters to replace in the string
  305. * if an array is passed then the array is used sequentially
  306. * if an object is passed then the object keys are used
  307. * if a variable number of args are passed then they are used like an array
  308. *
  309. * @returns {String} the formatted string
  310. * @memberOf comb.string
  311. * @static
  312. */
  313. 1function format(str, obj) {
  314. 180 if (obj instanceof Array) {
  315. 82 var i = 0, len = obj.length;
  316. //find the matches
  317. 82 return str.replace(FORMAT_REGEX, function (m, format, type) {
  318. 134 var replacer, ret;
  319. 134 if (i < len) {
  320. 132 replacer = obj[i++];
  321. } else {
  322. //we are out of things to replace with so
  323. //just return the match?
  324. 2 return m;
  325. }
  326. 132 if (m === "%s" || m === "%d" || m === "%D") {
  327. //fast path!
  328. 61 ret = replacer + "";
  329. 71 } else if (m === "%Z") {
  330. 2 ret = replacer.toUTCString();
  331. 69 } else if (m === "%j") {
  332. 4 try {
  333. 4 ret = JSON.stringify(replacer);
  334. } catch (e) {
  335. 2 throw new Error("comb.string.format : Unable to parse json from ", replacer);
  336. }
  337. } else {
  338. 65 format = format.replace(/^\[|\]$/g, "");
  339. 65 switch (type) {
  340. case "s":
  341. 8 ret = formatString(replacer, format);
  342. 8 break;
  343. case "d":
  344. 35 ret = formatNumber(replacer, format);
  345. 33 break;
  346. case "j":
  347. 6 ret = formatObject(replacer, format);
  348. 4 break;
  349. case "D":
  350. 8 ret = getDate().date.format(replacer, format);
  351. 8 break;
  352. case "Z":
  353. 8 ret = getDate().date.format(replacer, format, true);
  354. 8 break;
  355. }
  356. }
  357. 126 return ret;
  358. });
  359. 98 } else if (isHash(obj)) {
  360. 36 return str.replace(INTERP_REGEX, function (m, format, value) {
  361. 142 value = obj[value];
  362. 142 if (!misc.isUndefined(value)) {
  363. 140 if (format) {
  364. 86 if (comb.isString(value)) {
  365. 56 return formatString(value, format);
  366. 30 } else if (typeof value === "number") {
  367. 2 return formatNumber(value, format);
  368. 28 } else if (getDate().isDate(value)) {
  369. 26 return getDate().date.format(value, format);
  370. 2 } else if (typeof value === "object") {
  371. 2 return formatObject(value, format);
  372. }
  373. } else {
  374. 54 return "" + value;
  375. }
  376. }
  377. 2 return m;
  378. });
  379. } else {
  380. 62 var args = pSlice.call(arguments).slice(1);
  381. 62 return format(str, args);
  382. }
  383. }
  384. 1var styles = {
  385. //styles
  386. bold: 1,
  387. bright: 1,
  388. italic: 3,
  389. underline: 4,
  390. blink: 5,
  391. inverse: 7,
  392. crossedOut: 9,
  393. red: 31,
  394. green: 32,
  395. yellow: 33,
  396. blue: 34,
  397. magenta: 35,
  398. cyan: 36,
  399. white: 37,
  400. redBackground: 41,
  401. greenBackground: 42,
  402. yellowBackground: 43,
  403. blueBackground: 44,
  404. magentaBackground: 45,
  405. cyanBackground: 46,
  406. whiteBackground: 47,
  407. encircled: 52,
  408. overlined: 53,
  409. grey: 90,
  410. black: 90
  411. };
  412. /**
  413. * Styles a string according to the specified styles.
  414. *
  415. * @example
  416. * //style a string red
  417. * comb.string.style('myStr', 'red');
  418. * //style a string red and bold
  419. * comb.string.style('myStr', ['red', bold]);
  420. *
  421. * @param {String} str The string to style.
  422. * @param {String|Array} styles the style or styles to apply to a string.
  423. * options include :
  424. * <ul>
  425. * <li>bold</li>
  426. * <li>bright</li>
  427. * <li>italic</li>
  428. * <li>underline</li>
  429. * <li>inverse</li>
  430. * <li>crossedOut</li>
  431. * <li>blink</li>
  432. * <li>red</li>
  433. * <li>green</li>
  434. * <li>yellow</li>
  435. * <li>blue</li>
  436. * <li>magenta</li>
  437. * <li>cyan</li>
  438. * <li>white</li>
  439. * <li>redBackground</li>
  440. * <li>greenBackground</li>
  441. * <li>yellowBackground</li>
  442. * <li>blueBackground</li>
  443. * <li>magentaBackground</li>
  444. * <li>cyanBackground</li>
  445. * <li>whiteBackground</li>
  446. * <li>grey</li>
  447. * <li>black</li>
  448. *
  449. * </ul>
  450. * @memberOf comb.string
  451. * @static
  452. */
  453. 1function style(str, options) {
  454. 89 var ret = str;
  455. 89 if (options) {
  456. 89 if (ret instanceof Array) {
  457. 2 ret = ret.map(function (s) {
  458. 6 return style(s, options);
  459. });
  460. 87 } else if (options instanceof Array) {
  461. 8 options.forEach(function (option) {
  462. 24 ret = style(ret, option);
  463. });
  464. 79 } else if (options in styles) {
  465. 79 ret = '\x1B[' + styles[options] + 'm' + str + '\x1B[0m';
  466. }
  467. }
  468. 89 return ret;
  469. }
  470. /**
  471. * Returns a string duplicated n times;
  472. *
  473. * @example
  474. *
  475. * comb.string.multiply("HELLO", 5) => "HELLOHELLOHELLOHELLOHELLO"
  476. *
  477. * @memberOf comb.string
  478. * @static
  479. */
  480. 1function multiply(str, times) {
  481. 156 var ret = [];
  482. 156 if (times) {
  483. 150 for (var i = 0; i < times; i++) {
  484. 568 ret.push(str);
  485. }
  486. }
  487. 156 return ret.join("");
  488. }
  489. /**
  490. * Converts a string to an array
  491. *
  492. * @example
  493. *
  494. * comb.string.toArray("a|b|c|d", "|") => ["a","b","c","d"]
  495. * comb.string.toArray("a", "|") => ["a"]
  496. * comb.string.toArray("", "|") => []
  497. *
  498. * @param {String} str the string to parse
  499. * @param {String} delimeter the delimeter to use
  500. * @memberOf comb.string
  501. * @static
  502. */
  503. 1function toArray(testStr, delim) {
  504. 6 var ret = [];
  505. 6 if (testStr) {
  506. 4 if (testStr.indexOf(delim) > 0) {
  507. 2 ret = testStr.replace(/\s+/g, "").split(delim);
  508. } else {
  509. 2 ret = [testStr];
  510. }
  511. }
  512. 6 return ret;
  513. }
  514. /**@namespace String utilities*/
  515. 1comb.string = {
  516. pad: pad,
  517. truncate: truncate,
  518. format: format,
  519. toArray: toArray,
  520. multiply: multiply,
  521. style: style,
  522. escapeHtml: escapeHtml
  523. };
async.js
Coverage98.88 SLOC1245 LOC179 Missed2
  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. 1var nextTick;
  45. 1if (typeof setImmediate === "function") {
  46. // In IE10, or use https://github.com/NobleJS/setImmediate
  47. 1 nextTick = setImmediate;
  48. } else {
  49. 0 nextTick = function (cb) {
  50. 0 process.nextTick(cb);
  51. };
  52. }
  53. 1function _loopResults(cb, scope, results, index, offset, limit) {
  54. 79 return function () {
  55. 79 return when(results.slice(offset, limit + offset).map(function (r, i) {
  56. 574 var ret = new Promise();
  57. 574 nextTick(function () {
  58. 574 try {
  59. 574 when(cb.apply(scope || results, [r, i + offset, results])).then(function () {
  60. 564 ret.callback.apply(ret, arguments);
  61. }, function () {
  62. 5 ret.errback.apply(ret, arguments);
  63. });
  64. } catch (e) {
  65. 5 ret.errback(e);
  66. }
  67. });
  68. 574 ret.both(function () {
  69. 574 cb = scope = results = index = offset = limit = null;
  70. });
  71. 574 return ret;
  72. }));
  73. };
  74. }
  75. 1function asyncLoop(promise, cb, scope, limit) {
  76. 52 if (isNumber(scope)) {
  77. 13 limit = scope;
  78. 13 scope = null;
  79. }
  80. 52 return when(promise).chain(function (results) {
  81. 52 var loopResults = (isArray(results) ? results : [results]);
  82. 52 limit = limit || loopResults.length;
  83. 52 var list = [];
  84. 52 for (var offset = 0, i = 0, l = loopResults.length; offset < l; offset += limit, i++) {
  85. 79 list.push(_loopResults(cb, scope, loopResults, i, offset, limit));
  86. }
  87. 52 var ret = new Promise();
  88. 52 serial(list).then(function (loopResults) {
  89. 42 ret.callback({loopResults: flatten(loopResults) || [], arr: results});
  90. // loopResults = null;
  91. // results = null;
  92. }, function (error) {
  93. 10 error = compact(error);
  94. 10 ret.errback(error.length === 1 ? error[0] : error);
  95. // loopResults = null;
  96. // results = null;
  97. });
  98. 52 return ret;
  99. });
  100. }
  101. 1function normalizeResult(result) {
  102. 92 return isArray(result) ? result : isDefined(result) ? [result] : result;
  103. }
  104. /**
  105. * Loops through the results of an promise. The promise can return an array or just a single item.
  106. *
  107. * ```
  108. * function asyncArr(){
  109. * var ret = new comb.Promise();
  110. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  111. * return ret.promise;
  112. * }
  113. *
  114. * comb.async.forEach(asyncArr(), function(){
  115. * //do something with it
  116. * }).then(function(arr){
  117. * console.log(arr); //[1,2,3,4,5];
  118. * });
  119. *
  120. * ```
  121. *
  122. * You may also return a promise from the iterator block.
  123. *
  124. * ```
  125. * var myNewArr = [];
  126. *
  127. * comb.async.forEach(asyncArr(), function(item, index){
  128. * var ret = new comb.Promise();
  129. * process.nextTick(function(){
  130. * myNewArr.push([item, index]);
  131. * ret.callback();
  132. * });
  133. * return ret.promise();
  134. * }).then(function(){
  135. * console.log(myNewArr) //[[1,0], [2,1], [3,2], [4,3], [5,4]]
  136. * });
  137. * ```
  138. *
  139. *
  140. * @param {comb.Promise|Array} promise the promise or array to loop through
  141. * @param {Function} iterator a function to invoke for each item
  142. * @param [scope] optional scope to execute the function in.
  143. * @return {comb.Promise} a promise that is resolved with the original array.
  144. * @static
  145. * @memberof comb.async
  146. * @name forEach
  147. */
  148. 1function asyncForEach(promise, iterator, scope, limit) {
  149. 11 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  150. 9 return results.arr;
  151. }));
  152. }
  153. /**
  154. * Loops through the results of an promise resolving with the return value of the iterator function.
  155. * The promise can return an array or just a single item.
  156. *
  157. * ```
  158. * function asyncArr(){
  159. * var ret = new comb.Promise();
  160. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  161. * return ret.promise;
  162. * }
  163. *
  164. * comb.async.map(asyncArr(), function(item){
  165. * return item * 2;
  166. * }).then(function(arr){
  167. * console.log(arr); //[2,4,6,8,10];
  168. * });
  169. *
  170. * ```
  171. *
  172. * You may also return a promise from the iterator block.
  173. *
  174. * ```
  175. * comb.async.map(asyncArr(), function(item, index){
  176. * var ret = new comb.Promise();
  177. * process.nextTick(function(){
  178. * ret.callback(item * 2);
  179. * });
  180. * return ret.promise();
  181. * }).then(function(){
  182. * console.log(myNewArr) //[2,4,6,8,10];
  183. * });
  184. * ```
  185. *
  186. *
  187. * @param {comb.Promise|Array} promise the promise or array to loop through
  188. * @param {Function} iterator a function to invoke for each item
  189. * @param [scope] optional scope to execute the function in.
  190. * @return {comb.Promise} a promise that is resolved with the mapped array.
  191. * @static
  192. * @memberof comb.async
  193. * @name map
  194. */
  195. 1function asyncMap(promise, iterator, scope, limit) {
  196. 15 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  197. 13 return results.loopResults;
  198. }));
  199. }
  200. /**
  201. * Loops through the results of an promise resolving with the filtered array.
  202. * The promise can return an array or just a single item.
  203. *
  204. * ```
  205. * function asyncArr(){
  206. * var ret = new comb.Promise();
  207. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  208. * return ret.promise;
  209. * }
  210. *
  211. * comb.async.filter(asyncArr(), function(item){
  212. * return item % 2;
  213. * }).then(function(arr){
  214. * console.log(arr); //[1,3,5];
  215. * });
  216. *
  217. * ```
  218. *
  219. * You may also return a promise from the iterator block.
  220. *
  221. * ```
  222. * comb.async.filter(asyncArr(), function(item, index){
  223. * var ret = new comb.Promise();
  224. * process.nextTick(function(){
  225. * ret.callback(item % 2);
  226. * });
  227. * return ret.promise();
  228. * }).then(function(){
  229. * console.log(myNewArr) //[1,3,5];
  230. * })
  231. * ```
  232. *
  233. *
  234. * @param {comb.Promise|Array} promise the promise or array to loop through
  235. * @param {Function} iterator a function to invoke for each item
  236. * @param [scope] optional scope to execute the function in.
  237. * @return {comb.Promise} a promise that is resolved with the filtered array.
  238. * @static
  239. * @memberof comb.async
  240. * @name filter
  241. */
  242. 1function asyncFilter(promise, iterator, scope, limit) {
  243. 7 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  244. 5 var loopResults = results.loopResults, resultArr = results.arr;
  245. 5 return (isArray(resultArr) ? resultArr : [resultArr]).filter(function (res, i) {
  246. 65 return loopResults[i];
  247. });
  248. }));
  249. }
  250. /**
  251. * Loops through the results of an promise resolving with true if every item passed, false otherwise.
  252. * The promise can return an array or just a single item.
  253. *
  254. * ```
  255. * function asyncArr(){
  256. * var ret = new comb.Promise();
  257. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  258. * return ret.promise;
  259. * }
  260. *
  261. * comb.async.every(asyncArr(), function(item){
  262. * return item <= 5;
  263. * }).then(function(every){
  264. * console.log(every); //true
  265. * });
  266. *
  267. * ```
  268. *
  269. * You may also return a promise from the iterator block.
  270. *
  271. * ```
  272. * comb.async.every(asyncArr(), function(item, index){
  273. * var ret = new comb.Promise();
  274. * process.nextTick(function(){
  275. * ret.callback(item == 1);
  276. * });
  277. * return ret.promise();
  278. * }).then(function(){
  279. * console.log(myNewArr) //false;
  280. * })
  281. * ```
  282. *
  283. *
  284. * @param {comb.Promise|Array} promise the promise or array to loop through
  285. * @param {Function} iterator a function to invoke for each item
  286. * @param [scope] optional scope to execute the function in.
  287. * @return {comb.Promise} a promise that is resolved true if every item passed false otherwise.
  288. * @static
  289. * @memberof comb.async
  290. * @name every
  291. */
  292. 1function asyncEvery(promise, iterator, scope, limit) {
  293. 9 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  294. 7 return results.loopResults.every(function (res) {
  295. 52 return !!res;
  296. });
  297. }));
  298. }
  299. /**
  300. * Loops through the results of an promise resolving with true if some items passed, false otherwise.
  301. * The promise can return an array or just a single item.
  302. *
  303. * ```
  304. * function asyncArr(){
  305. * var ret = new comb.Promise();
  306. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  307. * return ret.promise;
  308. * }
  309. *
  310. * comb.async.some(asyncArr(), function(item){
  311. * return item == 1;
  312. * }).then(function(every){
  313. * console.log(every); //true
  314. * });
  315. *
  316. * ```
  317. *
  318. * You may also return a promise from the iterator block.
  319. *
  320. * ```
  321. * comb.async.some(asyncArr(), function(item, index){
  322. * var ret = new comb.Promise();
  323. * process.nextTick(function(){
  324. * ret.callback(item > 5);
  325. * });
  326. * return ret.promise();
  327. * }).then(function(){
  328. * console.log(myNewArr) //false;
  329. * })
  330. * ```
  331. *
  332. *
  333. * @param {comb.Promise|Array} promise the promise or array to loop through
  334. * @param {Function} iterator a function to invoke for each item
  335. * @param [scope] optional scope to execute the function in.
  336. * @return {comb.Promise} a promise that is resolved with true if some items passed false otherwise.
  337. * @static
  338. * @memberof comb.async
  339. * @name some
  340. */
  341. 1function asyncSome(promise, iterator, scope, limit) {
  342. 10 return asyncArray(asyncLoop(promise, iterator, scope, limit).chain(function (results) {
  343. 8 return results.loopResults.some(function (res) {
  344. 53 return !!res;
  345. });
  346. }));
  347. }
  348. /**
  349. * Zips results from promises into an array.
  350. *
  351. * ```
  352. * function asyncArr(){
  353. * var ret = new comb.Promise();
  354. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  355. * return ret.promise;
  356. * }
  357. *
  358. * comb.async.zip(asyncArr(), asyncArr()).then(function(zipped){
  359. * console.log(zipped); //[[1,1],[2,2],[3,3], [4,4], [5,5]]
  360. * });
  361. *
  362. * comb.async.array(asyncArr()).zip(asyncArr()).then(function(zipped){
  363. * console.log(zipped); //[[1,1],[2,2],[3,3], [4,4], [5,5]]
  364. * });
  365. *
  366. * ```
  367. *
  368. *
  369. * @return {comb.Promise} an array with all the arrays zipped together.
  370. * @static
  371. * @memberof comb.async
  372. * @name zip
  373. */
  374. 1function asyncZip() {
  375. 1 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  376. 1 return zip.apply(array, normalizeResult(result).map(function (arg) {
  377. 3 return isArray(arg) ? arg : [arg];
  378. }));
  379. }));
  380. }
  381. /**
  382. * Async version of {@link comb.array.avg}.
  383. *
  384. *
  385. * ```
  386. * function asyncArr(){
  387. * var ret = new comb.Promise();
  388. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  389. * return ret.promise;
  390. * }
  391. *
  392. * comb.async.array(asyncArr()).avg().then(function(avg){
  393. * console.log(avg); //3
  394. * })
  395. *
  396. * comb.async.avg(asyncArr()).then(function(avg){
  397. * console.log(avg); //3
  398. * })
  399. *
  400. * ```
  401. *
  402. * @static
  403. * @memberof comb.async
  404. * @name avg
  405. */
  406. 1function asyncAvg(avgPromise) {
  407. 5 return when(avgPromise).chain(function (result) {
  408. 5 return avg.call(array, normalizeResult(result));
  409. });
  410. }
  411. /**
  412. * Async version of {@link comb.array.cartesian}.
  413. *
  414. * ```
  415. * function asyncArr(){
  416. * var ret = new comb.Promise();
  417. * process.nextTick(ret.callback.bind(ret, [1,2]);
  418. * return ret.promise;
  419. * }
  420. *
  421. * comb.async.array(asyncArr()).cartesian([1,2,3]).then(function(avg){
  422. * console.log(avg); //[ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 2 ], [ 2, 3 ] ]
  423. * })
  424. *
  425. * comb.async.cartesian(asyncArr(), [1,2,3]).then(function(avg){
  426. * console.log(avg); //[ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 2 ], [ 2, 3 ] ]
  427. * })
  428. *
  429. * ```
  430. *
  431. * @static
  432. * @memberof comb.async
  433. * @name cartesian
  434. */
  435. 1function asyncCartesian() {
  436. 3 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  437. 3 return cartesian.apply(array, normalizeResult(result).map(function (arg) {
  438. 6 return isArray(arg) ? arg : [arg];
  439. }));
  440. }));
  441. }
  442. /**
  443. * Async version of {@link comb.array.compact}.
  444. *
  445. * ```
  446. * function asyncArr(){
  447. * var ret = new comb.Promise();
  448. * process.nextTick(ret.callback.bind(ret, [1,null,2,null,3,null,4,null,5]);
  449. * return ret.promise;
  450. * }
  451. *
  452. * comb.async.array(asyncArr()).compact().then(function(compacted){
  453. * console.log(compacted); //[1,2,3,4,5]
  454. * })
  455. *
  456. * comb.async.compact(asyncArr()).then(function(compacted){
  457. * console.log(compacted); //[1,2,3,4,5]
  458. * })
  459. *
  460. * ```
  461. *
  462. * @static
  463. * @memberof comb.async
  464. * @name compact
  465. */
  466. 1function asyncCompact(arr) {
  467. 2 return asyncArray(when(arr).chain(function (result) {
  468. 2 return compact.call(array, normalizeResult(result));
  469. }));
  470. }
  471. /**
  472. * Async version of {@link comb.array.difference}.
  473. *
  474. * ```
  475. * function asyncArr(){
  476. * var ret = new comb.Promise();
  477. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  478. * return ret.promise;
  479. * }
  480. *
  481. * comb.async.array(asyncArr()).difference([3,4,5]).then(function(diff){
  482. * console.log(diff); //[1,2]
  483. * })
  484. *
  485. * comb.async.difference(asyncArr(), [3,4,5]).then(function(diff){
  486. * console.log(diff); //[1,2]
  487. * })
  488. *
  489. * ```
  490. *
  491. * @static
  492. * @memberof comb.async
  493. * @name difference
  494. */
  495. 1function asyncDifference() {
  496. 5 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  497. 5 return difference.apply(array, normalizeResult(result).map(function (arg) {
  498. 11 return isArray(arg) ? arg : [arg];
  499. }));
  500. }));
  501. }
  502. /**
  503. * Async version of {@link comb.array.flatten}.
  504. *
  505. * ```
  506. * function asyncArr(){
  507. * var ret = new comb.Promise();
  508. * process.nextTick(ret.callback.bind(ret, [[1],[2],[3],[4],[5]]);
  509. * return ret.promise;
  510. * }
  511. *
  512. * comb.async.array(asyncArr()).flatten().then(function(flat){
  513. * console.log(flat); //[1,2,3,4,5]
  514. * });
  515. *
  516. * comb.async.flatten(asyncArr()).then(function(flat){
  517. * console.log(flat); //[1,2,3,4,5]
  518. * });
  519. *
  520. * ```
  521. *
  522. * @static
  523. * @memberof comb.async
  524. * @name flatten
  525. */
  526. 1function asyncFlatten() {
  527. 4 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  528. 4 return flatten.apply(array, normalizeResult(result).map(function (arg) {
  529. 12 return isArray(arg) ? arg : [arg];
  530. }));
  531. }));
  532. }
  533. /**
  534. * Async version of {@link comb.array.intersect}.
  535. *
  536. * ```
  537. * function asyncArr(){
  538. * var ret = new comb.Promise();
  539. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  540. * return ret.promise;
  541. * }
  542. *
  543. * comb.async.array(asyncArr()).intersect([3,4], [3]).then(function(intersection){
  544. * console.log(intersection); //[3]
  545. * });
  546. *
  547. * comb.async.intersect(asyncArr(), [3,4]).then(function(intersection){
  548. * console.log(intersection); //[3,4]
  549. * });
  550. *
  551. * ```
  552. *
  553. * @static
  554. * @memberof comb.async
  555. * @name intersect
  556. */
  557. 1function asyncIntersect() {
  558. 5 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  559. 5 return intersect.apply(array, normalizeResult(result).map(function (arg) {
  560. 15 return isArray(arg) ? arg : [arg];
  561. }));
  562. }));
  563. }
  564. /**
  565. * Async version of {@link comb.array.max}.
  566. *
  567. * ```
  568. * function asyncArr(){
  569. * var ret = new comb.Promise();
  570. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  571. * return ret.promise;
  572. * }
  573. *
  574. * comb.async.array(asyncArr()).max().then(function(max){
  575. * console.log(max); //5
  576. * })
  577. *
  578. * comb.async.max(asyncArr()).then(function(max){
  579. * console.log(max); //5
  580. * })
  581. *
  582. * ```
  583. *
  584. * @static
  585. * @memberof comb.async
  586. * @name max
  587. */
  588. 1function asyncMax() {
  589. 7 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  590. 7 if (isFunction(last) || isString(last)) {
  591. 3 cmp = last;
  592. } else {
  593. 4 args.push(last);
  594. }
  595. 7 return when.apply(null, args).chain(function (result) {
  596. 7 return max.call(array, normalizeResult(result), cmp);
  597. });
  598. }
  599. /**
  600. * Async version of {@link comb.array.min}.
  601. *
  602. * ```
  603. * function asyncArr(){
  604. * var ret = new comb.Promise();
  605. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  606. * return ret.promise;
  607. * }
  608. *
  609. * comb.async.array(asyncArr()).min().then(function(min){
  610. * console.log(min) //3
  611. * });
  612. *
  613. * comb.async.min(asyncArr()).then(function(min){
  614. * console.log(min) //3
  615. * });
  616. *
  617. * ```
  618. *
  619. * @static
  620. * @memberof comb.async
  621. * @name min
  622. */
  623. 1function asyncMin() {
  624. 7 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  625. 7 if (isFunction(last) || isString(last)) {
  626. 3 cmp = last;
  627. } else {
  628. 4 args.push(last);
  629. }
  630. 7 return when.apply(null, args).chain(function (result) {
  631. 7 return min.call(array, normalizeResult(result), cmp);
  632. });
  633. }
  634. /**
  635. * Async version of {@link comb.array.sort}.
  636. *
  637. * ```
  638. * function asyncArr(){
  639. * var ret = new comb.Promise();
  640. * process.nextTick(ret.callback.bind(ret, [1,3,2,5,4]);
  641. * return ret.promise;
  642. * }
  643. *
  644. * comb.async.array(asyncArr()).sort().then(function(sorted){
  645. * console.log(sorted); //[1,2,3,4,5]
  646. * });
  647. *
  648. * comb.async.sort(asyncArr()).then(function(sorted){
  649. * console.log(sorted); //[1,2,3,4,5]
  650. * });
  651. *
  652. * ```
  653. *
  654. * @static
  655. * @memberof comb.async
  656. * @name sort
  657. */
  658. 1function asyncSort() {
  659. 8 var args = argsToArray(arguments), last = args.pop(), cmp = null;
  660. 8 if (isFunction(last) || isString(last)) {
  661. 4 cmp = last;
  662. } else {
  663. 4 args.push(last);
  664. }
  665. 8 return asyncArray(when.apply(null, args).chain(function (result) {
  666. 8 return sort.call(array, normalizeResult(result), cmp);
  667. }));
  668. }
  669. /**
  670. * Async version of {@link comb.array.multiply}.
  671. *
  672. * ```
  673. * function asyncArr(){
  674. * var ret = new comb.Promise();
  675. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  676. * return ret.promise;
  677. * }
  678. *
  679. * comb.async.array(asyncArr()).multiply(2).then(function(mult){
  680. * console.log(mult); //[1,2,3,4,5,1,2,3,4,5]
  681. * });
  682. *
  683. * comb.async.multiply(asyncArr(),2 ).then(function(mult){
  684. * console.log(mult); //[1,2,3,4,5,1,2,3,4,5]
  685. * });
  686. *
  687. * ```
  688. *
  689. * @static
  690. * @memberof comb.async
  691. * @name multiply
  692. */
  693. 1function asyncMultiply() {
  694. 4 var args = argsToArray(arguments), last = args.pop(), times = null;
  695. 4 if (isNumber(last)) {
  696. 3 times = last;
  697. } else {
  698. 1 args.push(last);
  699. }
  700. 4 return asyncArray(when.apply(null, args).chain(function (result) {
  701. 4 return multiply.call(array, normalizeResult(result), times);
  702. }));
  703. }
  704. /**
  705. * Async version of {@link comb.array.permutations}.
  706. *
  707. * ```
  708. * function asyncArr(){
  709. * var ret = new comb.Promise();
  710. * process.nextTick(ret.callback.bind(ret, [1,2,3]);
  711. * return ret.promise;
  712. * }
  713. *
  714. * comb.async.array(asyncArr()).permutations().then(function(permutations){
  715. * console.log(permutations) //[ [ 1, 2, 3 ],
  716. * // [ 1, 3, 2 ],
  717. * // [ 2, 3, 1 ],
  718. * // [ 2, 1, 3 ],
  719. * // [ 3, 1, 2 ],
  720. * // [ 3, 2, 1 ] ]
  721. * });
  722. *
  723. * comb.async.permutations(asyncArr()).then(function(permutations){
  724. * console.log(permutations) //[ [ 1, 2, 3 ],
  725. * // [ 1, 3, 2 ],
  726. * // [ 2, 3, 1 ],
  727. * // [ 2, 1, 3 ],
  728. * // [ 3, 1, 2 ],
  729. * // [ 3, 2, 1 ] ]
  730. * });
  731. *
  732. * ```
  733. *
  734. * @static
  735. * @memberof comb.async
  736. * @name permutations
  737. */
  738. 1function asyncPermutations() {
  739. 5 var args = argsToArray(arguments), last = args.pop(), times = null;
  740. 5 if (isNumber(last)) {
  741. 4 times = last;
  742. } else {
  743. 1 args.push(last);
  744. }
  745. 5 return asyncArray(when.apply(null, args).chain(function (result) {
  746. 5 return permutations.call(array, normalizeResult(result), times);
  747. }));
  748. }
  749. /**
  750. * Async version of {@link comb.array.powerSet}.
  751. *
  752. * ```
  753. * function asyncArr(){
  754. * var ret = new comb.Promise();
  755. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  756. * return ret.promise;
  757. * }
  758. *
  759. * comb.async.array(asyncArr()).powerSet().then(function(set){
  760. * console.log(set); //[ [], [ 1 ], [ 2 ], [ 1, 2 ], [ 3 ], [ 1, 3 ], [ 2, 3 ], [ 1, 2, 3 ] ]
  761. * });
  762. *
  763. * comb.async.powerSet(asyncArr()).then(function(set){
  764. * console.log(set); //[ [], [ 1 ], [ 2 ], [ 1, 2 ], [ 3 ], [ 1, 3 ], [ 2, 3 ], [ 1, 2, 3 ] ]
  765. * });
  766. *
  767. * ```
  768. *
  769. * @static
  770. * @memberof comb.async
  771. * @name powerSet
  772. */
  773. 1function asyncPowerSet(arr) {
  774. 3 return asyncArray(when(arr).chain(function (result) {
  775. 3 return powerSet.call(array, normalizeResult(result));
  776. }));
  777. }
  778. /**
  779. * Async version of {@link comb.array.removeDuplicates}.
  780. *
  781. * ```
  782. * function asyncArr(){
  783. * var ret = new comb.Promise();
  784. * process.nextTick(ret.callback.bind(ret, [1,2,2,3,3,3,4,4,4]);
  785. * return ret.promise;
  786. * }
  787. *
  788. * comb.async.array(asyncArr()).removeDuplicates().then(function(unique){
  789. * console.log(unique); // [1, 2, 3, 4]
  790. * });
  791. *
  792. * comb.async.removeDuplicates(asyncArray()).then(function(unique){
  793. * console.log(unique); // [1, 2, 3, 4]
  794. * });
  795. * ```
  796. *
  797. * @static
  798. * @memberof comb.async
  799. * @name removeDuplicates
  800. */
  801. 1function asyncRemoveDuplicates(arr) {
  802. 4 return asyncArray(when(arr).chain(function (result) {
  803. 4 return removeDuplicates.call(array, normalizeResult(result));
  804. }));
  805. }
  806. /**
  807. * Async version of {@link comb.array.rotate}.
  808. *
  809. * ```
  810. * function asyncArr(){
  811. * var ret = new comb.Promise();
  812. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  813. * return ret.promise;
  814. * }
  815. *
  816. * comb.async.array(asyncArr()).rotate(2).then(function(rotated){
  817. * console.log(rotated); // [ 3, 4, 5, 1, 2 ]
  818. * });
  819. *
  820. * comb.async.rotate(asyncArr(), 2).then(function(rotated){
  821. * console.log(rotated); // [ 3, 4, 5, 1, 2 ]
  822. * });
  823. *
  824. * ```
  825. *
  826. * @static
  827. * @memberof comb.async
  828. * @name rotate
  829. */
  830. 1function asyncRotate() {
  831. 8 var args = argsToArray(arguments), last = args.pop(), times = null;
  832. 8 if (isNumber(last)) {
  833. 7 times = last;
  834. } else {
  835. 1 args.push(last);
  836. }
  837. 8 return asyncArray(when.apply(null, args).chain(function (result) {
  838. 8 return rotate.call(array, normalizeResult(result), times);
  839. }));
  840. }
  841. /**
  842. * Async version of {@link comb.array.sum}.
  843. *
  844. * ```
  845. * function asyncArr(){
  846. * var ret = new comb.Promise();
  847. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  848. * return ret.promise;
  849. * }
  850. *
  851. * comb.async.array(asyncArr()).sum().then(function(sum){
  852. * console.log(sum) //15
  853. * })
  854. *
  855. * comb.async.sum(asyncArr()).then(function(sum){
  856. * console.log(sum) //15
  857. * })
  858. *
  859. * ```
  860. *
  861. * @static
  862. * @memberof comb.async
  863. * @name sum
  864. */
  865. 1function asyncSum(arr) {
  866. 6 return when(arr).chain(function (result) {
  867. 6 return sum.call(array, normalizeResult(result));
  868. });
  869. }
  870. /**
  871. * Async version of {@link comb.array.transpose}.
  872. *
  873. * ```
  874. * function asyncArr(){
  875. * var ret = new comb.Promise();
  876. * process.nextTick(ret.callback.bind(ret, [[1, 2, 3], [4, 5, 6]]);
  877. * return ret.promise;
  878. * }
  879. *
  880. * comb.async.array(asyncArr()).transpose().then(function(transposed){
  881. * console.log(transposed); //[ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
  882. * });
  883. *
  884. * comb.async.transpose(asyncArr()).then(function(transposed){
  885. * console.log(transposed); //[ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
  886. * });
  887. *
  888. * ```
  889. *
  890. * @static
  891. * @memberof comb.async
  892. * @name transpose
  893. */
  894. 1function asyncTranspose(arr) {
  895. 3 return asyncArray(when(arr).chain(function (result) {
  896. 3 return transpose.call(array, normalizeResult(result));
  897. }));
  898. }
  899. /**
  900. * Async version of {@link comb.array.union}.
  901. *
  902. * ```
  903. * function asyncArr(){
  904. * var ret = new comb.Promise();
  905. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  906. * return ret.promise;
  907. * }
  908. *
  909. * comb.async.array(asyncArr()).union([3],[7], [9,10]).then(function(union){
  910. * console.log(union); //[1,2,3,4,5,7,9,10]
  911. * });
  912. *
  913. * comb.async.union(asyncArr(), [3],[7], [9,10]).then(function(union){
  914. * console.log(union); //[1,2,3,4,5,7,9,10]
  915. * });
  916. *
  917. * ```
  918. *
  919. * @static
  920. * @memberof comb.async
  921. * @name union
  922. */
  923. 1function asyncUnion() {
  924. 2 return asyncArray(when.apply(null, argsToArray(arguments)).chain(function (result) {
  925. 2 return union.apply(array, (normalizeResult(result)).map(function (arg) {
  926. 7 return isArray(arg) ? arg : [arg];
  927. }));
  928. }));
  929. }
  930. /**
  931. * Async version of {@link comb.array.unique}.
  932. *
  933. * ```
  934. * function asyncArr(){
  935. * var ret = new comb.Promise();
  936. * process.nextTick(ret.callback.bind(ret, [1,2,2,3,3,4,4,5]);
  937. * return ret.promise;
  938. * }
  939. *
  940. * comb.async.array(asyncArr()).unique().then(function(unique){
  941. * console.log(unique); //[1,2,3,4,5]
  942. * });
  943. *
  944. * comb.async.unique(asyncArr()).then(function(unique){
  945. * console.log(unique); //[1,2,3,4,5]
  946. * });
  947. *
  948. * ```
  949. *
  950. * @static
  951. * @memberof comb.async
  952. * @name unique
  953. */
  954. 1function asyncUnique() {
  955. 2 return asyncRemoveDuplicates.apply(null, arguments);
  956. }
  957. /**
  958. * Async version of {@link comb.array.valuesAt}.
  959. *
  960. * ```
  961. * function asyncArr(){
  962. * var ret = new comb.Promise();
  963. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  964. * return ret.promise;
  965. * }
  966. *
  967. * comb.async.array(asyncArr()).valuesAt(2,3,4).then(function(values){
  968. * console.log(values); //[3,4,5]
  969. * });
  970. *
  971. * comb.async.valuesAt(asyncArr(), 2,3,4).then(function(values){
  972. * console.log(values); //[3,4,5]
  973. * });
  974. *
  975. * ```
  976. *
  977. * @static
  978. * @memberof comb.async
  979. * @name valuesAt
  980. */
  981. 1function asyncValuesAt(arrPromise) {
  982. 3 var args = argsToArray(arguments, 1);
  983. 3 return asyncArray(when(arrPromise).chain(function (result) {
  984. 3 return when(valuesAt.apply(array, [normalizeResult(result)].concat(args)));
  985. }));
  986. }
  987. /**
  988. * Async version of {@link comb.array.pluck}.
  989. *
  990. * ```
  991. * var when = comb.when,
  992. * array = comb.async.array;
  993. * function asyncArr(){
  994. * var ret = new comb.Promise();
  995. * process.nextTick(ret.callback.bind(ret, [
  996. * {name:{first:when("Fred"), last:"Jones"}, age:when(50), roles:["a", "b", "c"]},
  997. * {name:{first:"Bob", last:"Yukon"}, age:40, roles:when(["b", "c"])},
  998. * {name:{first:"Alice", last:"Palace"}, age:when(35), roles:["c"]},
  999. * {name:{first:when("Johnny"), last:"P."}, age:56, roles:when([])}
  1000. * ]);
  1001. * return ret.promise;
  1002. * }
  1003. *
  1004. * array(asyncArr()).pluck("name.first").then(function(values){
  1005. * console.log(values); //["Fred", "Bob", "Alice", "Johnny"]
  1006. * });
  1007. *
  1008. * pluck(asyncArr(), "age").then(function(values){
  1009. * console.log(values); //[50, 40, 35, 56]
  1010. * });
  1011. *
  1012. * ```
  1013. *
  1014. * @static
  1015. * @memberof comb.async
  1016. * @name pluck
  1017. */
  1018. 1function asyncPluck(arrPromise, property) {
  1019. 5 var args = argsToArray(arguments, 1);
  1020. 5 return asyncArray(when(arrPromise).chain(function (result) {
  1021. 5 var prop = property.split(".");
  1022. 5 result = normalizeResult(result);
  1023. 5 return asyncArray(prop).forEach(function (prop) {
  1024. 8 var exec = prop.match(/(\w+)\(\)$/);
  1025. 8 return asyncArray(result).map(function (item) {
  1026. 32 return exec ? item[exec[1]]() : item[prop];
  1027. }, 1).chain(function (res) {
  1028. 8 result = res;
  1029. });
  1030. }, 1).chain(function () {
  1031. 5 return result;
  1032. });
  1033. }));
  1034. }
  1035. /**
  1036. *
  1037. * Async version of {@link comb.array.invoke}.
  1038. *
  1039. * ```
  1040. * function person(name, age) {
  1041. * return {
  1042. * getName:function () {
  1043. * return when(name);
  1044. * },
  1045. * getOlder:function () {
  1046. * age++;
  1047. * return when(this);
  1048. * },
  1049. * getAge:function () {
  1050. * return when(age);
  1051. * }
  1052. * };
  1053. * }
  1054. *
  1055. * var arr = [when(person("Bob", 40)), when(person("Alice", 35)), when(person("Fred", 50)), when(person("Johnny", 56))];
  1056. * console.log(comb.async.invoke(arr, "getName")); //["Bob", "Alice", "Fred", "Johnny"]
  1057. * console.log(array(arr).invoke("getOlder").pluck("getAge")) //[41, 36, 51, 57]
  1058. * ```
  1059. * @static
  1060. * @memberOf comb.async
  1061. * @name invoke
  1062. */
  1063. 1function asyncInvoke(arrPromise) {
  1064. 2 var args = argsToArray(arguments, 1);
  1065. 2 return asyncArray(when(arrPromise).chain(function (result) {
  1066. 2 return when(invoke.apply(array, [normalizeResult(result)].concat(args)));
  1067. }));
  1068. }
  1069. 1var asyncExports = (exports.async = {
  1070. array: asyncArray,
  1071. forEach: asyncForEach,
  1072. map: asyncMap,
  1073. filter: asyncFilter,
  1074. every: asyncEvery,
  1075. some: asyncSome,
  1076. zip: asyncZip,
  1077. sum: asyncSum,
  1078. avg: asyncAvg,
  1079. sort: asyncSort,
  1080. min: asyncMin,
  1081. max: asyncMax,
  1082. difference: asyncDifference,
  1083. removeDuplicates: asyncRemoveDuplicates,
  1084. unique: asyncUnique,
  1085. rotate: asyncRotate,
  1086. permutations: asyncPermutations,
  1087. transpose: asyncTranspose,
  1088. valuesAt: asyncValuesAt,
  1089. union: asyncUnion,
  1090. intersect: asyncIntersect,
  1091. powerSet: asyncPowerSet,
  1092. cartesian: asyncCartesian,
  1093. compact: asyncCompact,
  1094. multiply: asyncMultiply,
  1095. flatten: asyncFlatten,
  1096. pluck: asyncPluck,
  1097. invoke: asyncInvoke
  1098. });
  1099. 1var methods = ["forEach", "map", "filter", "some", "every", "zip", "sum", "avg", "sort", "min", "max", "difference", "removeDuplicates", "unique", "rotate",
  1100. "permutations", "transpose", "valuesAt", "union", "intersect", "powerSet", "cartesian", "compact",
  1101. "multiply", "flatten", "pluck", "invoke"];
  1102. /**
  1103. * Exposes array methods on a {@link comb.Promise}.
  1104. *
  1105. * The methods added are.
  1106. *
  1107. * * forEach : See {@link comb.async.forEach}.
  1108. * * map : See {@link comb.async.map}.
  1109. * * filter : See {@link comb.async.filter}.
  1110. * * some : See {@link comb.async.some}.
  1111. * * every : See {@link comb.async.every}.
  1112. * * zip : See {@link comb.async.zip}.
  1113. * * sum : See {@link comb.async.sum}.
  1114. * * avg : See {@link comb.async.avg}.
  1115. * * sort : See {@link comb.async.sort}.
  1116. * * min : See {@link comb.async.min}.
  1117. * * max : See {@link comb.async.max}.
  1118. * * difference : See {@link comb.async.difference}.
  1119. * * removeDuplicates : See {@link comb.async.removeDuplicates}.
  1120. * * unique : See {@link comb.async.unique}.
  1121. * * rotate : See {@link comb.async.rotate}.
  1122. * * permutations : See {@link comb.async.permutations}.
  1123. * * transpose : See {@link comb.async.transpose}.
  1124. * * valuesAt : See {@link comb.async.valuesAt}.
  1125. * * union : See {@link comb.async.union}.
  1126. * * intersect : See {@link comb.async.intersect}.
  1127. * * powerSet : See {@link comb.async.powerSet}.
  1128. * * cartesian : See {@link comb.async.cartesian}.
  1129. * * compact : See {@link comb.async.compact}.
  1130. * * multiply : See {@link comb.async.multiply}.
  1131. * * flatten : See {@link comb.async.flatten}.
  1132. * * pluck : See {@link comb.async.pluck}.
  1133. * * invoke : See {@link comb.async.invoke}.
  1134. *
  1135. * When using this method each of the methods are chainable so you can combine actions.
  1136. *
  1137. * ```
  1138. * var array = comb.async.array;
  1139. * function asyncArr(){
  1140. * var ret = new comb.Promise();
  1141. * process.nextTick(ret.callback.bind(ret, [1,2,3,4,5]);
  1142. * return ret.promise;
  1143. * }
  1144. *
  1145. * array(asyncArr())
  1146. * .map(function (num, i) {
  1147. * return num * (i + 1);
  1148. * }).filter(function (num) {
  1149. * return num % 2;
  1150. * }).avg().then(function(avg){
  1151. * console.log(avg); //11.666666666666666
  1152. * });
  1153. * ```
  1154. * @param {comb.Promise|[]} p the promise or array to use.
  1155. *
  1156. * @static
  1157. * @memberof comb.async
  1158. * @name array
  1159. */
  1160. 1function asyncArray(p) {
  1161. 626 var ret;
  1162. 626 if (!p || !p.__isArrayAsync__) {
  1163. 410 ret = merge(when(p), {
  1164. promise: function () {
  1165. 216 return asyncArray(this);
  1166. }
  1167. });
  1168. 410 forEach(methods, function (m) {
  1169. 11070 var func = asyncExports[m];
  1170. 11070 ret[m] = function () {
  1171. 143 var args = argsToArray(arguments), mRet = new Promise();
  1172. 143 nextTick(function () {
  1173. 143 func.apply(null, [ret].concat(args)).then(mRet);
  1174. });
  1175. 143 return asyncArray(mRet);
  1176. };
  1177. });
  1178. 410 ret.__isArrayAsync__ = true;
  1179. } else {
  1180. 216 ret = p;
  1181. }
  1182. 626 p = null;
  1183. 626 return ret;
  1184. }
logging/index.js
Coverage98.91 SLOC738 LOC184 Missed2
  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. 14 this.__root = root;
  20. 14 this.__name = root.name;
  21. 14 this.__level = root.level;
  22. 14 this.__parent = root._parent;
  23. 14 this.__map = {};
  24. },
  25. __getSubLoggers: function () {
  26. 6 var map = this.__map, ret = [], n;
  27. 6 for (var i in map) {
  28. 2 n = map[i];
  29. 2 if (n) {
  30. 2 ret = ret.concat(n.tree.getCurrentLoggers());
  31. }
  32. }
  33. 6 return ret;
  34. },
  35. __getLoggers: function () {
  36. 4 return [this.__root].concat(this.__getSubLoggers())
  37. },
  38. getCurrentLoggers: function () {
  39. 4 return this.__getLoggers();
  40. },
  41. getSubLoggers: function () {
  42. 2 return this.__getSubLoggers();
  43. },
  44. getLogger: function (name) {
  45. 31 var ret;
  46. 31 if (name) {
  47. 29 var parts = name.split(".");
  48. 29 if (parts.length) {
  49. 29 var category = parts.shift();
  50. 29 var lNode = this.__map[category];
  51. 29 if (!lNode) {
  52. 13 lNode = this.__map[category] = new Logger(category, this);
  53. 13 lNode.addAppenders(this.__root.appenders);
  54. }
  55. 29 ret = lNode;
  56. 29 if (parts.length) {
  57. //keep searching
  58. 10 name = parts.join(".");
  59. 10 ret = lNode.tree.getLogger(name);
  60. }
  61. }
  62. } else {
  63. 2 ret = this.__root;
  64. }
  65. 31 return ret;
  66. },
  67. getRootLogger: function () {
  68. 11 return this.__root;
  69. },
  70. isDisabled: function (level) {
  71. },
  72. resetConfiguration: function () {
  73. },
  74. /**
  75. * level = string|Level
  76. */
  77. addAppender: function (appender) {
  78. 63 var map = this.__map;
  79. 63 for (var i in map) {
  80. 41 map[i].addAppender(appender);
  81. }
  82. },
  83. removeAppender: function (name) {
  84. 46 var map = this.__map;
  85. 46 for (var i in map) {
  86. 39 map[i].removeAppender(name);
  87. }
  88. },
  89. setters: {
  90. level: function (level) {
  91. 57 this.__level = level;
  92. 57 if (level && level instanceof Level) {
  93. 57 var map = this.__map;
  94. 57 for (var i in map) {
  95. 2 map[i].level = level;
  96. }
  97. }
  98. }
  99. },
  100. getters: {
  101. categories: function () {
  102. 2 return this.getCurrentLoggers().map(function (l) {
  103. 3 return l.fullName;
  104. });
  105. },
  106. name: function () {
  107. 35 var ret = this.__name;
  108. 35 if (this.__parent) {
  109. 21 var pName = this.__parent.name;
  110. 21 if (pName) {
  111. 8 ret = pName + "." + ret;
  112. }
  113. }
  114. 35 return ret;
  115. },
  116. level: function () {
  117. 13 return this.__level;
  118. },
  119. additive: function () {
  120. 13 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. 14 this.__additive = true;
  217. 14 this.__name = name;
  218. 14 this._parent = parent;
  219. 14 this._tree = new LoggerTree(this);
  220. 14 this.fullName = this._tree.name;
  221. 14 if (!parent || !parent.additive) {
  222. 1 this.level = Level.ALL;
  223. } else {
  224. 13 this.level = parent.level;
  225. }
  226. 14 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. 17 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. 14 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. 14 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. 14 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. 14 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. 14 return this.log.apply(this, [Level.FATAL].concat(argsToArray(arguments)));
  287. },
  288. /**
  289. * Create a timer that can be used to log statements with a duration at the send.
  290. *
  291. * ```
  292. * var timer = LOGGER.timer();
  293. * setTimeout(function(){
  294. * timer.info("HELLO TIMERS!!!"); //HELLO TIMERS!!! [Duration: 5000ms]
  295. * }, 5000);
  296. * ```
  297. *
  298. * @param timerFormat
  299. * @returns {{info: *, debug: *, error: *, warn: *, trace: *, fatal: *, end: Function}}
  300. */
  301. timer: function (timerFormat) {
  302. 6 timerFormat = timerFormat || " [Duration: %dms]";
  303. 6 function timerLog(level, start) {
  304. 42 return function (message) {
  305. 24 var args = argsToArray(arguments, 1);
  306. 24 message += timerFormat;
  307. 24 args.push(new Date() - start);
  308. 24 self.log.apply(self, [level, message].concat(args));
  309. 24 return ret;
  310. };
  311. }
  312. 6 var start = new Date(),
  313. self = this,
  314. ret = {
  315. info: timerLog(Level.INFO, start),
  316. debug: timerLog(Level.DEBUG, start),
  317. error: timerLog(Level.ERROR, start),
  318. warn: timerLog(Level.WARN, start),
  319. trace: timerLog(Level.TRACE, start),
  320. fatal: timerLog(Level.FATAL, start),
  321. log: function (level) {
  322. 6 return timerLog(level, start).apply(this, argsToArray(arguments, 1));
  323. },
  324. end: function () {
  325. 0 start = self = null;
  326. }
  327. };
  328. 6 return ret;
  329. },
  330. /**
  331. * Creates a log event to be passed to appenders
  332. *
  333. * @param {comb.logging.Level} level the level of the logging event
  334. * @param {String} message the message to be logged
  335. * @return {Object} the logging event
  336. */
  337. getLogEvent: function getLogEvent(level, message) {
  338. 82 return {
  339. hostname: os.hostname(),
  340. pid: process.pid,
  341. gid: hasGetGid ? process.getgid() : null,
  342. processTitle: process.title,
  343. level: level,
  344. levelName: level.name,
  345. message: message,
  346. timeStamp: new Date(),
  347. name: this.fullName
  348. };
  349. },
  350. /**
  351. * Log a message
  352. *
  353. * @param {comb.logging.Level} level the level the message is
  354. * @param {String} message the message to log.
  355. *
  356. * @return {comb.logging.Logger} for chaining.
  357. */
  358. log: function (level, message) {
  359. 117 level = Level.toLevel(level);
  360. 117 if (this.hasLevelGt(level)) {
  361. 81 var args = argsToArray(arguments, 1);
  362. 81 if (args.length > 1) {
  363. 30 message = format.apply(null, args);
  364. }
  365. 81 if (Level.TRACE.equals(level)) {
  366. 13 var err = new Error;
  367. 13 err.name = "Trace";
  368. 13 err.message = message || '';
  369. 13 Error.captureStackTrace(err, arguments.callee);
  370. 13 message = err.stack;
  371. 68 } else if (Level.ERROR.equals(level) && isInstanceOf(message, Error)) {
  372. 0 message = message.stack;
  373. }
  374. 81 var type = level.name.toLowerCase(), appenders = this.__appenders;
  375. 81 var event = this.getLogEvent(level, message);
  376. 81 Object.keys(appenders).forEach(function (i) {
  377. 81 appenders[i].append(event);
  378. });
  379. }
  380. 117 return this;
  381. },
  382. /**
  383. * Add an appender to this logger. If this is additive then the appender is added to all subloggers.
  384. *
  385. * @example
  386. * comb.logger("my.logger")
  387. * .addAppender("ConsoleAppender")
  388. * .addAppender("FileAppender", {file:'/var/log/my.log'})
  389. * .addAppender("RollingFileAppender", {file:'/var/log/myRolling.log'})
  390. * .addAppender("JSONAppender", {file:'/var/log/myJson.log'});
  391. *
  392. * @param {comb.logging.Appender|String} If the appender is an {@link comb.logging.appenders.Appender} then it is added.
  393. * If the appender is a string then {@link comb.logging.appenders.Appender.createAppender} will be called to create it.
  394. *
  395. * @param {Object} [opts = null] If the first argument is a string then the opts will be used as constructor arguments for
  396. * creating the appender.
  397. *
  398. * @return {comb.logging.Logger} for chaining.
  399. */
  400. addAppender: function (appender, opts) {
  401. 82 var args = argsToArray(arguments);
  402. 82 if (isString(appender)) {
  403. 12 this.addAppender(Appender.createAppender(appender, opts));
  404. } else {
  405. 70 if (!isUndefinedOrNull(appender)) {
  406. 70 var name = appender.name;
  407. 70 if (!(name in this.__appenders)) {
  408. 63 this.__appenders[name] = appender;
  409. 63 if (!appender.level) {
  410. 15 appender.level = this.level;
  411. }
  412. 63 this._tree.addAppender(appender);
  413. }
  414. }
  415. }
  416. 82 return this;
  417. },
  418. /**
  419. * Short cut to add a list of appenders to this Logger
  420. * @param {Array<comb.logging.Appender>} appenders
  421. *
  422. * @return {comb.logging.Logger} for chaining.
  423. */
  424. addAppenders: function (appenders) {
  425. 14 appenders.forEach(this.addAppender.bind(this));
  426. 14 return this;
  427. },
  428. /**
  429. * Removes and appender from this logger.
  430. * @param {String} name the name of the appender
  431. * @return {comb.logging.Logger} for chaining.
  432. */
  433. removeAppender: function (name) {
  434. 46 if (name in this.__appenders) {
  435. 46 delete this.__appenders[name];
  436. 46 this._tree.removeAppender(name);
  437. }
  438. 46 return this;
  439. },
  440. /**
  441. * Removes a list of appenders from this logger.
  442. *
  443. * @param {String[]} appenders a list of names of appenders to remove
  444. * @return {comb.logging.Logger} for chaining.
  445. */
  446. removeAppenders: function (appenders) {
  447. 1 appenders.forEach(this.removeAppender, this);
  448. 1 return this;
  449. },
  450. /**
  451. * Removes all appenders from this logger and sub loggers if this Logger is additive.
  452. *
  453. * @return {comb.logging.Logger} for chaining.
  454. */
  455. removeAllAppenders: function () {
  456. 13 Object.keys(this.__appenders).forEach(this.removeAppender.bind(this));
  457. 13 return this;
  458. },
  459. /**
  460. * Determines if an appender is attached.
  461. *
  462. * @param {String} name the name of the appender.
  463. */
  464. isAppenderAttached: function (name) {
  465. 38 return (name in this.__appenders);
  466. },
  467. /**
  468. * Gets an appender from this logger
  469. *
  470. * @param {String} name the name of the appender.
  471. *
  472. * @return {comb.logging.Appender|undefined} returns the appender with the specified name or
  473. * undefined if it is not found.
  474. */
  475. getAppender: function (name) {
  476. 8 var ret;
  477. 8 if (name in this.__appenders) {
  478. 8 ret = this.__appenders[name];
  479. }
  480. 8 return ret;
  481. },
  482. hasLevelGt: function (level) {
  483. 117 var ret = level.isGreaterOrEqualToo(this.level), i, appenders, keys, l;
  484. 117 if (!ret) {
  485. 36 appenders = this.__appenders;
  486. 36 keys = Object.keys(appenders);
  487. 36 l = keys.length;
  488. 36 i = -1;
  489. 36 while (++i < l && !ret) {
  490. 36 ret = level.isGreaterOrEqualToo(appenders[keys[i]].level);
  491. }
  492. }
  493. 117 return ret;
  494. },
  495. /**
  496. * @ignore
  497. * */
  498. setters: {
  499. level: function (level) {
  500. 58 level = Level.toLevel(level);
  501. 58 if (this.__additive) {
  502. 57 this.__level = level;
  503. 57 var appenders = this.__appenders;
  504. 57 for (var i in appenders) {
  505. 47 appenders[i].level = level;
  506. }
  507. 57 this._tree.level = level;
  508. } else {
  509. 1 this.__level = level;
  510. }
  511. },
  512. additive: function (additive) {
  513. 2 this.__additive = additive;
  514. }
  515. },
  516. /**@ignore*/
  517. getters: {
  518. /**@ignore*/
  519. /**@ignore*/
  520. subLoggers: function () {
  521. 2 return this._tree.getSubLoggers();
  522. },
  523. /**@ignore*/
  524. level: function () {
  525. 223 return this.__level;
  526. },
  527. /**@ignore*/
  528. additive: function () {
  529. 15 return this.__additive;
  530. },
  531. isAll: function () {
  532. 8 return Level.ALL.isGreaterOrEqualToo(this.level);
  533. },
  534. /**@ignore*/
  535. isDebug: function () {
  536. 8 return Level.DEBUG.isGreaterOrEqualToo(this.level);
  537. },
  538. /**@ignore*/
  539. isTrace: function () {
  540. 8 return Level.TRACE.isGreaterOrEqualToo(this.level);
  541. },
  542. /**@ignore*/
  543. isInfo: function () {
  544. 8 return Level.INFO.isGreaterOrEqualToo(this.level);
  545. },
  546. /**@ignore*/
  547. isWarn: function () {
  548. 8 return Level.WARN.isGreaterOrEqualToo(this.level);
  549. },
  550. /**@ignore*/
  551. isError: function () {
  552. 8 return Level.ERROR.isGreaterOrEqualToo(this.level);
  553. },
  554. /**@ignore*/
  555. isFatal: function () {
  556. 8 return Level.FATAL.isGreaterOrEqualToo(this.level);
  557. },
  558. /**@ignore*/
  559. isOff: function () {
  560. 9 return Level.OFF.equals(this.level);
  561. },
  562. /**@ignore*/
  563. name: function () {
  564. 14 return this.__name;
  565. },
  566. /**@ignore*/
  567. tree: function () {
  568. 13 return this._tree;
  569. },
  570. /**@ignore*/
  571. appenders: function () {
  572. 18 var ret = [];
  573. 18 for (var i in this.__appenders) {
  574. 8 ret.push(this.__appenders[i]);
  575. }
  576. 18 return ret;
  577. },
  578. categories: function () {
  579. 2 return this._tree.categories;
  580. }
  581. }
  582. },
  583. static: {
  584. /**@lends comb.logging.Logger*/
  585. /**
  586. * Return the root of all loggers
  587. */
  588. getRootLogger: function () {
  589. 11 return rootTree.getRootLogger();
  590. },
  591. /**
  592. * Retrieves/Creates a logger based on the name passed in
  593. *
  594. * @param {String} name the name of the logger
  595. */
  596. getLogger: function (name) {
  597. 21 return rootTree.getLogger(name);
  598. }
  599. }
  600. }));
  601. /**
  602. *
  603. * @function
  604. * @description Alias to {@link comb.logging.Logger.getLogger}. See {@link comb.logging.Logger} for more information.
  605. *
  606. * @example
  607. *
  608. * comb.logger("my.logger");
  609. *
  610. * @memberOf comb
  611. * @namespace
  612. */
  613. 1exports.logger = Logger.getLogger.bind(Logger);
  614. /**
  615. * @function
  616. * @description Shortcut to configure loggers.
  617. *
  618. * @example
  619. *
  620. * //same as new comb.logging.BasicConfigurator().configure();
  621. * comb.logger.configure();
  622. *
  623. * //new comb.logging.PropertyConfigurator().configure("/location/to/logger/configuration.json");
  624. * comb.logger.configure("/location/to/logger/configuration.json");
  625. *
  626. * @memberOf comb.logger
  627. */
  628. 1exports.logger.configure = function configure() {
  629. 4 var args = argsToArray(arguments), configurator;
  630. 4 if (!args.length) {
  631. 1 configurator = new configurators.BasicConfigurator();
  632. } else {
  633. 3 var first = args[0];
  634. 3 if (isHash(first) || isString(first)) {
  635. 2 configurator = new configurators.PropertyConfigurator();
  636. } else {
  637. 1 configurator = new configurators.BasicConfigurator();
  638. }
  639. }
  640. 4 configurator.configure.apply(configurator, args);
  641. 4 return this;
  642. };
  643. /**
  644. * @function
  645. * @description Factory method for creating appenders. See {@link comb.logging.appenders.Appender.createAppender} for
  646. * arguments.
  647. *
  648. * @example
  649. *
  650. * var appender = comb.logger.appender("ConsoleAppender");
  651. * comb.logger("my.logger").addAppender(appender);
  652. *
  653. * @memberOf comb.logger
  654. *
  655. */
  656. 1exports.logger.appender = Appender.createAppender.bind(Appender);
  657. 1var rootLogger = new Logger("");
  658. 1rootTree = rootLogger._tree;
  659. /**
  660. * The root for all loggers.
  661. * @memberOf comb.logger
  662. * @type comb.logging.Logger
  663. */
  664. 1exports.logger.rootLogger = rootLogger;
base/date/index.js
Coverage99.33 SLOC910 LOC300 Missed2
  1. 1var string,
  2. transforms = require("./transforms.js"),
  3. addTransform = transforms.addTransform,
  4. differenceTransform = transforms.differenceTransform;
  5. 1function getString() {
  6. 298 return string || (string = require("../string").string);
  7. }
  8. /**
  9. * @ignore
  10. * Based on DOJO Date Implementation
  11. *
  12. * Dojo is available under *either* the terms of the modified BSD license *or* the
  13. * Academic Free License version 2.1. As a recipient of Dojo, you may choose which
  14. * license to receive this code under (except as noted in per-module LICENSE
  15. * files). Some modules may not be the copyright of the Dojo Foundation. These
  16. * modules contain explicit declarations of copyright in both the LICENSE files in
  17. * the directories in which they reside and in the code itself. No external
  18. * contributions are allowed under licenses which are fundamentally incompatible
  19. * with the AFL or BSD licenses that Dojo is distributed under.
  20. *
  21. */
  22. 1var floor = Math.floor, round = Math.round, min = Math.min, pow = Math.pow, ceil = Math.ceil, abs = Math.abs;
  23. 1var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
  24. 1var monthAbbr = ["Jan.", "Feb.", "Mar.", "Apr.", "May.", "Jun.", "Jul.", "Aug.", "Sep.", "Oct.", "Nov.", "Dec."];
  25. 1var monthLetter = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
  26. 1var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  27. 1var dayAbbr = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  28. 1var dayLetter = ["S", "M", "T", "W", "T", "F", "S"];
  29. 1var eraNames = ["Before Christ", "Anno Domini"];
  30. 1var eraAbbr = ["BC", "AD"];
  31. 1var eraLetter = ["B", "A"];
  32. 1var comb = exports, date;
  33. /**
  34. * Determines if obj is a Date
  35. *
  36. * @param {Anything} obj the thing to test if it is a Date
  37. * @memberOf comb
  38. * @returns {Boolean} true if it is a Date false otherwise
  39. */
  40. 1comb.isDate = function (obj) {
  41. 1252 var undef;
  42. 1252 return (obj !== undef && typeof obj === "object" && obj instanceof Date);
  43. };
  44. 1function getDayOfYear(/*Date*/dateObject, utc) {
  45. // summary: gets the day of the year as represented by dateObject
  46. 6 return date.difference(new Date(dateObject.getFullYear(), 0, 1, dateObject.getHours()), dateObject, null, utc) + 1; // Number
  47. }
  48. 1function getWeekOfYear(/*Date*/dateObject, /*Number*/firstDayOfWeek, utc) {
  49. 4 firstDayOfWeek = firstDayOfWeek || 0;
  50. 4 var fullYear = dateObject[utc ? "getUTCFullYear" : "getFullYear"]();
  51. 4 var firstDayOfYear = new Date(fullYear, 0, 1).getDay(),
  52. adj = (firstDayOfYear - firstDayOfWeek + 7) % 7,
  53. week = floor((getDayOfYear(dateObject) + adj - 1) / 7);
  54. // if year starts on the specified day, start counting weeks at 1
  55. 4 if (firstDayOfYear === firstDayOfWeek) {
  56. 4 week++;
  57. }
  58. 4 return week; // Number
  59. }
  60. 1function getTimezoneName(/*Date*/dateObject) {
  61. 37 var str = dateObject.toString();
  62. 37 var tz = '';
  63. 37 var pos = str.indexOf('(');
  64. 37 if (pos > -1) {
  65. 33 tz = str.substring(++pos, str.indexOf(')'));
  66. }
  67. 37 return tz; // String
  68. }
  69. 1function buildDateEXP(pattern, tokens) {
  70. 55 return pattern.replace(/([a-z])\1*/ig, function (match) {
  71. // Build a simple regexp. Avoid captures, which would ruin the tokens list
  72. 197 var s,
  73. c = match.charAt(0),
  74. l = match.length,
  75. p2 = '', p3 = '';
  76. 197 p2 = '0?';
  77. 197 p3 = '0{0,2}';
  78. 197 if (c === 'y') {
  79. 33 s = '\\d{2,4}';
  80. 164 } else if (c === "M") {
  81. 33 s = (l > 2) ? '\\S+?' : '1[0-2]|' + p2 + '[1-9]';
  82. 131 } else if (c === "D") {
  83. 4 s = '[12][0-9][0-9]|3[0-5][0-9]|36[0-6]|' + p3 + '[1-9][0-9]|' + p2 + '[1-9]';
  84. 127 } else if (c === "d") {
  85. 31 s = '3[01]|[12]\\d|' + p2 + '[1-9]';
  86. 96 } else if (c === "w") {
  87. 4 s = '[1-4][0-9]|5[0-3]|' + p2 + '[1-9]';
  88. 92 } else if (c === "E") {
  89. 12 s = '\\S+';
  90. 80 } else if (c === "h") {
  91. 10 s = '1[0-2]|' + p2 + '[1-9]';
  92. 70 } else if (c === "K") {
  93. 4 s = '1[01]|' + p2 + '\\d';
  94. 66 } else if (c === "H") {
  95. 2 s = '1\\d|2[0-3]|' + p2 + '\\d';
  96. 64 } else if (c === "k") {
  97. 2 s = '1\\d|2[0-4]|' + p2 + '[1-9]';
  98. 62 } else if (c === "m" || c === "s") {
  99. 30 s = '[0-5]\\d';
  100. 32 } else if (c === "S") {
  101. 10 s = '\\d{' + l + '}';
  102. 22 } else if (c === "a") {
  103. 6 var am = 'AM', pm = 'PM';
  104. 6 s = am + '|' + pm;
  105. 6 if (am !== am.toLowerCase()) {
  106. 6 s += '|' + am.toLowerCase();
  107. }
  108. 6 if (pm !== pm.toLowerCase()) {
  109. 6 s += '|' + pm.toLowerCase();
  110. }
  111. 6 s = s.replace(/\./g, "\\.");
  112. 16 } else if (c === 'v' || c === 'z' || c === 'Z' || c === 'G' || c === 'q' || c === 'Q') {
  113. 12 s = ".*";
  114. } else {
  115. 4 s = c === " " ? "\\s*" : c + "*";
  116. }
  117. 197 if (tokens) {
  118. 197 tokens.push(match);
  119. }
  120. 197 return "(" + s + ")"; // add capture
  121. }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE.
  122. }
  123. /**
  124. * @namespace Utilities for Dates
  125. */
  126. 1comb.date = {
  127. /**@lends comb.date*/
  128. /**
  129. * Returns the number of days in the month of a date
  130. *
  131. * @example
  132. *
  133. * comb.date.getDaysInMonth(new Date(2006, 1, 1)); //28
  134. * comb.date.getDaysInMonth(new Date(2004, 1, 1)); //29
  135. * comb.date.getDaysInMonth(new Date(2006, 2, 1)); //31
  136. * comb.date.getDaysInMonth(new Date(2006, 3, 1)); //30
  137. * comb.date.getDaysInMonth(new Date(2006, 4, 1)); //31
  138. * comb.date.getDaysInMonth(new Date(2006, 5, 1)); //30
  139. * comb.date.getDaysInMonth(new Date(2006, 6, 1)); //31
  140. * @param {Date} dateObject the date containing the month
  141. * @return {Number} the number of days in the month
  142. */
  143. getDaysInMonth: function (/*Date*/dateObject) {
  144. // summary:
  145. // Returns the number of days in the month used by dateObject
  146. 36 var month = dateObject.getMonth();
  147. 36 var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  148. 36 if (month === 1 && date.isLeapYear(dateObject)) {
  149. 6 return 29;
  150. } // Number
  151. 30 return days[month]; // Number
  152. },
  153. /**
  154. * Determines if a date is a leap year
  155. *
  156. * @example
  157. *
  158. * comb.date.isLeapYear(new Date(1600, 0, 1)); //true
  159. * comb.date.isLeapYear(new Date(2004, 0, 1)); //true
  160. * comb.date.isLeapYear(new Date(2000, 0, 1)); //true
  161. * comb.date.isLeapYear(new Date(2006, 0, 1)); //false
  162. * comb.date.isLeapYear(new Date(1900, 0, 1)); //false
  163. * comb.date.isLeapYear(new Date(1800, 0, 1)); //false
  164. * comb.date.isLeapYear(new Date(1700, 0, 1)); //false
  165. *
  166. * @param {Date} dateObject
  167. * @returns {Boolean} true if it is a leap year false otherwise
  168. */
  169. isLeapYear: function (/*Date*/dateObject, utc) {
  170. 28 var year = dateObject[utc ? "getUTCFullYear" : "getFullYear"]();
  171. 28 return (year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0);
  172. },
  173. /**
  174. * Determines if a date is on a weekend
  175. *
  176. * @example
  177. *
  178. * var thursday = new Date(2006, 8, 21);
  179. * var saturday = new Date(2006, 8, 23);
  180. * var sunday = new Date(2006, 8, 24);
  181. * var monday = new Date(2006, 8, 25);
  182. * comb.date.isWeekend(thursday)); //false
  183. * comb.date.isWeekend(saturday); //true
  184. * comb.date.isWeekend(sunday); //true
  185. * comb.date.isWeekend(monday)); //false
  186. *
  187. * @param {Date} dateObject the date to test
  188. *
  189. * @returns {Boolean} true if the date is a weekend
  190. */
  191. isWeekend: function (/*Date?*/dateObject, utc) {
  192. // summary:
  193. // Determines if the date falls on a weekend, according to local custom.
  194. 4 var day = (dateObject || new Date())[utc ? "getUTCDay" : "getDay"]();
  195. 4 return day === 0 || day === 6;
  196. },
  197. /**
  198. * Get the timezone of a date
  199. *
  200. * @example
  201. * //just setting the strLocal to simulate the toString() of a date
  202. * dt.str = 'Sun Sep 17 2006 22:25:51 GMT-0500 (CDT)';
  203. * //just setting the strLocal to simulate the locale
  204. * dt.strLocale = 'Sun 17 Sep 2006 10:25:51 PM CDT';
  205. * comb.date.getTimezoneName(dt); //'CDT'
  206. * dt.str = 'Sun Sep 17 2006 22:57:18 GMT-0500 (CDT)';
  207. * dt.strLocale = 'Sun Sep 17 22:57:18 2006';
  208. * comb.date.getTimezoneName(dt); //'CDT'
  209. * @param dateObject the date to get the timezone from
  210. *
  211. * @returns {String} the timezone of the date
  212. */
  213. getTimezoneName: getTimezoneName,
  214. /**
  215. * Compares two dates
  216. *
  217. * @example
  218. *
  219. * var d1 = new Date();
  220. * d1.setHours(0);
  221. * comb.date.compare(d1, d1); // 0
  222. *
  223. * var d1 = new Date();
  224. * d1.setHours(0);
  225. * var d2 = new Date();
  226. * d2.setFullYear(2005);
  227. * d2.setHours(12);
  228. * comb.date.compare(d1, d2, "date"); // 1
  229. * comb.date.compare(d1, d2, "datetime"); // 1
  230. *
  231. * var d1 = new Date();
  232. * d1.setHours(0);
  233. * var d2 = new Date();
  234. * d2.setFullYear(2005);
  235. * d2.setHours(12);
  236. * comb.date.compare(d2, d1, "date"); // -1
  237. * comb.date.compare(d1, d2, "time"); //-1
  238. *
  239. * @param {Date|String} date1 the date to comapare
  240. * @param {Date|String} [date2=new Date()] the date to compare date1 againse
  241. * @param {"date"|"time"|"datetime"} portion compares the portion specified
  242. *
  243. * @returns -1 if date1 is < date2 0 if date1 === date2 1 if date1 > date2
  244. */
  245. compare: function (/*Date*/date1, /*Date*/date2, /*String*/portion) {
  246. 10 date1 = new Date(date1);
  247. 10 date2 = new Date((date2 || new Date()));
  248. 10 if (portion === "date") {
  249. // Ignore times and compare dates.
  250. 4 date1.setHours(0, 0, 0, 0);
  251. 4 date2.setHours(0, 0, 0, 0);
  252. 6 } else if (portion === "time") {
  253. // Ignore dates and compare times.
  254. 2 date1.setFullYear(0, 0, 0);
  255. 2 date2.setFullYear(0, 0, 0);
  256. }
  257. 10 return date1 > date2 ? 1 : date1 < date2 ? -1 : 0;
  258. },
  259. /**
  260. * Adds a specified interval and amount to a date
  261. *
  262. * @example
  263. * var dtA = new Date(2005, 11, 27);
  264. * comb.date.add(dtA, "year", 1); //new Date(2006, 11, 27);
  265. * comb.date.add(dtA, "years", 1); //new Date(2006, 11, 27);
  266. *
  267. * dtA = new Date(2000, 0, 1);
  268. * comb.date.add(dtA, "quarter", 1); //new Date(2000, 3, 1);
  269. * comb.date.add(dtA, "quarters", 1); //new Date(2000, 3, 1);
  270. *
  271. * dtA = new Date(2000, 0, 1);
  272. * comb.date.add(dtA, "month", 1); //new Date(2000, 1, 1);
  273. * comb.date.add(dtA, "months", 1); //new Date(2000, 1, 1);
  274. *
  275. * dtA = new Date(2000, 0, 31);
  276. * comb.date.add(dtA, "month", 1); //new Date(2000, 1, 29);
  277. * comb.date.add(dtA, "months", 1); //new Date(2000, 1, 29);
  278. *
  279. * dtA = new Date(2000, 0, 1);
  280. * comb.date.add(dtA, "week", 1); //new Date(2000, 0, 8);
  281. * comb.date.add(dtA, "weeks", 1); //new Date(2000, 0, 8);
  282. *
  283. * dtA = new Date(2000, 0, 1);
  284. * comb.date.add(dtA, "day", 1); //new Date(2000, 0, 2);
  285. *
  286. * dtA = new Date(2000, 0, 1);
  287. * comb.date.add(dtA, "weekday", 1); //new Date(2000, 0, 3);
  288. *
  289. * dtA = new Date(2000, 0, 1, 11);
  290. * comb.date.add(dtA, "hour", 1); //new Date(2000, 0, 1, 12);
  291. *
  292. * dtA = new Date(2000, 11, 31, 23, 59);
  293. * comb.date.add(dtA, "minute", 1); //new Date(2001, 0, 1, 0, 0);
  294. *
  295. * dtA = new Date(2000, 11, 31, 23, 59, 59);
  296. * comb.date.add(dtA, "second", 1); //new Date(2001, 0, 1, 0, 0, 0);
  297. *
  298. * dtA = new Date(2000, 11, 31, 23, 59, 59, 999);
  299. * comb.date.add(dtA, "millisecond", 1); //new Date(2001, 0, 1, 0, 0, 0, 0);
  300. *
  301. * @param {Date} date
  302. * @param {String} interval the interval to add
  303. * <ul>
  304. * <li>day | days</li>
  305. * <li>weekday | weekdays</li>
  306. * <li>year | years</li>
  307. * <li>week | weeks</li>
  308. * <li>quarter | quarters</li>
  309. * <li>months | months</li>
  310. * <li>hour | hours</li>
  311. * <li>minute | minutes</li>
  312. * <li>second | seconds</li>
  313. * <li>millisecond | milliseconds</li>
  314. * </ul>
  315. * @param {Number} [amount=0] the amount to add
  316. */
  317. add: function (/*Date*/date, /*String*/interval, /*int*/amount) {
  318. 234 var res = addTransform(interval, date, amount || 0);
  319. 234 amount = res[0];
  320. 234 var property = res[1];
  321. 234 var sum = new Date(date);
  322. 234 var fixOvershoot = res[2];
  323. 234 if (property) {
  324. 234 sum["set" + property](sum["get" + property]() + amount);
  325. }
  326. 234 if (fixOvershoot && (sum.getDate() < date.getDate())) {
  327. 26 sum.setDate(0);
  328. }
  329. 234 return sum; // Date
  330. },
  331. /**
  332. * Finds the difference between two dates based on the specified interval
  333. *
  334. * @example
  335. *
  336. * var dtA, dtB;
  337. *
  338. * dtA = new Date(2005, 11, 27);
  339. * dtB = new Date(2006, 11, 27);
  340. * comb.date.difference(dtA, dtB, "year"); //1
  341. *
  342. * dtA = new Date(2000, 1, 29);
  343. * dtB = new Date(2001, 2, 1);
  344. * comb.date.difference(dtA, dtB, "quarter"); //4
  345. * comb.date.difference(dtA, dtB, "month"); //13
  346. *
  347. * dtA = new Date(2000, 1, 1);
  348. * dtB = new Date(2000, 1, 8);
  349. * comb.date.difference(dtA, dtB, "week"); //1
  350. *
  351. * dtA = new Date(2000, 1, 29);
  352. * dtB = new Date(2000, 2, 1);
  353. * comb.date.difference(dtA, dtB, "day"); //1
  354. *
  355. * dtA = new Date(2006, 7, 3);
  356. * dtB = new Date(2006, 7, 11);
  357. * comb.date.difference(dtA, dtB, "weekday"); //6
  358. *
  359. * dtA = new Date(2000, 11, 31, 23);
  360. * dtB = new Date(2001, 0, 1, 0);
  361. * comb.date.difference(dtA, dtB, "hour"); //1
  362. *
  363. * dtA = new Date(2000, 11, 31, 23, 59);
  364. * dtB = new Date(2001, 0, 1, 0, 0);
  365. * comb.date.difference(dtA, dtB, "minute"); //1
  366. *
  367. * dtA = new Date(2000, 11, 31, 23, 59, 59);
  368. * dtB = new Date(2001, 0, 1, 0, 0, 0);
  369. * comb.date.difference(dtA, dtB, "second"); //1
  370. *
  371. * dtA = new Date(2000, 11, 31, 23, 59, 59, 999);
  372. * dtB = new Date(2001, 0, 1, 0, 0, 0, 0);
  373. * comb.date.difference(dtA, dtB, "millisecond"); //1
  374. *
  375. *
  376. * @param {Date} date1
  377. * @param {Date} [date2 = new Date()]
  378. * @param {String} [interval = "day"] the intercal to find the difference of.
  379. * <ul>
  380. * <li>day | days</li>
  381. * <li>weekday | weekdays</li>
  382. * <li>year | years</li>
  383. * <li>week | weeks</li>
  384. * <li>quarter | quarters</li>
  385. * <li>months | months</li>
  386. * <li>hour | hours</li>
  387. * <li>minute | minutes</li>
  388. * <li>second | seconds</li>
  389. * <li>millisecond | milliseconds</li>
  390. * </ul>
  391. */
  392. difference: function (/*Date*/date1, /*Date?*/date2, /*String*/interval, utc) {
  393. 162 date2 = date2 || new Date();
  394. 162 interval = interval || "day";
  395. 162 return differenceTransform(interval, date1, date2, utc);
  396. },
  397. /**
  398. * Parses a date string into a date object
  399. *
  400. * @example
  401. * var aug_11_2006 = new Date(2006, 7, 11, 0);
  402. * comb.date.parse("08/11/06", "MM/dd/yy"); //aug_11_2006
  403. * comb.date.parse("11Aug2006", 'ddMMMyyyy'); //aug_11_2006
  404. * comb.date.parse("Aug2006", 'MMMyyyy'); //new Date(2006, 7, 1)
  405. * comb.date.parse("Aug 11, 2006", "MMM dd, yyyy"); //aug_11_2006
  406. * comb.date.parse("August 11, 2006", "MMMM dd, yyyy"); //aug_11_2006
  407. * comb.date.parse("Friday, August 11, 2006", "EEEE, MMMM dd, yyyy"); //aug_11_2006
  408. *
  409. * @param {String} dateStr The string to parse
  410. * @param {String} format the format of the date composed of the following options
  411. * <ul>
  412. * <li> G Era designator Text AD</li>
  413. * <li> y Year Year 1996; 96</li>
  414. * <li> M Month in year Month July; Jul; 07</li>
  415. * <li> w Week in year Number 27</li>
  416. * <li> W Week in month Number 2</li>
  417. * <li> D Day in year Number 189</li>
  418. * <li> d Day in month Number 10</li>
  419. * <li> E Day in week Text Tuesday; Tue</li>
  420. * <li> a Am/pm marker Text PM</li>
  421. * <li> H Hour in day (0-23) Number 0</li>
  422. * <li> k Hour in day (1-24) Number 24</li>
  423. * <li> K Hour in am/pm (0-11) Number 0</li>
  424. * <li> h Hour in am/pm (1-12) Number 12</li>
  425. * <li> m Minute in hour Number 30</li>
  426. * <li> s Second in minute Number 55</li>
  427. * <li> S Millisecond Number 978</li>
  428. * <li> z Time zone General time zone Pacific Standard Time; PST; GMT-08:00</li>
  429. * <li> Z Time zone RFC 822 time zone -0800 </li>
  430. * </ul>
  431. *
  432. * @returns {Date} the parsed date
  433. *
  434. *
  435. */
  436. parse: function (dateStr, format) {
  437. 57 if (!format) {
  438. 2 throw new Error('format required when calling comb.date.parse');
  439. }
  440. 55 var tokens = [], regexp = buildDateEXP(format, tokens),
  441. re = new RegExp("^" + regexp + "$", "i"),
  442. match = re.exec(dateStr);
  443. 55 if (!match) {
  444. 7 return null;
  445. } // null
  446. 48 var result = [1970, 0, 1, 0, 0, 0, 0], // will get converted to a Date at the end
  447. amPm = "",
  448. valid = match.every(function (v, i) {
  449. 212 if (i) {
  450. 164 var token = tokens[i - 1];
  451. 164 var l = token.length, type = token.charAt(0);
  452. 164 if (type === 'y') {
  453. 26 if (v < 100) {
  454. 6 v = parseInt(v, 10);
  455. //choose century to apply, according to a sliding window
  456. //of 80 years before and 20 years after present year
  457. 6 var year = '' + new Date().getFullYear(),
  458. century = year.substring(0, 2) * 100,
  459. cutoff = min(year.substring(2, 4) + 20, 99);
  460. 6 result[0] = (v < cutoff) ? century + v : century - 100 + v;
  461. } else {
  462. 20 result[0] = v;
  463. }
  464. 138 } else if (type === "M") {
  465. 28 if (l > 2) {
  466. 20 var months = monthNames;
  467. 20 if (l === 3) {
  468. 8 months = monthAbbr;
  469. }
  470. //Tolerate abbreviating period in month part
  471. //Case-insensitive comparison
  472. 20 v = v.replace(".", "").toLowerCase();
  473. 20 months = months.map(function (s) {
  474. 240 return s.replace(".", "").toLowerCase();
  475. });
  476. 20 if ((v = months.indexOf(v)) === -1) {
  477. 2 return false;
  478. }
  479. } else {
  480. 8 v--;
  481. }
  482. 26 result[1] = v;
  483. 110 } else if (type === "E" || type === "e") {
  484. 12 var days = dayNames;
  485. 12 if (l === 3) {
  486. 4 days = dayAbbr;
  487. }
  488. //Case-insensitive comparison
  489. 12 v = v.toLowerCase();
  490. 12 days = days.map(function (d) {
  491. 84 return d.toLowerCase();
  492. });
  493. 12 var d = days.indexOf(v);
  494. 12 if (d === -1) {
  495. 4 v = parseInt(v, 10);
  496. 4 if (isNaN(v) || v > days.length) {
  497. 2 return false;
  498. }
  499. } else {
  500. 8 v = d;
  501. }
  502. 98 } else if (type === 'D' || type === "d") {
  503. 30 if (type === "D") {
  504. 4 result[1] = 0;
  505. }
  506. 30 result[2] = v;
  507. 68 } else if (type === "a") {
  508. 4 var am = "am";
  509. 4 var pm = "pm";
  510. 4 var period = /\./g;
  511. 4 v = v.replace(period, '').toLowerCase();
  512. // we might not have seen the hours field yet, so store the state and apply hour change later
  513. 4 amPm = (v === pm) ? 'p' : (v === am) ? 'a' : '';
  514. 64 } else if (type === "k" || type === "h" || type === "H" || type === "K") {
  515. 14 if (type === "k" && (+v) === 24) {
  516. 2 v = 0;
  517. }
  518. 14 result[3] = v;
  519. 50 } else if (type === "m") {
  520. 14 result[4] = v;
  521. 36 } else if (type === "s") {
  522. 10 result[5] = v;
  523. 26 } else if (type === "S") {
  524. 8 result[6] = v;
  525. }
  526. }
  527. 208 return true;
  528. });
  529. 48 if (valid) {
  530. 44 var hours = +result[3];
  531. //account for am/pm
  532. 44 if (amPm === 'p' && hours < 12) {
  533. 2 result[3] = hours + 12; //e.g., 3pm -> 15
  534. 42 } else if (amPm === 'a' && hours === 12) {
  535. 2 result[3] = 0; //12am -> 0
  536. }
  537. 44 var dateObject = new Date(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // Date
  538. 44 var dateToken = (tokens.indexOf('d') !== -1),
  539. monthToken = (tokens.indexOf('M') !== -1),
  540. month = result[1],
  541. day = result[2],
  542. dateMonth = dateObject.getMonth(),
  543. dateDay = dateObject.getDate();
  544. 44 if ((monthToken && dateMonth > month) || (dateToken && dateDay > day)) {
  545. 2 return null;
  546. }
  547. 42 return dateObject; // Date
  548. } else {
  549. 4 return null;
  550. }
  551. },
  552. /**
  553. * Formats a date to the specidifed format string
  554. *
  555. * @example
  556. *
  557. * var date = new Date(2006, 7, 11, 0, 55, 12, 345);
  558. * comb.date.format(date, "EEEE, MMMM dd, yyyy"); //"Friday, August 11, 2006"
  559. * comb.date.format(date, "M/dd/yy"); //"8/11/06"
  560. * comb.date.format(date, "E"); //"6"
  561. * comb.date.format(date, "h:m a"); //"12:55 AM"
  562. * comb.date.format(date, 'h:m:s'); //"12:55:12"
  563. * comb.date.format(date, 'h:m:s.SS'); //"12:55:12.35"
  564. * comb.date.format(date, 'k:m:s.SS'); //"24:55:12.35"
  565. * comb.date.format(date, 'H:m:s.SS'); //"0:55:12.35"
  566. * comb.date.format(date, "ddMMyyyy"); //"11082006"
  567. *
  568. * @param date the date to format
  569. * @param {String} format the format of the date composed of the following options
  570. * <ul>
  571. * <li> G Era designator Text AD</li>
  572. * <li> y Year Year 1996; 96</li>
  573. * <li> M Month in year Month July; Jul; 07</li>
  574. * <li> w Week in year Number 27</li>
  575. * <li> W Week in month Number 2</li>
  576. * <li> D Day in year Number 189</li>
  577. * <li> d Day in month Number 10</li>
  578. * <li> E Day in week Text Tuesday; Tue</li>
  579. * <li> a Am/pm marker Text PM</li>
  580. * <li> H Hour in day (0-23) Number 0</li>
  581. * <li> k Hour in day (1-24) Number 24</li>
  582. * <li> K Hour in am/pm (0-11) Number 0</li>
  583. * <li> h Hour in am/pm (1-12) Number 12</li>
  584. * <li> m Minute in hour Number 30</li>
  585. * <li> s Second in minute Number 55</li>
  586. * <li> S Millisecond Number 978</li>
  587. * <li> z Time zone General time zone Pacific Standard Time; PST; GMT-08:00</li>
  588. * <li> Z Time zone RFC 822 time zone -0800 </li>
  589. * </ul>
  590. */
  591. format: function (date, format, utc) {
  592. 78 utc = utc || false;
  593. 78 var fullYear, month, day, d, hour, minute, second, millisecond;
  594. 78 if (utc) {
  595. 26 fullYear = date.getUTCFullYear();
  596. 26 month = date.getUTCMonth();
  597. 26 day = date.getUTCDay();
  598. 26 d = date.getUTCDate();
  599. 26 hour = date.getUTCHours();
  600. 26 minute = date.getUTCMinutes();
  601. 26 second = date.getUTCSeconds();
  602. 26 millisecond = date.getUTCMilliseconds();
  603. } else {
  604. 52 fullYear = date.getFullYear();
  605. 52 month = date.getMonth();
  606. 52 d = date.getDate();
  607. 52 day = date.getDay();
  608. 52 hour = date.getHours();
  609. 52 minute = date.getMinutes();
  610. 52 second = date.getSeconds();
  611. 52 millisecond = date.getMilliseconds();
  612. }
  613. 78 return format.replace(/([A-Za-z])\1*/g, function (match, options) {
  614. 372 var s, pad, h,
  615. c = match.charAt(0),
  616. l = match.length;
  617. 372 if (c === 'd') {
  618. 42 s = "" + d;
  619. 42 pad = true;
  620. 330 } else if (c === "H" && !s) {
  621. 30 s = "" + hour;
  622. 30 pad = true;
  623. 300 } else if (c === 'm' && !s) {
  624. 40 s = "" + minute;
  625. 40 pad = true;
  626. 260 } else if (c === 's') {
  627. 38 if (!s) {
  628. 38 s = "" + second;
  629. }
  630. 38 pad = true;
  631. 222 } else if (c === "G") {
  632. 4 s = ((l < 4) ? eraAbbr : eraNames)[fullYear < 0 ? 0 : 1];
  633. 218 } else if (c === "y") {
  634. 48 s = fullYear;
  635. 48 if (l > 1) {
  636. 48 if (l === 2) {
  637. 6 s = getString().truncate("" + s, 2, true);
  638. } else {
  639. 42 pad = true;
  640. }
  641. }
  642. 170 } else if (c.toUpperCase() === "Q") {
  643. 6 s = ceil((month + 1) / 3);
  644. 6 pad = true;
  645. 164 } else if (c === "M") {
  646. 42 if (l < 3) {
  647. 32 s = month + 1;
  648. 32 pad = true;
  649. } else {
  650. 10 s = (l === 3 ? monthAbbr : monthNames)[month];
  651. }
  652. 122 } else if (c === "w") {
  653. 4 s = getWeekOfYear(date, 0, utc);
  654. 4 pad = true;
  655. 118 } else if (c === "D") {
  656. 2 s = getDayOfYear(date, utc);
  657. 2 pad = true;
  658. 116 } else if (c === "E") {
  659. 12 if (l < 3) {
  660. 2 s = day + 1;
  661. 2 pad = true;
  662. } else {
  663. 10 s = (l === -3 ? dayAbbr : dayNames)[day];
  664. }
  665. 104 } else if (c === 'a') {
  666. 2 s = (hour < 12) ? 'AM' : 'PM';
  667. 102 } else if (c === "h") {
  668. 6 s = (hour % 12) || 12;
  669. 6 pad = true;
  670. 96 } else if (c === "K") {
  671. 2 s = (hour % 12);
  672. 2 pad = true;
  673. 94 } else if (c === "k") {
  674. 2 s = hour || 24;
  675. 2 pad = true;
  676. 92 } else if (c === "S") {
  677. 36 s = round(millisecond * pow(10, l - 3));
  678. 36 pad = true;
  679. 56 } else if (c === "z" || c === "v" || c === "Z") {
  680. 30 s = getTimezoneName(date);
  681. 30 if ((c === "z" || c === "v") && !s) {
  682. 4 l = 4;
  683. }
  684. 30 if (!s || c === "Z") {
  685. 4 var offset = date.getTimezoneOffset();
  686. 4 var tz = [
  687. (offset >= 0 ? "-" : "+"),
  688. getString().pad(floor(abs(offset) / 60), 2, "0"),
  689. getString().pad(abs(offset) % 60, 2, "0")
  690. ];
  691. 4 if (l === 4) {
  692. 4 tz.splice(0, 0, "GMT");
  693. 4 tz.splice(3, 0, ":");
  694. }
  695. 4 s = tz.join("");
  696. }
  697. } else {
  698. 26 s = match;
  699. }
  700. 372 if (pad) {
  701. 284 s = getString().pad(s, l, '0');
  702. }
  703. 372 return s;
  704. });
  705. }
  706. };
  707. 1date = comb.date;
  708. /**
  709. * Adds the specified year/s to the current date.
  710. *
  711. * @example
  712. *
  713. * //assuming that current year is 2012
  714. * comb.yearsFromNow(1); //2013-mm-dd hh:MM:ss
  715. *
  716. * @param {Number} val the number of years to add
  717. *
  718. * @return {Date} a date with the number of years added
  719. */
  720. 1comb.yearsFromNow = function (val) {
  721. 1 return date.add(new Date(), "years", val);
  722. };
  723. /**
  724. * Subtracts the specified year/s from the current date.
  725. *
  726. * @param {Number} val the number of years to subtract
  727. *
  728. * @return {Date} a date with the number of years subtracted
  729. */
  730. 1comb.yearsAgo = function (val) {
  731. 1 return date.add(new Date(), "years", -val);
  732. };
  733. /**
  734. * Adds the specified month/s to the current date.
  735. *
  736. * @example
  737. *
  738. * //assuming that current month is february
  739. * comb.yearsFromNow(2); //yyyy-04-dd hh:MM:ss
  740. *
  741. * @param {Number} val the number of months to add
  742. *
  743. * @return {Date} a date with the number of years added
  744. */
  745. 1comb.monthsFromNow = function (val) {
  746. 1 return date.add(new Date(), "months", val);
  747. };
  748. /**
  749. * Subtracts the specified month/s from the current date.
  750. *
  751. * @param {Number} val the number of months to subtract
  752. *
  753. * @return {Date} a date with the number of months subtracted
  754. */
  755. 1comb.monthsAgo = function (val) {
  756. 1 return date.add(new Date(), "months", -val);
  757. };
  758. /**
  759. * Adds the specified day/s to the current date.
  760. *
  761. * @param {Number} val the number of days to add
  762. *
  763. * @return {Date} a date with the number of days added
  764. */
  765. 1comb.daysFromNow = function (val) {
  766. 27 return date.add(new Date(), "days", val);
  767. };
  768. /**
  769. * Subtracts the specified day/s from the current date.
  770. *
  771. * @param {Number} val the number of days to subtract
  772. *
  773. * @return {Date} a date with the number of days subtracted
  774. */
  775. 1comb.daysAgo = function (val) {
  776. 1 return date.add(new Date(), "days", -val);
  777. };
  778. /**
  779. * Adds the specified weekday/s to the current date.
  780. *
  781. * @param {Number} val the number of weekdays to add
  782. *
  783. * @return {Date} a date with the number of weekdays added
  784. */
  785. 1comb.weekDaysFromNow = function (val) {
  786. 0 return date.add(new Date(), "weekdays", val);
  787. };
  788. /**
  789. * Subtracts the specified weekday/s from the current date.
  790. *
  791. * @param {Number} val the number of weekdays to subtract
  792. *
  793. * @return {Date} a date with the number of weekdays subtracted
  794. */
  795. 1comb.weekDaysAgo = function (val) {
  796. 0 return date.add(new Date(), "weekdays", -val);
  797. };
  798. /**
  799. * Adds the specified hour/s to the current date.
  800. *
  801. * @param {Number} val the number of hours to add
  802. *
  803. * @return {Date} a date with the number of hours added
  804. */
  805. 1comb.hoursFromNow = function (val) {
  806. 1 return date.add(new Date(), "hours", val);
  807. };
  808. /**
  809. * Subtracts the specified hour/s from the current date.
  810. *
  811. * @param {Number} val the number of hours to subtract
  812. *
  813. * @return {Date} a date with the number of hours subtracted
  814. */
  815. 1comb.hoursAgo = function (val) {
  816. 1 return date.add(new Date(), "hours", -val);
  817. };
  818. /**
  819. * Adds the specified minute/s to the current date.
  820. *
  821. * @param {Number} val the number of minutes to add
  822. *
  823. * @return {Date} a date with the number of minutes added
  824. */
  825. 1comb.minutesFromNow = function (val) {
  826. 1 return date.add(new Date(), "minutes", val);
  827. };
  828. /**
  829. * Subtracts the specified minute/s from the current date.
  830. *
  831. * @param {Number} val the number of minutes to subtract
  832. *
  833. * @return {Date} a date with the number of minutes subtracted
  834. */
  835. 1comb.minutesAgo = function (val) {
  836. 1 return date.add(new Date(), "minutes", -val);
  837. };
  838. /**
  839. * Adds the specified second/s to the current date.
  840. *
  841. * @param {Number} val the number of seconds to add
  842. *
  843. * @return {Date} a date with the number of seconds added
  844. */
  845. 1comb.secondsFromNow = function (val) {
  846. 1 return date.add(new Date(), "seconds", val);
  847. };
  848. /**
  849. * Subtracts the specified second/s from the current date.
  850. *
  851. * @param {Number} val the number of seconds to subtract
  852. *
  853. * @return {Date} a date with the number of seconds subtracted
  854. */
  855. 1comb.secondsAgo = function (val) {
  856. 1 return date.add(new Date(), "seconds", -val);
  857. };
base/array.js
Coverage100.00 SLOC1123 LOC363 Missed0
  1. 1var obj = require("./object"),
  2. merge = obj.merge,
  3. misc = require("./misc"),
  4. argsToArray = misc.argsToArray,
  5. string = require("./string"),
  6. isString = string.isString,
  7. number = require("./number.js"),
  8. floor = Math.floor,
  9. abs = Math.abs,
  10. mathMax = Math.max,
  11. mathMin = Math.min;
  12. 1var isArray = exports.isArray = function isArray(obj) {
  13. 12427 return Object.prototype.toString.call(obj) === "[object Array]";
  14. };
  15. 1function isDate(obj) {
  16. 126 var undef;
  17. 126 return (obj !== undef && typeof obj === "object" && obj instanceof Date);
  18. }
  19. 1function cross(num, cros) {
  20. 21 var ret = reduceRight(cros, function (a, b) {
  21. 57 if (!isArray(b)) {
  22. 57 b = [b];
  23. }
  24. 57 b.unshift(num);
  25. 57 a.unshift(b);
  26. 57 return a;
  27. }, []);
  28. 21 return ret;
  29. }
  30. 1function permute(num, cross, length) {
  31. 18 var ret = [];
  32. 18 for (var i = 0; i < cross.length; i++) {
  33. 36 ret.push([num].concat(rotate(cross, i)).slice(0, length));
  34. }
  35. 18 return ret;
  36. }
  37. 1function intersection(a, b) {
  38. 40 var ret = [], aOne;
  39. 40 if (isArray(a) && isArray(b) && a.length && b.length) {
  40. 38 for (var i = 0, l = a.length; i < l; i++) {
  41. 129 aOne = a[i];
  42. 129 if (indexOf(b, aOne) !== -1) {
  43. 94 ret.push(aOne);
  44. }
  45. }
  46. }
  47. 40 return ret;
  48. }
  49. 1var _sort = (function () {
  50. 1 var isAll = function (arr, test) {
  51. 63 return every(arr, test);
  52. };
  53. 1 var defaultCmp = function (a, b) {
  54. 90 return a - b;
  55. };
  56. 1 var dateSort = function (a, b) {
  57. 27 return a.getTime() - b.getTime();
  58. };
  59. 1 return function _sort(arr, property) {
  60. 66 var ret = [];
  61. 66 if (isArray(arr)) {
  62. 66 ret = arr.slice();
  63. 66 if (property) {
  64. 30 if (typeof property === "function") {
  65. 3 ret.sort(property);
  66. } else {
  67. 27 ret.sort(function (a, b) {
  68. 81 var aProp = a[property], bProp = b[property];
  69. 81 if (isString(aProp) && isString(bProp)) {
  70. 27 return aProp > bProp ? 1 : aProp < bProp ? -1 : 0;
  71. 54 } else if (isDate(aProp) && isDate(bProp)) {
  72. 27 return aProp.getTime() - bProp.getTime()
  73. } else {
  74. 27 return aProp - bProp;
  75. }
  76. });
  77. }
  78. } else {
  79. 36 if (isAll(ret, isString)) {
  80. 9 ret.sort();
  81. 27 } else if (isAll(ret, isDate)) {
  82. 9 ret.sort(dateSort);
  83. } else {
  84. 18 ret.sort(defaultCmp);
  85. }
  86. }
  87. }
  88. 66 return ret;
  89. };
  90. })();
  91. 1function indexOf(arr, searchElement, fromIndex) {
  92. 363 if (!isArray(arr)) {
  93. 1 throw new TypeError();
  94. }
  95. 362 var t = Object(arr);
  96. 362 var len = t.length >>> 0;
  97. 362 if (len === 0) {
  98. 39 return -1;
  99. }
  100. 323 var n = 0;
  101. 323 if (arguments.length > 2) {
  102. 8 n = Number(arguments[2]);
  103. 8 if (n !== n) { // shortcut for verifying if it's NaN
  104. 2 n = 0;
  105. 6 } else if (n !== 0 && n !== Infinity && n !== -Infinity) {
  106. 2 n = (n > 0 || -1) * floor(abs(n));
  107. }
  108. }
  109. 323 if (n >= len) {
  110. 2 return -1;
  111. }
  112. 321 var k = n >= 0 ? n : mathMax(len - abs(n), 0);
  113. 321 for (; k < len; k++) {
  114. 644 if (k in t && t[k] === searchElement) {
  115. 180 return k;
  116. }
  117. }
  118. 141 return -1;
  119. }
  120. 1function lastIndexOf(arr, searchElement, fromIndex) {
  121. 12 if (!isArray(arr)) {
  122. 1 throw new TypeError();
  123. }
  124. 11 var t = Object(arr);
  125. 11 var len = t.length >>> 0;
  126. 11 if (len === 0) {
  127. 1 return -1;
  128. }
  129. 10 var n = len;
  130. 10 if (arguments.length > 2) {
  131. 8 n = Number(arguments[2]);
  132. 8 if (n !== n) {
  133. 2 n = 0;
  134. 6 } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
  135. 2 n = (n > 0 || -1) * floor(abs(n));
  136. }
  137. }
  138. 10 var k = n >= 0 ? mathMin(n, len - 1) : len - abs(n);
  139. 10 for (; k >= 0; k--) {
  140. 18 if (k in t && t[k] === searchElement) {
  141. 6 return k;
  142. }
  143. }
  144. 4 return -1;
  145. }
  146. 1function filter(arr, iterator, scope) {
  147. 47 if (!isArray(arr) || typeof iterator !== "function") {
  148. 1 throw new TypeError();
  149. }
  150. 46 var t = Object(arr);
  151. 46 var len = t.length >>> 0;
  152. 46 var res = [];
  153. 46 for (var i = 0; i < len; i++) {
  154. 168 if (i in t) {
  155. 128 var val = t[i]; // in case fun mutates this
  156. 128 if (iterator.call(scope, val, i, t)) {
  157. 76 res.push(val);
  158. }
  159. }
  160. }
  161. 46 return res;
  162. }
  163. 1function forEach(arr, iterator, scope) {
  164. 8495 if (!isArray(arr) || typeof iterator !== "function") {
  165. 1 throw new TypeError();
  166. }
  167. 8494 for (var i = 0, len = arr.length; i < len; ++i) {
  168. 71440 iterator.call(scope || arr, arr[i], i, arr);
  169. }
  170. 8494 return arr;
  171. }
  172. 1function every(arr, iterator, scope) {
  173. 281 if (!isArray(arr) || typeof iterator !== "function") {
  174. 1 throw new TypeError();
  175. }
  176. 280 var t = Object(arr);
  177. 280 var len = t.length >>> 0;
  178. 280 for (var i = 0; i < len; i++) {
  179. 830 if (i in t && !iterator.call(scope, t[i], i, t)) {
  180. 171 return false;
  181. }
  182. }
  183. 109 return true;
  184. }
  185. 1function some(arr, iterator, scope) {
  186. 7 if (!isArray(arr) || typeof iterator !== "function") {
  187. 1 throw new TypeError();
  188. }
  189. 6 var t = Object(arr);
  190. 6 var len = t.length >>> 0;
  191. 6 for (var i = 0; i < len; i++) {
  192. 30 if (i in t && iterator.call(scope, t[i], i, t)) {
  193. 2 return true;
  194. }
  195. }
  196. 4 return false;
  197. }
  198. 1function map(arr, iterator, scope) {
  199. 53 if (!isArray(arr) || typeof iterator !== "function") {
  200. 1 throw new TypeError();
  201. }
  202. 52 var t = Object(arr);
  203. 52 var len = t.length >>> 0;
  204. 52 var res = [];
  205. 52 for (var i = 0; i < len; i++) {
  206. 177 if (i in t) {
  207. 177 res.push(iterator.call(scope, t[i], i, t));
  208. }
  209. }
  210. 52 return res;
  211. }
  212. 1function reduce(arr, accumulator, curr) {
  213. 211 if (!isArray(arr) || typeof accumulator !== "function") {
  214. 1 throw new TypeError();
  215. }
  216. 210 var i = 0, l = arr.length >> 0;
  217. 210 if (arguments.length < 3) {
  218. 24 if (l === 0) {
  219. 1 throw new TypeError("Array length is 0 and no second argument");
  220. }
  221. 23 curr = arr[0];
  222. 23 i = 1; // start accumulating at the second element
  223. } else {
  224. 186 curr = arguments[2];
  225. }
  226. 209 while (i < l) {
  227. 508 if (i in arr) {
  228. 508 curr = accumulator.call(undefined, curr, arr[i], i, arr);
  229. }
  230. 508 ++i;
  231. }
  232. 209 return curr;
  233. }
  234. 1function reduceRight(arr, accumulator, curr) {
  235. 27 if (!isArray(arr) || typeof accumulator !== "function") {
  236. 1 throw new TypeError();
  237. }
  238. 26 var t = Object(arr);
  239. 26 var len = t.length >>> 0;
  240. // no value to return if no initial value, empty array
  241. 26 if (len === 0 && arguments.length === 2) {
  242. 1 throw new TypeError();
  243. }
  244. 25 var k = len - 1;
  245. 25 if (arguments.length >= 3) {
  246. 23 curr = arguments[2];
  247. } else {
  248. 2 do {
  249. 2 if (k in arr) {
  250. 2 curr = arr[k--];
  251. 2 break;
  252. }
  253. }
  254. while (true);
  255. }
  256. 25 while (k >= 0) {
  257. 75 if (k in t) {
  258. 75 curr = accumulator.call(undefined, curr, t[k], k, t);
  259. }
  260. 75 k--;
  261. }
  262. 25 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. 101 var ret = [];
  279. 101 if (o != null) {
  280. 100 var args = argsToArray(arguments);
  281. 100 if (args.length === 1) {
  282. 98 if (isArray(o)) {
  283. 85 ret = o;
  284. 13 } else if (obj.isHash(o)) {
  285. 4 for (var i in o) {
  286. 6 if (o.hasOwnProperty(i)) {
  287. 6 ret.push([i, o[i]]);
  288. }
  289. }
  290. } else {
  291. 9 ret.push(o);
  292. }
  293. } else {
  294. 2 forEach(args, function (a) {
  295. 4 ret = ret.concat(toArray(a));
  296. });
  297. }
  298. }
  299. 101 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. 27 array = array || [];
  320. 27 if (array.length) {
  321. 21 return reduce(array, function (a, b) {
  322. 49 return a + b;
  323. });
  324. } else {
  325. 6 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. 13 arr = arr || [];
  342. 13 if (arr.length) {
  343. 8 var total = sum(arr);
  344. 8 if (number.isNumber(total)) {
  345. 4 return total / arr.length;
  346. } else {
  347. 4 throw new Error("Cannot average an array of non numbers.");
  348. }
  349. } else {
  350. 5 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. 24 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. 21 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. 21 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. 25 var ret = arr1, args = flatten(argsToArray(arguments, 1));
  430. 25 if (isArray(arr1)) {
  431. 25 ret = filter(arr1, function (a) {
  432. 72 return indexOf(args, a) === -1;
  433. });
  434. }
  435. 25 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. 43 if (isArray(arr)) {
  452. 43 return reduce(arr, function (a, b) {
  453. 151 if (indexOf(a, b) === -1) {
  454. 106 return a.concat(b);
  455. } else {
  456. 45 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. 4 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. 174 var ret = arr.slice();
  493. 174 if (typeof numberOfTimes !== "number") {
  494. 3 numberOfTimes = 1;
  495. }
  496. 174 if (numberOfTimes && isArray(arr)) {
  497. 96 if (numberOfTimes > 0) {
  498. 66 ret.push(ret.shift());
  499. 66 numberOfTimes--;
  500. } else {
  501. 30 ret.unshift(ret.pop());
  502. 30 numberOfTimes++;
  503. }
  504. 96 return rotate(ret, numberOfTimes);
  505. } else {
  506. 78 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. 15 var ret = [];
  528. 15 if (isArray(arr)) {
  529. 15 var copy = arr.slice(0);
  530. 15 if (typeof length !== "number") {
  531. 3 length = arr.length;
  532. }
  533. 15 if (!length) {
  534. 3 ret = [
  535. []
  536. ];
  537. 12 } else if (length <= arr.length) {
  538. 9 ret = reduce(arr, function (a, b, i) {
  539. 27 var ret;
  540. 27 if (length > 1) {
  541. 18 ret = permute(b, rotate(copy, i).slice(1), length);
  542. } else {
  543. 9 ret = [
  544. [b]
  545. ];
  546. }
  547. 27 return a.concat(ret);
  548. }, []);
  549. }
  550. }
  551. 15 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. 11 var ret = [];
  571. 11 var arrs = argsToArray(arguments);
  572. 11 if (arrs.length > 1) {
  573. 11 var arr1 = arrs.shift();
  574. 11 if (isArray(arr1)) {
  575. 11 ret = reduce(arr1, function (a, b, i) {
  576. 38 var curr = [b];
  577. 38 for (var j = 0; j < arrs.length; j++) {
  578. 76 var currArr = arrs[j];
  579. 76 if (isArray(currArr) && !misc.isUndefined(currArr[i])) {
  580. 66 curr.push(currArr[i]);
  581. } else {
  582. 10 curr.push(null);
  583. }
  584. }
  585. 38 a.push(curr);
  586. 38 return a;
  587. }, []);
  588. }
  589. }
  590. 11 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. 9 var ret = [];
  607. 9 if (isArray(arr) && arr.length) {
  608. 9 var last;
  609. 9 forEach(arr, function (a) {
  610. 24 if (isArray(a) && (!last || a.length === last.length)) {
  611. 18 forEach(a, function (b, i) {
  612. 39 if (!ret[i]) {
  613. 18 ret[i] = [];
  614. }
  615. 39 ret[i].push(b);
  616. });
  617. 18 last = a;
  618. }
  619. });
  620. }
  621. 9 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. 9 var ret = [];
  641. 9 indexes = argsToArray(arguments);
  642. 9 arr = indexes.shift();
  643. 9 var l = arr.length;
  644. 9 if (isArray(arr) && indexes.length) {
  645. 9 for (var i = 0; i < indexes.length; i++) {
  646. 27 ret.push(arr[indexes[i]] || null);
  647. }
  648. }
  649. 9 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. 6 var ret = [];
  666. 6 var arrs = argsToArray(arguments);
  667. 6 if (arrs.length > 1) {
  668. 6 ret = removeDuplicates(reduce(arrs, function (a, b) {
  669. 21 return a.concat(b);
  670. }, []));
  671. }
  672. 6 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. 25 var collect = [], set;
  693. 25 var args = argsToArray(arguments);
  694. 25 if (args.length > 1) {
  695. //assume we are intersections all the lists in the array
  696. 23 set = args;
  697. } else {
  698. 2 set = args[0];
  699. }
  700. 25 if (isArray(set)) {
  701. 25 var x = set.shift();
  702. 25 collect = reduce(set, function (a, b) {
  703. 40 return intersection(a, b);
  704. }, x);
  705. }
  706. 25 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. 9 var ret = [];
  755. 9 if (isArray(arr) && arr.length) {
  756. 9 ret = reduce(arr, function (a, b) {
  757. 27 var ret = map(a, function (c) {
  758. 75 return c.concat(b);
  759. });
  760. 27 return a.concat(ret);
  761. }, [
  762. []
  763. ]);
  764. }
  765. 9 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. 30 var ret = [];
  806. 30 if (isArray(a) && isArray(b) && a.length && b.length) {
  807. 21 ret = cross(a[0], b).concat(cartesian(a.slice(1), b));
  808. }
  809. 30 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. 16 var ret = [];
  826. 16 if (isArray(arr) && arr.length) {
  827. 16 ret = filter(arr, function (item) {
  828. 31 return !misc.isUndefinedOrNull(item);
  829. });
  830. }
  831. 16 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. 12 times = number.isNumber(times) ? times : 1;
  845. 12 if (!times) {
  846. //make sure times is greater than zero if it is zero then dont multiply it
  847. 3 times = 1;
  848. }
  849. 12 arr = toArray(arr || []);
  850. 12 var ret = [], i = 0;
  851. 12 while (++i <= times) {
  852. 15 ret = ret.concat(arr);
  853. }
  854. 12 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. 81 var set;
  871. 81 var args = argsToArray(arguments);
  872. 81 if (args.length > 1) {
  873. //assume we are intersections all the lists in the array
  874. 10 set = args;
  875. } else {
  876. 71 set = toArray(arr);
  877. }
  878. 81 return reduce(set, function (a, b) {
  879. 137 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. 7 prop = prop.split(".");
  908. 7 var result = arr.slice(0);
  909. 7 forEach(prop, function (prop) {
  910. 13 var exec = prop.match(/(\w+)\(\)$/);
  911. 13 result = map(result, function (item) {
  912. 52 return exec ? item[exec[1]]() : item[prop];
  913. });
  914. });
  915. 7 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. 8 args = argsToArray(arguments, 2);
  962. 8 return map(arr, function (item) {
  963. 32 var exec = isString(func) ? item[func] : func;
  964. 32 return exec.apply(item, args);
  965. });
  966. }
  967. /**
  968. * Paritition an array.
  969. *
  970. * ```
  971. * var arr = [1,2,3,4,5];
  972. *
  973. * comb.array.partition(arr, 1); //[[1], [2], [3], [4], [5]]
  974. * comb.array.partition(arr, 2); //[[1,2], [3,4], [5]]
  975. * comb.array.partition(arr, 3); //[[1,2,3], [4,5]]
  976. *
  977. * comb.array.partition(arr); //[[1, 2, 3, 4, 5]]
  978. * comb.array.partition(arr, 0); //[[1, 2, 3, 4, 5]]
  979. *
  980. *
  981. *
  982. * ```
  983. *
  984. * @param array
  985. * @param partitionSize
  986. * @returns {Array}
  987. *
  988. * @static
  989. * @memberOf comb.array
  990. */
  991. 1function partition(array, partitionSize) {
  992. 14 partitionSize = partitionSize || array.length;
  993. 14 var ret = [];
  994. 14 for (var i = 0, l = array.length; i < l; i += partitionSize) {
  995. 30 ret.push(array.slice(i, i + partitionSize));
  996. }
  997. 14 return ret;
  998. }
  999. 1var comb = exports;
  1000. /**
  1001. * @namespace Utilities for working with arrays.
  1002. *
  1003. * The `comb.array` namespace can be used to decorate arrays with additional chainable funcitonality.
  1004. *
  1005. * ```
  1006. *
  1007. * var arr = comb.array([1,3,2,5,4,6]);
  1008. * console.log(arr.sum()) //21
  1009. * console.log(arr.sort()) //[1,2,3,4,5,6]
  1010. * console.log(arr.min()) //[1]
  1011. * console.log(arr.max()) [6]
  1012. *
  1013. * ```
  1014. *
  1015. * @function
  1016. * @ignoreCode
  1017. */
  1018. 1comb.array = {
  1019. toArray: toArray,
  1020. sum: sum,
  1021. avg: avg,
  1022. sort: sort,
  1023. min: min,
  1024. max: max,
  1025. difference: difference,
  1026. removeDuplicates: removeDuplicates,
  1027. unique: unique,
  1028. rotate: rotate,
  1029. permutations: permutations,
  1030. zip: zip,
  1031. transpose: transpose,
  1032. valuesAt: valuesAt,
  1033. union: union,
  1034. intersect: intersect,
  1035. powerSet: powerSet,
  1036. cartesian: cartesian,
  1037. compact: compact,
  1038. multiply: multiply,
  1039. flatten: flatten,
  1040. pluck: pluck,
  1041. invoke: invoke,
  1042. forEach: forEach,
  1043. map: map,
  1044. filter: filter,
  1045. reduce: reduce,
  1046. reduceRight: reduceRight,
  1047. some: some,
  1048. every: every,
  1049. indexOf: indexOf,
  1050. lastIndexOf: lastIndexOf,
  1051. partition: partition
  1052. };
base/broadcast.js
Coverage100.00 SLOC171 LOC63 Missed0
  1. 1var func = require("./functions"),
  2. obj = require("./object");
  3. 1var comb = exports;
  4. 1var wrapper = function() {
  5. 8 return function() {
  6. 31 var c = arguments.callee, listeners = c.__listeners, func = c.func, r;
  7. 31 if (func) {
  8. 31 r = func.apply(this, arguments);
  9. }
  10. 31 for (var i = 0; i < listeners.length; i++) {
  11. 39 var lis = listeners[i];
  12. 39 if (lis) {
  13. 30 lis.apply(this, arguments);
  14. }
  15. }
  16. 31 return r;
  17. }
  18. };
  19. 1var listeners = {};
  20. 1obj.merge(comb, {
  21. /**@lends comb*/
  22. /**
  23. * Disconnects a listener to a function
  24. * @param {handle} A handle returned from comb.connect
  25. */
  26. disconnect : function(handle) {
  27. 10 if (handle && handle.length == 3) {
  28. 9 var obj = handle[0], method = handle[1], cb = handle[2];
  29. 9 if (typeof method != "string") throw "comb.disconnect : When calling disconnect the method must be string";
  30. 9 var scope = obj || global, ls;
  31. 9 if (typeof scope[method] == "function") {
  32. 8 ls = scope[method].__listeners;
  33. 8 if (ls && cb-- > 0) {
  34. //we dont want to splice it because our indexing will get off
  35. 8 ls[cb] = null;
  36. }
  37. } else {
  38. 1 throw new Error("unknown method " + method + " in object " + obj);
  39. }
  40. } else {
  41. 1 throw "comb.disconnect : invalid handle"
  42. }
  43. },
  44. /**
  45. * Function to listen when other functions are called
  46. *
  47. * @example
  48. *
  49. * comb.connect(obj, "event", myfunc);
  50. * comb.connect(obj, "event", "log", console);
  51. *
  52. * @param {Object} obj the object in which the method you are connecting to resides
  53. * @param {String} method the name of the method to connect to
  54. * @param {Function} cb the function to callback
  55. * @param {Object} [scope] the scope to call the specified cb in
  56. *
  57. * @returns {Array} handle to pass to {@link comb.disconnect}
  58. */
  59. connect : function(obj, method, cb, scope) {
  60. 14 var index;
  61. 14 if (typeof method != "string") throw new Error("When calling connect the method must be string");
  62. 14 if (!func.isFunction(cb)) throw new Error("When calling connect callback must be a string");
  63. 14 var scope = obj || global, listeners, newMethod;
  64. 14 if (typeof scope[method] == "function") {
  65. 13 listeners = scope[method].__listeners;
  66. 13 if (!listeners) {
  67. 8 newMethod = wrapper();
  68. 8 newMethod.func = obj[method];
  69. 8 listeners = (newMethod.__listeners = []);
  70. 8 scope[method] = newMethod;
  71. }
  72. 13 index = listeners.push(cb);
  73. } else {
  74. 1 throw new Error("unknow method " + method + " in object " + obj);
  75. }
  76. 13 return [obj, method, index];
  77. },
  78. /**
  79. * Broadcasts an event to all listeners
  80. * NOTE : the function takes a variable number of arguments
  81. * i.e. all arguments after the topic will be passed to the listeners
  82. *
  83. * @example
  84. *
  85. *
  86. * comb.broadcast("hello", "hello world");
  87. * //the args "hello" and "world" will be passed to any listener of the topic
  88. * //"hello"
  89. * comb.broadcast("hello", "hello", "world");
  90. *
  91. * @param {String} topic the topic to brodcast
  92. * @param params the information to bradcast
  93. */
  94. broadcast : function() {
  95. 4 var args = Array.prototype.slice.call(arguments);
  96. 4 var topic = args.splice(0, 1)[0];
  97. 4 if (topic) {
  98. 4 var list = listeners[topic];
  99. 4 if (list) {
  100. 4 for (var i = list.length - 1; i >= 0; i--) {
  101. 4 var han = list[i], cb = han.cb;
  102. 4 if (cb) {
  103. 4 cb.apply(this, args);
  104. }
  105. }
  106. }
  107. }
  108. },
  109. /**
  110. * Listen for the broadcast of certain events
  111. *
  112. * @example
  113. * comb.listen("hello", function(arg1, arg2){
  114. * console.log(arg1);
  115. * console.log(arg2);
  116. * });
  117. *
  118. * @param {String} topic the topic to listen for
  119. * @param {Function} callback the funciton to call when the topic is published
  120. *
  121. * @returns a handle to pass to {@link comb.unListen}
  122. */
  123. listen : function(topic, callback) {
  124. 3 if (!func.isFunction(callback)) throw new Error("callback must be a function");
  125. 3 var handle = {
  126. topic : topic,
  127. cb : callback,
  128. pos : null
  129. };
  130. 3 var list = listeners[topic];
  131. 3 if (!list) {
  132. 3 list = (listeners[topic] = []);
  133. }
  134. 3 list.push(handle);
  135. 3 handle.pos = list.length - 1;
  136. 3 return handle;
  137. },
  138. /**
  139. * Disconnects a listener
  140. *
  141. * @param handle a handle returned from {@link comb.listen}
  142. */
  143. unListen : function(handle) {
  144. 2 if (handle) {
  145. 2 var topic = handle.topic, list = listeners[topic];
  146. 2 if (list) {
  147. 2 for (var i = list.length - 1; i >= 0; i--) {
  148. 2 if (list[i] == handle) {
  149. 2 list.splice(i, 1);
  150. }
  151. }
  152. 2 if (!list.length) {
  153. 2 delete listeners[topic];
  154. }
  155. }
  156. }
  157. }
  158. });
base/characters.js
Coverage100.00 SLOC762 LOC2 Missed0
  1. 1var comb = exports;
  2. /**@namespace comb characters*/
  3. 1comb.characters = {
  4. /**@lends comb.characters*/
  5. /**
  6. * ☺
  7. */
  8. SMILEY: "☺",
  9. /**
  10. * ☻
  11. */
  12. SOLID_SMILEY: "☻",
  13. /**
  14. * ♥
  15. */
  16. HEART: "♥",
  17. /**
  18. * ♦
  19. */
  20. DIAMOND: "♦",
  21. /**
  22. * ♣
  23. */
  24. CLOVE: "♣",
  25. /**
  26. * â™ 
  27. */
  28. SPADE: "â™ ",
  29. /**
  30. * •
  31. */
  32. DOT: "•",
  33. /**
  34. * â—˜
  35. */
  36. SQUARE_CIRCLE: "â—˜",
  37. /**
  38. * â—‹
  39. */
  40. CIRCLE: "â—‹",
  41. /**
  42. * â—™
  43. */
  44. FILLED_SQUARE_CIRCLE: "â—™",
  45. /**
  46. * ♂
  47. */
  48. MALE: "♂",
  49. /**
  50. * ♀
  51. */
  52. FEMALE: "♀",
  53. /**
  54. * ♪
  55. */
  56. EIGHT_NOTE: "♪",
  57. /**
  58. * ♫
  59. */
  60. DOUBLE_EIGHT_NOTE: "♫",
  61. /**
  62. * ☼
  63. */
  64. SUN: "☼",
  65. /**
  66. * â–º
  67. */
  68. PLAY: "â–º",
  69. /**
  70. * â—„
  71. */
  72. REWIND: "â—„",
  73. /**
  74. * ↕
  75. */
  76. UP_DOWN: "↕",
  77. /**
  78. * ¶
  79. */
  80. PILCROW: "¶",
  81. /**
  82. * §
  83. */
  84. SECTION: "§",
  85. /**
  86. * â–¬
  87. */
  88. THICK_MINUS: "â–¬",
  89. /**
  90. * ↨
  91. */
  92. SMALL_UP_DOWN: "↨",
  93. /**
  94. * ↑
  95. */
  96. UP_ARROW: "↑",
  97. /**
  98. * ↓
  99. */
  100. DOWN_ARROW: "↓",
  101. /**
  102. * →
  103. */
  104. RIGHT_ARROW: "→",
  105. /**
  106. * ←
  107. */
  108. LEFT_ARROW: "←",
  109. /**
  110. * ∟
  111. */
  112. RIGHT_ANGLE: "∟",
  113. /**
  114. * ↔
  115. */
  116. LEFT_RIGHT_ARROW: "↔",
  117. /**
  118. * â–²
  119. */
  120. TRIANGLE: "â–²",
  121. /**
  122. * â–¼
  123. */
  124. DOWN_TRIANGLE: "â–¼",
  125. /**
  126. * ⌂
  127. */
  128. HOUSE: "⌂",
  129. /**
  130. * Ç
  131. */
  132. C_CEDILLA: "Ç",
  133. /**
  134. * ü
  135. */
  136. U_UMLAUT: "ü",
  137. /**
  138. * é
  139. */
  140. E_ACCENT: "é",
  141. /**
  142. * â
  143. */
  144. A_LOWER_CIRCUMFLEX: "â",
  145. /**
  146. * ä
  147. */
  148. A_LOWER_UMLAUT: "ä",
  149. /**
  150. * à
  151. */
  152. A_LOWER_GRAVE_ACCENT: "à",
  153. /**
  154. * å
  155. */
  156. A_LOWER_CIRCLE_OVER: "Ã¥",
  157. /**
  158. * ç
  159. */
  160. C_LOWER_CIRCUMFLEX: "ç",
  161. /**
  162. * ê
  163. */
  164. E_LOWER_CIRCUMFLEX: "ê",
  165. /**
  166. * ë
  167. */
  168. E_LOWER_UMLAUT: "ë",
  169. /**
  170. * è
  171. */
  172. E_LOWER_GRAVE_ACCENT: "è",
  173. /**
  174. * ï
  175. */
  176. I_LOWER_UMLAUT: "ï",
  177. /**
  178. * î
  179. */
  180. I_LOWER_CIRCUMFLEX: "î",
  181. /**
  182. * ì
  183. */
  184. I_LOWER_GRAVE_ACCENT: "ì",
  185. /**
  186. * Ä
  187. */
  188. A_UPPER_UMLAUT: "Ä",
  189. /**
  190. * Ã…
  191. */
  192. A_UPPER_CIRCLE: "Ã…",
  193. /**
  194. * É
  195. */
  196. E_UPPER_ACCENT: "É",
  197. /**
  198. * æ
  199. */
  200. A_E_LOWER: "æ",
  201. /**
  202. * Æ
  203. */
  204. A_E_UPPER: "Æ",
  205. /**
  206. * ô
  207. */
  208. O_LOWER_CIRCUMFLEX: "ô",
  209. /**
  210. * ö
  211. */
  212. O_LOWER_UMLAUT: "ö",
  213. /**
  214. * ò
  215. */
  216. O_LOWER_GRAVE_ACCENT: "ò",
  217. /**
  218. * û
  219. */
  220. U_LOWER_CIRCUMFLEX: "û",
  221. /**
  222. * ù
  223. */
  224. U_LOWER_GRAVE_ACCENT: "ù",
  225. /**
  226. * ÿ
  227. */
  228. Y_LOWER_UMLAUT: "ÿ",
  229. /**
  230. * Ö
  231. */
  232. O_UPPER_UMLAUT: "Ö",
  233. /**
  234. * Ü
  235. */
  236. U_UPPER_UMLAUT: "Ü",
  237. /**
  238. * ¢
  239. */
  240. CENTS: "¢",
  241. /**
  242. * £
  243. */
  244. POUND: "£",
  245. /**
  246. * ¥
  247. */
  248. YEN: "Â¥",
  249. /**
  250. * ¤
  251. */
  252. CURRENCY: "¤",
  253. /**
  254. * ₧
  255. */
  256. PTS: "₧",
  257. /**
  258. * Æ’
  259. */
  260. FUNCTION: "Æ’",
  261. /**
  262. * á
  263. */
  264. A_LOWER_ACCENT: "á",
  265. /**
  266. * í
  267. */
  268. I_LOWER_ACCENT: "í",
  269. /**
  270. * ó
  271. */
  272. O_LOWER_ACCENT: "ó",
  273. /**
  274. * ú
  275. */
  276. U_LOWER_ACCENT: "ú",
  277. /**
  278. * ñ
  279. */
  280. N_LOWER_TILDE: "ñ",
  281. /**
  282. * Ñ
  283. */
  284. N_UPPER_TILDE: "Ñ",
  285. /**
  286. * ª
  287. */
  288. A_SUPER: "ª",
  289. /**
  290. * º
  291. */
  292. O_SUPER: "º",
  293. /**
  294. * ¿
  295. */
  296. UPSIDEDOWN_QUESTION: "¿",
  297. /**
  298. * ⌐
  299. */
  300. SIDEWAYS_L: "⌐",
  301. /**
  302. * ¬
  303. */
  304. NEGATION: "¬",
  305. /**
  306. * ½
  307. */
  308. ONE_HALF: "½",
  309. /**
  310. * ¼
  311. */
  312. ONE_FOURTH: "¼",
  313. /**
  314. * ¡
  315. */
  316. UPSIDEDOWN_EXCLAMATION: "¡",
  317. /**
  318. * «
  319. */
  320. DOUBLE_LEFT: "«",
  321. /**
  322. * »
  323. */
  324. DOUBLE_RIGHT: "»",
  325. /**
  326. * â–‘
  327. */
  328. LIGHT_SHADED_BOX: "â–‘",
  329. /**
  330. * â–’
  331. */
  332. MEDIUM_SHADED_BOX: "â–’",
  333. /**
  334. * â–“
  335. */
  336. DARK_SHADED_BOX: "â–“",
  337. /**
  338. * │
  339. */
  340. VERTICAL_LINE: "│",
  341. /**
  342. * ┤
  343. */
  344. MAZE__SINGLE_RIGHT_T: "┤",
  345. /**
  346. * ┐
  347. */
  348. MAZE_SINGLE_RIGHT_TOP: "┐",
  349. /**
  350. * ┘
  351. */
  352. MAZE_SINGLE_RIGHT_BOTTOM_SMALL: "┘",
  353. /**
  354. * ┌
  355. */
  356. MAZE_SINGLE_LEFT_TOP_SMALL: "┌",
  357. /**
  358. * â””
  359. */
  360. MAZE_SINGLE_LEFT_BOTTOM_SMALL: "â””",
  361. /**
  362. * ├
  363. */
  364. MAZE_SINGLE_LEFT_T: "├",
  365. /**
  366. * â”´
  367. */
  368. MAZE_SINGLE_BOTTOM_T: "â”´",
  369. /**
  370. * ┬
  371. */
  372. MAZE_SINGLE_TOP_T: "┬",
  373. /**
  374. * ┼
  375. */
  376. MAZE_SINGLE_CENTER: "┼",
  377. /**
  378. * ─
  379. */
  380. MAZE_SINGLE_HORIZONTAL_LINE: "─",
  381. /**
  382. * â•¡
  383. */
  384. MAZE_SINGLE_RIGHT_DOUBLECENTER_T: "â•¡",
  385. /**
  386. * â•›
  387. */
  388. MAZE_SINGLE_RIGHT_DOUBLE_BL: "â•›",
  389. /**
  390. * â•¢
  391. */
  392. MAZE_SINGLE_RIGHT_DOUBLE_T: "â•¢",
  393. /**
  394. * â•–
  395. */
  396. MAZE_SINGLE_RIGHT_DOUBLEBOTTOM_TOP: "â•–",
  397. /**
  398. * â••
  399. */
  400. MAZE_SINGLE_RIGHT_DOUBLELEFT_TOP: "â••",
  401. /**
  402. * â•ž
  403. */
  404. MAZE_SINGLE_LEFT_DOUBLE_T: "â•ž",
  405. /**
  406. * ╧
  407. */
  408. MAZE_SINGLE_BOTTOM_DOUBLE_T: "╧",
  409. /**
  410. * ╤
  411. */
  412. MAZE_SINGLE_TOP_DOUBLE_T: "╤",
  413. /**
  414. * â•¥
  415. */
  416. MAZE_SINGLE_TOP_DOUBLECENTER_T: "â•¥",
  417. /**
  418. * ╨
  419. */
  420. MAZE_SINGLE_BOTTOM_DOUBLECENTER_T: "╨",
  421. /**
  422. * ╘
  423. */
  424. MAZE_SINGLE_LEFT_DOUBLERIGHT_BOTTOM: "╘",
  425. /**
  426. * â•’
  427. */
  428. MAZE_SINGLE_LEFT_DOUBLERIGHT_TOP: "â•’",
  429. /**
  430. * â•“
  431. */
  432. MAZE_SINGLE_LEFT_DOUBLEBOTTOM_TOP: "â•“",
  433. /**
  434. * â•™
  435. */
  436. MAZE_SINGLE_LEFT_DOUBLETOP_BOTTOM: "â•™",
  437. /**
  438. * Γ
  439. */
  440. MAZE_SINGLE_LEFT_TOP: "Γ",
  441. /**
  442. * ╜
  443. */
  444. MAZE_SINGLE_RIGHT_BOTTOM: "╜",
  445. /**
  446. * â•Ÿ
  447. */
  448. MAZE_SINGLE_LEFT_CENTER: "â•Ÿ",
  449. /**
  450. * â•«
  451. */
  452. MAZE_SINGLE_DOUBLECENTER_CENTER: "â•«",
  453. /**
  454. * ╪
  455. */
  456. MAZE_SINGLE_DOUBLECROSS_CENTER: "╪",
  457. /**
  458. * â•£
  459. */
  460. MAZE_DOUBLE_LEFT_CENTER: "â•£",
  461. /**
  462. * â•‘
  463. */
  464. MAZE_DOUBLE_VERTICAL: "â•‘",
  465. /**
  466. * â•—
  467. */
  468. MAZE_DOUBLE_RIGHT_TOP: "â•—",
  469. /**
  470. * ╝
  471. */
  472. MAZE_DOUBLE_RIGHT_BOTTOM: "╝",
  473. /**
  474. * â•š
  475. */
  476. MAZE_DOUBLE_LEFT_BOTTOM: "â•š",
  477. /**
  478. * â•”
  479. */
  480. MAZE_DOUBLE_LEFT_TOP: "â•”",
  481. /**
  482. * â•©
  483. */
  484. MAZE_DOUBLE_BOTTOM_T: "â•©",
  485. /**
  486. * ╦
  487. */
  488. MAZE_DOUBLE_TOP_T: "╦",
  489. /**
  490. * â• 
  491. */
  492. MAZE_DOUBLE_LEFT_T: "â• ",
  493. /**
  494. * ═
  495. */
  496. MAZE_DOUBLE_HORIZONTAL: "═",
  497. /**
  498. * ╬
  499. */
  500. MAZE_DOUBLE_CROSS: "╬",
  501. /**
  502. * â–ˆ
  503. */
  504. SOLID_RECTANGLE: "â–ˆ",
  505. /**
  506. * ▌
  507. */
  508. THICK_LEFT_VERTICAL: "▌",
  509. /**
  510. * ▐
  511. */
  512. THICK_RIGHT_VERTICAL: "▐",
  513. /**
  514. * â–„
  515. */
  516. SOLID_SMALL_RECTANGLE_BOTTOM: "â–„",
  517. /**
  518. * â–€
  519. */
  520. SOLID_SMALL_RECTANGLE_TOP: "â–€",
  521. /**
  522. * Φ
  523. */
  524. PHI_UPPER: "Φ",
  525. /**
  526. * ∞
  527. */
  528. INFINITY: "∞",
  529. /**
  530. * ∩
  531. */
  532. INTERSECTION: "∩",
  533. /**
  534. * ≡
  535. */
  536. DEFINITION: "≡",
  537. /**
  538. * ±
  539. */
  540. PLUS_MINUS: "±",
  541. /**
  542. * ≥
  543. */
  544. GT_EQ: "≥",
  545. /**
  546. * ≤
  547. */
  548. LT_EQ: "≤",
  549. /**
  550. * ⌠
  551. */
  552. THEREFORE: "⌠",
  553. /**
  554. * ∵
  555. */
  556. SINCE: "∵",
  557. /**
  558. * ∄
  559. */
  560. DOESNOT_EXIST: "∄",
  561. /**
  562. * ∃
  563. */
  564. EXISTS: "∃",
  565. /**
  566. * ∀
  567. */
  568. FOR_ALL: "∀",
  569. /**
  570. * ⊕
  571. */
  572. EXCLUSIVE_OR: "⊕",
  573. /**
  574. * ⌡
  575. */
  576. BECAUSE: "⌡",
  577. /**
  578. * ÷
  579. */
  580. DIVIDE: "÷",
  581. /**
  582. * ≈
  583. */
  584. APPROX: "≈",
  585. /**
  586. * °
  587. */
  588. DEGREE: "°",
  589. /**
  590. * ∙
  591. */
  592. BOLD_DOT: "∙",
  593. /**
  594. * ·
  595. */
  596. DOT_SMALL: "·",
  597. /**
  598. * √
  599. */
  600. CHECK: "√",
  601. /**
  602. * ✗
  603. */
  604. ITALIC_X: "✗",
  605. /**
  606. * ⁿ
  607. */
  608. SUPER_N: "ⁿ",
  609. /**
  610. * ²
  611. */
  612. SQUARED: "²",
  613. /**
  614. * ³
  615. */
  616. CUBED: "³",
  617. /**
  618. * â– 
  619. */
  620. SOLID_BOX: "â– ",
  621. /**
  622. * ‰
  623. */
  624. PERMILE: "‰",
  625. /**
  626. * ®
  627. */
  628. REGISTERED_TM: "®",
  629. /**
  630. * ©
  631. */
  632. COPYRIGHT: "©",
  633. /**
  634. * â„¢
  635. */
  636. TRADEMARK: "â„¢",
  637. /**
  638. * β
  639. */
  640. BETA: "β",
  641. /**
  642. * γ
  643. */
  644. GAMMA: "γ",
  645. /**
  646. * ζ
  647. */
  648. ZETA: "ζ",
  649. /**
  650. * η
  651. */
  652. ETA: "η",
  653. /**
  654. * ι
  655. */
  656. IOTA: "ι",
  657. /**
  658. * κ
  659. */
  660. KAPPA: "κ",
  661. /**
  662. * λ
  663. */
  664. LAMBDA: "λ",
  665. /**
  666. * ν
  667. */
  668. NU: "ν",
  669. /**
  670. * ξ
  671. */
  672. XI: "ξ",
  673. /**
  674. * ο
  675. */
  676. OMICRON: "ο",
  677. /**
  678. * ρ
  679. */
  680. RHO: "ρ",
  681. /**
  682. * Ï…
  683. */
  684. UPSILON: "Ï…",
  685. /**
  686. * φ
  687. */
  688. CHI_LOWER: "φ",
  689. /**
  690. * χ
  691. */
  692. CHI_UPPER: "χ",
  693. /**
  694. * ψ
  695. */
  696. PSI: "ψ",
  697. /**
  698. * α
  699. */
  700. ALPHA: "α",
  701. /**
  702. * ß
  703. */
  704. ESZETT: "ß",
  705. /**
  706. * π
  707. */
  708. PI: "Ï€",
  709. /**
  710. * Σ
  711. */
  712. SIGMA_UPPER: "Σ",
  713. /**
  714. * σ
  715. */
  716. SIGMA_LOWER: "σ",
  717. /**
  718. * µ
  719. */
  720. MU: "µ",
  721. /**
  722. * Ï„
  723. */
  724. TAU: "Ï„",
  725. /**
  726. * Θ
  727. */
  728. THETA: "Θ",
  729. /**
  730. * Ω
  731. */
  732. OMEGA: "Ω",
  733. /**
  734. * δ
  735. */
  736. DELTA: "δ",
  737. /**
  738. * φ
  739. */
  740. PHI_LOWER: "φ",
  741. /**
  742. * ε
  743. */
  744. EPSILON: "ε"
  745. };
base/date/transforms.js
Coverage100.00 SLOC148 LOC75 Missed0
  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. 62 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. 38 var days, weeks, mod = amount % 5, strt = date.getDay(), adj = 0;
  12. 38 if (!mod) {
  13. 12 days = (amount > 0) ? 5 : -5;
  14. 12 weeks = (amount > 0) ? ((amount - 5) / 5) : ((amount + 5) / 5);
  15. } else {
  16. 26 days = mod;
  17. 26 weeks = parseInt(amount / 5, 10);
  18. }
  19. 38 if (strt === 6 && amount > 0) {
  20. 4 adj = 1;
  21. 34 } else if (strt === 0 && amount < 0) {
  22. // Orig date is Sun / negative increment
  23. // Jump back over Sat
  24. 8 adj = -1;
  25. }
  26. // Get weekday val for the new date
  27. 38 var trgt = strt + days;
  28. // New date is on Sat or Sun
  29. 38 if ((trgt === 0 || trgt === 6) || ((trgt > 6 || trgt <= 0) && strt !== 6 && strt !== 0)) {
  30. 10 adj = (amount > 0) ? 2 : -2;
  31. }
  32. // Increment by number of weeks plus leftover days plus
  33. // weekend adjustments
  34. 38 return [(7 * weeks) + days + adj, "Date", false];
  35. },
  36. year: function addYear(date, amount) {
  37. 36 return [amount, "FullYear", true];
  38. },
  39. week: function addWeek(date, amount) {
  40. 6 return [amount * 7, "Date", false];
  41. },
  42. quarter: function addYear(date, amount) {
  43. 18 return [amount * 3, "Month", true];
  44. },
  45. month: function addYear(date, amount) {
  46. 22 return [amount, "Month", true];
  47. }
  48. };
  49. 1function addTransform(interval, date, amount) {
  50. 234 interval = interval.replace(/s$/, "");
  51. 234 if (addMap.hasOwnProperty(interval)) {
  52. 182 return addMap[interval](date, amount);
  53. }
  54. 52 return [amount, "UTC" + interval.charAt(0).toUpperCase() + interval.substring(1) + "s", false];
  55. }
  56. 1var differenceMap = {
  57. "quarter": function quarterDifference(date1, date2, utc) {
  58. 10 var yearDiff = date2.getFullYear() - date1.getFullYear();
  59. 10 var m1 = date1[utc ? "getUTCMonth" : "getMonth"]();
  60. 10 var m2 = date2[utc ? "getUTCMonth" : "getMonth"]();
  61. // Figure out which quarter the months are in
  62. 10 var q1 = floor(m1 / 3) + 1;
  63. 10 var q2 = floor(m2 / 3) + 1;
  64. // Add quarters for any year difference between the dates
  65. 10 q2 += (yearDiff * 4);
  66. 10 return q2 - q1;
  67. },
  68. "weekday": function weekdayDifference(date1, date2, utc) {
  69. 56 var days = differenceTransform("day", date1, date2, utc), weeks;
  70. 56 var mod = days % 7;
  71. // Even number of weeks
  72. 56 if (mod === 0) {
  73. 14 days = differenceTransform("week", date1, date2, utc) * 5;
  74. } else {
  75. // Weeks plus spare change (< 7 days)
  76. 42 var adj = 0, aDay = date1[utc ? "getUTCDay" : "getDay"](), bDay = date2[utc ? "getUTCDay" : "getDay"]();
  77. 42 weeks = parseInt(days / 7, 10);
  78. // Mark the date advanced by the number of
  79. // round weeks (may be zero)
  80. 42 var dtMark = new Date(date1);
  81. 42 dtMark.setDate(dtMark[utc ? "getUTCDate" : "getDate"]() + (weeks * 7));
  82. 42 var dayMark = dtMark[utc ? "getUTCDay" : "getDay"]();
  83. // Spare change days -- 6 or less
  84. 42 if (days > 0) {
  85. 24 if (aDay === 6 || bDay === 6) {
  86. 6 adj = -1;
  87. 18 } else if (aDay === 0) {
  88. 8 adj = 0;
  89. 10 } else if (bDay === 0 || (dayMark + mod) > 5) {
  90. 4 adj = -2;
  91. }
  92. 18 } else if (days < 0) {
  93. 18 if (aDay === 6) {
  94. 2 adj = 0;
  95. 16 } else if (aDay === 0 || bDay === 0) {
  96. 8 adj = 1;
  97. 8 } else if (bDay === 6 || (dayMark + mod) < 0) {
  98. 4 adj = 2;
  99. }
  100. }
  101. 42 days += adj;
  102. 42 days -= (weeks * 2);
  103. }
  104. 56 return days;
  105. },
  106. year: function (date1, date2) {
  107. 16 return date2.getFullYear() - date1.getFullYear();
  108. },
  109. month: function (date1, date2, utc) {
  110. 10 var m1 = date1[utc ? "getUTCMonth" : "getMonth"]();
  111. 10 var m2 = date2[utc ? "getUTCMonth" : "getMonth"]();
  112. 10 return (m2 - m1) + ((date2.getFullYear() - date1.getFullYear()) * 12);
  113. },
  114. week: function (date1, date2, utc) {
  115. 22 return round(differenceTransform("day", date1, date2, utc) / 7);
  116. },
  117. day: function (date1, date2) {
  118. 106 return 1.1574074074074074e-8 * (date2.getTime() - date1.getTime());
  119. },
  120. hour: function (date1, date2) {
  121. 12 return 2.7777777777777776e-7 * (date2.getTime() - date1.getTime());
  122. },
  123. minute: function (date1, date2) {
  124. 8 return 0.000016666666666666667 * (date2.getTime() - date1.getTime());
  125. },
  126. second: function (date1, date2) {
  127. 6 return 0.001 * (date2.getTime() - date1.getTime());
  128. },
  129. millisecond: function (date1, date2) {
  130. 8 return date2.getTime() - date1.getTime();
  131. }
  132. };
  133. 1function differenceTransform(interval, date1, date2, utc) {
  134. 254 interval = interval.replace(/s$/, "");
  135. 254 return round(differenceMap[interval](date1, date2, utc));
  136. }
  137. 1exports.addTransform = addTransform;
  138. 1exports.differenceTransform = differenceTransform;
base/index.js
Coverage100.00 SLOC13 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"),
  12. require("./characters"));
base/inflections.js
Coverage100.00 SLOC215 LOC93 Missed0
  1. /*
  2. * A port of the Rails/Sequel inflections class
  3. * http://sequel.rubyforge.org/rdoc/classes/Sequel/Inflections.html
  4. */
  5. 1var array = require("./array").array, misc = require("./misc");
  6. 1var comb = exports;
  7. 1var CAMELIZE_CONVERT_REGEXP = /_(.)/g;
  8. 1var DASH = '-';
  9. 1var UNDERSCORE = '_';
  10. 1var UNDERSCORE_CONVERT_REGEXP1 = /([A-Z]+)(\d+|[A-Z][a-z])/g;
  11. 1var UNDERSCORE_CONVERT_REGEXP2 = /(\d+|[a-z])(\d+|[A-Z])/g;
  12. 1var UNDERSCORE_CONVERT_REPLACE = '$1_$2';
  13. 1var PLURALS = [], SINGULARS = [], UNCOUNTABLES = [];
  14. 1var _plural = function (rule, replacement) {
  15. 20 PLURALS.unshift([rule, replacement])
  16. };
  17. 1var _singular = function (rule, replacement) {
  18. 23 SINGULARS.unshift([rule, replacement])
  19. };
  20. /**
  21. * Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
  22. # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
  23. #
  24. # Examples:
  25. # irregular 'octopus', 'octopi'
  26. # irregular 'person', 'people'
  27. * @param singular the singular version
  28. * @param plural the plural version
  29. */
  30. 1var _irregular = function (singular, plural) {
  31. 7 _plural(new RegExp("(" + singular.substr(0, 1) + ")" + singular.substr(1) + "$"), "$1" + plural.substr(1));
  32. 7 _singular(new RegExp("(" + plural.substr(0, 1) + ")" + plural.substr(1) + "$"), "$1" + singular.substr(1));
  33. };
  34. 1var _uncountable = function (words) {
  35. 1 UNCOUNTABLES.push(misc.argsToArray(arguments))
  36. 1 UNCOUNTABLES = array.flatten(UNCOUNTABLES);
  37. };
  38. 1_plural(/$/, 's');
  39. 1_plural(/s$/i, 's');
  40. 1_plural(/(alias|(?:stat|octop|vir|b)us)$/i, '$1es');
  41. 1_plural(/(buffal|tomat)o$/i, '$1oes');
  42. 1_plural(/([ti])um$/i, '$1a');
  43. 1_plural(/sis$/i, 'ses');
  44. 1_plural(/(?:([^f])fe|([lr])f)$/i, '$1$2ves');
  45. 1_plural(/(hive)$/i, '$1s');
  46. 1_plural(/([^aeiouy]|qu)y$/i, '$1ies');
  47. 1_plural(/(x|ch|ss|sh)$/i, '$1es');
  48. 1_plural(/(matr|vert|ind)ix|ex$/i, '$1ices');
  49. 1_plural(/([m|l])ouse$/i, '$1ice');
  50. 1_plural(/^(ox)$/i, "$1en");
  51. 1_singular(/s$/i, '');
  52. 1_singular(/([ti])a$/i, '$1um');
  53. 1_singular(/(analy|ba|cri|diagno|parenthe|progno|synop|the)ses$/i, '$1sis');
  54. 1_singular(/([^f])ves$/i, '$1fe');
  55. 1_singular(/([h|t]ive)s$/i, '$1');
  56. 1_singular(/([lr])ves$/i, '$1f');
  57. 1_singular(/([^aeiouy]|qu)ies$/i, '$1y');
  58. 1_singular(/(m)ovies$/i, '$1ovie');
  59. 1_singular(/(x|ch|ss|sh)es$/i, '$1');
  60. 1_singular(/([m|l])ice$/i, '$1ouse');
  61. 1_singular(/buses$/i, 'bus');
  62. 1_singular(/oes$/i, 'o');
  63. 1_singular(/shoes$/i, 'shoe');
  64. 1_singular(/(alias|(?:stat|octop|vir|b)us)es$/i, '$1');
  65. 1_singular(/(vert|ind)ices$/i, '$1ex');
  66. 1_singular(/matrices$/i, 'matrix');
  67. 1_irregular('person', 'people');
  68. 1_irregular('man', 'men');
  69. 1_irregular('child', 'children');
  70. 1_irregular('sex', 'sexes');
  71. 1_irregular('move', 'moves');
  72. 1_irregular('quiz', 'quizzes');
  73. 1_irregular('testis', 'testes');
  74. 1_uncountable("equipment", "information", "rice", "money", "species", "series", "fish", "sheep", "news");
  75. 1exports.singular = _singular;
  76. 1exports.plural = _plural;
  77. 1exports.uncountable = _uncountable;
  78. /**
  79. * Converts a string to camelcase
  80. *
  81. * @example
  82. * comb.camelize('hello_world') => helloWorld
  83. * comb.camelize('column_name') => columnName
  84. * comb.camelize('columnName') => columnName
  85. * comb.camelize(null) => null
  86. * comb.camelize() => undefined
  87. *
  88. * @param {String} str the string to camelize
  89. * @memberOf comb
  90. * @returns {String} the camelized version of the string
  91. */
  92. 1comb.camelize = function (str) {
  93. 14 var ret = str;
  94. 14 if (!misc.isUndefinedOrNull(str)) {
  95. 12 ret = str.replace(CAMELIZE_CONVERT_REGEXP, function (a, b) {
  96. 8 return b.toUpperCase();
  97. });
  98. }
  99. 14 return ret;
  100. };
  101. /**
  102. * The reverse of camelize. Makes an underscored form from the expression in the string.
  103. *
  104. * @example
  105. * comb.underscore('helloWorld') => hello_world
  106. * comb.underscore('column_name') => column_name
  107. * comb.underscore('columnName') => column_name
  108. * comb.underscore(null) => null
  109. * comb.underscore() => undefined
  110. * @param {String} str The string to underscore
  111. * @memberOf comb
  112. * @returns {String} the underscored version of the string
  113. * */
  114. 1comb.underscore = function (str) {
  115. 12 var ret = str;
  116. 12 if (!misc.isUndefinedOrNull(str)) {
  117. 10 ret = str.replace(UNDERSCORE_CONVERT_REGEXP1, UNDERSCORE_CONVERT_REPLACE)
  118. .replace(UNDERSCORE_CONVERT_REGEXP2, UNDERSCORE_CONVERT_REPLACE)
  119. .replace(DASH, UNDERSCORE).toLowerCase();
  120. }
  121. 12 return ret;
  122. };
  123. /**
  124. * Singularizes and camelizes the string. Also strips out all characters preceding
  125. * and including a period (".").
  126. *
  127. * @example
  128. * comb.classify('egg_and_hams') => "eggAndHam"
  129. * comb.classify('post') => "post"
  130. * comb.classify('schema.post') => "post"
  131. *
  132. * @param {String} str the string to classify
  133. * @memberOf comb
  134. * @returns {String} the classified version of the string
  135. **/
  136. 1comb.classify = function (str) {
  137. 8 var ret = str;
  138. 8 if (!misc.isUndefinedOrNull(str)) {
  139. 6 ret = comb.camelize(comb.singularize(str.replace(/.*\./g, '')));
  140. }
  141. 8 return ret;
  142. };
  143. /**
  144. * Returns the plural form of the word in the string.
  145. *
  146. * @example
  147. * comb.pluralize("post") => "posts"
  148. * comb.pluralize("octopus") => "octopi"
  149. * comb.pluralize("sheep") => "sheep"
  150. * comb.pluralize("words") => "words"
  151. * comb.pluralize("the blue mailman") => "the blue mailmen"
  152. * comb.pluralize("CamelOctopus") => "CamelOctopi"
  153. *
  154. * @param {String} str the string to pluralize
  155. * @memberOf comb
  156. * @returns {String} the pluralized version of the string
  157. **/
  158. 1comb.pluralize = function (str) {
  159. 100 var ret = str;
  160. 100 if (!misc.isUndefinedOrNull(str)) {
  161. 98 if (UNCOUNTABLES.indexOf(str) == -1) {
  162. 96 for (var i in PLURALS) {
  163. 1282 var s = PLURALS[i], rule = s[0], replacement = s[1];
  164. 1282 if ((ret = ret.replace(rule, replacement)) != str) {
  165. 96 break;
  166. }
  167. }
  168. }
  169. }
  170. 100 return ret;
  171. };
  172. /**
  173. * The reverse of pluralize, returns the singular form of a word in a string.
  174. *
  175. * @example
  176. * comb.singularize("posts") => "post"
  177. * comb.singularize("octopi")=> "octopus"
  178. * comb.singularize("sheep") => "sheep"
  179. * comb.singularize("word") => "word"
  180. * comb.singularize("the blue mailmen") => "the blue mailman"
  181. * comb.singularize("CamelOctopi") => "CamelOctopus"
  182. *
  183. * @param {String} str the string to singularize
  184. * @memberOf comb
  185. * @returns {String} the singularized version of the string
  186. * */
  187. 1comb.singularize = function (str) {
  188. 106 var ret = str;
  189. 106 if (!misc.isUndefinedOrNull(str)) {
  190. 104 if (UNCOUNTABLES.indexOf(str) == -1) {
  191. 102 for (var i in SINGULARS) {
  192. 1570 var s = SINGULARS[i], rule = s[0], replacement = s[1];
  193. 1570 if ((ret = ret.replace(rule, replacement)) != str) {
  194. 98 break;
  195. }
  196. }
  197. }
  198. }
  199. 106 return ret;
  200. };
base/misc.js
Coverage100.00 SLOC187 LOC51 Missed0
  1. 1var comb = exports,
  2. arraySlice = Array.prototype.slice;
  3. /**
  4. *
  5. * Converts an arguments object to an array
  6. *
  7. * @example
  8. *
  9. * function test(){
  10. * return comb.argsToArray(arguments);
  11. * }
  12. *
  13. * function testSlice(){
  14. * return comb.argsToArray(arguments, 3);
  15. * }
  16. *
  17. * console.log(test(1,2,3)); //[1,2,3]
  18. * console.log(test(1,2,3,4,5,6)); //[4,5,6]
  19. *
  20. * @function
  21. * @param {Arguments} args the arguments object to convert
  22. * @param {Number} [slice=0] the number of arguments to slice.
  23. * @memberOf comb
  24. * @static
  25. * @returns {Array} array version of the arguments object
  26. */
  27. 1function argsToArray(args, slice) {
  28. 16746 slice = slice || 0;
  29. 16746 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. 12270 var undef, type = typeof obj;
  42. 12270 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. 3334 var undef;
  54. 3334 return obj !== null && obj === undef;
  55. }
  56. /**
  57. * Determins if the obj is not undefined
  58. *
  59. * @param obj the thing to test if it is not undefined
  60. *
  61. * @return {Boolean} true if it is defined false otherwise
  62. * @memberOf comb
  63. * @static
  64. */
  65. 1function isDefined(obj) {
  66. 12 return !isUndefined(obj);
  67. }
  68. /**
  69. * Determines if obj is undefined or null
  70. *
  71. * @param {Anything} obj the thing to test if it is undefined or null
  72. * @returns {Boolean} true if it is undefined or null false otherwise
  73. * @memberOf comb
  74. * @static
  75. */
  76. 1function isUndefinedOrNull(obj) {
  77. 3094 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. 3086 var undef;
  90. 3086 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. 1257 return !isUndefinedOrNull(object) && Object.prototype.toString.call(object) == '[object Arguments]';
  103. }
  104. 1function isInstance(obj, clazz) {
  105. 1241 if (typeof clazz == "function") {
  106. 1240 return obj instanceof clazz;
  107. } else {
  108. 1 return false;
  109. }
  110. }
  111. /**
  112. * Determines if obj is an instance of a particular class
  113. *
  114. * @param {Anything} obj the thing to test if it and instance of a class
  115. * @param {Object} Clazz used to determine if the object is an instance of
  116. *
  117. * @returns {Boolean} true if it is an instance of the clazz false otherwise
  118. * @memberOf comb
  119. * @static
  120. */
  121. 1function isInstanceOf(obj, clazz) {
  122. 1241 return argsToArray(arguments, 1).some(function (c) {
  123. 1241 return isInstance(obj, c);
  124. });
  125. }
  126. 1(function () {
  127. 1 var listeners = [];
  128. 1 var setup = false;
  129. 1 function setupListener() {
  130. 13 if (!setup) {
  131. 1 var orig = process.emit;
  132. 1 process.emit = function (event) {
  133. 1 try {
  134. 1 if (event === 'exit') {
  135. 1 listeners.forEach(function (cb) {
  136. 13 cb();
  137. });
  138. }
  139. } finally {
  140. 1 orig.apply(this, arguments);
  141. }
  142. };
  143. 1 setup = true;
  144. }
  145. }
  146. /**
  147. * Adds listeners to process.exit without having to change setMaxListeners useful if you
  148. * are writing a library and do not want to change core setting.
  149. *
  150. * @param {Funciton} cb funciton to call when process is exiting
  151. * @memberOf comb
  152. * @static
  153. */
  154. 1 function listenForExit(cb) {
  155. 13 setupListener();
  156. 13 listeners.push(cb);
  157. }
  158. 1 comb.listenForExit = listenForExit;
  159. })();
  160. 1comb.argsToArray = argsToArray;
  161. 1comb.isBoolean = isBoolean;
  162. 1comb.isUndefined = isUndefined;
  163. 1comb.isDefined = isDefined;
  164. 1comb.isUndefinedOrNull = isUndefinedOrNull;
  165. 1comb.isNull = isNull;
  166. 1comb.isArguments = isArguments;
  167. 1comb.isInstanceOf = isInstanceOf;
base/number.js
Coverage100.00 SLOC66 LOC10 Missed0
  1. 1var comb = exports;
  2. /**
  3. * Determines if obj is a number
  4. *
  5. * @param {Anything} obj the thing to test if it is a Number
  6. *
  7. * @returns {Boolean} true if it is a number false otherwise
  8. */
  9. 1comb.isNumber = function(obj) {
  10. 2827 var undef;
  11. 2827 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. 24 increment = increment || 1e-20;
  38. 24 var factor = 10 / (10 * (increment || 10));
  39. 24 return (Math.ceil(factor * +number) / factor).toFixed(places) * 1; // Number
  40. },
  41. /**
  42. * Rounds a number to the specified places, rounding up.
  43. *
  44. * @example
  45. *
  46. * comb.number.roundCeil(10.000001, 2); //10.01
  47. * comb.number.roundCeil(10.000002, 5); //10.00001
  48. * comb.number.roundCeil(10.0003, 3); //10.001
  49. * comb.number.roundCeil(10.0004, 2); //10.01
  50. * comb.number.roundCeil(10.0005, 3); //10.001
  51. * comb.number.roundCeil(10.0002, 2); //10.01
  52. *
  53. * @param {Number} num the number to round.
  54. * @param {Number} places the number of places to round to.
  55. */
  56. roundCeil : function(number, places){
  57. 12 return Math.ceil(number * Math.pow(10, places))/Math.pow(10, places);
  58. }
  59. };
base/object.js
Coverage100.00 SLOC575 LOC162 Missed0
  1. 1var comb = exports,
  2. misc = require("./misc.js"),
  3. isUndefinedOrNull = misc.isUndefinedOrNull,
  4. isArguments = misc.isArguments,
  5. pSlice = Array.prototype.slice,
  6. pToString = Object.prototype.toString,
  7. _baseArray, _baseString;
  8. 1function baseArray() {
  9. 20 return _baseArray || (_baseArray = require("./array").array);
  10. }
  11. 1function baseString() {
  12. 20 return _baseString || (_baseString = require("./string"));
  13. }
  14. //taken from node js assert.js
  15. //https://github.com/joyent/node/blob/master/lib/assert.js
  16. 1function _deepEqual(actual, expected) {
  17. // 7.1. All identical values are equivalent, as determined by ===.
  18. 79 if (actual === expected) {
  19. 6 return true;
  20. 73 } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
  21. 2 if (actual.length != expected.length) return false;
  22. 2 for (var i = 0; i < actual.length; i++) {
  23. 6 if (actual[i] !== expected[i]) return false;
  24. }
  25. 2 return true;
  26. // 7.2. If the expected value is a Date object, the actual value is
  27. // equivalent if it is also a Date object that refers to the same time.
  28. 71 } else if (actual instanceof Date && expected instanceof Date) {
  29. 4 return actual.getTime() === expected.getTime();
  30. // 7.3 If the expected value is a RegExp object, the actual value is
  31. // equivalent if it is also a RegExp object with the same source and
  32. // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
  33. 67 } else if (actual instanceof RegExp && expected instanceof RegExp) {
  34. 4 return actual.source === expected.source &&
  35. actual.global === expected.global &&
  36. actual.multiline === expected.multiline &&
  37. actual.lastIndex === expected.lastIndex &&
  38. actual.ignoreCase === expected.ignoreCase;
  39. // 7.4. Other pairs that do not both pass typeof value == 'object',
  40. // equivalence is determined by ==.
  41. 63 } else if (typeof actual != 'object' && typeof expected != 'object') {
  42. 23 return actual == expected;
  43. // 7.5 For all other Object pairs, including Array objects, equivalence is
  44. // determined by having the same number of owned properties (as verified
  45. // with Object.prototype.hasOwnProperty.call), the same set of keys
  46. // (although not necessarily the same order), equivalent values for every
  47. // corresponding key, and an identical 'prototype' property. Note: this
  48. // accounts for both named and indexed properties on Arrays.
  49. } else {
  50. 40 return objEquiv(actual, expected);
  51. }
  52. }
  53. 1function objEquiv(a, b) {
  54. 40 if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
  55. 10 return false;
  56. // an identical 'prototype' property.
  57. 30 if (a.prototype !== b.prototype) return false;
  58. //~~~I've managed to break Object.keys through screwy arguments passing.
  59. // Converting to array solves the problem.
  60. 30 if (isArguments(a)) {
  61. 4 if (!isArguments(b)) {
  62. 2 return false;
  63. }
  64. 2 a = pSlice.call(a);
  65. 2 b = pSlice.call(b);
  66. 2 return _deepEqual(a, b);
  67. }
  68. 26 try {
  69. 26 var ka = Object.keys(a),
  70. kb = Object.keys(b),
  71. key, i;
  72. } catch (e) {//happens when one is a string literal and the other isn't
  73. 2 return false;
  74. }
  75. // having the same number of owned properties (keys incorporates
  76. // hasOwnProperty)
  77. 24 if (ka.length != kb.length)
  78. 4 return false;
  79. //the same set of keys (although not necessarily the same order),
  80. 20 ka.sort();
  81. 20 kb.sort();
  82. //~~~cheap key test
  83. 20 for (i = ka.length - 1; i >= 0; i--) {
  84. 20 if (ka[i] != kb[i])
  85. 4 return false;
  86. }
  87. //equivalent values for every corresponding key, and
  88. //~~~possibly expensive deep test
  89. 16 for (i = ka.length - 1; i >= 0; i--) {
  90. 16 key = ka[i];
  91. 22 if (!_deepEqual(a[key], b[key])) return false;
  92. }
  93. 10 return true;
  94. }
  95. 1function merge(target, source) {
  96. 468 var name, s;
  97. 468 for (name in source) {
  98. 656 s = source[name];
  99. 656 if (!(name in target) || (target[name] !== s)) {
  100. 654 target[name] = s;
  101. }
  102. }
  103. 468 return target;
  104. }
  105. 1function deepMerge(target, source) {
  106. 27 var name, s, t;
  107. 27 for (name in source) {
  108. 37 s = source[name], t = target[name];
  109. 37 if (!_deepEqual(t, s)) {
  110. 37 if (comb.isHash(t) && comb.isHash(s)) {
  111. 8 target[name] = deepMerge(t, s);
  112. 29 } else if (comb.isHash(s)) {
  113. 8 target[name] = deepMerge({}, s);
  114. } else {
  115. 21 target[name] = s;
  116. }
  117. }
  118. }
  119. 27 return target;
  120. }
  121. /**
  122. * Determines if an object is just a hash and not a qualified Object such as Number
  123. *
  124. * @example
  125. * comb.isHash({}) => true
  126. * comb.isHash({1 : 2, a : "b"}) => true
  127. * comb.isHash(new Date()) => false
  128. * comb.isHash(new String()) => false
  129. * comb.isHash(new Number()) => false
  130. * comb.isHash(new Boolean()) => false
  131. * comb.isHash() => false
  132. * comb.isHash("") => false
  133. * comb.isHash(1) => false
  134. * comb.isHash(false) => false
  135. * comb.isHash(true) => false
  136. * @param {Anything} obj the thing to test if it is a hash
  137. *
  138. * @returns {Boolean} true if it is a hash false otherwise
  139. * @memberOf comb
  140. * @static
  141. */
  142. 1function isHash(obj) {
  143. 327 var ret = comb.isObject(obj);
  144. 327 return ret && obj.constructor === Object;
  145. }
  146. /**
  147. * Merges objects together
  148. * NOTE: this function takes a variable number of objects to merge
  149. *
  150. * @example
  151. *
  152. * var myObj = {};
  153. * comb.merge(myObj, {test : true});
  154. *
  155. * myObj.test => true
  156. *
  157. * comb.merge(myObj, {test : false}, {test2 : false}, {test3 : "hello", test4 : "world"});
  158. * myObj.test => false
  159. * myObj.test2 => false
  160. * myObj.test3 => "hello"
  161. * myObj.test4 => "world"
  162. *
  163. *
  164. * @param {Object} obj the object to merge into
  165. * @param {Object} props variable number of objects to merge into the obj
  166. *
  167. * @returns {Object} the merged object
  168. * @memberOf comb
  169. * @name merge
  170. * @static
  171. */
  172. 1function combMerge(obj, props) {
  173. 442 if (!obj) {
  174. 1 obj = {};
  175. }
  176. 442 for (var i = 1, l = arguments.length; i < l; i++) {
  177. 464 merge(obj, arguments[i]);
  178. }
  179. 442 return obj; // Object
  180. }
  181. 1;
  182. /**
  183. * Merges objects together only overriding properties that are different.
  184. * NOTE: this function takes a variable number of objects to merge
  185. *
  186. * @example
  187. *
  188. * var myObj = {my : {cool : {property1 : 1, property2 : 2}}};
  189. * comb.deepMerge(myObj, {my : {cool : {property3 : 3}}});
  190. *
  191. * myObj.my.cool.property1 => 1
  192. * myObj.my.cool.property2 => 2
  193. * myObj.my.cool.property3 => 3
  194. *
  195. *
  196. * @param {Object} obj the object to merge into
  197. * @param {Object} props variable number of objects to merge into the obj
  198. *
  199. * @returns {Object} the merged object
  200. * @memberOf comb
  201. * @name deepMerge
  202. * @static
  203. */
  204. 1function combDeepMerge(obj, props) {
  205. 5 if (!obj) {
  206. 1 obj = {};
  207. }
  208. 5 for (var i = 1, l = arguments.length; i < l; i++) {
  209. 11 deepMerge(obj, arguments[i]);
  210. }
  211. 5 return obj; // Object
  212. }
  213. /**
  214. * Extends the prototype of an object if it exists otherwise it extends the object.
  215. *
  216. * @example
  217. *
  218. * var MyObj = function(){};
  219. * MyObj.prototype.test = true;
  220. * comb.extend(MyObj, {test2 : false, test3 : "hello", test4 : "world"});
  221. *
  222. * var myObj = new MyObj();
  223. *
  224. * myObj.test => true
  225. * myObj.test2 => false
  226. * myObj.test3 => "hello"
  227. * myObj.test4 => "world"
  228. *
  229. * var myObj2 = {};
  230. * myObj2.test = true;
  231. * comb.extend(myObj2, {test2 : false, test3 : "hello", test4 : "world"});
  232. *
  233. * myObj2.test => true
  234. * myObj2.test2 => false
  235. * myObj2.test3 => "hello"
  236. * myObj2.test4 => "world"
  237. *
  238. *
  239. * @param {Object} parent the parent object to extend
  240. * @param {Object} extend the extension object to mixin to the parent
  241. *
  242. * @returns {Object} returns the extended object
  243. * @memberOf comb
  244. * @static
  245. */
  246. 1function extend(parent, extend) {
  247. 4 var proto = parent.prototype || parent;
  248. 4 merge(proto, extend);
  249. 4 return parent;
  250. }
  251. /**
  252. * Determines if obj is an object
  253. *
  254. * @param {Anything} obj the thing to test if it is an object
  255. *
  256. * @returns {Boolean} true if it is an object false otherwise
  257. * @memberOf comb
  258. * @static
  259. */
  260. 1function isObject(obj) {
  261. 4303 var undef;
  262. 4303 return obj !== null && obj !== undef && typeof obj === "object";
  263. }
  264. /**
  265. * Determines if an object is empty
  266. *
  267. * @example
  268. *
  269. * comb.isEmpty({}) => true
  270. * comb.isEmpty({a : 1}) => false
  271. *
  272. * @param object the object to test
  273. * @returns {Boolean} true if the object is empty;
  274. * @memberOf comb
  275. * @static
  276. */
  277. 1function isEmpty(object) {
  278. 9 if (comb.isObject(object)) {
  279. 8 for (var i in object) {
  280. 4 if (object.hasOwnProperty(i)) {
  281. 4 return false;
  282. }
  283. }
  284. }
  285. 5 return true;
  286. }
  287. /**
  288. * Determines if two things are deep equal.
  289. *
  290. * @example
  291. *
  292. * comb.deepEqual({a : 1, b : 2}, {a : 1, b : 2}) => true
  293. * comb.deepEqual({a : 1}, {a : 1, b : 2}) => false
  294. *
  295. * @param o1 the first thing to compare
  296. * @param o3 the second thing to compare
  297. * @return {Boolean}
  298. * @memberOf comb
  299. * @static
  300. */
  301. 1function deepEqual(o1, o2) {
  302. 24 return _deepEqual(o1, o2);
  303. }
  304. 1comb.isHash = isHash;
  305. 1comb.isEmpty = isEmpty;
  306. 1comb.deepEqual = deepEqual;
  307. 1comb.isObject = isObject;
  308. 1comb.extend = extend;
  309. 1comb.deepMerge = combDeepMerge;
  310. 1comb.merge = combMerge;
  311. /**
  312. * Returns a new hash with the specified keys omitted from the hash.
  313. *
  314. * ```
  315. * var obj = {a: "a", b: "b", c: "c"};
  316. *
  317. * //as a function
  318. * comb.hash.omit(obj, ["a", "b"]); //{c: "c"}
  319. * comb.hash.omit(obj, "c"); //{a: "a", b: "b"};
  320. *
  321. * //as a monad
  322. * comb(obj).omit(["a", "b"]); //{c: "c"}
  323. * comb(obj).omit("c"); //{a: "a", b: "b"};
  324. *
  325. * ```
  326. *
  327. * @param {Object} hash The hash to omit values from
  328. * @param {Array|String} omitted the keys to omit.
  329. * @returns {Object} a new Object with the keys omitted
  330. * @memberOf comb.hash
  331. */
  332. 1function omit(hash, omitted) {
  333. 11 if (!isHash(hash)) {
  334. 1 throw new TypeError("expected object got ", pToString.call(hash));
  335. }
  336. 10 if (baseString().isString(omitted)) {
  337. 2 omitted = [omitted];
  338. }
  339. 10 var objKeys = baseArray().difference(Object.keys(hash), omitted), key, ret = {};
  340. 10 for (var i = 0, len = objKeys.length; i < len; ++i) {
  341. 18 key = objKeys[i];
  342. 18 ret[key] = hash[key];
  343. }
  344. 10 return ret;
  345. }
  346. /**
  347. * Returns a new hash with the specified keys omitted from the hash.
  348. *
  349. * ```
  350. * var obj = {a: "a", b: "b", c: "c"};
  351. *
  352. * //as a function
  353. * comb.hash.pick(obj, ["a", "b"]); //{a: "a", b:'b'}
  354. * comb.hash.pick(obj, "c"); //{c: "c"};
  355. *
  356. * //as a monad
  357. * comb(obj).pick(["a", "b"]); //{a: "a", b:'b'}
  358. * comb(obj).pick("c"); //{c: "c"};
  359. *
  360. * ```
  361. *
  362. * @param {Object} hash The hash to pick values from
  363. * @param {Array|String} picked the keys to pick.
  364. * @returns {Object} a new Object with only the keys specified
  365. * @memberOf comb.hash
  366. */
  367. 1function pick(hash, picked) {
  368. 11 if (!isHash(hash)) {
  369. 1 throw new TypeError("expected object got ", pToString.call(hash));
  370. }
  371. 10 if (baseString().isString(picked)) {
  372. 2 picked = [picked];
  373. }
  374. 10 var objKeys = baseArray().intersect(Object.keys(hash), picked), key, ret = {};
  375. 10 for (var i = 0, len = objKeys.length; i < len; ++i) {
  376. 10 key = objKeys[i];
  377. 10 ret[key] = hash[key];
  378. }
  379. 10 return ret;
  380. }
  381. /**
  382. * Loops through each k/v in a hash.
  383. *
  384. * ```
  385. * var obj = {a : "b", c : "d", e : "f"};
  386. * comb(obj).forEach(function(value, key){
  387. * console.log(value, key);
  388. * });
  389. *
  390. * comb.hash.forEach(obj, function(){
  391. * console.log(value, key);
  392. * });
  393. *
  394. * ```
  395. * @param {Object} hash the hash to iterate
  396. * @param {Function} iterator the interator function. Called with (key, value, hash).
  397. * @param {Object} [scope=hash] the scope to invoke the interator in.
  398. * @return {Object} the original hash.
  399. * @memberOf comb.hash
  400. */
  401. 1function forEach(hash, iterator, scope) {
  402. 6 if (!isHash(hash) || typeof iterator !== "function") {
  403. 4 throw new TypeError();
  404. }
  405. 2 var keys = Object.keys(hash), key;
  406. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  407. 6 key = keys[i];
  408. 6 iterator.call(scope || hash, hash[key], key, hash);
  409. }
  410. 2 return hash;
  411. }
  412. /**
  413. * Filters out key/value pairs in an object. Filters out key/value pairs that return a falsey value from the iterator.
  414. *
  415. * ```
  416. * var obj = {a : "b", c : "d", e : "f"};
  417. * comb(obj).filter(function(value, key){
  418. * return value == "b" || key === "e";
  419. * }); //{a : "b", e : "f"};
  420. *
  421. * comb.hash.filter(obj, function(){
  422. * return value == "b" || key === "e";
  423. * }); //{a : "b", e : "f"};
  424. *
  425. * ```
  426. * @param {Object} hash the hash to filter.
  427. * @param {Function} iterator the interator function. Called with (key, value, hash).
  428. * @param {Object} [scope=hash] the scope to invoke the interator in.
  429. * @return {Object} a new object with the values that returned true..
  430. * @memberOf comb.hash
  431. */
  432. 1function filter(hash, iterator, scope) {
  433. 6 if (!isHash(hash) || typeof iterator !== "function") {
  434. 4 throw new TypeError();
  435. }
  436. 2 var keys = Object.keys(hash), key, value, ret = {};
  437. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  438. 6 key = keys[i];
  439. 6 value = hash[key];
  440. 6 if (iterator.call(scope || hash, value, key, hash)) {
  441. 4 ret[key] = value;
  442. }
  443. }
  444. 2 return ret;
  445. }
  446. /**
  447. * Returns the values of a hash.
  448. *
  449. * ```
  450. * var obj = {a : "b", c : "d", e : "f"};
  451. * comb(obj).values(); //["b", "d", "f"]
  452. *
  453. * comb.hash.values(obj); //["b", "d", "f"]
  454. *
  455. * ```
  456. *
  457. * @param {Object} hash the object to retrieve the values of.
  458. * @return {Array} array of values.
  459. * @memberOf comb.hash
  460. */
  461. 1function values(hash) {
  462. 5 if (!isHash(hash)) {
  463. 3 throw new TypeError();
  464. }
  465. 2 var keys = Object.keys(hash), ret = [];
  466. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  467. 6 ret.push(hash[keys[i]]);
  468. }
  469. 2 return ret;
  470. }
  471. /**
  472. * Returns a new hash that is the invert of the hash.
  473. *
  474. * ```
  475. * var obj = {a : "b", c : "d", e : "f"};
  476. * comb(obj).invert(); //{b : "a", d : "c", f : "e"}
  477. *
  478. * comb.hash.invert(obj); //{b : "a", d : "c", f : "e"}
  479. * ```
  480. *
  481. * @param {Object} hash the hash to invert.
  482. * @return {Object} A new hash that is the invert of hash.
  483. * @memberOf comb.hash
  484. */
  485. 1function invert(hash) {
  486. 5 if (!isHash(hash)) {
  487. 3 throw new TypeError();
  488. }
  489. 2 var keys = Object.keys(hash), key, ret = {};
  490. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  491. 6 key = keys[i];
  492. 6 ret[hash[key]] = key;
  493. }
  494. 2 return ret;
  495. }
  496. /**
  497. * Converts a hash to an array.
  498. *
  499. * ```
  500. * var obj = {a : "b", c : "d", e : "f"};
  501. * comb(obj).toArray(); //[["a", "b"], ["c", "d"], ["e", "f"]]
  502. *
  503. * comb.hash.toArray(obj); //[["a", "b"], ["c", "d"], ["e", "f"]]
  504. * ```
  505. *
  506. * @param {Object} hash the hash to convert to an array.
  507. * @return {Array} a two dimensional array representing the hash.
  508. * @memberOf comb.hash
  509. */
  510. 1function toArray(hash) {
  511. 5 if (!isHash(hash)) {
  512. 3 throw new TypeError();
  513. }
  514. 2 var keys = Object.keys(hash), key, ret = [];
  515. 2 for (var i = 0, len = keys.length; i < len; ++i) {
  516. 6 key = keys[i];
  517. 6 ret.push([key, hash[key]]);
  518. }
  519. 2 return ret;
  520. }
  521. /**
  522. * @namespace utilities for working with hases i.e. {}
  523. * @ignoreCode
  524. */
  525. 1comb.hash = {
  526. forEach: forEach,
  527. omit: omit,
  528. pick: pick,
  529. filter: filter,
  530. invert: invert,
  531. values: values,
  532. toArray: toArray
  533. };
base/regexp.js
Coverage100.00 SLOC48 LOC11 Missed0
  1. 1var comb = exports;
  2. /**
  3. * Tests if something is a regular expression.
  4. *
  5. * @example
  6. *
  7. * comb.isRegExp(/hello/); //true
  8. * comb.isRegExp("hello"); //false
  9. *
  10. * @param obj the thing to test.
  11. * @return {Boolean}
  12. * @static
  13. * @memberOf comb
  14. *
  15. */
  16. 1function isRegExp(obj) {
  17. 13 var undef;
  18. 13 return obj !== undef && obj != null && (obj instanceof RegExp);
  19. }
  20. 1comb.isRexExp = isRegExp;
  21. 1comb.isRegExp = isRegExp;
  22. /**
  23. * @namespace Regeular expression utilities
  24. *
  25. */
  26. 1comb.regexp = {
  27. /**@lends comb.regexp*/
  28. /**
  29. * Escapes a string
  30. *
  31. * @param {String} str the string to escape
  32. * @param {String} [except] characters to ignore
  33. *
  34. * @returns {String} the escaped string
  35. */
  36. escapeString:function (str, except) {
  37. 46 return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function (ch) {
  38. 46 if (except && except.indexOf(ch) != -1) {
  39. 15 return ch;
  40. }
  41. 31 return "\\" + ch;
  42. }); // String
  43. }
  44. };
collections/AVLTree.js
Coverage100.00 SLOC203 LOC100 Missed0
  1. 1var define = require("../define").define,
  2. Tree = require("./Tree"),
  3. base = require("../base"),
  4. multiply = base.string.multiply;
  5. 1var abs = Math.abs;
  6. 1var makeNode = function(data) {
  7. 122 return {
  8. data : data,
  9. balance : 0,
  10. left : null,
  11. right : null
  12. }
  13. };
  14. 1var rotateSingle = function(root, dir, otherDir) {
  15. 80 var save = root[otherDir];
  16. 80 root[otherDir] = save[dir];
  17. 80 save[dir] = root;
  18. 80 return save;
  19. };
  20. 1var rotateDouble = function(root, dir, otherDir) {
  21. 15 root[otherDir] = rotateSingle(root[otherDir], otherDir, dir);
  22. 15 return rotateSingle(root, dir, otherDir);
  23. };
  24. 1var adjustBalance = function(root, dir, bal) {
  25. 15 var otherDir = dir == "left" ? "right" : "left";
  26. 15 var n = root[dir], nn = n[otherDir];
  27. 15 if (nn.balance == 0)
  28. 11 root.balance = n.balance = 0;
  29. 4 else if (nn.balance == bal) {
  30. 2 root.balance = -bal;
  31. 2 n.balance = 0;
  32. }
  33. else { /* nn.balance == -bal */
  34. 2 root.balance = 0;
  35. 2 n.balance = bal;
  36. }
  37. 15 nn.balance = 0;
  38. };
  39. 1var insertAdjustBalance = function(root, dir) {
  40. 60 var otherDir = dir == "left" ? "right" : "left";
  41. 60 var n = root[dir];
  42. 60 var bal = dir == "left" ? -1 : +1;
  43. 60 if (n.balance == bal) {
  44. 46 root.balance = n.balance = 0;
  45. 46 root = rotateSingle(root, otherDir, dir);
  46. }
  47. else {
  48. 14 adjustBalance(root, dir, bal);
  49. 14 root = rotateDouble(root, otherDir, dir);
  50. }
  51. 60 return root;
  52. };
  53. 1var removeAdjustBalance = function(root, dir, done) {
  54. 5 var otherDir = dir == "left" ? "right" : "left";
  55. 5 var n = root[otherDir];
  56. 5 var bal = dir == "left" ? -1 : 1;
  57. 5 if (n.balance == -bal) {
  58. 1 root.balance = n.balance = 0;
  59. 1 root = rotateSingle(root, dir, otherDir);
  60. }
  61. 4 else if (n.balance == bal) {
  62. 1 adjustBalance(root, otherDir, -bal);
  63. 1 root = rotateDouble(root, dir, otherDir);
  64. }
  65. else { /* n.balance == 0 */
  66. 3 root.balance = -bal;
  67. 3 n.balance = bal;
  68. 3 root = rotateSingle(root, dir, otherDir);
  69. 3 done.done = true;
  70. }
  71. 5 return root;
  72. };
  73. 1var insert = function(root, data, done, compare) {
  74. 472 if (root == null || root == undefined)
  75. 122 root = makeNode(data);
  76. else {
  77. 350 var dir = compare(data, root.data) == -1 ? "left" : "right";
  78. 350 root[dir] = insert(root[dir], data, done, compare);
  79. 350 if (!done.done) {
  80. /* Update balance factors */
  81. 255 root.balance += dir == "left" ? -1 : 1;
  82. /* Rebalance as necessary and terminate */
  83. 255 if (root.balance == 0)
  84. 21 done.done = true;
  85. 234 else if (abs(root.balance) > 1) {
  86. 60 root = insertAdjustBalance(root, dir);
  87. 60 done.done = true;
  88. }
  89. }
  90. }
  91. 472 return root;
  92. };
  93. 1var remove = function(root, data, done, compare) {
  94. 57 var dir, cmp, save, b;
  95. 57 if (root) {
  96. //Remove node
  97. 57 cmp = compare(data, root.data);
  98. 57 if (cmp === 0) {
  99. // Unlink and fix parent
  100. 26 var l = root.left, r = root.right;
  101. 26 if (!l || !r) {
  102. 18 dir = !l ? "right" : "left";
  103. 18 save = root[dir];
  104. 18 return save;
  105. }
  106. else {
  107. 8 var heir = l, r;
  108. 8 while ((r = heir.right) != null) {
  109. 3 heir = r;
  110. }
  111. 8 root.data = heir.data;
  112. //reset and start searching
  113. 8 data = heir.data;
  114. }
  115. }
  116. 39 dir = compare(root.data, data) == -1 ? "right" : "left";
  117. 39 root[dir] = remove(root[dir], data, done, compare);
  118. 39 if (!done.done) {
  119. /* Update balance factors */
  120. 24 b = (root.balance += (dir == "left" ? 1 : -1));
  121. /* Terminate or rebalance as necessary */
  122. 24 var a = abs(b);
  123. 24 if (a === 1)
  124. 10 done.done = true;
  125. 14 else if (a > 1)
  126. 5 root = removeAdjustBalance(root, dir, done);
  127. }
  128. }
  129. 39 return root;
  130. };
  131. /**
  132. * @ignoreCode
  133. * @class <p>An AVL tree is a self-balancing binary search tree.
  134. * In an AVL tree, the heights of the two child subtrees of any node differ by at most one.
  135. * Lookup, insertion, and deletion all take O(log n) time in both the average and worst cases,
  136. * where n is the number of nodes in the tree prior to the operation.
  137. * Insertions and deletions may require the tree to be rebalanced by one or more tree rotations.</p>
  138. * <p>AVL trees are more rigidly balanced than red-black trees, leading to slower insertion and removal but faster retrieval</p>
  139. *
  140. * <b>Performance</b>
  141. * <table>
  142. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  143. * <tr><td>Space</td><td>O(n)</td><td>O(n)</td></tr>
  144. * <tr><td>Search</td><td>O(log n)</td><td>O(log n)</td></tr>
  145. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  146. * <tr><td>Delete</td><td>O(log n)</td><td>O(log n)</td></tr>
  147. * <table>
  148. * @name AVLTree
  149. * @augments comb.collections.Tree
  150. * @memberOf comb.collections
  151. */
  152. 1module.exports = exports = define(Tree, {
  153. instance : {
  154. /**@lends comb.collections.AVLTree.prototype*/
  155. insert : function(data) {
  156. 122 var done = {done : false};
  157. 122 this.__root = insert(this.__root, data, done, this.compare);
  158. },
  159. remove : function(data) {
  160. 18 this.__root = remove(this.__root, data, {done : false}, this.compare);
  161. },
  162. __printNode : function(node, level) {
  163. 37 var str = [];
  164. 37 if (node == null) {
  165. 19 str.push(multiply('\t', level));
  166. 19 str.push("~");
  167. 19 console.log(str.join(""));
  168. } else {
  169. 18 this.__printNode(node.right, level + 1);
  170. 18 str.push(multiply('\t', level));
  171. 18 str.push(node.data + ":" + node.balance + "\n");
  172. 18 console.log(str.join(""));
  173. 18 this.__printNode(node.left, level + 1);
  174. }
  175. }
  176. }
  177. });
collections/AnderssonTree.js
Coverage100.00 SLOC165 LOC76 Missed0
  1. 1var define = require("../define").define,
  2. Tree = require("./Tree"),
  3. base = require("../base"),
  4. multiply = base.string.multiply;
  5. 1var RED = "red", BLACK = "black";
  6. 1var nil = {level:0, data:null};
  7. 1var makeNode = function (data, level) {
  8. 122 return {
  9. data:data,
  10. level:level,
  11. left:nil,
  12. right:nil
  13. }
  14. };
  15. 1var skew = function (root) {
  16. 384 if (root.level != 0 && root.left.level == root.level) {
  17. 66 var save = root.left;
  18. 66 root.left = save.right;
  19. 66 save.right = root;
  20. 66 root = save;
  21. }
  22. 384 return root;
  23. };
  24. 1var split = function (root) {
  25. 384 if (root.level != 0 && root.right.right.level == root.level) {
  26. 79 var save = root.right;
  27. 79 root.right = save.left;
  28. 79 save.left = root;
  29. 79 root = save;
  30. 79 ++root.level;
  31. }
  32. 384 return root;
  33. };
  34. 1var insert = function (root, data, compare) {
  35. 492 if (root == nil) {
  36. 122 root = makeNode(data, 1);
  37. }
  38. else {
  39. 370 var dir = compare(data, root.data) == -1 ? "left" : "right";
  40. 370 root[dir] = insert(root[dir], data, compare);
  41. 370 root = skew(root);
  42. 370 root = split(root);
  43. }
  44. 492 return root;
  45. };
  46. 1var remove = function (root, data, compare) {
  47. 59 var rLeft, rRight;
  48. 59 if (root != nil) {
  49. 59 var cmp = compare(data, root.data);
  50. 59 if (cmp == 0) {
  51. 26 rLeft = root.left, rRight = root.right;
  52. 26 if (rLeft != nil && rRight != nil) {
  53. 8 var heir = rLeft;
  54. 8 while (heir.right != nil)
  55. 3 heir = heir.right;
  56. 8 root.data = heir.data;
  57. 8 root.left = remove(rLeft, heir.data, compare);
  58. } else {
  59. 18 root = root[rLeft == nil ? "right" : "left"];
  60. }
  61. } else {
  62. 33 var dir = cmp == -1 ? "left" : "right";
  63. 33 root[dir] = remove(root[dir], data, compare);
  64. }
  65. }
  66. 59 if (root != nil) {
  67. 42 var rLevel = root.level;
  68. 42 var rLeftLevel = root.left.level, rRightLevel = root.right.level;
  69. 42 if (rLeftLevel < rLevel - 1 || rRightLevel < rLevel - 1) {
  70. 14 if (rRightLevel > --root.level)
  71. 2 root.right.level = root.level;
  72. 14 root = skew(root);
  73. 14 root = split(root);
  74. }
  75. }
  76. 59 return root;
  77. };
  78. /**
  79. *
  80. * @ignoreCode
  81. * @class <p>Andersson Trees are a version of a balanced Binary tree, while similar to RedBlack Trees the balancing is not as strict.</p>
  82. *
  83. * <b>Performance</b>
  84. * <table>
  85. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  86. * <tr><td>space</td><td>O(n)</td><td>O(n)</td></tr>
  87. * <tr><td>Search</td><td>O(log n)</td><td>O(log n)</td></tr>
  88. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  89. * <tr><td>Delete</td><td>O(log n)</td><td>O(log n)</td></tr>
  90. * <table>
  91. * @name AnderssonTree
  92. * @augments comb.collections.Tree
  93. * @memberOf comb.collections
  94. */
  95. 1module.exports = exports = define(Tree, {
  96. instance:{
  97. /**@lends comb.collections.AnderssonTree.prototype*/
  98. isEmpty:function () {
  99. 4 return this.__root == nil || this._super(arguments);
  100. },
  101. insert:function (data) {
  102. 130 if (this.__root == null) this.__root = nil;
  103. 122 this.__root = insert(this.__root, data, this.compare);
  104. },
  105. remove:function (data) {
  106. 18 this.__root = remove(this.__root, data, this.compare);
  107. },
  108. traverseWithCondition:function (node, order, callback) {
  109. 614 var cont = true;
  110. 614 if (node != nil) {
  111. 317 return this._super(arguments);
  112. }
  113. 297 return cont;
  114. },
  115. traverse:function (node, order, callback) {
  116. 989 if (node != nil) {
  117. 482 this._super(arguments);
  118. }
  119. },
  120. contains:function (value) {
  121. 44 if (this.__root != nil) {
  122. 26 return this._super(arguments);
  123. }
  124. 18 return false;
  125. },
  126. __printNode:function (node, level) {
  127. 37 var str = [];
  128. 37 if (node.data == null || node == null) {
  129. 19 str.push(multiply('\t', level));
  130. 19 str.push("~");
  131. 19 console.log(str.join(""));
  132. } else {
  133. 18 this.__printNode(node.right, level + 1);
  134. 18 str.push(multiply('\t', level));
  135. 18 str.push(node.data + ":" + node.level + "\n");
  136. 18 console.log(str.join(""));
  137. 18 this.__printNode(node.left, level + 1);
  138. }
  139. }
  140. }
  141. });
collections/BinaryTree.js
Coverage100.00 SLOC83 LOC30 Missed0
  1. 1var define = require("../define").define,
  2. Tree = require("./Tree"),
  3. base = require("../base");
  4. /**
  5. *
  6. * @ignoreCode
  7. * @class <p>A Search tree that maintains the following properties</p>
  8. * <ul>
  9. * <li>The left subtree of a node contains only nodes with keys less than the node's key.
  10. * <li>The right subtree of a node contains only nodes with keys greater than the node's key.
  11. * <li>Both the left and right subtrees must also be binary search trees.
  12. * </ul>
  13. *
  14. * <b>Performance</b>
  15. * <table>
  16. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  17. * <tr><td>Space</td><td>O(n)</td><td>O(n)</td></tr>
  18. * <tr><td>Search</td><td>O(log n)</td><td>O(n)</td></tr>
  19. * <tr><td>Insert</td><td>O(log n)</td><td>O(n)</td></tr>
  20. * <tr><td>Delete</td><td>O(log n)</td><td>O(n)</td></tr>
  21. * <table>
  22. * @name BinaryTree
  23. * @augments comb.collections.Tree
  24. * @memberOf comb.collections
  25. */
  26. 1module.exports = exports = define(Tree, {
  27. instance : {
  28. /**@lends comb.collections.BinaryTree.prototype*/
  29. insert : function(data) {
  30. 124 if (this.__root == null) {
  31. 9 return (this.__root = {
  32. data : data,
  33. parent : null,
  34. left : null,
  35. right : null
  36. });
  37. }
  38. 115 var compare = this.compare;
  39. 115 var root = this.__root;
  40. 115 while (root != null) {
  41. 505 var cmp = compare(data, root.data);
  42. 505 if (cmp) {
  43. 503 var leaf = (cmp == -1) ? "left" : "right";
  44. 503 var next = root[leaf];
  45. 503 if (next == null) {
  46. 113 return (root[leaf] = {data : data, parent : root, left : null, right : null});
  47. } else {
  48. 390 root = next;
  49. }
  50. } else {
  51. 2 return;
  52. }
  53. }
  54. },
  55. remove : function(data) {
  56. 19 if (this.__root != null) {
  57. 19 var head = {right : this.__root}, it = head;
  58. 19 var p, f = null;
  59. 19 var dir = "right";
  60. 19 while (it[dir] != null) {
  61. 51 p = it;
  62. 51 it = it[dir];
  63. 51 var cmp = this.compare(data, it.data);
  64. 51 if (!cmp) {
  65. 18 f = it;
  66. }
  67. 51 dir = (cmp == -1 ? "left" : "right");
  68. }
  69. 19 if (f != null) {
  70. 18 f.data = it.data;
  71. 18 p[p.right == it ? "right" : "left"] = it[it.left == null ? "right" : "left"];
  72. }
  73. 19 this.__root = head.right;
  74. }
  75. }
  76. }
  77. });
collections/Collection.js
Coverage100.00 SLOC56 LOC8 Missed0
  1. 1var define = require("../define").define,
  2. base = require("../base");
  3. /**
  4. * @ignoreCode
  5. * @class Base class for all collections
  6. * @name Collection
  7. * @memberOf comb.collections
  8. */
  9. 1define(null, {
  10. instance:{
  11. /**@lends comb.collections.Collection.prototype*/
  12. /**
  13. * Concats two collections
  14. */
  15. concat:function () {
  16. 1 throw new Error("Not Implemented");
  17. },
  18. /**
  19. * Joins two collections
  20. */
  21. join:function () {
  22. 1 throw new Error("Not Implemented");
  23. },
  24. /**
  25. * Slice a portion from a collection
  26. */
  27. slice:function () {
  28. 1 throw new Error("Not Implemented");
  29. },
  30. /**
  31. * Convert a collection to a string
  32. */
  33. toString:function () {
  34. 1 throw new Error("Not Implemented");
  35. },
  36. /**
  37. * Find the index of an item in a collection
  38. */
  39. indexOf:function () {
  40. 1 throw new Error("Not Implemented");
  41. },
  42. /**
  43. * Find the last index of an item in a collection
  44. */
  45. lastIndexOf:function () {
  46. 1 throw new Error("Not Implemented");
  47. }
  48. }
  49. }).as(module);
collections/HashTable.js
Coverage100.00 SLOC378 LOC126 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. Iterable = require("./Iterable"),
  4. base = require("../base");
  5. 1var hashFunction = function (key) {
  6. 100 if (typeof key == "string") {
  7. 26 return key;
  8. 74 } else if (typeof key == "object") {
  9. 44 return key.hashCode ? key.hashCode() : "" + key;
  10. } else {
  11. 30 return "" + key;
  12. }
  13. };
  14. 1var Bucket = define(null, {
  15. instance:{
  16. constructor:function () {
  17. 32 this.__entries = [];
  18. },
  19. pushValue:function (key, value) {
  20. 36 this.__entries.push({key:key, value:value});
  21. 36 return value;
  22. },
  23. remove:function (key) {
  24. 14 var ret = null, map = this.__entries, val;
  25. 14 var i = map.length - 1;
  26. 14 for (; i >= 0; i--) {
  27. 16 if ((val = map[i]) != null && val.key === key) {
  28. 12 map[i] = null;
  29. 12 return val.value;
  30. }
  31. }
  32. 2 return ret;
  33. },
  34. "set":function (key, value) {
  35. 14 var ret = null, map = this.__entries;
  36. 14 var i = map.length - 1;
  37. 14 for (; i >= 0; i--) {
  38. 18 var val = map[i];
  39. 18 if (val && key === val.key) {
  40. 6 val.value = value;
  41. 6 ret = value;
  42. 6 break;
  43. }
  44. }
  45. 14 if (!ret) {
  46. 8 map.push({key:key, value:value});
  47. }
  48. 14 return ret;
  49. },
  50. find:function (key) {
  51. 34 var ret = null, map = this.__entries, val;
  52. 34 var i = map.length - 1;
  53. 34 for (; i >= 0; i--) {
  54. 43 val = map[i];
  55. 43 if (val && key === val.key) {
  56. 26 ret = val.value;
  57. 26 break;
  58. }
  59. }
  60. 34 return ret;
  61. },
  62. getEntrySet:function (arr) {
  63. 78 var map = this.__entries, l = map.length;
  64. 78 if (l) {
  65. 78 for (var i = 0; i < l; i++) {
  66. 88 var e = map[i];
  67. 88 if (e) {
  68. 80 arr.push(e);
  69. }
  70. }
  71. }
  72. },
  73. getKeys:function (arr) {
  74. 21 var map = this.__entries, l = map.length;
  75. 21 if (l) {
  76. 21 for (var i = 0; i < l; i++) {
  77. 24 var e = map[i];
  78. 24 if (e) {
  79. 8 arr.push(e.key);
  80. }
  81. }
  82. }
  83. 21 return arr;
  84. },
  85. getValues:function (arr) {
  86. 14 var map = this.__entries, l = map.length;
  87. 14 if (l) {
  88. 14 for (var i = 0; i < l; i++) {
  89. 16 var e = map[i];
  90. 16 if (e) {
  91. 8 arr.push(e.value);
  92. }
  93. }
  94. }
  95. 14 return arr;
  96. }
  97. }
  98. });
  99. /**
  100. * @ignoreCode
  101. * @class <p>Implementation of a HashTable for javascript.
  102. * This HashTable implementation allows one to use anything as a key.
  103. * </p>
  104. * <b>NOTE: THIS IS ~ 3 times slower than javascript native objects</b>
  105. *
  106. * <p> A use case for this collection is when one needs to store items in which the key will not be a string, or number</p>
  107. *
  108. * @name HashTable
  109. * @augments comb.collections.Collection
  110. * @memberOf comb.collections
  111. *
  112. * @property {Array} keys all keys contained in the table
  113. * @property {Array} values all values contained in the table
  114. * @property {Array} entrySet an array of objects. Each object contains a key, and value property.
  115. */
  116. 1define([Collection, Iterable], {
  117. instance:{
  118. /**@lends comb.collections.HashTable.prototype*/
  119. constructor:function () {
  120. 13 this.__map = {};
  121. },
  122. __entrySet:function () {
  123. 14 var ret = [], es = [];
  124. 14 for (var i in this.__map) {
  125. 78 this.__map[i].getEntrySet(ret);
  126. }
  127. 14 return ret;
  128. },
  129. /**
  130. * Put a key, value pair into the table
  131. *
  132. * <b>NOTE :</b> the collection will not check if the key previously existed.
  133. *
  134. * @param {Anything} key the key to look up the object.
  135. * @param {Anything} value the value that corresponds to the key.
  136. *
  137. * @returns the value
  138. */
  139. put:function (key, value) {
  140. 29 var hash = hashFunction(key);
  141. 29 var bucket = null;
  142. 29 if ((bucket = this.__map[hash]) == null) {
  143. 25 bucket = (this.__map[hash] = new Bucket());
  144. }
  145. 29 bucket.pushValue(key, value);
  146. 29 return value;
  147. },
  148. /**
  149. * Remove a key value pair from the table.
  150. *
  151. * @param key the key of the key value pair to remove.
  152. *
  153. * @returns the removed value.
  154. */
  155. remove:function (key) {
  156. 16 var hash = hashFunction(key), ret = null;
  157. 16 var bucket = this.__map[hash];
  158. 16 if (bucket) {
  159. 14 ret = bucket.remove(key);
  160. }
  161. 16 return ret;
  162. },
  163. /**
  164. * Get the value corresponding to the key.
  165. *
  166. * @param key the key used to look up the value
  167. *
  168. * @returns null if not found, or the value.
  169. */
  170. "get":function (key) {
  171. 26 var hash = hashFunction(key), ret = null;
  172. 26 var bucket = null;
  173. 26 if ((bucket = this.__map[hash]) != null) {
  174. 26 ret = bucket.find(key);
  175. }
  176. 26 return ret;
  177. },
  178. /**
  179. * Set the value of a previously existing key,value pair or create a new entry.
  180. *
  181. * @param key the key to be be used
  182. * @param value the value to be set
  183. *
  184. * @returns the value.
  185. */
  186. "set":function (key, value) {
  187. 21 var hash = hashFunction(key), ret = null, bucket = null, map = this.__map;
  188. 21 if ((bucket = map[hash]) != null) {
  189. 14 ret = bucket.set(key, value);
  190. } else {
  191. 7 ret = (map[hash] = new Bucket()).pushValue(key, value);
  192. }
  193. 21 return ret;
  194. },
  195. /**
  196. * Tests if the table contains a particular key
  197. * @param key the key to test
  198. *
  199. * @returns {Boolean} true if it exitsts false otherwise.
  200. */
  201. contains:function (key) {
  202. 8 var hash = hashFunction(key), ret = false;
  203. 8 var bucket = null;
  204. 8 if ((bucket = this.__map[hash]) != null) {
  205. 8 ret = bucket.find(key) != null;
  206. }
  207. 8 return ret;
  208. },
  209. /**
  210. * Returns a new HashTable containing the values of this HashTable, and the other table.
  211. * </br>
  212. * <b> DOES NOT CHANGE THE ORIGINAL!</b>
  213. * @param {comb.collections.HashTable} hashTable the hash table to concat with this.
  214. *
  215. * @returns {comb.collections.HashTable} a new HashTable containing all values from both tables.
  216. */
  217. concat:function (hashTable) {
  218. 2 if (hashTable instanceof this._static) {
  219. 1 var ret = new this._static();
  220. 1 var otherEntrySet = hashTable.entrySet.concat(this.entrySet);
  221. 1 for (var i = otherEntrySet.length - 1; i >= 0; i--) {
  222. 4 var e = otherEntrySet[i];
  223. 4 ret.put(e.key, e.value);
  224. }
  225. 1 return ret;
  226. } else {
  227. 1 throw new TypeError("When joining hashtables the joining arg must be a HashTable");
  228. }
  229. },
  230. /**
  231. * Creates a new HashTable containg values that passed the filtering function.
  232. *
  233. * @param {Function} cb Function to callback with each item, the first aruguments is an object containing a key and value field
  234. * @param {Object} scope the scope to call the function.
  235. *
  236. * @returns {comb.collections.HashTable} the HashTable containing the values that passed the filter.
  237. */
  238. filter:function (cb, scope) {
  239. 1 var es = this.__entrySet(), ret = new this._static();
  240. 1 es = es.filter.apply(es, arguments);
  241. 1 for (var i = es.length - 1; i >= 0; i--) {
  242. 4 var e = es[i];
  243. 4 ret.put(e.key, e.value);
  244. }
  245. 1 return ret;
  246. },
  247. /**
  248. * Loop through each value in the hashtable
  249. *
  250. * @param {Function} cb the function to call with an object containing a key and value field
  251. * @param {Object} scope the scope to call the funciton in
  252. */
  253. forEach:function (cb, scope) {
  254. 1 var es = this.__entrySet(), l = es.length, f = cb.bind(scope || this);
  255. 1 es.forEach.apply(es, arguments);
  256. },
  257. /**
  258. * Determines if every item meets the condition returned by the callback.
  259. *
  260. * @param {Function} cb Function to callback with each item, the first aruguments is an object containing a key and value field
  261. * @param {Object} [scope=this] scope to call the function in
  262. *
  263. * @returns {Boolean} True if every item passed false otherwise
  264. */
  265. every:function () {
  266. 2 var es = this.__entrySet();
  267. 2 return es.every.apply(es, arguments);
  268. },
  269. /**
  270. * Loop through each value in the hashtable, collecting the value returned by the callback function.
  271. * @param {Function} cb Function to callback with each item, the first aruguments is an object containing a key and value field
  272. * @param {Object} [scope=this] scope to call the function in
  273. *
  274. * @returns {Array} an array containing the mapped values.
  275. */
  276. map:function () {
  277. 1 var es = this.__entrySet(), ret = new this._static();
  278. 1 return es.map.apply(es, arguments);
  279. },
  280. /**
  281. * Determines if some items meet the condition returned by the callback.
  282. *
  283. * @param {Function} cb Function to callback with each item, the first aruguments is an object containing a key and value field
  284. * @param {Object} [scope=this] scope to call the function in
  285. *
  286. * @returns {Boolean} True if some items passed false otherwise
  287. */
  288. some:function () {
  289. 2 var es = this.__entrySet();
  290. 2 return es.some.apply(es, arguments);
  291. },
  292. /**
  293. * Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value.
  294. *
  295. * @param {Function} callback Function to execute on each value in the array.
  296. * @param initialValue Value to use as the first argument to the first call of the callback..
  297. */
  298. reduce:function () {
  299. 1 var es = this.__entrySet();
  300. 1 return es.reduce.apply(es, arguments);
  301. },
  302. /**
  303. * Apply a function against an accumulator and each value of the array (from right-to-left) as to reduce it to a single value.
  304. *
  305. * @param {Function} callback Function to execute on each value in the array.
  306. * @param initialValue Value to use as the first argument to the first call of the callback..
  307. */
  308. reduceRight:function () {
  309. 1 var es = this.__entrySet();
  310. 1 return es.reduceRight.apply(es, arguments);
  311. },
  312. /**
  313. * Clears out all items from the table.
  314. */
  315. clear:function () {
  316. 1 this.__map = {};
  317. },
  318. getters:{
  319. keys:function () {
  320. 8 var ret = [], es = [];
  321. 8 for (var i in this.__map) {
  322. 21 this.__map[i].getKeys(ret);
  323. }
  324. 8 return ret;
  325. },
  326. values:function () {
  327. 6 var ret = [], es = [];
  328. 6 for (var i in this.__map) {
  329. 14 this.__map[i].getValues(ret);
  330. }
  331. 6 return ret;
  332. },
  333. entrySet:function () {
  334. 5 return this.__entrySet();
  335. },
  336. isEmpty:function () {
  337. 2 return this.keys.length == 0;
  338. }
  339. }
  340. }
  341. }).as(module);
collections/Heap.js
Coverage100.00 SLOC218 LOC62 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. base = require("../base");
  4. 1var padding = function(char, numSpaces) {
  5. 9 var ret = [];
  6. 9 for (var i = 0; i < numSpaces; i++)
  7. 16 ret.push(char);
  8. 9 return ret.join("");
  9. };
  10. 1module.exports = exports = define(Collection, {
  11. instance : {
  12. /**@lends comb.collections.Heap.prototype*/
  13. __getParentIndex : function(index) {
  14. 192 return Math.floor((index - 1) / 2);
  15. },
  16. __getLeftChildIndex : function(index) {
  17. 28 return (index * 2) + 1;
  18. },
  19. __getRightChildIndex : function(index) {
  20. 28 return (index * 2) + 2;
  21. },
  22. __makeNode : function(key, value) {
  23. 137 return {key : key, value : value};
  24. },
  25. /**
  26. * Base class for Heap Implementations.
  27. *
  28. *
  29. * @constructs
  30. * @augments comb.collections.Collection
  31. * @memberOf comb.collections
  32. *
  33. * @property {Number} count the current number of elements.
  34. * @property {Array} keys the keys of all items in the heap.
  35. * @property {Array} values the values contained in the heap.
  36. * @property {Boolean} isEmpty true if the Heap is empty.
  37. */
  38. constructor : function() {
  39. 32 this.__heap = [];
  40. },
  41. /**
  42. * Insert a key value into the key
  43. * @param key
  44. * @param value
  45. */
  46. insert : function(key, value) {
  47. 138 if (!base.isString(key)) {
  48. 137 var l = this.__heap.push(this.__makeNode(key, value));
  49. 137 this.__upHeap(l - 1);
  50. } else {
  51. 1 throw TypeError("Invalid key");
  52. }
  53. },
  54. /**
  55. * Removes the root from the heap
  56. *
  57. * @returns the value of the root
  58. */
  59. remove : function() {
  60. 48 var ret = undefined, heap = this.__heap, l = heap.length;
  61. 48 if (l) {
  62. 48 ret = heap[0];
  63. 48 if (l == 1) {
  64. 12 heap.length = 0;
  65. } else {
  66. 36 heap[0] = heap.pop();
  67. 36 this.__downHeap(0);
  68. }
  69. }
  70. 48 return ret ? ret.value : ret;
  71. },
  72. /**
  73. * Gets he value of the root node with out removing it.
  74. *
  75. * @returns the value of the root
  76. */
  77. peek : function() {
  78. 33 var ret = undefined, heap = this.__heap, l = heap.length;
  79. 33 if (l) {
  80. 30 ret = heap[0];
  81. }
  82. 33 return ret ? ret.value : ret;
  83. },
  84. /**
  85. * Gets the key of the root node without removing it.
  86. *
  87. * @returns the key of the root
  88. */
  89. peekKey : function() {
  90. 6 var ret = undefined, heap = this.__heap, l = heap.length;
  91. 6 if (l) {
  92. 4 ret = heap[0];
  93. }
  94. 6 return ret ? ret.key : ret;
  95. },
  96. /**
  97. * Perform the heapify operation after the an
  98. * item as been added to the bottom of the heap.
  99. *
  100. * @param index the index in which the new item was added
  101. */
  102. __upHeap : function(index) {
  103. 1 throw Error("NOT IMPLEMENTED");
  104. },
  105. /**
  106. * Heapify the heap after the root has been removed
  107. *
  108. * @param index the index of the root
  109. */
  110. __downHeap : function(index) {
  111. 1 throw Error("NOT IMPLEMENTED");
  112. },
  113. /**
  114. *
  115. * Determine if the heap contains a particular key.
  116. *
  117. * @param key key to test.
  118. *
  119. * @returns {Boolean} true if the key is contained in this heap.
  120. */
  121. containsKey : function(key) {
  122. 15 var heap = this.__heap;
  123. 15 for (var i = heap.length - 1; i >= 0; i--) {
  124. 42 if (heap[i].key == key) {
  125. 12 return true;
  126. }
  127. }
  128. 3 return false;
  129. },
  130. /**
  131. *
  132. * Determine if the heap contains a particular value.
  133. *
  134. * @param value value to test.
  135. *
  136. * @returns {Boolean} true if the value is contained in this heap.
  137. */
  138. containsValue : function(value) {
  139. 15 var heap = this.__heap;
  140. 15 for (var i = heap.length - 1; i >= 0; i--) {
  141. 42 if (heap[i].value == value) {
  142. 12 return true;
  143. }
  144. }
  145. 3 return false;
  146. },
  147. /**
  148. * Empty the heap.
  149. */
  150. clear : function() {
  151. 9 this.__heap.length = 0;
  152. },
  153. __printNode : function(index, level) {
  154. //console.log(level);
  155. 9 var str = [], node = this.__heap[index];
  156. 9 if (node == null || node == undefined) {
  157. 5 str.push(padding('\t', level));
  158. 5 str.push("~");
  159. 5 console.log(str.join(""));
  160. } else {
  161. 4 this.__printNode(this.__getRightChildIndex(index), level + 1);
  162. 4 str.push(padding('\t', level));
  163. 4 str.push(node.key + " : " + node.value + "\n");
  164. 4 console.log(str.join(""));
  165. 4 this.__printNode(this.__getLeftChildIndex(index), level + 1);
  166. }
  167. },
  168. /**
  169. * Print the heap.
  170. */
  171. print : function() {
  172. 1 this.__printNode(0, 0);
  173. },
  174. getters : {
  175. count : function() {
  176. 12 return this.__heap.length;
  177. },
  178. keys : function() {
  179. 3 return this.__heap.map(function(n) {
  180. 12 return n.key;
  181. });
  182. },
  183. values : function() {
  184. 3 return this.__heap.map(function(n) {
  185. 12 return n.value;
  186. });
  187. },
  188. isEmpty : function() {
  189. 18 return this.__heap.length == 0;
  190. }
  191. }
  192. }
  193. });
collections/Iterable.js
Coverage100.00 SLOC63 LOC9 Missed0
  1. 1var define = require("../define").define,
  2. base = require("../base");
  3. /**
  4. * @ignoreCode
  5. * @class Base class for all collections
  6. * @name Iterable
  7. * @memberOf comb.collections
  8. */
  9. 1define(null, {
  10. instance:{
  11. /**@lends comb.collections.Iterable.prototype*/
  12. /**
  13. * Filter items from a collection
  14. */
  15. filter:function () {
  16. 1 throw new Error("Not Implemented");
  17. },
  18. /**
  19. * Loop through the items in a collection
  20. */
  21. forEach:function () {
  22. 1 throw new Error("Not Implemented");
  23. },
  24. /**
  25. * Determine if every item in a collection meets the criteria
  26. */
  27. every:function () {
  28. 1 throw new Error("Not Implemented");
  29. },
  30. /**
  31. * Map every item in a collection
  32. */
  33. map:function () {
  34. 1 throw new Error("Not Implemented");
  35. },
  36. /**
  37. * Determing if some items in a colleciton meet the criteria
  38. */
  39. some:function () {
  40. 1 throw new Error("Not Implemented");
  41. },
  42. /**
  43. * Reduce a collection
  44. */
  45. reduce:function () {
  46. 1 throw new Error("Not Implemented");
  47. },
  48. /**
  49. * Reduce a collection starting from the right most position
  50. */
  51. reduceRight:function () {
  52. 1 throw new Error("Not Implemented");
  53. }
  54. }
  55. }).as(module);
collections/MaxHeap.js
Coverage100.00 SLOC66 LOC25 Missed0
  1. 1var define = require("../define").define,
  2. Heap = require("./Heap"),
  3. base = require("../base");
  4. /**
  5. * @ignoreCode
  6. *
  7. * @class <p> Max Heap implementation, lowest value in heap is always at the root.</p>
  8. * </br>
  9. * <b>Performance</b>
  10. * <table>
  11. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  12. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  13. * <tr><td>Remove</td><td>O(log n)</td><td>O(log n)</td></tr>
  14. * <tr><td>Peek</td><td>O(1)</td><td>O(1)</td></tr>
  15. * <tr><td>Contains</td><td>O(n)</td><td>O(n)</td></tr>
  16. * <table>
  17. * @name MaxHeap
  18. * @augments comb.collections.Heap
  19. * @memberOf comb.collections
  20. */
  21. 1exports = module.exports = define(Heap, {
  22. instance : {
  23. __upHeap : function(index) {
  24. 48 var heap = this.__heap;
  25. 48 var node = heap[index];
  26. 48 while (index >= 0) {
  27. 84 var parentIndex = this.__getParentIndex(index), parent = heap[parentIndex];
  28. 84 if (parent && parent.key < node.key) {
  29. 36 heap[index] = parent;
  30. 36 index = parentIndex;
  31. } else {
  32. 48 break;
  33. }
  34. }
  35. 48 heap[index] = node;
  36. },
  37. __downHeap : function(index) {
  38. 12 var heap = this.__heap;
  39. 12 var node = heap[index], length = heap.length;
  40. 12 while (index < Math.floor(length / 2)) {
  41. 8 var leftIndex = this.__getLeftChildIndex(index),
  42. rightIndex = this.__getRightChildIndex(index), left = heap[leftIndex], right = heap[rightIndex], child, childIndex;
  43. 8 if (rightIndex < length && right.key < left.key) {
  44. 4 childIndex = leftIndex;
  45. 4 child = left
  46. } else {
  47. 4 childIndex = leftIndex;
  48. 4 child = heap[leftIndex];
  49. }
  50. 8 if(child.key > node.key){
  51. 5 heap[index] = child;
  52. 5 index = childIndex;
  53. }else{
  54. 3 break;
  55. }
  56. }
  57. 12 heap[index] = node;
  58. }
  59. }
  60. });
collections/MinHeap.js
Coverage100.00 SLOC65 LOC26 Missed0
  1. 1var define = require("../define").define,
  2. Heap = require("./Heap"),
  3. base = require("../base");
  4. 1var floor = Math.floor, MinHeap;
  5. /**
  6. * @class <p> Min Heap implementation, lowest value in heap is always at the root.</p>
  7. * </br>
  8. * <b>Performance</b>
  9. * <table>
  10. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  11. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  12. * <tr><td>Remove</td><td>O(log n)</td><td>O(log n)</td></tr>
  13. * <tr><td>Peek</td><td>O(1)</td><td>O(1)</td></tr>
  14. * <tr><td>Contains</td><td>O(n)</td><td>O(n)</td></tr>
  15. * <table>
  16. * @name MinHeap
  17. * @augments comb.collections.Heap
  18. * @memberOf comb.collections
  19. * @ignoreCode
  20. */
  21. 1module.exports = exports = define(Heap, {
  22. instance : {
  23. __upHeap : function(index) {
  24. 88 var heap = this.__heap;
  25. 88 var node = heap[index], key = node.key, gpi = this.__getParentIndex;
  26. 88 while (index >= 0) {
  27. 108 var parentIndex = gpi(index), parent = heap[parentIndex];
  28. 108 if (parent && parent.key > key) {
  29. 20 heap[index] = parent;
  30. 20 index = parentIndex;
  31. } else {
  32. 88 break;
  33. }
  34. }
  35. 88 heap[index] = node;
  36. },
  37. __downHeap : function(index) {
  38. 24 var heap = this.__heap;
  39. 24 var node = heap[index], key = node.key, length = heap.length, max = floor(length / 2), glci = this.__getLeftChildIndex, grci = this.__getRightChildIndex;
  40. 24 while (index < max) {
  41. 16 var leftIndex = glci(index),
  42. rightIndex = grci(index), left = heap[leftIndex], right = heap[rightIndex], child, childIndex;
  43. 16 if (rightIndex < length && right.key < left.key) {
  44. 2 childIndex = rightIndex;
  45. 2 child = right;
  46. } else {
  47. 14 childIndex = leftIndex;
  48. 14 child = left;
  49. }
  50. 16 if (child.key < key) {
  51. 10 heap[index] = child;
  52. 10 index = childIndex;
  53. } else {
  54. 6 break;
  55. }
  56. }
  57. 24 heap[index] = node;
  58. }
  59. }
  60. });
collections/Pool.js
Coverage100.00 SLOC161 LOC50 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. Queue = require("./Queue"),
  4. base = require("../base");
  5. /**
  6. * @class Base class for a pool.
  7. *
  8. * @name Pool
  9. * @memberOf comb.collections
  10. *
  11. * @property {Number} count the total number of objects in the pool, including free and in use objects.
  12. * @property {Number} freeCount the number of free objects in this pool.
  13. * @property {Number} inUseCount the number of objects in use in this pool.
  14. * @property {Number} [minObjects=0] the minimum number of objects this pool should contain.
  15. * @property {Number} [maxObjects=1] the maximum number of objects this pool should contain
  16. * @ignoreCode
  17. */
  18. 1exports = module.exports = define(null, {
  19. instance: {
  20. /**@lends comb.collections.Pool.prototype*/
  21. __minObjects: 0,
  22. __maxObjects: 1,
  23. constructor: function (options) {
  24. 5 options = options || {};
  25. 5 this.__freeObjects = new Queue();
  26. 5 this.__inUseObjects = [];
  27. 5 this.__minObjects = options.minObjects || 0;
  28. 5 this.__maxObjects = options.maxObjects || 1;
  29. 5 this.minObjects = this.__minObjects;
  30. 4 this.maxObjects = this.__maxObjects;
  31. },
  32. /**
  33. * Retrieves an object from this pool.
  34. * `
  35. * @return {*} an object to contained in this pool
  36. */
  37. getObject: function () {
  38. 19 var ret = undefined;
  39. 19 if (this.count <= this.__maxObjects && this.freeCount > 0) {
  40. 2 ret = this.__freeObjects.dequeue();
  41. 2 this.__inUseObjects.push(ret);
  42. 17 } else if (this.count < this.__maxObjects) {
  43. 12 ret = this.createObject();
  44. 12 this.__inUseObjects.push(ret);
  45. }
  46. 19 return ret;
  47. },
  48. /**
  49. * Returns an object to this pool. The object is validated before it is returned to the pool,
  50. * if the validation fails then it is removed from the pool;
  51. * @param {*} obj the object to return to the pool
  52. */
  53. returnObject: function (obj) {
  54. 8 var index;
  55. 8 if (this.validate(obj) && this.count <= this.__maxObjects && (index = this.__inUseObjects.indexOf(obj)) > -1) {
  56. 7 this.__freeObjects.enqueue(obj);
  57. 7 this.__inUseObjects.splice(index, 1);
  58. } else {
  59. 1 this.removeObject(obj);
  60. }
  61. },
  62. /**
  63. * Removes an object from the pool, this can be overriden to provide any
  64. * teardown of objects that needs to take place.
  65. *
  66. * @param {*} obj the object that needs to be removed.
  67. *
  68. * @return {*} the object removed.
  69. */
  70. removeObject: function (obj) {
  71. 2 var index;
  72. 2 if (this.__freeObjects.contains(obj)) {
  73. 1 this.__freeObjects.remove(obj);
  74. 1 } else if ((index = this.__inUseObjects.indexOf(obj)) > -1) {
  75. 1 this.__inUseObjects.splice(index, 1);
  76. }
  77. //otherwise its not contained in this pool;
  78. 2 return obj;
  79. },
  80. /**
  81. * Validates an object in this pool.
  82. * </br>
  83. * <b>THIS SHOULD BE OVERRIDDEN TO VALIDATE</b>
  84. *
  85. * @param {*} obj the object to validate.
  86. */
  87. validate: function (obj) {
  88. 8 return true;
  89. },
  90. /**
  91. * Creates a new object for this pool.
  92. * * </br>
  93. * <b>THIS SHOULD BE OVERRIDDEN TO ADD THE CORRECT TYPE OF OBJECT</b>
  94. *
  95. * @return {Object} be default just creates an object.
  96. */
  97. createObject: function () {
  98. 16 return {};
  99. },
  100. setters: {
  101. minObjects: function (l) {
  102. 6 if (l <= this.__maxObjects) {
  103. 5 this.__minObjects = l;
  104. 5 var i;
  105. 5 if ((i = this.count) < l) {
  106. 1 while (i++ < l) {
  107. 4 this.__freeObjects.enqueue(this.createObject());
  108. }
  109. }
  110. } else {
  111. 1 throw "comb.collections.Pool : minObjects cannot be greater than maxObjects.";
  112. }
  113. },
  114. maxObjects: function (l) {
  115. 9 if (l >= this.__minObjects) {
  116. 8 this.__maxObjects = l;
  117. 8 var i = this.count, j = this.freeCount, fo = this.__freeObjects;
  118. 8 while (i > l && j >= 0) {
  119. 1 this.removeObject(fo.dequeue());
  120. 1 j--;
  121. 1 i--;
  122. }
  123. } else {
  124. 1 throw "comb.collections.Pool : maxObjects cannot be less than maxObjects.";
  125. }
  126. }
  127. },
  128. getters: {
  129. freeCount: function () {
  130. 41 return this.__freeObjects.count;
  131. },
  132. inUseCount: function () {
  133. 14 return this.__inUseObjects.length;
  134. },
  135. count: function () {
  136. 71 return this.__freeObjects.count + this.__inUseObjects.length;
  137. },
  138. minObjects: function () {
  139. 2 return this.__minObjects;
  140. },
  141. maxObjects: function () {
  142. 1 return this.__maxObjects;
  143. }
  144. }
  145. }
  146. });
collections/PriorityQueue.js
Coverage100.00 SLOC41 LOC6 Missed0
  1. 1var define = require("../define").define,
  2. MinHeap = require("./MinHeap"),
  3. base = require("../base");
  4. 1var PriorityQueue;
  5. /**
  6. * @class PriorityQueue Implementation where the value with the highest priority moves to the front
  7. * Priority starts at 0, and the greatest value being the lowest priority;
  8. * @name PriorityQueue
  9. * @augments comb.collections.MinHeap
  10. * @memberOf comb.collections
  11. * @ignoreCode
  12. */
  13. 1PriorityQueue = define(MinHeap, {
  14. instance : {
  15. /**@lends comb.collections.PriorityQueue.prototype*/
  16. /**
  17. * Adds the value with the specified priority to the queue
  18. *
  19. * @param {Number} priority the priority of the item
  20. * </br>
  21. * <b>0 = Highest, n = lowest</b>
  22. * @param value
  23. */
  24. enqueue : function(priority, value) {
  25. 44 return this.insert(priority, value);
  26. },
  27. /**
  28. * Removes the item with the highest priority from the queue
  29. *
  30. * @returns the value of the item
  31. */
  32. dequeue : function() {
  33. 16 return this.remove();
  34. }
  35. }
  36. });
  37. 1module.exports = exports = PriorityQueue;
collections/Queue.js
Coverage100.00 SLOC119 LOC32 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. base = require("../base");
  4. /**
  5. * @class <p>FIFO Data structure</p>
  6. * @name Queue
  7. * @augments comb.collections.Collection
  8. * @memberOf comb.collections
  9. *
  10. * @property {Number} count the current number of elements in this queue
  11. * @property {Boolean} isEmpty true if this queue is empty
  12. * @property {Array} values a copy of the values contained in this queue
  13. * @ignoreCode
  14. */
  15. 1module.exports = exports = define(Collection, {
  16. instance : {
  17. /**@lends comb.collections.Queue.prototype*/
  18. constructor : function() {
  19. 6 this.__queue = [];
  20. 6 this.__next = 0;
  21. 6 this.__last = 0;
  22. },
  23. /**
  24. * Add data to this queue
  25. * @param {*} data element to add
  26. */
  27. enqueue : function(data) {
  28. 27 this.__queue[this.__last++] = data;
  29. },
  30. /**
  31. * Removes first item from the head of the queue
  32. *
  33. * @return {*} The element removed from this queue. Returns undefined if the queue is empty.
  34. */
  35. dequeue : function() {
  36. 7 var ret = undefined,next = this.__next, queue;
  37. 7 if (next != this.__last) {
  38. 5 queue = this.__queue;
  39. 5 ret = queue[next];
  40. 5 queue[this.__next++] = undefined;
  41. }
  42. 7 return ret;
  43. },
  44. /**
  45. * Retrieves the item at the head of the queue without removing it
  46. *
  47. * @return {*} The element at the head of the queue. Returns undefined if the queue is empty.
  48. */
  49. peek : function() {
  50. 1 var ret = undefined, next = this.__next;
  51. 1 if (next != this.__last) {
  52. 1 ret = this.__queue[next];
  53. }
  54. 1 return ret;
  55. },
  56. /**
  57. * Removes all items from this queue
  58. */
  59. clear : function() {
  60. 1 this.__queue.length = 0;
  61. 1 this.__next = 0;
  62. 1 this.__last = 0;
  63. },
  64. /**
  65. * Determine if this queue contains the element
  66. * @param {*} obj the object to find
  67. *
  68. * @return {Boolean} true if this queue contains the element
  69. */
  70. contains : function(obj) {
  71. 5 return this.__queue.indexOf(obj) != -1;
  72. },
  73. /**
  74. * Removes an element from this queue.
  75. * @param {*} obj the data to remove.
  76. *
  77. * @return {Boolean} true if the element was removed, false otherwise.
  78. */
  79. remove : function(obj) {
  80. 8 var index = this.__queue.indexOf(obj), ret = false;
  81. 8 if (index != -1) {
  82. 8 if (index == this.__next) {
  83. 1 this.dequeue();
  84. } else {
  85. 7 this.__queue.splice(index, 1);
  86. 7 this.__last--;
  87. }
  88. 8 ret = true;
  89. }
  90. 8 return ret;
  91. },
  92. toString : function(){
  93. 3 return this.__queue.toString();
  94. },
  95. getters : {
  96. count : function() {
  97. 114 return this.__last - this.__next;
  98. },
  99. isEmpty : function() {
  100. 5 return this.__last - this.__next == 0;
  101. },
  102. values : function() {
  103. 1 return this.__queue.slice(this.__next, this.__last);
  104. }
  105. }
  106. }
  107. });
collections/RedBlackTree.js
Coverage100.00 SLOC188 LOC101 Missed0
  1. 1var define = require("../define").define,
  2. Tree = require("./Tree"),
  3. base = require("../base"),
  4. multiply = base.string.multiply;
  5. 1var RED = "red", BLACK = "black";
  6. 1var isRed = function(node) {
  7. 1156 return node != null && node.red;
  8. };
  9. 1var makeNode = function(data, parent) {
  10. 122 return {
  11. data : data,
  12. red : true,
  13. left : null,
  14. right : null
  15. }
  16. };
  17. 1var insert = function(root, data, compare) {
  18. 503 if (root == null) {
  19. 122 return makeNode(data, null);
  20. } else {
  21. 381 var cmp = compare(data, root.data);
  22. 381 if (cmp) {
  23. 381 var dir = cmp == -1 ? "left" : "right";
  24. 381 var otherDir = dir == "left" ? "right" : "left";
  25. 381 root[dir] = insert(root[dir], data, compare);
  26. 381 var node = root[dir];
  27. 381 if (isRed(node)) {
  28. 294 var sibling = root[otherDir];
  29. 294 if (isRed(sibling)) {
  30. /* Case 1 */
  31. 68 root.red = true;
  32. 68 node.red = false;
  33. 68 sibling.red = false;
  34. } else {
  35. 226 if (isRed(node[dir])) {
  36. 43 root = rotateSingle(root, otherDir);
  37. 183 } else if (isRed(node[otherDir])) {
  38. 11 root = rotateDouble(root, otherDir);
  39. }
  40. }
  41. }
  42. }
  43. }
  44. 381 return root;
  45. };
  46. 1var rotateSingle = function(root, dir) {
  47. 70 var otherDir = dir == "left" ? "right" : "left";
  48. 70 var save = root[otherDir];
  49. 70 root[otherDir] = save[dir];
  50. 70 save[dir] = root;
  51. 70 root.red = true;
  52. 70 save.red = false;
  53. 70 return save;
  54. };
  55. 1var rotateDouble = function(root, dir) {
  56. 12 var otherDir = dir == "left" ? "right" : "left";
  57. 12 root[otherDir] = rotateSingle(root[otherDir], otherDir);
  58. 12 return rotateSingle(root, dir);
  59. };
  60. 1var remove = function (root, data, done, compare) {
  61. 52 if (root == null) {
  62. 2 done.done = true;
  63. } else {
  64. 50 var dir;
  65. 50 if (compare(data, root.data) == 0) {
  66. 22 if (root.left == null || root.right == null) {
  67. 16 var save = root[root.left == null ? "right" : "left"];
  68. /* Case 0 */
  69. 16 if (isRed(root)) {
  70. 2 done.done = true;
  71. 14 } else if (isRed(save)) {
  72. 5 save.red = false;
  73. 5 done.done = true;
  74. }
  75. 16 return save;
  76. }
  77. else {
  78. 6 var heir = root.right, p;
  79. 6 while (heir.left != null) {
  80. 2 p = heir;
  81. 2 heir = heir.left;
  82. }
  83. 6 p && (p.left = null);
  84. 6 root.data = heir.data;
  85. 6 data = heir.data;
  86. }
  87. }
  88. 34 dir = compare(data, root.data) == -1 ? "left" : "right";
  89. 34 root[dir] = remove(root[dir], data, done, compare);
  90. 34 !done.done && (root = removeBalance(root, dir, done));
  91. }
  92. 36 return root;
  93. };
  94. 1var removeBalance = function(root, dir, done) {
  95. 11 var notDir = dir == "left" ? "right" : "left";
  96. 11 var p = root, s = p[notDir];
  97. 11 if (isRed(s)) {
  98. 2 root = rotateSingle(root, dir);
  99. 2 s = p[notDir];
  100. }
  101. 11 if (s != null) {
  102. 11 if (!isRed(s.left) && !isRed(s.right)) {
  103. 9 isRed(p) && (done.done = true);
  104. 9 p.red = 0;
  105. 9 s.red = 1;
  106. } else {
  107. 2 var save = p.red, newRoot = ( root === p );
  108. 2 p = (isRed(s[notDir]) ? rotateSingle : rotateDouble)(p, dir);
  109. 2 p.red = save;
  110. 2 p.left.red = p.right.red = 0;
  111. 2 if (newRoot) {
  112. 1 root = p;
  113. } else {
  114. 1 root[dir] = p;
  115. }
  116. 2 done.done = true;
  117. }
  118. }
  119. 11 return root;
  120. };
  121. 1var RedBlackTree;
  122. /**
  123. * @class <p>A RedBlack tree is a form of a self balancing binary tree.</p>
  124. *
  125. * <b>Performance</b>
  126. * <table>
  127. * <tr><td></td><td>Best</td><td>Worst</td></tr>
  128. * <tr><td>Space</td><td>O(n)</td><td>O(n)</td></tr>
  129. * <tr><td>Search</td><td>O(log n)</td><td>O(log n)</td></tr>
  130. * <tr><td>Insert</td><td>O(log n)</td><td>O(log n)</td></tr>
  131. * <tr><td>Delete</td><td>O(log n)</td><td>O(log n)</td></tr>
  132. * <table>
  133. * @name RedBlackTree
  134. * @augments comb.collections.Tree
  135. * @memberOf comb.collections
  136. * @ignoreCode
  137. */
  138. 1module.exports = exports = define(Tree, {
  139. instance : {
  140. /**@lends comb.collections.RedBlackTree.prototype*/
  141. insert : function(data) {
  142. 122 this.__root = insert(this.__root, data, this.compare);
  143. 122 this.__root.red = false;
  144. },
  145. remove : function(data) {
  146. 18 var done = {done : false};
  147. 18 var root = remove(this.__root, data, done, this.compare);
  148. 18 if (root != null)
  149. 17 root.red = 0;
  150. 18 this.__root = root;
  151. },
  152. __printNode : function(node, level) {
  153. 37 var str = [];
  154. 37 if (node == null || node == undefined) {
  155. 19 str.push(multiply('\t', level));
  156. 19 str.push("~");
  157. 19 console.log(str.join(""));
  158. } else {
  159. 18 this.__printNode(node.right, level + 1);
  160. 18 str.push(multiply('\t', level));
  161. 18 str.push((node.red ? "RED" : "BLACK") + ":" + node.data + "\n");
  162. 18 console.log(str.join(""));
  163. 18 this.__printNode(node.left, level + 1);
  164. }
  165. }
  166. }
  167. });
collections/Stack.js
Coverage100.00 SLOC117 LOC30 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. base = require("../base");
  4. /**
  5. * @class <p>LIFO Data structure</p>
  6. * @name Stack
  7. * @augments comb.collections.Collection
  8. * @memberOf comb.collections
  9. *
  10. * @property {Number} count the current number of elements in this queue
  11. * @property {Boolean} isEmpty true if this queue is empty
  12. * @property {Array} values a copy of the values contained in this queue
  13. * @ignoreCode
  14. */
  15. 1module.exports = exports = define(Collection, {
  16. instance : {
  17. /**@lends comb.collections.Stack.prototype*/
  18. constructor : function() {
  19. 1 this.__stack = [];
  20. 1 this.__next = -1;
  21. },
  22. /**
  23. * Add an item to the tail of this stack
  24. * @param {*} data item to qppend to this stack
  25. *
  26. */
  27. push : function(data) {
  28. 23 this.__stack[++this.__next] = data;
  29. },
  30. /**
  31. * Removes the tail of this static
  32. * @return {*} the data at the tail of this stack
  33. */
  34. pop : function() {
  35. 11 var ret = undefined, stack, next = this.__next;
  36. 11 if (next >= 0) {
  37. 10 stack = this.__stack;
  38. 10 ret = stack[next];
  39. 10 stack[this.__next--] = undefined;
  40. }
  41. 11 return ret;
  42. },
  43. /**
  44. * Retrieves the item at the tail of the stack without removing it
  45. *
  46. * @return {*} The element at the tail of the stack. Returns undefined if the stack is empty.
  47. */
  48. peek : function() {
  49. 1 var ret = undefined,next = this.__next;
  50. 1 if (next >= 0) {
  51. 1 ret = this.__stack[next];
  52. }
  53. 1 return ret;
  54. },
  55. /**
  56. * Removes all items from this stack.
  57. */
  58. clear : function() {
  59. 1 this.__stack.length = 0;
  60. 1 this.__next = -1;
  61. },
  62. /**
  63. * Determine if this stack contains the element
  64. * @param {*} obj the object to find
  65. *
  66. * @return {Boolean} true if this stack contains the element
  67. */
  68. contains : function(obj) {
  69. 3 return this.__stack.indexOf(obj) != -1;
  70. },
  71. /**
  72. * Removes an element from this stack.
  73. * @param {*} obj the data to remove.
  74. *
  75. * @return {Boolean} true if the element was removed, false otherwise.
  76. */
  77. remove : function(obj) {
  78. 7 var index = this.__stack.indexOf(obj), ret = false;
  79. 7 if (index != -1) {
  80. 7 if (index == this.__next) {
  81. 1 this.pop();
  82. } else {
  83. 6 this.__stack.splice(index, 1);
  84. 6 this.__next--;
  85. }
  86. 7 ret = true;
  87. }
  88. 7 return ret;
  89. },
  90. toString : function(){
  91. 3 return this.__stack.toString();
  92. },
  93. getters : {
  94. count : function() {
  95. 3 return this.__next + 1;
  96. },
  97. isEmpty : function() {
  98. 5 return this.__next < 0;
  99. },
  100. values : function() {
  101. 1 return this.__stack.slice(0, this.__next + 1).reverse();
  102. }
  103. }
  104. }
  105. });
collections/Tree.js
Coverage100.00 SLOC457 LOC160 Missed0
  1. 1var define = require("../define").define,
  2. Collection = require("./Collection"),
  3. Iterable = require("./Iterable"),
  4. base = require("../base"),
  5. multiply = base.string.multiply;
  6. 1var compare = function(a, b) {
  7. 2757 var ret = 0;
  8. 2757 if (a > b) {
  9. 1571 return 1;
  10. 1186 } else if (a < b) {
  11. 936 return -1;
  12. 250 }else if(!b){
  13. 1 return 1;
  14. }
  15. 249 return ret;
  16. };
  17. 1var Tree = define([Collection, Iterable], {
  18. instance : {
  19. /**@lends comb.collections.Tree.prototype*/
  20. /**
  21. * Prints a node
  22. * @param node node to print
  23. * @param level the current level the node is at, Used for formatting
  24. */
  25. __printNode : function(node, level) {
  26. //console.log(level);
  27. 37 var str = [];
  28. 37 if (node == null || node == undefined) {
  29. 19 str.push(multiply('\t', level));
  30. 19 str.push("~");
  31. 19 console.log(str.join(""));
  32. } else {
  33. 18 this.__printNode(node.right, level + 1);
  34. 18 str.push(multiply('\t', level));
  35. 18 str.push(node.data + "\n");
  36. 18 console.log(str.join(""));
  37. 18 this.__printNode(node.left, level + 1);
  38. }
  39. },
  40. /**
  41. * Base Class for all tree implementations
  42. * @constructs
  43. * @augments comb.collections.Collection
  44. * @augments comb.collections.Iterable
  45. * @memberOf comb.collections
  46. *
  47. * @param {Object} options options to initialize the tree
  48. * @param {Function} options.compare function used to compare items in a tree must return an integer
  49. * <ul>
  50. * </li>-1 for less than</li>
  51. * </li>0 for equal</li>
  52. * </li>1 for greater than</li>
  53. * </ul>
  54. *
  55. */
  56. constructor : function(options) {
  57. 45 options = options || {};
  58. 45 this.compare = options.compare || compare;
  59. 45 this.__root = null;
  60. },
  61. /**
  62. * Inserts an item into the tree
  63. * @param {Anything} data the item to insert
  64. */
  65. insert : function(data) {
  66. 1 throw new Error("Not Implemented");
  67. },
  68. /**
  69. * Removes an item from the tree
  70. * @param {Anything} data the item to insert
  71. */
  72. remove : function(data) {
  73. 1 throw new Error("Not Implemented");
  74. },
  75. /**
  76. * Clear all items from a tree
  77. */
  78. clear : function() {
  79. 4 this.__root = null;
  80. },
  81. /**
  82. * Test if a tree is empty
  83. *
  84. * @return {Boolean} true if empty false otherwise
  85. */
  86. isEmpty : function() {
  87. 15 return this.__root == null;
  88. },
  89. /**
  90. * Traverse a tree until the callback function returns false
  91. *
  92. * <p><b>Not typically used directly</b></p>
  93. *
  94. * @param {Object} node the node to start at
  95. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  96. * @param {Function} callback called for each item, traversal continues until the function returns false
  97. *
  98. */
  99. traverseWithCondition : function(node, order, callback) {
  100. 2172 var cont = true;
  101. 2172 if (node) {
  102. 1270 order = order || Tree.PRE_ORDER;
  103. 1270 if (order === Tree.PRE_ORDER) {
  104. 152 cont = callback(node.data);
  105. 152 if (cont) {
  106. 144 cont = this.traverseWithCondition(node.left, order, callback);
  107. 144 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  108. }
  109. 1118 } else if (order === Tree.IN_ORDER) {
  110. 510 cont = this.traverseWithCondition(node.left, order, callback);
  111. 510 if (cont) {
  112. 444 cont = callback(node.data);
  113. 444 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  114. }
  115. 608 } else if (order === Tree.POST_ORDER) {
  116. 178 cont = this.traverseWithCondition(node.left, order, callback);
  117. 178 if (cont) {
  118. 158 cont && (cont = this.traverseWithCondition(node.right, order, callback));
  119. 158 cont && (cont = callback(node.data));
  120. }
  121. 430 } else if (order === Tree.REVERSE_ORDER) {
  122. 430 cont = this.traverseWithCondition(node.right, order, callback);
  123. 430 if (cont) {
  124. 384 cont = callback(node.data);
  125. 384 cont && (cont = this.traverseWithCondition(node.left, order, callback));
  126. }
  127. }
  128. }
  129. 2172 return cont;
  130. },
  131. /**
  132. * Traverse a tree
  133. *
  134. * <p><b>Not typically used directly</b></p>
  135. *
  136. * @param {Object} node the node to start at
  137. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  138. * @param {Function} callback called for each item
  139. *
  140. */
  141. traverse : function(node, order, callback) {
  142. 3449 if (node) {
  143. 1928 order = order || Tree.PRE_ORDER;
  144. 1928 if (order === Tree.PRE_ORDER) {
  145. 632 callback(node.data);
  146. 632 this.traverse(node.left, order, callback);
  147. 632 this.traverse(node.right, order, callback);
  148. 1296 } else if (order === Tree.IN_ORDER) {
  149. 632 this.traverse(node.left, order, callback);
  150. 632 callback(node.data);
  151. 632 this.traverse(node.right, order, callback);
  152. 664 } else if (order === Tree.POST_ORDER) {
  153. 632 this.traverse(node.left, order, callback);
  154. 632 this.traverse(node.right, order, callback);
  155. 632 callback(node.data);
  156. 32 } else if (order === Tree.REVERSE_ORDER) {
  157. 32 this.traverseWithCondition(node.right, order, callback);
  158. 32 callback(node.data);
  159. 32 this.traverseWithCondition(node.left, order, callback);
  160. }
  161. }
  162. },
  163. /**
  164. * Loop through each item in the tree
  165. * @param {Function} cb called for each item in the tree
  166. * @param {Object} [scope=this] scope to call the function in
  167. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  168. */
  169. forEach : function(cb, scope, order) {
  170. 28 if (typeof cb !== "function")
  171. 4 throw new TypeError();
  172. 24 order = order || Tree.IN_ORDER;
  173. 24 scope = scope || this;
  174. 24 this.traverse(this.__root, order, function(node) {
  175. 312 cb.call(scope, node, this);
  176. });
  177. },
  178. /**
  179. * Loop through each item in the tree, collecting the value returned by the callback funciton.
  180. * @param {Function} cb called for each item in the tree.
  181. * Whatever the function returns is inserted into the return tree
  182. * @param {Object} [scope=this] scope to call the function in
  183. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  184. *
  185. * @return {comb.collections.Tree} the tree with the mapped items
  186. */
  187. map : function(cb, scope, order) {
  188. 28 if (typeof cb !== "function")
  189. 4 throw new TypeError();
  190. 24 order = order || Tree.IN_ORDER;
  191. 24 scope = scope || this;
  192. 24 var construct = this._static;
  193. 24 var ret = new this._static();
  194. 24 this.traverse(this.__root, order, function(node) {
  195. 312 ret.insert(cb.call(scope, node, this));
  196. });
  197. 24 return ret;
  198. },
  199. /**
  200. * Filters a tree, only returning items that result in true being returned from the callback
  201. *
  202. * @param {Function} cb called for each item in the tree
  203. * @param {Object} [scope=this] scope to call the function in
  204. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  205. *
  206. * @return {comb.collections.Tree} the tree with items that resulted in true being returned from the callback
  207. */
  208. filter : function(cb, scope, order) {
  209. 16 if (typeof cb !== "function")
  210. 4 throw new TypeError();
  211. 12 order = order || Tree.IN_ORDER;
  212. 12 scope = scope || this;
  213. 12 var ret = new this._static();
  214. 12 this.traverse(this.__root, order, function(node) {
  215. 216 var include = cb.call(scope, node, this);
  216. 216 include && ret.insert(node);
  217. });
  218. 12 return ret;
  219. },
  220. /**
  221. * Reduces a tree
  222. *
  223. * @param {Function} fun called for each item in the tree
  224. * @param [accumulator=First item in tree(Order dependant)] scope to call the function in
  225. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  226. *
  227. * @return the result of the reduce function
  228. */
  229. reduce : function(fun, accumulator, order) {
  230. 36 var arr = this.toArray(order);
  231. 36 var args = [fun];
  232. 36 !base.isUndefinedOrNull(accumulator) && args.push(accumulator)
  233. 36 return arr.reduce.apply(arr, args);
  234. },
  235. /**
  236. * Reduces from right to left
  237. *
  238. * @param {Function} fun called for each item in the tree
  239. * @param [accumulator=First item in tree(Order dependant)] scope to call the function in
  240. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  241. *
  242. * @return the result of the reduce function
  243. */
  244. reduceRight : function(fun, accumulator, order) {
  245. 12 var arr = this.toArray(order);
  246. 12 var args = [fun];
  247. 12 !base.isUndefinedOrNull(accumulator) && args.push(accumulator)
  248. 12 return arr.reduceRight.apply(arr, args);
  249. },
  250. /**
  251. * Determines if every item meets the condition returned by the callback.
  252. *
  253. * @param {Function} cb called for each item in the tree
  254. * @param {Object} [scope=this] scope to call the function in
  255. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  256. *
  257. * @return {Boolean} True if every item passed false otherwise
  258. */
  259. every : function(cb, scope, order) {
  260. 36 if (typeof cb !== "function")
  261. 4 throw new TypeError();
  262. 32 order = order || Tree.IN_ORDER;
  263. 32 scope = scope || this;
  264. 32 var ret = false;
  265. 32 this.traverseWithCondition(this.__root, order, function(node) {
  266. 264 return (ret = cb.call(scope, node, this));
  267. });
  268. 32 return ret;
  269. },
  270. /**
  271. * Determines if some item meet the condition returned by the callback. Traversal ends the first time true is found.
  272. *
  273. * @param {Function} cb called for each item in the tree
  274. * @param {Object} [scope=this] scope to call the function in
  275. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  276. *
  277. * @return {Boolean} True if every item passed false otherwise
  278. */
  279. some : function(cb, scope, order) {
  280. 36 if (typeof cb !== "function")
  281. 4 throw new TypeError();
  282. 32 order = order || Tree.IN_ORDER;
  283. 32 scope = scope || this;
  284. 32 var ret;
  285. 32 this.traverseWithCondition(this.__root, order, function(node) {
  286. 284 ret = cb.call(scope, node, this);
  287. 284 return !ret;
  288. });
  289. 32 return ret;
  290. },
  291. /**
  292. * Converts a tree into an array based on the specified order
  293. *
  294. * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
  295. *
  296. * @return {Array} array of all items in the order specified.
  297. */
  298. toArray : function(order) {
  299. 72 order = order || Tree.IN_ORDER;
  300. 72 var arr = [];
  301. 72 this.traverse(this.__root, order, function(node) {
  302. 1056 arr.push(node);
  303. });
  304. 72 return arr;
  305. },
  306. /**
  307. * Determines if a value is contained in the tree
  308. * @param {*} value the value to find
  309. *
  310. * @return {Boolean} true if the tree contains the item false otherwise.
  311. */
  312. contains : function(value) {
  313. 160 var ret = false;
  314. 160 var root = this.__root;
  315. 160 while (root != null) {
  316. 376 var cmp = this.compare(value, root.data);
  317. 376 if (cmp) {
  318. 271 root = root[(cmp == -1) ? "left" : "right"];
  319. } else {
  320. 105 ret = true;
  321. 105 root = null;
  322. }
  323. }
  324. 160 return ret;
  325. },
  326. /**
  327. * Finds a value in the tree
  328. * @param {*} value the value to find
  329. *
  330. * @return the value of the node that matched
  331. */
  332. find : function(value) {
  333. 8 var ret;
  334. 8 var root = this.__root;
  335. 8 while (root != null) {
  336. 35 var cmp = this.compare(value, root.data);
  337. 35 if (cmp) {
  338. 31 root = root[(cmp == -1) ? "left" : "right"];
  339. } else {
  340. 4 ret = root.data;
  341. 4 break;
  342. }
  343. }
  344. 8 return ret;
  345. },
  346. /**
  347. * Find all values less than a value
  348. * @param {*} value the value to find nodes less than
  349. * @param {Boolean} [exclusive=false] if true the value will NOT be included in the return array
  350. *
  351. * @return {Array} the array containing all values less than
  352. */
  353. findLessThan : function(value, exclusive) {
  354. //find a better way!!!!
  355. 32 var ret = [], compare = this.compare;
  356. 32 this.traverseWithCondition(this.__root, exports.IN_ORDER, function(v) {
  357. 200 var cmp = compare(value, v);
  358. 200 if ((!exclusive && cmp == 0) || cmp == 1) {
  359. 168 ret.push(v);
  360. 168 return true;
  361. } else {
  362. 32 return false;
  363. }
  364. });
  365. 32 return ret;
  366. },
  367. /**
  368. * Find all greater than a value
  369. * @param {*} value the value to find nodes greater than
  370. * @param {Boolean} [exclusive=false] if true the value will NOT be included in the return array
  371. *
  372. * @return {Array} the array containing all values greater than
  373. */
  374. findGreaterThan : function(value, exclusive) {
  375. //find a better way!!!!
  376. 32 var ret = [], compare = this.compare;
  377. 32 this.traverse(this.__root, exports.REVERSE_ORDER, function(v) {
  378. 416 var cmp = compare(value, v);
  379. 416 if ((!exclusive && cmp == 0) || cmp == -1) {
  380. 372 ret.push(v);
  381. 372 return true;
  382. } else {
  383. 44 return false;
  384. }
  385. });
  386. 32 return ret;
  387. },
  388. /**
  389. * Prints a tree to the console.
  390. */
  391. print : function() {
  392. 4 this.__printNode(this.__root, 0);
  393. }
  394. },
  395. static : {
  396. /** @lends comb.collections.Tree */
  397. /**
  398. * Pre Order
  399. */
  400. PRE_ORDER : "pre_order",
  401. /**
  402. * In Order
  403. */
  404. IN_ORDER : "in_order",
  405. /**
  406. * Post Order
  407. */
  408. POST_ORDER:"post_order",
  409. /**
  410. * Reverse Order
  411. */
  412. REVERSE_ORDER : "reverse_order"
  413. }
  414. });
  415. /**@ignore*/
  416. 1module.exports = exports = Tree;
collections/index.js
Coverage100.00 SLOC21 LOC2 Missed0
  1. 1var comb = exports;
  2. /**
  3. * @ignore
  4. * @namespace Various collections*/
  5. 1comb.collections = {
  6. Collection : require("./Collection"),
  7. Iterable : require("./Iterable"),
  8. Tree : require("./Tree"),
  9. BinaryTree : require("./BinaryTree"),
  10. RedBlackTree : require("./RedBlackTree"),
  11. AnderssonTree : require("./AnderssonTree"),
  12. AVLTree : require("./AVLTree"),
  13. HashTable : require("./HashTable"),
  14. Queue : require("./Queue"),
  15. Stack : require("./Stack"),
  16. Heap : require("./Heap"),
  17. MinHeap : require("./MinHeap"),
  18. MaxHeap : require("./MaxHeap"),
  19. PriorityQueue : require("./PriorityQueue"),
  20. Pool : require("./Pool")
  21. };
extensions/arguments.js
Coverage100.00 SLOC14 LOC4 Missed0
  1. 1var base = require("../base"),
  2. utils = require("./utils"),
  3. extend = utils.extend,
  4. array = base.array;
  5. 1var methods = [
  6. ["argsToArray", "toArray"]
  7. ];
  8. 1module.exports = function (o) {
  9. 4 return extend(o, methods, base);
  10. };
extensions/array.js
Coverage100.00 SLOC21 LOC7 Missed0
  1. 1var base = require("../base"),
  2. array = base.array,
  3. string = base.string,
  4. utils = require("./utils"),
  5. extend = utils.extend;
  6. 1var stringMethods = ["style"];
  7. 1var methods = ["forEach", "map", "filter", "reduce", "reduceRight", "some", "every", "indexOf", "lastIndexOf",
  8. "zip", "sum", "avg", "sort", "min", "max", "difference", "removeDuplicates", "unique", "rotate",
  9. "permutations", "transpose", "valuesAt", "union", "intersect", "powerSet", "cartesian", "compact",
  10. "multiply", "flatten", "pluck", "invoke", "partition"];
  11. 1module.exports = function (o) {
  12. 191 extend(o, methods, array);
  13. 191 extend(o, stringMethods, string);
  14. 191 return o;
  15. };
extensions/cast.js
Coverage100.00 SLOC45 LOC13 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend,
  5. arrayExtension = require("./array"),
  6. argumentsExtension = require("./arguments"),
  7. dateExtension = require("./date"),
  8. functionExtension = require("./function"),
  9. numberExtension = require("./number"),
  10. objectExtension = require("./object"),
  11. stringExtension = require("./string");
  12. 1var methods = {
  13. array:function () {
  14. 191 return arrayExtension(this);
  15. },
  16. date:function () {
  17. 277 return dateExtension(this);
  18. },
  19. args:function () {
  20. 4 return argumentsExtension(this);
  21. },
  22. func:function () {
  23. 42 return functionExtension(this);
  24. },
  25. number:function () {
  26. 159 return numberExtension(this);
  27. },
  28. string:function () {
  29. 485 return stringExtension(this);
  30. },
  31. object:function () {
  32. 535 return objectExtension(this);
  33. }
  34. };
  35. 1module.exports = function (o) {
  36. 1214 extend(o, Object.keys(methods), methods, function (name, func) {
  37. 8498 return base.partial(func);
  38. });
  39. 1214 return o;
  40. };
extensions/date.js
Coverage100.00 SLOC26 LOC5 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend,
  5. date = base.date,
  6. string = base.string;
  7. 1var methods = [
  8. "add",
  9. "compare",
  10. "difference",
  11. "format",
  12. "getDaysInMonth",
  13. "getTimezoneName",
  14. "isLeapYear",
  15. "isWeekend"
  16. ];
  17. 1module.exports = function (o) {
  18. 277 extend(o, methods, date);
  19. 277 return o;
  20. };
extensions/function.js
Coverage100.00 SLOC42 LOC16 Missed0
  1. 1var base = require("../base"),
  2. argsToArray = base.argsToArray,
  3. utils = require("./utils"),
  4. array = base.array,
  5. extend = utils.extend,
  6. comb;
  7. 1var methods = [
  8. "hitch",
  9. "bind",
  10. "hitchIgnore",
  11. "bindIgnore",
  12. "partial",
  13. "applyFirst",
  14. "bindFirst",
  15. "curry",
  16. "extend"
  17. ];
  18. 1var baseMethods = ["extend"];
  19. 1module.exports = function (o) {
  20. 42 comb = comb || (require("../index"));
  21. 42 extend(o, methods, base, function (name, func) {
  22. 378 var ret;
  23. 378 if (name !== 'partial' && name !== "applyFirst") {
  24. 294 ret = function (arg1) {
  25. 7 var args = argsToArray(arguments, 1);
  26. 7 return comb(func.apply(null, [arg1, this].concat(args)));
  27. };
  28. } else {
  29. 84 ret = function (arg1) {
  30. 3 return comb(func.apply(null, [this].concat(argsToArray(arguments))));
  31. };
  32. }
  33. 378 return ret;
  34. });
  35. 42 extend(o, baseMethods, base);
  36. 42 return o;
  37. };
extensions/index.js
Coverage100.00 SLOC47 LOC24 Missed0
  1. 1var base = require("../base"),
  2. stringExtension = require("./string"),
  3. functionExtension = require("./function"),
  4. objectExtension = require("./object"),
  5. argumentsExtension = require("./arguments"),
  6. dateExtension = require("./date"),
  7. isExtension = require("./is"),
  8. castExtension = require("./cast.js"),
  9. numberExtension = require("./number"),
  10. arrayExtension = require("./array");
  11. 1var TRUE = isExtension(true),
  12. FALSE = isExtension(false);
  13. 1function createExtension(obj) {
  14. 1302 if (base.isBoolean(obj)) {
  15. 17 return obj ? TRUE : FALSE;
  16. }
  17. 1285 if (!base.isUndefinedOrNull(obj) && !obj.__isExtended__) {
  18. 1214 obj = isExtension(obj);
  19. 1214 obj = castExtension(obj);
  20. 1214 if (obj.isObject()) {
  21. 535 obj.object();
  22. }
  23. 1214 if (obj.isArguments()) {
  24. 4 obj.args();
  25. }
  26. 1214 if (obj.isArray()) {
  27. 191 obj.array();
  28. }
  29. 1214 if (obj.isFunction()) {
  30. 42 obj.func();
  31. }
  32. 1214 if (obj.isString()) {
  33. 485 obj.string();
  34. }
  35. 1214 if (obj.isDate()) {
  36. 275 obj.date();
  37. }
  38. 1214 if (obj.isNumber()) {
  39. 159 obj.number();
  40. }
  41. }
  42. 1285 return obj;
  43. }
  44. 1exports.createExtension = createExtension;
extensions/is.js
Coverage100.00 SLOC87 LOC28 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend;
  5. 1var methods = [
  6. "isDefined",
  7. "isUndefined",
  8. "isNull",
  9. "isUndefinedOrNull",
  10. "isArguments",
  11. "isObject",
  12. "isHash",
  13. "isBoolean",
  14. "isDate",
  15. "isEmpty",
  16. "isArray",
  17. "isFunction",
  18. "isInstanceOf",
  19. "isNumber",
  20. "isPromiseLike",
  21. "isRegExp",
  22. "isString",
  23. "deepEqual"
  24. ];
  25. 1module.exports = function (o) {
  26. 1216 var valueOf = false, val;
  27. 1216 if (!base.isInstanceOf(o, Object)) {
  28. 639 if (base.isBoolean(o)) {
  29. 2 val = new Boolean(o);
  30. 2 valueOf = true;
  31. 637 } else if (base.isNumber(o)) {
  32. 155 val = new Number(o);
  33. 155 valueOf = true;
  34. 482 } else if (base.isString(o)) {
  35. 482 val = new String(o);
  36. 482 valueOf = true;
  37. }
  38. } else {
  39. 577 val = o;
  40. }
  41. 1216 var ret = extend(val, methods, base, null, valueOf);
  42. 1216 if (valueOf) {
  43. 639 Object.defineProperty(ret, "valueOf", {
  44. value:function () {
  45. 5102 return o;
  46. },
  47. writable:false,
  48. enumerable:false,
  49. configurable:true
  50. });
  51. }
  52. 1216 Object.defineProperty(ret, "eq", {
  53. value:function (other) {
  54. 6 return o === other;
  55. },
  56. writable:false,
  57. enumerable:false,
  58. configurable:true
  59. });
  60. 1216 Object.defineProperty(ret, "neq", {
  61. value:function (other) {
  62. 6 return o !== other;
  63. },
  64. writable:false,
  65. enumerable:false,
  66. configurable:true
  67. });
  68. 1216 Object.defineProperty(ret, "print", {
  69. value:function () {
  70. 3 console.log(o);
  71. 3 return val;
  72. },
  73. writable:false,
  74. enumerable:false,
  75. configurable:true
  76. });
  77. 1216 Object.defineProperty(ret, "__isExtended__", {
  78. value:true,
  79. writable:false,
  80. enumerable:false,
  81. configurable:true
  82. });
  83. 1216 return ret;
  84. };
extensions/number.js
Coverage100.00 SLOC17 LOC5 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend,
  5. number = base.number,
  6. string = base.string;
  7. 1var methods = [
  8. "round",
  9. "roundCeil"
  10. ];
  11. 1module.exports = function (o) {
  12. 159 extend(o, methods, number, null, true);
  13. 159 return o;
  14. };
extensions/object.js
Coverage100.00 SLOC41 LOC12 Missed0
  1. 1var base = require("../base"),
  2. define = require("../define").define,
  3. utils = require("./utils"),
  4. extend = utils.extend,
  5. hash = base.hash,
  6. argsToArray = base.argsToArray,
  7. comb;
  8. 1var methods = [
  9. "hitch",
  10. "hitchIgnore",
  11. "bind",
  12. "bindIgnore",
  13. "merge",
  14. "extend",
  15. "deepMerge"
  16. ];
  17. 1var lastMethod = ["curry"];
  18. 1var hashMethods = [
  19. "forEach",
  20. "filter",
  21. "invert",
  22. "values",
  23. "toArray",
  24. "omit",
  25. "pick",
  26. "keys"
  27. ];
  28. 1module.exports = function (o) {
  29. 535 comb = comb || (require("../index"));
  30. 535 extend(o, methods, base);
  31. 535 extend(o, hashMethods, hash);
  32. 535 extend(o, lastMethod, base, function (name, func) {
  33. 535 return function () {
  34. 1 return comb(func.apply(null, argsToArray(arguments).concat([this])));
  35. };
  36. });
  37. 535 return o;
  38. };
extensions/string.js
Coverage100.00 SLOC45 LOC21 Missed0
  1. 1var base = require("../base"),
  2. string = base.string,
  3. date = base.date,
  4. utils = require("./utils"),
  5. extend = utils.extend,
  6. regexp = base.regexp,
  7. array = base.array,
  8. argsToArray = base.argsToArray,
  9. comb;
  10. 1var methods = ["style", "multiply", "toArray", "format", "truncate", "pad"];
  11. 1var baseMethods = ["camelize", "underscore", "classify", "pluralize", "singularize", "applyFirst", "bindFirst", "partial"];
  12. 1var functionMethods = ["hitch", "bind", "hitchIgnore", "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. 485 comb = comb || (require("../index"));
  22. 485 extend(o, methods, string, null, true);
  23. 485 extend(o, baseMethods, base, null, true);
  24. 485 extend(o, dateMethods, date, null, true);
  25. 485 extend(o, regexpMethods, regexp, null, true);
  26. 485 extend(o, arrayMethods, array, function (name, func) {
  27. 970 return function () {
  28. 7 return comb(func.apply(null, argsToArray(arguments).concat([this.valueOf()])));
  29. };
  30. }, true);
  31. 485 extend(o, functionMethods, base, function (name, func) {
  32. 2425 return function (arg1) {
  33. 3 var args = argsToArray(arguments, 1);
  34. 3 return comb(func.apply(null, [arg1, this.valueOf()].concat(args)));
  35. };
  36. });
  37. 485 return o;
  38. };
extensions/utils.js
Coverage100.00 SLOC45 LOC19 Missed0
  1. 1var define = require("../define").define,
  2. base = require("../base"),
  3. array = base.array,
  4. isBoolean = base.isBoolean,
  5. argsToArray = base.argsToArray,
  6. comb;
  7. 1function proxyFunc(name, func, valueOf, scope) {
  8. 46556 return function proxy() {
  9. 9212 comb || (comb = require("../index"));
  10. 9212 var ret = func.apply(scope, [valueOf ? this.valueOf() : this].concat(argsToArray(arguments)));
  11. 9209 if (ret && !base.isBoolean(ret) && ret !== this && !ret.__isExtended__) {
  12. 586 ret = comb(ret);
  13. }
  14. 9209 return ret;
  15. };
  16. }
  17. 1function extend(obj, methods, base, creator, valueOf) {
  18. 7851 valueOf = isBoolean(valueOf) ? valueOf : false;
  19. 7851 array.forEach(methods, function (method) {
  20. 59362 var newFunc, func, m, name;
  21. 59362 if (Array.isArray(method) && method.length === 2) {
  22. 974 m = method[0];
  23. 974 name = method[1];
  24. } else {
  25. 58388 m = name = method;
  26. }
  27. 59362 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. 7851 return obj;
  35. }
  36. 1module.exports = {
  37. proxyFunc:proxyFunc,
  38. extend:extend
  39. };
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/appender.js
Coverage100.00 SLOC167 LOC30 Missed0
  1. 1var define = require("../../define.js").define, base = require("../../base"), Level = require("../level");
  2. 1var APPENDER_TYPES = {};
  3. /**
  4. * @class Base class for all appenders
  5. *
  6. * @name Appender
  7. * @memberOf comb.logging.appenders
  8. *
  9. * @param {Object} [options] options to assign to this Appender
  10. * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender
  11. * on a logger it must have a different name.
  12. * @param {String} [options.pattern="[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}"]
  13. * <p>Available Options for formatting see {@link comb.string.format} for formatting options</p>
  14. * <ul>
  15. * <li>timeStamp - the timestamp of the event being logged</li>
  16. * <li>level - the {@link comb.logging.Level} of the event</li>
  17. * <li>levelName - the name of the level being logged</li>
  18. * <li>name - the name of the logger logging the event</li>
  19. * <li>message - the message being logged</li>
  20. * </ul>
  21. * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender
  22. * <p><b>Note:</b> the level can be different from the logger in the case that you want a particular logger
  23. * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the
  24. * appenders level is lower than the logger is will not receive any messages.</p>
  25. *
  26. * @property {String} name the name of this Appender.
  27. * @property {String} pattern the pattern for this Appender.
  28. * @property {comb.logging.Level} level the level of this Appender.
  29. * @ignoreCode
  30. */
  31. 1define(null, {
  32. instance:{
  33. /**@lends comb.logging.appenders.Appender.prototype*/
  34. constructor:function (options) {
  35. 19 options = options || {};
  36. 19 this.name = options.name || "appender";
  37. 19 this.pattern = options.pattern || "[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}";
  38. 19 var level = options.level;
  39. 19 if (options.level && (level = Level.toLevel(level))) {
  40. 3 this.__level = level;
  41. }
  42. },
  43. /**
  44. * Appends a message to a log.
  45. * <b>This method is abstract and must be implemented in subclasses</b>
  46. * @param {Object} event the logging event to log.
  47. * @param {Date} event.timeStamp the timeStamp of the event.
  48. * @param {comb.logging.Level} level the level of the event.
  49. * @param {String} name the name of the logger the event was emitted from.
  50. * @param {String} message the message that is being logged.
  51. *
  52. */
  53. append:function (event) {
  54. 1 throw new Error("abstract method");
  55. },
  56. _canAppend:function (event) {
  57. 93 return !base.isUndefinedOrNull(this.__level) && event.level.isGreaterOrEqualToo(this.__level);
  58. },
  59. /**@ignore*/
  60. setters:{
  61. /**@ignore*/
  62. level:function (level) {
  63. 69 if (level && level instanceof Level) {
  64. 62 this.__level = level;
  65. } else {
  66. //try to get the level
  67. 7 level = Level.toLevel(level);
  68. 7 if (level) {
  69. 4 this.__level = level;
  70. }
  71. }
  72. },
  73. pattern:function (patt) {
  74. 20 if (base.isString(patt)) {
  75. 20 this.__pattern = patt;
  76. }
  77. },
  78. name:function (name) {
  79. 25 if (base.isString(name)) {
  80. 25 this.__name = name;
  81. }
  82. }
  83. },
  84. /**@ignore*/
  85. getters:{
  86. /**@ignore*/
  87. level:function () {
  88. 106 return this.__level;
  89. },
  90. name:function () {
  91. 90 return this.__name;
  92. },
  93. pattern:function () {
  94. 3 return this.__pattern;
  95. }
  96. }
  97. },
  98. "static":{
  99. /**@lends comb.logging.appenders.Appender*/
  100. /**
  101. * Register an appender so it can be used with {@link comb.logging.PropertyConfigurator}
  102. *
  103. * @example
  104. *
  105. * var Appender = comb.logging.appenders.Appender;
  106. * comb.define(Appender, {
  107. * instance : {
  108. * append : function(event){
  109. * //log the message
  110. * }
  111. * }
  112. * }).registerType("MyAppender").as(module);
  113. *
  114. * @param {String} type the identifier for your appender type.
  115. * @return returns the Appender class for chaining.
  116. */
  117. registerType:function (type) {
  118. 5 if (base.isString(type)) {
  119. 5 APPENDER_TYPES[type.toLowerCase()] = this;
  120. }
  121. 5 return this;
  122. },
  123. /**
  124. * Acts as a factory for appenders.
  125. *
  126. * @example
  127. *
  128. * var logging = comb.logging,
  129. * Logger = logging.Logger,
  130. * Appender = logging.appenders.Appender;
  131. *
  132. * var logger = comb.logging.Logger.getLogger("my.logger");
  133. * logger.addAppender(Appender.createAppender("consoleAppender"));
  134. *
  135. * @param {String} type the type of appender to create.
  136. * @param {Object} [options={}] additional options to pass to the appender.
  137. * @return {comb.logging.appenders.Appender} an appender to add to a logger.
  138. */
  139. createAppender:function (type, options) {
  140. 14 var caseType = type.toLowerCase();
  141. 14 if (caseType in APPENDER_TYPES) {
  142. 13 return new APPENDER_TYPES[caseType](options);
  143. } else {
  144. 1 throw new Error(type + " appender is not registered!");
  145. }
  146. }
  147. }
  148. }).as(module);
logging/appenders/consoleAppender.js
Coverage100.00 SLOC37 LOC17 Missed0
  1. 1var define = require("../../define.js").define, base = require("../../base"), string = base.string, style = string.style, format = string.format, Appender = require("./appender"), Level = require("../level");
  2. /**
  3. * @class Appends messages to the console.
  4. *
  5. * @name ConsoleAppender
  6. * @augments comb.logging.appenders.Appender
  7. * @memberOf comb.logging.appenders
  8. */
  9. 1Appender.extend({
  10. instance: {
  11. constructor: function (options) {
  12. 7 options = options || {};
  13. 7 !options.name && (options.name = "consoleAppender");
  14. 7 this._super(arguments, [options]);
  15. },
  16. append: function (event) {
  17. 6 if (this._canAppend(event)) {
  18. 6 var message = format(this.__pattern, event);
  19. 6 var level = event.level;
  20. 6 if (Level.ERROR.equals(level) || Level.FATAL.equals(level)) {
  21. 2 console.log(style(message, "red"));
  22. 4 } else if (Level.WARN.equals(level)) {
  23. 1 console.log(style(message, "yellow"));
  24. 3 } else if (Level.DEBUG.equals(level)) {
  25. 1 console.log(style(message, "magenta"));
  26. 2 } else if (Level.TRACE.equals(level)) {
  27. 1 console.log(style(message, "cyan"));
  28. } else {
  29. 1 console.log(message);
  30. }
  31. }
  32. }
  33. }
  34. }).registerType("ConsoleAppender").as(module);
logging/appenders/fileAppender.js
Coverage100.00 SLOC86 LOC22 Missed0
  1. 1var define = require("../../define.js").define,
  2. base = require("../../base"),
  3. promise = require("../../promise"),
  4. string = base.string,
  5. Promise = promise.Promise,
  6. PromiseList = promise.PromiseList,
  7. style = string.style,
  8. format = string.format,
  9. Appender = require("./appender"),
  10. Level = require("../level"),
  11. fs = require("fs");
  12. /**
  13. * @class Appends messages to a file.
  14. *
  15. * @example
  16. * var fileAppender = new comb.logging.appenders.FileAppender({
  17. * file : "/var/log/myLog.log"
  18. * });
  19. *
  20. *
  21. * @name FileAppender
  22. * @augments comb.logging.appenders.Appender
  23. * @memberOf comb.logging.appenders
  24. *
  25. * @param {Object} [options] options to assign to this Appender
  26. * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender
  27. * on a logger it must have a different name.
  28. * @param {String} [options.pattern="[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}"]
  29. * <p>Available Options for formatting see {@link comb.string.format} for formatting options</p>
  30. * <ul>
  31. * <li>timeStamp - the timestamp of the event being logged</li>
  32. * <li>level - the {@link comb.logging.Level} of the event</li>
  33. * <li>levelName - the name of the level being logged</li>
  34. * <li>name - the name of the logger logging the event</li>
  35. * <li>message - the message being logged</li>
  36. * </ul>
  37. * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender
  38. * <p><b>Note:</b> the level can be different from the logger in the case that you want a particular logger
  39. * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the
  40. * appenders level is lower than the logger is will not recieve any messages.</p>
  41. *
  42. * @param {String} [options.file="./log.log"] the file to log events to.
  43. * @param {String} [options.encoding="utf8"] the encoding of the file.
  44. * @param {Boolean} [options.overwrite=false] if true the log file is overwritten otherwise it is appended to.
  45. * @ignoreCode
  46. */
  47. 1Appender.extend({
  48. instance: {
  49. constructor: function (options) {
  50. 8 options = options || {};
  51. 8 !options.name && (options.name = "fileAppender");
  52. 8 this.__file = options.file || "./log.log";
  53. 8 this.__encoding = options.encoding || "utf8";
  54. 8 this.__overwrite = options.overwrite || false;
  55. 8 this.__writeStream = options.writeStream || fs.createWriteStream(this.__file, {
  56. flags: this.__overwrite ? "w" : 'a',
  57. encoding: this.__encoding
  58. });
  59. 8 this._super([options]);
  60. 8 this.__pattern += "\n";
  61. 8 base.listenForExit(base.hitch(this, "__onExit"));
  62. },
  63. __onExit: function () {
  64. 14 var ret = new Promise();
  65. 14 var ws = this.__writeStream;
  66. 14 this.__writeStream = null;
  67. 14 ws.on("close", base.hitch(ret, "callback"));
  68. 14 ws.destroySoon();
  69. 14 return ret.promise();
  70. },
  71. append: function (event) {
  72. 15 var ws = this.__writeStream;
  73. 15 if (this._canAppend(event) && ws && ws.writable) {
  74. 15 var message = format(this.__pattern, event);
  75. 15 var level = event.level;
  76. 15 ws.write(message);
  77. }
  78. }
  79. }
  80. }).registerType("FileAppender").as(module);
logging/appenders/index.js
Coverage100.00 SLOC11 LOC5 Missed0
  1. /**@ignore*/
  2. 1exports.Appender = require("./appender");
  3. /**@ignore*/
  4. 1exports.ConsoleAppender = require("./consoleAppender");
  5. /**@ignore*/
  6. 1exports.FileAppender = require("./fileAppender");
  7. /**@ignore*/
  8. 1exports.JSONAppender = require("./jsonAppender");
  9. /**@ignore*/
  10. 1exports.RollingFileAppender = require("./rollingFileAppender");
logging/appenders/jsonAppender.js
Coverage100.00 SLOC85 LOC19 Missed0
  1. 1var define = require("../../define.js").define,
  2. base = require("../../base"),
  3. string = base.string,
  4. escape = base.regexp.escapeString,
  5. FileAppender = require("./fileAppender"),
  6. format = string.format,
  7. Level = require("../level"),
  8. fs = require("fs");
  9. /**
  10. * @class Appends messages to a file in JSON format. The messages are logged to an array in a JSON file
  11. * <b>The file is always overwritten</b>
  12. *
  13. * @example
  14. * //example log.json
  15. * [
  16. * {
  17. * "timestamp" : "Wed Jun 08 2011 11:16:20 GMT-0500 (CDT)",
  18. * "level" : "INFO",
  19. * "name" : "comb",
  20. * "message" : "INFO MESSAGE!!!!"
  21. * }
  22. * ]
  23. *
  24. *
  25. * @name JSONAppender
  26. * @augments comb.logging.appenders.FileAppender
  27. * @memberOf comb.logging.appenders
  28. *
  29. * @param {Object} [options] options to assign to this Appender
  30. * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender
  31. * on a logger it must have a different name.
  32. * @param {String} [options.pattern="{"timestamp" : "{timeStamp}", "level" : "{levelName}", "name" : "{name}", "message" : "{message}"}"]
  33. * <p>Available Options for formatting see {@link comb.string.format} for formatting options</p>
  34. * <ul>
  35. * <li>timeStamp - the timestamp of the event being logged</li>
  36. * <li>level - the {@link comb.logging.Level} of the event</li>
  37. * <li>levelName - the name of the level being logged</li>
  38. * <li>name - the name of the logger logging the event</li>
  39. * <li>message - the message being logged</li>
  40. * </ul>
  41. * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender
  42. * <p><b>Note:</b> the level can be different from the logger in the case that you want a particular logger
  43. * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the
  44. * appenders level is lower than the logger is will not recieve any messages.</p>
  45. *
  46. * @param {String} [options.file="./log.json"] the file to log events to.
  47. * @param {String} [options.encoding="utf8"] the encoding of the file.
  48. *
  49. * @ignoreCode
  50. */
  51. 1FileAppender.extend({
  52. instance:{
  53. constructor:function (options) {
  54. 5 options = options || {};
  55. 5 this.name = options.name || "JSONAppender";
  56. 5 this.__count = 0;
  57. 5 this.__file = options.file || "./log.json";
  58. 5 this.__encoding = options.encoding || "utf8";
  59. 5 this.__writeStream = options.writeStream || fs.createWriteStream(this.__file, { flags:"w", encoding:this.__encoding});
  60. 5 this.__writeStream.write("[\n");
  61. 5 this.level = options.level;
  62. //explicit overwrite of patter
  63. 5 this.__pattern = '{"timestamp" : "{timeStamp}", "level" : "{levelName}", "name" : "{name}", "message" : "{message}"}';
  64. 5 base.listenForExit(base.hitch(this, "__onExit"));
  65. },
  66. append:function (event) {
  67. 6 if (this._canAppend(event)) {
  68. 6 event.message = event.message.replace(/\n+/g, "\\n");
  69. 6 var message = (this.__count ? ",\n" : "\n") + format(this.__pattern, event);
  70. 6 this.__writeStream.write(message);
  71. 6 this.__count++;
  72. }
  73. },
  74. __onExit:function () {
  75. 5 this.__writeStream.write("]");
  76. 5 this._super(arguments);
  77. }
  78. }
  79. }).registerType("JSONAppender").as(module);
logging/config.js
Coverage100.00 SLOC193 LOC38 Missed0
  1. 1var define = require("../define.js").define, base = require("../base"), fs = require('fs'), Appender = require("./appenders/appender.js");
  2. 1var logging, Logger, Level, appenders;
  3. 1var parseProperties = function (properties) {
  4. 4 for (var i in properties) {
  5. 4 var logger = Logger.getLogger(i);
  6. 4 var props = properties[i], level = props.level, appenderArr = props.appenders;
  7. 4 if (level) {
  8. 4 level = Level.toLevel(level);
  9. 4 if (level) {
  10. 4 logger.level = level;
  11. }
  12. }
  13. 4 if (appenderArr && base.isArray(appenderArr)) {
  14. 4 for (var j = appenderArr.length - 1; j >= 0; j--) {
  15. 14 var appenderProps = base.merge({}, appenderArr[j]), type = appenderProps.type;
  16. 14 appenderProps.type = null;
  17. 14 if (type) {
  18. 12 logger.addAppender(type, appenderProps);
  19. }
  20. }
  21. }
  22. }
  23. };
  24. /**
  25. * @class default configurator for logging
  26. *
  27. * @name BasicConfigurator
  28. * @memberOf comb.logging
  29. * @ignoreCode
  30. *
  31. */
  32. 1var BasicConfigurator = (exports.BasicConfigurator = define(null, {
  33. instance: {
  34. /**@lends comb.logging.BasicConfigurator.prototype*/
  35. constructor: function () {
  36. 6 if (!Logger) {
  37. 1 logging = require("./index").logging;
  38. 1 Logger = logging.Logger;
  39. 1 Level = logging.Level;
  40. 1 appenders = logging.appenders;
  41. }
  42. },
  43. /**
  44. * Configure logging.
  45. *
  46. * @param {comb.logging.Appender} [appender=null] appender to add to the root logger, by default a console logger is added.
  47. */
  48. configure: function (appender) {
  49. 4 var rootLogger = Logger.getRootLogger();
  50. 4 rootLogger.removeAllAppenders();
  51. 4 if (base.isInstanceOf(appender, appenders.Appender)) {
  52. 2 rootLogger.addAppender(appender);
  53. } else {
  54. 2 rootLogger.addAppender(new appenders.ConsoleAppender());
  55. }
  56. }
  57. }
  58. }));
  59. /**
  60. * @class Configures comb.Logger with the properties or properties contained within a file
  61. *
  62. * @example
  63. *
  64. * var propertyConfigurator = new comb.logging.PropertyConfigurator();
  65. *
  66. * propertyConfigurator.configure("/location/of/combLogger.json");
  67. *
  68. * //or
  69. *
  70. * var config = {
  71. * "my.logger" : {
  72. * level : "INFO",
  73. * appenders : [
  74. * {
  75. * //default file appender
  76. * type : "FileAppender",
  77. * file : "/var/log/myApp.log",
  78. * },
  79. * {
  80. * //default JSON appender
  81. * type : "JSONAppender",
  82. * file : "/var/log/myApp.JSON",
  83. * },
  84. * {
  85. * type : "FileAppender",
  86. * //override default patter
  87. * pattern : "{[EEEE, MMMM dd, yyyy h:m a]timeStamp} {[5]level}"
  88. * + " {[- 5]levelName} {[-20]name} : {message}",
  89. * //location of my log file
  90. * file : "/var/log/myApp-errors.log",
  91. * //override name so it will get added to the log
  92. * name : "errorFileAppender",
  93. * //overwrite each time
  94. * overwrite : true,
  95. * //explicity set the appender to only accept errors
  96. * level : "ERROR"
  97. * },
  98. * {
  99. * type : "JSONAppender",
  100. * file : "/var/log/myApp-error.json",
  101. * //explicity set the appender to only accept errors
  102. * level : "ERROR"
  103. * }
  104. * ]
  105. * }
  106. * //repeat for more loggers
  107. *
  108. * propertyConfigurator.configure(config);
  109. * }
  110. *
  111. * @name PropertyConfigurator
  112. * @augments comb.logging.BasicConfigurator
  113. * @memberOf comb.logging
  114. * @ignoreCode
  115. *
  116. */
  117. 1exports.PropertyConfigurator = define(BasicConfigurator, {
  118. instance: {
  119. /**@lends comb.logging.PropertyConfigurator.prototype*/
  120. /**
  121. * Call to configure logging
  122. *
  123. * @example
  124. *
  125. * //Example configuration
  126. * {
  127. * "my.logger" : {
  128. * level : "INFO",
  129. * appenders : [
  130. * {
  131. * //default file appender
  132. * type : "FileAppender",
  133. * file : "/var/log/myApp.log",
  134. * },
  135. * {
  136. * //default JSON appender
  137. * type : "JSONAppender",
  138. * file : "/var/log/myApp.JSON",
  139. * },
  140. * {
  141. * type : "FileAppender",
  142. * //override default patter
  143. * pattern : "{[EEEE, MMMM dd, yyyy h:m a]timeStamp} {[5]level}"
  144. * + " {[- 5]levelName} {[-20]name} : {message}",
  145. * //location of my log file
  146. * file : "/var/log/myApp-errors.log",
  147. * //override name so it will get added to the log
  148. * name : "errorFileAppender",
  149. * //overwrite each time
  150. * overwrite : true,
  151. * //explicity set the appender to only accept errors
  152. * level : "ERROR"
  153. * },
  154. * {
  155. * type : "JSONAppender",
  156. * file : "/var/log/myApp-error.json",
  157. * //explicity set the appender to only accept errors
  158. * level : "ERROR"
  159. * }
  160. * ]
  161. * }
  162. *
  163. * @param {Object|String} properties Object containing configuration or string containing a file name with the configuration.
  164. */
  165. configure: function (properties) {
  166. 6 var rootLogger = Logger.getRootLogger();
  167. 6 rootLogger.removeAllAppenders();
  168. 6 if (base.isHash(properties)) {
  169. 2 parseProperties(base.deepMerge({}, properties));
  170. } else {
  171. 4 fs.readFile(properties, function (err, res) {
  172. 4 if (err) {
  173. 1 throw err;
  174. } else {
  175. 3 try {
  176. 3 parseProperties(JSON.parse(res));
  177. } catch (e) {
  178. 1 throw e;
  179. }
  180. }
  181. });
  182. }
  183. }
  184. }
  185. });
logging/level.js
Coverage100.00 SLOC188 LOC33 Missed0
  1. 1var define = require("../define.js").define, base = require("../base");
  2. 1var LEVELS = {
  3. ALL:-100000,
  4. DEBUG:1,
  5. TRACE:2,
  6. INFO:3,
  7. WARN:4,
  8. ERROR:5,
  9. FATAL:6,
  10. OFF:100000
  11. };
  12. 1var LEVELS_REVERSE = {
  13. "-100000":"ALL",
  14. "1":"DEBUG",
  15. "2":"TRACE",
  16. "3":"INFO",
  17. "4":"WARN",
  18. "5":"ERROR",
  19. "6":"FATAL",
  20. "100000":"OFF"
  21. };
  22. /**
  23. * @class Level class used to describe logging levels. The levels determine what types of events are logged to the appenders
  24. * for example the if Level.ALL is used then all event will be logged, however if Level.INFO was used then <b>ONLY</b>
  25. * INFO, WARN, ERROR, and FATAL events will be logged. To turn off logging for a logger use Level.OFF.
  26. *
  27. * <p><b>Not typically instantiated directly, but through staticly defined levels</b></p>
  28. * @example
  29. * //Levels in ascending order
  30. * comb.logging.Level.ALL
  31. * comb.logging.Level.DEBUG
  32. * comb.logging.Level.TRACE
  33. * comb.logging.Level.INFO
  34. * comb.logging.Level.WARN
  35. * comb.logging.Level.ERROR
  36. * comb.logging.Level.FATAL
  37. * comb.logging.Level.OFF
  38. *
  39. * //or
  40. * Level.getLevel("INFO");
  41. *
  42. * @name Level
  43. * @memberOf comb.logging
  44. *
  45. * @property {Number} level the numerical representation of this level.
  46. * @property {String} name the name of level.
  47. * @ignoreCode
  48. */
  49. 1var Level = (exports = module.exports = define(null, {
  50. instance:{
  51. /**@lends comb.logging.Level.prototype*/
  52. constructor:function (level, name) {
  53. 9 this.level = level;
  54. 9 this.name = name;
  55. },
  56. /**
  57. * Determing if this level is >= another level
  58. * @param {comb.logging.Level} level the level to test against
  59. *
  60. * @returns {Boolean} true if this is >= false otherwise.
  61. */
  62. isGreaterOrEqualToo:function (level) {
  63. 366 var ret = false;
  64. 366 if (level && base.isNumber(level.level)) {
  65. 366 if (this.level >= level.level) {
  66. 223 ret = true;
  67. }
  68. }
  69. 366 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. 178 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. 229 var ret = null;
  95. 229 var args = base.argsToArray(arguments);
  96. 229 if (args.length === 1) {
  97. 227 var level = args[0];
  98. 227 if (base.isNumber(level)) {
  99. 11 var strLevel = LEVELS_REVERSE[level];
  100. 11 ret = Level[strLevel];
  101. 216 } else if (base.isString(level)) {
  102. 66 ret = Level[level.toUpperCase()];
  103. } else {
  104. 150 ret = level;
  105. }
  106. } else {
  107. 2 ret = (Level.toLevel(args[0]) || args[1]);
  108. }
  109. 229 return ret;
  110. },
  111. /**
  112. * Adds a new level to the Level object.
  113. *
  114. * @example
  115. *
  116. * logger = Logger.getLogger("my.logger");
  117. *
  118. * //create the custom level
  119. * Level.addLevel("custom_Level", 20);
  120. *
  121. * //now set the level on a logger
  122. * logger.level = Level.CUSTOM_LEVEL;
  123. *
  124. * @param {string} label the label of the level, <b>Note:</b> the label will be coverted to uppercase.
  125. * @param {number} level the level of the level
  126. *
  127. * @return {undefined|comb.logging.Level} the level that was created.
  128. *
  129. */
  130. addLevel:function (label, level) {
  131. 1 var ret;
  132. 1 if (base.isString(label) && base.isNumber(level)) {
  133. 1 label = label.toUpperCase();
  134. 1 LEVELS_REVERSE[level] = label;
  135. 1 LEVELS[label] = level;
  136. 1 ret = (this[label] = new Level(level, label));
  137. }
  138. 1 return ret;
  139. },
  140. /**
  141. * Level to allow logging of all events.
  142. */
  143. ALL:null,
  144. /**
  145. * Logs only events debug or greater.
  146. */
  147. DEBUG:null,
  148. /**
  149. * Like debug but provides a finer level of detail
  150. */
  151. TRACE:null,
  152. /**
  153. * Only info, or error related events
  154. */
  155. INFO:null,
  156. /**
  157. * Only warn or error related events
  158. */
  159. WARN:null,
  160. /**
  161. * Error or fatal events
  162. */
  163. ERROR:null,
  164. /**
  165. * Only fatal events
  166. */
  167. FATAL:null,
  168. /**
  169. * No events will be logged.
  170. */
  171. OFF:null
  172. }
  173. }));
  174. 1for (var i in LEVELS_REVERSE) {
  175. 8 Level[LEVELS_REVERSE[i]] = new Level(parseInt(i, 10), LEVELS_REVERSE[i]);
  176. }
plugins/Broadcaster.js
Coverage100.00 SLOC113 LOC26 Missed0
  1. 1var func = require("../base/functions"),
  2. define = require("../define").define;
  3. 1var Broadcaster = define(null, {
  4. instance : {
  5. /** @lends comb.plugins.Broadcaster.prototype */
  6. /**
  7. * Plugin to allow a class to easily broadcast events
  8. *
  9. * @example
  10. *
  11. * var Mammal = define(comb.plugins.Broadcaster, {
  12. * instance : {
  13. *
  14. * constructor: function(options) {
  15. * options = options || {};
  16. * this._super(arguments);
  17. * this._type = options.type || "mammal";
  18. * },
  19. *
  20. * speak : function() {
  21. * var str = "A mammal of type " + this._type + " sounds like";
  22. * this.broadcast("speak", str);
  23. * this.onSpeak(str);
  24. * return str;
  25. * },
  26. *
  27. * onSpeak : function(){}
  28. * }
  29. * });
  30. *
  31. *
  32. * var m = new Mammal({color : "gold"});
  33. * m.listen("speak", function(str){
  34. * //called back from the broadcast event
  35. * console.log(str);
  36. * });
  37. * m.speak();
  38. *
  39. * @constructs
  40. */
  41. constructor : function() {
  42. 2 this.__listeners = {};
  43. },
  44. /**
  45. * Broadcasts an event from an object
  46. *
  47. * @param name the name of the event to broadcast
  48. * @param {Object|String|Function|Date|Number} [args] variable number of arguments to pass to listeners, can be anything
  49. */
  50. broadcast : function(topic, args) {
  51. 2 var args = Array.prototype.slice.call(arguments, 0), topic = args.shift();
  52. 2 if (topic && topic in this.__listeners) {
  53. 2 var list = this.__listeners[topic], i = list.length - 1;
  54. 2 while (i >= 0) {
  55. 2 list[i--].cb.apply(this, args);
  56. }
  57. }
  58. },
  59. /**
  60. * Listens to a broadcasted event
  61. * Simimlar to {@link comb.listen}
  62. *
  63. * @param {String} topic the topic to listen to
  64. * @param {Function} callback the function to callback on event publish
  65. *
  66. * @returns {Array} handle to disconnect a topic
  67. */
  68. listen : function(topic, callback) {
  69. 3 if (!func.isFunction(callback)) throw new Error("callback must be a function");
  70. 3 var handle = {
  71. topic : topic,
  72. cb : callback
  73. };
  74. 3 var list = this.__listeners[topic];
  75. 3 if (!list) {
  76. 2 list = (this.__listeners[topic] = [handle]);
  77. 2 handle.pos = 0;
  78. } else {
  79. 1 handle.pos = list.push(handle);
  80. }
  81. 3 return handle;
  82. },
  83. /**
  84. * Disconnects a listener
  85. * Similar to {@link comb.unListen}
  86. *
  87. * @param handle disconnect a handle returned from Broadcaster.listen
  88. */
  89. unListen : function(handle) {
  90. 1 if (handle) {
  91. 1 var topic = handle.topic;
  92. 1 if (topic in this.__listeners) {
  93. 1 var listeners = this.__listeners, list = listeners[topic];
  94. 1 if (list) {
  95. 1 for (var i = list.length - 1; i >= 0; i--) {
  96. 1 if (list[i] == handle) {
  97. 1 list.splice(i, 1);
  98. 1 break;
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }
  105. }
  106. });
  107. 1exports = module.exports = Broadcaster;
plugins/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. };