├── .github └── workflows │ └── whitesource.yml ├── .gitignore ├── .jshintrc ├── .npmignore ├── .nvmrc ├── .travis.yml ├── Gruntfile.js ├── History.md ├── LICENSE ├── README.md ├── benchmark ├── base │ └── array.benchmark.js ├── collections │ ├── AVLTree.benchmark.js │ ├── AnderssonTree.benchmark.js │ ├── BinaryTree.benchmark.js │ ├── HashTable.benchmark.js │ └── RedBlackTree.benchmark.js ├── define.benchmark.js └── promise.benchmark.js ├── docs-md ├── coverage.html ├── define.md ├── introduction.md ├── logging.md ├── promise.md └── utilities.md ├── docs ├── History.html ├── assets │ ├── css │ │ ├── bootstrap-responsive.css │ │ ├── bootstrap-responsive.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.min.css │ │ └── prettify.css │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ ├── jquery.js │ │ └── prettify.js ├── comb.html ├── comb_Promise.html ├── comb_PromiseList.html ├── comb_array.html ├── comb_async.html ├── comb_characters.html ├── comb_collections_AVLTree.html ├── comb_collections_AnderssonTree.html ├── comb_collections_BinaryTree.html ├── comb_collections_Collection.html ├── comb_collections_HashTable.html ├── comb_collections_Heap.html ├── comb_collections_Iterable.html ├── comb_collections_MaxHeap.html ├── comb_collections_MinHeap.html ├── comb_collections_Pool.html ├── comb_collections_PriorityQueue.html ├── comb_collections_Queue.html ├── comb_collections_RedBlackTree.html ├── comb_collections_Stack.html ├── comb_collections_Tree.html ├── comb_date.html ├── comb_hash.html ├── comb_logger.html ├── comb_logging_BasicConfigurator.html ├── comb_logging_Level.html ├── comb_logging_Logger.html ├── comb_logging_PropertyConfigurator.html ├── comb_logging_appenders_Appender.html ├── comb_logging_appenders_ConsoleAppender.html ├── comb_logging_appenders_FileAppender.html ├── comb_logging_appenders_JSONAppender.html ├── comb_logging_appenders_RollingFileAppender.html ├── comb_number.html ├── comb_plugins.html ├── comb_plugins_Broadcaster.html ├── comb_plugins_Middleware.html ├── comb_regexp.html ├── comb_string.html ├── define.html ├── index.html ├── introduction.html ├── logging.html ├── promise.html └── utilities.html ├── index.js ├── lib ├── async.js ├── base │ ├── array.js │ ├── broadcast.js │ ├── characters.js │ ├── date │ │ ├── index.js │ │ └── transforms.js │ ├── functions.js │ ├── index.js │ ├── inflections.js │ ├── misc.js │ ├── number.js │ ├── object.js │ ├── regexp.js │ └── string.js ├── collections │ ├── AVLTree.js │ ├── AnderssonTree.js │ ├── BinaryTree.js │ ├── Collection.js │ ├── HashTable.js │ ├── Heap.js │ ├── Iterable.js │ ├── MaxHeap.js │ ├── MinHeap.js │ ├── Pool.js │ ├── PriorityQueue.js │ ├── Queue.js │ ├── RedBlackTree.js │ ├── Stack.js │ ├── Tree.js │ └── index.js ├── define.js ├── extensions │ ├── arguments.js │ ├── array.js │ ├── cast.js │ ├── date.js │ ├── function.js │ ├── index.js │ ├── is.js │ ├── number.js │ ├── object.js │ ├── string.js │ └── utils.js ├── index.js ├── logging │ ├── appenders │ │ ├── appender.js │ │ ├── consoleAppender.js │ │ ├── fileAppender.js │ │ ├── index.js │ │ ├── jsonAppender.js │ │ └── rollingFileAppender.js │ ├── config.js │ ├── index.js │ └── level.js ├── plugins │ ├── Broadcaster.js │ ├── Middleware.js │ └── index.js └── promise.js ├── package.json └── test ├── async.test.js ├── base.test.js ├── base ├── array.test.js ├── broadcast.test.js ├── date.test.js ├── functions.test.js ├── inflections.test.js ├── misc.test.js ├── number.test.js ├── object.test.js ├── regexp.test.js └── string.test.js ├── collections ├── AVLTree.test.js ├── AnderssonTree.test.js ├── BinaryTree.test.js ├── Collection.test.js ├── HashTable.test.js ├── Heap.test.js ├── Iterable.test.js ├── MaxHeap.test.js ├── MinHeap.test.js ├── Pool.test.js ├── PriorityQueue.test.js ├── Queue.test.js ├── RedBlackTree.test.js ├── Stack.test.js ├── Tree.test.js └── treeTest.helper.js ├── define.test.js ├── extensions └── is.test.js ├── logging ├── appenders │ ├── appender.test.js │ ├── consoleAppender.test.js │ ├── fileAppender.test.js │ ├── jsonAppender.test.js │ └── rollingFileAppender.test.js ├── config.test.js ├── level.test.js └── logger.test.js ├── plugins ├── Broadcaster.test.js └── Middleware.test.js ├── promise.test.js └── promiseList.test.js /.github/workflows/whitesource.yml: -------------------------------------------------------------------------------- 1 | name: NPM WhiteSource Scan 2 | 3 | on: 4 | pull_request: 5 | branches: [ master* ] 6 | 7 | jobs: 8 | WhiteSource-Unified-Agent: 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | node-version: [14.x] 15 | 16 | steps: 17 | - name: Checkout https://github.com/${{ github.repository }}@${{ github.ref }} 18 | uses: actions/checkout@v2 19 | with: 20 | persist-credentials: false 21 | 22 | - name: Set up Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | 27 | - uses: actions/cache@v2 28 | with: 29 | path: ~/.npm 30 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 31 | restore-keys: | 32 | ${{ runner.os }}-node- 33 | - name: Install dependencies 34 | run: npm install --only=prod 35 | 36 | - name: WhiteSource Unified Agent Scan 37 | env: 38 | WS_APIKEY: ${{secrets.WHITESOURCE_ORG_API_KEY}} 39 | WS_USERKEY: ${{secrets.WHITESOURCE_PRIORTIZE_USERKEY}} 40 | WS_WSS_URL: https://saas.whitesourcesoftware.com/agent 41 | WS_PRODUCTNAME: GH_${{github.event.repository.name}} 42 | WS_PROJECTNAME: GH_${{github.event.repository.name}} 43 | run: | 44 | curl -LJO https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar 45 | echo Unified Agent downloaded successfully 46 | java -jar wss-unified-agent.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | atlassian-ide-plugin.xml 3 | node_modules 4 | lib-cov 5 | *.iml 6 | .idea -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "console", 4 | "setImmediate" 5 | ], 6 | "globals": { 7 | "Promise": true, 8 | "setImmediate": false 9 | }, 10 | "node": true, 11 | "browser": false, 12 | "devel": true, 13 | "jquery": false, 14 | "bitwise": false, 15 | "camelcase": true, 16 | "curly": true, 17 | "eqeqeq": true, 18 | "forin": false, 19 | "immed": true, 20 | "indent": 4, 21 | "latedef": "nofunc", 22 | "newcap": true, 23 | "noarg": true, 24 | "noempty": true, 25 | "nonew": false, 26 | "plusplus": false, 27 | "quotmark": false, 28 | "regexp": false, 29 | "undef": true, 30 | "unused": false, 31 | "strict": true, 32 | "trailing": true, 33 | "white": false, 34 | "asi": false, 35 | "boss": false, 36 | "debug": false, 37 | "eqnull": true, 38 | "esnext": true, 39 | "evil": false, 40 | "expr": true, 41 | "funcscope": false, 42 | "globalstrict": false, 43 | "iterator": false, 44 | "lastsemic": false, 45 | "laxbreak": false, 46 | "laxcomma": false, 47 | "loopfunc": false, 48 | "multistr": false, 49 | "onecase": false, 50 | "proto": false, 51 | "regexdash": false, 52 | "scripturl": false, 53 | "smarttabs": false, 54 | "shadow": false, 55 | "sub": true, 56 | "supernew": true, 57 | "validthis": false 58 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | node_modules 3 | examples 4 | benchmark 5 | docs 6 | support 7 | lib-cov 8 | Makefile 9 | .travis.yml 10 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_script: 2 | - npm install -g grunt-cli 3 | 4 | sudo: false 5 | 6 | after_script: 7 | - grunt coveralls 8 | 9 | language: node_js 10 | node_js: 11 | - "10" 12 | - "12" 13 | - "14" 14 | - "15" 15 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /*global module:false*/ 3 | module.exports = function (grunt) { 4 | 5 | // Automatic module definition loading. Significantly speeds up build cycles 6 | require('jit-grunt')(grunt); 7 | 8 | // Time how long tasks take. Can help when optimizing build times 9 | require('time-grunt')(grunt); 10 | 11 | // Project configuration. 12 | var DEFAULT_COVERAGE_ARGS = ["cover", "-x", "Gruntfile.js", "--report", "none", "--print", "none", "--include-pid", "grunt", "--", "it"], 13 | path = require("path"); 14 | 15 | grunt.initConfig({ 16 | pkg: grunt.file.readJSON('package.json'), 17 | 18 | comb: { 19 | paths: { 20 | root: './', 21 | lib: './lib', 22 | test: './test' 23 | } 24 | }, 25 | 26 | jshint: { 27 | src: [ 28 | "./index.js", 29 | "<%= comb.paths.lib %>/**/*.js", 30 | "<%= comb.paths.test %>/**/*.js", 31 | "Gruntfile.js" 32 | ], 33 | options: { 34 | jshintrc: '.jshintrc' 35 | } 36 | }, 37 | 38 | exec: { 39 | sendToCoveralls: "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", 40 | removeCoverage: "rm -rf ./coverage", 41 | removeDocs: "rm -rf docs/*", 42 | createDocs: 'coddoc -f multi-html -d ./lib --dir ./docs' 43 | }, 44 | 45 | it: { 46 | all: { 47 | src: 'test/**/*.test.js', 48 | options: { 49 | timeout: 3000, // not fully supported yet 50 | reporter: 'tap' 51 | } 52 | } 53 | } 54 | }); 55 | 56 | grunt.registerTask("benchmarks", "runs benchmarks", function () { 57 | var done = this.async(); 58 | require("./benchmark/benchmark")() 59 | .then(function () { 60 | done(true); 61 | }) 62 | .catch(function (err) { 63 | console.log(err.stack || err); 64 | done(false); 65 | 66 | }); 67 | }); 68 | 69 | grunt.registerTask("spawn-test-coverage", "spawn tests with coverage", function () { 70 | var done = this.async(); 71 | var env = process.env; 72 | grunt.util.spawn({ 73 | cmd: "./node_modules/istanbul/lib/cli.js", 74 | args: DEFAULT_COVERAGE_ARGS, 75 | opts: {stdio: 'inherit', env: env} 76 | }, function (err) { 77 | if (err) { 78 | console.log(err); 79 | done(false); 80 | } else { 81 | done(); 82 | } 83 | }); 84 | }); 85 | 86 | 87 | grunt.registerTask("process-coverage", "process coverage obects", function () { 88 | var files = grunt.file.expand("./coverage/coverage*.json"), 89 | istanbul = require('istanbul'), 90 | collector = new istanbul.Collector(), 91 | reporter = new istanbul.Reporter(), 92 | sync = false, 93 | done = this.async(); 94 | 95 | files.forEach(function (file) { 96 | collector.add(grunt.file.readJSON(file)); 97 | }); 98 | 99 | reporter.add('text'); 100 | reporter.addAll(['lcovonly']); 101 | reporter.write(collector, sync, function (err) { 102 | if (err) { 103 | console.error(err.stack); 104 | return done(false); 105 | } 106 | console.log('All reports generated'); 107 | done(); 108 | }); 109 | }); 110 | 111 | grunt.registerTask('default', ['jshint', "test", "test-coverage", "docs"]); 112 | 113 | grunt.registerTask('test', ['it']); 114 | 115 | grunt.registerTask('coveralls', ['exec:removeCoverage', 'spawn-test-coverage', 'process-coverage', 'exec:sendToCoveralls', 'exec:removeCoverage']); 116 | grunt.registerTask('test-coverage', ['exec:removeCoverage', 'spawn-test-coverage', 'process-coverage', 'exec:removeCoverage']); 117 | 118 | 119 | grunt.registerTask("docs", ["exec:removeDocs", "exec:createDocs"]); 120 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2011-2012 Pollenware 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /benchmark/base/array.benchmark.js: -------------------------------------------------------------------------------- 1 | var Benchmark = require("benchmark"), 2 | comb = require("../../index.js"), 3 | array = comb.array; 4 | var suite = new Benchmark.Suite(); 5 | 6 | var arr = array.flatten(array.powerSet([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); 7 | 8 | // add tests 9 | suite 10 | .add('for loop ++i', function () { 11 | for (var i = 0; i < arr.length; ++i) { 12 | var item = arr[i]; 13 | } 14 | }) 15 | .add('for loop i++', function () { 16 | for (var i = 0; i < arr.length; i++) { 17 | var item = arr[i]; 18 | } 19 | }) 20 | 21 | .add('while loop', function () { 22 | var i = arr.length; 23 | while (--i) { 24 | var item = arr[i]; 25 | } 26 | }) 27 | 28 | .add('for loop optimized i++', function () { 29 | for (var i = 0, l = arr.length; i < l; i++) { 30 | var item = arr[i]; 31 | } 32 | }) 33 | .add('for loop optimized ++i', function () { 34 | for (var i = 0, l = arr.length; i < l; ++i) { 35 | var item = arr[i]; 36 | } 37 | }) 38 | .add('for loop backward --i', function () { 39 | for (var i = arr.length - 1; i >= 0; --i) { 40 | var item = arr[i]; 41 | } 42 | }) 43 | .add('for loop backward i--', function () { 44 | for (var i = arr.length - 1; i >= 0; i--) { 45 | var item = arr[i]; 46 | } 47 | }) 48 | .add('arr.forEach', function () { 49 | arr.forEach(function (item) { 50 | }); 51 | }) 52 | .add('comb([arr]).forEach', function () { 53 | comb(arr).forEach(function (item) { 54 | }); 55 | }) 56 | .on('cycle', function (event) { 57 | console.log(String(event.target)); 58 | }) 59 | .on('complete', function () { 60 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 61 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 62 | }) 63 | .run(); -------------------------------------------------------------------------------- /benchmark/collections/AVLTree.benchmark.js: -------------------------------------------------------------------------------- 1 | var comb = require("../../lib"), 2 | Tree = comb.collections.AVLTree, 3 | Benchmark = require("benchmark"); 4 | 5 | var suite = new Benchmark.Suite(); 6 | 7 | var words = comb(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"].reverse()) 8 | .powerSet() 9 | .map(function (w) { 10 | return w.join(""); 11 | }).filter(function (a) { 12 | return a; 13 | }); 14 | console.log("AVLTree Benchmark"); 15 | 16 | suite 17 | .add("insert words in order in array", function () { 18 | var arr = []; 19 | for (var i = 0, l = words.length; i < l; i++) { 20 | arr.push(words[i]); 21 | arr.sort(); 22 | } 23 | }) 24 | .add("insert words into AVLTree", function () { 25 | var tree = new Tree(); 26 | for (var i = 0, l = words.length; i < l; i++) { 27 | tree.insert(words[i]); 28 | } 29 | }) 30 | .on('cycle', function (event) { 31 | console.log(String(event.target)); 32 | }) 33 | .on('complete', function () { 34 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 35 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 36 | }) 37 | .run(); 38 | 39 | (function () { 40 | var arr = [], tree = new Tree(); 41 | for (var i = 0, l = words.length; i < l; i++) { 42 | var word = words[i]; 43 | arr.push(word); 44 | tree.insert(word); 45 | } 46 | arr.sort(); 47 | 48 | new Benchmark.Suite() 49 | .add("look up words in array", function () { 50 | for (var i = 0, l = words.length; i < l; i++) { 51 | var index = arr.indexOf(words[i]); 52 | if (index === -1) { 53 | console.log("INDEX ERROR"); 54 | } 55 | } 56 | }) 57 | .add("look up words in AVLTree", function () { 58 | for (var i = 0, l = words.length; i < l; i++) { 59 | if (!tree.contains(words[i])) { 60 | console.log("INDEX ERROR"); 61 | } 62 | } 63 | }) 64 | .on('cycle', function (event) { 65 | console.log(String(event.target)); 66 | }) 67 | .on('complete', function () { 68 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 69 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 70 | }) 71 | .run(); 72 | 73 | new Benchmark.Suite() 74 | .add("remove words from array", function () { 75 | 76 | }); 77 | 78 | })(); 79 | 80 | -------------------------------------------------------------------------------- /benchmark/collections/AnderssonTree.benchmark.js: -------------------------------------------------------------------------------- 1 | var comb = require("../../lib"), 2 | Tree = comb.collections.AnderssonTree, 3 | Benchmark = require("benchmark"); 4 | 5 | var suite = new Benchmark.Suite(); 6 | 7 | var words = comb(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"].reverse()) 8 | .powerSet() 9 | .map(function (w) { 10 | return w.join(""); 11 | }).filter(function (a) { 12 | return a; 13 | }); 14 | console.log("AnderssonTree Benchmark"); 15 | 16 | suite 17 | .add("insert words in order in array", function () { 18 | var arr = []; 19 | for (var i = 0, l = words.length; i < l; i++) { 20 | arr.push(words[i]); 21 | arr.sort(); 22 | } 23 | }) 24 | .add("insert words into AnderssonTree", function () { 25 | var tree = new Tree(); 26 | for (var i = 0, l = words.length; i < l; i++) { 27 | tree.insert(words[i]); 28 | } 29 | }) 30 | .on('cycle', function (event) { 31 | console.log(String(event.target)); 32 | }) 33 | .on('complete', function () { 34 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 35 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 36 | }) 37 | .run(); 38 | 39 | (function () { 40 | var arr = [], tree = new Tree(); 41 | for (var i = 0, l = words.length; i < l; i++) { 42 | var word = words[i]; 43 | arr.push(word); 44 | tree.insert(word); 45 | } 46 | arr.sort(); 47 | 48 | new Benchmark.Suite() 49 | .add("look up words in array", function () { 50 | for (var i = 0, l = words.length; i < l; i++) { 51 | var index = arr.indexOf(words[i]); 52 | if (index === -1) { 53 | console.log("INDEX ERROR"); 54 | } 55 | } 56 | }) 57 | .add("look up words in AnderssonTree", function () { 58 | for (var i = 0, l = words.length; i < l; i++) { 59 | if (!tree.contains(words[i])) { 60 | console.log("INDEX ERROR"); 61 | } 62 | } 63 | }) 64 | .on('cycle', function (event) { 65 | console.log(String(event.target)); 66 | }) 67 | .on('complete', function () { 68 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 69 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 70 | }) 71 | .run(); 72 | 73 | new Benchmark.Suite() 74 | .add("remove words from array", function () { 75 | 76 | }); 77 | 78 | })(); 79 | 80 | -------------------------------------------------------------------------------- /benchmark/collections/BinaryTree.benchmark.js: -------------------------------------------------------------------------------- 1 | var comb = require("../../lib"), 2 | Tree = comb.collections.BinaryTree, 3 | Benchmark = require("benchmark"); 4 | 5 | var suite = new Benchmark.Suite(); 6 | 7 | var words = comb(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"].reverse()) 8 | .powerSet() 9 | .map(function (w) { 10 | return w.join(""); 11 | }).filter(function (a) { 12 | return a; 13 | }); 14 | console.log("BinaryTree Benchmark"); 15 | 16 | suite 17 | .add("insert words in order in array", function () { 18 | var arr = []; 19 | for (var i = 0, l = words.length; i < l; i++) { 20 | arr.push(words[i]); 21 | arr.sort(); 22 | } 23 | }) 24 | .add("insert words into BinaryTree", function () { 25 | var tree = new Tree(); 26 | for (var i = 0, l = words.length; i < l; i++) { 27 | tree.insert(words[i]); 28 | } 29 | }) 30 | .on('cycle', function (event) { 31 | console.log(String(event.target)); 32 | }) 33 | .on('complete', function () { 34 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 35 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 36 | }) 37 | .run(); 38 | 39 | (function () { 40 | var arr = [], tree = new Tree(); 41 | for (var i = 0, l = words.length; i < l; i++) { 42 | var word = words[i]; 43 | arr.push(word); 44 | tree.insert(word); 45 | } 46 | arr.sort(); 47 | 48 | new Benchmark.Suite() 49 | .add("look up words in array", function () { 50 | for (var i = 0, l = words.length; i < l; i++) { 51 | var index = arr.indexOf(words[i]); 52 | if (index === -1) { 53 | console.log("INDEX ERROR"); 54 | } 55 | } 56 | }) 57 | .add("look up words in BinaryTree", function () { 58 | for (var i = 0, l = words.length; i < l; i++) { 59 | if (!tree.contains(words[i])) { 60 | console.log("INDEX ERROR"); 61 | } 62 | } 63 | }) 64 | .on('cycle', function (event) { 65 | console.log(String(event.target)); 66 | }) 67 | .on('complete', function () { 68 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 69 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 70 | }) 71 | .run(); 72 | 73 | new Benchmark.Suite() 74 | .add("remove words from array", function () { 75 | 76 | }); 77 | 78 | })(); 79 | 80 | -------------------------------------------------------------------------------- /benchmark/collections/HashTable.benchmark.js: -------------------------------------------------------------------------------- 1 | var comb = require("../../lib"), 2 | Collection = comb.collections.HashTable, 3 | Benchmark = require("benchmark"); 4 | 5 | var suite = new Benchmark.Suite(); 6 | 7 | var words = comb(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"].reverse()) 8 | .powerSet() 9 | .map(function (w) { 10 | return w.join(""); 11 | }).filter(function (a) { 12 | return a; 13 | }); 14 | console.log("HashTable Benchmark"); 15 | 16 | suite 17 | .add("insert words into {}", function () { 18 | var map = {}; 19 | for (var i = 0, l = words.length; i < l; i++) { 20 | var word = words[i]; 21 | map[word] = word; 22 | } 23 | }) 24 | .add("insert words into HashTable", function () { 25 | var collection = new Collection(); 26 | for (var i = 0, l = words.length; i < l; i++) { 27 | var word = words[i]; 28 | collection.put(word, word); 29 | } 30 | }) 31 | .on('cycle', function (event) { 32 | console.log(String(event.target)); 33 | }) 34 | .on('complete', function () { 35 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 36 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 37 | }) 38 | .run(); 39 | 40 | (function () { 41 | var map = {}, collection = new Collection(); 42 | for (var i = 0, l = words.length; i < l; i++) { 43 | var word = words[i]; 44 | map[word] = word; 45 | collection.put(word, word); 46 | } 47 | 48 | new Benchmark.Suite() 49 | .add("look up words in {}", function () { 50 | for (var i = 0, l = words.length; i < l; i++) { 51 | if (!map.hasOwnProperty(words[i])) { 52 | console.log("INDEX ERROR"); 53 | } 54 | } 55 | }) 56 | .add("look up words in HashTable", function () { 57 | for (var i = 0, l = words.length; i < l; i++) { 58 | if (!collection.contains(words[i])) { 59 | console.log("INDEX ERROR"); 60 | } 61 | } 62 | }) 63 | .on('cycle', function (event) { 64 | console.log(String(event.target)); 65 | }) 66 | .on('complete', function () { 67 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 68 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 69 | }) 70 | .run(); 71 | 72 | new Benchmark.Suite() 73 | .add("remove words from array", function () { 74 | 75 | }); 76 | 77 | })(); 78 | 79 | -------------------------------------------------------------------------------- /benchmark/collections/RedBlackTree.benchmark.js: -------------------------------------------------------------------------------- 1 | var comb = require("../../lib"), 2 | Tree = comb.collections.RedBlackTree, 3 | Benchmark = require("benchmark"); 4 | 5 | var suite = new Benchmark.Suite(); 6 | 7 | var words = comb(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"].reverse()) 8 | .powerSet() 9 | .map(function (w) { 10 | return w.join(""); 11 | }).filter(function (a) { 12 | return a; 13 | }); 14 | console.log("RedBlackTree Benchmark"); 15 | 16 | suite 17 | .add("insert words in order in array", function () { 18 | var arr = []; 19 | for (var i = 0, l = words.length; i < l; i++) { 20 | arr.push(words[i]); 21 | arr.sort(); 22 | } 23 | }) 24 | .add("insert words into RedBlackTree", function () { 25 | var tree = new Tree(); 26 | for (var i = 0, l = words.length; i < l; i++) { 27 | tree.insert(words[i]); 28 | } 29 | }) 30 | .on('cycle', function (event) { 31 | console.log(String(event.target)); 32 | }) 33 | .on('complete', function () { 34 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 35 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 36 | }) 37 | .run(); 38 | 39 | (function () { 40 | var arr = [], tree = new Tree(); 41 | for (var i = 0, l = words.length; i < l; i++) { 42 | var word = words[i]; 43 | arr.push(word); 44 | tree.insert(word); 45 | } 46 | arr.sort(); 47 | 48 | new Benchmark.Suite() 49 | .add("look up words in array", function () { 50 | for (var i = 0, l = words.length; i < l; i++) { 51 | var index = arr.indexOf(words[i]); 52 | if (index === -1) { 53 | console.log("INDEX ERROR"); 54 | } 55 | } 56 | }) 57 | .add("look up words in RedBlackTree", function () { 58 | for (var i = 0, l = words.length; i < l; i++) { 59 | if (!tree.contains(words[i])) { 60 | console.log("INDEX ERROR"); 61 | } 62 | } 63 | }) 64 | .on('cycle', function (event) { 65 | console.log(String(event.target)); 66 | }) 67 | .on('complete', function () { 68 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 69 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 70 | }) 71 | .run(); 72 | 73 | new Benchmark.Suite() 74 | .add("remove words from array", function () { 75 | 76 | }); 77 | 78 | })(); 79 | 80 | -------------------------------------------------------------------------------- /benchmark/define.benchmark.js: -------------------------------------------------------------------------------- 1 | var Benchmark = require("benchmark"), 2 | comb = require("../index.js"), 3 | define = comb.define, 4 | sys = require("sys"), 5 | array = comb.array; 6 | var suite = new Benchmark.Suite(); 7 | suite.add('prototype', (function () { 8 | function User(name) { 9 | if (name) { 10 | this.name = name.trim(); 11 | } 12 | } 13 | 14 | function Admin(name) { 15 | User.call(this, name); 16 | } 17 | 18 | Admin.prototype = new User(); 19 | return function () { 20 | var admin = new Admin('doug'); 21 | }; 22 | }())); 23 | suite.add('sys.inherits()', (function () { 24 | function User(name) { 25 | if (name) { 26 | this.name = name.trim(); 27 | } 28 | } 29 | 30 | function Admin(name) { 31 | User.call(this, name); 32 | } 33 | 34 | sys.inherits(Admin, User); 35 | return function () { 36 | var admin = new Admin('doug'); 37 | }; 38 | }())); 39 | suite.add('comb.define', (function () { 40 | var User = define(null, { 41 | instance:{ 42 | constructor:function (name) { 43 | this.name = name.trim(); 44 | } 45 | } 46 | }); 47 | var Admin = define(User); 48 | return function () { 49 | var admin = new Admin('doug'); 50 | }; 51 | })()); 52 | suite.on('cycle', function (event) { 53 | console.log(String(event.target)); 54 | }); 55 | suite.on('complete', function () { 56 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 57 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 58 | }); 59 | suite.run(); 60 | 61 | var suite2 = new Benchmark.Suite() 62 | .add('prototype instance',function () { 63 | 64 | function User(name) { 65 | if (name) { 66 | this.name = name.trim(); 67 | } 68 | } 69 | 70 | function Admin(name) { 71 | User.call(this, name); 72 | } 73 | 74 | Admin.prototype = new User(); 75 | 76 | var admin = new Admin('doug'); 77 | }).add('sys.inherits() instance', function () { 78 | 79 | function User(name) { 80 | this.name = name.trim(); 81 | } 82 | 83 | function Admin(name) { 84 | User.call(this, name) 85 | } 86 | 87 | sys.inherits(Admin, User); 88 | var admin = new Admin('doug'); 89 | }) 90 | 91 | .add('comb.define instance', function () { 92 | var User = comb.define(null, { 93 | instance:{ 94 | constructor:function (name) { 95 | this.name = name.trim(); 96 | } 97 | }}); 98 | var Admin = define(User); 99 | var admin = new Admin('doug'); 100 | }) 101 | .on('cycle', function (event) { 102 | console.log(String(event.target)); 103 | }) 104 | .on('complete', function () { 105 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 106 | console.log('Slowest is ' + this.filter('slowest').pluck('name')); 107 | }) 108 | .run(); 109 | 110 | -------------------------------------------------------------------------------- /benchmark/promise.benchmark.js: -------------------------------------------------------------------------------- 1 | function runComb() { 2 | var Promise = require("../lib/promise").Promise; 3 | var start = new Date(); 4 | var i = -1, 5 | p = new Promise().callback(1); 6 | 7 | while (++i < 100000) { 8 | p = p.chain(function () { 9 | return i; 10 | }); 11 | } 12 | return p.chain(function () { 13 | console.log("%d MB", (process.memoryUsage().rss / 1024 / 1024)); 14 | console.log("%d ms", new Date() - start); 15 | }); 16 | } 17 | 18 | function runQ() { 19 | var Q = require("q"); 20 | var start = new Date(); 21 | var i = -1, 22 | p = Q(1); 23 | 24 | while (++i < 100000) { 25 | p = p.then(function () { 26 | return i; 27 | }); 28 | } 29 | p.then(function () { 30 | console.log("%d MB", (process.memoryUsage().rss / 1024 / 1024)); 31 | console.log("%d ms", new Date() - start); 32 | }); 33 | } 34 | 35 | runComb(); 36 | //runQ(); 37 | 38 | -------------------------------------------------------------------------------- /docs/assets/css/prettify.css: -------------------------------------------------------------------------------- 1 | .com{color:#93a1a1}.lit{color:#195f91}.pun,.opn,.clo{color:#93a1a1}.fun{color:#dc322f}.str,.atv{color:#D14}.kwd,.linenums .tag{color:#1e347b}.typ,.atn,.dec,.var{color:teal}.pln{color:#48484c}.prettyprint{padding:8px;background-color:#f7f7f9;border:1px solid #e1e1e8}.prettyprint.linenums{-webkit-box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0;-moz-box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0;box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0}ol.linenums{margin:0 0 0 33px}ol.linenums li{padding-left:12px;color:#bebec5;line-height:18px;text-shadow:0 1px 0 #fff} -------------------------------------------------------------------------------- /docs/assets/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/C2FO/comb/7fb23d2f56025721592ede14165311469ad30e76/docs/assets/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /docs/assets/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/C2FO/comb/7fb23d2f56025721592ede14165311469ad30e76/docs/assets/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./lib"); -------------------------------------------------------------------------------- /lib/base/broadcast.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var func = require("./functions"), 3 | obj = require("./object"); 4 | 5 | var comb = exports; 6 | 7 | 8 | var wrapper = function () { 9 | return function c() { 10 | var listeners = c.__listeners, func = c.func, r; 11 | if (func) { 12 | r = func.apply(this, arguments); 13 | } 14 | for (var i = 0; i < listeners.length; i++) { 15 | var lis = listeners[i]; 16 | if (lis) { 17 | lis.apply(this, arguments); 18 | } 19 | } 20 | return r; 21 | }; 22 | }; 23 | 24 | 25 | var listeners = {}; 26 | obj.merge(comb, { 27 | /**@lends comb*/ 28 | /** 29 | * Disconnects a listener to a function 30 | * @param {handle} A handle returned from comb.connect 31 | */ 32 | disconnect: function (handle) { 33 | if (handle && handle.length === 3) { 34 | var obj = handle[0], method = handle[1], cb = handle[2]; 35 | if (typeof method !== "string") { 36 | throw "comb.disconnect : When calling disconnect the method must be string"; 37 | } 38 | var scope = obj || global, ls; 39 | if (typeof scope[method] === "function") { 40 | ls = scope[method].__listeners; 41 | if (ls && cb-- > 0) { 42 | //we dont want to splice it because our indexing will get off 43 | ls[cb] = null; 44 | } 45 | } else { 46 | throw new Error("unknown method " + method + " in object " + obj); 47 | } 48 | } else { 49 | throw new Error("comb.disconnect : invalid handle"); 50 | } 51 | }, 52 | 53 | /** 54 | * Function to listen when other functions are called 55 | * 56 | * @example 57 | * 58 | * comb.connect(obj, "event", myfunc); 59 | * comb.connect(obj, "event", "log", console); 60 | * 61 | * @param {Object} obj the object in which the method you are connecting to resides 62 | * @param {String} method the name of the method to connect to 63 | * @param {Function} cb the function to callback 64 | * @param {Object} [scope] the scope to call the specified cb in 65 | * 66 | * @returns {Array} handle to pass to {@link comb.disconnect} 67 | */ 68 | connect: function (obj, method, cb, scope) { 69 | var index, 70 | listeners; 71 | if (typeof method !== "string") { 72 | throw new Error("When calling connect the method must be string"); 73 | } 74 | if (!func.isFunction(cb)) { 75 | throw new Error("When calling connect callback must be a string"); 76 | } 77 | scope = obj || global; 78 | if (typeof scope[method] === "function") { 79 | listeners = scope[method].__listeners; 80 | if (!listeners) { 81 | var newMethod = wrapper(); 82 | newMethod.func = obj[method]; 83 | listeners = (newMethod.__listeners = []); 84 | scope[method] = newMethod; 85 | } 86 | index = listeners.push(cb); 87 | } else { 88 | throw new Error("unknow method " + method + " in object " + obj); 89 | } 90 | return [obj, method, index]; 91 | }, 92 | 93 | 94 | /** 95 | * Broadcasts an event to all listeners 96 | * NOTE : the function takes a variable number of arguments 97 | * i.e. all arguments after the topic will be passed to the listeners 98 | * 99 | * @example 100 | * 101 | * 102 | * comb.broadcast("hello", "hello world"); 103 | * //the args "hello" and "world" will be passed to any listener of the topic 104 | * //"hello" 105 | * comb.broadcast("hello", "hello", "world"); 106 | * 107 | * @param {String} topic the topic to brodcast 108 | * @param params the information to bradcast 109 | */ 110 | broadcast: function () { 111 | var args = Array.prototype.slice.call(arguments); 112 | var topic = args.splice(0, 1)[0]; 113 | if (topic) { 114 | var list = listeners[topic]; 115 | if (list) { 116 | for (var i = list.length - 1; i >= 0; i--) { 117 | var han = list[i], cb = han.cb; 118 | if (cb) { 119 | cb.apply(this, args); 120 | } 121 | } 122 | } 123 | } 124 | }, 125 | 126 | /** 127 | * Listen for the broadcast of certain events 128 | * 129 | * @example 130 | * comb.listen("hello", function(arg1, arg2){ 131 | * console.log(arg1); 132 | * console.log(arg2); 133 | * }); 134 | * 135 | * @param {String} topic the topic to listen for 136 | * @param {Function} callback the funciton to call when the topic is published 137 | * 138 | * @returns a handle to pass to {@link comb.unListen} 139 | */ 140 | listen: function (topic, callback) { 141 | if (!func.isFunction(callback)) { 142 | throw new Error("callback must be a function"); 143 | } 144 | var handle = { 145 | topic: topic, 146 | cb: callback, 147 | pos: null 148 | }; 149 | var list = listeners[topic]; 150 | if (!list) { 151 | list = (listeners[topic] = []); 152 | } 153 | list.push(handle); 154 | handle.pos = list.length - 1; 155 | return handle; 156 | }, 157 | 158 | /** 159 | * Disconnects a listener 160 | * 161 | * @param handle a handle returned from {@link comb.listen} 162 | */ 163 | unListen: function (handle) { 164 | if (handle) { 165 | var topic = handle.topic, list = listeners[topic]; 166 | if (list) { 167 | for (var i = list.length - 1; i >= 0; i--) { 168 | if (list[i] === handle) { 169 | list.splice(i, 1); 170 | } 171 | } 172 | if (!list.length) { 173 | delete listeners[topic]; 174 | } 175 | } 176 | } 177 | } 178 | 179 | }); 180 | 181 | -------------------------------------------------------------------------------- /lib/base/date/transforms.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var floor = Math.floor, round = Math.round, min = Math.min, pow = Math.pow, ceil = Math.ceil, abs = Math.abs; 3 | 4 | var addMap = { 5 | day: function addDay(date, amount) { 6 | return [amount, "Date", false]; 7 | }, 8 | weekday: function addWeekday(date, amount) { 9 | // Divide the increment time span into weekspans plus leftover days 10 | // e.g., 8 days is one 5-day weekspan / and two leftover days 11 | // Can't have zero leftover days, so numbers divisible by 5 get 12 | // a days value of 5, and the remaining days make up the number of weeks 13 | var days, weeks, mod = amount % 5, strt = date.getDay(), adj = 0; 14 | if (!mod) { 15 | days = (amount > 0) ? 5 : -5; 16 | weeks = (amount > 0) ? ((amount - 5) / 5) : ((amount + 5) / 5); 17 | } else { 18 | days = mod; 19 | weeks = parseInt(amount / 5, 10); 20 | } 21 | if (strt === 6 && amount > 0) { 22 | adj = 1; 23 | } else if (strt === 0 && amount < 0) { 24 | // Orig date is Sun / negative increment 25 | // Jump back over Sat 26 | adj = -1; 27 | } 28 | // Get weekday val for the new date 29 | var trgt = strt + days; 30 | // New date is on Sat or Sun 31 | if ((trgt === 0 || trgt === 6) || ((trgt > 6 || trgt <= 0) && strt !== 6 && strt !== 0)) { 32 | adj = (amount > 0) ? 2 : -2; 33 | } 34 | // Increment by number of weeks plus leftover days plus 35 | // weekend adjustments 36 | return [(7 * weeks) + days + adj, "Date", false]; 37 | }, 38 | year: function addYear(date, amount) { 39 | return [amount, "FullYear", true]; 40 | }, 41 | week: function addWeek(date, amount) { 42 | return [amount * 7, "Date", false]; 43 | }, 44 | quarter: function addYear(date, amount) { 45 | return [amount * 3, "Month", true]; 46 | }, 47 | month: function addYear(date, amount) { 48 | return [amount, "Month", true]; 49 | } 50 | }; 51 | 52 | function addTransform(interval, date, amount) { 53 | interval = interval.replace(/s$/, ""); 54 | if (addMap.hasOwnProperty(interval)) { 55 | return addMap[interval](date, amount); 56 | } 57 | return [amount, "UTC" + interval.charAt(0).toUpperCase() + interval.substring(1) + "s", false]; 58 | } 59 | 60 | 61 | var differenceMap = { 62 | "quarter": function quarterDifference(date1, date2, utc) { 63 | var yearDiff = date2.getFullYear() - date1.getFullYear(); 64 | var m1 = date1[utc ? "getUTCMonth" : "getMonth"](); 65 | var m2 = date2[utc ? "getUTCMonth" : "getMonth"](); 66 | // Figure out which quarter the months are in 67 | var q1 = floor(m1 / 3) + 1; 68 | var q2 = floor(m2 / 3) + 1; 69 | // Add quarters for any year difference between the dates 70 | q2 += (yearDiff * 4); 71 | return q2 - q1; 72 | }, 73 | 74 | "weekday": function weekdayDifference(date1, date2, utc) { 75 | var days = differenceTransform("day", date1, date2, utc), weeks; 76 | var mod = days % 7; 77 | // Even number of weeks 78 | if (mod === 0) { 79 | days = differenceTransform("week", date1, date2, utc) * 5; 80 | } else { 81 | // Weeks plus spare change (< 7 days) 82 | var adj = 0, aDay = date1[utc ? "getUTCDay" : "getDay"](), bDay = date2[utc ? "getUTCDay" : "getDay"](); 83 | weeks = parseInt(days / 7, 10); 84 | // Mark the date advanced by the number of 85 | // round weeks (may be zero) 86 | var dtMark = new Date(date1); 87 | dtMark.setDate(dtMark[utc ? "getUTCDate" : "getDate"]() + (weeks * 7)); 88 | var dayMark = dtMark[utc ? "getUTCDay" : "getDay"](); 89 | 90 | // Spare change days -- 6 or less 91 | if (days > 0) { 92 | if (aDay === 6 || bDay === 6) { 93 | adj = -1; 94 | } else if (aDay === 0) { 95 | adj = 0; 96 | } else if (bDay === 0 || (dayMark + mod) > 5) { 97 | adj = -2; 98 | } 99 | } else if (days < 0) { 100 | if (aDay === 6) { 101 | adj = 0; 102 | } else if (aDay === 0 || bDay === 0) { 103 | adj = 1; 104 | } else if (bDay === 6 || (dayMark + mod) < 0) { 105 | adj = 2; 106 | } 107 | } 108 | days += adj; 109 | days -= (weeks * 2); 110 | } 111 | return days; 112 | }, 113 | year: function (date1, date2) { 114 | return date2.getFullYear() - date1.getFullYear(); 115 | }, 116 | month: function (date1, date2, utc) { 117 | var m1 = date1[utc ? "getUTCMonth" : "getMonth"](); 118 | var m2 = date2[utc ? "getUTCMonth" : "getMonth"](); 119 | return (m2 - m1) + ((date2.getFullYear() - date1.getFullYear()) * 12); 120 | }, 121 | week: function (date1, date2, utc) { 122 | return round(differenceTransform("day", date1, date2, utc) / 7); 123 | }, 124 | day: function (date1, date2) { 125 | return 1.1574074074074074e-8 * (date2.getTime() - date1.getTime()); 126 | }, 127 | hour: function (date1, date2) { 128 | return 2.7777777777777776e-7 * (date2.getTime() - date1.getTime()); 129 | }, 130 | minute: function (date1, date2) { 131 | return 0.000016666666666666667 * (date2.getTime() - date1.getTime()); 132 | }, 133 | second: function (date1, date2) { 134 | return 0.001 * (date2.getTime() - date1.getTime()); 135 | }, 136 | millisecond: function (date1, date2) { 137 | return date2.getTime() - date1.getTime(); 138 | } 139 | }; 140 | 141 | 142 | function differenceTransform(interval, date1, date2, utc) { 143 | interval = interval.replace(/s$/, ""); 144 | return round(differenceMap[interval](date1, date2, utc)); 145 | } 146 | 147 | 148 | exports.addTransform = addTransform; 149 | exports.differenceTransform = differenceTransform; -------------------------------------------------------------------------------- /lib/base/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var objectBase = require("./object"); 3 | 4 | objectBase.merge(exports, objectBase, 5 | require("./broadcast"), 6 | require("./functions"), 7 | require("./string"), 8 | require("./number"), 9 | require("./misc"), 10 | require("./date"), 11 | require("./array"), 12 | require("./regexp"), 13 | require("./inflections"), 14 | require("./characters")); -------------------------------------------------------------------------------- /lib/base/misc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var comb = exports, 3 | arraySlice = Array.prototype.slice; 4 | 5 | /** 6 | * 7 | * Converts an arguments object to an array 8 | * 9 | * @example 10 | * 11 | * function test(){ 12 | * return comb.argsToArray(arguments); 13 | * } 14 | * 15 | * function testSlice(){ 16 | * return comb.argsToArray(arguments, 3); 17 | * } 18 | * 19 | * console.log(test(1,2,3)); //[1,2,3] 20 | * console.log(test(1,2,3,4,5,6)); //[4,5,6] 21 | * 22 | * @function 23 | * @param {Arguments} args the arguments object to convert 24 | * @param {Number} [slice=0] the number of arguments to slice. 25 | * @memberOf comb 26 | * @static 27 | * @returns {Array} array version of the arguments object 28 | */ 29 | function argsToArray(args, slice) { 30 | slice = slice || 0; 31 | return arraySlice.call(args, slice); 32 | } 33 | 34 | /** 35 | * Determines if obj is a boolean 36 | * 37 | * @param {Anything} obj the thing to test if it is a boolean 38 | * 39 | * @returns {Boolean} true if it is a boolean false otherwise 40 | * @memberOf comb 41 | * @static 42 | */ 43 | function isBoolean(obj) { 44 | var undef, type = typeof obj; 45 | return obj !== undef && type === "boolean" || type === "Boolean"; 46 | } 47 | 48 | /** 49 | * Determines if obj is undefined 50 | * 51 | * @param {Anything} obj the thing to test if it is undefined 52 | * @returns {Boolean} true if it is undefined false otherwise 53 | * @memberOf comb 54 | * @static 55 | */ 56 | function isUndefined(obj) { 57 | var undef; 58 | return obj !== null && obj === undef; 59 | } 60 | 61 | 62 | /** 63 | * Determins if the obj is not undefined 64 | * 65 | * @param obj the thing to test if it is not undefined 66 | * 67 | * @return {Boolean} true if it is defined false otherwise 68 | * @memberOf comb 69 | * @static 70 | */ 71 | function isDefined(obj) { 72 | return !isUndefined(obj); 73 | } 74 | 75 | /** 76 | * Determines if obj is undefined or null 77 | * 78 | * @param {Anything} obj the thing to test if it is undefined or null 79 | * @returns {Boolean} true if it is undefined or null false otherwise 80 | * @memberOf comb 81 | * @static 82 | */ 83 | function isUndefinedOrNull(obj) { 84 | return isUndefined(obj) || isNull(obj); 85 | } 86 | 87 | /** 88 | * Determines if obj is null 89 | * 90 | * @param {Anything} obj the thing to test if it is null 91 | * 92 | * @returns {Boolean} true if it is null false otherwise 93 | * @memberOf comb 94 | * @static 95 | */ 96 | function isNull(obj) { 97 | var undef; 98 | return obj !== undef && obj == null; 99 | } 100 | 101 | /** 102 | * Determines if obj is an Arguments object; 103 | * 104 | * @param {Anything} obj the thing to test if it is null 105 | * 106 | * @returns {Boolean} true if it is an Arguments Object false otherwise 107 | * @memberOf comb 108 | * @static 109 | */ 110 | function isArguments(object) { 111 | return !isUndefinedOrNull(object) && Object.prototype.toString.call(object) === '[object Arguments]'; 112 | } 113 | 114 | 115 | function isInstance(obj, clazz) { 116 | if (typeof clazz === "function") { 117 | return obj instanceof clazz; 118 | } else { 119 | return false; 120 | } 121 | } 122 | 123 | /** 124 | * Determines if obj is an instance of a particular class 125 | * 126 | * @param {Anything} obj the thing to test if it and instance of a class 127 | * @param {Object} Clazz used to determine if the object is an instance of 128 | * 129 | * @returns {Boolean} true if it is an instance of the clazz false otherwise 130 | * @memberOf comb 131 | * @static 132 | */ 133 | function isInstanceOf(obj, clazz) { 134 | return argsToArray(arguments, 1).some(function (c) { 135 | return isInstance(obj, c); 136 | }); 137 | } 138 | 139 | (function () { 140 | 141 | var listeners = []; 142 | var setup = false; 143 | 144 | function setupListener() { 145 | if (!setup) { 146 | var orig = process.emit; 147 | process.emit = function (event) { 148 | try { 149 | if (event === 'exit') { 150 | listeners.forEach(function (cb) { 151 | cb(); 152 | }); 153 | } 154 | } finally { 155 | orig.apply(this, arguments); 156 | } 157 | }; 158 | setup = true; 159 | } 160 | } 161 | 162 | /** 163 | * Adds listeners to process.exit without having to change setMaxListeners useful if you 164 | * are writing a library and do not want to change core setting. 165 | * 166 | * @param {Funciton} cb funciton to call when process is exiting 167 | * @memberOf comb 168 | * @static 169 | */ 170 | function listenForExit(cb) { 171 | setupListener(); 172 | listeners.push(cb); 173 | } 174 | 175 | comb.listenForExit = listenForExit; 176 | })(); 177 | 178 | comb.argsToArray = argsToArray; 179 | comb.isBoolean = isBoolean; 180 | comb.isUndefined = isUndefined; 181 | comb.isDefined = isDefined; 182 | comb.isUndefinedOrNull = isUndefinedOrNull; 183 | comb.isNull = isNull; 184 | comb.isArguments = isArguments; 185 | comb.isInstanceOf = isInstanceOf; 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /lib/base/number.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var comb = exports; 3 | 4 | /** 5 | * Determines if obj is a number 6 | * 7 | * @param {Anything} obj the thing to test if it is a Number 8 | * 9 | * @returns {Boolean} true if it is a number false otherwise 10 | */ 11 | comb.isNumber = function(obj) { 12 | var undef; 13 | return obj !== undef && obj != null && (typeof obj === "number" || obj instanceof Number); 14 | }; 15 | 16 | /** 17 | * @private 18 | */ 19 | var round = Math.round, pow = Math.pow; 20 | 21 | 22 | /** 23 | * Rounds a number to the specified places, rounding up. 24 | * 25 | * @example 26 | * comb.number.roundCeil(10.000001, 2); //10.01 27 | * comb.number.roundCeil(10.000002, 5); //10.00001 28 | * comb.number.roundCeil(10.0003, 3); //10.001 29 | * comb.number.roundCeil(10.0004, 2); //10.01 30 | * comb.number.roundCeil(10.0005, 3); //10.001 31 | * comb.number.roundCeil(10.0002, 2); //10.01 32 | * 33 | * @param {Number} num the number to round. 34 | * @param {Number} precision the number of places to round to. 35 | * @static 36 | * @memberOf comb.number 37 | */ 38 | function roundCeil(num, precision){ 39 | return Math.ceil(num * Math.pow(10, precision))/Math.pow(10, precision); 40 | } 41 | 42 | 43 | /** 44 | * Rounds a number half down to the specified places. 45 | * @example 46 | * 47 | * comb.number.roundHalfDown(0.225, 2); //0.22 48 | * comb.number.roundHalfDown(10.384, 2); //10.38 49 | * comb.number.roundHalfDown(10.386, 2); //10.38 50 | * comb.number.roundHalfDown(10.3869, 3); //10.386 51 | * comb.number.roundHalfDown(10.3861, 3); //10.386 52 | * comb.number.roundHalfDown(10.269019, 5); //10.26901 53 | * 54 | * @param {Number} num the number to round. 55 | * @param {Number} precision the number of places to round to. 56 | * @static 57 | * @memberOf comb.number 58 | */ 59 | function roundHalfDown(num, precision) { 60 | var multiplier = pow(10, precision); 61 | return Math.floor(num * multiplier) / multiplier; 62 | } 63 | 64 | 65 | /** 66 | * Rounds a number half up to the specified places. 67 | * @example 68 | * 69 | * comb.number.roundHalfUp(0.225, 2); //0.23 70 | * comb.number.roundHalfUp(10.384, 2); //10.38 71 | * comb.number.roundHalfUp(10.386, 2); //10.39 72 | * comb.number.roundHalfUp(10.3869, 3); //10.387 73 | * comb.number.roundHalfUp(10.3861, 3); //10.386 74 | * comb.number.roundHalfUp(10.269019, 5); //10.26902 75 | * comb.number.roundHalfUp(-2.384, 2); //-2.38 76 | * comb.number.roundHalfUp(-2.385, 2); //-2.38 77 | * comb.number.roundHalfUp(-2.386, 2); //-2.39 78 | * 79 | * @param {Number} num the number to round. 80 | * @param {Number} precision the number of places to round to. 81 | * @static 82 | * @memberOf comb.number 83 | */ 84 | function roundHalfUp(num, precision) { 85 | var multiplier = pow(10, precision), 86 | numMod = parseInt((num * multiplier), 10), 87 | lastDigit = parseInt(num * (multiplier * 10), 10) - (numMod * 10); 88 | if (lastDigit < -5) { 89 | numMod -= 1; 90 | } else if (lastDigit >= 5) { 91 | numMod += 1; 92 | } 93 | return numMod / multiplier; 94 | } 95 | 96 | 97 | /** 98 | * @namespace Utilities for numbers 99 | * 100 | * The `comb.number` namespace can be used to decorate numbers with additional chainable functionality 101 | * @ignoreCode 102 | */ 103 | comb.number = { 104 | round: roundHalfUp, 105 | roundCeil: roundCeil, 106 | roundHalfDown: roundHalfDown, 107 | roundHalfUp: roundHalfUp 108 | }; -------------------------------------------------------------------------------- /lib/base/regexp.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var comb = exports; 3 | 4 | 5 | /** 6 | * Tests if something is a regular expression. 7 | * 8 | * @example 9 | * 10 | * comb.isRegExp(/hello/); //true 11 | * comb.isRegExp("hello"); //false 12 | * 13 | * @param obj the thing to test. 14 | * @return {Boolean} 15 | * @static 16 | * @memberOf comb 17 | * 18 | */ 19 | function isRegExp(obj) { 20 | var undef; 21 | return obj !== undef && obj != null && (obj instanceof RegExp); 22 | } 23 | 24 | comb.isRexExp = isRegExp; 25 | comb.isRegExp = isRegExp; 26 | 27 | /** 28 | * @namespace Regeular expression utilities 29 | * 30 | */ 31 | comb.regexp = { 32 | /**@lends comb.regexp*/ 33 | /** 34 | * Escapes a string 35 | * 36 | * @param {String} str the string to escape 37 | * @param {String} [except] characters to ignore 38 | * 39 | * @returns {String} the escaped string 40 | */ 41 | escapeString:function (str, except) { 42 | return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function (ch) { 43 | if (except && except.indexOf(ch) !== -1) { 44 | return ch; 45 | } 46 | return "\\" + ch; 47 | }); // String 48 | } 49 | }; -------------------------------------------------------------------------------- /lib/collections/AnderssonTree.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | Tree = require("./Tree"), 4 | base = require("../base"), 5 | multiply = base.string.multiply, 6 | RED = "red", 7 | BLACK = "black", 8 | nil = {level: 0, data: null}; 9 | 10 | /** 11 | * 12 | * @ignoreCode 13 | * @class

