diff --git a/app.js b/app.js
index dedcd90..847eb6e 100644
--- a/app.js
+++ b/app.js
@@ -8,6 +8,7 @@ var bodyParser = require('body-parser');
var index = require('./routes/index');
var success = require('./routes/success');
var temperature = require('./routes/temperature');
+var newpost = require('./routes/newpost');
var mongoose = require('mongoose');
var mainLoop = require('./main');
@@ -37,6 +38,7 @@ app.use(express.static(path.join(__dirname, 'public')));
app.use('/', index);
app.use('/success', success);
app.use('/temperature', temperature);
+app.use('/newpost', newpost);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
@@ -73,4 +75,4 @@ app.use(function(err, req, res, next) {
//////////////////Main program loop //////////////////
mainLoop.queryLoop;
-module.exports = app;
+module.exports = app;
\ No newline at end of file
diff --git a/models/posts.js b/models/posts.js
index b7a296d..22ce6a2 100644
--- a/models/posts.js
+++ b/models/posts.js
@@ -2,9 +2,10 @@ var mongoose = require('mongoose');
var postsSchema = new mongoose.Schema({
title: String,
- date: Date,
+ date: String,
intro: String,
- file: String,
+ path: String,
+ fileName: String,
updated: {type: Date, default: Date.now}
});
diff --git a/node_modules/mongo-express/.jshintignore b/node_modules/mongo-express/.jshintignore
new file mode 100644
index 0000000..196030b
--- /dev/null
+++ b/node_modules/mongo-express/.jshintignore
@@ -0,0 +1,11 @@
+*.md
+.gitignore
+.jshintrc
+.jshintignore
+.travis.yml
+
+Dockerfile
+node_modules
+package.json
+public
+test
diff --git a/node_modules/mongo-express/.jshintrc b/node_modules/mongo-express/.jshintrc
new file mode 100644
index 0000000..ffd7e2e
--- /dev/null
+++ b/node_modules/mongo-express/.jshintrc
@@ -0,0 +1,60 @@
+{
+ /*
+ * ENVIRONMENTS
+ * =================
+ */
+
+ // Define globals exposed by modern browsers, jQuery & Node.js.
+ "browser": true,
+ "jquery": true,
+ "node": true,
+
+ // Define globals used by M Project and Wherewolf
+ "predef": [
+ ],
+
+ /*
+ * ENFORCING OPTIONS
+ * =================
+ */
+
+ // Force all variable names to use either camelCase style or UPPER_CASE
+ // with underscores.
+ "camelcase": false,
+
+ // Prohibit use of == and != in favor of === and !==.
+ "eqeqeq": true,
+
+ // Suppress warnings about == null comparisons.
+ "eqnull": false,
+
+ // Force ES3 compliance - radix parameters and trailing commas
+ "es3":true,
+
+ // Enforce tab width of 4 spaces.
+ "indent": 4,
+
+ // Prohibit use of a variable before it is defined.
+ "latedef": true,
+
+ // Enforce line length to 80 characters
+ "maxlen": 150,
+
+ // Require capitalized names for constructor functions.
+ "newcap": true,
+
+ // Enforce use of single quotation marks for strings.
+ "quotmark": "single",
+
+ // Enforce placing 'use strict' at the top function scope
+ "strict": true,
+
+ // Prohibit trailing whitespace.
+ "trailing": true,
+
+ // Prohibit use of explicitly undeclared variables.
+ "undef": true,
+
+ // Warn when variables are defined but never used.
+ "unused": true
+}
diff --git a/node_modules/mongo-express/.npmignore b/node_modules/mongo-express/.npmignore
new file mode 100644
index 0000000..472179b
--- /dev/null
+++ b/node_modules/mongo-express/.npmignore
@@ -0,0 +1,20 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+*.swo
+*.swp
+
+pids
+logs
+results
+
+node_modules
+npm-debug.log
+
+config.js
+admins.json
diff --git a/node_modules/mongo-express/.travis.yml b/node_modules/mongo-express/.travis.yml
new file mode 100644
index 0000000..0f351b4
--- /dev/null
+++ b/node_modules/mongo-express/.travis.yml
@@ -0,0 +1,8 @@
+language: node_js
+node_js:
+ - 0.12
+branches:
+ only:
+ - master
+ - develop
+script: "npm test"
diff --git a/node_modules/mongo-express/Dockerfile b/node_modules/mongo-express/Dockerfile
new file mode 100644
index 0000000..e7d286e
--- /dev/null
+++ b/node_modules/mongo-express/Dockerfile
@@ -0,0 +1,12 @@
+FROM node:latest
+
+WORKDIR /app
+ADD . /app
+
+RUN npm install
+RUN cp config.default.js config.js
+
+EXPOSE 8081
+
+CMD ["npm", "start"]
+
diff --git a/node_modules/mongo-express/HISTORY.md b/node_modules/mongo-express/HISTORY.md
new file mode 100644
index 0000000..7a4e274
--- /dev/null
+++ b/node_modules/mongo-express/HISTORY.md
@@ -0,0 +1,103 @@
+0.21.0
+------
+
+* Added database statistics (karthik25)
+* Added basic auth (netpi)
+* Added complex querying (kathik25)
+
+0.20.0
+------
+
+* Added JSON find type
+* Added collection export
+* Added confirmation dialog on delete
+* Added uptime info in days
+* Fixed long collection name issue
+
+0.18.0
+------
+
+* Updated express package version
+* Updated swig package version
+* Added simple key-value document searching
+
+0.17.5
+------
+
+* Specified version 3.0.0alpha1 of express in package.json. Latest version of express is causing errors.
+
+0.17.4
+------
+
+* Removed hacky BSON->string conversion function
+* Replaced it with a modified JSON.stringify function
+
+0.17.3
+------
+
+* Removed requirement for doc IDs to be ObjectID type
+
+0.17.2
+------
+
+* Added build status images in README
+
+0.17.1
+------
+
+* Added tests for BSON conversion functions
+
+0.17.0
+------
+
+* Added support for all BSON data types except Binary
+* Fixed BSON converter so not only top-level values get converted
+* Updated README with more BSON data type examples
+
+0.16.1
+------
+
+* Fixed bug: when trying to delete document, collection gets deleted
+
+0.16.0
+------
+
+* Added support for some BSON data types when viewing docs
+* Updated README with list of supported data types
+
+0.15.0
+------
+
+* Added support for BSON data types when adding/editing docs
+
+0.14.1
+------
+
+* Forgot to update HISTORY file
+
+0.14.0
+------
+
+* Added success/error feedback messages
+* Added cookie/session middleware
+* Added cookieSecret and sessionSecret options to config
+* Moved config.js to config.default.js
+* Updated configuration instructions in README
+
+0.13.0
+------
+
+* Added version history
+* Added MIT license
+* Added pager links above document list
+* Added pagination links below document list
+* Added config option to set number of documents displayed per page
+
+0.12.0
+------
+
+* Added async package to dependencies
+* Added system.users to list of un-editable collections
+* Changed config file to allow lists of databases and associated auth details
+* Added support for regular MongoDB users
+* Removed requirement for admin access to the MongoDB server
diff --git a/node_modules/mongo-express/README.md b/node_modules/mongo-express/README.md
new file mode 100644
index 0000000..85a42db
--- /dev/null
+++ b/node_modules/mongo-express/README.md
@@ -0,0 +1,274 @@
+mongo-express
+=============
+
+Web-based MongoDB admin interface written with Node.js and express
+
+[](http://travis-ci.org/andzdroid/mongo-express) - Master (stable) branch
+
+[](http://travis-ci.org/andzdroid/mongo-express) - Develop branch
+
+
+Features
+--------
+
+Current features:
+
+* Connect to multiple databases
+* Connect and authenticate to individual databases
+* Authenticate as admin to view all databases
+* Database blacklist/whitelist
+* View/add/rename/delete collections
+* View/add/update/delete documents
+* Use BSON data types in documents
+
+Planned features:
+
+* Support for replica set connections
+* Web-based command-line interface
+* Site authentication
+* REST interface
+
+
+Limitations
+-----------
+
+* Documents must have `document._id` property to be edited
+* No GridFS support (might become a planned feature)
+* Binary BSON data type not tested
+
+JSON documents are parsed through a javascript virtual machine, so **the web
+interface can be used for executing malicious javascript on a server**.
+
+**mongo-express should only be used privately for development purposes**.
+
+
+Screenshots
+-----------
+
+
+
+Click here for more screenshots:
+[http://imgur.com/a/OTZHe](http://imgur.com/a/OTZHe)
+
+These screenshots are from version 0.11.0.
+
+
+Usage
+-----
+
+**To install:**
+
+ npm install mongo-express
+
+Or if you want to install a global copy:
+
+ npm install -g mongo-express
+
+**To configure:**
+
+Copy `YOUR_PATH/node_modules/mongo-express/config.default.js` into a new file called `YOUR_PATH/node_modules/mongo-express/config.js`.
+
+*Note:* YOUR_PATH will depend on your current OS user and system configuration. You can see it in the output text shown after executing npm install.
+
+Fill in your MongoDB connection details and any other options you want to change in `config.js`.
+
+**To run:**
+
+ cd YOUR_PATH/node_modules/mongo-express/ && node app.js
+
+**To mount as Express 4 middleware (see `node_modules/mongo-express/app.js`):**
+
+ var
+ express = require('express')
+ , http = require('http')
+ ;
+
+ var
+ config = require('./config')
+ , middleware = require('./middleware')
+ ;
+
+ var app = express();
+ app.use('/your-mountpath', middleware(config));
+ app.listen(config.site.port, function() {
+ console.log("Mongo Express server listening on port " + (config.site.port || 80));
+ });
+
+**To run as a Docker container:**
+
+First, build the container from the project directorty:
+
+ docker build -t mongo-express .
+
+If you have a running [MongoDB container](https://registry.hub.docker.com/_/mongo/):
+
+ docker run -d -p 8081:8081 --link mongodb:mongodb mongo-express
+
+You can use the following [environment variables](https://docs.docker.com/reference/run/#env-environment-variables):
+
+- Variable name: `ME_CONFIG_MONGODB_SERVER`
+- Description: MongoDB host name or IP address.
+- Default value: `localhost`
+
+
+- Variable name: `ME_CONFIG_MONGODB_PORT`
+- Description: MongoDB port.
+- Default value: `27017`
+
+- Variable name: `ME_CONFIG_MONGODB_ADMINUSERNAME`
+- Description: Administrator username.
+- Default value: ``
+
+- Variable name: `ME_CONFIG_MONGODB_ADMINPASSWORD`
+- Description: Administrator password.
+- Default value: ``
+
+- Variable name: `ME_CONFIG_SITE_COOKIESECRET`
+- Description: String used by [cookie-parser middleware](https://www.npmjs.com/package/cookie-parser) to sign cookies.
+- Default value: `cookiesecret`
+
+
+- Variable name: `ME_CONFIG_SITE_SESSIONSECRET`
+- Description: String used to sign the session ID cookie by [express-session middleware](https://www.npmjs.com/package/express-session).
+- Default value: `sessionsecret`
+
+
+- Variable name: `ME_CONFIG_BASICAUTH_USERNAME`
+- Description: mongo-express login name. Sending an empty string will disable basic authentication.
+- Default value: `admin`
+
+
+- Variable name: `ME_CONFIG_BASICAUTH_PASSWORD`
+- Description: mongo-express login password.
+- Default value `pass`
+
+- Variable name: `ME_CONFIG_OPTIONS_EDITORTHEME`
+- Description: Web editor color theme.
+- Default value: `rubyblue`
+
+-
+
+**To use:**
+
+Visit `http://localhost:8081` or whatever URL/port you entered into your
+config (if running standalone) or whatever `config.site.baseUrl` (if mounting
+as a middleware).
+
+
+BSON Data Types
+---------------
+
+The following BSON data types are supported in the mongo-express document editor/viewer.
+
+**Native Javascript Types**
+
+Strings, numbers, lists, booleans, null, etc.
+
+All numbers in Javascript are 64-bit floating points.
+
+**ObjectID/ObjectId**
+
+ ObjectID()
+
+Creates a new Object ID type.
+
+ ObjectID(id)
+
+Use Object ID with the given 24-digit hexadecimal string.
+
+**ISODate**
+
+ ISODate()
+
+Creates a new ISODate object with current time.
+
+`new Date()` can also be used (note the `new` keyword there).
+
+ ISODate(timestamp)
+
+Uses ISODate object with the given timestamp.
+
+**DBRef/Dbref**
+
+ DBRef(collection, objectID)
+
+ DBRef(collection, objectID, database)
+
+Object ID is the ID string, not the ObjectID type.
+
+The database value is optional.
+
+**Timestamp**
+
+ Timestamp()
+
+Creates a new Timestamp object with a value of 0.
+
+ Timestamp(time, ordinal)
+
+Example: `Timestamp(ISODate(), 0)`.
+
+See [http://www.mongodb.org/display/DOCS/Timestamp+data+type](http://www.mongodb.org/display/DOCS/Timestamp+data+type) for more info about the Timestamp data type.
+
+**Code**
+
+ Code(code)
+
+Code can be a native Javascript function, or it can be a string.
+
+Specifying a scope/context is not supported.
+
+**MinKey**
+
+ MinKey()
+
+**MaxKey**
+
+ MaxKey()
+
+**Symbol**
+
+ Symbol(string)
+
+---
+
+Not tested:
+
+* Binary/BinData
+
+Here is an example of a document which can be read/edited in mongo-express:
+
+ {
+ "_id": ObjectID(), // or ObjectId()
+ "dates": {
+ "date": ISODate("2012-05-14T16:20:09.314Z"),
+ "new_date": ISODate(),
+ "alternative": new Date()
+ },
+ "bool": true,
+ "string": "hello world!",
+ "list of numbers": [
+ 123,
+ 111e+87,
+ 4.4,
+ -12345.765
+ ],
+ "reference": DBRef("collection", "4fb1299686a989240b000001"),
+ "ts": Timestamp(ISODate(), 1),
+ "minkey": MinKey(),
+ "maxkey": MaxKey(),
+ "func": Code(function() { alert('Hello World!') }),
+ "symbol": Symbol("test")
+ }
+
+License
+-------
+MIT License
+
+Copyright (c) 2012 Chun-hao Hu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/mongo-express/app.js b/node_modules/mongo-express/app.js
new file mode 100644
index 0000000..c996a69
--- /dev/null
+++ b/node_modules/mongo-express/app.js
@@ -0,0 +1,15 @@
+'use strict';
+
+var express = require('express');
+var config = require('./config');
+var middleware = require('./middleware');
+
+var app = express();
+
+app.use('/', middleware(config));
+app.set('read_only', config.options.readOnly || false);
+app.listen(config.site.port, config.site.host, function() {
+ console.log('Mongo Express server listening',
+ 'on port ' + (config.site.port || 80),
+ 'at ' + (config.site.host || '0.0.0.0'));
+});
diff --git a/node_modules/mongo-express/bson.js b/node_modules/mongo-express/bson.js
new file mode 100644
index 0000000..c8314e5
--- /dev/null
+++ b/node_modules/mongo-express/bson.js
@@ -0,0 +1,98 @@
+'use strict';
+
+var mongodb = require('mongodb');
+var vm = require('vm');
+var json = require('./json');
+
+
+//Adaptors for BSON types
+
+var DBRef = function(namespace, oid, db) {
+ //Allow empty/undefined db value
+ if (db === undefined || db === null) {
+ db = '';
+ }
+ return mongodb.DBRef(namespace, oid, db);
+};
+
+var Timestamp = function(high, low) {
+ //Switch low/high bits to Timestamp constructor
+ return mongodb.Timestamp(low, high);
+};
+
+//Create sandbox with BSON data types
+exports.getSandbox = function() {
+ return {
+ Long: mongodb.Long,
+ NumberLong: mongodb.Long,
+ Double: mongodb.Double,
+ NumberDouble: mongodb.Double,
+ ObjectId: mongodb.ObjectID,
+ ObjectID: mongodb.ObjectID,
+ Timestamp: Timestamp,
+ DBRef: DBRef,
+ Dbref: DBRef,
+ Binary: mongodb.Binary,
+ BinData: mongodb.Binary,
+ Code: mongodb.Code,
+ Symbol: mongodb.Symbol,
+ MinKey: mongodb.MinKey,
+ MaxKey: mongodb.MaxKey,
+ ISODate: Date,
+ Date: Date
+ };
+};
+
+//JSON.parse doesn't support BSON data types
+//Document is evaluated in a vm in order to support BSON data types
+//Sandbox contains BSON data type functions from node-mongodb-native
+exports.toBSON = function(string) {
+ var sandbox = exports.getSandbox();
+
+ string = string.replace(/ISODate\(/g, 'new ISODate(');
+
+ vm.runInNewContext('doc = eval((' + string + '));', sandbox);
+
+ return sandbox.doc;
+};
+
+// This function as the name suggests attempts to parse
+// the free form string in to BSON, since the possibilities of failure
+// are higher, this function uses a try..catch
+exports.toSafeBSON = function(string) {
+ try{
+ var bsonObject = exports.toBSON(string);
+ return bsonObject;
+ }
+ catch(err){
+ return null;
+ }
+};
+
+// Converts string to ObjectID. TODO: Add validation.
+exports.toObjectId = function(string){
+ var sandbox = exports.getSandbox();
+ // Strip quotes
+ string = string.replace('"', '').replace('"', '');
+ // Convert ObjectId("526ddf5a9f610ffd26000001") to 526ddf5a9f610ffd26000001
+ string = string.replace(/ObjectID\(/i, '').replace(')', '');
+ // Make sure it's a 24-character string to prevent errors.
+ if (string.length === 24) {
+ return sandbox.ObjectID(string);
+ } else {
+ return false;
+ }
+};
+
+//Convert BSON documents to string
+exports.toString = function(doc) {
+ //Use custom json stringify function from json.js
+ return json.stringify(doc, null, ' ');
+};
+
+exports.toJsonString = function(doc) {
+ var sJson = json.stringify(doc, null);
+ sJson = sJson.replace(/ObjectID\(/g, '{ "$oid": ');
+ sJson = sJson.replace(/\)/g, ' }');
+ return sJson;
+};
diff --git a/node_modules/mongo-express/config.js b/node_modules/mongo-express/config.js
new file mode 100644
index 0000000..77263cf
--- /dev/null
+++ b/node_modules/mongo-express/config.js
@@ -0,0 +1,89 @@
+var url = require('url');
+
+if (typeof process.env.MONGODB_PORT === 'string') {
+ var mongoConnection = url.parse(process.env.MONGODB_PORT);
+ process.env.ME_CONFIG_MONGODB_SERVER = mongoConnection.hostname;
+ process.env.ME_CONFIG_MONGODB_PORT = mongoConnection.port;
+}
+
+module.exports = {
+ mongodb: {
+ server: process.env.ME_CONFIG_MONGODB_SERVER || 'localhost',
+ port: process.env.ME_CONFIG_MONGODB_PORT || 27017,
+
+ //autoReconnect: automatically reconnect if connection is lost
+ autoReconnect: true,
+ //poolSize: size of connection pool (number of connections to use)
+ poolSize: 4,
+ //set admin to true if you want to turn on admin features
+ //if admin is true, the auth list below will be ignored
+ //if admin is true, you will need to enter an admin username/password below (if it is needed)
+ admin: true,
+
+
+ // >>>> If you are using regular accounts, fill out auth details in the section below
+ // >>>> If you have admin auth, leave this section empty and skip to the next section
+ auth: [
+ /*
+ * Add the the name, the username, and the password of the databases you want to connect to
+ * Add as many databases as you want!
+
+ {
+ database: 'sitedb',
+ username: 'adminUser',
+ password: 'password'
+ }
+ */
+ ],
+
+
+ // >>>> If you are using an admin mongodb account, or no admin account exists, fill out section below
+ // >>>> Using an admin account allows you to view and edit all databases, and view stats
+
+ //leave username and password empty if no admin account exists
+ adminUsername: process.env.ME_CONFIG_MONGODB_ADMINUSERNAME || '',
+ adminPassword: process.env.ME_CONFIG_MONGODB_ADMINPASSWORD || '',
+ //whitelist: hide all databases except the ones in this list (empty list for no whitelist)
+ whitelist: [],
+ //blacklist: hide databases listed in the blacklist (empty list for no blacklist)
+ blacklist: []
+ },
+
+ site: {
+ host: '0.0.0.0',
+ port: 8081,
+ cookieSecret: process.env.ME_CONFIG_SITE_COOKIESECRET || 'cookiesecret',
+ sessionSecret: process.env.ME_CONFIG_SITE_SESSIONSECRET || 'sessionsecret',
+ cookieKeyName: 'mongo-express'
+ },
+
+ //set useBasicAuth to true if you want to authehticate mongo-express loggins
+ //if admin is false, the basicAuthInfo list below will be ignored
+ //this will be true unless ME_CONFIG_BASICAUTH_USERNAME is set and is the empty string
+ useBasicAuth: process.env.ME_CONFIG_BASICAUTH_USERNAME != '',
+
+ basicAuth: {
+ username: process.env.ME_CONFIG_BASICAUTH_USERNAME || '',
+ password: process.env.ME_CONFIG_BASICAUTH_PASSWORD || ''
+ },
+
+ options: {
+ //documentsPerPage: how many documents you want to see at once in collection view
+ documentsPerPage: 10,
+ //editorTheme: Name of the theme you want to use for displaying documents
+ //See http://codemirror.net/demo/theme.html for all examples
+ editorTheme: process.env.ME_CONFIG_OPTIONS_EDITORTHEME || 'rubyblue',
+
+ //The options below aren't being used yet
+
+ //cmdType: the type of command line you want mongo express to run
+ //values: eval, subprocess
+ // eval - uses db.eval. commands block, so only use this if you have to
+ // subprocess - spawns a mongo command line as a subprocess and pipes output to mongo express
+ cmdType: 'eval',
+ //subprocessTimeout: number of seconds of non-interaction before a subprocess is shut down
+ subprocessTimeout: 300,
+ //readOnly: if readOnly is true, components of writing are not visible.
+ readOnly: true
+ }
+};
diff --git a/node_modules/mongo-express/db.js b/node_modules/mongo-express/db.js
new file mode 100644
index 0000000..3cffae4
--- /dev/null
+++ b/node_modules/mongo-express/db.js
@@ -0,0 +1,170 @@
+'use strict';
+
+var async = require('async');
+var mongodb = require('mongodb');
+var _ = require('underscore');
+
+var connect = function(config) {
+ // set up database stuff
+ var host = config.mongodb.server || 'localhost';
+ var port = config.mongodb.port || mongodb.Connection.DEFAULT_PORT;
+ var dbOptions = {
+ auto_reconnect: config.mongodb.autoReconnect,
+ poolSize: config.mongodb.poolSize
+ };
+
+ var db;
+
+ if (Array.isArray(host)) {
+ host = host.map(function(host) {
+ return new mongodb.Server(host, port, dbOptions);
+ });
+ db = new mongodb.Db('local', new mongodb.ReplSet(host), { safe: true, w: 0 });
+ } else {
+ db = new mongodb.Db('local', new mongodb.Server(host, port, dbOptions), {safe:true});
+ }
+
+ var connections = {};
+ var databases = [];
+ var collections = {};
+ //get admin instance
+ var adminDb = db.admin();
+ var mainConn; // main db connection
+
+
+ // update the collections list
+ var updateCollections = function(db, dbName, callback) {
+ db.listCollections().toArray(function (err, result) {
+ var names = [];
+
+ for (var r in result) {
+ names.push(result[r].name);
+ }
+
+ collections[dbName] = names.sort();
+
+ if (callback) {
+ callback(err);
+ }
+ });
+ };
+
+ // update database list
+ var updateDatabases = function(admin, callback) {
+ admin.listDatabases(function(err, dbs) {
+ databases = [];
+ if (err) {
+ //TODO: handle error
+ console.error(err);
+ }
+
+ for (var key in dbs.databases) {
+ var dbName = dbs.databases[key].name;
+
+ //'local' is special database, ignore it
+ if (dbName === 'local') {
+ continue;
+ }
+
+ if (config.mongodb.whitelist.length !== 0) {
+ if (!_.include(config.mongodb.whitelist, dbName)) {
+ continue;
+ }
+ }
+ if (config.mongodb.blacklist.length !== 0) {
+ if (_.include(config.mongodb.blacklist, dbName)) {
+ continue;
+ }
+ }
+
+ connections[dbName] = mainConn.db(dbName);
+ databases.push(dbName);
+
+ updateCollections(connections[dbName], dbName);
+ }
+
+ //Sort database names
+ databases = databases.sort();
+
+ if(callback){
+ callback(databases);
+ }
+ });
+ };
+
+
+ // connect to mongodb database
+ db.open(function(err, db) {
+ if (err) {
+ throw err;
+ }
+
+ console.log('Database connected!');
+
+ mainConn = db;
+
+ //Check if admin features are on
+ if (config.mongodb.admin === true) {
+ if (config.mongodb.adminUsername.length === 0) {
+ console.log('Admin Database connected');
+ updateDatabases(adminDb);
+ } else {
+ //auth details were supplied, authenticate admin account with them
+ adminDb.authenticate(config.mongodb.adminUsername, config.mongodb.adminPassword, function(err) {
+ if (err) {
+ //TODO: handle error
+ console.error(err);
+ }
+
+ console.log('Admin Database connected');
+ updateDatabases(adminDb);
+ });
+ }
+ } else {
+ //Regular user authentication
+ if (typeof config.mongodb.auth === 'undefined' || config.mongodb.auth.length === 0) {
+ throw new Error('Add auth details to config or turn on admin!');
+ }
+
+ async.forEachSeries(config.mongodb.auth, function(auth, callback) {
+ console.log('Connecting to ' + auth.database + '...');
+ connections[auth.database] = mainConn.db(auth.database);
+ databases.push(auth.database);
+
+ if (typeof auth.username !== 'undefined' && auth.username.length !== 0) {
+ connections[auth.database].authenticate(auth.username, auth.password, function(err, success) {
+ if (err) {
+ //TODO: handle error
+ console.error(err);
+ }
+
+ if (!success) {
+ console.error('Could not authenticate to database "' + auth.database + '"');
+ }
+
+ updateCollections(connections[auth.database], auth.database);
+ console.log('Connected!');
+ callback();
+ });
+ } else {
+ updateCollections(connections[auth.database], auth.database);
+ console.log('Connected!');
+ callback();
+ }
+ });
+ }
+ });
+
+ return {
+ updateCollections: updateCollections,
+ updateDatabases: updateDatabases,
+ connections: connections,
+ databases: databases,
+ collections: collections,
+ adminDb: adminDb,
+ mainConn: mainConn
+ };
+};
+
+module.exports = connect;
+
diff --git a/node_modules/mongo-express/filters.js b/node_modules/mongo-express/filters.js
new file mode 100644
index 0000000..baa9f67
--- /dev/null
+++ b/node_modules/mongo-express/filters.js
@@ -0,0 +1,31 @@
+'use strict';
+
+exports.json = function(input) {
+ return JSON.stringify(input, null, ' ');
+};
+
+exports.convertBytes = function(input) {
+ input = parseInt(input, 10);
+ if (input < 1024) {
+ return input.toString() + ' Bytes';
+ } else if (input < 1024 * 1024) {
+ //Convert to KB and keep 2 decimal values
+ input = Math.round((input / 1024) * 100) / 100;
+ return input.toString() + ' KB';
+ } else if (input < 1024 * 1024 * 1024) {
+ input = Math.round((input / (1024 * 1024)) * 100) / 100;
+ return input.toString() + ' MB';
+ } else if (input < 1024 * 1024 * 1024 * 1024) {
+ input = Math.round((input / (1024 * 1024 * 1024)) * 100) / 100;
+ return input.toString() + ' GB';
+ } else if (input < 1024 * 1024 * 1024 * 1024 * 1024) {
+ input = Math.round((input / (1024 * 1024 * 1024 * 1024)) * 100) / 100;
+ return input.toString() + ' TB';
+ } else {
+ return input.toString() + ' Bytes';
+ }
+};
+
+exports.to_string = function (input) {
+ return input !== null ? input.toString() : '';
+};
diff --git a/node_modules/mongo-express/json.js b/node_modules/mongo-express/json.js
new file mode 100644
index 0000000..c3bd96b
--- /dev/null
+++ b/node_modules/mongo-express/json.js
@@ -0,0 +1,264 @@
+'use strict';
+
+var mongodb = require('mongodb');
+
+//Original code from official JSON.stringify function
+//Modified to accept BSON data types
+
+
+function f(n) {
+ // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+}
+
+/*
+if (typeof Date.prototype.toJSON !== 'function') {
+
+ Date.prototype.toJSON = function (key) {
+
+ return isFinite(this.valueOf())
+ ? this.getUTCFullYear() + '-' +
+ f(this.getUTCMonth() + 1) + '-' +
+ f(this.getUTCDate()) + 'T' +
+ f(this.getUTCHours()) + ':' +
+ f(this.getUTCMinutes()) + ':' +
+ f(this.getUTCSeconds()) + 'Z'
+ : null;
+ };
+
+ String.prototype.toJSON =
+ Number.prototype.toJSON =
+ Boolean.prototype.toJSON = function (key) {
+ return this.valueOf();
+ };
+}
+*/
+
+var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ gap,
+ indent,
+ meta = { // table of character substitutions
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ },
+ rep;
+
+
+function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+ escapable.lastIndex = 0;
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
+ var c = meta[a];
+ return typeof c === 'string' ?
+ c :
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+}
+
+
+function str(key, holder) {
+
+// Produce a string from holder[key].
+
+ var i, // The loop counter.
+ k, // The member key.
+ v, // The member value.
+ length,
+ mind = gap,
+ partial,
+ value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+ //Custom code to handle BSON data types
+ if (value instanceof mongodb.ObjectID) {
+ return 'ObjectID("' + value + '")';
+ } else if (value instanceof mongodb.Timestamp) {
+ return 'Timestamp(' + value.high_ + ', ' + value.low_ + ')';
+ } else if (value instanceof Date) {
+ return 'ISODate("' + value.toJSON() + '")';
+ } else if (value instanceof mongodb.DBRef) {
+ if (value.db === '') {
+ return 'DBRef("' + value.namespace + '", "' + value.oid + '")';
+ } else {
+ return 'DBRef("' + value.namespace + '", "' + value.oid + '", "' + value.db + '")';
+ }
+ } else if (value instanceof mongodb.Code) {
+ return 'Code("' + value.code + '")';
+ } else if (value instanceof mongodb.MinKey) {
+ return 'MinKey()';
+ } else if (value instanceof mongodb.MaxKey) {
+ return 'MaxKey()';
+ } else if (value instanceof mongodb.Symbol) {
+ return 'Symbol("' + value + '")';
+ }
+
+ if (value && typeof value === 'object' &&
+ typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+ if (typeof rep === 'function') {
+ value = rep.call(holder, key, value);
+ }
+
+// What happens next depends on the value's type.
+
+ switch (typeof value) {
+ case 'string':
+ return quote(value);
+
+ case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+ return isFinite(value) ? String(value) : 'null';
+
+ case 'boolean':
+ case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+ return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+ case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+ if (!value) {
+ return 'null';
+ }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+ gap += indent;
+ partial = [];
+
+// Is the value an array?
+
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+ length = value.length;
+ for (i = 0; i < length; i += 1) {
+ partial[i] = str(i, value) || 'null';
+ }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+ v = partial.length === 0 ?
+ '[]' :
+ gap ?
+ '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
+ '[' + partial.join(',') + ']';
+ gap = mind;
+ return v;
+ }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+ if (rep && typeof rep === 'object') {
+ length = rep.length;
+ for (i = 0; i < length; i += 1) {
+ if (typeof rep[i] === 'string') {
+ k = rep[i];
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+ for (k in value) {
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
+ v = str(k, value);
+ if (v) {
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
+ }
+ }
+ }
+ }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+ v = partial.length === 0 ?
+ '{}' :
+ gap ?
+ '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
+ '{' + partial.join(',') + '}';
+ gap = mind;
+ return v;
+ }
+}
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+
+exports.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+ var i;
+ gap = '';
+ indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+ if (typeof space === 'number') {
+ for (i = 0; i < space; i += 1) {
+ indent += ' ';
+ }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+ } else if (typeof space === 'string') {
+ indent = space;
+ }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+ rep = replacer;
+ if (replacer && typeof replacer !== 'function' &&
+ (typeof replacer !== 'object' ||
+ typeof replacer.length !== 'number')) {
+ throw new Error('JSON.stringify');
+ }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+ return str('', {'': value});
+};
diff --git a/node_modules/mongo-express/middleware.js b/node_modules/mongo-express/middleware.js
new file mode 100644
index 0000000..a2e9d48
--- /dev/null
+++ b/node_modules/mongo-express/middleware.js
@@ -0,0 +1,29 @@
+'use strict';
+
+var express = require('express'),
+ cons = require('consolidate'),
+ swig = require('swig');
+
+var swigFilters = require('./filters'),
+ router = require('./router');
+
+var middleware = function(config) {
+ var app = express();
+
+ //Set up swig
+ app.engine('html', cons.swig);
+ Object.keys(swigFilters).forEach(function (name) {
+ swig.setFilter(name, swigFilters[name]);
+ });
+
+ //App configuration
+ app.set('views', __dirname + '/views');
+ app.set('view engine', 'html');
+ app.set('view options', {layout: false});
+
+ app.use('/', router(config));
+
+ return app;
+};
+
+module.exports = middleware;
diff --git a/node_modules/mongo-express/node_modules/.bin/swig b/node_modules/mongo-express/node_modules/.bin/swig
new file mode 120000
index 0000000..b4b1e73
--- /dev/null
+++ b/node_modules/mongo-express/node_modules/.bin/swig
@@ -0,0 +1 @@
+../swig/bin/swig.js
\ No newline at end of file
diff --git a/node_modules/mongo-express/node_modules/async/CHANGELOG.md b/node_modules/mongo-express/node_modules/async/CHANGELOG.md
new file mode 100644
index 0000000..3f8eb69
--- /dev/null
+++ b/node_modules/mongo-express/node_modules/async/CHANGELOG.md
@@ -0,0 +1,104 @@
+# v1.4.2
+
+- Ensure coverage files don't get published on npm (#879)
+
+# v1.4.1
+
+- Add in overlooked `detectLimit` method (#866)
+- Removed unnecessary files from npm releases (#861)
+- Removed usage of a reserved word to prevent :boom: in older environments (#870)
+
+# v1.4.0
+
+- `asyncify` now supports promises (#840)
+- Added `Limit` versions of `filter` and `reject` (#836)
+- Add `Limit` versions of `detect`, `some` and `every` (#828, #829)
+- `some`, `every` and `detect` now short circuit early (#828, #829)
+- Improve detection of the global object (#804), enabling use in WebWorkers
+- `whilst` now called with arguments from iterator (#823)
+- `during` now gets called with arguments from iterator (#824)
+- Code simplifications and optimizations aplenty ([diff](https://github.com/caolan/async/compare/v1.3.0...v1.4.0))
+
+
+# v1.3.0
+
+New Features:
+- Added `constant`
+- Added `asyncify`/`wrapSync` for making sync functions work with callbacks. (#671, #806)
+- Added `during` and `doDuring`, which are like `whilst` with an async truth test. (#800)
+- `retry` now accepts an `interval` parameter to specify a delay between retries. (#793)
+- `async` should work better in Web Workers due to better `root` detection (#804)
+- Callbacks are now optional in `whilst`, `doWhilst`, `until`, and `doUntil` (#642)
+- Various internal updates (#786, #801, #802, #803)
+- Various doc fixes (#790, #794)
+
+Bug Fixes:
+- `cargo` now exposes the `payload` size, and `cargo.payload` can be changed on the fly after the `cargo` is created. (#740, #744, #783)
+
+
+# v1.2.1
+
+Bug Fix:
+
+- Small regression with synchronous iterator behavior in `eachSeries` with a 1-element array. Before 1.1.0, `eachSeries`'s callback was called on the same tick, which this patch restores. In 2.0.0, it will be called on the next tick. (#782)
+
+
+# v1.2.0
+
+New Features:
+
+- Added `timesLimit` (#743)
+- `concurrency` can be changed after initialization in `queue` by setting `q.concurrency`. The new concurrency will be reflected the next time a task is processed. (#747, #772)
+
+Bug Fixes:
+
+- Fixed a regression in `each` and family with empty arrays that have additional properties. (#775, #777)
+
+
+# v1.1.1
+
+Bug Fix:
+
+- Small regression with synchronous iterator behavior in `eachSeries` with a 1-element array. Before 1.1.0, `eachSeries`'s callback was called on the same tick, which this patch restores. In 2.0.0, it will be called on the next tick. (#782)
+
+
+# v1.1.0
+
+New Features:
+
+- `cargo` now supports all of the same methods and event callbacks as `queue`.
+- Added `ensureAsync` - A wrapper that ensures an async function calls its callback on a later tick. (#769)
+- Optimized `map`, `eachOf`, and `waterfall` families of functions
+- Passing a `null` or `undefined` array to `map`, `each`, `parallel` and families will be treated as an empty array (#667).
+- The callback is now optional for the composed results of `compose` and `seq`. (#618)
+- Reduced file size by 4kb, (minified version by 1kb)
+- Added code coverage through `nyc` and `coveralls` (#768)
+
+Bug Fixes:
+
+- `forever` will no longer stack overflow with a synchronous iterator (#622)
+- `eachLimit` and other limit functions will stop iterating once an error occurs (#754)
+- Always pass `null` in callbacks when there is no error (#439)
+- Ensure proper conditions when calling `drain()` after pushing an empty data set to a queue (#668)
+- `each` and family will properly handle an empty array (#578)
+- `eachSeries` and family will finish if the underlying array is modified during execution (#557)
+- `queue` will throw if a non-function is passed to `q.push()` (#593)
+- Doc fixes (#629, #766)
+
+
+# v1.0.0
+
+No known breaking changes, we are simply complying with semver from here on out.
+
+Changes:
+
+- Start using a changelog!
+- Add `forEachOf` for iterating over Objects (or to iterate Arrays with indexes available) (#168 #704 #321)
+- Detect deadlocks in `auto` (#663)
+- Better support for require.js (#527)
+- Throw if queue created with concurrency `0` (#714)
+- Fix unneeded iteration in `queue.resume()` (#758)
+- Guard against timer mocking overriding `setImmediate` (#609 #611)
+- Miscellaneous doc fixes (#542 #596 #615 #628 #631 #690 #729)
+- Use single noop function internally (#546)
+- Optimize internal `_each`, `_map` and `_keys` functions.
diff --git a/node_modules/mongo-express/node_modules/async/LICENSE b/node_modules/mongo-express/node_modules/async/LICENSE
new file mode 100644
index 0000000..8f29698
--- /dev/null
+++ b/node_modules/mongo-express/node_modules/async/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010-2014 Caolan McMahon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/mongo-express/node_modules/async/lib/async.js b/node_modules/mongo-express/node_modules/async/lib/async.js
new file mode 100644
index 0000000..1f1e0ef
--- /dev/null
+++ b/node_modules/mongo-express/node_modules/async/lib/async.js
@@ -0,0 +1,1222 @@
+/*!
+ * async
+ * https://github.com/caolan/async
+ *
+ * Copyright 2010-2014 Caolan McMahon
+ * Released under the MIT license
+ */
+(function () {
+
+ var async = {};
+ function noop() {}
+ function identity(v) {
+ return v;
+ }
+ function toBool(v) {
+ return !!v;
+ }
+ function notId(v) {
+ return !v;
+ }
+
+ // global on the server, window in the browser
+ var previous_async;
+
+ // Establish the root object, `window` (`self`) in the browser, `global`
+ // on the server, or `this` in some virtual machines. We use `self`
+ // instead of `window` for `WebWorker` support.
+ var root = typeof self === 'object' && self.self === self && self ||
+ typeof global === 'object' && global.global === global && global ||
+ this;
+
+ if (root != null) {
+ previous_async = root.async;
+ }
+
+ async.noConflict = function () {
+ root.async = previous_async;
+ return async;
+ };
+
+ function only_once(fn) {
+ return function() {
+ if (fn === null) throw new Error("Callback was already called.");
+ fn.apply(this, arguments);
+ fn = null;
+ };
+ }
+
+ function _once(fn) {
+ return function() {
+ if (fn === null) return;
+ fn.apply(this, arguments);
+ fn = null;
+ };
+ }
+
+ //// cross-browser compatiblity functions ////
+
+ var _toString = Object.prototype.toString;
+
+ var _isArray = Array.isArray || function (obj) {
+ return _toString.call(obj) === '[object Array]';
+ };
+
+ // Ported from underscore.js isObject
+ var _isObject = function(obj) {
+ var type = typeof obj;
+ return type === 'function' || type === 'object' && !!obj;
+ };
+
+ function _isArrayLike(arr) {
+ return _isArray(arr) || (
+ // has a positive integer length property
+ typeof arr.length === "number" &&
+ arr.length >= 0 &&
+ arr.length % 1 === 0
+ );
+ }
+
+ function _each(coll, iterator) {
+ return _isArrayLike(coll) ?
+ _arrayEach(coll, iterator) :
+ _forEachOf(coll, iterator);
+ }
+
+ function _arrayEach(arr, iterator) {
+ var index = -1,
+ length = arr.length;
+
+ while (++index < length) {
+ iterator(arr[index], index, arr);
+ }
+ }
+
+ function _map(arr, iterator) {
+ var index = -1,
+ length = arr.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iterator(arr[index], index, arr);
+ }
+ return result;
+ }
+
+ function _range(count) {
+ return _map(Array(count), function (v, i) { return i; });
+ }
+
+ function _reduce(arr, iterator, memo) {
+ _arrayEach(arr, function (x, i, a) {
+ memo = iterator(memo, x, i, a);
+ });
+ return memo;
+ }
+
+ function _forEachOf(object, iterator) {
+ _arrayEach(_keys(object), function (key) {
+ iterator(object[key], key);
+ });
+ }
+
+ function _indexOf(arr, item) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] === item) return i;
+ }
+ return -1;
+ }
+
+ var _keys = Object.keys || function (obj) {
+ var keys = [];
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ keys.push(k);
+ }
+ }
+ return keys;
+ };
+
+ function _keyIterator(coll) {
+ var i = -1;
+ var len;
+ var keys;
+ if (_isArrayLike(coll)) {
+ len = coll.length;
+ return function next() {
+ i++;
+ return i < len ? i : null;
+ };
+ } else {
+ keys = _keys(coll);
+ len = keys.length;
+ return function next() {
+ i++;
+ return i < len ? keys[i] : null;
+ };
+ }
+ }
+
+ // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
+ // This accumulates the arguments passed into an array, after a given index.
+ // From underscore.js (https://github.com/jashkenas/underscore/pull/2140).
+ function _restParam(func, startIndex) {
+ startIndex = startIndex == null ? func.length - 1 : +startIndex;
+ return function() {
+ var length = Math.max(arguments.length - startIndex, 0);
+ var rest = Array(length);
+ for (var index = 0; index < length; index++) {
+ rest[index] = arguments[index + startIndex];
+ }
+ switch (startIndex) {
+ case 0: return func.call(this, rest);
+ case 1: return func.call(this, arguments[0], rest);
+ }
+ // Currently unused but handle cases outside of the switch statement:
+ // var args = Array(startIndex + 1);
+ // for (index = 0; index < startIndex; index++) {
+ // args[index] = arguments[index];
+ // }
+ // args[startIndex] = rest;
+ // return func.apply(this, args);
+ };
+ }
+
+ function _withoutIndex(iterator) {
+ return function (value, index, callback) {
+ return iterator(value, callback);
+ };
+ }
+
+ //// exported async module functions ////
+
+ //// nextTick implementation with browser-compatible fallback ////
+
+ // capture the global reference to guard against fakeTimer mocks
+ var _setImmediate = typeof setImmediate === 'function' && setImmediate;
+
+ var _delay = _setImmediate ? function(fn) {
+ // not a direct alias for IE10 compatibility
+ _setImmediate(fn);
+ } : function(fn) {
+ setTimeout(fn, 0);
+ };
+
+ if (typeof process === 'object' && typeof process.nextTick === 'function') {
+ async.nextTick = process.nextTick;
+ } else {
+ async.nextTick = _delay;
+ }
+ async.setImmediate = _setImmediate ? _delay : async.nextTick;
+
+
+ async.forEach =
+ async.each = function (arr, iterator, callback) {
+ return async.eachOf(arr, _withoutIndex(iterator), callback);
+ };
+
+ async.forEachSeries =
+ async.eachSeries = function (arr, iterator, callback) {
+ return async.eachOfSeries(arr, _withoutIndex(iterator), callback);
+ };
+
+
+ async.forEachLimit =
+ async.eachLimit = function (arr, limit, iterator, callback) {
+ return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback);
+ };
+
+ async.forEachOf =
+ async.eachOf = function (object, iterator, callback) {
+ callback = _once(callback || noop);
+ object = object || [];
+ var size = _isArrayLike(object) ? object.length : _keys(object).length;
+ var completed = 0;
+ if (!size) {
+ return callback(null);
+ }
+ _each(object, function (value, key) {
+ iterator(object[key], key, only_once(done));
+ });
+ function done(err) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ completed += 1;
+ if (completed >= size) {
+ callback(null);
+ }
+ }
+ }
+ };
+
+ async.forEachOfSeries =
+ async.eachOfSeries = function (obj, iterator, callback) {
+ callback = _once(callback || noop);
+ obj = obj || [];
+ var nextKey = _keyIterator(obj);
+ var key = nextKey();
+ function iterate() {
+ var sync = true;
+ if (key === null) {
+ return callback(null);
+ }
+ iterator(obj[key], key, only_once(function (err) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ key = nextKey();
+ if (key === null) {
+ return callback(null);
+ } else {
+ if (sync) {
+ async.nextTick(iterate);
+ } else {
+ iterate();
+ }
+ }
+ }
+ }));
+ sync = false;
+ }
+ iterate();
+ };
+
+
+
+ async.forEachOfLimit =
+ async.eachOfLimit = function (obj, limit, iterator, callback) {
+ _eachOfLimit(limit)(obj, iterator, callback);
+ };
+
+ function _eachOfLimit(limit) {
+
+ return function (obj, iterator, callback) {
+ callback = _once(callback || noop);
+ obj = obj || [];
+ var nextKey = _keyIterator(obj);
+ if (limit <= 0) {
+ return callback(null);
+ }
+ var done = false;
+ var running = 0;
+ var errored = false;
+
+ (function replenish () {
+ if (done && running <= 0) {
+ return callback(null);
+ }
+
+ while (running < limit && !errored) {
+ var key = nextKey();
+ if (key === null) {
+ done = true;
+ if (running <= 0) {
+ callback(null);
+ }
+ return;
+ }
+ running += 1;
+ iterator(obj[key], key, only_once(function (err) {
+ running -= 1;
+ if (err) {
+ callback(err);
+ errored = true;
+ }
+ else {
+ replenish();
+ }
+ }));
+ }
+ })();
+ };
+ }
+
+
+ function doParallel(fn) {
+ return function (obj, iterator, callback) {
+ return fn(async.eachOf, obj, iterator, callback);
+ };
+ }
+ function doParallelLimit(fn) {
+ return function (obj, limit, iterator, callback) {
+ return fn(_eachOfLimit(limit), obj, iterator, callback);
+ };
+ }
+ function doSeries(fn) {
+ return function (obj, iterator, callback) {
+ return fn(async.eachOfSeries, obj, iterator, callback);
+ };
+ }
+
+ function _asyncMap(eachfn, arr, iterator, callback) {
+ callback = _once(callback || noop);
+ var results = [];
+ eachfn(arr, function (value, index, callback) {
+ iterator(value, function (err, v) {
+ results[index] = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+
+ async.map = doParallel(_asyncMap);
+ async.mapSeries = doSeries(_asyncMap);
+ async.mapLimit = doParallelLimit(_asyncMap);
+
+ // reduce only has a series version, as doing reduce in parallel won't
+ // work in many situations.
+ async.inject =
+ async.foldl =
+ async.reduce = function (arr, memo, iterator, callback) {
+ async.eachOfSeries(arr, function (x, i, callback) {
+ iterator(memo, x, function (err, v) {
+ memo = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err || null, memo);
+ });
+ };
+
+ async.foldr =
+ async.reduceRight = function (arr, memo, iterator, callback) {
+ var reversed = _map(arr, identity).reverse();
+ async.reduce(reversed, memo, iterator, callback);
+ };
+
+ function _filter(eachfn, arr, iterator, callback) {
+ var results = [];
+ eachfn(arr, function (x, index, callback) {
+ iterator(x, function (v) {
+ if (v) {
+ results.push({index: index, value: x});
+ }
+ callback();
+ });
+ }, function () {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ }
+
+ async.select =
+ async.filter = doParallel(_filter);
+
+ async.selectLimit =
+ async.filterLimit = doParallelLimit(_filter);
+
+ async.selectSeries =
+ async.filterSeries = doSeries(_filter);
+
+ function _reject(eachfn, arr, iterator, callback) {
+ _filter(eachfn, arr, function(value, cb) {
+ iterator(value, function(v) {
+ cb(!v);
+ });
+ }, callback);
+ }
+ async.reject = doParallel(_reject);
+ async.rejectLimit = doParallelLimit(_reject);
+ async.rejectSeries = doSeries(_reject);
+
+ function _createTester(eachfn, check, getResult) {
+ return function(arr, limit, iterator, cb) {
+ function done() {
+ if (cb) cb(getResult(false, void 0));
+ }
+ function iteratee(x, _, callback) {
+ if (!cb) return callback();
+ iterator(x, function (v) {
+ if (cb && check(v)) {
+ cb(getResult(true, x));
+ cb = iterator = false;
+ }
+ callback();
+ });
+ }
+ if (arguments.length > 3) {
+ eachfn(arr, limit, iteratee, done);
+ } else {
+ cb = iterator;
+ iterator = limit;
+ eachfn(arr, iteratee, done);
+ }
+ };
+ }
+
+ async.any =
+ async.some = _createTester(async.eachOf, toBool, identity);
+
+ async.someLimit = _createTester(async.eachOfLimit, toBool, identity);
+
+ async.all =
+ async.every = _createTester(async.eachOf, notId, notId);
+
+ async.everyLimit = _createTester(async.eachOfLimit, notId, notId);
+
+ function _findGetResult(v, x) {
+ return x;
+ }
+ async.detect = _createTester(async.eachOf, identity, _findGetResult);
+ async.detectSeries = _createTester(async.eachOfSeries, identity, _findGetResult);
+ async.detectLimit = _createTester(async.eachOfLimit, identity, _findGetResult);
+
+ async.sortBy = function (arr, iterator, callback) {
+ async.map(arr, function (x, callback) {
+ iterator(x, function (err, criteria) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ callback(null, {value: x, criteria: criteria});
+ }
+ });
+ }, function (err, results) {
+ if (err) {
+ return callback(err);
+ }
+ else {
+ callback(null, _map(results.sort(comparator), function (x) {
+ return x.value;
+ }));
+ }
+
+ });
+
+ function comparator(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }
+ };
+
+ async.auto = function (tasks, callback) {
+ callback = _once(callback || noop);
+ var keys = _keys(tasks);
+ var remainingTasks = keys.length;
+ if (!remainingTasks) {
+ return callback(null);
+ }
+
+ var results = {};
+
+ var listeners = [];
+ function addListener(fn) {
+ listeners.unshift(fn);
+ }
+ function removeListener(fn) {
+ var idx = _indexOf(listeners, fn);
+ if (idx >= 0) listeners.splice(idx, 1);
+ }
+ function taskComplete() {
+ remainingTasks--;
+ _arrayEach(listeners.slice(0), function (fn) {
+ fn();
+ });
+ }
+
+ addListener(function () {
+ if (!remainingTasks) {
+ callback(null, results);
+ }
+ });
+
+ _arrayEach(keys, function (k) {
+ var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
+ var taskCallback = _restParam(function(err, args) {
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ if (err) {
+ var safeResults = {};
+ _forEachOf(results, function(val, rkey) {
+ safeResults[rkey] = val;
+ });
+ safeResults[k] = args;
+ callback(err, safeResults);
+ }
+ else {
+ results[k] = args;
+ async.setImmediate(taskComplete);
+ }
+ });
+ var requires = task.slice(0, task.length - 1);
+ // prevent dead-locks
+ var len = requires.length;
+ var dep;
+ while (len--) {
+ if (!(dep = tasks[requires[len]])) {
+ throw new Error('Has inexistant dependency');
+ }
+ if (_isArray(dep) && _indexOf(dep, k) >= 0) {
+ throw new Error('Has cyclic dependencies');
+ }
+ }
+ function ready() {
+ return _reduce(requires, function (a, x) {
+ return (a && results.hasOwnProperty(x));
+ }, true) && !results.hasOwnProperty(k);
+ }
+ if (ready()) {
+ task[task.length - 1](taskCallback, results);
+ }
+ else {
+ addListener(listener);
+ }
+ function listener() {
+ if (ready()) {
+ removeListener(listener);
+ task[task.length - 1](taskCallback, results);
+ }
+ }
+ });
+ };
+
+
+
+ async.retry = function(times, task, callback) {
+ var DEFAULT_TIMES = 5;
+ var DEFAULT_INTERVAL = 0;
+
+ var attempts = [];
+
+ var opts = {
+ times: DEFAULT_TIMES,
+ interval: DEFAULT_INTERVAL
+ };
+
+ function parseTimes(acc, t){
+ if(typeof t === 'number'){
+ acc.times = parseInt(t, 10) || DEFAULT_TIMES;
+ } else if(typeof t === 'object'){
+ acc.times = parseInt(t.times, 10) || DEFAULT_TIMES;
+ acc.interval = parseInt(t.interval, 10) || DEFAULT_INTERVAL;
+ } else {
+ throw new Error('Unsupported argument type for \'times\': ' + typeof t);
+ }
+ }
+
+ var length = arguments.length;
+ if (length < 1 || length > 3) {
+ throw new Error('Invalid arguments - must be either (task), (task, callback), (times, task) or (times, task, callback)');
+ } else if (length <= 2 && typeof times === 'function') {
+ callback = task;
+ task = times;
+ }
+ if (typeof times !== 'function') {
+ parseTimes(opts, times);
+ }
+ opts.callback = callback;
+ opts.task = task;
+
+ function wrappedTask(wrappedCallback, wrappedResults) {
+ function retryAttempt(task, finalAttempt) {
+ return function(seriesCallback) {
+ task(function(err, result){
+ seriesCallback(!err || finalAttempt, {err: err, result: result});
+ }, wrappedResults);
+ };
+ }
+
+ function retryInterval(interval){
+ return function(seriesCallback){
+ setTimeout(function(){
+ seriesCallback(null);
+ }, interval);
+ };
+ }
+
+ while (opts.times) {
+
+ var finalAttempt = !(opts.times-=1);
+ attempts.push(retryAttempt(opts.task, finalAttempt));
+ if(!finalAttempt && opts.interval > 0){
+ attempts.push(retryInterval(opts.interval));
+ }
+ }
+
+ async.series(attempts, function(done, data){
+ data = data[data.length - 1];
+ (wrappedCallback || opts.callback)(data.err, data.result);
+ });
+ }
+
+ // If a callback is passed, run this as a controll flow
+ return opts.callback ? wrappedTask() : wrappedTask;
+ };
+
+ async.waterfall = function (tasks, callback) {
+ callback = _once(callback || noop);
+ if (!_isArray(tasks)) {
+ var err = new Error('First argument to waterfall must be an array of functions');
+ return callback(err);
+ }
+ if (!tasks.length) {
+ return callback();
+ }
+ function wrapIterator(iterator) {
+ return _restParam(function (err, args) {
+ if (err) {
+ callback.apply(null, [err].concat(args));
+ }
+ else {
+ var next = iterator.next();
+ if (next) {
+ args.push(wrapIterator(next));
+ }
+ else {
+ args.push(callback);
+ }
+ ensureAsync(iterator).apply(null, args);
+ }
+ });
+ }
+ wrapIterator(async.iterator(tasks))();
+ };
+
+ function _parallel(eachfn, tasks, callback) {
+ callback = callback || noop;
+ var results = _isArrayLike(tasks) ? [] : {};
+
+ eachfn(tasks, function (task, key, callback) {
+ task(_restParam(function (err, args) {
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ results[key] = args;
+ callback(err);
+ }));
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+
+ async.parallel = function (tasks, callback) {
+ _parallel(async.eachOf, tasks, callback);
+ };
+
+ async.parallelLimit = function(tasks, limit, callback) {
+ _parallel(_eachOfLimit(limit), tasks, callback);
+ };
+
+ async.series = function(tasks, callback) {
+ _parallel(async.eachOfSeries, tasks, callback);
+ };
+
+ async.iterator = function (tasks) {
+ function makeCallback(index) {
+ function fn() {
+ if (tasks.length) {
+ tasks[index].apply(null, arguments);
+ }
+ return fn.next();
+ }
+ fn.next = function () {
+ return (index < tasks.length - 1) ? makeCallback(index + 1): null;
+ };
+ return fn;
+ }
+ return makeCallback(0);
+ };
+
+ async.apply = _restParam(function (fn, args) {
+ return _restParam(function (callArgs) {
+ return fn.apply(
+ null, args.concat(callArgs)
+ );
+ });
+ });
+
+ function _concat(eachfn, arr, fn, callback) {
+ var result = [];
+ eachfn(arr, function (x, index, cb) {
+ fn(x, function (err, y) {
+ result = result.concat(y || []);
+ cb(err);
+ });
+ }, function (err) {
+ callback(err, result);
+ });
+ }
+ async.concat = doParallel(_concat);
+ async.concatSeries = doSeries(_concat);
+
+ async.whilst = function (test, iterator, callback) {
+ callback = callback || noop;
+ if (test()) {
+ var next = _restParam(function(err, args) {
+ if (err) {
+ callback(err);
+ } else if (test.apply(this, args)) {
+ iterator(next);
+ } else {
+ callback(null);
+ }
+ });
+ iterator(next);
+ } else {
+ callback(null);
+ }
+ };
+
+ async.doWhilst = function (iterator, test, callback) {
+ var calls = 0;
+ return async.whilst(function() {
+ return ++calls <= 1 || test.apply(this, arguments);
+ }, iterator, callback);
+ };
+
+ async.until = function (test, iterator, callback) {
+ return async.whilst(function() {
+ return !test.apply(this, arguments);
+ }, iterator, callback);
+ };
+
+ async.doUntil = function (iterator, test, callback) {
+ return async.doWhilst(iterator, function() {
+ return !test.apply(this, arguments);
+ }, callback);
+ };
+
+ async.during = function (test, iterator, callback) {
+ callback = callback || noop;
+
+ var next = _restParam(function(err, args) {
+ if (err) {
+ callback(err);
+ } else {
+ args.push(check);
+ test.apply(this, args);
+ }
+ });
+
+ var check = function(err, truth) {
+ if (err) {
+ callback(err);
+ } else if (truth) {
+ iterator(next);
+ } else {
+ callback(null);
+ }
+ };
+
+ test(check);
+ };
+
+ async.doDuring = function (iterator, test, callback) {
+ var calls = 0;
+ async.during(function(next) {
+ if (calls++ < 1) {
+ next(null, true);
+ } else {
+ test.apply(this, arguments);
+ }
+ }, iterator, callback);
+ };
+
+ function _queue(worker, concurrency, payload) {
+ if (concurrency == null) {
+ concurrency = 1;
+ }
+ else if(concurrency === 0) {
+ throw new Error('Concurrency must not be zero');
+ }
+ function _insert(q, data, pos, callback) {
+ if (callback != null && typeof callback !== "function") {
+ throw new Error("task callback must be a function");
+ }
+ q.started = true;
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length === 0 && q.idle()) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ q.drain();
+ });
+ }
+ _arrayEach(data, function(task) {
+ var item = {
+ data: task,
+ callback: callback || noop
+ };
+
+ if (pos) {
+ q.tasks.unshift(item);
+ } else {
+ q.tasks.push(item);
+ }
+
+ if (q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ });
+ async.setImmediate(q.process);
+ }
+ function _next(q, tasks) {
+ return function(){
+ workers -= 1;
+ var args = arguments;
+ _arrayEach(tasks, function (task) {
+ task.callback.apply(task, args);
+ });
+ if (q.tasks.length + workers === 0) {
+ q.drain();
+ }
+ q.process();
+ };
+ }
+
+ var workers = 0;
+ var q = {
+ tasks: [],
+ concurrency: concurrency,
+ payload: payload,
+ saturated: noop,
+ empty: noop,
+ drain: noop,
+ started: false,
+ paused: false,
+ push: function (data, callback) {
+ _insert(q, data, false, callback);
+ },
+ kill: function () {
+ q.drain = noop;
+ q.tasks = [];
+ },
+ unshift: function (data, callback) {
+ _insert(q, data, true, callback);
+ },
+ process: function () {
+ if (!q.paused && workers < q.concurrency && q.tasks.length) {
+ while(workers < q.concurrency && q.tasks.length){
+ var tasks = q.payload ?
+ q.tasks.splice(0, q.payload) :
+ q.tasks.splice(0, q.tasks.length);
+
+ var data = _map(tasks, function (task) {
+ return task.data;
+ });
+
+ if (q.tasks.length === 0) {
+ q.empty();
+ }
+ workers += 1;
+ var cb = only_once(_next(q, tasks));
+ worker(data, cb);
+ }
+ }
+ },
+ length: function () {
+ return q.tasks.length;
+ },
+ running: function () {
+ return workers;
+ },
+ idle: function() {
+ return q.tasks.length + workers === 0;
+ },
+ pause: function () {
+ q.paused = true;
+ },
+ resume: function () {
+ if (q.paused === false) { return; }
+ q.paused = false;
+ var resumeCount = Math.min(q.concurrency, q.tasks.length);
+ // Need to call q.process once per concurrent
+ // worker to preserve full concurrency after pause
+ for (var w = 1; w <= resumeCount; w++) {
+ async.setImmediate(q.process);
+ }
+ }
+ };
+ return q;
+ }
+
+ async.queue = function (worker, concurrency) {
+ var q = _queue(function (items, cb) {
+ worker(items[0], cb);
+ }, concurrency, 1);
+
+ return q;
+ };
+
+ async.priorityQueue = function (worker, concurrency) {
+
+ function _compareTasks(a, b){
+ return a.priority - b.priority;
+ }
+
+ function _binarySearch(sequence, item, compare) {
+ var beg = -1,
+ end = sequence.length - 1;
+ while (beg < end) {
+ var mid = beg + ((end - beg + 1) >>> 1);
+ if (compare(item, sequence[mid]) >= 0) {
+ beg = mid;
+ } else {
+ end = mid - 1;
+ }
+ }
+ return beg;
+ }
+
+ function _insert(q, data, priority, callback) {
+ if (callback != null && typeof callback !== "function") {
+ throw new Error("task callback must be a function");
+ }
+ q.started = true;
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length === 0) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ q.drain();
+ });
+ }
+ _arrayEach(data, function(task) {
+ var item = {
+ data: task,
+ priority: priority,
+ callback: typeof callback === 'function' ? callback : noop
+ };
+
+ q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
+
+ if (q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
+ }
+
+ // Start with a normal queue
+ var q = async.queue(worker, concurrency);
+
+ // Override push to accept second parameter representing priority
+ q.push = function (data, priority, callback) {
+ _insert(q, data, priority, callback);
+ };
+
+ // Remove unshift function
+ delete q.unshift;
+
+ return q;
+ };
+
+ async.cargo = function (worker, payload) {
+ return _queue(worker, 1, payload);
+ };
+
+ function _console_fn(name) {
+ return _restParam(function (fn, args) {
+ fn.apply(null, args.concat([_restParam(function (err, args) {
+ if (typeof console === 'object') {
+ if (err) {
+ if (console.error) {
+ console.error(err);
+ }
+ }
+ else if (console[name]) {
+ _arrayEach(args, function (x) {
+ console[name](x);
+ });
+ }
+ }
+ })]));
+ });
+ }
+ async.log = _console_fn('log');
+ async.dir = _console_fn('dir');
+ /*async.info = _console_fn('info');
+ async.warn = _console_fn('warn');
+ async.error = _console_fn('error');*/
+
+ async.memoize = function (fn, hasher) {
+ var memo = {};
+ var queues = {};
+ hasher = hasher || identity;
+ var memoized = _restParam(function memoized(args) {
+ var callback = args.pop();
+ var key = hasher.apply(null, args);
+ if (key in memo) {
+ async.nextTick(function () {
+ callback.apply(null, memo[key]);
+ });
+ }
+ else if (key in queues) {
+ queues[key].push(callback);
+ }
+ else {
+ queues[key] = [callback];
+ fn.apply(null, args.concat([_restParam(function (args) {
+ memo[key] = args;
+ var q = queues[key];
+ delete queues[key];
+ for (var i = 0, l = q.length; i < l; i++) {
+ q[i].apply(null, args);
+ }
+ })]));
+ }
+ });
+ memoized.memo = memo;
+ memoized.unmemoized = fn;
+ return memoized;
+ };
+
+ async.unmemoize = function (fn) {
+ return function () {
+ return (fn.unmemoized || fn).apply(null, arguments);
+ };
+ };
+
+ function _times(mapper) {
+ return function (count, iterator, callback) {
+ mapper(_range(count), iterator, callback);
+ };
+ }
+
+ async.times = _times(async.map);
+ async.timesSeries = _times(async.mapSeries);
+ async.timesLimit = function (count, limit, iterator, callback) {
+ return async.mapLimit(_range(count), limit, iterator, callback);
+ };
+
+ async.seq = function (/* functions... */) {
+ var fns = arguments;
+ return _restParam(function (args) {
+ var that = this;
+
+ var callback = args[args.length - 1];
+ if (typeof callback == 'function') {
+ args.pop();
+ } else {
+ callback = noop;
+ }
+
+ async.reduce(fns, args, function (newargs, fn, cb) {
+ fn.apply(that, newargs.concat([_restParam(function (err, nextargs) {
+ cb(err, nextargs);
+ })]));
+ },
+ function (err, results) {
+ callback.apply(that, [err].concat(results));
+ });
+ });
+ };
+
+ async.compose = function (/* functions... */) {
+ return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+ };
+
+
+ function _applyEach(eachfn) {
+ return _restParam(function(fns, args) {
+ var go = _restParam(function(args) {
+ var that = this;
+ var callback = args.pop();
+ return eachfn(fns, function (fn, _, cb) {
+ fn.apply(that, args.concat([cb]));
+ },
+ callback);
+ });
+ if (args.length) {
+ return go.apply(this, args);
+ }
+ else {
+ return go;
+ }
+ });
+ }
+
+ async.applyEach = _applyEach(async.eachOf);
+ async.applyEachSeries = _applyEach(async.eachOfSeries);
+
+
+ async.forever = function (fn, callback) {
+ var done = only_once(callback || noop);
+ var task = ensureAsync(fn);
+ function next(err) {
+ if (err) {
+ return done(err);
+ }
+ task(next);
+ }
+ next();
+ };
+
+ function ensureAsync(fn) {
+ return _restParam(function (args) {
+ var callback = args.pop();
+ args.push(function () {
+ var innerArgs = arguments;
+ if (sync) {
+ async.setImmediate(function () {
+ callback.apply(null, innerArgs);
+ });
+ } else {
+ callback.apply(null, innerArgs);
+ }
+ });
+ var sync = true;
+ fn.apply(this, args);
+ sync = false;
+ });
+ }
+
+ async.ensureAsync = ensureAsync;
+
+ async.constant = _restParam(function(values) {
+ var args = [null].concat(values);
+ return function (callback) {
+ return callback.apply(this, args);
+ };
+ });
+
+ async.wrapSync =
+ async.asyncify = function asyncify(func) {
+ return _restParam(function (args) {
+ var callback = args.pop();
+ var result;
+ try {
+ result = func.apply(this, args);
+ } catch (e) {
+ return callback(e);
+ }
+ // if result is Promise object
+ if (_isObject(result) && typeof result.then === "function") {
+ result.then(function(value) {
+ callback(null, value);
+ })["catch"](function(err) {
+ callback(err.message ? err : new Error(err));
+ });
+ } else {
+ callback(null, result);
+ }
+ });
+ };
+
+ // Node.js
+ if (typeof module === 'object' && module.exports) {
+ module.exports = async;
+ }
+ // AMD / RequireJS
+ else if (typeof define === 'function' && define.amd) {
+ define([], function () {
+ return async;
+ });
+ }
+ // included directly via
+
+
+
+
+
+