├── .gitignore ├── circle.yml ├── index.js ├── views └── top.jade ├── server.js ├── sample ├── promise │ ├── sample.promise.js │ ├── sample.race.js │ ├── sample.all.js │ └── sample.then.js ├── sync │ ├── sample.min.js │ ├── sample.function.js │ ├── sample.array.js │ ├── sample.find.js │ ├── sample.for.js │ ├── sample.forEach.js │ └── sample.forin.js ├── async │ ├── sample.parallel.js │ ├── sample.map.js │ ├── sample.parallelLimit.js │ ├── sample.waterfall.js │ ├── sample.each.js │ └── sample.series.js └── statistic │ └── async │ └── sample.waterfall.js ├── package.json ├── .jshintrc ├── lib ├── util.js ├── statistic.js └── comparator.js ├── README.md └── public └── js ├── comparator.js ├── underscore-min.js ├── neo_async.min.js ├── lodash.min.js └── async.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 5 4 | 5 | test: 6 | override: 7 | - node sample/async/sample.each.js 8 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./lib/comparator'); 4 | module.exports.statistic = require('./lib/statistic'); 5 | 6 | -------------------------------------------------------------------------------- /views/top.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | script(type='text/javascript', src='/js/comparator.js') 5 | script(type='text/javascript', src='/js/lodash.min.js') 6 | script(type='text/javascript', src='/js/underscore-min.js') 7 | script(type='text/javascript', src='/js/async.js') 8 | script(type='text/javascript', src='/js/neo_async.js') 9 | body 10 | h1 Comparator v#{version} 11 | 12 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var path = require('path'); 5 | 6 | var app = express(); 7 | 8 | app.set('view engine', 'jade'); 9 | app.use(express.static(path.join(__dirname, 'public'), { maxAge: 1 })); 10 | app.get('/', function(req, res) { 11 | res.render('top', { 12 | version: require('./package.json').version 13 | }); 14 | }); 15 | 16 | var port = process.env.PORT || 2000; 17 | app.listen(port); 18 | console.info('server started', { port: port }); 19 | -------------------------------------------------------------------------------- /sample/promise/sample.promise.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var Bluebird = require('bluebird'); 5 | var Aigle = require('aigle'); 6 | 7 | // sampling times 8 | var times = 1000000; 9 | var handler = function(resolve) { 10 | resolve(); 11 | }; 12 | var funcs = { 13 | // 'promise': function() { 14 | // return new Promise(handler); 15 | // }, 16 | 'bluebird': function() { 17 | return new Bluebird(handler); 18 | }, 19 | 'aigle': function() { 20 | return new Aigle(handler); 21 | } 22 | }; 23 | 24 | comparator 25 | .set(funcs) 26 | .times(times) 27 | .async() 28 | .concurrency(2) 29 | .start() 30 | .result(function(err, res) { 31 | console.log(res); 32 | }); 33 | 34 | 35 | -------------------------------------------------------------------------------- /sample/sync/sample.min.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --expose_gc 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | 5 | // roop count 6 | // sampling times 7 | var times = 100000; 8 | var x = 9; 9 | var y = 10; 10 | var nativeMin = Math.min; 11 | var funcs = { 12 | 'Math.min': function() { 13 | Math.min(x, y) ; 14 | }, 15 | 'Math.min2': function() { 16 | Math.min(y, x); 17 | }, 18 | 'xy': function() { 22 | x > y ? y : x; 23 | }, 24 | 'nativeMin': function() { 25 | nativeMin(x, y); 26 | }, 27 | 'nativeMin2': function() { 28 | nativeMin(y, x); 29 | } 30 | }; 31 | 32 | var res = comparator 33 | .set(funcs) 34 | .times(times) 35 | .start() 36 | .result(); 37 | 38 | console.log(res); 39 | 40 | -------------------------------------------------------------------------------- /sample/async/sample.parallel.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var _ = require('lodash'); 5 | var async = require('async'); 6 | var neo_async = require('neo-async'); 7 | 8 | // roop count 9 | var count = 1000; 10 | // sampling times 11 | var times = 1000; 12 | var array = _.shuffle(_.times(count)); 13 | var tasks = _.map(array, function(n) { 14 | return function(next) { 15 | next(null, n); 16 | }; 17 | }); 18 | var funcs = { 19 | 'async': function(callback) { 20 | async.parallel(tasks, callback); 21 | }, 22 | 'neo-async': function(callback) { 23 | neo_async.parallel(tasks, callback); 24 | } 25 | }; 26 | 27 | comparator 28 | .set(funcs) 29 | .times(times) 30 | .async() 31 | .start() 32 | .result(function(err, res) { 33 | console.log(res); 34 | }); 35 | 36 | 37 | -------------------------------------------------------------------------------- /sample/async/sample.map.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var _ = require('lodash'); 5 | var async = require('async'); 6 | var neo_async = require('neo-async'); 7 | 8 | // roop count 9 | var count = 100; 10 | // sampling times 11 | var times = 1000; 12 | var array = _.shuffle(_.times(count)); 13 | var c = 0; 14 | var iterator = function(n, callback) { 15 | callback(null, c++); 16 | }; 17 | var funcs = { 18 | 'async': function(callback) { 19 | c = 0; 20 | async.each(array, iterator, callback); 21 | }, 22 | 'neo-async': function(callback) { 23 | c = 0; 24 | neo_async.each(array, iterator, callback); 25 | } 26 | }; 27 | 28 | comparator 29 | .set(funcs) 30 | .times(times) 31 | .async() 32 | .start() 33 | .result(function(err, res) { 34 | console.log(res); 35 | }); 36 | 37 | 38 | -------------------------------------------------------------------------------- /sample/async/sample.parallelLimit.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var _ = require('lodash'); 5 | var async = require('async'); 6 | var neo_async = require('neo-async'); 7 | 8 | // roop count 9 | var count = 1000; 10 | // sampling times 11 | var times = 1000; 12 | var array = _.shuffle(_.times(count)); 13 | var tasks = _.map(array, function(n) { 14 | return function(next) { 15 | next(null, n); 16 | }; 17 | }); 18 | var funcs = { 19 | 'async': function(callback) { 20 | async.parallelLimit(tasks, 4, callback); 21 | }, 22 | 'neo-async': function(callback) { 23 | neo_async.parallelLimit(tasks, 4, callback); 24 | } 25 | }; 26 | 27 | comparator 28 | .set(funcs) 29 | .times(times) 30 | .async() 31 | .start() 32 | .result(function(err, res) { 33 | console.log(res); 34 | }); 35 | 36 | 37 | -------------------------------------------------------------------------------- /sample/sync/sample.function.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --expose_gc 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var lodash = require('lodash'); 5 | 6 | // roop count 7 | // sampling times 8 | var times = 100000; 9 | var funcs = { 10 | 'var-hoge': function() { 11 | var hoge = function(a, b) { 12 | return a + b; 13 | }; 14 | hoge(1, 2); 15 | }, 16 | 'b-hoge': function() { 17 | hoge(1, 2); 18 | function hoge(a, b) { 19 | return a + b; 20 | } 21 | }, 22 | 'a-hoge': function() { 23 | function hoge(a, b) { 24 | return a + b; 25 | } 26 | hoge(1, 2); 27 | }, 28 | 'i-hoge': function() { 29 | (function hoge(a, b) { 30 | return a + b; 31 | })(1, 2); 32 | } 33 | }; 34 | 35 | var res = comparator 36 | .set(funcs) 37 | .times(times) 38 | .start() 39 | .result(); 40 | 41 | console.log(res); 42 | 43 | -------------------------------------------------------------------------------- /sample/async/sample.waterfall.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var _ = require('lodash'); 5 | var async = require('async'); 6 | var neo_async = require('neo-async'); 7 | 8 | // roop count 9 | var count = 1000; 10 | // sampling times 11 | var times = 1000; 12 | var array = _.shuffle(_.times(count)); 13 | var tasks = _.map(array, function(n, i) { 14 | if (i === 0) { 15 | return function(next) { 16 | next(null, n); 17 | }; 18 | } 19 | return function(total, next) { 20 | next(null, total + n); 21 | }; 22 | }); 23 | var funcs = { 24 | 'async': function(callback) { 25 | async.waterfall(tasks, callback); 26 | }, 27 | 'neo-async': function(callback) { 28 | neo_async.waterfall(tasks, callback); 29 | } 30 | }; 31 | 32 | comparator 33 | .set(funcs) 34 | .async() 35 | .times(times) 36 | .start() 37 | .result(function(err, res) { 38 | console.log(res); 39 | }); 40 | 41 | 42 | -------------------------------------------------------------------------------- /sample/sync/sample.array.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --expose_gc 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | 5 | // roop count 6 | var count = 100; 7 | // sampling times 8 | var times = 1000; 9 | var funcs = { 10 | 'for': function() { 11 | var array = []; 12 | for(var i = 0; i < count; i++) { 13 | array[i] = i; 14 | } 15 | }, 16 | 'init-for': function() { 17 | var array = []; 18 | array = Array(count); 19 | for(var i = 0; i++ < count;) { 20 | array[i] = i; 21 | } 22 | }, 23 | 'while': function() { 24 | var i = -1; 25 | var array = []; 26 | while(++i < count) { 27 | array[i] = i; 28 | } 29 | }, 30 | 'init-while': function() { 31 | var i = -1; 32 | var array = []; 33 | array = Array(count); 34 | while(++i < count) { 35 | array[i] = i; 36 | } 37 | } 38 | }; 39 | 40 | var res = comparator 41 | .set(funcs) 42 | .times(times) 43 | .start() 44 | .result(); 45 | 46 | console.log(res); 47 | 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "func-comparator", 3 | "version": "0.7.3", 4 | "description": "func-comparator is intended functions speed comparison", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/mocha/bin/_mocha ./test" 8 | }, 9 | "lisence": "MIT", 10 | "directories": { 11 | "test": "test/" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git@github.com:suguru03/func-comparator.git" 16 | }, 17 | "homepage": "https://github.com/suguru03/func-comparator", 18 | "dependencies": { 19 | "neo-timer": "^0.2.0" 20 | }, 21 | "devDependencies": { 22 | "async": "^0.9.0", 23 | "bluebird": "^3.4.0", 24 | "espower-loader": "^0.10.0", 25 | "express": "^4.10.4", 26 | "intelli-espower-loader": "^0.5.0", 27 | "jade": "^1.8.1", 28 | "lodash": "^4.17.19", 29 | "lodash-node": "^2.4.1", 30 | "mocha": "^2.0.1", 31 | "neo-async": "^2.0.0-rc.1", 32 | "power-assert": "^0.10.0", 33 | "underscore": "^1.7.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sample/async/sample.each.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var _ = require('lodash'); 5 | var async = require('async'); 6 | var Promise = require('bluebird'); 7 | var neo_async = require('neo-async'); 8 | 9 | // roop count 10 | var count = 100; 11 | // sampling times 12 | var times = 1000; 13 | var array = _.shuffle(_.times(count)); 14 | var c = 0; 15 | var iterator = function(n, callback) { 16 | c++; 17 | callback(); 18 | }; 19 | var promiseIterator = function(n) { 20 | c++; 21 | return n; 22 | }; 23 | var funcs = { 24 | 'async': function(callback) { 25 | c = 0; 26 | async.each(array, iterator, callback); 27 | }, 28 | 'neo-async': function(callback) { 29 | c = 0; 30 | neo_async.each(array, iterator, callback); 31 | }, 32 | 'bluebird': function() { 33 | c = 0; 34 | return Promise.map(array, promiseIterator); 35 | } 36 | }; 37 | 38 | comparator 39 | .set(funcs) 40 | .times(times) 41 | .async() 42 | .concurrency(2) 43 | .start() 44 | .result(function(err, res) { 45 | console.log(res); 46 | }); 47 | 48 | 49 | -------------------------------------------------------------------------------- /sample/statistic/async/sample.waterfall.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var statistic = require('../../../lib/statistic'); 4 | var _ = require('lodash'); 5 | var async = require('async'); 6 | var neo_async = require('neo-async'); 7 | 8 | var times = 100; 9 | var create = function(count) { 10 | 11 | // sampling times 12 | var array = _.shuffle(_.times(count)); 13 | var tasks = _.map(array, function(n, i) { 14 | if (i === 0) { 15 | return function(next) { 16 | next(null, n); 17 | }; 18 | } 19 | return function(total, next) { 20 | next(null, total + n); 21 | }; 22 | }); 23 | var funcs = { 24 | 'async': function(callback) { 25 | async.waterfall(tasks, callback); 26 | }, 27 | 'neo-async': function(callback) { 28 | neo_async.waterfall(tasks, callback); 29 | } 30 | }; 31 | return funcs; 32 | }; 33 | 34 | statistic 35 | .create(create) 36 | .times(times) 37 | .async() 38 | .count({ 39 | lower: 10, 40 | upper: 1000, 41 | interval: 10 42 | }) 43 | .start() 44 | .result(function(err, res) { 45 | console.log(res); 46 | }) 47 | .csv('waterfall_iojs_' + _.now()); 48 | 49 | 50 | -------------------------------------------------------------------------------- /sample/promise/sample.race.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var _ = require('lodash'); 4 | var comparator = require('../../'); 5 | var Bluebird = require('bluebird'); 6 | var Aigle = require('aigle'); 7 | 8 | // sampling times 9 | var count = 100; 10 | var times = 100000; 11 | var tasks = _.times(count, function() { 12 | return new Aigle(function(resolve) { 13 | resolve(); 14 | }); 15 | }); 16 | var bluebirdTasks = _.times(count, function() { 17 | return new Bluebird(function(resolve) { 18 | resolve(); 19 | }); 20 | }); 21 | var aigleTasks = _.times(count, function() { 22 | return new Aigle(function(resolve) { 23 | resolve(); 24 | }); 25 | }); 26 | 27 | var funcs = { 28 | // 'promise': function() { 29 | // return Promise.all(tasks); 30 | // }, 31 | 'bluebird': function() { 32 | return Bluebird.race(bluebirdTasks); 33 | }, 34 | 'aigle': function() { 35 | return Aigle.race(aigleTasks); 36 | } 37 | }; 38 | 39 | comparator 40 | .set(funcs) 41 | .times(times) 42 | .async() 43 | .concurrency(1) 44 | .start() 45 | .result(function(err, res) { 46 | console.log(res); 47 | }); 48 | 49 | 50 | -------------------------------------------------------------------------------- /sample/async/sample.series.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var util = require('../../lib/util'); 5 | var _ = require('lodash'); 6 | var async = require('async'); 7 | var neo_async = require('neo-async'); 8 | 9 | // roop count 10 | var count = 1000; 11 | // sampling times 12 | var times = 1000; 13 | var array = _.shuffle(_.times(count)); 14 | var tasks = _.map(array, function(n) { 15 | return function(next) { 16 | next(null, n); 17 | }; 18 | }); 19 | var funcs = { 20 | 'async': function(callback) { 21 | async.series(tasks, callback); 22 | }, 23 | 'neo-async': function(callback) { 24 | neo_async.series(tasks, callback); 25 | }, 26 | //'iojs': function(callback) { 27 | // util.forEach(tasks, function *(task) { 28 | // yield task; 29 | // }); 30 | // callback(); 31 | //} 32 | }; 33 | if (typeof process != 'object' || !process.execArgv || process.execArgv.indexOf('--harmony') < 0) { 34 | delete funcs.iojs; 35 | } 36 | 37 | comparator 38 | .set(funcs) 39 | .times(times) 40 | .async() 41 | .start() 42 | .result(function(err, res) { 43 | console.log(res); 44 | }); 45 | 46 | 47 | -------------------------------------------------------------------------------- /sample/promise/sample.all.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var _ = require('lodash'); 4 | var comparator = require('../../'); 5 | var Bluebird = require('bluebird'); 6 | var Aigle = require('aigle'); 7 | 8 | // sampling times 9 | var count = 100; 10 | var times = 100000; 11 | var tasks = _.times(count, function() { 12 | return new Aigle(function(resolve) { 13 | resolve(); 14 | }); 15 | }); 16 | var bluebirdTasks = _.times(count, function() { 17 | return new Bluebird(function(resolve) { 18 | process.nextTick(resolve); 19 | }); 20 | }); 21 | var aigleTasks = _.times(count, function() { 22 | return new Aigle(function(resolve) { 23 | process.nextTick(resolve); 24 | }); 25 | }); 26 | 27 | var funcs = { 28 | // 'promise': function() { 29 | // return Promise.race(tasks); 30 | // }, 31 | 'bluebird': function() { 32 | return Bluebird.all(bluebirdTasks); 33 | }, 34 | 'aigle': function() { 35 | return Aigle.all(aigleTasks); 36 | } 37 | }; 38 | 39 | comparator 40 | .set(funcs) 41 | .times(times) 42 | .async() 43 | .concurrency(1) 44 | .start() 45 | .result(function(err, res) { 46 | console.log(res); 47 | }); 48 | 49 | 50 | -------------------------------------------------------------------------------- /sample/sync/sample.find.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --expose_gc 2 | 'use strict'; 3 | var _ = require('lodash'); 4 | var comparator = require('../../'); 5 | 6 | // roop count 7 | var count = 10; 8 | // sampling times 9 | var times = 100000; 10 | var array = _.times(count, function(n) { 11 | return { 12 | id: n, 13 | text: n + '' 14 | }; 15 | }); 16 | var findList = _.shuffle(_.times(count)); 17 | 18 | var funcs = { 19 | 'find1': function() { 20 | _.forEach(findList, function(n) { 21 | _.find(array, 'id', n); 22 | }); 23 | }, 24 | 'find2': function() { 25 | _.forEach(findList, function(n) { 26 | _.find(array, { 27 | id: n 28 | }); 29 | }); 30 | }, 31 | 'find3': function() { 32 | _.forEach(findList, function(n) { 33 | _.find(array, function(data) { 34 | return data.id === n; 35 | }); 36 | }); 37 | }, 38 | 'map': function() { 39 | var map = {}; 40 | _.forEach(array, function(data) { 41 | map[data.id] = data; 42 | }); 43 | _.forEach(findList, function(n) { 44 | map[n]; 45 | }); 46 | } 47 | }; 48 | 49 | var res = comparator 50 | .set(funcs) 51 | .times(times) 52 | .start() 53 | .result(); 54 | 55 | console.log(res); 56 | 57 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "predef": [ 4 | "define" 5 | ], 6 | 7 | "indent": 2, 8 | 9 | "bitwise": false, 10 | "camelcase": false, 11 | "curly": false, 12 | "eqeqeq": false, 13 | "forin": false, 14 | "immed": true, 15 | "latedef": false, 16 | "newcap": false, 17 | "noarg": true, 18 | "noempty": false, 19 | "nonew": false, 20 | "plusplus": false, 21 | "quotmark": false, 22 | "regexp": false, 23 | "undef": true, 24 | "unused": true, 25 | "strict": false, 26 | "trailing": true, 27 | "maxparams": false, 28 | "maxdepth": false, 29 | "maxstatements": false, 30 | "maxcomplexity": false, 31 | "maxlen": false, 32 | 33 | "boss": false, 34 | "debug": false, 35 | "eqnull": false, 36 | "esnext": true, 37 | "evil": true, 38 | "expr": false, 39 | "funcscope": false, 40 | "globalstrict": false, 41 | "lastsemic": false, 42 | "laxbreak": false, 43 | "laxcomma": true, 44 | "loopfunc": false, 45 | "multistr": false, 46 | "onecase": false, 47 | "proto": true, 48 | "regexdash": false, 49 | "scripturl": true, 50 | "smarttabs": false, 51 | "shadow": false, 52 | "sub": false, 53 | "supernew": false, 54 | "validthis": false, 55 | 56 | "browser": true, 57 | "jquery": true, 58 | "node": true, 59 | "worker": true, 60 | 61 | "nomen": false, 62 | "onevar": false, 63 | "white": false 64 | } 65 | -------------------------------------------------------------------------------- /sample/promise/sample.then.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --stack-size=65536 2 | 'use strict'; 3 | var _ = require('lodash'); 4 | var comparator = require('../../'); 5 | var Bluebird = require('bluebird'); 6 | var Aigle = require('aigle'); 7 | 8 | // sampling times 9 | var count = 100; 10 | var times = 30000; 11 | var funcs = { 12 | // 'promise': function() { 13 | // var p = new Promise(function(resolve) { 14 | // resolve(0); 15 | // }); 16 | // _.times(count, function() { 17 | // p = p.then(function(value) { 18 | // return ++value; 19 | // }); 20 | // }); 21 | // return p; 22 | // }, 23 | 'bluebird': function() { 24 | var sync; 25 | var p = new Bluebird(function(resolve) { 26 | resolve(0); 27 | }); 28 | _.times(count, function() { 29 | p = p.then(function(value) { 30 | return ++value; 31 | }); 32 | }); 33 | return p; 34 | }, 35 | 'aigle': function() { 36 | var p = new Aigle(function(resolve) { 37 | resolve(0); 38 | }); 39 | _.times(count, function() { 40 | p = p.then(function(value) { 41 | return ++value; 42 | }); 43 | }); 44 | return p; 45 | } 46 | }; 47 | 48 | comparator 49 | .set(funcs) 50 | .times(times) 51 | .async() 52 | .concurrency(1) 53 | .start() 54 | .result(function(err, res) { 55 | console.log(res); 56 | }); 57 | 58 | 59 | -------------------------------------------------------------------------------- /sample/sync/sample.for.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --expose_gc 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var lodash = require('lodash'); 5 | var lodash_node = require('lodash-node'); 6 | var underscore = require('underscore'); 7 | 8 | // roop count 9 | var count = 100; 10 | // sampling times 11 | var times = 100000; 12 | var funcs = { 13 | 'for': function() { 14 | for(var i = 0; i < count; i++) { 15 | Math.floor(i); 16 | } 17 | }, 18 | 'for2': function() { 19 | for(var i = 0; i++ < count;) { 20 | Math.floor(i); 21 | } 22 | }, 23 | 'while': function() { 24 | var i = -1; 25 | while(++i < count) { 26 | Math.floor(i); 27 | } 28 | }, 29 | 'lodash': function() { 30 | lodash.times(count, function(i) { 31 | Math.floor(i); 32 | }); 33 | }, 34 | 'lodash-one': function() { 35 | lodash.times(count, Math.floor); 36 | }, 37 | 'lodash-node': function() { 38 | lodash_node.times(count, function(i) { 39 | Math.floor(i); 40 | }); 41 | }, 42 | 'lodash-node-one': function() { 43 | lodash_node.times(count, Math.floor); 44 | }, 45 | 'underscore': function() { 46 | underscore.times(count, function(i) { 47 | Math.floor(i); 48 | }); 49 | }, 50 | 'underscore-one': function() { 51 | underscore.times(count, Math.floor); 52 | } 53 | }; 54 | 55 | var res = comparator 56 | .set(funcs) 57 | .times(times) 58 | .start() 59 | .result(); 60 | 61 | console.log(res); 62 | 63 | -------------------------------------------------------------------------------- /sample/sync/sample.forEach.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --expose_gc 2 | 3 | 'use strict'; 4 | var comparator = require('../../'); 5 | var lodash = require('lodash'); 6 | var lodash_node = require('lodash-node'); 7 | var underscore = require('underscore'); 8 | var _ = lodash; 9 | 10 | // roop count 11 | var count = 100; 12 | // sampling times 13 | var times = 10000; 14 | var array = _.shuffle(_.times(count)); 15 | var funcs = { 16 | 'forEach': function() { 17 | array.forEach(function(n) { 18 | Math.floor(n); 19 | }); 20 | }, 21 | 'while': function() { 22 | var i = -1; 23 | var l = array.length; 24 | while(++i < l) { 25 | Math.floor(array[i]); 26 | } 27 | }, 28 | 'for': function() { 29 | var l = array.length; 30 | for(var i = 0; i < l; i++) { 31 | Math.floor(array[i]); 32 | } 33 | }, 34 | 'lodash': function() { 35 | lodash.forEach(array, function(n) { 36 | Math.floor(n); 37 | }); 38 | }, 39 | 'lodash-one': function() { 40 | lodash.forEach(array, Math.floor); 41 | }, 42 | 'lodash-node': function() { 43 | lodash_node.forEach(array, function(n) { 44 | Math.floor(n); 45 | }); 46 | }, 47 | 'lodash-node-one': function() { 48 | lodash_node.forEach(array, Math.floor); 49 | }, 50 | 'underscore': function() { 51 | underscore.forEach(array, function(n) { 52 | Math.floor(n); 53 | }); 54 | }, 55 | 'underscore-one': function() { 56 | underscore.forEach(array, Math.floor); 57 | } 58 | }; 59 | 60 | var res = comparator 61 | .set(funcs) 62 | .times(times) 63 | .start() 64 | .result(); 65 | 66 | console.log(res); 67 | 68 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.noop = noop; 4 | exports.reverse = reverse; 5 | exports.forEach = forEach; 6 | exports.times = times; 7 | exports.shuffle = shuffle; 8 | exports.arrayMin = arrayMin; 9 | exports.arrayMax = arrayMax; 10 | 11 | function noop() {} 12 | 13 | function reverse(func) { 14 | 15 | return function(value, key) { 16 | func(key, value); 17 | }; 18 | } 19 | 20 | function forEach(object, iterator) { 21 | 22 | var i = -1; 23 | var l = 0; 24 | if (Array.isArray(object)) { 25 | l = object.length; 26 | while(++i < l) { 27 | if (iterator(object[i], i, object) === false) { 28 | break; 29 | } 30 | } 31 | } else if (typeof object == 'object') { 32 | var keys = Object.keys(object); 33 | l = keys.length; 34 | while(++i < l) { 35 | var key = keys[i]; 36 | if (iterator(object[key], key, object) === false) { 37 | break; 38 | } 39 | } 40 | } 41 | return object; 42 | } 43 | 44 | function times(n, iterator) { 45 | 46 | var i = -1; 47 | while(++i < n) { 48 | iterator(i); 49 | } 50 | } 51 | 52 | function shuffle(array) { 53 | 54 | var index = -1; 55 | var result = Array(array.length); 56 | forEach(array, function(value) { 57 | var rand = Math.floor(Math.random() * (++index + 1)); 58 | result[index] = result[rand]; 59 | result[rand] = value; 60 | }); 61 | return result; 62 | } 63 | 64 | function arrayMin(array) { 65 | var min = Infinity; 66 | forEach(array, function(num) { 67 | if (num < min) { 68 | min = num; 69 | } 70 | }); 71 | return min; 72 | } 73 | 74 | function arrayMax(array) { 75 | var max = 0; 76 | forEach(array, function(num) { 77 | if (max < num) { 78 | max = num; 79 | } 80 | }); 81 | return max; 82 | } 83 | 84 | -------------------------------------------------------------------------------- /sample/sync/sample.forin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node --expose_gc 2 | 'use strict'; 3 | var comparator = require('../../'); 4 | var lodash = require('lodash'); 5 | var lodash_node = require('lodash-node'); 6 | var underscore = require('underscore'); 7 | var _ = lodash; 8 | 9 | // roop count 10 | var count = 100; 11 | // sampling times 12 | var times = 10000; 13 | var object = _.chain(_.times(count)) 14 | .sample(count) 15 | .reduce(function(memo, value, index) { 16 | memo['_' + index] = value; 17 | return memo; 18 | }, {}) 19 | .value(); 20 | 21 | var funcs = { 22 | 'for-in': function() { 23 | for (var key in object) { 24 | Math.floor(object[key]); 25 | } 26 | }, 27 | 'while': function() { 28 | var i = -1; 29 | var keys = Object.keys(object); 30 | var l = keys.length; 31 | while(++i < l) { 32 | Math.floor(object[keys[i]]); 33 | } 34 | }, 35 | 'for': function() { 36 | var keys = Object.keys(object); 37 | var l = keys.length; 38 | for(var i = 0; i < l; i++) { 39 | Math.floor(object[keys[i]]); 40 | } 41 | }, 42 | 'lodash': function() { 43 | lodash.forEach(object, function(n) { 44 | Math.floor(n); 45 | }); 46 | }, 47 | 'lodash-one': function() { 48 | lodash.forEach(object, Math.floor); 49 | }, 50 | 'lodash-node': function() { 51 | lodash_node.forEach(object, function(n) { 52 | Math.floor(n); 53 | }); 54 | }, 55 | 'lodash-node-one': function() { 56 | lodash_node.forEach(object, Math.floor); 57 | }, 58 | 'underscore': function() { 59 | underscore.forEach(object, function(n) { 60 | Math.floor(n); 61 | }); 62 | }, 63 | 'underscore-one': function() { 64 | underscore.forEach(object, Math.floor); 65 | } 66 | }; 67 | 68 | var res = comparator 69 | .set(funcs) 70 | .times(times) 71 | .start() 72 | .result(); 73 | 74 | console.log(res); 75 | 76 | -------------------------------------------------------------------------------- /lib/statistic.js: -------------------------------------------------------------------------------- 1 | var util = require('./util'); 2 | var comparator = require('./comparator'); 3 | for(var key in comparator) { 4 | if (/^start$|^result$/.test(key)) { 5 | Statistic.prototype['_' + key] = comparator[key]; 6 | } else { 7 | Statistic.prototype[key] = comparator[key]; 8 | } 9 | } 10 | 11 | function Statistic() { 12 | } 13 | 14 | Statistic.prototype.create = function(createFuncs) { 15 | 16 | this._createFuncs = createFuncs; 17 | return this; 18 | }; 19 | 20 | /** 21 | * @param {Object} options 22 | * @param {Number} options.times - default 10 23 | * @param {Object} options.count 24 | * @param {Number} options.count.lower - default 1 25 | * @param {Number} options.count.upper - default 10 26 | * @param {Number} options.count.interval - default 1 27 | */ 28 | Statistic.prototype.option = function(options) { 29 | 30 | var self = this; 31 | util.forEach(options, function(value, key) { 32 | self._options[key] = value; 33 | }); 34 | return this; 35 | }; 36 | 37 | /** 38 | * @param {Boolean} bool 39 | */ 40 | Statistic.prototype.async = function(bool) { 41 | 42 | this._options.async = bool === false ? false : true; 43 | return this; 44 | }; 45 | 46 | /** 47 | * @param {Number} times 48 | */ 49 | Statistic.prototype.times = function(times) { 50 | 51 | if (times) { 52 | this._options.times = times; 53 | } 54 | return this; 55 | }; 56 | 57 | /** 58 | * @param {Object} count 59 | */ 60 | Statistic.prototype.count = function(count) { 61 | 62 | if (count) { 63 | this._options.count = { 64 | lower: count.lower || 0, 65 | upper: count.upper || 1, 66 | interval: count.interval || 1 67 | }; 68 | } 69 | return this; 70 | }; 71 | 72 | 73 | // TODO async only 74 | Statistic.prototype.start = function() { 75 | 76 | var self = this; 77 | self._options.async = true; 78 | var createFuncs = self._createFuncs; 79 | if (typeof createFuncs != 'function') { 80 | throw new Error('create functions is required'); 81 | } 82 | 83 | var count = self._options.count || {}; 84 | var lower = count.lower || 1; 85 | var upper = count.upper || 10; 86 | var interval = count.interval || 1; 87 | var results = []; 88 | 89 | (function execute(count) { 90 | self._started = true; 91 | self._result(function(err, res) { 92 | if (err) { 93 | return self.emit('statistic_result', err); 94 | } 95 | res.count = count; 96 | results.push(res); 97 | count += interval; 98 | if (count > upper) { 99 | self._results = results; 100 | self.emit('statistic_result'); 101 | } else { 102 | execute(count); 103 | } 104 | }); 105 | self.set(createFuncs(count)); 106 | self._start(); 107 | })(lower); 108 | 109 | return this; 110 | }; 111 | 112 | Statistic.prototype.result = function result(callback) { 113 | 114 | callback = callback || function() {}; 115 | if (this._options.async && this._started) { 116 | this.once('statistic_result', result.bind(this, callback)); 117 | return this; 118 | } 119 | var results = { 120 | count: [] 121 | }; 122 | util.forEach(this._results, function(result) { 123 | util.forEach(result, function(data, key) { 124 | results[key] = results[key] || []; 125 | if (key === 'count') { 126 | results[key].push(data); 127 | } else { 128 | results[key].push(data.average); 129 | } 130 | }); 131 | }); 132 | 133 | callback(null, results); 134 | 135 | return this; 136 | }; 137 | 138 | var fs = require('fs'); 139 | Statistic.prototype.csv = function(name, callback) { 140 | 141 | callback = callback || function() {}; 142 | this.result(function(err, res) { 143 | if (err) { 144 | return callback(err); 145 | } 146 | var LF = String.fromCharCode(10); 147 | var keys = Object.keys(res); 148 | var w = keys.length; 149 | var str = keys.toString() + LF; 150 | var h = res[keys[0]].length; 151 | util.times(h, function(n) { 152 | util.forEach(keys, function(key, i) { 153 | str += res[key][n]; 154 | if (i !== w - 1) { 155 | str += ','; 156 | } 157 | }); 158 | str += LF; 159 | }); 160 | fs.writeFileSync(name + '.csv', str, { encoding: 'utf8' }); 161 | callback(); 162 | }); 163 | return this; 164 | }; 165 | 166 | var objectTypes = { 167 | 'function': true, 168 | 'object': true 169 | }; 170 | 171 | (function() { 172 | var statistic = new Statistic(); 173 | if (objectTypes[typeof module] && module && module.exports) { 174 | module.exports = statistic; 175 | } else { 176 | this.statistic = statistic; 177 | } 178 | }).call(this); 179 | 180 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Comparator 2 | Comparator is intended functions speed comparison. 3 | 4 | Run functions on the random, it is possible to easily compare the average time, variance, standard deviation and versus. 5 | 6 | ## installation 7 | ```bash 8 | npm install func-comparator 9 | ``` 10 | ## async samples 11 | 12 | ### async.waterfall 13 | 14 | ```bash 15 | $ node sample/async/sample.waterfall.js 16 | # using gc 17 | $ node --expose_gc sample/async/sample.waterfall.js 18 | ``` 19 | 20 | ```js 21 | var comparator = require('func-comparator'); 22 | var _ = require('lodash'); 23 | var async = require('async'); 24 | var neo_async = require('neo-async'); 25 | 26 | // roop count 27 | var count = 100; 28 | // sampling times 29 | var times = 1000; 30 | var array = _.shuffle(_.times(count)); 31 | var tasks = _.map(array, function(n, i) { 32 | if (i === 0) { 33 | return function(next) { 34 | next(null, n); 35 | }; 36 | } 37 | return function(total, next) { 38 | next(null, total + n); 39 | }; 40 | }); 41 | 42 | var funcs = { 43 | 'async': function(callback) { 44 | async.waterfall(tasks, callback); 45 | }, 46 | 'neo-async': function(callback) { 47 | neo_async.waterfall(tasks, callback); 48 | } 49 | }; 50 | 51 | comparator 52 | .set(funcs) 53 | .times(times) 54 | .async() 55 | .start() 56 | .result(function(err, res) { 57 | console.log(res); 58 | }); 59 | /* 60 | { async: 61 | { min: 478.96, 62 | max: 8620.69, 63 | average: 733.06, 64 | variance: 318972.64, 65 | standard_deviation: 564.77, 66 | vs: { 'neo-async': 10.25 } }, 67 | 'neo-async': 68 | { min: 21.63, 69 | max: 8403.57, 70 | average: 75.19, 71 | variance: 204911.01, 72 | standard_deviation: 452.67, 73 | vs: { async: 974.94 } } } 74 | */ 75 | // using gc 76 | /* 77 | { async: 78 | { min: 726.73, 79 | max: 3904.74, 80 | average: 1044.36, 81 | variance: 58954.15, 82 | standard_deviation: 242.8, 83 | vs: { 'neo-async': 17.03 } }, 84 | 'neo-async': 85 | { min: 44.85, 86 | max: 680.1, 87 | average: 177.95, 88 | variance: 18799.21, 89 | standard_deviation: 137.11, 90 | vs: { async: 586.88 } } } 91 | */ 92 | ``` 93 | 94 | 95 | ## sync samples 96 | ### Node.js 97 | 98 | #### for 99 | 100 | ```bash 101 | $ node samle/sync/sample.for.js 102 | ``` 103 | 104 | ```js 105 | // roop count 106 | var count = 1000; 107 | // sampling times 108 | var times = 10000; 109 | var funcs = { 110 | 'for': function() { 111 | for(var i = 0; i < count; i++) { 112 | Math.floor(i); 113 | } 114 | }, 115 | 'for2': function() { 116 | for(var i = 0; i++ < count;) { 117 | Math.floor(i); 118 | } 119 | }, 120 | 'while': function() { 121 | var i = -1; 122 | while(++i < count) { 123 | Math.floor(i); 124 | } 125 | } 126 | }; 127 | 128 | // get result 129 | var result = comparator 130 | .set(funcs) 131 | .times(times) 132 | .start() 133 | .result(); 134 | 135 | console.log(result); 136 | /* 137 | { for: 138 | { min: 1.11, 139 | max: 78.15, 140 | average: 1.23, 141 | variance: 1.11, 142 | standard_deviation: 1.05, 143 | vs: { for2: 125.2, while: 128.45 } }, 144 | for2: 145 | { min: 1.4, 146 | max: 113.01, 147 | average: 1.54, 148 | variance: 2.93, 149 | standard_deviation: 1.71, 150 | vs: { for: 79.87, while: 102.59 } }, 151 | while: 152 | { min: 1.39, 153 | max: 439.58, 154 | average: 1.58, 155 | variance: 25.15, 156 | standard_deviation: 5.01, 157 | vs: { for: 77.84, for2: 97.46 } } } 158 | */ 159 | ``` 160 | 161 | #### forEach 162 | 163 | ```bash 164 | $ node sample/sync/sample.forEach.js 165 | ``` 166 | 167 | ```js 168 | /* 169 | { forEach: 170 | { min: 33.27, 171 | max: 990.1, 172 | average: 37.2, 173 | variance: 202.43, 174 | standard_deviation: 14.22, 175 | vs: { while: 6.61, for: 5.99 } }, 176 | while: 177 | { min: 2.24, 178 | max: 134.94, 179 | average: 2.46, 180 | variance: 2.62, 181 | standard_deviation: 1.61, 182 | vs: { forEach: 1512.19, for: 90.65 } }, 183 | for: 184 | { min: 1.96, 185 | max: 491.39, 186 | average: 2.23, 187 | variance: 30.4, 188 | standard_deviation: 5.51, 189 | vs: { forEach: 1668.16, while: 110.31 } } } 190 | */ 191 | ``` 192 | 193 | ### Chrome 194 | 195 | ```bash 196 | $ node server.js 197 | server started { port: 2000 } 198 | ``` 199 | 200 | * Access to http://localhost:2000 201 | * Copy sample script to chrome console 202 | 203 | ```js 204 | /* global comparator, _: lodash, __: underscore */ 205 | // roop count 206 | var count = 100; 207 | // sampling times 208 | var times = 1000; 209 | var funcs = { 210 | 'for': function() { 211 | for(var i = 0; i < count; i++) { 212 | Math.floor(i); 213 | } 214 | }, 215 | 'for2': function() { 216 | for(var i = 0; i++ < count;) { 217 | Math.floor(i); 218 | } 219 | }, 220 | 'while': function() { 221 | var i = -1; 222 | while(++i < count) { 223 | Math.floor(i); 224 | } 225 | } 226 | }; 227 | 228 | var res = comparator 229 | .set(funcs) 230 | .times(times) 231 | .start() 232 | .result(); 233 | 234 | console.log(res); 235 | /* 236 | { 237 | "for":{ 238 | "min":23.999993572942913, 239 | "max":188.99999849963933, 240 | "average":28.66, 241 | "variance":116.42, 242 | "standard_deviation":10.789810007595129, 243 | "vs":{ 244 | "for2":100.27, 245 | "while":99.02 246 | } 247 | }, 248 | "for2":{ 249 | "min":23.999993572942913, 250 | "max":197.00000120792538, 251 | "average":28.74, 252 | "variance":97.1, 253 | "standard_deviation":9.85393322486001, 254 | "vs":{ 255 | "for":99.72, 256 | "while":98.74 257 | } 258 | }, 259 | "while":{ 260 | "min":23.00000051036477, 261 | "max":151.99999324977398, 262 | "average":28.38, 263 | "variance":83.23, 264 | "standard_deviation":9.123047736365299, 265 | "vs":{ 266 | "for":100.98, 267 | "for2":101.26 268 | } 269 | } 270 | } 271 | */ 272 | ``` 273 | 274 | ## statistic sample 275 | 276 | Specifications are as follows. 277 | 278 | * Execute tasks from lower to upper in the specified interval 279 | * Random execution order 280 | * Execute gc every time 281 | * Measure the average speed[μs] of n times 282 | 283 | ### async.waterfall vs neo_async.waterfall 284 | 285 | * async v0.9.0 286 | * neo-async v0.4.9 287 | 288 | __demo.js__ 289 | 290 | ```js 291 | var statistic = require('func-comparator').statistic; 292 | var _ = require('lodash'); 293 | var async = require('async'); 294 | var neo_async = require('neo-async'); 295 | 296 | var n = 100; // the number of trial times 297 | var create = function(count) { 298 | // count is the number of tasks 299 | var array = _.shuffle(_.times(count)); 300 | var tasks = _.map(array, function(n, i) { 301 | if (i === 0) { 302 | return function(next) { 303 | next(null, n); 304 | }; 305 | } 306 | return function(total, next) { 307 | next(null, total + n); 308 | }; 309 | }); 310 | var funcs = { 311 | 'async': function(callback) { 312 | async.waterfall(tasks, callback); 313 | }, 314 | 'neo-async': function(callback) { 315 | neo_async.waterfall(tasks, callback); 316 | } 317 | }; 318 | return funcs; 319 | }; 320 | 321 | statistic 322 | .create(create) 323 | .times(n) 324 | .count({ 325 | lower: 10, 326 | upper: 1000, 327 | interval: 10 328 | }) 329 | .async() 330 | .start() 331 | .result(console.log) 332 | .csv('waterfall_' + _.now()); 333 | ``` 334 | 335 | __execute__ 336 | 337 | * lower: 10 338 | * upper: 1000 339 | * interval: 10 340 | * sampling number: 100 341 | 342 | Test environment are as follows. 343 | 344 | * node v0.10.35 345 | * iojs v1.0.2 346 | 347 | ```bash 348 | $ node --expose_gc demo2.js 349 | $ iojs --expose_gc demo2.js 350 | ``` 351 | 352 | __result__ 353 | 354 | Test result are in the following figure. 355 | * x-axis: number of tasks 356 | * y-axis: average times[μs] 357 | 358 | ![node](https://raw.githubusercontent.com/wiki/suguru03/neo-async/images/func_comparator_node_waterfall.png) 359 | 360 | figure 1: speed comparison of node 361 | 362 | ![iojs](https://raw.githubusercontent.com/wiki/suguru03/neo-async/images/func_comparator_iojs_waterfall.png) 363 | 364 | figure 2: speed comparison of iojs 365 | -------------------------------------------------------------------------------- /lib/comparator.js: -------------------------------------------------------------------------------- 1 | var Timer = require('neo-timer').Timer; 2 | 3 | var util = require('./util'); 4 | var nextTick = /^0.10/.test(process.versions.node) ? setImmediate : process.nextTick; 5 | 6 | function Comparator() { 7 | this._funcs = {}; 8 | this._events = {}; 9 | this._options = { 10 | result: false, 11 | adjustment: 10e5, 12 | min: true, 13 | max: true, 14 | average: true, 15 | variance: true, 16 | standard_deviation: true, 17 | versus: true, 18 | vs: undefined, 19 | times: 10, 20 | skip: 0.5, 21 | concurrency: 1, 22 | async: false 23 | }; 24 | } 25 | 26 | /** 27 | * @param {string|Object} name 28 | * @param {Function} func 29 | */ 30 | Comparator.prototype.set = function set(name, func) { 31 | if (typeof name == 'object') { 32 | util.forEach(name, util.reverse(set.bind(this))); 33 | } else { 34 | this._funcs[name] = func; 35 | } 36 | return this; 37 | }; 38 | 39 | /** 40 | * @param {string|string[]} name 41 | */ 42 | Comparator.prototype.get = function(name) { 43 | var self = this; 44 | if (Array.isArray(name)) { 45 | return name.reduce(name, function(memo, name) { 46 | memo[name] = self._funcs[name]; 47 | return memo; 48 | }, {}); 49 | } 50 | return this._funcs[name]; 51 | }; 52 | 53 | /** 54 | * @param {Object} options 55 | * @param {number} options.times - default 10 56 | * @param {number} option.skip - skip rate is to wait for the optimization | default 0.5 57 | * @param {number} options.concurrency - default 1 (async only) 58 | * @param {boolean} options.adjustment - default [μs] (10e-6) 59 | * @param {boolean} options.result - display result array [μs] | default false 60 | * @param {boolean} options.min - display min [μs] | default true 61 | * @param {boolean} options.max - display max [μs] | default true 62 | * @param {boolean} options.average - display average [μs] | default true 63 | * @param {boolean} options.variance - display variance | default true 64 | * @param {boolean} options.standard_deviation - display standard deviation [μs] | default true 65 | * @param {boolean} options.versus - vs other functions [%] | default true 66 | * @param {boolean} options.vs - alias 67 | * @param {boolean} options.async - use asynchronous | default false 68 | * @param {boolean} options.error - default true 69 | */ 70 | Comparator.prototype.option = function(options) { 71 | var self = this; 72 | util.forEach(options, function(value, key) { 73 | self._options[key] = value; 74 | }); 75 | return this; 76 | }; 77 | 78 | /** 79 | * use asynchronous 80 | * @param {boolean} bool - option.async 81 | */ 82 | Comparator.prototype.async = function(bool) { 83 | this._options.async = bool === false ? false : true; 84 | return this; 85 | }; 86 | 87 | /** 88 | * @param {number} concurrency - option.concurrency 89 | */ 90 | Comparator.prototype.concurrency = function(concurrency) { 91 | this._options.concurrency = concurrency; 92 | return this; 93 | }; 94 | 95 | 96 | /** 97 | * @param {number} times - option.times 98 | */ 99 | Comparator.prototype.times = function(times) { 100 | if (times) { 101 | this._options.times = times; 102 | } 103 | return this; 104 | }; 105 | 106 | /** 107 | * @param {number} skip - option.skip 108 | */ 109 | Comparator.prototype.skip = function(skip) { 110 | if (typeof skip === 'number') { 111 | this._options.skip = skip; 112 | } 113 | return this; 114 | }; 115 | 116 | /** 117 | * @param {Function|boolean} func 118 | */ 119 | Comparator.prototype.error = function(func) { 120 | if (typeof func !== 'undefined') { 121 | this._options.error = typeof func === 'function' ? func : util.noop; 122 | } 123 | return this; 124 | }; 125 | 126 | Comparator.prototype.start = function() { 127 | var self = this; 128 | var funcs = self._funcs; 129 | var keys = Object.keys(funcs); 130 | var size = keys.length; 131 | if (!size) { 132 | throw new Error('function does not set yet'); 133 | } 134 | 135 | var times = self._options.times; 136 | var error = self._options.error; 137 | var skip = Math.floor(times * self._options.skip); 138 | var results = {}; 139 | util.forEach(keys, function(key) { 140 | results[key] = Array(times - skip); 141 | }); 142 | 143 | var timer = getTimer(); 144 | if (!timer) { 145 | throw new Error('timer can not use'); 146 | } 147 | var gc = objectTypes[typeof global] && global && global.gc; 148 | if (self._options.async) { 149 | self._started = true; 150 | whilist(); 151 | return self; 152 | } 153 | util.times(times, function(i) { 154 | var sample = util.shuffle(keys); 155 | util.forEach(sample, function(key) { 156 | var func = funcs[key]; 157 | timer.init().start(); 158 | func(); 159 | var diff = timer.diff(); 160 | results[key][i] = diff; 161 | }); 162 | if (gc) { 163 | gc(); 164 | } 165 | }); 166 | self._results = results; 167 | return self; 168 | 169 | function whilist() { 170 | var started = 0; 171 | var called = 0; 172 | var end = false; 173 | var concurrency = self._options.concurrency; 174 | util.times(Math.min(concurrency, times), function() { 175 | nextTick(iterate); 176 | }); 177 | 178 | function done(err) { 179 | self._started = false; 180 | self._err = err; 181 | self._results = results; 182 | self.emit('result'); 183 | } 184 | 185 | function iterate() { 186 | if (!end && started < times) { 187 | iterator(started++, iteratee); 188 | } 189 | } 190 | 191 | function iteratee(err) { 192 | if (err) { 193 | return done(err); 194 | } 195 | if (++called >= times) { 196 | return done(); 197 | } 198 | nextTick(iterate); 199 | } 200 | 201 | function iterator(count, callback) { 202 | var index = 0; 203 | var called = 0; 204 | var sample = util.shuffle(keys); 205 | _iterate(); 206 | 207 | function _iterate() { 208 | var key = sample[index++]; 209 | timer.init().start(); 210 | var p = funcs[key](_done); 211 | if (p && p.toString() === '[object Promise]') { 212 | return p.then(function() { 213 | _done(); 214 | }, function(err) { 215 | _done(err); 216 | }); 217 | } 218 | 219 | function _done(err) { 220 | // s -> μs 221 | var diff = timer.diff(); 222 | if (count >= skip) { 223 | results[key][count - skip] = diff; 224 | } 225 | if (err) { 226 | if (!error) { 227 | return callback(err); 228 | } 229 | error(); 230 | } 231 | if (gc) { 232 | global.gc(); 233 | } 234 | if (++called === size) { 235 | callback(); 236 | } else { 237 | nextTick(_iterate); 238 | } 239 | } 240 | } 241 | } 242 | 243 | } 244 | }; 245 | 246 | Comparator.prototype.on = function on(key, callback) { 247 | if (typeof key == 'object') { 248 | util.forEach(key, util.reverse(on)); 249 | return this; 250 | } 251 | this._events[key] = this._events[key] || []; 252 | this._events[key].push(callback); 253 | return this; 254 | }; 255 | 256 | Comparator.prototype.once = function once(key, callback) { 257 | if (typeof key == 'object') { 258 | util.forEach(key, util.reverse(once)); 259 | return this; 260 | } 261 | callback._once = true; 262 | this._events[key] = this._events[key] || []; 263 | this._events[key].push(callback); 264 | return this; 265 | }; 266 | 267 | Comparator.prototype.emit = function emit(key, err, res) { 268 | var events = this._events[key] || []; 269 | if (!events.length) { 270 | return this; 271 | } 272 | var deleteFlags = []; 273 | util.forEach(events, function(func, index) { 274 | func(err, res); 275 | if (func._once) { 276 | deleteFlags.unshift(index); 277 | } 278 | }); 279 | if (deleteFlags.length) { 280 | util.forEach(deleteFlags, function(index) { 281 | events.splice(index, 1); 282 | }); 283 | } 284 | return this; 285 | }; 286 | 287 | Comparator.prototype.result = function result(callback) { 288 | var results = {}; 289 | var opts = this._options; 290 | var async = opts.async; 291 | if (async && this._started) { 292 | this.once('result', result.bind(this, callback)); 293 | return 'calculating...'; 294 | } 295 | 296 | var adjustment = opts.adjustment; 297 | var res = opts.result === true; 298 | var max = opts.max !== false; 299 | var min = opts.min !== false; 300 | var ave = opts.average !== false; 301 | var varia = opts.variance !== false; 302 | var dev = opts.standard_deviation !== false; 303 | var vs = !(opts.vs === false || opts.versus === false); 304 | util.forEach(this._results, function(array, key) { 305 | var l = array.length; 306 | if (!l) { 307 | return; 308 | } 309 | if (adjustment) { 310 | util.forEach(array, function(value, index) { 311 | array[index] = value * adjustment; 312 | }); 313 | } 314 | var data = {}; 315 | if (res) { 316 | data.result = array; 317 | } 318 | if (ave || varia || dev || vs) { 319 | var sum = 0; 320 | util.forEach(array, function(n) { 321 | sum += n; 322 | }); 323 | var average = sum / array.length; 324 | if (min) { 325 | data.min = util.arrayMin(array); 326 | } 327 | if (max) { 328 | data.max = util.arrayMax(array); 329 | } 330 | if (ave || vs) { 331 | data.average = average; 332 | } 333 | if (varia || dev) { 334 | var variance = (function() { 335 | var variance = 0; 336 | util.forEach(array, function(value) { 337 | variance += Math.pow(value - average, 2); 338 | }); 339 | return variance / l; 340 | })(); 341 | variance = variance; 342 | if (varia) { 343 | data.variance = variance; 344 | } 345 | if (dev) { 346 | data.standard_deviation = Math.sqrt(variance, 2); 347 | } 348 | } 349 | } 350 | 351 | results[key] = data; 352 | }); 353 | if (vs) { 354 | util.forEach(results, function(data, key) { 355 | data.vs = {}; 356 | util.forEach(results, function(_data, _key) { 357 | if (key === _key) { 358 | return; 359 | } 360 | data.vs[_key] = resolveDecimal(100 * _data.average / data.average); 361 | }); 362 | if (!ave) { 363 | delete data.average; 364 | } 365 | }); 366 | } 367 | 368 | if (callback) { 369 | callback(this._err, results); 370 | } 371 | return results; 372 | }; 373 | 374 | function getTimer() { 375 | return new Timer(); 376 | } 377 | Comparator.prototype.getTimer = getTimer; 378 | 379 | function resolveDecimal(num) { 380 | return Math.floor(100 * num) / 100; 381 | } 382 | 383 | var objectTypes = { 384 | 'function': true, 385 | 'object': true 386 | }; 387 | 388 | (function() { 389 | 390 | 'use strict'; 391 | 392 | var comparator = new Comparator(); 393 | if (objectTypes[typeof module] && module && module.exports) { 394 | module.exports = comparator; 395 | module.exports.Comparator = Comparator; 396 | } else { 397 | this.comparator = comparator; 398 | } 399 | }).call(this); 400 | 401 | -------------------------------------------------------------------------------- /public/js/comparator.js: -------------------------------------------------------------------------------- 1 | /* global performance */ 2 | 3 | var util = { 4 | reverse: reverse, 5 | forEach: forEach, 6 | times: times, 7 | shuffle: shuffle 8 | }; 9 | 10 | function reverse(func) { 11 | 12 | return function(value, key) { 13 | func(key, value); 14 | }; 15 | } 16 | 17 | function forEach(object, iterator) { 18 | 19 | var i = -1; 20 | var l = 0; 21 | if (Array.isArray(object)) { 22 | l = object.length; 23 | while(++i < l) { 24 | if (iterator(object[i], i, object) === false) { 25 | break; 26 | } 27 | } 28 | } else if (typeof object == 'object') { 29 | var keys = Object.keys(object); 30 | l = keys.length; 31 | while(++i < l) { 32 | var key = keys[i]; 33 | if (iterator(object[key], key, object) === false) { 34 | break; 35 | } 36 | } 37 | } 38 | return object; 39 | } 40 | 41 | function times(n, iterator) { 42 | 43 | var i = -1; 44 | while(++i < n) { 45 | iterator(i); 46 | } 47 | } 48 | 49 | function shuffle(array) { 50 | 51 | var index = -1; 52 | var result = Array(array.length); 53 | forEach(array, function(value) { 54 | var rand = Math.floor(Math.random() * (++index + 1)); 55 | result[index] = result[rand]; 56 | result[rand] = value; 57 | }); 58 | return result; 59 | } 60 | 61 | 62 | function Comparator() { 63 | 64 | this._funcs = {}; 65 | this._events = {}; 66 | this._options = {}; 67 | } 68 | 69 | /** 70 | * @param {String|Object} name 71 | * @param {Function} func 72 | */ 73 | Comparator.prototype.set = function set(name, func) { 74 | 75 | if (typeof name == 'object') { 76 | util.forEach(name, util.reverse(set.bind(this))); 77 | } else { 78 | this._funcs[name] = func; 79 | } 80 | return this; 81 | }; 82 | 83 | /** 84 | * @param {String|String[]} name 85 | */ 86 | Comparator.prototype.get = function(name) { 87 | 88 | var self = this; 89 | if (Array.isArray(name)) { 90 | return name.reduce(name, function(memo, name) { 91 | memo[name] = self._funcs[name]; 92 | return memo; 93 | }, {}); 94 | } 95 | return this._funcs[name]; 96 | }; 97 | 98 | /** 99 | * @param {Object} options 100 | * @param {Number} options.times - default 10 101 | * @param {Boolean} options.result - display result array [μs] | default false 102 | * @param {Boolean} options.min - display min [μs] | default true 103 | * @param {Boolean} options.max - display max [μs] | default true 104 | * @param {Boolean} options.average - display average [μs] | default true 105 | * @param {Boolean} options.variance - display variance | default true 106 | * @param {Boolean} options.standard_deviation - display standard deviation [μs] | default true 107 | * @param {Boolean} options.versus - vs other functions [%] | default true 108 | * @param {Boolean} options.vs - alias 109 | * @param {Boolean} options.async - use asynchronous 110 | */ 111 | Comparator.prototype.option = function(options) { 112 | 113 | var self = this; 114 | util.forEach(options, function(value, key) { 115 | self._options[key] = value; 116 | }); 117 | return this; 118 | }; 119 | 120 | /** 121 | * use asynchronous 122 | * @param {Boolean} bool - option.async 123 | */ 124 | Comparator.prototype.async = function(bool) { 125 | 126 | this._options.async = bool === false ? false : true; 127 | return this; 128 | }; 129 | 130 | /** 131 | * @param {Number} times - option.times 132 | */ 133 | Comparator.prototype.times = function(times) { 134 | 135 | if (times) { 136 | this._options.times = times; 137 | } 138 | return this; 139 | }; 140 | 141 | Comparator.prototype.start = function() { 142 | 143 | var self = this; 144 | var funcs = self._funcs; 145 | var keys = Object.keys(funcs); 146 | var size = keys.length; 147 | if (!size) { 148 | throw new Error('function does not set yet'); 149 | } 150 | 151 | var times = self._options.times || 10; 152 | var results = {}; 153 | util.forEach(keys, function(key) { 154 | results[key] = Array(times); 155 | }); 156 | 157 | // comparison 158 | var timer = getTimer(); 159 | if (!timer) { 160 | throw new Error('timer can not use'); 161 | } 162 | 163 | var count = 0; 164 | var end = false; 165 | var gc = objectTypes[typeof global] && global && global.gc; 166 | if (self._options.async) { 167 | self._started = true; 168 | iterate(); 169 | return self; 170 | } 171 | util.times(times, function(i) { 172 | var sample = util.shuffle(keys); 173 | util.forEach(sample, function(key) { 174 | var func = funcs[key]; 175 | timer.init().start(); 176 | func(); 177 | var diff = timer.diff(); 178 | results[key][i] = diff; 179 | }); 180 | if (gc) { 181 | gc(); 182 | } 183 | }); 184 | self._results = results; 185 | return self; 186 | 187 | function iterate() { 188 | 189 | if (!end) { 190 | iterator(function(err) { 191 | if (err) { 192 | return done(err); 193 | } 194 | if (count++ >= times) { 195 | return done(); 196 | } 197 | setTimeout(iterate); 198 | }); 199 | } else { 200 | done(); 201 | } 202 | } 203 | 204 | function done(err) { 205 | self._started = false; 206 | self._err = err; 207 | self._results = results; 208 | self.emit('result'); 209 | } 210 | 211 | function iterator(callback) { 212 | 213 | var index = 0; 214 | var sample = util.shuffle(keys); 215 | (function _iterate() { 216 | var key = sample[index++]; 217 | if (!key) { 218 | return callback(); 219 | } 220 | timer.init().start(); 221 | funcs[key](function(err) { 222 | results[key][count] = timer.diff(); 223 | if (err) { 224 | return callback(err); 225 | } 226 | if (gc) { 227 | gc(); 228 | } 229 | setTimeout(_iterate); 230 | }); 231 | })(); 232 | } 233 | }; 234 | 235 | Comparator.prototype.on = function on(key, callback) { 236 | 237 | if (typeof key == 'object') { 238 | util.forEach(key, util.reverse(on)); 239 | return this; 240 | } 241 | this._events[key] = this._events[key] || []; 242 | this._events[key].push(callback); 243 | return this; 244 | }; 245 | 246 | Comparator.prototype.once = function once(key, callback) { 247 | 248 | if (typeof key == 'object') { 249 | util.forEach(key, util.reverse(once)); 250 | return this; 251 | } 252 | callback._once = true; 253 | this._events[key] = this._events[key] || []; 254 | this._events[key].push(callback); 255 | return this; 256 | }; 257 | 258 | Comparator.prototype.emit = function emit(key, err, res) { 259 | 260 | var events = this._events[key] || []; 261 | if (!events.length) { 262 | return this; 263 | } 264 | var deleteFlags = []; 265 | util.forEach(events, function(func, index) { 266 | func(err, res); 267 | if (func._once) { 268 | deleteFlags.unshift(index); 269 | } 270 | }); 271 | if (deleteFlags.length) { 272 | util.forEach(deleteFlags, function(index) { 273 | events.splice(index, 1); 274 | }); 275 | } 276 | return this; 277 | }; 278 | 279 | Comparator.prototype.result = function result(callback) { 280 | 281 | var results = {}; 282 | var opts = this._options; 283 | var async = opts.async; 284 | if (async && this._started) { 285 | this.once('result', result.bind(this, callback)); 286 | return 'calculating...'; 287 | } 288 | 289 | var res = opts.result === true; 290 | var max = opts.max !== false; 291 | var min = opts.min !== false; 292 | var ave = opts.average !== false; 293 | var varia = opts.variance !== false; 294 | var dev = opts.standard_deviation !== false; 295 | var vs = !(opts.versus === false || opts.vs === false); 296 | util.forEach(this._results, function(array, key) { 297 | var l = array.length; 298 | if (!l) { 299 | return; 300 | } 301 | var data = {}; 302 | if (res) { 303 | data.result = array; 304 | } 305 | if (ave || varia || dev || vs) { 306 | var sum = 0; 307 | util.forEach(array, function(n) { 308 | sum += n; 309 | }); 310 | var average = sum / array.length; 311 | if (min) { 312 | data.min = resolveDecimal(Math.min.apply(null, array)); 313 | } 314 | if (max) { 315 | data.max = resolveDecimal(Math.max.apply(null, array)); 316 | } 317 | if (ave || vs) { 318 | data.average = resolveDecimal(average); 319 | } 320 | if (varia || dev) { 321 | var variance = (function() { 322 | var variance = 0; 323 | util.forEach(array, function(value) { 324 | variance += Math.pow(value - average, 2); 325 | }); 326 | return variance / l; 327 | })(); 328 | variance = resolveDecimal(variance); 329 | if (varia) { 330 | data.variance = variance; 331 | } 332 | if (dev) { 333 | data.standard_deviation = resolveDecimal(Math.sqrt(variance, 2)); 334 | } 335 | } 336 | } 337 | 338 | results[key] = data; 339 | }); 340 | if (vs) { 341 | util.forEach(results, function(data, key) { 342 | data.vs = {}; 343 | util.forEach(results, function(_data, _key) { 344 | if (key === _key) { 345 | return; 346 | } 347 | data.vs[_key] = resolveDecimal(100 * _data.average / data.average); 348 | }); 349 | if (!ave) { 350 | delete data.average; 351 | } 352 | }); 353 | } 354 | 355 | if (callback) { 356 | callback(this._err, results); 357 | } 358 | return results; 359 | }; 360 | 361 | function getTimer() { 362 | 363 | if (objectTypes[typeof process] && process && process.hrtime) { 364 | return new NodeTimer(); 365 | } 366 | if (objectTypes[typeof performance] && performance && performance.now) { 367 | return new PerformanceTimer(); 368 | } 369 | if (objectTypes[typeof Date]) { 370 | return new DateTimer(); 371 | } 372 | } 373 | Comparator.prototype.getTimer = getTimer; 374 | 375 | // process.hrtime 376 | function NodeTimer() { 377 | 378 | this._startTime = null; 379 | this._diff = null; 380 | } 381 | 382 | NodeTimer.prototype.init = function() { 383 | 384 | this._startTime = null; 385 | this._diff = null; 386 | return this; 387 | }; 388 | 389 | NodeTimer.prototype.start = function () { 390 | 391 | this._startTime = process.hrtime(); 392 | return this; 393 | }; 394 | 395 | NodeTimer.prototype.diff = function() { 396 | 397 | var diff = process.hrtime(this._startTime); 398 | // ns 399 | this._diff = diff[0] * 1e9 + diff[1]; 400 | // μs 401 | return this._diff / 1000; 402 | }; 403 | 404 | // performance.now 405 | function PerformanceTimer() { 406 | 407 | this._startTime = null; 408 | this._diff = null; 409 | } 410 | 411 | PerformanceTimer.prototype.init = function() { 412 | 413 | this._startTime = null; 414 | this._diff = null; 415 | return this; 416 | }; 417 | 418 | PerformanceTimer.prototype.start = function () { 419 | 420 | this._startTime = performance.now(); 421 | return this; 422 | }; 423 | 424 | PerformanceTimer.prototype.diff = function() { 425 | 426 | // ms 427 | this._diff = performance.now() - this._startTime; 428 | // μs 429 | return this._diff * 1000; 430 | }; 431 | 432 | function DateTimer() { 433 | 434 | this._startTime = null; 435 | this._diff = null; 436 | } 437 | 438 | DateTimer.prototype.init = function() { 439 | 440 | this._key = new Date().getTime(); 441 | this._startTime = null; 442 | this._diff = null; 443 | return this; 444 | }; 445 | 446 | DateTimer.prototype.start = function () { 447 | 448 | this._startTime = new Date().getTime(); 449 | return this; 450 | }; 451 | 452 | DateTimer.prototype.diff = function() { 453 | 454 | // ms 455 | this._diff = new Date().getTime() - this._startTime; 456 | // μs 457 | return this._diff * 1000; 458 | }; 459 | 460 | function resolveDecimal(num) { 461 | 462 | return Math.floor(100 * num) / 100; 463 | } 464 | 465 | var objectTypes = { 466 | 'function': true, 467 | 'object': true 468 | }; 469 | 470 | (function() { 471 | var comparator = new Comparator(); 472 | if (objectTypes[typeof module] && module && module.exports) { 473 | module.exports = comparator; 474 | } else { 475 | this.comparator = comparator; 476 | } 477 | }).call(this); 478 | 479 | -------------------------------------------------------------------------------- /public/js/underscore-min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.7.0 2 | // http://underscorejs.org 3 | // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | (function(){var n=this,t=n._,r=Array.prototype,e=Object.prototype,u=Function.prototype,i=r.push,a=r.slice,o=r.concat,l=e.toString,c=e.hasOwnProperty,f=Array.isArray,s=Object.keys,p=u.bind,h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=h),exports.__=h):n.__=h,h.VERSION="1.7.0";var g=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}};h.iteratee=function(n,t,r){return null==n?h.identity:h.isFunction(n)?g(n,t,r):h.isObject(n)?h.matches(n):h.property(n)},h.each=h.forEach=function(n,t,r){if(null==n)return n;t=g(t,r);var e,u=n.length;if(u===+u)for(e=0;u>e;e++)t(n[e],e,n);else{var i=h.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},h.map=h.collect=function(n,t,r){if(null==n)return[];t=h.iteratee(t,r);for(var e,u=n.length!==+n.length&&h.keys(n),i=(u||n).length,a=Array(i),o=0;i>o;o++)e=u?u[o]:o,a[o]=t(n[e],e,n);return a};var v="Reduce of empty array with no initial value";h.reduce=h.foldl=h.inject=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length,o=0;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[o++]:o++]}for(;a>o;o++)u=i?i[o]:o,r=t(r,n[u],u,n);return r},h.reduceRight=h.foldr=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[--a]:--a]}for(;a--;)u=i?i[a]:a,r=t(r,n[u],u,n);return r},h.find=h.detect=function(n,t,r){var e;return t=h.iteratee(t,r),h.some(n,function(n,r,u){return t(n,r,u)?(e=n,!0):void 0}),e},h.filter=h.select=function(n,t,r){var e=[];return null==n?e:(t=h.iteratee(t,r),h.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e)},h.reject=function(n,t,r){return h.filter(n,h.negate(h.iteratee(t)),r)},h.every=h.all=function(n,t,r){if(null==n)return!0;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,!t(n[u],u,n))return!1;return!0},h.some=h.any=function(n,t,r){if(null==n)return!1;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,t(n[u],u,n))return!0;return!1},h.contains=h.include=function(n,t){return null==n?!1:(n.length!==+n.length&&(n=h.values(n)),h.indexOf(n,t)>=0)},h.invoke=function(n,t){var r=a.call(arguments,2),e=h.isFunction(t);return h.map(n,function(n){return(e?t:n[t]).apply(n,r)})},h.pluck=function(n,t){return h.map(n,h.property(t))},h.where=function(n,t){return h.filter(n,h.matches(t))},h.findWhere=function(n,t){return h.find(n,h.matches(t))},h.max=function(n,t,r){var e,u,i=-1/0,a=-1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],e>i&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(u>a||u===-1/0&&i===-1/0)&&(i=n,a=u)});return i},h.min=function(n,t,r){var e,u,i=1/0,a=1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],i>e&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(a>u||1/0===u&&1/0===i)&&(i=n,a=u)});return i},h.shuffle=function(n){for(var t,r=n&&n.length===+n.length?n:h.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=h.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},h.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=h.values(n)),n[h.random(n.length-1)]):h.shuffle(n).slice(0,Math.max(0,t))},h.sortBy=function(n,t,r){return t=h.iteratee(t,r),h.pluck(h.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var m=function(n){return function(t,r,e){var u={};return r=h.iteratee(r,e),h.each(t,function(e,i){var a=r(e,i,t);n(u,e,a)}),u}};h.groupBy=m(function(n,t,r){h.has(n,r)?n[r].push(t):n[r]=[t]}),h.indexBy=m(function(n,t,r){n[r]=t}),h.countBy=m(function(n,t,r){h.has(n,r)?n[r]++:n[r]=1}),h.sortedIndex=function(n,t,r,e){r=h.iteratee(r,e,1);for(var u=r(t),i=0,a=n.length;a>i;){var o=i+a>>>1;r(n[o])t?[]:a.call(n,0,t)},h.initial=function(n,t,r){return a.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},h.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:a.call(n,Math.max(n.length-t,0))},h.rest=h.tail=h.drop=function(n,t,r){return a.call(n,null==t||r?1:t)},h.compact=function(n){return h.filter(n,h.identity)};var y=function(n,t,r,e){if(t&&h.every(n,h.isArray))return o.apply(e,n);for(var u=0,a=n.length;a>u;u++){var l=n[u];h.isArray(l)||h.isArguments(l)?t?i.apply(e,l):y(l,t,r,e):r||e.push(l)}return e};h.flatten=function(n,t){return y(n,t,!1,[])},h.without=function(n){return h.difference(n,a.call(arguments,1))},h.uniq=h.unique=function(n,t,r,e){if(null==n)return[];h.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=h.iteratee(r,e));for(var u=[],i=[],a=0,o=n.length;o>a;a++){var l=n[a];if(t)a&&i===l||u.push(l),i=l;else if(r){var c=r(l,a,n);h.indexOf(i,c)<0&&(i.push(c),u.push(l))}else h.indexOf(u,l)<0&&u.push(l)}return u},h.union=function(){return h.uniq(y(arguments,!0,!0,[]))},h.intersection=function(n){if(null==n)return[];for(var t=[],r=arguments.length,e=0,u=n.length;u>e;e++){var i=n[e];if(!h.contains(t,i)){for(var a=1;r>a&&h.contains(arguments[a],i);a++);a===r&&t.push(i)}}return t},h.difference=function(n){var t=y(a.call(arguments,1),!0,!0,[]);return h.filter(n,function(n){return!h.contains(t,n)})},h.zip=function(n){if(null==n)return[];for(var t=h.max(arguments,"length").length,r=Array(t),e=0;t>e;e++)r[e]=h.pluck(arguments,e);return r},h.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},h.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=h.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}for(;u>e;e++)if(n[e]===t)return e;return-1},h.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=n.length;for("number"==typeof r&&(e=0>r?e+r+1:Math.min(e,r+1));--e>=0;)if(n[e]===t)return e;return-1},h.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var d=function(){};h.bind=function(n,t){var r,e;if(p&&n.bind===p)return p.apply(n,a.call(arguments,1));if(!h.isFunction(n))throw new TypeError("Bind must be called on a function");return r=a.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(a.call(arguments)));d.prototype=n.prototype;var u=new d;d.prototype=null;var i=n.apply(u,r.concat(a.call(arguments)));return h.isObject(i)?i:u}},h.partial=function(n){var t=a.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===h&&(e[u]=arguments[r++]);for(;r=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=h.bind(n[r],n);return n},h.memoize=function(n,t){var r=function(e){var u=r.cache,i=t?t.apply(this,arguments):e;return h.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},h.delay=function(n,t){var r=a.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},h.defer=function(n){return h.delay.apply(h,[n,1].concat(a.call(arguments,1)))},h.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var l=function(){o=r.leading===!1?0:h.now(),a=null,i=n.apply(e,u),a||(e=u=null)};return function(){var c=h.now();o||r.leading!==!1||(o=c);var f=t-(c-o);return e=this,u=arguments,0>=f||f>t?(clearTimeout(a),a=null,o=c,i=n.apply(e,u),a||(e=u=null)):a||r.trailing===!1||(a=setTimeout(l,f)),i}},h.debounce=function(n,t,r){var e,u,i,a,o,l=function(){var c=h.now()-a;t>c&&c>0?e=setTimeout(l,t-c):(e=null,r||(o=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,a=h.now();var c=r&&!e;return e||(e=setTimeout(l,t)),c&&(o=n.apply(i,u),i=u=null),o}},h.wrap=function(n,t){return h.partial(t,n)},h.negate=function(n){return function(){return!n.apply(this,arguments)}},h.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},h.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},h.before=function(n,t){var r;return function(){return--n>0?r=t.apply(this,arguments):t=null,r}},h.once=h.partial(h.before,2),h.keys=function(n){if(!h.isObject(n))return[];if(s)return s(n);var t=[];for(var r in n)h.has(n,r)&&t.push(r);return t},h.values=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},h.pairs=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},h.invert=function(n){for(var t={},r=h.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},h.functions=h.methods=function(n){var t=[];for(var r in n)h.isFunction(n[r])&&t.push(r);return t.sort()},h.extend=function(n){if(!h.isObject(n))return n;for(var t,r,e=1,u=arguments.length;u>e;e++){t=arguments[e];for(r in t)c.call(t,r)&&(n[r]=t[r])}return n},h.pick=function(n,t,r){var e,u={};if(null==n)return u;if(h.isFunction(t)){t=g(t,r);for(e in n){var i=n[e];t(i,e,n)&&(u[e]=i)}}else{var l=o.apply([],a.call(arguments,1));n=new Object(n);for(var c=0,f=l.length;f>c;c++)e=l[c],e in n&&(u[e]=n[e])}return u},h.omit=function(n,t,r){if(h.isFunction(t))t=h.negate(t);else{var e=h.map(o.apply([],a.call(arguments,1)),String);t=function(n,t){return!h.contains(e,t)}}return h.pick(n,t,r)},h.defaults=function(n){if(!h.isObject(n))return n;for(var t=1,r=arguments.length;r>t;t++){var e=arguments[t];for(var u in e)n[u]===void 0&&(n[u]=e[u])}return n},h.clone=function(n){return h.isObject(n)?h.isArray(n)?n.slice():h.extend({},n):n},h.tap=function(n,t){return t(n),n};var b=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof h&&(n=n._wrapped),t instanceof h&&(t=t._wrapped);var u=l.call(n);if(u!==l.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]===n)return e[i]===t;var a=n.constructor,o=t.constructor;if(a!==o&&"constructor"in n&&"constructor"in t&&!(h.isFunction(a)&&a instanceof a&&h.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c,f;if("[object Array]"===u){if(c=n.length,f=c===t.length)for(;c--&&(f=b(n[c],t[c],r,e)););}else{var s,p=h.keys(n);if(c=p.length,f=h.keys(t).length===c)for(;c--&&(s=p[c],f=h.has(t,s)&&b(n[s],t[s],r,e)););}return r.pop(),e.pop(),f};h.isEqual=function(n,t){return b(n,t,[],[])},h.isEmpty=function(n){if(null==n)return!0;if(h.isArray(n)||h.isString(n)||h.isArguments(n))return 0===n.length;for(var t in n)if(h.has(n,t))return!1;return!0},h.isElement=function(n){return!(!n||1!==n.nodeType)},h.isArray=f||function(n){return"[object Array]"===l.call(n)},h.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},h.each(["Arguments","Function","String","Number","Date","RegExp"],function(n){h["is"+n]=function(t){return l.call(t)==="[object "+n+"]"}}),h.isArguments(arguments)||(h.isArguments=function(n){return h.has(n,"callee")}),"function"!=typeof/./&&(h.isFunction=function(n){return"function"==typeof n||!1}),h.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},h.isNaN=function(n){return h.isNumber(n)&&n!==+n},h.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===l.call(n)},h.isNull=function(n){return null===n},h.isUndefined=function(n){return n===void 0},h.has=function(n,t){return null!=n&&c.call(n,t)},h.noConflict=function(){return n.__=t,this},h.identity=function(n){return n},h.constant=function(n){return function(){return n}},h.noop=function(){},h.property=function(n){return function(t){return t[n]}},h.matches=function(n){var t=h.pairs(n),r=t.length;return function(n){if(null==n)return!r;n=new Object(n);for(var e=0;r>e;e++){var u=t[e],i=u[0];if(u[1]!==n[i]||!(i in n))return!1}return!0}},h.times=function(n,t,r){var e=Array(Math.max(0,n));t=g(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},h.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},h.now=Date.now||function(){return(new Date).getTime()};var _={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},w=h.invert(_),j=function(n){var t=function(t){return n[t]},r="(?:"+h.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};h.escape=j(_),h.unescape=j(w),h.result=function(n,t){if(null==n)return void 0;var r=n[t];return h.isFunction(r)?n[t]():r};var x=0;h.uniqueId=function(n){var t=++x+"";return n?n+t:t},h.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var A=/(.)^/,k={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},O=/\\|'|\r|\n|\u2028|\u2029/g,F=function(n){return"\\"+k[n]};h.template=function(n,t,r){!t&&r&&(t=r),t=h.defaults({},t,h.templateSettings);var e=RegExp([(t.escape||A).source,(t.interpolate||A).source,(t.evaluate||A).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(O,F),u=o+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":a&&(i+="';\n"+a+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=new Function(t.variable||"obj","_",i)}catch(o){throw o.source=i,o}var l=function(n){return a.call(this,n,h)},c=t.variable||"obj";return l.source="function("+c+"){\n"+i+"}",l},h.chain=function(n){var t=h(n);return t._chain=!0,t};var E=function(n){return this._chain?h(n).chain():n};h.mixin=function(n){h.each(h.functions(n),function(t){var r=h[t]=n[t];h.prototype[t]=function(){var n=[this._wrapped];return i.apply(n,arguments),E.call(this,r.apply(h,n))}})},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=r[n];h.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],E.call(this,r)}}),h.each(["concat","join","slice"],function(n){var t=r[n];h.prototype[n]=function(){return E.call(this,t.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}).call(this); 6 | //# sourceMappingURL=underscore-min.map -------------------------------------------------------------------------------- /public/js/neo_async.min.js: -------------------------------------------------------------------------------- 1 | (function(){function t(b){for(var d=Object.keys(b),a=-1,c=d.length,e=Array(c);++a=c)return[];for(var e=Array(c);++a=f&&(a(),a=function(){})}a=a||function(){};var f,g=0,k=c?d.bind(c):d;d=function(a){k(a,r(e))};if(Array.isArray(b)){f=b.length;if(!f)return a();s(b,d)}else{c=Object.keys(b);f=c.length;if(!f)return a();v(b,d,c)}}function I(b,d,a,c){function e(b,c){if(k)throw Error("Callback was already called.");k=!0;if(b)return a(b);if(++h>=f||!1===c)return a();g()} 4 | a=a||function(){};var f,g,k,h=0,l=c?d.bind(c):d;if(Array.isArray(b)){f=b.length;if(!f)return a();g=function(){k=!1;l(b[h],e)}}else{var m=Object.keys(b);f=m.length;if(!f)return a();g=function(){k=!1;l(b[m[h]],e)}}g()}function W(b,d,a,c,e){function f(a,b){a?(c(a),c=function(){}):++h>=g?(c(),c=function(){}):!1===b?(c(),c=function(){}):h>=l+d&&(l=h,k())}c=c||function(){};var g,k,h=0,l=0,m=e?a.bind(e):a;if(Array.isArray(b)){g=b.length;if(!g)return c();k=function(){q(d,function(a){a=l+a;a>=g||m(b[a],r(f))})}}else{var n= 5 | Object.keys(b);g=n.length;if(!g)return c();k=function(){q(d,function(a){a=l+a;a>=g||m(b[n[a]],r(f))})}}k()}function X(b,d,a,c){function e(b){var c=!1;return function(e,d){if(c)throw Error("Callback was already called.");c=!0;g[b]=d;e?(a(e,w(g)),a=function(){}):++h>=f&&(a(null,g),a=function(){})}}a=a||function(){};var f,g,k=0,h=0,l=c?d.bind(c):d;d=function(a){l(a,e(k++))};if(Array.isArray(b)){f=b.length;g=Array(f);if(!f)return a(null,g);s(b,d)}else{c=Object.keys(b);f=c.length;g=Array(f);if(!f)return a(null, 6 | g);v(b,d,c)}}function Y(b,d,a,c){function e(b){var c=!1;return function(e,d){if(c)throw Error("Callback was already called.");c=!0;g[b]=d;e?(a(e,w(g)),a=function(){}):++h>=f?(a(null,g),a=function(){}):k()}}a=a||function(){};var f,g,k,h=0,l=c?d.bind(c):d;if(Array.isArray(b)){f=b.length;g=Array(f);if(!f)return a(null,g);k=function(){l(b[h],e(h))}}else{var m=Object.keys(b);f=m.length;g=Array(f);if(!f)return a(null,g);k=function(){l(b[m[h]],e(h))}}k()}function Z(b,d,a,c,e){function f(a){var b=!1;return function(e, 7 | f){if(b)throw Error("Callback was already called.");b=!0;k[a]=f;e?(c(e,w(k)),c=function(){}):++l>=g?(c(null,k),c=function(){}):l>=m+d&&(m=l,h())}}c=c||function(){};var g,k,h,l=0,m=0,n=e?a.bind(e):a;if(Array.isArray(b)){g=b.length;k=Array(g);if(!g)return c(null,k);h=function(){q(d,function(a){a=m+a;a>=g||n(b[a],f(a))})}}else{var p=Object.keys(b);g=p.length;k=Array(g);if(!g)return c(null,k);h=function(){q(d,function(a){a=m+a;a>=g||n(b[p[a]],f(a))})}}h()}function $(b,d,a,c){J(b,d,function(c){a=a||function(){}; 8 | a(Array.isArray(b)?c:t(c))},c)}function aa(b,d,a,c){K(b,d,function(c){a=a||function(){};a(Array.isArray(b)?c:t(c))},c)}function ba(b,d,a,c,e){L(b,d,a,function(a){c=c||function(){};c(Array.isArray(b)?a:t(a))},e)}function M(b,d,a,c,e){a=a||function(){};var f,g=0,k=function(){function b(c){return function(b){b?++g>=f&&(a(),a=function(){}):(a(c),a=function(){})}}function c(b){return function(c){c?(a(b),a=function(){}):++g>=f&&(a(),a=function(){})}}return e?b:c}(),h=c?d.bind(c):d;d=function(a){h(a,k(a))}; 9 | if(Array.isArray(b)){f=b.length;if(!f)return a();s(b,d)}else{f=Object.keys(b).length;if(!f)return a();v(b,d)}}function N(b,d,a,c,e){a=a||function(){};var f,g,k,h=0,l=function(){function b(c){return function(b){if(k)throw Error("Callback was already called.");k=!0;if(!b)return a(c);if(++h>=f)return a();g()}}function c(b){return function(c){if(k)throw Error("Callback was already called.");k=!0;if(c)return a(b);if(++h>=f)return a();g()}}return e?b:c}(),m=c?d.bind(c):d;if(Array.isArray(b)){f=b.length; 10 | if(!f)return a();g=function(){k=!1;var a=b[h];m(a,l(a))}}else{var n=Object.keys(b);f=n.length;if(!f)return a();g=function(){k=!1;var a=b[n[h]];m(a,l(a))}}g()}function O(b,d,a,c,e,f){c=c||function(){};var g,k,h=0,l=0,m=function(){function a(b){return function(a){a?++h>=g?(c(),c=function(){}):h>=l+d&&(l=h,k()):(c(b),c=function(){})}}function b(a){return function(b){b?(c(a),c=function(){}):++h>=g?(c(),c=function(){}):h>=l+d&&(l=h,k())}}return f?a:b}(),n=e?a.bind(e):a;if(Array.isArray(b)){g=b.length; 11 | if(!g)return c();k=function(){q(d,function(a){a=l+a;a>=g||(a=b[a],n(a,r(m(a))))})}}else{var p=Object.keys(b);g=p.length;if(!g)return c();k=function(){q(d,function(a){a=l+a;a>=g||(a=b[p[a]],n(a,r(m(a))))})}}k()}function J(b,d,a,c,e){a=a||function(){};var f,g=Array.isArray(b),k={},h=0,l=function(){function b(c,e){var d=!1;return function(b){if(d)throw Error("Callback was already called.");d=!0;b||(k[c+""]=e);++h>=f&&a(g?t(k):k)}}function c(b,e){var d=!1;return function(c){if(d)throw Error("Callback was already called."); 12 | d=!0;c&&(k[b+""]=e);++h>=f&&a(g?t(k):k)}}return e?b:c}(),m=c?d.bind(c):d;d=function(a,b){m(a,l(b,a))};if(g){f=b.length;if(!f)return a([]);s(b,d)}else{f=Object.keys(b).length;if(!f)return a({});v(b,d)}}function K(b,d,a,c,e){a=a||function(){};var f,g,k=Array.isArray(b),h={},l=0,m=function(){function b(c,d){var e=!1;return function(b){if(e)throw Error("Callback was already called.");e=!0;b||(h[c+""]=d);if(++l>=f)return a(k?t(h):h);g()}}function c(b,e){var d=!1;return function(c){if(d)throw Error("Callback was already called."); 13 | d=!0;c&&(h[b+""]=e);if(++l>=f)return a(k?t(h):h);g()}}return e?b:c}(),n=c?d.bind(c):d;if(k){f=b.length;if(!f)return a([]);g=function(){var a=b[l];n(a,m(l,a))}}else{var p=Object.keys(b);f=p.length;if(!f)return a({});g=function(){var a=p[l],c=b[a];n(c,m(a,c))}}g()}function L(b,d,a,c,e,f){c=c||function(){};var g,k,h=Array.isArray(b),l={},m=0,n=0,p=function(){function a(b,e){var f=!1;return function(a){if(f)throw Error("Callback was already called.");f=!0;a||(l[b+""]=e);if(++m>=g)return c(h?t(l):l);m>= 14 | n+d&&(n=m,k())}}function b(a,e){var f=!1;return function(b){if(f)throw Error("Callback was already called.");f=!0;b&&(l[a+""]=e);if(++m>=g)return c(h?t(l):l);m>=n+d&&(n=m,k())}}return f?a:b}(),y=e?a.bind(e):a;if(h){g=b.length;if(!g)return c([]);k=function(){q(d,function(a){a=n+a;if(!(a>=g)){var c=b[a];y(c,p(a,c))}})}}else{var C=Object.keys(b);g=C.length;if(!g)return c({});k=function(){q(d,function(a){a=n+a;if(!(a>=g)){a=C[a];var c=b[a];y(c,p(a,c))}})}}k()}function P(b,d,a,c,e){function f(a,b){if(h)throw Error("Callback was already called."); 15 | h=!0;if(a)return c(a);if(++l>=g)return c(null,b);k(b)}c=c||function(){};var g,k,h,l=0,m=e?a.bind(e):a;if(Array.isArray(b)){g=b.length;if(!g)return c(null,d);k=function(a){h=!1;m(a,b[l],f)}}else{var n=Object.keys(b);g=n.length;if(!g)return c(null,d);k=function(a){h=!1;m(a,b[n[l]],f)}}k(d)}function ca(b,d,a,c,e){function f(b,c){b?(a(b,k?w(h):B(h)),a=function(){}):!1===c?(a(null,k?w(h):B(h)),a=function(){}):++l>=g&&(a(null,h),a=function(){})}a=a||function(){};var g,k=Array.isArray(b),h=void 0!==c?c: 16 | k?[]:{},l=0,m=e?d.bind(e):d;d=function(a,b){m(h,a,b,r(f))};if(k){g=b.length;if(!g)return a(null,h);s(b,d)}else{c=Object.keys(b);g=c.length;if(!g)return a(null,h);v(b,d,c)}}function da(b,d,a,c,e){function f(b,c){if(h)throw Error("Callback was already called.");h=!0;if(b)return a(b,m);if(!1===c||++n>=g)return a(null,m);k()}a=a||function(){};var g,k,h,l=Array.isArray(b),m=void 0!==c?c:l?[]:{},n=0,p=e?d.bind(e):d;if(l){g=b.length;if(!g)return a(null,m);k=function(){h=!1;p(m,b[n],n,f)}}else{var y=Object.keys(b); 17 | g=y.length;if(!g)return a(null,m);k=function(){h=!1;var a=y[n];p(m,b[a],a,f)}}k()}function ea(b,d,a,c,e,f){function g(a,b){a?(c(a,l?w(m):B(m)),c=function(){}):!1===b?(c(null,l?w(m):B(m)),c=function(){}):++n>=k?(c(null,m),c=function(){}):n>=p+d&&(p=n,h())}c=c||function(){};var k,h,l=Array.isArray(b),m=void 0!==e?e:l?[]:{},n=0,p=0,y=f?a.bind(f):a;if(l){k=b.length;if(!k)return c(null,m);h=function(){q(d,function(a){a=p+a;a>=k||y(m,b[a],a,r(g))})}}else{var C=Object.keys(b);k=C.length;if(!k)return c(null, 18 | m);h=function(){q(d,function(a){a=p+a;a>=k||(a=C[a],y(m,b[a],a,r(g)))})}}h()}function Q(b){function d(a,b,d,h){b=c(b,h);Y(a,b,e(d))}function a(a,b,d,h,l){d=c(d,l);Z(a,b,d,e(h))}function c(a,b){var c=b?a.bind(b):a;return function(a,b){c(a,function(c,d){if(c)return b(c);b(null,{item:a,criteria:d})})}}function e(a){return function(b,c){if(b)return a(b);var d=c.sort(function(a,b){return b.criteria=e.concurrency&&e.saturated();x(e.process)})): 21 | e.idle()&&"function"==typeof e.drain&&x(e.drain)}var e={tasks:[],concurrency:d||1,saturated:null,empty:null,drain:null,started:!1,paused:!1,push:function(a,b,d){c(a,b,d)},kill:function(){e.drain=null;e.tasks=[]},process:function(){function a(){f--;b.callback&&b.callback.apply(b,arguments);"function"==typeof e.drain&&e.idle()&&e.drain();e.process()}if(!(e.paused||f>=e.concurrency)&&e.length()){var b=e.tasks.shift();"function"!=typeof e.empty||e.length()||e.empty();f++;(e._thisArg?e._worker.bind(e._thisArg): 22 | e._worker)(b.task,r(a))}},length:function(){return e.tasks.length},running:function(){return f},idle:function(){return 0===e.length()+f},pause:function(){e.paused=!0},resume:function(){!1!==e.paused&&(e.paused=!1,q(e.concurrency,function(){D.setImmediate(e.process)}))},_worker:b,_thisArg:a},f=0;return e}function ka(b,d,a,c){function e(c){var d=!1;return function(m,n){if(d)throw Error("Callback was already called.");d=!0;f[c]=n;if(m)return a(m);if(++g>=b)return a(null,f);k(g,e(g))}}a=a||function(){}; 23 | var f=[];if(1>b)return a(null,f);var g=0,k=c?d.bind(c):d;k(g,e(g))}function T(b){function d(a){if(A[typeof console])if(a)console.error&&console.error(a);else if(console[b]){var c=u(arguments,1);s(c,function(a){console[b](a)})}}return function(a){var b=u(arguments,1);b.push(d);a.apply(null,b)}}function z(b,d){this._emitter=b||ga;this._limit=d||4;this._events={}}var G=this,na=G&&G.async,A={"function":!0,object:!0},E,x;(function(){A[typeof process]&&process.nextTick?(E=process.nextTick,x=A[typeof setImmediate]? 24 | function(b){setImmediate(b)}:E):x=E=A[typeof setImmediate]?function(b){setImmediate(b)}:function(b){setTimeout(b,0)}})();var D={VERSION:"0.4.3",each:H,eachSeries:I,eachLimit:W,forEach:H,forEachSeries:I,forEachLimit:W,map:X,mapSeries:Y,mapLimit:Z,filter:$,filterSeries:aa,filterLimit:ba,select:$,selectSeries:aa,selectLimit:ba,reject:function(b,d,a,c){J(b,d,a,c,!0)},rejectSeries:function(b,d,a,c){K(b,d,a,c,!0)},rejectLimit:function(b,d,a,c,e){L(b,d,a,c,e,!0)},detect:M,detectSeries:N,detectLimit:O,pick:J, 25 | pickSeries:K,pickLimit:L,reduce:P,reduceRight:function(b,d,a,c,e){function f(a,b){if(h)throw Error("Callback was already called.");h=!0;if(a)return c(a);if(++l>=g)return c(null,b);k(b)}c=c||function(){};var g,k,h,l=0,m=e?a.bind(e):a;if(Array.isArray(b)){g=b.length;if(!g)return c(null,d);k=function(a){h=!1;m(a,b[g-l-1],f)}}else{var n=Object.keys(b);g=n.length;if(!g)return c(null,d);k=function(a){h=!1;m(a,b[n[g-l-1]],f)}}k(d)},transform:ca,transformSeries:da,transformLimit:ea,sortBy:Q(),sortBySeries:Q("series"), 26 | sortByLimit:Q("limit"),some:function(b,d,a,c){M(b,d,function(b){a=a||function(){};a(!!b)},c)},someSeries:function(b,d,a,c){N(b,d,function(b){a=a||function(){};a(!!b)},c)},someLimit:function(b,d,a,c,e){O(b,d,a,function(a){c=c||function(){};c(!!a)},e)},every:function(b,d,a,c){M(b,d,function(b){a=a||function(){};a(!b)},c,!0)},everySeries:function(b,d,a,c){N(b,d,function(b){a=a||function(){};a(!b)},c,!0)},everyLimit:function(b,d,a,c,e){O(b,d,a,function(a){c=c||function(){};c(!a)},e,!0)},concat:function(b, 27 | d,a,c){function e(b,c){c&&Array.prototype.push.apply(g,Array.isArray(c)?c:[c]);b?(a(b,w(g)),a=function(){}):++k>=f&&(a(null,g),a=function(){})}a=a||function(){};var f,g=[],k=0,h=c?d.bind(c):d;d=function(a){h(a,r(e))};if(Array.isArray(b)){f=b.length;if(!f)return a(null,g);s(b,d)}else{c=Object.keys(b);f=c.length;if(!f)return a(null,g);v(b,d,c)}},concatSeries:function(b,d,a,c){function e(b,c){if(k)throw Error("Callback was already called.");k=!0;c&&Array.prototype.push.apply(h,Array.isArray(c)?c:[c]); 28 | if(b)return a(b,h);if(++l>=f)return a(null,h);g()}a=a||function(){};var f,g,k,h=[],l=0,m=c?d.bind(c):d;if(Array.isArray(b)){f=b.length;if(!f)return a(null,h);g=function(){k=!1;m(b[l],e)}}else{var n=Object.keys(b);f=n.length;if(!f)return a(null,h);g=function(){k=!1;m(b[n[l]],e)}}g()},concatLimit:function(b,d,a,c,e){function f(a,b){b&&Array.prototype.push.apply(h,Array.isArray(b)?b:[b]);a?(c(a,h),c=function(){}):++l>=g?(c(null,h),c=function(){}):l>=m+d&&(m=l,k())}c=c||function(){};var g,k,h=[],l=0, 29 | m=0,n=e?a.bind(e):a;if(Array.isArray(b)){g=b.length;if(!g)return c(null,h);k=function(){q(d,function(a){a=m+a;a>=g||n(b[a],r(f))})}}else{var p=Object.keys(b);g=p.length;if(!g)return c(null,h);k=function(){q(d,function(a){a=m+a;a>=g||n(b[p[a]],r(f))})}}k()},multiEach:function(b,d,a){function c(c){c?(a(c),a=function(){}):k>=g&&l&&(a(null,b),a=function(){})}function e(a){var b=f[a];return function(d,f){if(d)return c(d);var q=e(a+1);b&&"object"==typeof f&&ma(f,function(a,d){if(a===h)return l=!0,c();g++; 30 | b(a,d,q)});if(a)return k++,c()}}a=a||function(){};var f=Array.isArray(d)?d:t(d);if(!d||!f.length)return a(null,b);var g=0,k=0,h={},l=!1;d=Array.isArray(b)?w(b):t(b);d.push(h);e(0)(null,d)},parallel:fa,series:ga,parallelLimit:S,waterfall:function(b,d,a){d=d||function(){};var c=function(){return a?function(b,c,d){function k(a){if(a)return d(a);var c=u(arguments,1);b.args=c;d(null,b)}var h=b.args||[];switch(h.length){case 0:return c.call(a,k);case 1:return c.call(a,h[0],k);case 2:return c.call(a,h[0], 31 | h[1],k);case 3:return c.call(a,h[0],h[1],h[2],k);case 4:return c.call(a,h[0],h[1],h[2],h[3],k);case 5:return c.call(a,h[0],h[1],h[2],h[3],h[4],k);default:return h.push(k),c.apply(a,h)}}:function(a,b,c){function d(b){if(b)return c(b);var f=u(arguments,1);a.args=f;c(null,a)}var h=a.args||[];switch(h.length){case 0:return b(d);case 1:return b(h[0],d);case 2:return b(h[0],h[1],d);case 3:return b(h[0],h[1],h[2],d);case 4:return b(h[0],h[1],h[2],h[3],d);case 5:return b(h[0],h[1],h[2],h[3],h[4],d);default:return h.push(d), 32 | b.apply(null,h)}}}();P(b,{},c,function(b,c){if(b)return d(b);var g=c.args||[];g.unshift(b);d.apply(a,g)})},whilst:function(b,d,a,c){function e(){b()?f(function(b){if(b)return a(b);e()}):a()}a=a||function(){};var f=c?d.bind(c):d;e()},doWhilst:function(b,d,a,c){function e(){f(function(b){if(b)return a(b);var f=u(arguments,1);d.apply(c,f)?e():a()})}a=a||function(){};var f=c?b.bind(c):b;e()},until:function(b,d,a,c){function e(){b()?a():f(function(b){if(b)return a(b);e()})}a=a||function(){};var f=c?d.bind(c): 33 | d;e()},doUntil:function(b,d,a,c){function e(){f(function(b){if(b)return a(b);var f=u(arguments,1);d.apply(c,f)?a():e()})}a=a||function(){};var f=c?b.bind(c):b;e()},forever:function(b,d,a){function c(){e(function(a){if(a)return d(a);c()})}d=d||function(){};var e=a?b.bind(a):b;c()},compose:function(){return ha.apply(null,la(arguments))},seq:ha,applyEach:ia(),applyEachSeries:ia("series"),queue:function(b,d,a){function c(a,b,c){var d=Array.isArray(a)?a:[a];a&&d.length?(e.started=!0,b="function"==typeof b? 34 | b:null,s(d,function(a){a={task:a,callback:b};c?e.tasks.unshift(a):e.tasks.push(a);"function"==typeof e.saturated&&e.length()>=e.concurrency&&e.saturated();x(e.process)})):e.idle()&&"function"==typeof e.drain&&x(e.drain)}var e=ja(b,d,a);e.unshift=function(a,b){c(a,b,!0)};e.push=function(a,b){c(a,b)};return e},priorityQueue:ja,cargo:function(b,d){var a={tasks:[],payload:d,saturated:null,empty:null,drain:null,drained:!0,push:function(b,c){b=Array.isArray(b)?b:[b];c="function"==typeof c?c:null;s(b,function(b){a.tasks.push({data:b, 35 | callback:c});a.drained=!1;"function"==typeof a.saturated&&a.length()===a.payload&&a.saturated()});x(a.process)},process:function(){if(!c)if(a.length()){var e="number"==typeof a.payload?a.tasks.splice(0,d):a.tasks,f=V(e,"data");a.length()||"function"!=typeof a.empty||a.empty();c=!0;b(f,function(){c=!1;var b=arguments;s(e,function(a){a.callback&&a.callback.apply(null,b)});a.process()})}else"function"!=typeof a.drain||a.drained||a.drain(),a.drained=!0},length:function(){return a.tasks.length},running:function(){return c}}, 36 | c=!1;return a},auto:function(b,d){function a(a){g.unshift(a)}function c(){f--;s(g.slice(0),function(a){a()})}d=d?r(d):function(){};var e=Object.keys(b),f=e.length;if(!f)return d();var g=[],k={};a(function(){f||d(null,k)});v(b,function(b,e){function f(a){var b=u(arguments,1);1>=b.length&&(b=b[0]);if(a){var g=B(k);g[e]=b;return d(a,g)}k[e]=b;x(c)}function n(){return!k.hasOwnProperty(e)&&U(s,function(a){return k.hasOwnProperty(a)})}function p(){if(n()){var a;a:{a=-1;for(var b=g.length;++a=b&&(a(null,f),a=function(){})}}a=a||function(){};var f=[];if(1> 39 | b)return a(null,f);var g=0,k=c?d.bind(c):d;q(b,function(a){k(a,e(a))})},timesSeries:ka,timesLimit:function(b,d,a,c,e){function f(a){var e=!1;return function(f,l){if(e)throw Error("Callback was already called.");e=!0;g[a]=l;f?(c(f),c=function(){}):++k>=b?(c(null,g),c=function(){}):k>=h+d&&(h=k,m())}}c=c||function(){};var g=[];if(1>b)return c(null,g);var k=0,h=0,l=e?a.bind(e):a,m=function(){q(d,function(a){a=h+a;a>=b||l(a,f(a))})};m()},memoize:function(b,d,a){d=d||function(a){return a};var c={},e={}, 40 | f=function(){function f(){var b=F(arguments);c[l]=b;var d=e[l];delete e[l];for(var g=-1,h=d.length;++ga||typeof i=="undefined")return 1;if(ie?0:e);++r=b&&i===n,l=[];if(f){var p=o(r);p?(i=t,r=p):f=false}for(;++ui(r,p)&&l.push(p);return f&&c(r),l}function ut(n,t,e,r){r=(r||0)-1;for(var u=n?n.length:0,o=[];++r=b&&f===n,h=u||v?a():s; 18 | for(v&&(h=o(h),f=t);++if(h,y))&&((u||v)&&h.push(y),s.push(g))}return v?(l(h.k),c(h)):u&&l(h),s}function lt(n){return function(t,e,r){var u={};e=J.createCallback(e,r,3),r=-1;var o=t?t.length:0;if(typeof o=="number")for(;++re?Ie(0,o+e):e)||0,Te(n)?i=-1o&&(o=a)}}else t=null==t&&kt(n)?r:J.createCallback(t,e,3),St(n,function(n,e,r){e=t(n,e,r),e>u&&(u=e,o=n)});return o}function Dt(n,t,e,r){if(!n)return e;var u=3>arguments.length;t=J.createCallback(t,r,4);var o=-1,i=n.length;if(typeof i=="number")for(u&&(e=n[++o]);++oarguments.length;return t=J.createCallback(t,r,4),Et(n,function(n,r,o){e=u?(u=false,n):t(e,n,r,o)}),e}function Tt(n){var t=-1,e=n?n.length:0,r=Xt(typeof e=="number"?e:0);return St(n,function(n){var e=at(0,++t);r[t]=r[e],r[e]=n}),r}function Ft(n,t,e){var r;t=J.createCallback(t,e,3),e=-1;var u=n?n.length:0;if(typeof u=="number")for(;++er?Ie(0,u+r):r||0}else if(r)return r=zt(t,e),t[r]===e?r:-1;return n(t,e,r)}function qt(n,t,e){if(typeof t!="number"&&null!=t){var r=0,u=-1,o=n?n.length:0;for(t=J.createCallback(t,e,3);++u>>1,e(n[r])e?0:e);++t=v; 29 | m?(i&&(i=ve(i)),s=f,a=n.apply(l,o)):i||(i=_e(r,v))}return m&&c?c=ve(c):c||t===h||(c=_e(u,t)),e&&(m=true,a=n.apply(l,o)),!m||c||i||(o=l=null),a}}function Ut(n){return n}function Gt(n,t,e){var r=true,u=t&&bt(t);t&&(e||u.length)||(null==e&&(e=t),o=Q,t=n,n=J,u=bt(t)),false===e?r=false:wt(e)&&"chain"in e&&(r=e.chain);var o=n,i=dt(o);St(u,function(e){var u=n[e]=t[e];i&&(o.prototype[e]=function(){var t=this.__chain__,e=this.__wrapped__,i=[e];if(be.apply(i,arguments),i=u.apply(n,i),r||t){if(e===i&&wt(i))return this; 30 | i=new o(i),i.__chain__=t}return i})})}function Ht(){}function Jt(n){return function(t){return t[n]}}function Qt(){return this.__wrapped__}e=e?Y.defaults(G.Object(),e,Y.pick(G,A)):G;var Xt=e.Array,Yt=e.Boolean,Zt=e.Date,ne=e.Function,te=e.Math,ee=e.Number,re=e.Object,ue=e.RegExp,oe=e.String,ie=e.TypeError,ae=[],fe=re.prototype,le=e._,ce=fe.toString,pe=ue("^"+oe(ce).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),se=te.ceil,ve=e.clearTimeout,he=te.floor,ge=ne.prototype.toString,ye=vt(ye=re.getPrototypeOf)&&ye,me=fe.hasOwnProperty,be=ae.push,_e=e.setTimeout,de=ae.splice,we=ae.unshift,je=function(){try{var n={},t=vt(t=re.defineProperty)&&t,e=t(n,n,n)&&t 31 | }catch(r){}return e}(),ke=vt(ke=re.create)&&ke,xe=vt(xe=Xt.isArray)&&xe,Ce=e.isFinite,Oe=e.isNaN,Ne=vt(Ne=re.keys)&&Ne,Ie=te.max,Se=te.min,Ee=e.parseInt,Re=te.random,Ae={};Ae[$]=Xt,Ae[T]=Yt,Ae[F]=Zt,Ae[B]=ne,Ae[q]=re,Ae[W]=ee,Ae[z]=ue,Ae[P]=oe,Q.prototype=J.prototype;var De=J.support={};De.funcDecomp=!vt(e.a)&&E.test(s),De.funcNames=typeof ne.name=="string",J.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:N,variable:"",imports:{_:J}},ke||(nt=function(){function n(){}return function(t){if(wt(t)){n.prototype=t; 32 | var r=new n;n.prototype=null}return r||e.Object()}}());var $e=je?function(n,t){M.value=t,je(n,"__bindData__",M)}:Ht,Te=xe||function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&ce.call(n)==$||false},Fe=Ne?function(n){return wt(n)?Ne(n):[]}:H,Be={"&":"&","<":"<",">":">",'"':""","'":"'"},We=_t(Be),qe=ue("("+Fe(We).join("|")+")","g"),ze=ue("["+Fe(Be).join("")+"]","g"),Pe=ye?function(n){if(!n||ce.call(n)!=q)return false;var t=n.valueOf,e=vt(t)&&(e=ye(t))&&ye(e);return e?n==e||ye(n)==e:ht(n) 33 | }:ht,Ke=lt(function(n,t,e){me.call(n,e)?n[e]++:n[e]=1}),Le=lt(function(n,t,e){(me.call(n,e)?n[e]:n[e]=[]).push(t)}),Me=lt(function(n,t,e){n[e]=t}),Ve=Rt,Ue=vt(Ue=Zt.now)&&Ue||function(){return(new Zt).getTime()},Ge=8==Ee(d+"08")?Ee:function(n,t){return Ee(kt(n)?n.replace(I,""):n,t||0)};return J.after=function(n,t){if(!dt(t))throw new ie;return function(){return 1>--n?t.apply(this,arguments):void 0}},J.assign=U,J.at=function(n){for(var t=arguments,e=-1,r=ut(t,true,false,1),t=t[2]&&t[2][t[1]]===n?1:r.length,u=Xt(t);++e=b&&o(r?e[r]:s)))}var p=e[0],h=-1,g=p?p.length:0,y=[];n:for(;++h(m?t(m,v):f(s,v))){for(r=u,(m||s).push(v);--r;)if(m=i[r],0>(m?t(m,v):f(e[r],v)))continue n;y.push(v)}}for(;u--;)(m=i[u])&&c(m);return l(i),l(s),y},J.invert=_t,J.invoke=function(n,t){var e=p(arguments,2),r=-1,u=typeof t=="function",o=n?n.length:0,i=Xt(typeof o=="number"?o:0);return St(n,function(n){i[++r]=(u?t:n[t]).apply(n,e)}),i},J.keys=Fe,J.map=Rt,J.mapValues=function(n,t,e){var r={}; 39 | return t=J.createCallback(t,e,3),h(n,function(n,e,u){r[e]=t(n,e,u)}),r},J.max=At,J.memoize=function(n,t){function e(){var r=e.cache,u=t?t.apply(this,arguments):m+arguments[0];return me.call(r,u)?r[u]:r[u]=n.apply(this,arguments)}if(!dt(n))throw new ie;return e.cache={},e},J.merge=function(n){var t=arguments,e=2;if(!wt(n))return n;if("number"!=typeof t[2]&&(e=t.length),3e?Ie(0,r+e):Se(e,r-1))+1);r--;)if(n[r]===t)return r;return-1},J.mixin=Gt,J.noConflict=function(){return e._=le,this},J.noop=Ht,J.now=Ue,J.parseInt=Ge,J.random=function(n,t,e){var r=null==n,u=null==t;return null==e&&(typeof n=="boolean"&&u?(e=n,n=1):u||typeof t!="boolean"||(e=t,u=true)),r&&u&&(t=1),n=+n||0,u?(t=n,n=0):t=+t||0,e||n%1||t%1?(e=Re(),Se(n+e*(t-n+parseFloat("1e-"+((e+"").length-1))),t)):at(n,t) 50 | },J.reduce=Dt,J.reduceRight=$t,J.result=function(n,t){if(n){var e=n[t];return dt(e)?n[t]():e}},J.runInContext=s,J.size=function(n){var t=n?n.length:0;return typeof t=="number"?t:Fe(n).length},J.some=Ft,J.sortedIndex=zt,J.template=function(n,t,e){var r=J.templateSettings;n=oe(n||""),e=_({},e,r);var u,o=_({},e.imports,r.imports),r=Fe(o),o=xt(o),a=0,f=e.interpolate||S,l="__p+='",f=ue((e.escape||S).source+"|"+f.source+"|"+(f===N?x:S).source+"|"+(e.evaluate||S).source+"|$","g");n.replace(f,function(t,e,r,o,f,c){return r||(r=o),l+=n.slice(a,c).replace(R,i),e&&(l+="'+__e("+e+")+'"),f&&(u=true,l+="';"+f+";\n__p+='"),r&&(l+="'+((__t=("+r+"))==null?'':__t)+'"),a=c+t.length,t 51 | }),l+="';",f=e=e.variable,f||(e="obj",l="with("+e+"){"+l+"}"),l=(u?l.replace(w,""):l).replace(j,"$1").replace(k,"$1;"),l="function("+e+"){"+(f?"":e+"||("+e+"={});")+"var __t,__p='',__e=_.escape"+(u?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}";try{var c=ne(r,"return "+l).apply(v,o)}catch(p){throw p.source=l,p}return t?c(t):(c.source=l,c)},J.unescape=function(n){return null==n?"":oe(n).replace(qe,gt)},J.uniqueId=function(n){var t=++y;return oe(null==n?"":n)+t 52 | },J.all=Ot,J.any=Ft,J.detect=It,J.findWhere=It,J.foldl=Dt,J.foldr=$t,J.include=Ct,J.inject=Dt,Gt(function(){var n={};return h(J,function(t,e){J.prototype[e]||(n[e]=t)}),n}(),false),J.first=Bt,J.last=function(n,t,e){var r=0,u=n?n.length:0;if(typeof t!="number"&&null!=t){var o=u;for(t=J.createCallback(t,e,3);o--&&t(n[o],o,n);)r++}else if(r=t,null==r||e)return n?n[u-1]:v;return p(n,Ie(0,u-r))},J.sample=function(n,t,e){return n&&typeof n.length!="number"&&(n=xt(n)),null==t||e?n?n[at(0,n.length-1)]:v:(n=Tt(n),n.length=Se(Ie(0,t),n.length),n) 53 | },J.take=Bt,J.head=Bt,h(J,function(n,t){var e="sample"!==t;J.prototype[t]||(J.prototype[t]=function(t,r){var u=this.__chain__,o=n(this.__wrapped__,t,r);return u||null!=t&&(!r||e&&typeof t=="function")?new Q(o,u):o})}),J.VERSION="2.4.1",J.prototype.chain=function(){return this.__chain__=true,this},J.prototype.toString=function(){return oe(this.__wrapped__)},J.prototype.value=Qt,J.prototype.valueOf=Qt,St(["join","pop","shift"],function(n){var t=ae[n];J.prototype[n]=function(){var n=this.__chain__,e=t.apply(this.__wrapped__,arguments); 54 | return n?new Q(e,n):e}}),St(["push","reverse","sort","unshift"],function(n){var t=ae[n];J.prototype[n]=function(){return t.apply(this.__wrapped__,arguments),this}}),St(["concat","slice","splice"],function(n){var t=ae[n];J.prototype[n]=function(){return new Q(t.apply(this.__wrapped__,arguments),this.__chain__)}}),J}var v,h=[],g=[],y=0,m=+new Date+"",b=75,_=40,d=" \t\x0B\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",w=/\b__p\+='';/g,j=/\b(__p\+=)''\+/g,k=/(__e\(.*?\)|\b__t\))\+'';/g,x=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,C=/\w*$/,O=/^\s*function[ \n\r\t]+\w/,N=/<%=([\s\S]+?)%>/g,I=RegExp("^["+d+"]*0+(?=.$)"),S=/($^)/,E=/\bthis\b/,R=/['\n\r\t\u2028\u2029\\]/g,A="Array Boolean Date Function Math Number Object RegExp String _ attachEvent clearTimeout isFinite isNaN parseInt setTimeout".split(" "),D="[object Arguments]",$="[object Array]",T="[object Boolean]",F="[object Date]",B="[object Function]",W="[object Number]",q="[object Object]",z="[object RegExp]",P="[object String]",K={}; 55 | K[B]=false,K[D]=K[$]=K[T]=K[F]=K[W]=K[q]=K[z]=K[P]=true;var L={leading:false,maxWait:0,trailing:false},M={configurable:false,enumerable:false,value:null,writable:false},V={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false},U={"\\":"\\","'":"'","\n":"n","\r":"r","\t":"t","\u2028":"u2028","\u2029":"u2029"},G=V[typeof window]&&window||this,H=V[typeof exports]&&exports&&!exports.nodeType&&exports,J=V[typeof module]&&module&&!module.nodeType&&module,Q=J&&J.exports===H&&H,X=V[typeof global]&&global;!X||X.global!==X&&X.window!==X||(G=X); 56 | var Y=s();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(G._=Y, define(function(){return Y})):H&&J?Q?(J.exports=Y)._=Y:H._=Y:G._=Y}).call(this); -------------------------------------------------------------------------------- /public/js/async.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * async 3 | * https://github.com/caolan/async 4 | * 5 | * Copyright 2010-2014 Caolan McMahon 6 | * Released under the MIT license 7 | */ 8 | /*jshint onevar: false, indent:4 */ 9 | /*global setImmediate: false, setTimeout: false, console: false */ 10 | (function () { 11 | 12 | var async = {}; 13 | 14 | // global on the server, window in the browser 15 | var root, previous_async; 16 | 17 | root = this; 18 | if (root != null) { 19 | previous_async = root.async; 20 | } 21 | 22 | async.noConflict = function () { 23 | root.async = previous_async; 24 | return async; 25 | }; 26 | 27 | function only_once(fn) { 28 | var called = false; 29 | return function() { 30 | if (called) throw new Error("Callback was already called."); 31 | called = true; 32 | fn.apply(root, arguments); 33 | } 34 | } 35 | 36 | //// cross-browser compatiblity functions //// 37 | 38 | var _toString = Object.prototype.toString; 39 | 40 | var _isArray = Array.isArray || function (obj) { 41 | return _toString.call(obj) === '[object Array]'; 42 | }; 43 | 44 | var _each = function (arr, iterator) { 45 | if (arr.forEach) { 46 | return arr.forEach(iterator); 47 | } 48 | for (var i = 0; i < arr.length; i += 1) { 49 | iterator(arr[i], i, arr); 50 | } 51 | }; 52 | 53 | var _map = function (arr, iterator) { 54 | if (arr.map) { 55 | return arr.map(iterator); 56 | } 57 | var results = []; 58 | _each(arr, function (x, i, a) { 59 | results.push(iterator(x, i, a)); 60 | }); 61 | return results; 62 | }; 63 | 64 | var _reduce = function (arr, iterator, memo) { 65 | if (arr.reduce) { 66 | return arr.reduce(iterator, memo); 67 | } 68 | _each(arr, function (x, i, a) { 69 | memo = iterator(memo, x, i, a); 70 | }); 71 | return memo; 72 | }; 73 | 74 | var _keys = function (obj) { 75 | if (Object.keys) { 76 | return Object.keys(obj); 77 | } 78 | var keys = []; 79 | for (var k in obj) { 80 | if (obj.hasOwnProperty(k)) { 81 | keys.push(k); 82 | } 83 | } 84 | return keys; 85 | }; 86 | 87 | //// exported async module functions //// 88 | 89 | //// nextTick implementation with browser-compatible fallback //// 90 | if (typeof process === 'undefined' || !(process.nextTick)) { 91 | if (typeof setImmediate === 'function') { 92 | async.nextTick = function (fn) { 93 | // not a direct alias for IE10 compatibility 94 | setImmediate(fn); 95 | }; 96 | async.setImmediate = async.nextTick; 97 | } 98 | else { 99 | async.nextTick = function (fn) { 100 | setTimeout(fn, 0); 101 | }; 102 | async.setImmediate = async.nextTick; 103 | } 104 | } 105 | else { 106 | async.nextTick = process.nextTick; 107 | if (typeof setImmediate !== 'undefined') { 108 | async.setImmediate = function (fn) { 109 | // not a direct alias for IE10 compatibility 110 | setImmediate(fn); 111 | }; 112 | } 113 | else { 114 | async.setImmediate = async.nextTick; 115 | } 116 | } 117 | 118 | async.each = function (arr, iterator, callback) { 119 | callback = callback || function () {}; 120 | if (!arr.length) { 121 | return callback(); 122 | } 123 | var completed = 0; 124 | _each(arr, function (x) { 125 | iterator(x, only_once(done) ); 126 | }); 127 | function done(err) { 128 | if (err) { 129 | callback(err); 130 | callback = function () {}; 131 | } 132 | else { 133 | completed += 1; 134 | if (completed >= arr.length) { 135 | callback(); 136 | } 137 | } 138 | } 139 | }; 140 | async.forEach = async.each; 141 | 142 | async.eachSeries = function (arr, iterator, callback) { 143 | callback = callback || function () {}; 144 | if (!arr.length) { 145 | return callback(); 146 | } 147 | var completed = 0; 148 | var iterate = function () { 149 | iterator(arr[completed], function (err) { 150 | if (err) { 151 | callback(err); 152 | callback = function () {}; 153 | } 154 | else { 155 | completed += 1; 156 | if (completed >= arr.length) { 157 | callback(); 158 | } 159 | else { 160 | iterate(); 161 | } 162 | } 163 | }); 164 | }; 165 | iterate(); 166 | }; 167 | async.forEachSeries = async.eachSeries; 168 | 169 | async.eachLimit = function (arr, limit, iterator, callback) { 170 | var fn = _eachLimit(limit); 171 | fn.apply(null, [arr, iterator, callback]); 172 | }; 173 | async.forEachLimit = async.eachLimit; 174 | 175 | var _eachLimit = function (limit) { 176 | 177 | return function (arr, iterator, callback) { 178 | callback = callback || function () {}; 179 | if (!arr.length || limit <= 0) { 180 | return callback(); 181 | } 182 | var completed = 0; 183 | var started = 0; 184 | var running = 0; 185 | 186 | (function replenish () { 187 | if (completed >= arr.length) { 188 | return callback(); 189 | } 190 | 191 | while (running < limit && started < arr.length) { 192 | started += 1; 193 | running += 1; 194 | iterator(arr[started - 1], function (err) { 195 | if (err) { 196 | callback(err); 197 | callback = function () {}; 198 | } 199 | else { 200 | completed += 1; 201 | running -= 1; 202 | if (completed >= arr.length) { 203 | callback(); 204 | } 205 | else { 206 | replenish(); 207 | } 208 | } 209 | }); 210 | } 211 | })(); 212 | }; 213 | }; 214 | 215 | 216 | var doParallel = function (fn) { 217 | return function () { 218 | var args = Array.prototype.slice.call(arguments); 219 | return fn.apply(null, [async.each].concat(args)); 220 | }; 221 | }; 222 | var doParallelLimit = function(limit, fn) { 223 | return function () { 224 | var args = Array.prototype.slice.call(arguments); 225 | return fn.apply(null, [_eachLimit(limit)].concat(args)); 226 | }; 227 | }; 228 | var doSeries = function (fn) { 229 | return function () { 230 | var args = Array.prototype.slice.call(arguments); 231 | return fn.apply(null, [async.eachSeries].concat(args)); 232 | }; 233 | }; 234 | 235 | 236 | var _asyncMap = function (eachfn, arr, iterator, callback) { 237 | arr = _map(arr, function (x, i) { 238 | return {index: i, value: x}; 239 | }); 240 | if (!callback) { 241 | eachfn(arr, function (x, callback) { 242 | iterator(x.value, function (err) { 243 | callback(err); 244 | }); 245 | }); 246 | } else { 247 | var results = []; 248 | eachfn(arr, function (x, callback) { 249 | iterator(x.value, function (err, v) { 250 | results[x.index] = v; 251 | callback(err); 252 | }); 253 | }, function (err) { 254 | callback(err, results); 255 | }); 256 | } 257 | }; 258 | async.map = doParallel(_asyncMap); 259 | async.mapSeries = doSeries(_asyncMap); 260 | async.mapLimit = function (arr, limit, iterator, callback) { 261 | return _mapLimit(limit)(arr, iterator, callback); 262 | }; 263 | 264 | var _mapLimit = function(limit) { 265 | return doParallelLimit(limit, _asyncMap); 266 | }; 267 | 268 | // reduce only has a series version, as doing reduce in parallel won't 269 | // work in many situations. 270 | async.reduce = function (arr, memo, iterator, callback) { 271 | async.eachSeries(arr, function (x, callback) { 272 | iterator(memo, x, function (err, v) { 273 | memo = v; 274 | callback(err); 275 | }); 276 | }, function (err) { 277 | callback(err, memo); 278 | }); 279 | }; 280 | // inject alias 281 | async.inject = async.reduce; 282 | // foldl alias 283 | async.foldl = async.reduce; 284 | 285 | async.reduceRight = function (arr, memo, iterator, callback) { 286 | var reversed = _map(arr, function (x) { 287 | return x; 288 | }).reverse(); 289 | async.reduce(reversed, memo, iterator, callback); 290 | }; 291 | // foldr alias 292 | async.foldr = async.reduceRight; 293 | 294 | var _filter = function (eachfn, arr, iterator, callback) { 295 | var results = []; 296 | arr = _map(arr, function (x, i) { 297 | return {index: i, value: x}; 298 | }); 299 | eachfn(arr, function (x, callback) { 300 | iterator(x.value, function (v) { 301 | if (v) { 302 | results.push(x); 303 | } 304 | callback(); 305 | }); 306 | }, function (err) { 307 | callback(_map(results.sort(function (a, b) { 308 | return a.index - b.index; 309 | }), function (x) { 310 | return x.value; 311 | })); 312 | }); 313 | }; 314 | async.filter = doParallel(_filter); 315 | async.filterSeries = doSeries(_filter); 316 | // select alias 317 | async.select = async.filter; 318 | async.selectSeries = async.filterSeries; 319 | 320 | var _reject = function (eachfn, arr, iterator, callback) { 321 | var results = []; 322 | arr = _map(arr, function (x, i) { 323 | return {index: i, value: x}; 324 | }); 325 | eachfn(arr, function (x, callback) { 326 | iterator(x.value, function (v) { 327 | if (!v) { 328 | results.push(x); 329 | } 330 | callback(); 331 | }); 332 | }, function (err) { 333 | callback(_map(results.sort(function (a, b) { 334 | return a.index - b.index; 335 | }), function (x) { 336 | return x.value; 337 | })); 338 | }); 339 | }; 340 | async.reject = doParallel(_reject); 341 | async.rejectSeries = doSeries(_reject); 342 | 343 | var _detect = function (eachfn, arr, iterator, main_callback) { 344 | eachfn(arr, function (x, callback) { 345 | iterator(x, function (result) { 346 | if (result) { 347 | main_callback(x); 348 | main_callback = function () {}; 349 | } 350 | else { 351 | callback(); 352 | } 353 | }); 354 | }, function (err) { 355 | main_callback(); 356 | }); 357 | }; 358 | async.detect = doParallel(_detect); 359 | async.detectSeries = doSeries(_detect); 360 | 361 | async.some = function (arr, iterator, main_callback) { 362 | async.each(arr, function (x, callback) { 363 | iterator(x, function (v) { 364 | if (v) { 365 | main_callback(true); 366 | main_callback = function () {}; 367 | } 368 | callback(); 369 | }); 370 | }, function (err) { 371 | main_callback(false); 372 | }); 373 | }; 374 | // any alias 375 | async.any = async.some; 376 | 377 | async.every = function (arr, iterator, main_callback) { 378 | async.each(arr, function (x, callback) { 379 | iterator(x, function (v) { 380 | if (!v) { 381 | main_callback(false); 382 | main_callback = function () {}; 383 | } 384 | callback(); 385 | }); 386 | }, function (err) { 387 | main_callback(true); 388 | }); 389 | }; 390 | // all alias 391 | async.all = async.every; 392 | 393 | async.sortBy = function (arr, iterator, callback) { 394 | async.map(arr, function (x, callback) { 395 | iterator(x, function (err, criteria) { 396 | if (err) { 397 | callback(err); 398 | } 399 | else { 400 | callback(null, {value: x, criteria: criteria}); 401 | } 402 | }); 403 | }, function (err, results) { 404 | if (err) { 405 | return callback(err); 406 | } 407 | else { 408 | var fn = function (left, right) { 409 | var a = left.criteria, b = right.criteria; 410 | return a < b ? -1 : a > b ? 1 : 0; 411 | }; 412 | callback(null, _map(results.sort(fn), function (x) { 413 | return x.value; 414 | })); 415 | } 416 | }); 417 | }; 418 | 419 | async.auto = function (tasks, callback) { 420 | callback = callback || function () {}; 421 | var keys = _keys(tasks); 422 | var remainingTasks = keys.length 423 | if (!remainingTasks) { 424 | return callback(); 425 | } 426 | 427 | var results = {}; 428 | 429 | var listeners = []; 430 | var addListener = function (fn) { 431 | listeners.unshift(fn); 432 | }; 433 | var removeListener = function (fn) { 434 | for (var i = 0; i < listeners.length; i += 1) { 435 | if (listeners[i] === fn) { 436 | listeners.splice(i, 1); 437 | return; 438 | } 439 | } 440 | }; 441 | var taskComplete = function () { 442 | remainingTasks-- 443 | _each(listeners.slice(0), function (fn) { 444 | fn(); 445 | }); 446 | }; 447 | 448 | addListener(function () { 449 | if (!remainingTasks) { 450 | var theCallback = callback; 451 | // prevent final callback from calling itself if it errors 452 | callback = function () {}; 453 | 454 | theCallback(null, results); 455 | } 456 | }); 457 | 458 | _each(keys, function (k) { 459 | var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]]; 460 | var taskCallback = function (err) { 461 | var args = Array.prototype.slice.call(arguments, 1); 462 | if (args.length <= 1) { 463 | args = args[0]; 464 | } 465 | if (err) { 466 | var safeResults = {}; 467 | _each(_keys(results), function(rkey) { 468 | safeResults[rkey] = results[rkey]; 469 | }); 470 | safeResults[k] = args; 471 | callback(err, safeResults); 472 | // stop subsequent errors hitting callback multiple times 473 | callback = function () {}; 474 | } 475 | else { 476 | results[k] = args; 477 | async.setImmediate(taskComplete); 478 | } 479 | }; 480 | var requires = task.slice(0, Math.abs(task.length - 1)) || []; 481 | var ready = function () { 482 | return _reduce(requires, function (a, x) { 483 | return (a && results.hasOwnProperty(x)); 484 | }, true) && !results.hasOwnProperty(k); 485 | }; 486 | if (ready()) { 487 | task[task.length - 1](taskCallback, results); 488 | } 489 | else { 490 | var listener = function () { 491 | if (ready()) { 492 | removeListener(listener); 493 | task[task.length - 1](taskCallback, results); 494 | } 495 | }; 496 | addListener(listener); 497 | } 498 | }); 499 | }; 500 | 501 | async.retry = function(times, task, callback) { 502 | var DEFAULT_TIMES = 5; 503 | var attempts = []; 504 | // Use defaults if times not passed 505 | if (typeof times === 'function') { 506 | callback = task; 507 | task = times; 508 | times = DEFAULT_TIMES; 509 | } 510 | // Make sure times is a number 511 | times = parseInt(times, 10) || DEFAULT_TIMES; 512 | var wrappedTask = function(wrappedCallback, wrappedResults) { 513 | var retryAttempt = function(task, finalAttempt) { 514 | return function(seriesCallback) { 515 | task(function(err, result){ 516 | seriesCallback(!err || finalAttempt, {err: err, result: result}); 517 | }, wrappedResults); 518 | }; 519 | }; 520 | while (times) { 521 | attempts.push(retryAttempt(task, !(times-=1))); 522 | } 523 | async.series(attempts, function(done, data){ 524 | data = data[data.length - 1]; 525 | (wrappedCallback || callback)(data.err, data.result); 526 | }); 527 | } 528 | // If a callback is passed, run this as a controll flow 529 | return callback ? wrappedTask() : wrappedTask 530 | }; 531 | 532 | async.waterfall = function (tasks, callback) { 533 | callback = callback || function () {}; 534 | if (!_isArray(tasks)) { 535 | var err = new Error('First argument to waterfall must be an array of functions'); 536 | return callback(err); 537 | } 538 | if (!tasks.length) { 539 | return callback(); 540 | } 541 | var wrapIterator = function (iterator) { 542 | return function (err) { 543 | if (err) { 544 | callback.apply(null, arguments); 545 | callback = function () {}; 546 | } 547 | else { 548 | var args = Array.prototype.slice.call(arguments, 1); 549 | var next = iterator.next(); 550 | if (next) { 551 | args.push(wrapIterator(next)); 552 | } 553 | else { 554 | args.push(callback); 555 | } 556 | async.setImmediate(function () { 557 | iterator.apply(null, args); 558 | }); 559 | } 560 | }; 561 | }; 562 | wrapIterator(async.iterator(tasks))(); 563 | }; 564 | 565 | var _parallel = function(eachfn, tasks, callback) { 566 | callback = callback || function () {}; 567 | if (_isArray(tasks)) { 568 | eachfn.map(tasks, function (fn, callback) { 569 | if (fn) { 570 | fn(function (err) { 571 | var args = Array.prototype.slice.call(arguments, 1); 572 | if (args.length <= 1) { 573 | args = args[0]; 574 | } 575 | callback.call(null, err, args); 576 | }); 577 | } 578 | }, callback); 579 | } 580 | else { 581 | var results = {}; 582 | eachfn.each(_keys(tasks), function (k, callback) { 583 | tasks[k](function (err) { 584 | var args = Array.prototype.slice.call(arguments, 1); 585 | if (args.length <= 1) { 586 | args = args[0]; 587 | } 588 | results[k] = args; 589 | callback(err); 590 | }); 591 | }, function (err) { 592 | callback(err, results); 593 | }); 594 | } 595 | }; 596 | 597 | async.parallel = function (tasks, callback) { 598 | _parallel({ map: async.map, each: async.each }, tasks, callback); 599 | }; 600 | 601 | async.parallelLimit = function(tasks, limit, callback) { 602 | _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback); 603 | }; 604 | 605 | async.series = function (tasks, callback) { 606 | callback = callback || function () {}; 607 | if (_isArray(tasks)) { 608 | async.mapSeries(tasks, function (fn, callback) { 609 | if (fn) { 610 | fn(function (err) { 611 | var args = Array.prototype.slice.call(arguments, 1); 612 | if (args.length <= 1) { 613 | args = args[0]; 614 | } 615 | callback.call(null, err, args); 616 | }); 617 | } 618 | }, callback); 619 | } 620 | else { 621 | var results = {}; 622 | async.eachSeries(_keys(tasks), function (k, callback) { 623 | tasks[k](function (err) { 624 | var args = Array.prototype.slice.call(arguments, 1); 625 | if (args.length <= 1) { 626 | args = args[0]; 627 | } 628 | results[k] = args; 629 | callback(err); 630 | }); 631 | }, function (err) { 632 | callback(err, results); 633 | }); 634 | } 635 | }; 636 | 637 | async.iterator = function (tasks) { 638 | var makeCallback = function (index) { 639 | var fn = function () { 640 | if (tasks.length) { 641 | tasks[index].apply(null, arguments); 642 | } 643 | return fn.next(); 644 | }; 645 | fn.next = function () { 646 | return (index < tasks.length - 1) ? makeCallback(index + 1): null; 647 | }; 648 | return fn; 649 | }; 650 | return makeCallback(0); 651 | }; 652 | 653 | async.apply = function (fn) { 654 | var args = Array.prototype.slice.call(arguments, 1); 655 | return function () { 656 | return fn.apply( 657 | null, args.concat(Array.prototype.slice.call(arguments)) 658 | ); 659 | }; 660 | }; 661 | 662 | var _concat = function (eachfn, arr, fn, callback) { 663 | var r = []; 664 | eachfn(arr, function (x, cb) { 665 | fn(x, function (err, y) { 666 | r = r.concat(y || []); 667 | cb(err); 668 | }); 669 | }, function (err) { 670 | callback(err, r); 671 | }); 672 | }; 673 | async.concat = doParallel(_concat); 674 | async.concatSeries = doSeries(_concat); 675 | 676 | async.whilst = function (test, iterator, callback) { 677 | if (test()) { 678 | iterator(function (err) { 679 | if (err) { 680 | return callback(err); 681 | } 682 | async.whilst(test, iterator, callback); 683 | }); 684 | } 685 | else { 686 | callback(); 687 | } 688 | }; 689 | 690 | async.doWhilst = function (iterator, test, callback) { 691 | iterator(function (err) { 692 | if (err) { 693 | return callback(err); 694 | } 695 | var args = Array.prototype.slice.call(arguments, 1); 696 | if (test.apply(null, args)) { 697 | async.doWhilst(iterator, test, callback); 698 | } 699 | else { 700 | callback(); 701 | } 702 | }); 703 | }; 704 | 705 | async.until = function (test, iterator, callback) { 706 | if (!test()) { 707 | iterator(function (err) { 708 | if (err) { 709 | return callback(err); 710 | } 711 | async.until(test, iterator, callback); 712 | }); 713 | } 714 | else { 715 | callback(); 716 | } 717 | }; 718 | 719 | async.doUntil = function (iterator, test, callback) { 720 | iterator(function (err) { 721 | if (err) { 722 | return callback(err); 723 | } 724 | var args = Array.prototype.slice.call(arguments, 1); 725 | if (!test.apply(null, args)) { 726 | async.doUntil(iterator, test, callback); 727 | } 728 | else { 729 | callback(); 730 | } 731 | }); 732 | }; 733 | 734 | async.queue = function (worker, concurrency) { 735 | if (concurrency === undefined) { 736 | concurrency = 1; 737 | } 738 | function _insert(q, data, pos, callback) { 739 | if (!q.started){ 740 | q.started = true; 741 | } 742 | if (!_isArray(data)) { 743 | data = [data]; 744 | } 745 | if(data.length == 0) { 746 | // call drain immediately if there are no tasks 747 | return async.setImmediate(function() { 748 | if (q.drain) { 749 | q.drain(); 750 | } 751 | }); 752 | } 753 | _each(data, function(task) { 754 | var item = { 755 | data: task, 756 | callback: typeof callback === 'function' ? callback : null 757 | }; 758 | 759 | if (pos) { 760 | q.tasks.unshift(item); 761 | } else { 762 | q.tasks.push(item); 763 | } 764 | 765 | if (q.saturated && q.tasks.length === q.concurrency) { 766 | q.saturated(); 767 | } 768 | async.setImmediate(q.process); 769 | }); 770 | } 771 | 772 | var workers = 0; 773 | var q = { 774 | tasks: [], 775 | concurrency: concurrency, 776 | saturated: null, 777 | empty: null, 778 | drain: null, 779 | started: false, 780 | paused: false, 781 | push: function (data, callback) { 782 | _insert(q, data, false, callback); 783 | }, 784 | kill: function () { 785 | q.drain = null; 786 | q.tasks = []; 787 | }, 788 | unshift: function (data, callback) { 789 | _insert(q, data, true, callback); 790 | }, 791 | process: function () { 792 | if (!q.paused && workers < q.concurrency && q.tasks.length) { 793 | var task = q.tasks.shift(); 794 | if (q.empty && q.tasks.length === 0) { 795 | q.empty(); 796 | } 797 | workers += 1; 798 | var next = function () { 799 | workers -= 1; 800 | if (task.callback) { 801 | task.callback.apply(task, arguments); 802 | } 803 | if (q.drain && q.tasks.length + workers === 0) { 804 | q.drain(); 805 | } 806 | q.process(); 807 | }; 808 | var cb = only_once(next); 809 | worker(task.data, cb); 810 | } 811 | }, 812 | length: function () { 813 | return q.tasks.length; 814 | }, 815 | running: function () { 816 | return workers; 817 | }, 818 | idle: function() { 819 | return q.tasks.length + workers === 0; 820 | }, 821 | pause: function () { 822 | if (q.paused === true) { return; } 823 | q.paused = true; 824 | q.process(); 825 | }, 826 | resume: function () { 827 | if (q.paused === false) { return; } 828 | q.paused = false; 829 | q.process(); 830 | } 831 | }; 832 | return q; 833 | }; 834 | 835 | async.priorityQueue = function (worker, concurrency) { 836 | 837 | function _compareTasks(a, b){ 838 | return a.priority - b.priority; 839 | }; 840 | 841 | function _binarySearch(sequence, item, compare) { 842 | var beg = -1, 843 | end = sequence.length - 1; 844 | while (beg < end) { 845 | var mid = beg + ((end - beg + 1) >>> 1); 846 | if (compare(item, sequence[mid]) >= 0) { 847 | beg = mid; 848 | } else { 849 | end = mid - 1; 850 | } 851 | } 852 | return beg; 853 | } 854 | 855 | function _insert(q, data, priority, callback) { 856 | if (!q.started){ 857 | q.started = true; 858 | } 859 | if (!_isArray(data)) { 860 | data = [data]; 861 | } 862 | if(data.length == 0) { 863 | // call drain immediately if there are no tasks 864 | return async.setImmediate(function() { 865 | if (q.drain) { 866 | q.drain(); 867 | } 868 | }); 869 | } 870 | _each(data, function(task) { 871 | var item = { 872 | data: task, 873 | priority: priority, 874 | callback: typeof callback === 'function' ? callback : null 875 | }; 876 | 877 | q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item); 878 | 879 | if (q.saturated && q.tasks.length === q.concurrency) { 880 | q.saturated(); 881 | } 882 | async.setImmediate(q.process); 883 | }); 884 | } 885 | 886 | // Start with a normal queue 887 | var q = async.queue(worker, concurrency); 888 | 889 | // Override push to accept second parameter representing priority 890 | q.push = function (data, priority, callback) { 891 | _insert(q, data, priority, callback); 892 | }; 893 | 894 | // Remove unshift function 895 | delete q.unshift; 896 | 897 | return q; 898 | }; 899 | 900 | async.cargo = function (worker, payload) { 901 | var working = false, 902 | tasks = []; 903 | 904 | var cargo = { 905 | tasks: tasks, 906 | payload: payload, 907 | saturated: null, 908 | empty: null, 909 | drain: null, 910 | drained: true, 911 | push: function (data, callback) { 912 | if (!_isArray(data)) { 913 | data = [data]; 914 | } 915 | _each(data, function(task) { 916 | tasks.push({ 917 | data: task, 918 | callback: typeof callback === 'function' ? callback : null 919 | }); 920 | cargo.drained = false; 921 | if (cargo.saturated && tasks.length === payload) { 922 | cargo.saturated(); 923 | } 924 | }); 925 | async.setImmediate(cargo.process); 926 | }, 927 | process: function process() { 928 | if (working) return; 929 | if (tasks.length === 0) { 930 | if(cargo.drain && !cargo.drained) cargo.drain(); 931 | cargo.drained = true; 932 | return; 933 | } 934 | 935 | var ts = typeof payload === 'number' 936 | ? tasks.splice(0, payload) 937 | : tasks.splice(0, tasks.length); 938 | 939 | var ds = _map(ts, function (task) { 940 | return task.data; 941 | }); 942 | 943 | if(cargo.empty) cargo.empty(); 944 | working = true; 945 | worker(ds, function () { 946 | working = false; 947 | 948 | var args = arguments; 949 | _each(ts, function (data) { 950 | if (data.callback) { 951 | data.callback.apply(null, args); 952 | } 953 | }); 954 | 955 | process(); 956 | }); 957 | }, 958 | length: function () { 959 | return tasks.length; 960 | }, 961 | running: function () { 962 | return working; 963 | } 964 | }; 965 | return cargo; 966 | }; 967 | 968 | var _console_fn = function (name) { 969 | return function (fn) { 970 | var args = Array.prototype.slice.call(arguments, 1); 971 | fn.apply(null, args.concat([function (err) { 972 | var args = Array.prototype.slice.call(arguments, 1); 973 | if (typeof console !== 'undefined') { 974 | if (err) { 975 | if (console.error) { 976 | console.error(err); 977 | } 978 | } 979 | else if (console[name]) { 980 | _each(args, function (x) { 981 | console[name](x); 982 | }); 983 | } 984 | } 985 | }])); 986 | }; 987 | }; 988 | async.log = _console_fn('log'); 989 | async.dir = _console_fn('dir'); 990 | /*async.info = _console_fn('info'); 991 | async.warn = _console_fn('warn'); 992 | async.error = _console_fn('error');*/ 993 | 994 | async.memoize = function (fn, hasher) { 995 | var memo = {}; 996 | var queues = {}; 997 | hasher = hasher || function (x) { 998 | return x; 999 | }; 1000 | var memoized = function () { 1001 | var args = Array.prototype.slice.call(arguments); 1002 | var callback = args.pop(); 1003 | var key = hasher.apply(null, args); 1004 | if (key in memo) { 1005 | async.nextTick(function () { 1006 | callback.apply(null, memo[key]); 1007 | }); 1008 | } 1009 | else if (key in queues) { 1010 | queues[key].push(callback); 1011 | } 1012 | else { 1013 | queues[key] = [callback]; 1014 | fn.apply(null, args.concat([function () { 1015 | memo[key] = arguments; 1016 | var q = queues[key]; 1017 | delete queues[key]; 1018 | for (var i = 0, l = q.length; i < l; i++) { 1019 | q[i].apply(null, arguments); 1020 | } 1021 | }])); 1022 | } 1023 | }; 1024 | memoized.memo = memo; 1025 | memoized.unmemoized = fn; 1026 | return memoized; 1027 | }; 1028 | 1029 | async.unmemoize = function (fn) { 1030 | return function () { 1031 | return (fn.unmemoized || fn).apply(null, arguments); 1032 | }; 1033 | }; 1034 | 1035 | async.times = function (count, iterator, callback) { 1036 | var counter = []; 1037 | for (var i = 0; i < count; i++) { 1038 | counter.push(i); 1039 | } 1040 | return async.map(counter, iterator, callback); 1041 | }; 1042 | 1043 | async.timesSeries = function (count, iterator, callback) { 1044 | var counter = []; 1045 | for (var i = 0; i < count; i++) { 1046 | counter.push(i); 1047 | } 1048 | return async.mapSeries(counter, iterator, callback); 1049 | }; 1050 | 1051 | async.seq = function (/* functions... */) { 1052 | var fns = arguments; 1053 | return function () { 1054 | var that = this; 1055 | var args = Array.prototype.slice.call(arguments); 1056 | var callback = args.pop(); 1057 | async.reduce(fns, args, function (newargs, fn, cb) { 1058 | fn.apply(that, newargs.concat([function () { 1059 | var err = arguments[0]; 1060 | var nextargs = Array.prototype.slice.call(arguments, 1); 1061 | cb(err, nextargs); 1062 | }])) 1063 | }, 1064 | function (err, results) { 1065 | callback.apply(that, [err].concat(results)); 1066 | }); 1067 | }; 1068 | }; 1069 | 1070 | async.compose = function (/* functions... */) { 1071 | return async.seq.apply(null, Array.prototype.reverse.call(arguments)); 1072 | }; 1073 | 1074 | var _applyEach = function (eachfn, fns /*args...*/) { 1075 | var go = function () { 1076 | var that = this; 1077 | var args = Array.prototype.slice.call(arguments); 1078 | var callback = args.pop(); 1079 | return eachfn(fns, function (fn, cb) { 1080 | fn.apply(that, args.concat([cb])); 1081 | }, 1082 | callback); 1083 | }; 1084 | if (arguments.length > 2) { 1085 | var args = Array.prototype.slice.call(arguments, 2); 1086 | return go.apply(this, args); 1087 | } 1088 | else { 1089 | return go; 1090 | } 1091 | }; 1092 | async.applyEach = doParallel(_applyEach); 1093 | async.applyEachSeries = doSeries(_applyEach); 1094 | 1095 | async.forever = function (fn, callback) { 1096 | function next(err) { 1097 | if (err) { 1098 | if (callback) { 1099 | return callback(err); 1100 | } 1101 | throw err; 1102 | } 1103 | fn(next); 1104 | } 1105 | next(); 1106 | }; 1107 | 1108 | // Node.js 1109 | if (typeof module !== 'undefined' && module.exports) { 1110 | module.exports = async; 1111 | } 1112 | // AMD / RequireJS 1113 | else if (typeof define !== 'undefined' && define.amd) { 1114 | define([], function () { 1115 | return async; 1116 | }); 1117 | } 1118 | // included directly via