Andersson Trees are a version of a balanced Binary tree, while similar to RedBlack Trees the balancing is not as strict.

14 | * 15 | * Performance 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | *
BestWorst
spaceO(n)O(n)
SearchO(log n)O(log n)
InsertO(log n)O(log n)
DeleteO(log n)O(log n)
23 | * @name AnderssonTree 24 | * @augments comb.collections.Tree 25 | * @memberOf comb.collections 26 | */ 27 | 28 | 29 | module.exports = exports = define(Tree, { 30 | 31 | instance: { 32 | /**@lends comb.collections.AnderssonTree.prototype*/ 33 | 34 | isEmpty: function () { 35 | return this.__root === nil || this._super(arguments); 36 | }, 37 | 38 | insert: function (data) { 39 | if (this.__root == null) { 40 | this.__root = nil; 41 | } 42 | this.__root = insert(this.__root, data, this.compare); 43 | }, 44 | 45 | remove: function (data) { 46 | this.__root = remove(this.__root, data, this.compare); 47 | }, 48 | 49 | 50 | traverseWithCondition: function (node, order, callback) { 51 | var cont = true; 52 | if (node !== nil) { 53 | return this._super(arguments); 54 | } 55 | return cont; 56 | }, 57 | 58 | 59 | traverse: function (node, order, callback) { 60 | if (node !== nil) { 61 | this._super(arguments); 62 | } 63 | }, 64 | 65 | contains: function (value) { 66 | if (this.__root !== nil) { 67 | return this._super(arguments); 68 | } 69 | return false; 70 | }, 71 | 72 | __printNode: function (node, level) { 73 | var str = []; 74 | if (node.data == null || node == null) { 75 | str.push(multiply('\t', level)); 76 | str.push("~"); 77 | console.log(str.join("")); 78 | } else { 79 | this.__printNode(node.right, level + 1); 80 | str.push(multiply('\t', level)); 81 | str.push(node.data + ":" + node.level + "\n"); 82 | console.log(str.join("")); 83 | this.__printNode(node.left, level + 1); 84 | } 85 | } 86 | 87 | } 88 | 89 | }); 90 | 91 | function makeNode(data, level) { 92 | return { 93 | data: data, 94 | level: level, 95 | left: nil, 96 | right: nil 97 | }; 98 | } 99 | 100 | function skew(root) { 101 | if (root.level !== 0 && root.left.level === root.level) { 102 | var save = root.left; 103 | root.left = save.right; 104 | save.right = root; 105 | root = save; 106 | } 107 | return root; 108 | } 109 | 110 | function split(root) { 111 | if (root.level !== 0 && root.right.right.level === root.level) { 112 | var save = root.right; 113 | root.right = save.left; 114 | save.left = root; 115 | root = save; 116 | ++root.level; 117 | } 118 | return root; 119 | } 120 | 121 | function insert(root, data, compare) { 122 | if (root === nil) { 123 | root = makeNode(data, 1); 124 | } 125 | else { 126 | var dir = compare(data, root.data) === -1 ? "left" : "right"; 127 | root[dir] = insert(root[dir], data, compare); 128 | root = skew(root); 129 | root = split(root); 130 | } 131 | return root; 132 | } 133 | 134 | function remove(root, data, compare) { 135 | var rLeft, rRight; 136 | if (root !== nil) { 137 | var cmp = compare(data, root.data); 138 | if (cmp === 0) { 139 | rLeft = root.left, rRight = root.right; 140 | if (rLeft !== nil && rRight !== nil) { 141 | var heir = rLeft; 142 | while (heir.right !== nil) { 143 | heir = heir.right; 144 | } 145 | root.data = heir.data; 146 | root.left = remove(rLeft, heir.data, compare); 147 | } else { 148 | root = root[rLeft === nil ? "right" : "left"]; 149 | } 150 | } else { 151 | var dir = cmp === -1 ? "left" : "right"; 152 | root[dir] = remove(root[dir], data, compare); 153 | } 154 | } 155 | if (root !== nil) { 156 | var rLevel = root.level; 157 | var rLeftLevel = root.left.level, rRightLevel = root.right.level; 158 | if (rLeftLevel < rLevel - 1 || rRightLevel < rLevel - 1) { 159 | if (rRightLevel > --root.level) { 160 | root.right.level = root.level; 161 | } 162 | 163 | root = skew(root); 164 | root = split(root); 165 | } 166 | } 167 | return root; 168 | } 169 | 170 | -------------------------------------------------------------------------------- /lib/collections/BinaryTree.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | Tree = require("./Tree"), 4 | base = require("../base"); 5 | 6 | /** 7 | * 8 | * @ignoreCode 9 | * @class

A Search tree that maintains the following properties

10 | * 15 | * 16 | * Performance 17 | *
18 | * 19 | * 20 | * 21 | * 22 | * 23 | *
BestWorst
SpaceO(n)O(n)
SearchO(log n)O(n)
InsertO(log n)O(n)
DeleteO(log n)O(n)
24 | * @name BinaryTree 25 | * @augments comb.collections.Tree 26 | * @memberOf comb.collections 27 | */ 28 | module.exports = exports = define(Tree, { 29 | instance: { 30 | /**@lends comb.collections.BinaryTree.prototype*/ 31 | 32 | insert: function (data) { 33 | if (this.__root == null) { 34 | this.__root = { 35 | data: data, 36 | parent: null, 37 | left: null, 38 | right: null 39 | }; 40 | return this.__root; 41 | } 42 | var compare = this.compare; 43 | var root = this.__root; 44 | while (root != null) { 45 | var cmp = compare(data, root.data); 46 | if (cmp) { 47 | var leaf = (cmp === -1) ? "left" : "right"; 48 | var next = root[leaf]; 49 | if (next == null) { 50 | root[leaf] = {data: data, parent: root, left: null, right: null}; 51 | return root[leaf]; 52 | } else { 53 | root = next; 54 | } 55 | } else { 56 | return null; 57 | } 58 | } 59 | }, 60 | 61 | remove: function (data) { 62 | if (this.__root != null) { 63 | var head = {right: this.__root}, it = head; 64 | var p, f = null; 65 | var dir = "right"; 66 | while (it[dir] != null) { 67 | p = it; 68 | it = it[dir]; 69 | var cmp = this.compare(data, it.data); 70 | if (!cmp) { 71 | f = it; 72 | } 73 | dir = (cmp === -1 ? "left" : "right"); 74 | } 75 | if (f != null) { 76 | f.data = it.data; 77 | p[p.right === it ? "right" : "left"] = it[it.left == null ? "right" : "left"]; 78 | } 79 | this.__root = head.right; 80 | } 81 | 82 | } 83 | } 84 | }); 85 | 86 | 87 | -------------------------------------------------------------------------------- /lib/collections/Collection.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | base = require("../base"); 4 | 5 | /** 6 | * @ignoreCode 7 | * @class Base class for all collections 8 | * @name Collection 9 | * @memberOf comb.collections 10 | */ 11 | define(null, { 12 | instance: { 13 | /**@lends comb.collections.Collection.prototype*/ 14 | 15 | /** 16 | * Concats two collections 17 | */ 18 | concat: function () { 19 | throw new Error("Not Implemented"); 20 | }, 21 | 22 | /** 23 | * Joins two collections 24 | */ 25 | join: function () { 26 | throw new Error("Not Implemented"); 27 | }, 28 | 29 | /** 30 | * Slice a portion from a collection 31 | */ 32 | slice: function () { 33 | throw new Error("Not Implemented"); 34 | }, 35 | 36 | /** 37 | * Convert a collection to a string 38 | */ 39 | toString: function () { 40 | throw new Error("Not Implemented"); 41 | }, 42 | 43 | /** 44 | * Find the index of an item in a collection 45 | */ 46 | indexOf: function () { 47 | throw new Error("Not Implemented"); 48 | }, 49 | 50 | /** 51 | * Find the last index of an item in a collection 52 | */ 53 | lastIndexOf: function () { 54 | throw new Error("Not Implemented"); 55 | } 56 | } 57 | }).as(module); -------------------------------------------------------------------------------- /lib/collections/Iterable.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | base = require("../base"); 4 | 5 | /** 6 | * @ignoreCode 7 | * @class Base class for all collections 8 | * @name Iterable 9 | * @memberOf comb.collections 10 | */ 11 | define(null, { 12 | instance: { 13 | /**@lends comb.collections.Iterable.prototype*/ 14 | 15 | /** 16 | * Filter items from a collection 17 | */ 18 | filter: function () { 19 | throw new Error("Not Implemented"); 20 | }, 21 | 22 | /** 23 | * Loop through the items in a collection 24 | */ 25 | forEach: function () { 26 | throw new Error("Not Implemented"); 27 | }, 28 | 29 | /** 30 | * Determine if every item in a collection meets the criteria 31 | */ 32 | every: function () { 33 | throw new Error("Not Implemented"); 34 | }, 35 | 36 | /** 37 | * Map every item in a collection 38 | */ 39 | map: function () { 40 | throw new Error("Not Implemented"); 41 | }, 42 | 43 | /** 44 | * Determing if some items in a colleciton meet the criteria 45 | */ 46 | some: function () { 47 | throw new Error("Not Implemented"); 48 | }, 49 | 50 | /** 51 | * Reduce a collection 52 | */ 53 | reduce: function () { 54 | throw new Error("Not Implemented"); 55 | }, 56 | 57 | /** 58 | * Reduce a collection starting from the right most position 59 | */ 60 | reduceRight: function () { 61 | throw new Error("Not Implemented"); 62 | } 63 | } 64 | }).as(module); 65 | -------------------------------------------------------------------------------- /lib/collections/MaxHeap.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | Heap = require("./Heap"), 4 | base = require("../base"); 5 | 6 | /** 7 | * @ignoreCode 8 | * 9 | * @class

Max Heap implementation, lowest value in heap is always at the root.

10 | *
11 | * Performance 12 | *
13 | * 14 | * 15 | * 16 | * 17 | * 18 | *
BestWorst
InsertO(log n)O(log n)
RemoveO(log n)O(log n)
PeekO(1)O(1)
ContainsO(n)O(n)
19 | * @name MaxHeap 20 | * @augments comb.collections.Heap 21 | * @memberOf comb.collections 22 | */ 23 | exports = module.exports = define(Heap, { 24 | instance: { 25 | 26 | 27 | __upHeap: function (index) { 28 | var heap = this.__heap; 29 | var node = heap[index]; 30 | while (index >= 0) { 31 | var parentIndex = this.__getParentIndex(index), parent = heap[parentIndex]; 32 | if (parent && parent.key < node.key) { 33 | heap[index] = parent; 34 | index = parentIndex; 35 | } else { 36 | break; 37 | } 38 | } 39 | heap[index] = node; 40 | }, 41 | 42 | __downHeap: function (index) { 43 | var heap = this.__heap; 44 | var node = heap[index], length = heap.length; 45 | while (index < Math.floor(length / 2)) { 46 | var leftIndex = this.__getLeftChildIndex(index), 47 | rightIndex = this.__getRightChildIndex(index), left = heap[leftIndex], right = heap[rightIndex], child, childIndex; 48 | if (rightIndex < length && right.key < left.key) { 49 | childIndex = leftIndex; 50 | child = left; 51 | } else { 52 | childIndex = leftIndex; 53 | child = heap[leftIndex]; 54 | } 55 | if (child.key > node.key) { 56 | heap[index] = child; 57 | index = childIndex; 58 | } else { 59 | break; 60 | } 61 | 62 | } 63 | heap[index] = node; 64 | } 65 | 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /lib/collections/MinHeap.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | Heap = require("./Heap"), 4 | base = require("../base"); 5 | 6 | var floor = Math.floor, MinHeap; 7 | /** 8 | * @class

Min Heap implementation, lowest value in heap is always at the root.

9 | *
10 | * Performance 11 | *
12 | * 13 | * 14 | * 15 | * 16 | * 17 | *
BestWorst
InsertO(log n)O(log n)
RemoveO(log n)O(log n)
PeekO(1)O(1)
ContainsO(n)O(n)
18 | * @name MinHeap 19 | * @augments comb.collections.Heap 20 | * @memberOf comb.collections 21 | * @ignoreCode 22 | */ 23 | module.exports = exports = define(Heap, { 24 | instance: { 25 | 26 | __upHeap: function (index) { 27 | var heap = this.__heap; 28 | var node = heap[index], key = node.key, gpi = this.__getParentIndex; 29 | while (index >= 0) { 30 | var parentIndex = gpi(index), parent = heap[parentIndex]; 31 | if (parent && parent.key > key) { 32 | heap[index] = parent; 33 | index = parentIndex; 34 | } else { 35 | break; 36 | } 37 | } 38 | heap[index] = node; 39 | }, 40 | 41 | __downHeap: function (index) { 42 | var heap = this.__heap; 43 | var node = heap[index], key = node.key, length = heap.length, max = floor(length / 2), glci = this.__getLeftChildIndex, grci = this.__getRightChildIndex; 44 | while (index < max) { 45 | var leftIndex = glci(index), 46 | rightIndex = grci(index), left = heap[leftIndex], right = heap[rightIndex], child, childIndex; 47 | if (rightIndex < length && right.key < left.key) { 48 | childIndex = rightIndex; 49 | child = right; 50 | } else { 51 | childIndex = leftIndex; 52 | child = left; 53 | } 54 | if (child.key < key) { 55 | heap[index] = child; 56 | index = childIndex; 57 | } else { 58 | break; 59 | } 60 | 61 | } 62 | heap[index] = node; 63 | } 64 | } 65 | }); 66 | 67 | -------------------------------------------------------------------------------- /lib/collections/Pool.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | Collection = require("./Collection"), 4 | Queue = require("./Queue"), 5 | base = require("../base"); 6 | 7 | /** 8 | * @class Base class for a pool. 9 | * 10 | * @name Pool 11 | * @memberOf comb.collections 12 | * 13 | * @property {Number} count the total number of objects in the pool, including free and in use objects. 14 | * @property {Number} freeCount the number of free objects in this pool. 15 | * @property {Number} inUseCount the number of objects in use in this pool. 16 | * @property {Number} [minObjects=0] the minimum number of objects this pool should contain. 17 | * @property {Number} [maxObjects=1] the maximum number of objects this pool should contain 18 | * @ignoreCode 19 | */ 20 | exports = module.exports = define(null, { 21 | instance: { 22 | /**@lends comb.collections.Pool.prototype*/ 23 | 24 | __minObjects: 0, 25 | 26 | __maxObjects: 1, 27 | 28 | constructor: function (options) { 29 | options = options || {}; 30 | this.__freeObjects = new Queue(); 31 | this.__inUseObjects = []; 32 | this.__minObjects = options.minObjects || 0; 33 | this.__maxObjects = options.maxObjects || 1; 34 | this.minObjects = this.__minObjects; 35 | this.maxObjects = this.__maxObjects; 36 | }, 37 | 38 | /** 39 | * Retrieves an object from this pool. 40 | * ` 41 | * @return {*} an object to contained in this pool 42 | */ 43 | getObject: function () { 44 | var ret; 45 | if (this.count <= this.__maxObjects && this.freeCount > 0) { 46 | ret = this.__freeObjects.dequeue(); 47 | this.__inUseObjects.push(ret); 48 | } else if (this.count < this.__maxObjects) { 49 | ret = this.createObject(); 50 | this.__inUseObjects.push(ret); 51 | } 52 | return ret; 53 | }, 54 | 55 | /** 56 | * Returns an object to this pool. The object is validated before it is returned to the pool, 57 | * if the validation fails then it is removed from the pool; 58 | * @param {*} obj the object to return to the pool 59 | */ 60 | returnObject: function (obj) { 61 | var index; 62 | if (this.validate(obj) && this.count <= this.__maxObjects && (index = this.__inUseObjects.indexOf(obj)) > -1) { 63 | this.__freeObjects.enqueue(obj); 64 | this.__inUseObjects.splice(index, 1); 65 | } else { 66 | this.removeObject(obj); 67 | } 68 | }, 69 | 70 | /** 71 | * Removes an object from the pool, this can be overriden to provide any 72 | * teardown of objects that needs to take place. 73 | * 74 | * @param {*} obj the object that needs to be removed. 75 | * 76 | * @return {*} the object removed. 77 | */ 78 | removeObject: function (obj) { 79 | var index; 80 | if (this.__freeObjects.contains(obj)) { 81 | this.__freeObjects.remove(obj); 82 | } else if ((index = this.__inUseObjects.indexOf(obj)) > -1) { 83 | this.__inUseObjects.splice(index, 1); 84 | } 85 | //otherwise its not contained in this pool; 86 | return obj; 87 | }, 88 | 89 | /** 90 | * Validates an object in this pool. 91 | *
92 | * THIS SHOULD BE OVERRIDDEN TO VALIDATE 93 | * 94 | * @param {*} obj the object to validate. 95 | */ 96 | validate: function (obj) { 97 | return true; 98 | }, 99 | 100 | /** 101 | * Creates a new object for this pool. 102 | * *
103 | * THIS SHOULD BE OVERRIDDEN TO ADD THE CORRECT TYPE OF OBJECT 104 | * 105 | * @return {Object} be default just creates an object. 106 | */ 107 | createObject: function () { 108 | return {}; 109 | }, 110 | 111 | setters: { 112 | minObjects: function (l) { 113 | 114 | if (l <= this.__maxObjects) { 115 | this.__minObjects = l; 116 | var i; 117 | if ((i = this.count) < l) { 118 | while (i++ < l) { 119 | this.__freeObjects.enqueue(this.createObject()); 120 | } 121 | } 122 | } else { 123 | throw "comb.collections.Pool : minObjects cannot be greater than maxObjects."; 124 | } 125 | }, 126 | maxObjects: function (l) { 127 | if (l >= this.__minObjects) { 128 | this.__maxObjects = l; 129 | var i = this.count, j = this.freeCount, fo = this.__freeObjects; 130 | while (i > l && j >= 0) { 131 | this.removeObject(fo.dequeue()); 132 | j--; 133 | i--; 134 | } 135 | } else { 136 | throw "comb.collections.Pool : maxObjects cannot be less than maxObjects."; 137 | } 138 | 139 | } 140 | }, 141 | 142 | getters: { 143 | freeCount: function () { 144 | return this.__freeObjects.count; 145 | }, 146 | inUseCount: function () { 147 | return this.__inUseObjects.length; 148 | }, 149 | count: function () { 150 | return this.__freeObjects.count + this.__inUseObjects.length; 151 | 152 | }, 153 | 154 | minObjects: function () { 155 | return this.__minObjects; 156 | }, 157 | maxObjects: function () { 158 | return this.__maxObjects; 159 | } 160 | } 161 | } 162 | }); -------------------------------------------------------------------------------- /lib/collections/PriorityQueue.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | MinHeap = require("./MinHeap"), 4 | base = require("../base"), 5 | PriorityQueue; 6 | 7 | /** 8 | * @class PriorityQueue Implementation where the value with the highest priority moves to the front 9 | * Priority starts at 0, and the greatest value being the lowest priority; 10 | * @name PriorityQueue 11 | * @augments comb.collections.MinHeap 12 | * @memberOf comb.collections 13 | * @ignoreCode 14 | */ 15 | PriorityQueue = define(MinHeap, { 16 | instance: { 17 | /**@lends comb.collections.PriorityQueue.prototype*/ 18 | 19 | /** 20 | * Adds the value with the specified priority to the queue 21 | * 22 | * @param {Number} priority the priority of the item 23 | *
24 | * 0 = Highest, n = lowest 25 | * @param value 26 | */ 27 | enqueue: function (priority, value) { 28 | return this.insert(priority, value); 29 | }, 30 | 31 | /** 32 | * Removes the item with the highest priority from the queue 33 | * 34 | * @returns the value of the item 35 | */ 36 | dequeue: function () { 37 | return this.remove(); 38 | } 39 | } 40 | }); 41 | 42 | module.exports = exports = PriorityQueue; -------------------------------------------------------------------------------- /lib/collections/Queue.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | Collection = require("./Collection"), 4 | base = require("../base"); 5 | 6 | /** 7 | * @class

FIFO Data structure

8 | * @name Queue 9 | * @augments comb.collections.Collection 10 | * @memberOf comb.collections 11 | * 12 | * @property {Number} count the current number of elements in this queue 13 | * @property {Boolean} isEmpty true if this queue is empty 14 | * @property {Array} values a copy of the values contained in this queue 15 | * @ignoreCode 16 | */ 17 | module.exports = exports = define(Collection, { 18 | instance: { 19 | /**@lends comb.collections.Queue.prototype*/ 20 | 21 | constructor: function () { 22 | this.__queue = []; 23 | this.__next = 0; 24 | this.__last = 0; 25 | }, 26 | 27 | /** 28 | * Add data to this queue 29 | * @param {*} data element to add 30 | */ 31 | enqueue: function (data) { 32 | this.__queue[this.__last++] = data; 33 | }, 34 | 35 | /** 36 | * Removes first item from the head of the queue 37 | * 38 | * @return {*} The element removed from this queue. Returns undefined if the queue is empty. 39 | */ 40 | dequeue: function () { 41 | var next = this.__next, 42 | ret, 43 | queue; 44 | if (next !== this.__last) { 45 | queue = this.__queue; 46 | ret = queue[next]; 47 | queue[this.__next++] = undefined; 48 | } 49 | return ret; 50 | }, 51 | 52 | /** 53 | * Retrieves the item at the head of the queue without removing it 54 | * 55 | * @return {*} The element at the head of the queue. Returns undefined if the queue is empty. 56 | */ 57 | peek: function () { 58 | var next = this.__next, 59 | ret; 60 | if (next !== this.__last) { 61 | ret = this.__queue[next]; 62 | } 63 | return ret; 64 | }, 65 | 66 | /** 67 | * Removes all items from this queue 68 | */ 69 | clear: function () { 70 | this.__queue.length = 0; 71 | this.__next = 0; 72 | this.__last = 0; 73 | }, 74 | 75 | /** 76 | * Determine if this queue contains the element 77 | * @param {*} obj the object to find 78 | * 79 | * @return {Boolean} true if this queue contains the element 80 | */ 81 | contains: function (obj) { 82 | return this.__queue.indexOf(obj) !== -1; 83 | }, 84 | /** 85 | * Removes an element from this queue. 86 | * @param {*} obj the data to remove. 87 | * 88 | * @return {Boolean} true if the element was removed, false otherwise. 89 | */ 90 | remove: function (obj) { 91 | var index = this.__queue.indexOf(obj), ret = false; 92 | if (index !== -1) { 93 | if (index === this.__next) { 94 | this.dequeue(); 95 | } else { 96 | this.__queue.splice(index, 1); 97 | this.__last--; 98 | } 99 | ret = true; 100 | } 101 | return ret; 102 | }, 103 | 104 | toString: function () { 105 | return this.__queue.toString(); 106 | }, 107 | 108 | getters: { 109 | 110 | count: function () { 111 | return this.__last - this.__next; 112 | }, 113 | 114 | isEmpty: function () { 115 | return this.__last - this.__next === 0; 116 | }, 117 | 118 | values: function () { 119 | return this.__queue.slice(this.__next, this.__last); 120 | } 121 | } 122 | } 123 | }); -------------------------------------------------------------------------------- /lib/collections/RedBlackTree.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | Tree = require("./Tree"), 4 | base = require("../base"), 5 | multiply = base.string.multiply, 6 | RED = "red", 7 | BLACK = "black", 8 | RedBlackTree; 9 | 10 | function isRed(node) { 11 | return node != null && node.red; 12 | } 13 | 14 | function makeNode(data) { 15 | return { 16 | data: data, 17 | red: true, 18 | left: null, 19 | right: null 20 | }; 21 | } 22 | 23 | var insert = function (root, data, compare) { 24 | if (root == null) { 25 | return makeNode(data, null); 26 | 27 | } else { 28 | var cmp = compare(data, root.data); 29 | if (cmp) { 30 | var dir = cmp === -1 ? "left" : "right"; 31 | var otherDir = dir === "left" ? "right" : "left"; 32 | root[dir] = insert(root[dir], data, compare); 33 | var node = root[dir]; 34 | 35 | if (isRed(node)) { 36 | 37 | var sibling = root[otherDir]; 38 | if (isRed(sibling)) { 39 | /* Case 1 */ 40 | root.red = true; 41 | node.red = false; 42 | sibling.red = false; 43 | } else { 44 | if (isRed(node[dir])) { 45 | root = rotateSingle(root, otherDir); 46 | } else if (isRed(node[otherDir])) { 47 | 48 | root = rotateDouble(root, otherDir); 49 | } 50 | } 51 | 52 | } 53 | } 54 | } 55 | return root; 56 | }; 57 | 58 | function rotateSingle(root, dir) { 59 | var otherDir = dir === "left" ? "right" : "left"; 60 | var save = root[otherDir]; 61 | root[otherDir] = save[dir]; 62 | save[dir] = root; 63 | root.red = true; 64 | save.red = false; 65 | return save; 66 | } 67 | 68 | function rotateDouble(root, dir) { 69 | var otherDir = dir === "left" ? "right" : "left"; 70 | root[otherDir] = rotateSingle(root[otherDir], otherDir); 71 | return rotateSingle(root, dir); 72 | } 73 | 74 | 75 | function remove(root, data, done, compare) { 76 | if (root == null) { 77 | done.done = true; 78 | } else { 79 | var dir; 80 | if (compare(data, root.data) === 0) { 81 | if (root.left == null || root.right == null) { 82 | var save = root[root.left == null ? "right" : "left"]; 83 | /* Case 0 */ 84 | if (isRed(root)) { 85 | done.done = true; 86 | } else if (isRed(save)) { 87 | save.red = false; 88 | done.done = true; 89 | } 90 | return save; 91 | } else { 92 | var heir = root.right, p; 93 | while (heir.left != null) { 94 | p = heir; 95 | heir = heir.left; 96 | } 97 | p && (p.left = null); 98 | root.data = heir.data; 99 | data = heir.data; 100 | } 101 | } 102 | dir = compare(data, root.data) === -1 ? "left" : "right"; 103 | root[dir] = remove(root[dir], data, done, compare); 104 | !done.done && (root = removeBalance(root, dir, done)); 105 | } 106 | return root; 107 | } 108 | 109 | function removeBalance(root, dir, done) { 110 | var notDir = dir === "left" ? "right" : "left"; 111 | var p = root, s = p[notDir]; 112 | if (isRed(s)) { 113 | root = rotateSingle(root, dir); 114 | s = p[notDir]; 115 | } 116 | if (s != null) { 117 | if (!isRed(s.left) && !isRed(s.right)) { 118 | isRed(p) && (done.done = true); 119 | p.red = 0; 120 | s.red = 1; 121 | } else { 122 | var save = p.red, newRoot = ( root === p ); 123 | p = (isRed(s[notDir]) ? rotateSingle : rotateDouble)(p, dir); 124 | p.red = save; 125 | p.left.red = p.right.red = 0; 126 | if (newRoot) { 127 | root = p; 128 | } else { 129 | root[dir] = p; 130 | } 131 | done.done = true; 132 | } 133 | } 134 | return root; 135 | } 136 | 137 | /** 138 | * @class

A RedBlack tree is a form of a self balancing binary tree.

139 | * 140 | * Performance 141 | *
142 | * 143 | * 144 | * 145 | * 146 | * 147 | *
BestWorst
SpaceO(n)O(n)
SearchO(log n)O(log n)
InsertO(log n)O(log n)
DeleteO(log n)O(log n)
148 | * @name RedBlackTree 149 | * @augments comb.collections.Tree 150 | * @memberOf comb.collections 151 | * @ignoreCode 152 | */ 153 | module.exports = exports = define(Tree, { 154 | instance: { 155 | /**@lends comb.collections.RedBlackTree.prototype*/ 156 | insert: function (data) { 157 | this.__root = insert(this.__root, data, this.compare); 158 | this.__root.red = false; 159 | }, 160 | 161 | remove: function (data) { 162 | var done = {done: false}; 163 | var root = remove(this.__root, data, done, this.compare); 164 | if (root != null) { 165 | root.red = 0; 166 | } 167 | this.__root = root; 168 | }, 169 | 170 | 171 | __printNode: function (node, level) { 172 | var str = []; 173 | if (node == null || node === undefined) { 174 | str.push(multiply('\t', level)); 175 | str.push("~"); 176 | console.log(str.join("")); 177 | } else { 178 | this.__printNode(node.right, level + 1); 179 | str.push(multiply('\t', level)); 180 | str.push((node.red ? "RED" : "BLACK") + ":" + node.data + "\n"); 181 | console.log(str.join("")); 182 | this.__printNode(node.left, level + 1); 183 | } 184 | } 185 | 186 | } 187 | }); 188 | 189 | -------------------------------------------------------------------------------- /lib/collections/Stack.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | Collection = require("./Collection"), 4 | base = require("../base"); 5 | 6 | /** 7 | * @class

LIFO Data structure

8 | * @name Stack 9 | * @augments comb.collections.Collection 10 | * @memberOf comb.collections 11 | * 12 | * @property {Number} count the current number of elements in this queue 13 | * @property {Boolean} isEmpty true if this queue is empty 14 | * @property {Array} values a copy of the values contained in this queue 15 | * @ignoreCode 16 | */ 17 | module.exports = exports = define(Collection, { 18 | instance: { 19 | /**@lends comb.collections.Stack.prototype*/ 20 | 21 | constructor: function () { 22 | this.__stack = []; 23 | this.__next = -1; 24 | }, 25 | 26 | /** 27 | * Add an item to the tail of this stack 28 | * @param {*} data item to qppend to this stack 29 | * 30 | */ 31 | push: function (data) { 32 | this.__stack[++this.__next] = data; 33 | }, 34 | 35 | /** 36 | * Removes the tail of this static 37 | * @return {*} the data at the tail of this stack 38 | */ 39 | pop: function () { 40 | var next = this.__next, 41 | ret, 42 | stack; 43 | 44 | if (next >= 0) { 45 | stack = this.__stack; 46 | ret = stack[next]; 47 | stack[this.__next--] = undefined; 48 | } 49 | return ret; 50 | }, 51 | 52 | /** 53 | * Retrieves the item at the tail of the stack without removing it 54 | * 55 | * @return {*} The element at the tail of the stack. Returns undefined if the stack is empty. 56 | */ 57 | peek: function () { 58 | var next = this.__next, 59 | ret; 60 | if (next >= 0) { 61 | ret = this.__stack[next]; 62 | } 63 | return ret; 64 | }, 65 | 66 | /** 67 | * Removes all items from this stack. 68 | */ 69 | clear: function () { 70 | this.__stack.length = 0; 71 | this.__next = -1; 72 | }, 73 | 74 | /** 75 | * Determine if this stack contains the element 76 | * @param {*} obj the object to find 77 | * 78 | * @return {Boolean} true if this stack contains the element 79 | */ 80 | contains: function (obj) { 81 | return this.__stack.indexOf(obj) !== -1; 82 | }, 83 | 84 | /** 85 | * Removes an element from this stack. 86 | * @param {*} obj the data to remove. 87 | * 88 | * @return {Boolean} true if the element was removed, false otherwise. 89 | */ 90 | remove: function (obj) { 91 | var index = this.__stack.indexOf(obj), ret = false; 92 | if (index !== -1) { 93 | if (index === this.__next) { 94 | this.pop(); 95 | } else { 96 | this.__stack.splice(index, 1); 97 | this.__next--; 98 | } 99 | ret = true; 100 | } 101 | return ret; 102 | }, 103 | 104 | toString: function () { 105 | return this.__stack.toString(); 106 | }, 107 | 108 | getters: { 109 | count: function () { 110 | return this.__next + 1; 111 | }, 112 | 113 | isEmpty: function () { 114 | return this.__next < 0; 115 | }, 116 | 117 | values: function () { 118 | return this.__stack.slice(0, this.__next + 1).reverse(); 119 | } 120 | } 121 | } 122 | }); -------------------------------------------------------------------------------- /lib/collections/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var comb = exports; 3 | /** 4 | * @ignore 5 | * @namespace Various collections*/ 6 | comb.collections = { 7 | Collection: require("./Collection"), 8 | Iterable: require("./Iterable"), 9 | Tree: require("./Tree"), 10 | BinaryTree: require("./BinaryTree"), 11 | RedBlackTree: require("./RedBlackTree"), 12 | AnderssonTree: require("./AnderssonTree"), 13 | AVLTree: require("./AVLTree"), 14 | HashTable: require("./HashTable"), 15 | Queue: require("./Queue"), 16 | Stack: require("./Stack"), 17 | Heap: require("./Heap"), 18 | MinHeap: require("./MinHeap"), 19 | MaxHeap: require("./MaxHeap"), 20 | PriorityQueue: require("./PriorityQueue"), 21 | Pool: require("./Pool") 22 | }; -------------------------------------------------------------------------------- /lib/extensions/arguments.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | utils = require("./utils"), 4 | extend = utils.extend, 5 | array = base.array; 6 | 7 | var methods = [ 8 | ["argsToArray", "toArray"] 9 | ]; 10 | 11 | module.exports = function (o) { 12 | return extend(o, methods, base); 13 | }; 14 | 15 | 16 | -------------------------------------------------------------------------------- /lib/extensions/array.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | array = base.array, 4 | string = base.string, 5 | utils = require("./utils"), 6 | extend = utils.extend; 7 | 8 | var stringMethods = ["style"]; 9 | var methods = ["forEach", "map", "filter", "reduce", "reduceRight", "some", "every", "indexOf", "lastIndexOf", 10 | "zip", "sum", "avg", "sort", "min", "max", "difference", "removeDuplicates", "unique", "rotate", 11 | "permutations", "transpose", "valuesAt", "union", "intersect", "powerSet", "cartesian", "compact", 12 | "multiply", "flatten", "pluck", "invoke", "partition"]; 13 | 14 | 15 | module.exports = function (o) { 16 | extend(o, methods, array); 17 | extend(o, stringMethods, string); 18 | return o; 19 | }; 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/extensions/cast.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | define = require("../define").define, 4 | utils = require("./utils"), 5 | extend = utils.extend, 6 | arrayExtension = require("./array"), 7 | argumentsExtension = require("./arguments"), 8 | dateExtension = require("./date"), 9 | functionExtension = require("./function"), 10 | numberExtension = require("./number"), 11 | objectExtension = require("./object"), 12 | stringExtension = require("./string"); 13 | 14 | var methods = { 15 | array:function () { 16 | return arrayExtension(this); 17 | }, 18 | 19 | date:function () { 20 | return dateExtension(this); 21 | }, 22 | args:function () { 23 | return argumentsExtension(this); 24 | }, 25 | func:function () { 26 | return functionExtension(this); 27 | }, 28 | number:function () { 29 | return numberExtension(this); 30 | }, 31 | 32 | string:function () { 33 | return stringExtension(this); 34 | }, 35 | 36 | object:function () { 37 | return objectExtension(this); 38 | } 39 | }; 40 | 41 | module.exports = function (o) { 42 | extend(o, Object.keys(methods), methods, function (name, func) { 43 | return base.partial(func); 44 | }); 45 | return o; 46 | }; -------------------------------------------------------------------------------- /lib/extensions/date.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | define = require("../define").define, 4 | utils = require("./utils"), 5 | extend = utils.extend, 6 | date = base.date, 7 | string = base.string; 8 | 9 | var methods = [ 10 | "add", 11 | "compare", 12 | "difference", 13 | "format", 14 | "getDaysInMonth", 15 | "getTimezoneName", 16 | "isLeapYear", 17 | "isWeekend" 18 | ]; 19 | 20 | module.exports = function (o) { 21 | extend(o, methods, date); 22 | return o; 23 | }; 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/extensions/function.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | argsToArray = base.argsToArray, 4 | utils = require("./utils"), 5 | array = base.array, 6 | extend = utils.extend, 7 | comb; 8 | 9 | var methods = [ 10 | "hitch", 11 | "bind", 12 | "hitchIgnore", 13 | "bindIgnore", 14 | "partial", 15 | "applyFirst", 16 | "bindFirst", 17 | "curry", 18 | "extend" 19 | ]; 20 | var baseMethods = ["extend"]; 21 | 22 | module.exports = function (o) { 23 | comb = comb || (require("../index")); 24 | extend(o, methods, base, function (name, func) { 25 | var ret; 26 | if (name !== 'partial' && name !== "applyFirst") { 27 | ret = function (arg1) { 28 | var args = argsToArray(arguments, 1); 29 | return comb(func.apply(null, [arg1, this].concat(args))); 30 | }; 31 | } else { 32 | ret = function (arg1) { 33 | return comb(func.apply(null, [this].concat(argsToArray(arguments)))); 34 | }; 35 | } 36 | return ret; 37 | 38 | }); 39 | extend(o, baseMethods, base); 40 | return o; 41 | }; 42 | 43 | 44 | -------------------------------------------------------------------------------- /lib/extensions/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | stringExtension = require("./string"), 4 | functionExtension = require("./function"), 5 | objectExtension = require("./object"), 6 | argumentsExtension = require("./arguments"), 7 | dateExtension = require("./date"), 8 | isExtension = require("./is"), 9 | castExtension = require("./cast.js"), 10 | numberExtension = require("./number"), 11 | arrayExtension = require("./array"); 12 | 13 | var TRUE = isExtension(true), 14 | FALSE = isExtension(false); 15 | 16 | function createExtension(obj) { 17 | if (base.isBoolean(obj)) { 18 | return obj ? TRUE : FALSE; 19 | } 20 | if (!base.isUndefinedOrNull(obj) && !obj.__isExtended__) { 21 | obj = isExtension(obj); 22 | obj = castExtension(obj); 23 | if (obj.isObject()) { 24 | obj.object(); 25 | } 26 | if (obj.isArguments()) { 27 | obj.args(); 28 | } 29 | if (obj.isArray()) { 30 | obj.array(); 31 | } 32 | if (obj.isFunction()) { 33 | obj.func(); 34 | } 35 | if (obj.isString()) { 36 | obj.string(); 37 | } 38 | if (obj.isDate()) { 39 | obj.date(); 40 | } 41 | if (obj.isNumber()) { 42 | obj.number(); 43 | } 44 | } 45 | return obj; 46 | } 47 | 48 | exports.createExtension = createExtension; -------------------------------------------------------------------------------- /lib/extensions/is.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | define = require("../define").define, 4 | utils = require("./utils"), 5 | extend = utils.extend; 6 | 7 | var methods = [ 8 | "isDefined", 9 | "isUndefined", 10 | "isNull", 11 | "isUndefinedOrNull", 12 | "isArguments", 13 | "isObject", 14 | "isHash", 15 | "isBoolean", 16 | "isDate", 17 | "isEmpty", 18 | "isArray", 19 | "isFunction", 20 | "isInstanceOf", 21 | "isNumber", 22 | "isPromiseLike", 23 | "isRegExp", 24 | "isString", 25 | "deepEqual" 26 | ]; 27 | 28 | module.exports = function (o) { 29 | var valueOf = false, val; 30 | if (!base.isInstanceOf(o, Object)) { 31 | /*jshint -W053 */ 32 | if (base.isBoolean(o)) { 33 | val = new Boolean(o); 34 | valueOf = true; 35 | } else if (base.isNumber(o)) { 36 | val = new Number(o); 37 | valueOf = true; 38 | } else if (base.isString(o)) { 39 | val = new String(o); 40 | valueOf = true; 41 | } 42 | } else { 43 | val = o; 44 | } 45 | var ret = extend(val, methods, base, null, valueOf); 46 | if (valueOf) { 47 | Object.defineProperty(ret, "valueOf", { 48 | value: function () { 49 | return o; 50 | }, 51 | writable: false, 52 | enumerable: false, 53 | configurable: true 54 | }); 55 | } 56 | Object.defineProperty(ret, "eq", { 57 | value: function (other) { 58 | return o === other; 59 | }, 60 | writable: false, 61 | enumerable: false, 62 | configurable: true 63 | }); 64 | 65 | Object.defineProperty(ret, "neq", { 66 | value: function (other) { 67 | return o !== other; 68 | }, 69 | writable: false, 70 | enumerable: false, 71 | configurable: true 72 | }); 73 | Object.defineProperty(ret, "print", { 74 | value: function () { 75 | console.log(o); 76 | return val; 77 | }, 78 | writable: false, 79 | enumerable: false, 80 | configurable: true 81 | }); 82 | Object.defineProperty(ret, "__isExtended__", { 83 | value: true, 84 | writable: false, 85 | enumerable: false, 86 | configurable: true 87 | }); 88 | return ret; 89 | }; -------------------------------------------------------------------------------- /lib/extensions/number.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | define = require("../define").define, 4 | utils = require("./utils"), 5 | extend = utils.extend, 6 | number = base.number, 7 | string = base.string; 8 | 9 | var methods = [ 10 | "round", 11 | "roundCeil", 12 | "roundHalfDown", 13 | "roundHalfUp" 14 | ]; 15 | 16 | module.exports = function (o) { 17 | extend(o, methods, number, null, true); 18 | return o; 19 | }; 20 | 21 | -------------------------------------------------------------------------------- /lib/extensions/object.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | define = require("../define").define, 4 | utils = require("./utils"), 5 | extend = utils.extend, 6 | hash = base.hash, 7 | argsToArray = base.argsToArray, 8 | comb; 9 | 10 | var methods = [ 11 | "hitch", 12 | "hitchIgnore", 13 | "bind", 14 | "bindIgnore", 15 | "merge", 16 | "extend", 17 | "deepMerge" 18 | ]; 19 | var lastMethod = ["curry"]; 20 | var hashMethods = [ 21 | "forEach", 22 | "filter", 23 | "invert", 24 | "values", 25 | "toArray", 26 | "omit", 27 | "pick", 28 | "keys" 29 | ]; 30 | 31 | module.exports = function (o) { 32 | comb = comb || (require("../index")); 33 | extend(o, methods, base); 34 | extend(o, hashMethods, hash); 35 | extend(o, lastMethod, base, function (name, func) { 36 | return function () { 37 | return comb(func.apply(null, argsToArray(arguments).concat([this]))); 38 | }; 39 | }); 40 | return o; 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /lib/extensions/string.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("../base"), 3 | string = base.string, 4 | date = base.date, 5 | utils = require("./utils"), 6 | extend = utils.extend, 7 | regexp = base.regexp, 8 | array = base.array, 9 | argsToArray = base.argsToArray, 10 | comb; 11 | 12 | 13 | var methods = ["style", "multiply", "toArray", "format", "truncate", "pad"]; 14 | var baseMethods = ["camelize", "underscore", "classify", "pluralize", "singularize", "applyFirst", "bindFirst", "partial"]; 15 | var functionMethods = ["hitch", "bind", "hitchIgnore", "bindIgnore","curry"]; 16 | var arrayMethods = ["pluck", "invoke"]; 17 | var dateMethods = [ 18 | ["parse", "parseDate"] 19 | ]; 20 | var regexpMethods = [ 21 | ["escapeString", "escape"] 22 | ]; 23 | 24 | module.exports = function (o) { 25 | comb = comb || (require("../index")); 26 | extend(o, methods, string, null, true); 27 | extend(o, baseMethods, base, null, true); 28 | extend(o, dateMethods, date, null, true); 29 | extend(o, regexpMethods, regexp, null, true); 30 | extend(o, arrayMethods, array, function (name, func) { 31 | return function () { 32 | return comb(func.apply(null, argsToArray(arguments).concat([this.valueOf()]))); 33 | }; 34 | }, true); 35 | extend(o, functionMethods, base, function (name, func) { 36 | return function (arg1) { 37 | var args = argsToArray(arguments, 1); 38 | return comb(func.apply(null, [arg1, this.valueOf()].concat(args))); 39 | }; 40 | }); 41 | 42 | return o; 43 | }; 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lib/extensions/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define").define, 3 | base = require("../base"), 4 | array = base.array, 5 | isBoolean = base.isBoolean, 6 | argsToArray = base.argsToArray, 7 | comb; 8 | 9 | function proxyFunc(name, func, valueOf, scope) { 10 | return function proxy() { 11 | comb || (comb = require("../index")); 12 | var ret = func.apply(scope, [valueOf ? this.valueOf() : this].concat(argsToArray(arguments))); 13 | if (ret && !base.isBoolean(ret) && ret !== this && !ret["__isExtended__"]) { 14 | ret = comb(ret); 15 | } 16 | return ret; 17 | }; 18 | } 19 | 20 | 21 | function extend(obj, methods, base, creator, valueOf) { 22 | valueOf = isBoolean(valueOf) ? valueOf : false; 23 | array.forEach(methods, function (method) { 24 | var newFunc, func, m, name; 25 | if (Array.isArray(method) && method.length === 2) { 26 | m = method[0]; 27 | name = method[1]; 28 | } else { 29 | m = name = method; 30 | } 31 | Object.defineProperty(obj, name, { 32 | value: (creator || proxyFunc)(name, base[m], valueOf, base), 33 | writable: false, 34 | enumerable: false, 35 | configurable: true 36 | }); 37 | 38 | }); 39 | return obj; 40 | } 41 | 42 | 43 | module.exports = { 44 | proxyFunc: proxyFunc, 45 | extend: extend 46 | }; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var base = require("./base"), 3 | extension = require("./extensions"), 4 | createExtension = extension.createExtension; 5 | 6 | /** 7 | * @projectName comb 8 | * 9 | * @github https://github.com/C2FO/comb 10 | * 11 | * @includeDoc [Getting Started] ../docs-md/introduction.md 12 | * @includeDoc [OO] ../docs-md/define.md 13 | * @includeDoc [Promises] ../docs-md/promise.md 14 | * @includeDoc [Logging] ../docs-md/logging.md 15 | * @includeDoc [Utilities] ../docs-md/utilities.md 16 | * @includeDoc [Change Log] ../History.md 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 & 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 79 | * 80 | * ##Meta 81 | * * Code: `git clone git://github.com/C2FO/comb.git` 82 | * * Website: 83 | * * Twitter: [http://twitter.com/c2fo](http://twitter.com/c2fo) - 877.465.4045 84 | */ 85 | 86 | /** 87 | * Utilities for javascript, optimized for the server environment. 88 | * 89 | * 90 | * @namespace 91 | * @ignoreCode 92 | */ 93 | var comb = createExtension; 94 | base.merge(comb, base, require("./define"), require("./promise"), require("./async"), require("./plugins"), require("./collections"), require("./logging")); 95 | 96 | 97 | comb.definePlugin = function (obj) { 98 | if (comb.isHash(obj)) { 99 | comb.deepMerge(comb, obj); 100 | } 101 | return comb; 102 | }; 103 | 104 | module.exports = comb; 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /lib/logging/appenders/consoleAppender.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../../define.js").define, 3 | base = require("../../base"), 4 | string = base.string, 5 | style = string.style, 6 | format = string.format, 7 | Appender = require("./appender"), 8 | Level = require("../level"); 9 | 10 | /** 11 | * @class Appends messages to the console. 12 | * 13 | * @name ConsoleAppender 14 | * @augments comb.logging.appenders.Appender 15 | * @memberOf comb.logging.appenders 16 | */ 17 | 18 | Appender.extend({ 19 | instance: { 20 | 21 | constructor: function (options) { 22 | options = options || {}; 23 | !options.name && (options.name = "consoleAppender"); 24 | this.__wrapStyle = options.wrapStyle; 25 | if (!("wrapStyle" in options)) { 26 | this.__wrapStyle = true; 27 | } 28 | this._super(arguments, [options]); 29 | }, 30 | 31 | getLevelColor: function (level) { 32 | if (Level.ERROR.equals(level) || Level.FATAL.equals(level)) { 33 | return "red"; 34 | } else if (Level.WARN.equals(level)) { 35 | return "yellow"; 36 | } else if (Level.DEBUG.equals(level)) { 37 | return "cyan"; 38 | } else if (Level.TRACE.equals(level)) { 39 | return "magenta"; 40 | } else if (Level.INFO.equals(level)) { 41 | return "blue"; 42 | } 43 | return null; 44 | }, 45 | 46 | extraEventData: function (event) { 47 | var level = event.level, 48 | color = this.getLevelColor(level); 49 | return base.merge({ 50 | levelNameColored: color ? style(level.name, color) : level, 51 | }, event); 52 | }, 53 | 54 | append: function (event) { 55 | if (this._canAppend(event)) { 56 | var level = event.level, 57 | message = format(this.__pattern, this.extraEventData(event)); 58 | if (this.__wrapStyle) { 59 | console.log(style(message, this.getLevelColor(level))); 60 | } else { 61 | console.log(message); 62 | } 63 | } 64 | } 65 | } 66 | }).registerType("ConsoleAppender").as(module); -------------------------------------------------------------------------------- /lib/logging/appenders/fileAppender.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../../define.js").define, 3 | base = require("../../base"), 4 | promise = require("../../promise"), 5 | string = base.string, 6 | Promise = promise.Promise, 7 | PromiseList = promise.PromiseList, 8 | style = string.style, 9 | format = string.format, 10 | Appender = require("./appender"), 11 | Level = require("../level"), 12 | fs = require("fs"); 13 | 14 | 15 | /** 16 | * @class Appends messages to a file. 17 | * 18 | * @example 19 | * var fileAppender = new comb.logging.appenders.FileAppender({ 20 | * file : "/var/log/myLog.log" 21 | * }); 22 | * 23 | * 24 | * @name FileAppender 25 | * @augments comb.logging.appenders.Appender 26 | * @memberOf comb.logging.appenders 27 | * 28 | * @param {Object} [options] options to assign to this Appender 29 | * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender 30 | * on a logger it must have a different name. 31 | * @param {String} [options.pattern="[{[yyyy-MM-ddTHH:mm:ss:SSS (z)]timeStamp}] {[- 5]levelName} {[-20]name} - {message}"] 32 | *

Available Options for formatting see {@link comb.string.format} for formatting options

33 | * 40 | * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender 41 | *

Note: the level can be different from the logger in the case that you want a particular logger 42 | * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the 43 | * appenders level is lower than the logger is will not recieve any messages.

44 | * 45 | * @param {String} [options.file="./log.log"] the file to log events to. 46 | * @param {String} [options.encoding="utf8"] the encoding of the file. 47 | * @param {Boolean} [options.overwrite=false] if true the log file is overwritten otherwise it is appended to. 48 | * @ignoreCode 49 | */ 50 | Appender.extend({ 51 | instance: { 52 | 53 | constructor: function (options) { 54 | options = options || {}; 55 | !options.name && (options.name = "fileAppender"); 56 | this.__file = options.file || "./log.log"; 57 | this.__encoding = options.encoding || "utf8"; 58 | this.__overwrite = options.overwrite || false; 59 | this.__writeStream = options.writeStream || fs.createWriteStream(this.__file, { 60 | flags: this.__overwrite ? "w" : 'a', 61 | encoding: this.__encoding 62 | }); 63 | this._super([options]); 64 | this.__pattern += "\n"; 65 | base.listenForExit(base.hitch(this, "__onExit")); 66 | }, 67 | 68 | 69 | __onExit: function () { 70 | var ret = new Promise(); 71 | var ws = this.__writeStream; 72 | this.__writeStream = null; 73 | ws.on("close", base.hitch(ret, "callback")); 74 | ws.destroySoon(); 75 | return ret.promise(); 76 | }, 77 | 78 | append: function (event) { 79 | var ws = this.__writeStream; 80 | if (this._canAppend(event) && ws && ws.writable) { 81 | var message = format(this.__pattern, event); 82 | var level = event.level; 83 | ws.write(message); 84 | } 85 | } 86 | } 87 | }).registerType("FileAppender").as(module); -------------------------------------------------------------------------------- /lib/logging/appenders/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /**@ignore*/ 3 | exports.Appender = require("./appender"); 4 | /**@ignore*/ 5 | exports.ConsoleAppender = require("./consoleAppender"); 6 | /**@ignore*/ 7 | exports.FileAppender = require("./fileAppender"); 8 | /**@ignore*/ 9 | exports.JSONAppender = require("./jsonAppender"); 10 | /**@ignore*/ 11 | exports.RollingFileAppender = require("./rollingFileAppender"); 12 | 13 | -------------------------------------------------------------------------------- /lib/logging/appenders/jsonAppender.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../../define.js").define, 3 | base = require("../../base"), 4 | string = base.string, 5 | escape = base.regexp.escapeString, 6 | FileAppender = require("./fileAppender"), 7 | format = string.format, 8 | Level = require("../level"), 9 | fs = require("fs"); 10 | 11 | 12 | /** 13 | * @class Appends messages to a file in JSON format. The messages are logged to an array in a JSON file 14 | * The file is always overwritten 15 | * 16 | * @example 17 | * //example log.json 18 | * [ 19 | * { 20 | * "timestamp" : "Wed Jun 08 2011 11:16:20 GMT-0500 (CDT)", 21 | * "level" : "INFO", 22 | * "name" : "comb", 23 | * "message" : "INFO MESSAGE!!!!" 24 | * } 25 | * ] 26 | * 27 | * 28 | * @name JSONAppender 29 | * @augments comb.logging.appenders.FileAppender 30 | * @memberOf comb.logging.appenders 31 | * 32 | * @param {Object} [options] options to assign to this Appender 33 | * @param {String} [options.name="appender"] the name of this Appender. If you want two of the same type of appender 34 | * on a logger it must have a different name. 35 | * @param {String} [options.pattern="{"timestamp" : "{timeStamp}", "level" : "{levelName}", "name" : "{name}", "message" : "{message}"}"] 36 | *

Available Options for formatting see {@link comb.string.format} for formatting options

37 | * 44 | * @param {comb.logging.Level|String} [options.level=comb.logging.Level.INFO] the logging level of this appender 45 | *

Note: the level can be different from the logger in the case that you want a particular logger 46 | * to only log particular event of a level. For example an appender that only logs errors. BEWARE that if the 47 | * appenders level is lower than the logger is will not recieve any messages.

48 | * 49 | * @param {String} [options.file="./log.json"] the file to log events to. 50 | * @param {String} [options.encoding="utf8"] the encoding of the file. 51 | * 52 | * @ignoreCode 53 | */ 54 | FileAppender.extend({ 55 | instance:{ 56 | 57 | constructor:function (options) { 58 | options = options || {}; 59 | this.name = options.name || "JSONAppender"; 60 | this.__count = 0; 61 | this.__file = options.file || "./log.json"; 62 | this.__encoding = options.encoding || "utf8"; 63 | this.__writeStream = options.writeStream || fs.createWriteStream(this.__file, { flags:"w", encoding:this.__encoding}); 64 | this.__writeStream.write("[\n"); 65 | this.level = options.level; 66 | //explicit overwrite of patter 67 | this.__pattern = '{"timestamp" : "{timeStamp}", "level" : "{levelName}", "name" : "{name}", "message" : "{message}"}'; 68 | base.listenForExit(base.hitch(this, "__onExit")); 69 | }, 70 | 71 | append:function (event) { 72 | if (this._canAppend(event)) { 73 | event.message = event.message.replace(/\n+/g, "\\n"); 74 | var message = (this.__count ? ",\n" : "\n") + format(this.__pattern, event); 75 | this.__writeStream.write(message); 76 | this.__count++; 77 | } 78 | }, 79 | 80 | 81 | __onExit:function () { 82 | this.__writeStream.write("]"); 83 | this._super(arguments); 84 | } 85 | } 86 | }).registerType("JSONAppender").as(module); -------------------------------------------------------------------------------- /lib/logging/level.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var define = require("../define.js").define, base = require("../base"); 3 | 4 | var LEVELS = { 5 | ALL: -100000, 6 | DEBUG: 1, 7 | TRACE: 2, 8 | INFO: 3, 9 | WARN: 4, 10 | ERROR: 5, 11 | FATAL: 6, 12 | OFF: 100000 13 | }; 14 | 15 | var LEVELS_REVERSE = { 16 | "-100000": "ALL", 17 | "1": "DEBUG", 18 | "2": "TRACE", 19 | "3": "INFO", 20 | "4": "WARN", 21 | "5": "ERROR", 22 | "6": "FATAL", 23 | "100000": "OFF" 24 | }; 25 | 26 | /** 27 | * @class Level class used to describe logging levels. The levels determine what types of events are logged to the appenders 28 | * for example the if Level.ALL is used then all event will be logged, however if Level.INFO was used then ONLY 29 | * INFO, WARN, ERROR, and FATAL events will be logged. To turn off logging for a logger use Level.OFF. 30 | * 31 | *

Not typically instantiated directly, but through staticly defined levels

32 | * @example 33 | * //Levels in ascending order 34 | * comb.logging.Level.ALL 35 | * comb.logging.Level.DEBUG 36 | * comb.logging.Level.TRACE 37 | * comb.logging.Level.INFO 38 | * comb.logging.Level.WARN 39 | * comb.logging.Level.ERROR 40 | * comb.logging.Level.FATAL 41 | * comb.logging.Level.OFF 42 | * 43 | * //or 44 | * Level.getLevel("INFO"); 45 | * 46 | * @name Level 47 | * @memberOf comb.logging 48 | * 49 | * @property {Number} level the numerical representation of this level. 50 | * @property {String} name the name of level. 51 | * @ignoreCode 52 | */ 53 | var Level = (exports = module.exports = define(null, { 54 | instance: { 55 | /**@lends comb.logging.Level.prototype*/ 56 | 57 | constructor: function (level, name) { 58 | this.level = level; 59 | this.name = name; 60 | }, 61 | 62 | /** 63 | * Determing if this level is >= another level 64 | * @param {comb.logging.Level} level the level to test against 65 | * 66 | * @returns {Boolean} true if this is >= false otherwise. 67 | */ 68 | isGreaterOrEqualToo: function (level) { 69 | var ret = false; 70 | if (level && base.isNumber(level.level)) { 71 | if (this.level >= level.level) { 72 | ret = true; 73 | } 74 | } 75 | return ret; 76 | }, 77 | 78 | /** 79 | * Determing if this level is equal to another level based off of the numerical rank. 80 | * 81 | * @param {comb.logging.Level} level the level to compare 82 | * 83 | * @returns {Boolean} true if this is equal to that false otherwise. 84 | */ 85 | equals: function (level) { 86 | return level.level === this.level; 87 | } 88 | }, 89 | static: { 90 | /**@lends comb.logging.Level*/ 91 | 92 | /** 93 | * Converts a numerical or string representation of a level, if a default level is provided, 94 | * then if a level cannot be determined then the default level is used. 95 | * 96 | * @param {Number|String|comb.logging.Level} level the level to try to convert 97 | * @param {comb.logging.Level} [defaultLevel] default level to use if one cannot be determined, 98 | * 99 | * @returns {comb.logging.Level|null} returns a level if one can be determined null otherwise. 100 | */ 101 | toLevel: function (level, defaultLevel) { 102 | var ret = null; 103 | var args = base.argsToArray(arguments); 104 | if (args.length === 1) { 105 | level = args[0]; 106 | if (base.isNumber(level)) { 107 | var strLevel = LEVELS_REVERSE[level]; 108 | ret = Level[strLevel]; 109 | } else if (base.isString(level)) { 110 | ret = Level[level.toUpperCase()]; 111 | } else { 112 | ret = level; 113 | } 114 | } else { 115 | ret = (Level.toLevel(args[0]) || args[1]); 116 | } 117 | return ret; 118 | }, 119 | 120 | /** 121 | * Adds a new level to the Level object. 122 | * 123 | * @example 124 | * 125 | * logger = Logger.getLogger("my.logger"); 126 | * 127 | * //create the custom level 128 | * Level.addLevel("custom_Level", 20); 129 | * 130 | * //now set the level on a logger 131 | * logger.level = Level.CUSTOM_LEVEL; 132 | * 133 | * @param {string} label the label of the level, Note: the label will be coverted to uppercase. 134 | * @param {number} level the level of the level 135 | * 136 | * @return {undefined|comb.logging.Level} the level that was created. 137 | * 138 | */ 139 | addLevel: function (label, level) { 140 | var ret; 141 | if (base.isString(label) && base.isNumber(level)) { 142 | label = label.toUpperCase(); 143 | LEVELS_REVERSE[level] = label; 144 | LEVELS[label] = level; 145 | ret = (this[label] = new Level(level, label)); 146 | } 147 | return ret; 148 | }, 149 | 150 | /** 151 | * Level to allow logging of all events. 152 | */ 153 | ALL: null, 154 | /** 155 | * Logs only events debug or greater. 156 | */ 157 | DEBUG: null, 158 | /** 159 | * Like debug but provides a finer level of detail 160 | */ 161 | TRACE: null, 162 | /** 163 | * Only info, or error related events 164 | */ 165 | INFO: null, 166 | /** 167 | * Only warn or error related events 168 | */ 169 | WARN: null, 170 | /** 171 | * Error or fatal events 172 | */ 173 | ERROR: null, 174 | /** 175 | * Only fatal events 176 | */ 177 | FATAL: null, 178 | /** 179 | * No events will be logged. 180 | */ 181 | OFF: null 182 | 183 | } 184 | })); 185 | 186 | for (var i in LEVELS_REVERSE) { 187 | Level[LEVELS_REVERSE[i]] = new Level(parseInt(i, 10), LEVELS_REVERSE[i]); 188 | } -------------------------------------------------------------------------------- /lib/plugins/Broadcaster.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var func = require("../base/functions"), 3 | define = require("../define").define; 4 | 5 | 6 | var Broadcaster = define(null, { 7 | instance: { 8 | /** @lends comb.plugins.Broadcaster.prototype */ 9 | /** 10 | * Plugin to allow a class to easily broadcast events 11 | * 12 | * @example 13 | * 14 | * var Mammal = define(comb.plugins.Broadcaster, { 15 | * instance : { 16 | * 17 | * constructor: function(options) { 18 | * options = options || {}; 19 | * this._super(arguments); 20 | * this._type = options.type || "mammal"; 21 | * }, 22 | * 23 | * speak : function() { 24 | * var str = "A mammal of type " + this._type + " sounds like"; 25 | * this.broadcast("speak", str); 26 | * this.onSpeak(str); 27 | * return str; 28 | * }, 29 | * 30 | * onSpeak : function(){} 31 | * } 32 | * }); 33 | * 34 | * 35 | * var m = new Mammal({color : "gold"}); 36 | * m.listen("speak", function(str){ 37 | * //called back from the broadcast event 38 | * console.log(str); 39 | * }); 40 | * m.speak(); 41 | * 42 | * @constructs 43 | */ 44 | constructor: function () { 45 | this.__listeners = {}; 46 | }, 47 | 48 | /** 49 | * Broadcasts an event from an object 50 | * 51 | * @param name the name of the event to broadcast 52 | * @param {Object|String|Function|Date|Number} [args] variable number of arguments to pass to listeners, can be anything 53 | */ 54 | broadcast: function (topic, args) { 55 | args = Array.prototype.slice.call(arguments, 0); 56 | topic = args.shift(); 57 | if (topic && topic in this.__listeners) { 58 | var list = this.__listeners[topic], i = list.length - 1; 59 | while (i >= 0) { 60 | list[i--].cb.apply(this, args); 61 | } 62 | } 63 | }, 64 | 65 | /** 66 | * Listens to a broadcasted event 67 | * Simimlar to {@link comb.listen} 68 | * 69 | * @param {String} topic the topic to listen to 70 | * @param {Function} callback the function to callback on event publish 71 | * 72 | * @returns {Array} handle to disconnect a topic 73 | */ 74 | listen: function (topic, callback) { 75 | if (!func.isFunction(callback)) { 76 | throw new Error("callback must be a function"); 77 | } 78 | var handle = { 79 | topic: topic, 80 | cb: callback 81 | }; 82 | var list = this.__listeners[topic]; 83 | if (!list) { 84 | list = (this.__listeners[topic] = [handle]); 85 | handle.pos = 0; 86 | } else { 87 | handle.pos = list.push(handle); 88 | } 89 | return handle; 90 | }, 91 | 92 | /** 93 | * Disconnects a listener 94 | * Similar to {@link comb.unListen} 95 | * 96 | * @param handle disconnect a handle returned from Broadcaster.listen 97 | */ 98 | unListen: function (handle) { 99 | if (handle) { 100 | var topic = handle.topic; 101 | if (topic in this.__listeners) { 102 | var listeners = this.__listeners, list = listeners[topic]; 103 | if (list) { 104 | for (var i = list.length - 1; i >= 0; i--) { 105 | if (list[i] === handle) { 106 | list.splice(i, 1); 107 | break; 108 | } 109 | } 110 | } 111 | } 112 | } 113 | } 114 | } 115 | }); 116 | 117 | exports = module.exports = Broadcaster; -------------------------------------------------------------------------------- /lib/plugins/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var comb = exports; 3 | /**@namespace plugins for classes using {@link comb.define}*/ 4 | comb.plugins = { 5 | Broadcaster : require("./Broadcaster"), 6 | Middleware : require("./Middleware") 7 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "comb", 3 | "description": "A framework for node", 4 | "version": "2.0.0", 5 | "keywords": [ 6 | "OO", 7 | "Object Oriented", 8 | "Collections", 9 | "Tree", 10 | "HashTable", 11 | "Pool", 12 | "Logging", 13 | "Promise", 14 | "Promises", 15 | "Proxy" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:C2FO/comb.git" 20 | }, 21 | "homepage": "http://c2fo.github.com/comb/", 22 | "author": "C2FO (http://c2fo.github.com)", 23 | "main": "index.js", 24 | "directories": { 25 | "lib": "lib" 26 | }, 27 | "devDependencies": { 28 | "coveralls": "^2.11.4", 29 | "grunt": "^0.4.5", 30 | "grunt-contrib-jshint": "^0.11.3", 31 | "grunt-exec": "^0.4.6", 32 | "grunt-it": "^1.0.0", 33 | "istanbul": "^0.4.1", 34 | "it": "^1.1.0", 35 | "jit-grunt": "^0.9.1", 36 | "time-grunt": "^1.2.2" 37 | }, 38 | "scripts": { 39 | "test": "grunt test" 40 | }, 41 | "engines": { 42 | "node": ">= 10.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/base.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require("it"), 3 | assert = require("assert"), 4 | comb = require("../index"); 5 | 6 | it.describe("comb", function(it){ 7 | it.should("define a plugin", function(){ 8 | comb.definePlugin({a : true}); 9 | assert.isTrue(comb.a); 10 | }); 11 | }).as(module); -------------------------------------------------------------------------------- /test/base/broadcast.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | define = comb.define, 6 | hitch = comb.hitch, 7 | Broadcaster = comb; 8 | 9 | 10 | it.describe("comb/base/broadcast.js", function (it) { 11 | 12 | //Super of other classes 13 | var Mammal = define(null, { 14 | instance: { 15 | 16 | constructor: function (options) { 17 | options = options || {}; 18 | this._super(arguments); 19 | this._type = options.type || "mammal"; 20 | }, 21 | 22 | speak: function () { 23 | var str = "A mammal of type " + this._type + " sounds like"; 24 | comb.broadcast("speak", str); 25 | this.onSpeak(str); 26 | return str; 27 | }, 28 | 29 | onSpeak: function () { 30 | 31 | } 32 | } 33 | }); 34 | 35 | 36 | it.should("#listen", function (next) { 37 | var m = new Mammal({color: "gold"}), h; 38 | h = comb.listen("speak", hitch(this, function (str) { 39 | assert.equal(str, "A mammal of type mammal sounds like"); 40 | comb.unListen(h); 41 | next(); 42 | })); 43 | m.speak(); 44 | }); 45 | 46 | it.should("#unlisten", function (next) { 47 | var m = new Mammal({color: "gold"}); 48 | var han = comb.listen("speak", next); 49 | comb.unListen(han); 50 | comb.listen("speak", hitch(this, function () { 51 | next(); 52 | })); 53 | m.speak(); 54 | }); 55 | 56 | it.should("#connect", function (next) { 57 | var m = new Mammal({color: "gold"}), h; 58 | h = comb.connect(m, "speak", hitch(this, function (str) { 59 | comb.disconnect(h); 60 | next(); 61 | })); 62 | m.speak(); 63 | }); 64 | 65 | it.should("#disconnect", function (next) { 66 | var m = new Mammal({color: "gold"}); 67 | var han = comb.connect(m, "speak", next); 68 | comb.disconnect(han); 69 | comb.connect(m, "speak", hitch(this, function () { 70 | next(); 71 | })); 72 | m.speak(); 73 | }); 74 | 75 | it.should("throw an error on #connect if the function does not exist", function () { 76 | var m = new Mammal({color: "gold"}); 77 | assert.throws(function () { 78 | comb.connect(m, "someMethod", function () { 79 | }); 80 | }); 81 | }); 82 | 83 | it.should("throw an error on #disconnect if an invalid handler is passed in", function () { 84 | assert.throws(function () { 85 | comb.disconnect(); 86 | }); 87 | 88 | assert.throws(function () { 89 | comb.disconnect([comb, "someMethod", function () { 90 | }]); 91 | }); 92 | }); 93 | 94 | }).as(module); 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /test/base/inflections.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"); 5 | 6 | it.describe("comb/base/inflections.js", function (it) { 7 | var INFLECTIONS = { 8 | test: "tests", 9 | ax: "axes", 10 | testis: "testes", 11 | octopus: "octopuses", 12 | virus: "viruses", 13 | alias: "aliases", 14 | status: "statuses", 15 | bus: "buses", 16 | buffalo: "buffaloes", 17 | tomato: "tomatoes", 18 | datum: "data", 19 | bacterium: "bacteria", 20 | analysis: "analyses", 21 | basis: "bases", 22 | diagnosis: "diagnoses", 23 | parenthesis: "parentheses", 24 | prognosis: "prognoses", 25 | synopsis: "synopses", 26 | thesis: "theses", 27 | wife: "wives", 28 | giraffe: "giraffes", 29 | self: "selves", 30 | dwarf: "dwarves", 31 | hive: "hives", 32 | fly: "flies", 33 | buy: "buys", 34 | soliloquy: "soliloquies", 35 | day: "days", 36 | attorney: "attorneys", 37 | boy: "boys", 38 | hoax: "hoaxes", 39 | lunch: "lunches", 40 | princess: "princesses", 41 | matrix: "matrices", 42 | vertex: "vertices", 43 | index: "indices", 44 | mouse: "mice", 45 | louse: "lice", 46 | quiz: "quizzes", 47 | motive: "motives", 48 | movie: "movies", 49 | series: "series", 50 | crisis: "crises", 51 | person: "people", 52 | man: "men", 53 | woman: "women", 54 | child: "children", 55 | sex: "sexes", 56 | move: "moves" 57 | }; 58 | //Super of other classes 59 | 60 | it.should("camelize correctly", function () { 61 | assert.isNull(comb.camelize(null)); 62 | assert.isUndefined(comb.camelize()); 63 | assert.equal(comb.camelize("hello_world"), "helloWorld"); 64 | assert.equal(comb.camelize("column_name"), "columnName"); 65 | assert.equal(comb.camelize("columnName"), "columnName"); 66 | 67 | assert.equal(comb("hello_world").camelize(), "helloWorld"); 68 | assert.equal(comb("column_name").camelize(), "columnName"); 69 | assert.equal(comb("columnName").camelize(), "columnName"); 70 | }); 71 | 72 | 73 | it.should("underscore correctly", function () { 74 | assert.isNull(comb.underscore(null)); 75 | assert.isUndefined(comb.underscore()); 76 | assert.equal(comb.underscore("helloWorld"), "hello_world"); 77 | assert.equal(comb.underscore("helloWorld1"), "hello_world_1"); 78 | assert.equal(comb.underscore("1HelloWorld"), "1_hello_world"); 79 | assert.equal(comb.underscore("column_name"), "column_name"); 80 | assert.equal(comb.underscore("columnName"), "column_name"); 81 | 82 | assert.equal(comb("helloWorld").underscore(), "hello_world"); 83 | assert.equal(comb("helloWorld1").underscore(), "hello_world_1"); 84 | assert.equal(comb("1HelloWorld").underscore(), "1_hello_world"); 85 | assert.equal(comb("column_name").underscore(), "column_name"); 86 | assert.equal(comb("columnName").underscore(), "column_name"); 87 | }); 88 | 89 | it.should("classify correctly", function () { 90 | assert.isNull(comb.classify(null)); 91 | assert.isUndefined(comb.classify()); 92 | assert.equal(comb.classify('egg_and_hams'), "eggAndHam"); 93 | assert.equal(comb.classify('post'), "post"); 94 | assert.equal(comb.classify('schema.post'), "post"); 95 | 96 | assert.equal(comb('egg_and_hams').classify(), "eggAndHam"); 97 | assert.equal(comb('post').classify(), "post"); 98 | assert.equal(comb('schema.post').classify(), "post"); 99 | }); 100 | 101 | 102 | it.should("singularize correctly", function () { 103 | assert.isNull(comb.singularize(null)); 104 | assert.isUndefined(comb.singularize()); 105 | for (var i in INFLECTIONS) { 106 | assert.equal(comb.singularize(INFLECTIONS[i]), i); 107 | assert.equal(comb(INFLECTIONS[i]).singularize(), i); 108 | } 109 | }); 110 | 111 | it.should("pluralize correctly", function () { 112 | assert.isNull(comb.pluralize(null)); 113 | assert.isUndefined(comb.pluralize()); 114 | for (var i in INFLECTIONS) { 115 | assert.equal(comb.pluralize(i), INFLECTIONS[i]); 116 | assert.equal(comb(i).pluralize(), INFLECTIONS[i]); 117 | } 118 | }); 119 | 120 | 121 | }).as(module); 122 | 123 | -------------------------------------------------------------------------------- /test/base/misc.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"); 5 | 6 | it.describe("comb/base/misc.js", function (it) { 7 | //Super of other classes 8 | it.should("#isBoolean", function () { 9 | //This is true because they inherit from eachother! 10 | assert.isTrue(comb.isBoolean(true)); 11 | assert.isTrue(comb(true).isBoolean()); 12 | assert.isFalse(comb.isBoolean(function () { 13 | })); 14 | 15 | assert.isFalse(comb(function () { 16 | }).isBoolean()); 17 | 18 | assert.isFalse(comb.isBoolean("hello")); 19 | assert.isFalse(comb("hello").isBoolean()); 20 | assert.isFalse(comb.isBoolean({})); 21 | assert.isFalse(comb({}).isBoolean()); 22 | assert.isFalse(comb(new Date()).isBoolean()); 23 | assert.isFalse(comb(1).isBoolean()); 24 | }); 25 | 26 | it.should("#isArguments", function () { 27 | assert.isTrue(comb.isArguments(arguments)); 28 | assert.isTrue(comb(arguments).isArguments()); 29 | assert.isFalse(comb.isArguments(function () { 30 | })); 31 | 32 | assert.isFalse(comb(function () { 33 | }).isArguments()); 34 | assert.isFalse(comb.isArguments("hello")); 35 | assert.isFalse(comb("hello").isArguments()); 36 | assert.isFalse(comb({}).isArguments()); 37 | assert.isFalse(comb(new Date()).isArguments()); 38 | assert.isFalse(comb(1).isArguments()); 39 | }); 40 | 41 | it.should("#isUndefined", function () { 42 | assert.isTrue(comb.isUndefined(undefined)); 43 | assert.isFalse(comb.isUndefined(null)); 44 | assert.isFalse(comb.isUndefined(true)); 45 | assert.isFalse(comb(true).isUndefined()); 46 | assert.isFalse(comb.isUndefined(function () { 47 | })); 48 | assert.isFalse(comb(function () { 49 | }).isUndefined()); 50 | assert.isFalse(comb("hello").isUndefined()); 51 | assert.isFalse(comb({}).isUndefined()); 52 | assert.isFalse(comb(new Date()).isUndefined()); 53 | assert.isFalse(comb(1).isUndefined()); 54 | }); 55 | 56 | it.should("#isNull", function () { 57 | //This is true because they inherit from eachother! 58 | assert.isTrue(comb.isNull(null)); 59 | assert.isFalse(comb.isNull(undefined)); 60 | assert.isFalse(comb.isNull(true)); 61 | assert.isFalse(comb(true).isNull()); 62 | assert.isFalse(comb.isNull(function () { 63 | })); 64 | assert.isFalse(comb(function () { 65 | }).isNull()); 66 | assert.isFalse(comb("hello").isNull()); 67 | assert.isFalse(comb({}).isNull()); 68 | assert.isFalse(comb(new Date()).isNull()); 69 | assert.isFalse(comb(1).isNull()); 70 | }); 71 | 72 | it.should("#isDefined", function () { 73 | //This is true because they inherit from eachother! 74 | assert.isTrue(comb.isDefined(null)); 75 | assert.isFalse(comb.isDefined(undefined)); 76 | assert.isTrue(comb.isDefined(true)); 77 | assert.isTrue(comb(true).isDefined()); 78 | assert.isTrue(comb.isDefined(function () { 79 | })); 80 | assert.isTrue(comb(function () { 81 | }).isDefined()); 82 | assert.isTrue(comb("hello").isDefined()); 83 | assert.isTrue(comb({}).isDefined()); 84 | assert.isTrue(comb(new Date()).isDefined()); 85 | assert.isTrue(comb(1).isDefined()); 86 | }); 87 | 88 | it.should("#isInstanceOf", function () { 89 | /*jshint -W053 */ 90 | //This is true because they inherit from eachother! 91 | assert.isTrue(comb.isInstanceOf(new Date(), Date)); 92 | assert.isTrue(comb(new Date()).isInstanceOf(Date)); 93 | assert.isTrue(comb(new Number(1)).isInstanceOf(Number)); 94 | assert.isTrue(comb(new String(1)).isInstanceOf(String)); 95 | assert.isFalse(comb.isInstanceOf(undefined, String)); 96 | assert.isFalse(comb.isInstanceOf(undefined, 1)); 97 | }); 98 | 99 | it.should("convert arumgents to array", function () { 100 | 101 | (function () { 102 | assert.deepEqual(comb.argsToArray(arguments), [1, 2, 3]); 103 | assert.deepEqual(comb(arguments).toArray(), [1, 2, 3]); 104 | assert.deepEqual(comb.argsToArray(arguments, 1), [2, 3]); 105 | assert.deepEqual(comb(arguments).toArray(1), [2, 3]); 106 | })(1, 2, 3); 107 | 108 | }); 109 | }).as(module); 110 | 111 | -------------------------------------------------------------------------------- /test/base/regexp.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"); 5 | 6 | 7 | it.describe("comb/base/regexp.js", function (it) { 8 | //Super of other classes 9 | 10 | it.should("escape it properly", function () { 11 | var chars = [".", "$", "?", "*", "|", "{", "}", "(", ")", "[", "]", "\\", "/", "+", "^"]; 12 | chars.forEach(function (c) { 13 | assert.equal(comb.regexp.escapeString(c), "\\" + c); 14 | assert.equal(comb(c).escape(), "\\" + c); 15 | }); 16 | chars.forEach(function (c) { 17 | assert.equal(comb(c).escape([c]), c); 18 | }); 19 | }); 20 | 21 | 22 | it.should("determine if something is a RegExp", function () { 23 | assert.isTrue(comb.isRexExp(/a/)); 24 | assert.isTrue(comb.isRexExp(new RegExp("a"))); 25 | assert.isFalse(comb.isRexExp()); 26 | assert.isFalse(comb.isRexExp("")); 27 | assert.isFalse(comb.isRexExp(1)); 28 | assert.isFalse(comb.isRexExp(false)); 29 | assert.isFalse(comb.isRexExp(true)); 30 | 31 | assert.isTrue(comb(/a/).isRegExp()); 32 | assert.isTrue(comb(new RegExp("a")).isRegExp()); 33 | assert.isFalse(comb("").isRegExp()); 34 | assert.isFalse(comb(1).isRegExp()); 35 | assert.isFalse(comb(false).isRegExp()); 36 | assert.isFalse(comb(true).isRegExp()); 37 | }); 38 | 39 | 40 | }).as(module); -------------------------------------------------------------------------------- /test/collections/AVLTree.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | helper = require("./treeTest.helper.js"), 6 | Mammal = helper.Mammal, 7 | AVLTree = comb.collections.AVLTree; 8 | 9 | 10 | it.describe("comb.collections.AVLTree", function (it) { 11 | 12 | var words = ["c", "ca", "b", "ba", "bb", "a", "aa", "ab", "z", "d", "f", "h", "i", "ee", "ff", "hi", "j", "ajk"]; 13 | var wordsInOrder = ['a', 'aa', 'ab', 'ajk', 'b', 'ba', 'bb', 'c', 'ca', 'd', 'ee', 'f', 'ff', 'h', 'hi', 'i', 'j', 'z']; 14 | var wordsPreOrder = ['ba', 'aa', 'a', 'ajk', 'ab', 'b', 'h', 'd', 'c', 'bb', 'ca', 'f', 'ee', 'ff', 'i', 'hi', 'z', 'j']; 15 | var wordsPostOrder = ['a', 'ab', 'b', 'ajk', 'aa', 'bb', 'ca', 'c', 'ee', 'ff', 'f', 'd', 'hi', 'j', 'z', 'i', 'h', 'ba']; 16 | 17 | 18 | var mammals = [ 19 | new Mammal({type: "bird"}), 20 | new Mammal({type: "dog"}), 21 | new Mammal({type: "rat"}), 22 | new Mammal({type: "zebra"}), 23 | new Mammal({type: "mouse"}), 24 | new Mammal({type: "horse"}), 25 | new Mammal({type: "squirrel"}), 26 | new Mammal({type: "groundhog"}) 27 | ], 28 | 29 | mammalsInOrder = [ 30 | new Mammal({type: "bird"}), 31 | new Mammal({type: "dog"}), 32 | new Mammal({type: "groundhog"}), 33 | new Mammal({type: "horse"}), 34 | new Mammal({type: "mouse"}), 35 | new Mammal({type: "rat"}), 36 | new Mammal({type: "squirrel"}), 37 | new Mammal({type: "zebra"}) 38 | ], 39 | 40 | mammalsPreOrder = [ 41 | new Mammal({type: "mouse"}), 42 | new Mammal({type: "dog"}), 43 | new Mammal({type: "bird"}), 44 | new Mammal({type: "horse"}), 45 | new Mammal({type: "groundhog"}), 46 | new Mammal({type: "squirrel"}), 47 | new Mammal({type: "rat"}), 48 | new Mammal({type: "zebra"}) 49 | 50 | ], 51 | 52 | mammalsPostOrder = [ 53 | new Mammal({type: "bird"}), 54 | new Mammal({type: "groundhog"}), 55 | new Mammal({type: "horse"}), 56 | new Mammal({type: "dog"}), 57 | new Mammal({type: "rat"}), 58 | new Mammal({type: "zebra"}), 59 | new Mammal({type: "squirrel"}), 60 | new Mammal({type: "mouse"}) 61 | 62 | ]; 63 | var orderedMammals = [ 64 | [AVLTree.IN_ORDER, mammalsInOrder], 65 | [AVLTree.PRE_ORDER, mammalsPreOrder], 66 | [AVLTree.POST_ORDER, mammalsPostOrder] 67 | ]; 68 | 69 | 70 | var expectedOutput = "\t\t\t\t~\n\t\t\tz:-1\n\n\t\t\t\t\t~\n\t\t\t\tj:0\n\n\t\t\t\t\t~\n\t\ti:1\n\n\t\t\t\t~\n\t\t\t" + 71 | "hi:0\n\n\t\t\t\t~\n\th:0\n\n\t\t\t\t\t~\n\t\t\t\tff:0\n\n\t\t\t\t\t~\n\t\t\tf:0\n\n\t\t\t\t\t~\n\t\t\t\t" + 72 | "ee:0\n\n\t\t\t\t\t~\n\t\td:0\n\n\t\t\t\t\t~\n\t\t\t\tca:0\n\n\t\t\t\t\t~\n\t\t\tc:0\n\n\t\t\t\t\t~\n\t\t\t\t" + 73 | "bb:0\n\n\t\t\t\t\t~\nba:1\n\n\t\t\t\t~\n\t\t\tb:0\n\n\t\t\t\t~\n\t\tajk:0\n\n\t\t\t\t~\n\t\t\tab:0\n\n\t\t\t\t~\n\t" + 74 | "aa:1\n\n\t\t\t~\n\t\ta:0\n\n\t\t\t~"; 75 | 76 | var orderedWords = [ 77 | [AVLTree.IN_ORDER, wordsInOrder], 78 | [AVLTree.PRE_ORDER, wordsPreOrder], 79 | [AVLTree.POST_ORDER, wordsPostOrder] 80 | ]; 81 | 82 | 83 | helper.setup( 84 | it, 85 | AVLTree, 86 | words, 87 | orderedWords, 88 | mammals, 89 | orderedMammals, 90 | expectedOutput); 91 | 92 | }).as(module); -------------------------------------------------------------------------------- /test/collections/AnderssonTree.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | helper = require("./treeTest.helper.js"), 6 | Mammal = helper.Mammal, 7 | AnderssonTree = comb.collections.AnderssonTree; 8 | 9 | 10 | it.describe("comb.collections.AnderssonTree", function (it) { 11 | var words = ["c", "ca", "b", "ba", "bb", "a", "aa", "ab", "z", "d", "f", "h", "i", "ee", "ff", "hi", "j", "ajk"]; 12 | var wordsInOrder = ['a', 'aa', 'ab', 'ajk', 'b', 'ba', 'bb', 'c', 'ca', 'd', 'ee', 'f', 'ff', 'h', 'hi', 'i', 'j', 'z']; 13 | var wordsPreOrder = ['d', 'ba', 'aa', 'a', 'ajk', 'ab', 'b', 'c', 'bb', 'ca', 'h', 'f', 'ee', 'ff', 'i', 'hi', 'j', 'z']; 14 | var wordsPostOrder = ['a', 'ab', 'b', 'ajk', 'aa', 'bb', 'ca', 'c', 'ba', 'ee', 'ff', 'f', 'hi', 'z', 'j', 'i', 'h', 'd']; 15 | 16 | 17 | var mammals = [ 18 | new Mammal({type: "bird"}), 19 | new Mammal({type: "dog"}), 20 | new Mammal({type: "rat"}), 21 | new Mammal({type: "zebra"}), 22 | new Mammal({type: "mouse"}), 23 | new Mammal({type: "horse"}), 24 | new Mammal({type: "squirrel"}), 25 | new Mammal({type: "groundhog"}) 26 | ], 27 | 28 | mammalsInOrder = [ 29 | new Mammal({type: "bird"}), 30 | new Mammal({type: "dog"}), 31 | new Mammal({type: "groundhog"}), 32 | new Mammal({type: "horse"}), 33 | new Mammal({type: "mouse"}), 34 | new Mammal({type: "rat"}), 35 | new Mammal({type: "squirrel"}), 36 | new Mammal({type: "zebra"}) 37 | ], 38 | 39 | mammalsPreOrder = [ 40 | new Mammal({type: "horse"}), 41 | new Mammal({type: "dog"}), 42 | new Mammal({type: "bird"}), 43 | new Mammal({type: "groundhog"}), 44 | new Mammal({type: "rat"}), 45 | new Mammal({type: "mouse"}), 46 | new Mammal({type: "squirrel"}), 47 | new Mammal({type: "zebra"}) 48 | 49 | ], 50 | 51 | mammalsPostOrder = [ 52 | new Mammal({type: "bird"}), 53 | new Mammal({type: "groundhog"}), 54 | new Mammal({type: "dog"}), 55 | new Mammal({type: "mouse"}), 56 | new Mammal({type: "zebra"}), 57 | new Mammal({type: "squirrel"}), 58 | new Mammal({type: "rat"}), 59 | new Mammal({type: "horse"}) 60 | ]; 61 | var orderedMammals = [ 62 | [AnderssonTree.IN_ORDER, mammalsInOrder], 63 | [AnderssonTree.PRE_ORDER, mammalsPreOrder], 64 | [AnderssonTree.POST_ORDER, mammalsPostOrder] 65 | ]; 66 | 67 | 68 | var orderedWords = [ 69 | [AnderssonTree.IN_ORDER, wordsInOrder], 70 | [AnderssonTree.PRE_ORDER, wordsPreOrder], 71 | [AnderssonTree.POST_ORDER, wordsPostOrder] 72 | ]; 73 | helper.setup( 74 | it, 75 | AnderssonTree, 76 | words, 77 | orderedWords, 78 | mammals, 79 | orderedMammals, 80 | "\t\t\t\t\t~\n\t\t\t\tz:1\n\n\t\t\t\t\t~\n\t\t\tj:1\n\n\t\t\t\t~\n\t\ti:2\n\n\t\t\t\t~\n\t\t\thi:1\n\n\t\t\t\t" + 81 | "~\n\th:3\n\n\t\t\t\t~\n\t\t\tff:1\n\n\t\t\t\t~\n\t\tf:2\n\n\t\t\t\t~\n\t\t\tee:1\n\n\t\t\t\t" + 82 | "~\nd:4\n\n\t\t\t\t~\n\t\t\tca:1\n\n\t\t\t\t~\n\t\tc:2\n\n\t\t\t\t~\n\t\t\tbb:1\n\n\t\t\t\t" + 83 | "~\n\tba:3\n\n\t\t\t\t\t~\n\t\t\t\tb:1\n\n\t\t\t\t\t~\n\t\t\tajk:2\n\n\t\t\t\t\t~\n\t\t\t\t" + 84 | "ab:1\n\n\t\t\t\t\t~\n\t\taa:2\n\n\t\t\t\t~\n\t\t\ta:1\n\n\t\t\t\t~"); 85 | }).as(module); 86 | -------------------------------------------------------------------------------- /test/collections/BinaryTree.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | helper = require("./treeTest.helper.js"), 6 | Mammal = helper.Mammal, 7 | BinaryTree = comb.collections.BinaryTree; 8 | 9 | 10 | it.describe("comb.collections.BinaryTree", function (it) { 11 | 12 | var words = ["c", "ca", "b", "ba", "bb", "a", "a", "aa", "ab", "z", "d", "f", "h", "i", "ee", "ff", "hi", "j", "ajk"]; 13 | var wordsInOrder = ['a', 'aa', 'ab', 'ajk', 'b', 'ba', 'bb', 'c', 'ca', 'd', 'ee', 'f', 'ff', 'h', 'hi', 'i', 'j', 'z']; 14 | var wordsPreOrder = ["c", "b", "a", "aa", "ab", "ajk", "ba", "bb", "ca", "z", "d", "f", "ee", "h", "ff", "i", "hi", "j"]; 15 | var wordsPostOrder = ["ajk", "ab", "aa", "a", "bb", "ba", "b", "ee", "ff", "hi", "j", "i", "h", "f", "d", "z", "ca", "c"]; 16 | 17 | var mammals = [ 18 | new Mammal({type: "bird"}), 19 | new Mammal({type: "dog"}), 20 | new Mammal({type: "rat"}), 21 | new Mammal({type: "mouse"}), 22 | new Mammal({type: "zebra"}), 23 | new Mammal({type: "horse"}), 24 | new Mammal({type: "squirrel"}), 25 | new Mammal({type: "groundhog"}) 26 | ], 27 | 28 | mammalsInOrder = [ 29 | new Mammal({type: "bird"}), 30 | new Mammal({type: "dog"}), 31 | new Mammal({type: "groundhog"}), 32 | new Mammal({type: "horse"}), 33 | new Mammal({type: "mouse"}), 34 | new Mammal({type: "rat"}), 35 | new Mammal({type: "squirrel"}), 36 | new Mammal({type: "zebra"}) 37 | ], 38 | 39 | mammalsPreOrder = [ 40 | new Mammal({type: "bird"}), 41 | new Mammal({type: "dog"}), 42 | new Mammal({type: "rat"}), 43 | new Mammal({type: "mouse"}), 44 | new Mammal({type: "horse"}), 45 | new Mammal({type: "groundhog"}), 46 | new Mammal({type: "zebra"}), 47 | new Mammal({type: "squirrel"}) 48 | ], 49 | 50 | mammalsPostOrder = [ 51 | new Mammal({type: "groundhog"}), 52 | new Mammal({type: "horse"}), 53 | new Mammal({type: "mouse"}), 54 | new Mammal({type: "squirrel"}), 55 | new Mammal({type: "zebra"}), 56 | new Mammal({type: "rat"}), 57 | new Mammal({type: "dog"}), 58 | new Mammal({type: "bird"}) 59 | ]; 60 | 61 | var expectedOutput = "\t\t\t~\n\t\tz\n\n\t\t\t\t\t\t\t\t~\n\t\t\t\t\t\t\tj\n\n\t\t\t\t\t\t\t\t~\n\t\t\t\t\t\t" + 62 | "i\n\n\t\t\t\t\t\t\t\t~\n\t\t\t\t\t\t\thi\n\n\t\t\t\t\t\t\t\t~\n\t\t\t\t\th\n\n\t\t\t\t\t\t\t~\n\t\t\t\t\t\t" + 63 | "ff\n\n\t\t\t\t\t\t\t~\n\t\t\t\tf\n\n\t\t\t\t\t\t~\n\t\t\t\t\tee\n\n\t\t\t\t\t\t~\n\t\t\td\n\n\t\t\t\t~\n\t" + 64 | "ca\n\n\t\t~\nc\n\n\t\t\t\t~\n\t\t\tbb\n\n\t\t\t\t~\n\t\tba\n\n\t\t\t~\n\tb\n\n\t\t\t\t\t\t~\n\t\t\t\t\t" + 65 | "ajk\n\n\t\t\t\t\t\t~\n\t\t\t\tab\n\n\t\t\t\t\t~\n\t\t\taa\n\n\t\t\t\t~\n\t\ta\n\n\t\t\t~"; 66 | var orderedWords = [ 67 | [BinaryTree.IN_ORDER, wordsInOrder], 68 | [BinaryTree.PRE_ORDER, wordsPreOrder], 69 | [BinaryTree.POST_ORDER, wordsPostOrder] 70 | ]; 71 | var orderedMammals = [ 72 | [BinaryTree.IN_ORDER, mammalsInOrder], 73 | [BinaryTree.PRE_ORDER, mammalsPreOrder], 74 | [BinaryTree.POST_ORDER, mammalsPostOrder] 75 | ]; 76 | 77 | 78 | helper.setup( 79 | it, 80 | BinaryTree, 81 | words, 82 | orderedWords, 83 | mammals, 84 | orderedMammals, 85 | expectedOutput); 86 | 87 | 88 | }).as(module); 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /test/collections/Collection.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | Collection = comb.collections.Collection; 6 | 7 | 8 | it.describe("comb.collections.Collection", function (it) { 9 | 10 | 11 | var collection = new Collection(); 12 | ["concat", "join", "slice", "toString", "indexOf", "lastIndexOf"].forEach(function (m) { 13 | it.describe("#" + m, function (it) { 14 | it.should("be a method", function () { 15 | assert.isFunction(collection[m]); 16 | }); 17 | 18 | it.should("throw an error if invoked", function () { 19 | assert.throws(function () { 20 | collection[m](); 21 | }); 22 | }); 23 | }); 24 | }); 25 | 26 | }).as(module); 27 | -------------------------------------------------------------------------------- /test/collections/Heap.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | Heap = comb.collections.Heap; 6 | 7 | 8 | it.describe("comb.collections.Heap", function (it) { 9 | var heap = new Heap(); 10 | 11 | it.should("throw an error when calling insert and remove methods", function () { 12 | assert.throws(function () { 13 | heap.insert(1, "hello"); 14 | }); 15 | assert.throws(function () { 16 | heap.__downHeap(); 17 | }); 18 | }); 19 | 20 | it.should("throw an error when using an invalid key", function () { 21 | assert.throws(function () { 22 | heap.insert("1", 2); 23 | }); 24 | }); 25 | 26 | 27 | }).as(module); 28 | 29 | -------------------------------------------------------------------------------- /test/collections/Iterable.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | Iterable = comb.collections.Iterable; 6 | 7 | 8 | it.describe("comb.collections.Iterable", function (it) { 9 | 10 | var iter = new Iterable(); 11 | ["filter", "forEach", "every", "map", "some", "reduce", "reduceRight"].forEach(function (m) { 12 | it.describe("#" + m, function (it) { 13 | 14 | it.should("be a function", function () { 15 | assert.isFunction(iter[m]); 16 | }); 17 | 18 | it.should("throw an error if invoked", function () { 19 | assert.throws(function () { 20 | iter[m](); 21 | }); 22 | }); 23 | 24 | }); 25 | }); 26 | 27 | }).as(module); 28 | -------------------------------------------------------------------------------- /test/collections/MaxHeap.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | MaxHeap = comb.collections.MaxHeap; 6 | 7 | it.describe("comb.collections.MaxHeap", function (it) { 8 | 9 | it.should("know its count", function () { 10 | var h = new MaxHeap(); 11 | h.insert(0, 'a'); 12 | h.insert(1, 'b'); 13 | h.insert(2, 'c'); 14 | h.insert(3, 'd'); 15 | assert.equal(h.count, 4); 16 | h.remove(); 17 | assert.equal(h.count, 3); 18 | h.remove(); 19 | h.remove(); 20 | h.remove(); 21 | assert.equal(h.count, 0); 22 | }); 23 | 24 | it.should("know its keys", function () { 25 | var h = new MaxHeap(); 26 | h.insert(0, 'a'); 27 | h.insert(1, 'b'); 28 | h.insert(2, 'c'); 29 | h.insert(3, 'd'); 30 | var keys = h.keys; 31 | for (var i = 0; i < 4; i++) { 32 | assert.isTrue(keys.indexOf(i) !== -1); 33 | } 34 | for (i = 0; i < 4; i++) { 35 | assert.isTrue(h.containsKey(i)); 36 | } 37 | assert.isFalse(h.containsKey(4)); 38 | }); 39 | 40 | it.should("know its values", function () { 41 | var h = new MaxHeap(); 42 | h.insert(0, 'a'); 43 | h.insert(1, 'b'); 44 | h.insert(2, 'c'); 45 | h.insert(3, 'd'); 46 | var vals = h.values; 47 | assert.isTrue(vals.indexOf("a") !== -1); 48 | assert.isTrue(vals.indexOf("b") !== -1); 49 | assert.isTrue(vals.indexOf("c") !== -1); 50 | assert.isTrue(vals.indexOf("d") !== -1); 51 | assert.isTrue(h.containsValue("a")); 52 | assert.isTrue(h.containsValue("b")); 53 | assert.isTrue(h.containsValue("c")); 54 | assert.isTrue(h.containsValue("d")); 55 | assert.isFalse(h.containsValue("e")); 56 | }); 57 | 58 | it.should("remove items ", function () { 59 | var h = new MaxHeap(); 60 | h.insert(0, 'a'); 61 | h.insert(1, 'b'); 62 | h.insert(2, 'c'); 63 | h.insert(3, 'd'); 64 | assert.isFalse(h.isEmpty); 65 | h.remove(); 66 | assert.isFalse(h.isEmpty); 67 | h.remove(); 68 | assert.isFalse(h.isEmpty); 69 | h.remove(); 70 | assert.isFalse(h.isEmpty); 71 | h.remove(); 72 | assert.isTrue(h.isEmpty); 73 | }); 74 | 75 | it.should("clear all values", function () { 76 | var h = new MaxHeap(); 77 | h.insert(0, 'a'); 78 | h.insert(1, 'b'); 79 | h.insert(2, 'c'); 80 | h.insert(3, 'd'); 81 | h.clear(); 82 | assert.isTrue(h.isEmpty); 83 | assert.equal(h.count, 0, 'count, should be 4'); 84 | }); 85 | 86 | it.should("peek in order ", function () { 87 | var h = new MaxHeap(); 88 | h.insert(0, 'a'); 89 | h.insert(1, 'b'); 90 | h.insert(2, 'c'); 91 | h.insert(3, 'd'); 92 | assert.equal(h.peekKey(), 3); 93 | assert.equal(h.peek(), "d"); 94 | h.clear(); 95 | assert.isUndefined(h.peek()); 96 | assert.isUndefined(h.peekKey()); 97 | h = new MaxHeap(); 98 | h.insert(1, 'b'); 99 | h.insert(3, 'd'); 100 | h.insert(0, 'a'); 101 | h.insert(2, 'c'); 102 | assert.equal(h.peekKey(), 3); 103 | assert.equal(h.peek(), "d"); 104 | 105 | }); 106 | 107 | it.should("remove elements in order", function () { 108 | var h = new MaxHeap(); 109 | h.insert(0, 'a'); 110 | h.insert(1, 'b'); 111 | h.insert(2, 'c'); 112 | h.insert(3, 'd'); 113 | assert.equal(h.remove(), "d"); 114 | assert.equal(h.remove(), "c"); 115 | assert.equal(h.remove(), "b"); 116 | assert.equal(h.remove(), "a"); 117 | h = new MaxHeap(); 118 | h.insert(1, 'b'); 119 | h.insert(3, 'd'); 120 | h.insert(0, 'a'); 121 | h.insert(2, 'c'); 122 | assert.equal(h.remove(), "d"); 123 | assert.equal(h.remove(), "c"); 124 | assert.equal(h.remove(), "b"); 125 | assert.equal(h.remove(), "a"); 126 | 127 | }); 128 | 129 | 130 | it.should("peek as items are inserted ", function () { 131 | var h = new MaxHeap(); 132 | h.insert(3, 'c'); 133 | assert.equal(h.peek(), 'c'); 134 | h.insert(2, 'b'); 135 | assert.equal(h.peek(), 'c'); 136 | h.insert(1, 'd'); 137 | assert.equal(h.peek(), 'c'); 138 | h.insert(0, 'a'); 139 | assert.equal(h.peek(), 'c'); 140 | h.clear(); 141 | h.insert(1, 'a'); 142 | assert.equal(h.peek(), 'a'); 143 | h.insert(3, 'b'); 144 | assert.equal(h.peek(), 'b'); 145 | h.insert(0, 'c'); 146 | assert.equal(h.peek(), 'b'); 147 | h.insert(2, 'd'); 148 | assert.equal(h.peek(), 'b'); 149 | }); 150 | 151 | it.should("print", function () { 152 | var h = new MaxHeap(); 153 | h.insert(1, 'b'); 154 | h.insert(3, 'd'); 155 | h.insert(0, 'a'); 156 | h.insert(2, 'c'); 157 | var res = []; 158 | var orig = console.log; 159 | console.log = function (str) { 160 | res.push(str); 161 | }; 162 | h.print(); 163 | console.log = orig; 164 | res = res.join("\n"); 165 | 166 | var expected = "\t\t~\n\t0 : a\n\n\t\t~\n3 : d\n\n\t\t~\n\t2 : c\n\n\t\t\t~\n\t\t1 : b\n\n\t\t\t~"; 167 | assert.equal(res, expected); 168 | }); 169 | 170 | 171 | }).as(module); 172 | 173 | 174 | -------------------------------------------------------------------------------- /test/collections/MinHeap.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | MinHeap = comb.collections.MinHeap; 6 | 7 | it.describe("comb.collections.MinHeap", function (it) { 8 | 9 | it.should("know its count", function () { 10 | var h = new MinHeap(); 11 | h.insert(0, 'a'); 12 | h.insert(1, 'b'); 13 | h.insert(2, 'c'); 14 | h.insert(3, 'd'); 15 | assert.equal(h.count, 4); 16 | h.remove(); 17 | assert.equal(h.count, 3); 18 | h.remove(); 19 | h.remove(); 20 | h.remove(); 21 | assert.equal(h.count, 0); 22 | }); 23 | 24 | it.should("know its keys", function () { 25 | var h = new MinHeap(); 26 | h.insert(0, 'a'); 27 | h.insert(1, 'b'); 28 | h.insert(2, 'c'); 29 | h.insert(3, 'd'); 30 | var keys = h.keys; 31 | for (var i = 0; i < 4; i++) { 32 | assert.isTrue(keys.indexOf(i) !== -1); 33 | } 34 | for (i = 0; i < 4; i++) { 35 | assert.isTrue(h.containsKey(i)); 36 | } 37 | assert.isFalse(h.containsKey(4)); 38 | }); 39 | 40 | it.should("know its values", function () { 41 | var h = new MinHeap(); 42 | h.insert(0, 'a'); 43 | h.insert(1, 'b'); 44 | h.insert(2, 'c'); 45 | h.insert(3, 'd'); 46 | var vals = h.values; 47 | assert.isTrue(vals.indexOf("a") !== -1); 48 | assert.isTrue(vals.indexOf("b") !== -1); 49 | assert.isTrue(vals.indexOf("c") !== -1); 50 | assert.isTrue(vals.indexOf("d") !== -1); 51 | assert.isTrue(h.containsValue("a")); 52 | assert.isTrue(h.containsValue("b")); 53 | assert.isTrue(h.containsValue("c")); 54 | assert.isTrue(h.containsValue("d")); 55 | assert.isFalse(h.containsValue("e")); 56 | }); 57 | 58 | it.should("remove items ", function () { 59 | var h = new MinHeap(); 60 | h.insert(0, 'a'); 61 | h.insert(1, 'b'); 62 | h.insert(2, 'c'); 63 | h.insert(3, 'd'); 64 | assert.isFalse(h.isEmpty); 65 | h.remove(); 66 | assert.isFalse(h.isEmpty); 67 | h.remove(); 68 | assert.isFalse(h.isEmpty); 69 | h.remove(); 70 | assert.isFalse(h.isEmpty); 71 | h.remove(); 72 | assert.isTrue(h.isEmpty); 73 | }); 74 | 75 | it.should("clear all values", function () { 76 | var h = new MinHeap(); 77 | h.insert(0, 'a'); 78 | h.insert(1, 'b'); 79 | h.insert(2, 'c'); 80 | h.insert(3, 'd'); 81 | h.clear(); 82 | assert.isTrue(h.isEmpty); 83 | assert.equal(h.count, 0, 'count, should be 4'); 84 | }); 85 | 86 | it.should("peek in order ", function () { 87 | var h = new MinHeap(); 88 | h.insert(0, 'a'); 89 | h.insert(1, 'b'); 90 | h.insert(2, 'c'); 91 | h.insert(3, 'd'); 92 | assert.equal(h.peekKey(), 0); 93 | assert.equal(h.peek(), "a"); 94 | h.clear(); 95 | assert.isUndefined(h.peek()); 96 | assert.isUndefined(h.peekKey()); 97 | h = new MinHeap(); 98 | h.insert(1, 'b'); 99 | h.insert(3, 'd'); 100 | h.insert(0, 'a'); 101 | h.insert(2, 'c'); 102 | assert.equal(h.peekKey(), 0); 103 | assert.equal(h.peek(), "a"); 104 | 105 | }); 106 | 107 | it.should("remove elements in order", function () { 108 | var h = new MinHeap(); 109 | h.insert(0, 'a'); 110 | h.insert(1, 'b'); 111 | h.insert(2, 'c'); 112 | h.insert(3, 'd'); 113 | assert.equal(h.remove(), "a"); 114 | assert.equal(h.remove(), "b"); 115 | assert.equal(h.remove(), "c"); 116 | assert.equal(h.remove(), "d"); 117 | h = new MinHeap(); 118 | h.insert(1, 'b'); 119 | h.insert(3, 'd'); 120 | h.insert(0, 'a'); 121 | h.insert(2, 'c'); 122 | assert.equal(h.remove(), "a"); 123 | assert.equal(h.remove(), "b"); 124 | assert.equal(h.remove(), "c"); 125 | assert.equal(h.remove(), "d"); 126 | }); 127 | 128 | 129 | it.should("peek as items are inserted ", function () { 130 | var h = new MinHeap(); 131 | h.insert(3, 'd'); 132 | assert.equal(h.peek(), 'd'); 133 | h.insert(2, 'c'); 134 | assert.equal(h.peek(), 'c'); 135 | h.insert(1, 'b'); 136 | assert.equal(h.peek(), 'b'); 137 | h.insert(0, 'a'); 138 | assert.equal(h.peek(), 'a'); 139 | h.clear(); 140 | h.insert(1, 'b'); 141 | assert.equal(h.peek(), 'b'); 142 | h.insert(3, 'd'); 143 | assert.equal(h.peek(), 'b'); 144 | h.insert(0, 'a'); 145 | assert.equal(h.peek(), 'a'); 146 | h.insert(2, 'c'); 147 | assert.equal(h.peek(), 'a'); 148 | }); 149 | 150 | 151 | }).as(module); 152 | 153 | -------------------------------------------------------------------------------- /test/collections/Pool.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | Pool = comb.collections.Pool; 6 | 7 | it.describe("comb.collections.Pool", function (it) { 8 | 9 | var p = new Pool(); 10 | 11 | it.should("know its minObjects count ", function () { 12 | assert.equal(p.minObjects, 0); 13 | }); 14 | 15 | it.should("know its free count", function () { 16 | assert.equal(p.freeCount, 0); 17 | }); 18 | 19 | it.should("know its in use count", function () { 20 | assert.equal(p.inUseCount, 0); 21 | }); 22 | 23 | it.should("know its total count", function () { 24 | assert.equal(p.count, 0); 25 | }); 26 | 27 | it.should("only return 1 object", function () { 28 | var o = p.getObject(); 29 | assert.isObject(o); 30 | assert.isUndefined(p.getObject()); 31 | p.returnObject(o); 32 | }); 33 | 34 | it.should("update the maxObjects ", function () { 35 | p.maxObjects = 3; 36 | var o = p.getObject(), o1 = p.getObject(), o2 = p.getObject(); 37 | assert.isObject(o); 38 | assert.isUndefined(p.getObject()); 39 | p.maxObjects = 1; 40 | p.returnObject(o2); 41 | p.returnObject(o1); 42 | p.returnObject(o); 43 | assert.equal(p.getObject(), o); 44 | p.returnObject(o); 45 | assert.throws(function () { 46 | p.maxObjects = -1; 47 | }); 48 | p.maxObjects = 5; 49 | }); 50 | 51 | it.should("update the minObjects ", function () { 52 | p.minObjects = 5; 53 | assert.equal(p.inUseCount, 0); 54 | assert.equal(p.count, 5); 55 | assert.equal(p.freeCount, 5); 56 | assert.equal(p.minObjects, 5); 57 | }); 58 | 59 | it.should("throw an error when creating a with maxObjects less than minObject", function () { 60 | assert.throws(function () { 61 | new Pool({maxObject:1, minObjects:3}); 62 | }); 63 | }); 64 | 65 | it.should("return objects properly", function () { 66 | var p = new Pool({maxObjects:3}); 67 | 68 | var o1 = p.getObject(); 69 | assert.isObject(o1); 70 | assert.equal(p.freeCount, 0); 71 | assert.equal(p.inUseCount, 1); 72 | assert.equal(p.count, 1); 73 | var o2 = p.getObject(); 74 | assert.isObject(o2); 75 | assert.equal(p.freeCount, 0); 76 | assert.equal(p.inUseCount, 2); 77 | assert.equal(p.count, 2); 78 | var o3 = p.getObject(); 79 | assert.isObject(o3); 80 | assert.equal(p.freeCount, 0); 81 | assert.equal(p.inUseCount, 3); 82 | assert.equal(p.count, 3); 83 | assert.isUndefined(p.getObject()); 84 | assert.equal(p.freeCount, 0); 85 | assert.equal(p.inUseCount, 3); 86 | assert.equal(p.count, 3); 87 | p.returnObject(o1); 88 | assert.equal(p.freeCount, 1); 89 | assert.equal(p.inUseCount, 2); 90 | assert.equal(p.count, 3); 91 | p.returnObject(o2); 92 | assert.equal(p.freeCount, 2); 93 | assert.equal(p.inUseCount, 1); 94 | assert.equal(p.count, 3); 95 | p.returnObject(o3); 96 | assert.equal(p.freeCount, 3); 97 | assert.equal(p.inUseCount, 0); 98 | assert.equal(p.count, 3); 99 | 100 | }); 101 | 102 | it.should("allow adjusting maxObjects", function () { 103 | var p = new Pool(); 104 | 105 | assert.equal(p.freeCount, 0); 106 | assert.equal(p.inUseCount, 0); 107 | assert.equal(p.count, 0); 108 | assert.isObject(p.getObject()); 109 | assert.isUndefined(p.getObject()); 110 | p.maxObjects = 3; 111 | assert.equal(p.freeCount, 0); 112 | assert.equal(p.inUseCount, 1); 113 | assert.equal(p.count, 1); 114 | assert.isObject(p.getObject()); 115 | assert.equal(p.freeCount, 0); 116 | assert.equal(p.inUseCount, 2); 117 | assert.equal(p.count, 2); 118 | assert.isObject(p.getObject()); 119 | assert.equal(p.freeCount, 0); 120 | assert.equal(p.inUseCount, 3); 121 | assert.equal(p.count, 3); 122 | 123 | }); 124 | 125 | it.describe("creating a new pool maxObjects", function (it) { 126 | var p = new Pool({maxObjects:3}); 127 | 128 | it.should("know its maxObjects count ", function () { 129 | assert.equal(p.maxObjects, 3); 130 | }); 131 | 132 | it.should("only return 3 object", function () { 133 | assert.isObject(p.getObject()); 134 | assert.isObject(p.getObject()); 135 | assert.isObject(p.getObject()); 136 | assert.isUndefined(p.getObject()); 137 | }); 138 | 139 | it.should("know its free count", function () { 140 | assert.equal(p.freeCount, 0); 141 | }); 142 | 143 | it.should("know its in use count", function () { 144 | assert.equal(p.inUseCount, 3); 145 | }); 146 | 147 | it.should("know its total count", function () { 148 | assert.equal(p.count, 3); 149 | }); 150 | }); 151 | }).as(module); 152 | -------------------------------------------------------------------------------- /test/collections/PriorityQueue.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | PriorityQueue = comb.collections.PriorityQueue; 6 | 7 | it.describe("comb.collections.PriorityQueue", function (it) { 8 | 9 | it.should("know its count", function () { 10 | var q = new PriorityQueue(); 11 | q.enqueue(0, 'a'); 12 | q.enqueue(1, 'b'); 13 | q.enqueue(2, 'c'); 14 | q.enqueue(3, 'd'); 15 | assert.equal(q.count, 4); 16 | q.dequeue(); 17 | assert.equal(q.count, 3); 18 | q.dequeue(); 19 | q.dequeue(); 20 | q.dequeue(); 21 | assert.equal(q.count, 0); 22 | }); 23 | 24 | it.should("know its keys", function () { 25 | var q = new PriorityQueue(); 26 | q.enqueue(0, 'a'); 27 | q.enqueue(1, 'b'); 28 | q.enqueue(2, 'c'); 29 | q.enqueue(3, 'd'); 30 | 31 | var keys = q.keys; 32 | for (var i = 0; i < 4; i++) { 33 | assert.isTrue(keys.indexOf(i) !== -1); 34 | } 35 | for (i = 0; i < 4; i++) { 36 | assert.isTrue(q.containsKey(i)); 37 | } 38 | assert.isFalse(q.containsKey(4)); 39 | 40 | }); 41 | 42 | it.should("know its values", function () { 43 | var q = new PriorityQueue(); 44 | q.enqueue(0, 'a'); 45 | q.enqueue(1, 'b'); 46 | q.enqueue(2, 'c'); 47 | q.enqueue(3, 'd'); 48 | var vals = q.values; 49 | assert.isTrue(vals.indexOf("a") !== -1); 50 | assert.isTrue(vals.indexOf("b") !== -1); 51 | assert.isTrue(vals.indexOf("c") !== -1); 52 | assert.isTrue(vals.indexOf("d") !== -1); 53 | assert.isTrue(q.containsValue("a")); 54 | assert.isTrue(q.containsValue("b")); 55 | assert.isTrue(q.containsValue("c")); 56 | assert.isTrue(q.containsValue("d")); 57 | assert.isFalse(q.containsValue("e")); 58 | }); 59 | 60 | it.should("dequeue items ", function () { 61 | var q = new PriorityQueue(); 62 | q.enqueue(0, 'a'); 63 | q.enqueue(1, 'b'); 64 | q.enqueue(2, 'c'); 65 | q.enqueue(3, 'd'); 66 | assert.isFalse(q.isEmpty); 67 | q.dequeue(); 68 | assert.isFalse(q.isEmpty); 69 | q.dequeue(); 70 | assert.isFalse(q.isEmpty); 71 | q.dequeue(); 72 | assert.isFalse(q.isEmpty); 73 | q.dequeue(); 74 | assert.isTrue(q.isEmpty); 75 | }); 76 | 77 | it.should("clear its items", function () { 78 | 79 | var q = new PriorityQueue(); 80 | q.enqueue(0, 'a'); 81 | q.enqueue(1, 'b'); 82 | q.enqueue(2, 'c'); 83 | q.enqueue(3, 'd'); 84 | q.clear(); 85 | assert.isTrue(q.isEmpty); 86 | assert.equal(q.count, 0, 'count, should be 4'); 87 | }); 88 | 89 | it.should("peek items in order ", function () { 90 | var q = new PriorityQueue(); 91 | q.enqueue(0, 'a'); 92 | q.enqueue(1, 'b'); 93 | q.enqueue(2, 'c'); 94 | q.enqueue(3, 'd'); 95 | assert.equal(q.peek(), "a"); 96 | q.clear(); 97 | assert.isUndefined(q.peek()); 98 | q = new PriorityQueue(); 99 | q.enqueue(1, 'b'); 100 | q.enqueue(3, 'd'); 101 | q.enqueue(0, 'a'); 102 | q.enqueue(2, 'c'); 103 | assert.equal(q.peek(), "a"); 104 | }); 105 | 106 | it.should("dequeue in order", function () { 107 | var q = new PriorityQueue(); 108 | q.enqueue(0, 'a'); 109 | q.enqueue(1, 'b'); 110 | q.enqueue(2, 'c'); 111 | q.enqueue(3, 'd'); 112 | assert.equal(q.dequeue(), "a"); 113 | assert.equal(q.dequeue(), "b"); 114 | assert.equal(q.dequeue(), "c"); 115 | assert.equal(q.dequeue(), "d"); 116 | q = new PriorityQueue(); 117 | q.enqueue(1, 'b'); 118 | q.enqueue(3, 'd'); 119 | q.enqueue(0, 'a'); 120 | q.enqueue(2, 'c'); 121 | assert.equal(q.dequeue(), "a"); 122 | assert.equal(q.dequeue(), "b"); 123 | assert.equal(q.dequeue(), "c"); 124 | assert.equal(q.dequeue(), "d"); 125 | }); 126 | 127 | it.should("peek as enqueued properly", function () { 128 | var q = new PriorityQueue(); 129 | q.enqueue(3, 'd'); 130 | assert.equal(q.peek(), 'd'); 131 | q.enqueue(2, 'c'); 132 | assert.equal(q.peek(), 'c'); 133 | q.enqueue(1, 'b'); 134 | assert.equal(q.peek(), 'b'); 135 | q.enqueue(0, 'a'); 136 | assert.equal(q.peek(), 'a'); 137 | q.clear(); 138 | q.enqueue(1, 'b'); 139 | assert.equal(q.peek(), 'b'); 140 | q.enqueue(3, 'd'); 141 | assert.equal(q.peek(), 'b'); 142 | q.enqueue(0, 'a'); 143 | assert.equal(q.peek(), 'a'); 144 | q.enqueue(2, 'c'); 145 | assert.equal(q.peek(), 'a'); 146 | }); 147 | 148 | }).as(module); 149 | 150 | 151 | -------------------------------------------------------------------------------- /test/collections/Queue.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | Queue = comb.collections.Queue; 6 | 7 | 8 | it.describe("comb.collections.Queue", function (it) { 9 | var queue = new Queue(); 10 | 11 | it.should("enqueue", function () { 12 | queue.enqueue("test"); 13 | assert.isTrue(queue.contains("test")); 14 | assert.equal("test", queue.toString()); 15 | }); 16 | it.should("dequeue", function () { 17 | assert.equal(queue.dequeue(), "test"); 18 | assert.isFalse(queue.contains("test")); 19 | }); 20 | it.should("peek", function () { 21 | queue.enqueue("test"); 22 | assert.equal(queue.peek(), "test"); 23 | assert.isTrue(queue.contains("test")); 24 | }); 25 | it.should("know if it is empty", function () { 26 | assert.isFalse(queue.isEmpty); 27 | queue.dequeue(); 28 | assert.isTrue(queue.isEmpty); 29 | }); 30 | it.should("know its element count", function () { 31 | queue.enqueue("test"); 32 | queue.enqueue("test1"); 33 | queue.enqueue("test2"); 34 | queue.enqueue("test3"); 35 | queue.enqueue("test4"); 36 | queue.enqueue("test5"); 37 | queue.enqueue("test6"); 38 | assert.equal(queue.count, 7); 39 | assert.equal(",,test,test1,test2,test3,test4,test5,test6", queue.toString()); 40 | }); 41 | it.should("return all values", function () { 42 | var vals = ["test", "test1", "test2", "test3", "test4", "test5", "test6"]; 43 | assert.isTrue(queue.values.every(function (v, i) { 44 | return v === vals[i]; 45 | })); 46 | }); 47 | 48 | it.should("remove values", function () { 49 | queue.remove("test6"); 50 | queue.remove("test5"); 51 | queue.remove("test4"); 52 | queue.remove("test3"); 53 | queue.remove("test2"); 54 | queue.remove("test1"); 55 | queue.remove("test"); 56 | assert.isTrue(queue.isEmpty); 57 | }); 58 | 59 | it.should("clear all elements", function () { 60 | queue.enqueue("test"); 61 | queue.enqueue("test1"); 62 | queue.enqueue("test2"); 63 | queue.enqueue("test3"); 64 | queue.enqueue("test4"); 65 | queue.enqueue("test5"); 66 | queue.enqueue("test6"); 67 | assert.isFalse(queue.isEmpty); 68 | assert.equal(",,,test,test1,test2,test3,test4,test5,test6", queue.toString()); 69 | queue.clear(); 70 | assert.isTrue(queue.isEmpty); 71 | assert.equal(queue.count, 0); 72 | }); 73 | it.should("return undefined if empty and dequeue is called", function () { 74 | assert.isUndefined(queue.dequeue()); 75 | }); 76 | 77 | 78 | }).as(module); 79 | -------------------------------------------------------------------------------- /test/collections/RedBlackTree.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | helper = require("./treeTest.helper.js"), 6 | Mammal = helper.Mammal, 7 | RedBlackTree = comb.collections.RedBlackTree; 8 | 9 | 10 | it.describe("comb.collections.RedBlackTree", function (it) { 11 | 12 | var words = ["c", "ca", "b", "ba", "bb", "a", "aa", "ab", "z", "d", "f", "h", "i", "ee", "ff", "hi", "j", "ajk"]; 13 | var wordsInOrder = [ 'a', 'aa', 'ab', 'ajk', 'b', 'ba', 'bb', 'c', 'ca', 'd', 'ee', 'f', 'ff', 'h', 'hi', 'i', 'j', 'z' ]; 14 | var wordsPreOrder = ['d', 'ba', 'aa', 'a', 'ajk', 'ab', 'b', 'c', 'bb', 'ca', 'h', 'f', 'ee', 'ff', 'i', 'hi', 'z', 'j']; 15 | var wordsPostOrder = [ 'a', 'ab', 'b', 'ajk', 'aa', 'bb', 'ca', 'c', 'ba', 'ee', 'ff', 'f', 'hi', 'j', 'z', 'i', 'h', 'd']; 16 | 17 | 18 | 19 | var mammals = [ 20 | new Mammal({type:"bird"}), 21 | new Mammal({type:"dog"}), 22 | new Mammal({type:"rat"}), 23 | new Mammal({type:"zebra"}), 24 | new Mammal({type:"mouse"}), 25 | new Mammal({type:"horse"}), 26 | new Mammal({type:"squirrel"}), 27 | new Mammal({type:"groundhog"}) 28 | ], 29 | 30 | mammalsInOrder = [ 31 | new Mammal({type:"bird"}), 32 | new Mammal({type:"dog"}), 33 | new Mammal({type:"groundhog"}), 34 | new Mammal({type:"horse"}), 35 | new Mammal({type:"mouse"}), 36 | new Mammal({type:"rat"}), 37 | new Mammal({type:"squirrel"}), 38 | new Mammal({type:"zebra"}) 39 | ], 40 | 41 | mammalsPreOrder = [ 42 | new Mammal({type:"dog"}), 43 | new Mammal({type:"bird"}), 44 | new Mammal({type:"rat"}), 45 | new Mammal({type:"horse"}), 46 | new Mammal({type:"groundhog"}), 47 | new Mammal({type:"mouse"}), 48 | new Mammal({type:"zebra"}), 49 | new Mammal({type:"squirrel"}) 50 | ], 51 | 52 | mammalsPostOrder = [ 53 | new Mammal({type:"bird"}), 54 | new Mammal({type:"groundhog"}), 55 | new Mammal({type:"mouse"}), 56 | new Mammal({type:"horse"}), 57 | new Mammal({type:"squirrel"}), 58 | new Mammal({type:"zebra"}), 59 | new Mammal({type:"rat"}), 60 | new Mammal({type:"dog"}) 61 | ]; 62 | 63 | var orderedMammals = [ 64 | [RedBlackTree.IN_ORDER, mammalsInOrder], 65 | [RedBlackTree.PRE_ORDER, mammalsPreOrder], 66 | [RedBlackTree.POST_ORDER, mammalsPostOrder] 67 | ]; 68 | 69 | var expectedOutput = "\t\t\t\t~\n\t\t\tBLACK:z\n\n\t\t\t\t\t~\n\t\t\t\tRED:j\n\n\t\t\t\t\t~\n\t\tBLACK:i" + 70 | "\n\n\t\t\t\t~\n\t\t\tBLACK:hi\n\n\t\t\t\t~\n\tBLACK:h\n\n\t\t\t\t~\n\t\t\tBLACK:ff\n\n\t\t\t\t~" + 71 | "\n\t\tBLACK:f\n\n\t\t\t\t~\n\t\t\tBLACK:ee\n\n\t\t\t\t~\nBLACK:d\n\n\t\t\t\t~\n\t\t\tBLACK:ca" + 72 | "\n\n\t\t\t\t~\n\t\tBLACK:c\n\n\t\t\t\t~\n\t\t\tBLACK:bb\n\n\t\t\t\t~\n\tBLACK:ba" + 73 | "\n\n\t\t\t\t\t~\n\t\t\t\tRED:b\n\n\t\t\t\t\t~\n\t\t\tBLACK:ajk\n\n\t\t\t\t\t~\n\t\t\t\t" + 74 | "RED:ab\n\n\t\t\t\t\t~\n\t\tBLACK:aa\n\n\t\t\t\t~\n\t\t\tBLACK:a\n\n\t\t\t\t~"; 75 | var orderedWords = [ 76 | [RedBlackTree.IN_ORDER, wordsInOrder], 77 | [RedBlackTree.PRE_ORDER, wordsPreOrder], 78 | [RedBlackTree.POST_ORDER, wordsPostOrder] 79 | ]; 80 | 81 | helper.setup( 82 | it, 83 | RedBlackTree, 84 | words, 85 | orderedWords, 86 | mammals, 87 | orderedMammals, 88 | expectedOutput); 89 | 90 | }).as(module); -------------------------------------------------------------------------------- /test/collections/Stack.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | Stack = comb.collections.Stack; 6 | 7 | it.describe("comb.collections.Stack", function (it) { 8 | var stack = new Stack(); 9 | 10 | it.should("push", function () { 11 | stack.push("test"); 12 | assert.isTrue(stack.contains("test")); 13 | assert.equal("test", stack.toString()); 14 | }); 15 | 16 | it.should("pop", function () { 17 | assert.equal(stack.pop(), "test"); 18 | assert.isFalse(stack.contains("test")); 19 | }); 20 | 21 | it.should("peek", function () { 22 | stack.push("test"); 23 | assert.equal(stack.peek(), "test"); 24 | assert.isTrue(stack.contains("test")); 25 | }); 26 | 27 | it.should("know if it is empty", function () { 28 | assert.isFalse(stack.isEmpty); 29 | stack.pop(); 30 | assert.isTrue(stack.isEmpty); 31 | }); 32 | 33 | it.should("know its element count", function () { 34 | stack.push("test"); 35 | stack.push("test1"); 36 | stack.push("test2"); 37 | stack.push("test3"); 38 | stack.push("test4"); 39 | stack.push("test5"); 40 | stack.push("test6"); 41 | assert.equal(stack.count, 7); 42 | assert.equal("test,test1,test2,test3,test4,test5,test6", stack.toString()); 43 | }); 44 | 45 | it.should("pop values", function () { 46 | assert.equal(stack.pop(), "test6"); 47 | assert.equal(stack.pop(), "test5"); 48 | assert.equal(stack.pop(), "test4"); 49 | assert.equal(stack.pop(), "test3"); 50 | assert.equal(stack.pop(), "test2"); 51 | assert.equal(stack.pop(), "test1"); 52 | assert.equal(stack.pop(), "test"); 53 | assert.equal(stack.count, 0); 54 | }); 55 | 56 | it.should("return all values", function () { 57 | stack.push("test"); 58 | stack.push("test1"); 59 | stack.push("test2"); 60 | stack.push("test3"); 61 | stack.push("test4"); 62 | stack.push("test5"); 63 | stack.push("test6"); 64 | var vals = ["test", "test1", "test2", "test3", "test4", "test5", "test6"].reverse(); 65 | assert.isTrue(stack.values.every(function (v, i) { 66 | return v === vals[i]; 67 | })); 68 | assert.equal("test,test1,test2,test3,test4,test5,test6", stack.toString()); 69 | }); 70 | 71 | it.should("remove values", function () { 72 | stack.remove("test"); 73 | stack.remove("test1"); 74 | stack.remove("test2"); 75 | stack.remove("test3"); 76 | stack.remove("test4"); 77 | stack.remove("test5"); 78 | stack.remove("test6"); 79 | assert.isTrue(stack.isEmpty); 80 | }); 81 | 82 | it.should("clear all elements", function () { 83 | stack.push("test"); 84 | stack.push("test1"); 85 | stack.push("test2"); 86 | stack.push("test3"); 87 | stack.push("test4"); 88 | stack.push("test5"); 89 | stack.push("test6"); 90 | assert.isFalse(stack.isEmpty); 91 | stack.clear(); 92 | assert.isTrue(stack.isEmpty); 93 | assert.equal(stack.count, 0); 94 | }); 95 | 96 | it.should("return undefined if empty and dequeue is called", function () { 97 | assert.isUndefined(stack.pop()); 98 | }); 99 | 100 | }).as(module); 101 | -------------------------------------------------------------------------------- /test/collections/Tree.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | Tree = comb.collections.Tree; 6 | 7 | 8 | it.describe("A Tree interface", function (it) { 9 | var methods = ["insert", "remove"]; 10 | 11 | var tree = new Tree(); 12 | methods.forEach(function (m) { 13 | it.describe("#" + m, function (it) { 14 | it.should("be abstract", function () { 15 | assert.throws(function () { 16 | tree[m](); 17 | }); 18 | }); 19 | }); 20 | }); 21 | 22 | }).as(module); 23 | 24 | -------------------------------------------------------------------------------- /test/extensions/is.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var comb = require("../../index"), 3 | assert = require("assert"), 4 | it = require("it"); 5 | 6 | it.describe("is extension",function (it) { 7 | 8 | it.should("expose equality methods", function () { 9 | assert.isTrue(comb(1).eq(1)); 10 | assert.isFalse(comb(1).eq(2)); 11 | assert.isTrue(comb("hello").eq("hello")); 12 | assert.isFalse(comb("hello").eq("world")); 13 | assert.isFalse(comb(new Date(2001, 1, 1, 1, 1, 1, 111)).eq(new Date(2001, 1, 1, 1, 1, 1, 111))); 14 | assert.isFalse(comb({}).eq({})); 15 | }); 16 | 17 | it.should("expose neq methods", function () { 18 | assert.isFalse(comb(1).neq(1)); 19 | assert.isTrue(comb(1).neq(2)); 20 | assert.isFalse(comb("hello").neq("hello")); 21 | assert.isTrue(comb("hello").neq("world")); 22 | assert.isTrue(comb(new Date(2001, 1, 1, 1, 1, 1, 111)).neq(new Date(2001, 1, 1, 1, 1, 1, 111))); 23 | assert.isTrue(comb({}).neq({})); 24 | }); 25 | 26 | it.should("expose value of properly", function () { 27 | var date = new Date(2001, 1, 1, 1, 1, 1, 111), 28 | date2 = new Date(2001, 1, 1, 1, 1, 1, 111); 29 | assert.deepEqual(comb(date).valueOf(), date.valueOf()); 30 | assert.strictEqual(comb("hello").valueOf(), "hello"); 31 | assert.strictEqual(comb(true).valueOf(), true); 32 | assert.deepEqual(comb({}).valueOf(), {}); 33 | }); 34 | 35 | it.should("print properly", function () { 36 | var orig = console.log, val; 37 | console.log = function (arg) { 38 | val = arg; 39 | }; 40 | comb("hello").print(); 41 | assert.strictEqual(val, "hello"); 42 | 43 | comb(1).print(); 44 | assert.strictEqual(val, 1); 45 | 46 | comb(true).print(); 47 | assert.strictEqual(val, true); 48 | console.log = orig; 49 | }); 50 | 51 | }).as(module); -------------------------------------------------------------------------------- /test/logging/appenders/appender.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../../index"), 5 | logging = comb.logging, 6 | Appender = logging.appenders.Appender; 7 | 8 | it.describe("comb.logging.appenders.Appender", function (it) { 9 | 10 | var MyAppender = comb.define(Appender, { 11 | instance: { 12 | append: function (event) { 13 | if (this._canAppend(event)) { 14 | this.onAppend(comb.string.format(this.__pattern, event)); 15 | } 16 | }, 17 | 18 | onAppend: function () { 19 | 20 | } 21 | } 22 | }).registerType("MyAppender"); 23 | 24 | 25 | var logger1 = logging.Logger.getLogger("AppenderTest"), 26 | logger2 = logging.Logger.getLogger("AppenderTest.one"), 27 | logger3 = logging.Logger.getLogger("AppenderTest.one.two"); 28 | it.describe("adding appender to loggers", function (it) { 29 | var appender; 30 | it.beforeAll(function () { 31 | appender = new Appender({name: "myAppender1"}); 32 | logger1.addAppender(appender); 33 | }); 34 | 35 | it.should("be added to all subloggers", function () { 36 | assert.isTrue(logger1.isAppenderAttached(appender.name)); 37 | assert.isTrue(logger2.isAppenderAttached(appender.name)); 38 | assert.isTrue(logger3.isAppenderAttached(appender.name)); 39 | }); 40 | 41 | it.should("have it level defaulted to all", function () { 42 | assert.equal(appender.level, logging.Level.ALL); 43 | }); 44 | 45 | it.should("allow its level to be set", function () { 46 | appender.level = "info"; 47 | assert.equal(appender.level, logging.Level.INFO); 48 | }); 49 | 50 | it.should("allow its pattern to be set", function () { 51 | appender.pattern = "{message}"; 52 | assert.equal(appender.pattern, "{message}"); 53 | }); 54 | 55 | it.should("be removed from loggers if removeAllAppenders is called", function () { 56 | logger1.removeAppender(appender.name); 57 | assert.isFalse(logger1.isAppenderAttached(appender.name)); 58 | assert.isFalse(logger2.isAppenderAttached(appender.name)); 59 | assert.isFalse(logger3.isAppenderAttached(appender.name)); 60 | logger2.addAppenders([appender]); 61 | assert.isFalse(logger1.isAppenderAttached(appender.name)); 62 | assert.isTrue(logger2.isAppenderAttached(appender.name)); 63 | assert.isTrue(logger3.isAppenderAttached(appender.name)); 64 | logger2.removeAppenders([appender.name]); 65 | assert.isFalse(logger1.isAppenderAttached(appender.name)); 66 | assert.isFalse(logger2.isAppenderAttached(appender.name)); 67 | assert.isFalse(logger3.isAppenderAttached(appender.name)); 68 | logger3.addAppender(appender); 69 | assert.isFalse(logger1.isAppenderAttached(appender.name)); 70 | assert.isFalse(logger2.isAppenderAttached(appender.name)); 71 | assert.isTrue(logger3.isAppenderAttached(appender.name)); 72 | logger3.removeAllAppenders(); 73 | assert.isFalse(logger1.isAppenderAttached(appender.name)); 74 | assert.isFalse(logger2.isAppenderAttached(appender.name)); 75 | assert.isFalse(logger3.isAppenderAttached(appender.name)); 76 | }); 77 | 78 | it.should("throw an error if append is called", function () { 79 | assert.throws(function () { 80 | appender.append(); 81 | }); 82 | }); 83 | }); 84 | 85 | it.describe("adding an appender and setting the level ", function (it) { 86 | var topic; 87 | it.beforeAll(function () { 88 | topic = new MyAppender({level: 6}); 89 | }); 90 | 91 | it.should("have its level set to FATAL", function () { 92 | assert.equal(topic.level, logging.Level.FATAL); 93 | }); 94 | 95 | it.should("only log fatal messages ", function () { 96 | logger1.addAppender(topic); 97 | topic.level = 6; 98 | var levels = ["debug", "trace", "info", "warn", "error", "fatal"]; 99 | var count = 0; 100 | comb.connect(topic, "onAppend", function () { 101 | count++; 102 | }); 103 | levels.forEach(function (l) { 104 | logger1[l](l); 105 | logger2[l](l); 106 | logger3[l](l); 107 | }); 108 | assert.equal(count, 3); 109 | 110 | }); 111 | 112 | }); 113 | 114 | it.describe(".createAppender", function (it) { 115 | it.should("create an appender from a type", function () { 116 | var appender = Appender.createAppender("MyAppender"); 117 | assert.instanceOf(appender, MyAppender); 118 | }); 119 | 120 | it.should("throw an error if an appender is not registered", function () { 121 | assert.throws(function () { 122 | Appender.createAppender("nonexistentappender"); 123 | }); 124 | }); 125 | }); 126 | 127 | }).as(module); 128 | -------------------------------------------------------------------------------- /test/logging/appenders/consoleAppender.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../../index"), 5 | logging = comb.logging, 6 | ConsoleAppender = logging.appenders.ConsoleAppender; 7 | 8 | 9 | it.describe("comb.logging.appenders.ConsoleAppender", function (it) { 10 | 11 | var logger = comb.logger("ConsoleLoggerTest"), appender; 12 | it.beforeAll(function () { 13 | appender = new ConsoleAppender(); 14 | logger.addAppender(appender); 15 | }); 16 | 17 | it.should("be added to the logger", function () { 18 | assert.isTrue(logger.isAppenderAttached("consoleAppender")); 19 | assert.deepEqual(logger.appenders, [appender]); 20 | }); 21 | 22 | 23 | it.should("have then logged to the appender ", function () { 24 | var orig = console.log; 25 | try { 26 | var count = 0; 27 | var levels = ["debug", "trace", "info", "warn", "error", "fatal"]; 28 | console.log = function (str) { 29 | assert.isTrue(str.match(levels[count]) != null); 30 | count++; 31 | }; 32 | levels.forEach(function (l) { 33 | logger[l](l); 34 | }); 35 | assert.equal(count, 6); 36 | } catch (e) { 37 | throw e; 38 | } finally { 39 | console.log = orig; 40 | } 41 | 42 | }); 43 | 44 | it.should("the logger should log no events to the appender ", function () { 45 | 46 | var orig = console.log; 47 | try { 48 | logger.level = logging.Level.OFF; 49 | var count = 0; 50 | var levels = ["debug", "trace", "info", "warn", "error", "fatal"]; 51 | console.log = function (str) { 52 | assert.isTrue(str.match(levels[count]) != null); 53 | count++; 54 | }; 55 | levels.forEach(function (l) { 56 | logger[l](l); 57 | }); 58 | assert.equal(count, 0); 59 | } catch (e) { 60 | throw e; 61 | } finally { 62 | console.log = orig; 63 | } 64 | }); 65 | 66 | it.should("have an wrapStyle option to turn off style", function () { 67 | var styleLogger = comb.logger("ConsoleLoggerIncludeStyleTest"), 68 | styleAppender = new ConsoleAppender({ 69 | wrapStyle: false, 70 | pattern: "{message}" 71 | }), 72 | orig = console.log; 73 | styleLogger.addAppender(styleAppender); 74 | try { 75 | var count = 0; 76 | var levels = ["debug", "info", "warn", "error", "fatal"]; 77 | console.log = function (str) { 78 | assert.equal(str, levels[count]); 79 | count++; 80 | }; 81 | levels.forEach(function (l) { 82 | styleLogger[l](l); 83 | }); 84 | assert.equal(count, 5); 85 | 86 | console.log = function (str) { 87 | assert.isTrue(str.match(/Trace: message/) != null); 88 | count++; 89 | }; 90 | styleLogger.trace("message"); 91 | } catch (e) { 92 | throw e; 93 | } finally { 94 | console.log = orig; 95 | } 96 | }); 97 | 98 | it.should("supports levelNameColored property", function () { 99 | var styleLogger = comb.logger("ConsoleLoggerIncludeStyleTest2"), 100 | styleAppender = new ConsoleAppender({ 101 | wrapStyle: false, 102 | pattern: "{levelNameColored} {message}" 103 | }), 104 | orig = console.log; 105 | styleLogger.addAppender(styleAppender); 106 | try { 107 | var count = 0; 108 | console.log = function (str) { 109 | assert.equal(str, "\x1B[31mERROR\x1B[0m message"); 110 | count++; 111 | }; 112 | styleLogger.error("message"); 113 | assert.equal(count, 1); 114 | } catch (e) { 115 | throw e; 116 | } finally { 117 | console.log = orig; 118 | } 119 | }); 120 | 121 | }).as(module); 122 | -------------------------------------------------------------------------------- /test/logging/appenders/fileAppender.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../../index"), 5 | logging = comb.logging, 6 | FileAppender = logging.appenders.FileAppender; 7 | 8 | 9 | it.describe("comb.logging.appenders.FileAppender", function (it) { 10 | 11 | var MockWriteStream = { 12 | writable: true, 13 | on: function () { 14 | }, 15 | destroySoon: function () { 16 | }, 17 | write: function (str) { 18 | return str; 19 | } 20 | }; 21 | 22 | var logger = logging.Logger.getLogger("FilerAppenderTest"), 23 | appender = new FileAppender({writeStream: MockWriteStream}); 24 | logger.addAppender(appender); 25 | 26 | it.should("be added to the logger", function () { 27 | assert.isTrue(logger.isAppenderAttached("fileAppender")); 28 | assert.deepEqual(logger.appenders, [appender]); 29 | }); 30 | 31 | it.should("have events logged to it by the logger according to level", function () { 32 | 33 | var count = 0; 34 | var levels = ["debug", "trace", "info", "warn", "error", "fatal"]; 35 | var conn = comb.connect(MockWriteStream, "write", function (str) { 36 | assert.isTrue(str.match(levels[count]) != null); 37 | count++; 38 | }); 39 | levels.forEach(function (l) { 40 | logger[l](l); 41 | }); 42 | assert.equal(count, 6); 43 | comb.disconnect(conn); 44 | 45 | 46 | logger.level = logging.Level.OFF; 47 | count = 0; 48 | conn = comb.connect(MockWriteStream, "write", function (str) { 49 | assert.isTrue(str.match(levels[count]) != null); 50 | count++; 51 | }); 52 | levels.forEach(function (l) { 53 | logger[l](l); 54 | }); 55 | assert.equal(count, 0); 56 | comb.disconnect(conn); 57 | 58 | 59 | }); 60 | 61 | 62 | }).as(module); 63 | -------------------------------------------------------------------------------- /test/logging/appenders/jsonAppender.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../../index"), 5 | logging = comb.logging, 6 | JSONAppender = logging.appenders.JSONAppender; 7 | 8 | 9 | it.describe("comb.logging.appenders.JSONAppender", function (it) { 10 | 11 | var MockWriteStream = { 12 | writable: true, 13 | on: function () { 14 | }, 15 | destroySoon: function () { 16 | }, 17 | write: function (str) { 18 | return str; 19 | } 20 | }; 21 | 22 | var logger = logging.Logger.getLogger("JSONAppenderTest"), 23 | appender = new JSONAppender({writeStream: MockWriteStream}); 24 | logger.addAppender(appender); 25 | 26 | it.should("be added to the logger", function () { 27 | assert.isTrue(logger.isAppenderAttached("JSONAppender")); 28 | assert.deepEqual(logger.appenders, [appender]); 29 | }); 30 | 31 | it.should("have events logged to it by the logger according to level", function () { 32 | 33 | var count = 0; 34 | var levels = ["debug", "trace", "info", "warn", "error", "fatal"]; 35 | var conn = comb.connect(MockWriteStream, "write", function (str) { 36 | if (!str.match(/^\[|\]$/)) { 37 | str = str.replace(/^,/, ""); 38 | var obj = JSON.parse(str); 39 | assert.isTrue(obj.message.match(levels[count]) != null); 40 | count++; 41 | } 42 | }); 43 | levels.forEach(function (l) { 44 | logger[l](l); 45 | }); 46 | assert.equal(count, 6); 47 | comb.disconnect(conn); 48 | logger.level = logging.Level.OFF; 49 | count = 0; 50 | conn = comb.connect(MockWriteStream, "write", function (str) { 51 | count++; 52 | }); 53 | levels.forEach(function (l) { 54 | logger[l](l); 55 | }); 56 | assert.equal(count, 0); 57 | comb.disconnect(conn); 58 | }); 59 | 60 | }).as(module); 61 | 62 | 63 | -------------------------------------------------------------------------------- /test/logging/appenders/rollingFileAppender.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../../index"), 5 | logging = comb.logging, 6 | RollingFileAppender = logging.appenders.RollingFileAppender, 7 | fs = require("fs"); 8 | 9 | 10 | it.describe("comb.logging.appenders.RollingFileAppender", function (it) { 11 | var origWatch = fs.watchFile; 12 | var origStat = fs.stat; 13 | var origCreateWriteStream = fs.createWriteStream; 14 | var origReadDir = fs.readdir; 15 | var origUnlink = fs.unlink; 16 | var origRename = fs.rename; 17 | 18 | var size = 0, callwatch; 19 | var setUpFS = function () { 20 | fs.watchFile = function (name, cb) { 21 | //do nothing 22 | }; 23 | 24 | fs.stat = function (name, cb) { 25 | setTimeout(comb.partial(cb, null, {size: size++ % 2 ? 1024 : 0}), 10); 26 | }; 27 | 28 | fs.createWriteStream = function () { 29 | return MockWriteStream; 30 | }; 31 | 32 | fs.readdir = function (dir, cb) { 33 | var ret = ["log.log.3", "noMatch.log.3", "log.log", "log.log.1", "log.log.2"]; 34 | setTimeout(comb.partial(cb, null, ret), 10); 35 | }; 36 | 37 | fs.unlink = function (str, cb) { 38 | setTimeout(cb, 10); 39 | }; 40 | 41 | fs.rename = function (str, str2, cb) { 42 | setTimeout(cb, 10); 43 | }; 44 | }; 45 | 46 | var resetFs = function () { 47 | fs.watchFile = origWatch; 48 | fs.stat = origStat; 49 | fs.createWriteStream = origCreateWriteStream; 50 | fs.readdir = origReadDir; 51 | fs.unlink = origUnlink; 52 | fs.rename = origRename; 53 | }; 54 | 55 | var MockWriteStream = { 56 | writable: true, 57 | on: function (str, cb) { 58 | cb(); 59 | }, 60 | destroySoon: function () { 61 | }, 62 | write: function (str) { 63 | return str; 64 | } 65 | }; 66 | var appender, logger; 67 | it.beforeAll(function () { 68 | logger = logging.Logger.getLogger("RollingFileAppenderTest"); 69 | setUpFS(); 70 | appender = new RollingFileAppender({writeStream: MockWriteStream, maxSize: "1KB", maxBackupIndex: 3}); 71 | logger.addAppender(appender); 72 | }); 73 | 74 | 75 | it.should("be added to the logger", function () { 76 | assert.isTrue(logger.isAppenderAttached("rollingFileAppender")); 77 | assert.deepEqual(logger.appenders, [appender]); 78 | }); 79 | 80 | it.should("have events logged to it", function () { 81 | var count = 0; 82 | var levels = ["debug", "trace", "info", "warn", "error", "fatal"]; 83 | var conn = comb.connect(MockWriteStream, "write", function (str) { 84 | assert.isTrue(str.match(levels[count]) != null); 85 | count++; 86 | }); 87 | levels.forEach(function (l) { 88 | logger[l](l); 89 | }); 90 | assert.equal(count, 6); 91 | comb.disconnect(conn); 92 | 93 | logger.level = logging.Level.OFF; 94 | count = 0; 95 | conn = comb.connect(MockWriteStream, "write", function (str) { 96 | count++; 97 | }); 98 | levels.forEach(function (l) { 99 | logger[l](l); 100 | }); 101 | assert.equal(count, 0); 102 | comb.disconnect(conn); 103 | 104 | }); 105 | 106 | 107 | it.should("rollover files ", function (next) { 108 | 109 | logger.level = logging.Level.INFO; 110 | var renameLogs = [ 111 | {name: "./log.log.2", newName: "./log.log.3"}, 112 | {name: "./log.log.1", newName: "./log.log.2"}, 113 | {name: "./log.log", newName: "./log.log.1"} 114 | ]; 115 | var unlinkCount = 0, renameCount = 0, logCount = 0; 116 | var unlinkConn = comb.connect(fs, "unlink", comb.hitch(this, function (name) { 117 | assert.isTrue(name === "./log.log.3"); 118 | unlinkCount++; 119 | })); 120 | var conn = comb.connect(MockWriteStream, "write", function (str) { 121 | logCount++; 122 | }); 123 | var renameConn = comb.connect(fs, "rename", comb.hitch(this, function (name, newName) { 124 | var o = renameLogs[renameCount]; 125 | assert.isTrue(name === o.name); 126 | assert.isTrue(newName === o.newName); 127 | renameCount++; 128 | })); 129 | var called = false; 130 | //max sure that the callback is called right away 131 | appender.__checkFile({size: 1023}) 132 | .chain(function () { 133 | called = true; 134 | }) 135 | .chain(function () { 136 | 137 | assert.isTrue(called); 138 | //set the next state 139 | called = false; 140 | appender.__inRollover = true; 141 | return appender.__checkFile({size: 1025}).chain(comb.hitch(this, function () { 142 | called = true; 143 | })); 144 | 145 | }) 146 | .chain(function () { 147 | assert.isTrue(called); 148 | appender.__inRollover = false; 149 | called = false; 150 | //now make sure it just creates a new write stream; 151 | appender.maxBackupIndex = 0; 152 | appender.__checkFile({size: 1025}).then(comb.hitch(this, function () { 153 | called = true; 154 | })).chain(function () { 155 | 156 | assert.isTrue(called); 157 | appender.maxBackupIndex = 3; 158 | called = false; 159 | //now make sure it acutally rolls over 160 | appender.__checkFile({size: 1025}).then(comb.hitch(this, function () { 161 | called = true; 162 | assert.equal(unlinkCount, 1); 163 | assert.equal(renameCount, 3); 164 | assert.equal(logCount, 3); 165 | next(); 166 | })); 167 | 168 | assert.isFalse(called); 169 | logger.info("hello"); 170 | logger.info("hello2"); 171 | logger.info("hello3"); 172 | 173 | }); 174 | }); 175 | }); 176 | 177 | 178 | it.afterAll(resetFs); 179 | 180 | 181 | }).as(module); 182 | 183 | -------------------------------------------------------------------------------- /test/plugins/Broadcaster.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | define = comb.define, 6 | hitch = comb.hitch; 7 | 8 | 9 | it.describe("comb.plugins.Broadcaster", function (it) { 10 | //Super of other classes 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 | 33 | it.should("broadcast a speak event", function (next) { 34 | var m = new Mammal({color: "gold"}); 35 | m.listen("speak", function (str) { 36 | assert.equal(str, "A mammal of type mammal sounds like"); 37 | next(); 38 | }); 39 | m.speak(); 40 | }); 41 | 42 | it.should("should unListen a listener", function (next) { 43 | var m = new Mammal({color: "gold"}); 44 | var han = m.listen("speak", function () { 45 | next("Should not have called"); 46 | }); 47 | m.unListen(han); 48 | m.listen("speak", hitch(this, function (str) { 49 | assert.equal(str, "A mammal of type mammal sounds like"); 50 | next(); 51 | })); 52 | m.speak(); 53 | }); 54 | 55 | 56 | }).as(module); 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /test/plugins/Middleware.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../../index"), 5 | define = comb.define, 6 | hitch = comb.hitch; 7 | 8 | 9 | it.describe("comb.plugins.Middleware",function (it) { 10 | //Super of other classes 11 | function valWrapper(val) { 12 | return function () { 13 | return val; 14 | }; 15 | } 16 | 17 | var Mammal = define(comb.plugins.Middleware, { 18 | instance:{ 19 | 20 | constructor:function (options) { 21 | options = options || {}; 22 | this._super(arguments); 23 | this._type = options.type || "mammal"; 24 | }, 25 | 26 | speak:function () { 27 | return this._hook("pre", "speak") 28 | .chain(comb.hitch(this, "_hook", "post", "speak")) 29 | .chain(valWrapper("speak")).promise(); 30 | }, 31 | 32 | speakAgain:function () { 33 | return this._hook("pre", "speakAgain") 34 | .chain(comb.hitch(this, "_hook", "post", "speakAgain")) 35 | .chain(valWrapper("speakAgain")).promise(); 36 | 37 | }, 38 | 39 | eat:function () { 40 | return this._hook("pre", "eat") 41 | .chain(comb.hitch(this, "_hook", "post", "eat")) 42 | .chain(valWrapper("eat")).promise(); 43 | } 44 | } 45 | }); 46 | 47 | it.should("call pre middleware", function (next) { 48 | Mammal.pre('speak', function (n) { 49 | assert.isTrue(comb.isFunction(n)); 50 | n(); 51 | next(); 52 | }); 53 | var m = new Mammal({color:"gold"}); 54 | m.speak(); 55 | }); 56 | 57 | it.should("call pre middleware on an instance of middleware", function (next) { 58 | var m = new Mammal({color:"gold"}); 59 | m.pre('speakAgain', function (n) { 60 | assert.isTrue(comb.isFunction(n)); 61 | n(); 62 | next(); 63 | }); 64 | m.speakAgain(); 65 | }); 66 | 67 | it.should("call post middleware", function (next) { 68 | Mammal.post('speak', function (n) { 69 | assert.isTrue(comb.isFunction(n)); 70 | n(); 71 | next(); 72 | }); 73 | var m = new Mammal({color:"gold"}); 74 | m.speak(); 75 | }); 76 | 77 | it.should("call post middleware on an instance of middleware", function (next) { 78 | var m = new Mammal({color:"gold"}); 79 | m.post('speakAgain', function (n) { 80 | assert.isTrue(comb.isFunction(n)); 81 | n(); 82 | next(); 83 | }); 84 | m.speakAgain(); 85 | }); 86 | 87 | it.should("callback right away if there is no middleware", function (next) { 88 | var m = new Mammal({color:"gold"}); 89 | m.eat().then(comb.hitch(this, function (str) { 90 | assert.equal(str, "eat"); 91 | next(); 92 | })); 93 | }); 94 | 95 | it.should("errback if the first argument to next is not null/undefined", function (next) { 96 | Mammal.pre('speak', function (n) { 97 | assert.isTrue(comb.isFunction(n)); 98 | n("error"); 99 | }); 100 | var m = new Mammal({color:"gold"}); 101 | m.speak().then(next, function (err) { 102 | assert.equal(err, "error"); 103 | next(); 104 | }); 105 | }); 106 | 107 | }).as(module); 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /test/promiseList.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var it = require('it'), 3 | assert = require('assert'), 4 | comb = require("../index"), 5 | Promise = comb.Promise, 6 | PromiseList = comb.PromiseList; 7 | 8 | it.describe("comb.PromiseList", function (it) { 9 | 10 | 11 | it.should("should callback after all have fired ", function (next) { 12 | 13 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 14 | setTimeout(comb.hitch(promise, "callback", "hello"), 100); 15 | setTimeout(comb.hitch(promise2, "callback", "world"), 150); 16 | setTimeout(comb.hitch(promise3, "callback", "!"), 200); 17 | new PromiseList([promise, promise2, promise3]).then(function (res) { 18 | var expected = ["hello", "world", "!"]; 19 | res.forEach(function (r, i) { 20 | assert.equal(r[1], expected[i]); 21 | }); 22 | next(); 23 | }, next); 24 | }); 25 | 26 | it.should("callback immediately if no promises are provided", function (next) { 27 | new PromiseList().then(function (res) { 28 | assert.deepEqual(res, []); 29 | next(); 30 | }, next); 31 | }); 32 | 33 | it.should("callback immediately if and empty array is provided", function (next) { 34 | new PromiseList([]).then(function (res) { 35 | assert.deepEqual(res, []); 36 | next(); 37 | }, next); 38 | }); 39 | 40 | it.should("callback if provided promises that have already fired", function (next) { 41 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 42 | promise.callback("hello"); 43 | promise2.callback("world"); 44 | promise3.callback("!"); 45 | new PromiseList([promise, promise2, promise3]).then(function (res) { 46 | var expected = ["hello", "world", "!"]; 47 | res.forEach(function (r, i) { 48 | assert.equal(r[1], expected[i]); 49 | }); 50 | next(); 51 | }, next); 52 | }); 53 | 54 | it.should("errback if provided promises that have already fired and one errored back", function (next) { 55 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 56 | promise.callback("hello"); 57 | promise2.callback("world"); 58 | promise3.errback("error"); 59 | var pl = new PromiseList([promise, promise2, promise3]); 60 | process.nextTick(function () { 61 | pl.then(next, function (res) { 62 | res.forEach(function (res) { 63 | assert.equal(res[1], "error"); 64 | }); 65 | next(); 66 | }); 67 | }); 68 | }); 69 | 70 | it.should("throw an error if callback is called after firing", function (next) { 71 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 72 | promise.callback("hello"); 73 | promise2.callback("world"); 74 | promise3.callback("!"); 75 | var pl = new PromiseList([promise, promise2, promise3]); 76 | pl.addCallback(function () { 77 | assert.throws(function () { 78 | pl.callback(); 79 | }); 80 | }); 81 | return pl; 82 | }); 83 | 84 | it.should("throw an error if errback is called after firing", function (next) { 85 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 86 | promise.callback("hello"); 87 | promise2.callback("world"); 88 | promise3.callback("!"); 89 | var pl = new PromiseList([promise, promise2, promise3]); 90 | pl.addCallback(function () { 91 | assert.throws(function () { 92 | pl.errback(); 93 | }); 94 | }); 95 | return pl; 96 | }); 97 | 98 | it.should("handle the ordering of results if resolved out of order", function (next) { 99 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 100 | setTimeout(comb.hitch(promise, "callback", "hello"), 200); 101 | setTimeout(comb.hitch(promise2, "callback", "world"), 150); 102 | setTimeout(comb.hitch(promise3, "callback", "!"), 100); 103 | new PromiseList([promise, promise2, promise3]).then(function (res) { 104 | var expected = ["hello", "world", "!"]; 105 | res.forEach(function (res, i) { 106 | assert.equal(res[1], expected[i]); 107 | }); 108 | next(); 109 | }, next); 110 | }); 111 | 112 | it.should("normalize results", function (next) { 113 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 114 | setTimeout(comb.hitch(promise, "callback", "hello"), 200); 115 | setTimeout(comb.hitch(promise2, "callback", "world"), 150); 116 | setTimeout(comb.hitch(promise3, "callback", "!"), 100); 117 | new PromiseList([promise, promise2, promise3], true).then(function (res) { 118 | assert.deepEqual(res, ["hello", "world", "!"]); 119 | next(); 120 | }, next); 121 | }); 122 | 123 | it.should("accept a promise as a callback", function (next) { 124 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 125 | process.nextTick(comb.hitch(promise, "callback", "hello")); 126 | process.nextTick(comb.hitch(promise2, "callback", "world")); 127 | process.nextTick(comb.hitch(promise3, "callback", "!")); 128 | var p2 = new Promise(); 129 | p2.then(function (res) { 130 | assert.deepEqual(res, ["hello", "world", "!"]); 131 | next(); 132 | }); 133 | new PromiseList([promise, promise2, promise3], true).addCallback(p2).addErrback(next); 134 | }); 135 | 136 | it.should("accept a promise as a errback", function (next) { 137 | var promise = new Promise(), promise2 = new Promise(), promise3 = new Promise(); 138 | process.nextTick(comb.hitch(promise, "callback", "hello")); 139 | process.nextTick(comb.hitch(promise2, "callback", "world")); 140 | process.nextTick(comb.hitch(promise3, "errback", "error")); 141 | var p2 = new Promise(); 142 | p2.then(next, function (res) { 143 | assert.equal(res[2], "error"); 144 | next(); 145 | }); 146 | new PromiseList([promise, promise2, promise3], true).addCallback(next).addErrback(p2); 147 | }); 148 | 149 | }).as(module); 150 | --------------------------------------------------------------------------------