diff --git a/chapter25/01_package.json b/chapter25/01_package.json new file mode 100644 index 0000000..f191c0d --- /dev/null +++ b/chapter25/01_package.json @@ -0,0 +1,10 @@ +{ + "name": "application-name" + , "version": "0.0.1" + , "private": true + , "dependencies": { + "express": "2.5.11" + , "jade": ">= 0.0.1" + , "mongoose": ">=2.7.0" + } +} \ No newline at end of file diff --git a/chapter25/02_user_schema.js b/chapter25/02_user_schema.js new file mode 100644 index 0000000..c0f536a --- /dev/null +++ b/chapter25/02_user_schema.js @@ -0,0 +1,9 @@ +var mongoose = require('mongoose'); + +var UserSchema = new mongoose.Schema({ + username: String, + name: String, + password: String +}); + +module.exports = UserSchema; \ No newline at end of file diff --git a/chapter25/03_user_model.js b/chapter25/03_user_model.js new file mode 100644 index 0000000..e93cb61 --- /dev/null +++ b/chapter25/03_user_model.js @@ -0,0 +1,6 @@ +var mongoose = require('mongoose'); +var UserSchema = require('../schemas/user'); + +var User = mongoose.model('User', UserSchema); + +module.exports = User; \ No newline at end of file diff --git a/chapter25/04_load_user_route_middleware_new_version.js b/chapter25/04_load_user_route_middleware_new_version.js new file mode 100644 index 0000000..74e75cf --- /dev/null +++ b/chapter25/04_load_user_route_middleware_new_version.js @@ -0,0 +1,16 @@ +var User = require('../../data/models/user'); + +function loadUser(req, res, next) { + User.findOne({username: req.params.name}, function(err, user) { + if (err) { + return next(err); + } + if (! user) { + return res.send('Not found', 404); + } + req.user = user; + next(); + }); +} + +module.exports = loadUser; \ No newline at end of file diff --git a/chapter25/05_user_routes_new_version.js b/chapter25/05_user_routes_new_version.js new file mode 100644 index 0000000..fde5058 --- /dev/null +++ b/chapter25/05_user_routes_new_version.js @@ -0,0 +1,56 @@ +/* + * User Routes + */ + +var User = require('../data/models/user'); +var notLoggedIn = require('./middleware/not_logged_in'); +var loadUser = require('./middleware/load_user'); +var restrictUserToSelf = require('./middleware/restrict_user_to_self'); + + +module.exports = function(app) { + + app.get('/users', function(req, res,next){ + User.find({}, function(err, users) { + if (err) { + return next(err); + } + res.render('users/index', {title: 'Users', users: users}); + }); + }); + + app.get('/users/new', notLoggedIn, function(req, res) { + res.render('users/new', {title: "New User"}); + }); + + app.get('/users/:name', loadUser, function(req, res, next){ + res.render('users/profile', {title: 'User profile', user: req.user}); + }); + + app.post('/users', notLoggedIn, function(req, res, next) { + User.findOne({username: req.body.username}, function(err, user) { + if (err) { + return next(err); + } + if (user) { + return res.send('Conflict', 409); + } + User.create(req.body, function(err) { + if (err) { + return next(err); + } + res.redirect('/users'); + }); + }); + }); + + app.del('/users/:name', loadUser, restrictUserToSelf, + function(req, res, next) { + req.user.remove(function(err) { + if (err) { return next(err); } + res.redirect('/users'); + }); + + }); + +}; \ No newline at end of file diff --git a/chapter25/06_user_list_with_paging.jade b/chapter25/06_user_list_with_paging.jade new file mode 100644 index 0000000..833b49e --- /dev/null +++ b/chapter25/06_user_list_with_paging.jade @@ -0,0 +1,21 @@ +h1 Users + +p + a(href="/users/new") Create new profile + + + + + +ul + - for(var username in users) { + li + a(href="/users/" + encodeURIComponent(username))= users[username].name + - }; + +- if (page > 0) { + a(href="?page=" + (page - 1)) Previous +   +- } + +a(href="?page=" + (page + 1)) Next \ No newline at end of file diff --git a/chapter25/07_user_list_with_paging_new_version.js b/chapter25/07_user_list_with_paging_new_version.js new file mode 100644 index 0000000..baa33a6 --- /dev/null +++ b/chapter25/07_user_list_with_paging_new_version.js @@ -0,0 +1,19 @@ +h1 Users + +p + a(href="/users/new") Create new profile + +ul + - users.forEach(function(user) { + li + a(href="/users/" + encodeURIComponent(user.username))= user.name + - }); + +- if (page > 0) { + a(href="?page=" + (page - 1)) Previous +   +- } + +- if (! lastPage) { + a(href="?page=" + (page + 1)) Next +- } \ No newline at end of file diff --git a/chapter25/08_package_new_version.json b/chapter25/08_package_new_version.json new file mode 100644 index 0000000..1376aad --- /dev/null +++ b/chapter25/08_package_new_version.json @@ -0,0 +1,11 @@ +{ + "name": "application-name" + , "version": "0.0.1" + , "private": true + , "dependencies": { + "express": "2.5.11" + , "jade": ">= 0.0.1" + , "mongoose": ">=2.7.0" + , "async": "0.1.22" + } +} \ No newline at end of file diff --git a/chapter25/09_user_schema_unique_index.js b/chapter25/09_user_schema_unique_index.js new file mode 100644 index 0000000..e518172 --- /dev/null +++ b/chapter25/09_user_schema_unique_index.js @@ -0,0 +1,9 @@ +var mongoose = require('mongoose'); + +var UserSchema = new mongoose.Schema({ + username: {type: String, unique: true}, + name: String, + password: String +}); + +module.exports = UserSchema; \ No newline at end of file diff --git a/chapter25/10_user_schema_with_validated_email.js b/chapter25/10_user_schema_with_validated_email.js new file mode 100644 index 0000000..b8cb0e5 --- /dev/null +++ b/chapter25/10_user_schema_with_validated_email.js @@ -0,0 +1,18 @@ +var mongoose = require('mongoose'); + +// simple but incomplete email regexp: +var emailRegexp = /.+\@.+\..+/; + +var UserSchema = new mongoose.Schema({ + username: {type: String, unique: true}, + name: String, + password: String, + email: { + type: String, + required: true, + match: emailRegexp + + } +}); + +module.exports = UserSchema; \ No newline at end of file diff --git a/chapter25/11_user_schema_with_enumerated_field.js b/chapter25/11_user_schema_with_enumerated_field.js new file mode 100644 index 0000000..2b03517 --- /dev/null +++ b/chapter25/11_user_schema_with_enumerated_field.js @@ -0,0 +1,22 @@ +var mongoose = require('mongoose'); + +var emailRegexp = /.+\@.+\..+/; + +var UserSchema = new mongoose.Schema({ + username: {type: String, unique: true}, + name: String, + password: String, + email: { + type: String, + required: true, + match: emailRegexp + }, + gender: { + type: String, + required: true, + uppercase: true, + 'enum': ['M', 'F'] + } +}); + +module.exports = UserSchema; \ No newline at end of file diff --git a/chapter25/12_user_form_with_gender_field.jade b/chapter25/12_user_form_with_gender_field.jade new file mode 100644 index 0000000..c48f724 --- /dev/null +++ b/chapter25/12_user_form_with_gender_field.jade @@ -0,0 +1,23 @@ +h1 New User + +form(method="POST", action="/users") + p + label(for="username") Username
+ input#username(name="username") + p + label(for="name") Name
+ input#name(name="name") + p + label(for="email") Email
+ input#email(name="email") + p + label(for="password") Password
+ input#password(type="password", name="password") + p + label(for="gender") Gender
+ input#gender(name="gender") + p + label(for="bio") Bio
+ textarea#bio(name="bio") + p + input(type="submit", value="Create") \ No newline at end of file diff --git a/chapter25/13_user_form_with_birthday.jade b/chapter25/13_user_form_with_birthday.jade new file mode 100644 index 0000000..d49c670 --- /dev/null +++ b/chapter25/13_user_form_with_birthday.jade @@ -0,0 +1,26 @@ +h1 New User + +form(method="POST", action="/users") + p + label(for="username") Username
+ input#username(name="username") + p + label(for="name") Name
+ input#name(name="name") + p + label(for="email") Email
+ input#email(name="email") + p + label(for="password") Password
+ input#password(type="password", name="password") + p + label(for="gender") Gender
+ input#gender(name="gender") + p + label(for="birthday") Birthday
+ input#birthday(name="birthday") + p + label(for="bio") Bio
+ textarea#bio(name="bio") + p + input(type="submit", value="Create") \ No newline at end of file diff --git a/chapter25/14_package_with_request.json b/chapter25/14_package_with_request.json new file mode 100644 index 0000000..8000eee --- /dev/null +++ b/chapter25/14_package_with_request.json @@ -0,0 +1,12 @@ +{ + "name": "application-name" + , "version": "0.0.1" + , "private": true + , "dependencies": { + "express": "2.5.11" + , "jade": ">= 0.0.1" + , "mongoose": ">=2.7.0" + , "async": "0.1.22" + , "request": "2.10.0" + } +} \ No newline at end of file diff --git a/chapter25/15_user_profile_with_virtual_attribute.jade b/chapter25/15_user_profile_with_virtual_attribute.jade new file mode 100644 index 0000000..cf838c0 --- /dev/null +++ b/chapter25/15_user_profile_with_virtual_attribute.jade @@ -0,0 +1,24 @@ +h1= user.name + +h2 Bio +p= user.bio + +h2 Email +p= user.email + +h2 Gender +p= user.gender + +h2 Birthday +p= user.birthday + +- if (user.twitter) { + h2 Twitter + p + a(href= user.twitter_url)="@" + user.twitter +- } + + +form(action="/users/" + encodeURIComponent(user.username), method="POST") + input(name="_method", type="hidden", value="DELETE") + input(type="submit", value="Delete") \ No newline at end of file diff --git a/chapter25/16_user_form_with_virtual_attribute.jade b/chapter25/16_user_form_with_virtual_attribute.jade new file mode 100644 index 0000000..72f0f65 --- /dev/null +++ b/chapter25/16_user_form_with_virtual_attribute.jade @@ -0,0 +1,29 @@ +h1 New User + +form(method="POST", action="/users") + p + label(for="username") Username
+ input#username(name="username") + p + label(for="full_name") Full name (first and last)
+ input#full_name(name="full_name") + p + label(for="email") Email
+ input#email(name="email") + p + label(for="password") Password
+ input#password(type="password", name="password") + p + label(for="gender") Gender
+ input#gender(name="gender") + p + label(for="birthday") Birthday
+ input#birthday(name="birthday") + p + label(for="twitter") Twitter Handle
+ input#twitter(name="twitter") + p + label(for="bio") Bio
+ textarea#bio(name="bio") + p + input(type="submit", value="Create") \ No newline at end of file diff --git a/chapter25/17_user_profile_with_virtual_attribute.jade b/chapter25/17_user_profile_with_virtual_attribute.jade new file mode 100644 index 0000000..0548af3 --- /dev/null +++ b/chapter25/17_user_profile_with_virtual_attribute.jade @@ -0,0 +1,24 @@ +h1= user.full_name + +h2 Bio +p= user.bio + +h2 Email +p= user.email + +h2 Gender +p= user.gender + +h2 Birthday +p= user.birthday + +- if (user.twitter) { + h2 Twitter + p + a(href= user.twitter_url)="@" + user.twitter +- } + + +form(action="/users/" + encodeURIComponent(user.username), method="POST") + input(name="_method", type="hidden", value="DELETE") + input(type="submit", value="Delete") \ No newline at end of file diff --git a/chapter25/18_user_list_with_virtual_attribute.jade b/chapter25/18_user_list_with_virtual_attribute.jade new file mode 100644 index 0000000..b880402 --- /dev/null +++ b/chapter25/18_user_list_with_virtual_attribute.jade @@ -0,0 +1,19 @@ +h1 Users + +p + a(href="/users/new") Create new profile + +ul + - users.forEach(function(user) { + li + a(href="/users/" + encodeURIComponent(user.username))= user.full_name + - }); + +- if (page > 0) { + a(href="?page=" + (page - 1)) Previous +   +- } + +- if (! lastPage) { + a(href="?page=" + (page + 1)) Next +- } \ No newline at end of file diff --git a/chapter25/19_article_schema.js b/chapter25/19_article_schema.js new file mode 100644 index 0000000..3d01bb4 --- /dev/null +++ b/chapter25/19_article_schema.js @@ -0,0 +1,20 @@ +var Schema = require('mongoose').Schema; + +var ArticleSchema = new Schema({ + title: { + type: String, + unique: true + }, + body: String, + author: { + type: Schema.ObjectId, + ref: 'User', + required: true + }, + created_at: { + type: Date, + 'default': Date.now + } +}); + +module.exports = ArticleSchema; \ No newline at end of file diff --git a/chapter25/20_article_model.js b/chapter25/20_article_model.js new file mode 100644 index 0000000..36701ab --- /dev/null +++ b/chapter25/20_article_model.js @@ -0,0 +1,6 @@ +var mongoose = require('mongoose'); +var ArticleSchema = require('../schemas/article'); + +var Article = mongoose.model('Article', ArticleSchema); + +module.exports = Article; \ No newline at end of file diff --git a/chapter25/21_article_routes.js b/chapter25/21_article_routes.js new file mode 100644 index 0000000..8ba70b0 --- /dev/null +++ b/chapter25/21_article_routes.js @@ -0,0 +1,96 @@ +/* + * Article Routes + */ + +var async = require('async'); + +var Article = require('../data/models/article'); +var notLoggedIn = require('./middleware/not_logged_in'); +var loadArticle = require('./middleware/load_article'); +var loggedIn = require('./middleware/logged_in'); + +var maxArticlesPerPage = 5; + +module.exports = function(app) { + + app.get('/articles', function(req, res, next){ + var page = req.query.page && parseInt(req.query.page, 10) || 0; + async.parallel([ + + function(next) { + Article.count(next); + }, + + function(next) { + Article.find({}) + .sort('title', 1) + .skip(page * maxArticlesPerPage) + .limit(maxArticlesPerPage) + .exec(next); + } + ], + + // final callback + function(err, results) { + + if (err) { + return next(err); + } + + var count = results[0]; + var articles = results[1]; + + var lastPage = (page + 1) * maxArticlesPerPage >= count; + + res.render('articles/index', { + title: 'Articles', + articles: articles, + page: page, + lastPage: lastPage + }); + + } + ); + }); + + app.get('/articles/new', loggedIn, function(req, res) { + res.render('Articles/new', {title: "New Article"}); + }); + + app.get('/articles/:title', loadArticle, function(req, res, next){ + res.render('articles/article', {title: req.article.title, + article: req.article}); + }); + + app.post('/articles', loggedIn, function(req, res, next) { + var article = req.body; + article.author = req.session.user._id; + Article.create(article, function(err) { + if (err) { + if (err.code === 11000) { + res.send('Conflict', 409); + } else { + if (err.name === 'ValidationError') { + return res.send(Object.keys(err.errors).map(function(errField) { + return err.errors[errField].message; + }).join('. '), 406); + } else { + next(err); + } + + } + return; + } + res.redirect('/articles'); + }); + }); + + app.del('/articles/:title', loggedIn, loadArticle, function(req, res, next) { + req.article.remove(function(err) { + if (err) { return next(err); } + res.redirect('/articles'); + }); + + }); + +}; \ No newline at end of file diff --git a/chapter25/22_load_article_middleware.js b/chapter25/22_load_article_middleware.js new file mode 100644 index 0000000..631039b --- /dev/null +++ b/chapter25/22_load_article_middleware.js @@ -0,0 +1,18 @@ +var Article = require('../../data/models/article'); + +function loadArticle(req, res, next) { + Article.findOne({title: req.params.title}) + .populate('author') + .exec(function(err, article) { + if (err) { + return next(err); + } + if (! article) { + return res.send('Not found', 404); + } + req.article = article; + next(); + }); +} + +module.exports = loadArticle; \ No newline at end of file diff --git a/chapter25/23_article_detail.jade b/chapter25/23_article_detail.jade new file mode 100644 index 0000000..b3c8112 --- /dev/null +++ b/chapter25/23_article_detail.jade @@ -0,0 +1,13 @@ +h1= article.title + +div!= article.body + +hr + +p + span Author: +   + a(href="/users/" + encodeURIComponent(article.author.username))= article.author.full_name + +p + a(href="/articles") Back to all articles \ No newline at end of file diff --git a/chapter25/24_logged_in_middleware.js b/chapter25/24_logged_in_middleware.js new file mode 100644 index 0000000..f726997 --- /dev/null +++ b/chapter25/24_logged_in_middleware.js @@ -0,0 +1,9 @@ +function loggedIn(req, res, next) { + if (! req.session.user) { + res.send('Forbidden. Please log in first.', 403); + } else { + next(); + } +} + +module.exports = loggedIn; \ No newline at end of file diff --git a/chapter25/25_article_list.jade b/chapter25/25_article_list.jade new file mode 100644 index 0000000..515318a --- /dev/null +++ b/chapter25/25_article_list.jade @@ -0,0 +1,19 @@ +h1 Articles + +p + a(href="/articles/new") Create new article + +ul + - articles.forEach(function(article) { + li + a(href="/articles/" + encodeURIComponent(article.title))= article.title + - }); + +- if (page > 0) { + a(href="?page=" + (page - 1)) Previous +   +- } + +- if (! lastPage) { + a(href="?page=" + (page + 1)) Next +- } diff --git a/chapter25/26_article_creation_form.jade b/chapter25/26_article_creation_form.jade new file mode 100644 index 0000000..322cc73 --- /dev/null +++ b/chapter25/26_article_creation_form.jade @@ -0,0 +1,11 @@ +h1 New Article + +form(method="POST", action="/articles") + p + label(for="title") Title
+ input#title(name="title") + p + label(for="body") Body
+ textarea#body(name="body") + p + input(type="submit", value="Create") diff --git a/chapter25/27_article_list_partial.jade b/chapter25/27_article_list_partial.jade new file mode 100644 index 0000000..df95420 --- /dev/null +++ b/chapter25/27_article_list_partial.jade @@ -0,0 +1,5 @@ +ul + - articles.forEach(function(article) { + li + a(href="/articles/" + encodeURIComponent(article.title))= article.title + - }); \ No newline at end of file diff --git a/chapter25/28_article_list_new_version.jade b/chapter25/28_article_list_new_version.jade new file mode 100644 index 0000000..d04e0ea --- /dev/null +++ b/chapter25/28_article_list_new_version.jade @@ -0,0 +1,15 @@ +h1 Articles + +p + a(href="/articles/new") Create new article + +p!= partial('articles/list', {articles: articles}) + +- if (page > 0) { + a(href="?page=" + (page - 1)) Previous +   +- } + +- if (! lastPage) { + a(href="?page=" + (page + 1)) Next +- } \ No newline at end of file diff --git a/test/app.js b/test/app.js new file mode 100644 index 0000000..52923be --- /dev/null +++ b/test/app.js @@ -0,0 +1,44 @@ + +/** + * Module dependencies. + */ + +var express = require('express') + , routes = require('./routes'); +var async = require('async'); +var app = module.exports = express.createServer(); + +// Configuration + +app.configure(function(){ + app.set('views', __dirname + '/views'); + app.set('view engine', 'jade'); + app.use(express.bodyParser()); + app.use(express.methodOverride()); + app.use(express.cookieParser('my secret string')); + app.use(express.session({ + secret: 'my secret string', + maxAge: 3600000 + })); + app.use(app.router); + app.use(express.static(__dirname + '/public')); +}); + +app.configure('development', function(){ + app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); +}); + +app.configure('production', function(){ + app.use(express.errorHandler()); +}); + +// Routes + +require('./routes/index')(app); +require('./routes/users')(app); +require('./routes/session')(app); + + +app.listen(3000, function(){ + console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env); +}); diff --git a/test/data/models/user.js b/test/data/models/user.js new file mode 100644 index 0000000..e93cb61 --- /dev/null +++ b/test/data/models/user.js @@ -0,0 +1,6 @@ +var mongoose = require('mongoose'); +var UserSchema = require('../schemas/user'); + +var User = mongoose.model('User', UserSchema); + +module.exports = User; \ No newline at end of file diff --git a/test/data/users.json b/test/data/users.json new file mode 100644 index 0000000..a9d0ffd --- /dev/null +++ b/test/data/users.json @@ -0,0 +1,24 @@ +{ + + "frank": { + "username": "frank", + "name": "Frank Sinatra", + "bio": "Singer", + "password": "password" + }, + + "jobim": { + "username": "jobim", + "name": "Antonio Carlos Jobim", + "bio": "Composer", + "password": "password" + }, + + "fred": { + "username": "fred", + "name": "Fred Astaire", + "bio": "Dancer and Actor", + "password": "password" + } + +} \ No newline at end of file diff --git a/test/node_modules/.bin/express b/test/node_modules/.bin/express new file mode 100644 index 0000000..cad5a1e --- /dev/null +++ b/test/node_modules/.bin/express @@ -0,0 +1,15 @@ +#!/bin/sh +basedir=`dirname "$0"` + +case `uname` in + *CYGWIN*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + "$basedir/node" "$basedir/../express/bin/express" "$@" + ret=$? +else + node "$basedir/../express/bin/express" "$@" + ret=$? +fi +exit $ret diff --git a/test/node_modules/.bin/express.cmd b/test/node_modules/.bin/express.cmd new file mode 100644 index 0000000..54cebb3 --- /dev/null +++ b/test/node_modules/.bin/express.cmd @@ -0,0 +1,6 @@ +:: Created by npm, please don't edit manually. +@IF EXIST "%~dp0\node.exe" ( + "%~dp0\node.exe" "%~dp0\..\express\bin\express" %* +) ELSE ( + node "%~dp0\..\express\bin\express" %* +) \ No newline at end of file diff --git a/test/node_modules/.bin/jade b/test/node_modules/.bin/jade new file mode 100644 index 0000000..7de066b --- /dev/null +++ b/test/node_modules/.bin/jade @@ -0,0 +1,15 @@ +#!/bin/sh +basedir=`dirname "$0"` + +case `uname` in + *CYGWIN*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + "$basedir/node" "$basedir/../jade/bin/jade" "$@" + ret=$? +else + node "$basedir/../jade/bin/jade" "$@" + ret=$? +fi +exit $ret diff --git a/test/node_modules/.bin/jade.cmd b/test/node_modules/.bin/jade.cmd new file mode 100644 index 0000000..2d85713 --- /dev/null +++ b/test/node_modules/.bin/jade.cmd @@ -0,0 +1,6 @@ +:: Created by npm, please don't edit manually. +@IF EXIST "%~dp0\node.exe" ( + "%~dp0\node.exe" "%~dp0\..\jade\bin\jade" %* +) ELSE ( + node "%~dp0\..\jade\bin\jade" %* +) \ No newline at end of file diff --git a/test/node_modules/async/.gitmodules b/test/node_modules/async/.gitmodules new file mode 100644 index 0000000..a9aae98 --- /dev/null +++ b/test/node_modules/async/.gitmodules @@ -0,0 +1,9 @@ +[submodule "deps/nodeunit"] + path = deps/nodeunit + url = git://github.com/caolan/nodeunit.git +[submodule "deps/UglifyJS"] + path = deps/UglifyJS + url = https://github.com/mishoo/UglifyJS.git +[submodule "deps/nodelint"] + path = deps/nodelint + url = https://github.com/tav/nodelint.git diff --git a/test/node_modules/async/.npmignore b/test/node_modules/async/.npmignore new file mode 100644 index 0000000..9bdfc97 --- /dev/null +++ b/test/node_modules/async/.npmignore @@ -0,0 +1,4 @@ +deps +dist +test +nodelint.cfg \ No newline at end of file diff --git a/test/node_modules/async/LICENSE b/test/node_modules/async/LICENSE new file mode 100644 index 0000000..b7f9d50 --- /dev/null +++ b/test/node_modules/async/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 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/test/node_modules/async/Makefile b/test/node_modules/async/Makefile new file mode 100644 index 0000000..bad647c --- /dev/null +++ b/test/node_modules/async/Makefile @@ -0,0 +1,25 @@ +PACKAGE = asyncjs +NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node) +CWD := $(shell pwd) +NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit +UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs +NODELINT = $(CWD)/node_modules/nodelint/nodelint + +BUILDDIR = dist + +all: clean test build + +build: $(wildcard lib/*.js) + mkdir -p $(BUILDDIR) + $(UGLIFY) lib/async.js > $(BUILDDIR)/async.min.js + +test: + $(NODEUNIT) test + +clean: + rm -rf $(BUILDDIR) + +lint: + $(NODELINT) --config nodelint.cfg lib/async.js + +.PHONY: test build all diff --git a/test/node_modules/async/README.md b/test/node_modules/async/README.md new file mode 100644 index 0000000..1bbbc47 --- /dev/null +++ b/test/node_modules/async/README.md @@ -0,0 +1,1021 @@ +# Async.js + +Async is a utility module which provides straight-forward, powerful functions +for working with asynchronous JavaScript. Although originally designed for +use with [node.js](http://nodejs.org), it can also be used directly in the +browser. + +Async provides around 20 functions that include the usual 'functional' +suspects (map, reduce, filter, forEach…) as well as some common patterns +for asynchronous control flow (parallel, series, waterfall…). All these +functions assume you follow the node.js convention of providing a single +callback as the last argument of your async function. + + +## Quick Examples + + async.map(['file1','file2','file3'], fs.stat, function(err, results){ + // results is now an array of stats for each file + }); + + async.filter(['file1','file2','file3'], path.exists, function(results){ + // results now equals an array of the existing files + }); + + async.parallel([ + function(){ ... }, + function(){ ... } + ], callback); + + async.series([ + function(){ ... }, + function(){ ... } + ]); + +There are many more functions available so take a look at the docs below for a +full list. This module aims to be comprehensive, so if you feel anything is +missing please create a GitHub issue for it. + + +## Download + +Releases are available for download from +[GitHub](http://github.com/caolan/async/downloads). +Alternatively, you can install using Node Package Manager (npm): + + npm install async + + +__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 17.5kb Uncompressed + +__Production:__ [async.min.js](https://github.com/caolan/async/raw/master/dist/async.min.js) - 1.7kb Packed and Gzipped + + +## In the Browser + +So far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage: + + + + + +## Documentation + +### Collections + +* [forEach](#forEach) +* [map](#map) +* [filter](#filter) +* [reject](#reject) +* [reduce](#reduce) +* [detect](#detect) +* [sortBy](#sortBy) +* [some](#some) +* [every](#every) +* [concat](#concat) + +### Control Flow + +* [series](#series) +* [parallel](#parallel) +* [whilst](#whilst) +* [until](#until) +* [waterfall](#waterfall) +* [queue](#queue) +* [auto](#auto) +* [iterator](#iterator) +* [apply](#apply) +* [nextTick](#nextTick) + +### Utils + +* [memoize](#memoize) +* [unmemoize](#unmemoize) +* [log](#log) +* [dir](#dir) +* [noConflict](#noConflict) + + +## Collections + + +### forEach(arr, iterator, callback) + +Applies an iterator function to each item in an array, in parallel. +The iterator is called with an item from the list and a callback for when it +has finished. If the iterator passes an error to this callback, the main +callback for the forEach function is immediately called with the error. + +Note, that since this function applies the iterator to each item in parallel +there is no guarantee that the iterator functions will complete in order. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(err) - A callback which is called after all the iterator functions + have finished, or an error has occurred. + +__Example__ + + // assuming openFiles is an array of file names and saveFile is a function + // to save the modified contents of that file: + + async.forEach(openFiles, saveFile, function(err){ + // if any of the saves produced an error, err would equal that error + }); + +--------------------------------------- + + +### forEachSeries(arr, iterator, callback) + +The same as forEach only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. This means the iterator functions will complete in order. + + +--------------------------------------- + + +### forEachLimit(arr, limit, iterator, callback) + +The same as forEach only the iterator is applied to batches of items in the +array, in series. The next batch of iterators is only called once the current +one has completed processing. + +__Arguments__ + +* arr - An array to iterate over. +* limit - How many items should be in each batch. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(err) - A callback which is called after all the iterator functions + have finished, or an error has occurred. + +__Example__ + + // Assume documents is an array of JSON objects and requestApi is a + // function that interacts with a rate-limited REST api. + + async.forEachLimit(documents, 20, requestApi, function(err){ + // if any of the saves produced an error, err would equal that error + }); +--------------------------------------- + + +### map(arr, iterator, callback) + +Produces a new array of values by mapping each value in the given array through +the iterator function. The iterator is called with an item from the array and a +callback for when it has finished processing. The callback takes 2 arguments, +an error and the transformed item from the array. If the iterator passes an +error to this callback, the main callback for the map function is immediately +called with the error. + +Note, that since this function applies the iterator to each item in parallel +there is no guarantee that the iterator functions will complete in order, however +the results array will be in the same order as the original array. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed + with an error (which can be null) and a transformed item. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is an array of the + transformed items from the original array. + +__Example__ + + async.map(['file1','file2','file3'], fs.stat, function(err, results){ + // results is now an array of stats for each file + }); + +--------------------------------------- + + +### mapSeries(arr, iterator, callback) + +The same as map only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. The results array will be in the same order as the original. + + +--------------------------------------- + + +### filter(arr, iterator, callback) + +__Alias:__ select + +Returns a new array of all the values which pass an async truth test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like path.exists. This operation is +performed in parallel, but the results array will be in the same order as the +original. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(results) - A callback which is called after all the iterator + functions have finished. + +__Example__ + + async.filter(['file1','file2','file3'], path.exists, function(results){ + // results now equals an array of the existing files + }); + +--------------------------------------- + + +### filterSeries(arr, iterator, callback) + +__alias:__ selectSeries + +The same as filter only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. The results array will be in the same order as the original. + +--------------------------------------- + + +### reject(arr, iterator, callback) + +The opposite of filter. Removes values that pass an async truth test. + +--------------------------------------- + + +### rejectSeries(arr, iterator, callback) + +The same as filter, only the iterator is applied to each item in the array +in series. + + +--------------------------------------- + + +### reduce(arr, memo, iterator, callback) + +__aliases:__ inject, foldl + +Reduces a list of values into a single value using an async iterator to return +each successive step. Memo is the initial state of the reduction. This +function only operates in series. For performance reasons, it may make sense to +split a call to this function into a parallel map, then use the normal +Array.prototype.reduce on the results. This function is for situations where +each step in the reduction needs to be async, if you can get the data before +reducing it then its probably a good idea to do so. + +__Arguments__ + +* arr - An array to iterate over. +* memo - The initial state of the reduction. +* iterator(memo, item, callback) - A function applied to each item in the + array to produce the next step in the reduction. The iterator is passed a + callback which accepts an optional error as its first argument, and the state + of the reduction as the second. If an error is passed to the callback, the + reduction is stopped and the main callback is immediately called with the + error. +* callback(err, result) - A callback which is called after all the iterator + functions have finished. Result is the reduced value. + +__Example__ + + async.reduce([1,2,3], 0, function(memo, item, callback){ + // pointless async: + process.nextTick(function(){ + callback(null, memo + item) + }); + }, function(err, result){ + // result is now equal to the last value of memo, which is 6 + }); + +--------------------------------------- + + +### reduceRight(arr, memo, iterator, callback) + +__Alias:__ foldr + +Same as reduce, only operates on the items in the array in reverse order. + + +--------------------------------------- + + +### detect(arr, iterator, callback) + +Returns the first value in a list that passes an async truth test. The +iterator is applied in parallel, meaning the first iterator to return true will +fire the detect callback with that result. That means the result might not be +the first item in the original array (in terms of order) that passes the test. + +If order within the original array is important then look at detectSeries. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(result) - A callback which is called as soon as any iterator returns + true, or after all the iterator functions have finished. Result will be + the first item in the array that passes the truth test (iterator) or the + value undefined if none passed. + +__Example__ + + async.detect(['file1','file2','file3'], path.exists, function(result){ + // result now equals the first file in the list that exists + }); + +--------------------------------------- + + +### detectSeries(arr, iterator, callback) + +The same as detect, only the iterator is applied to each item in the array +in series. This means the result is always the first in the original array (in +terms of array order) that passes the truth test. + + +--------------------------------------- + + +### sortBy(arr, iterator, callback) + +Sorts a list by the results of running each value through an async iterator. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed + with an error (which can be null) and a value to use as the sort criteria. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is the items from + the original array sorted by the values returned by the iterator calls. + +__Example__ + + async.sortBy(['file1','file2','file3'], function(file, callback){ + fs.stat(file, function(err, stats){ + callback(err, stats.mtime); + }); + }, function(err, results){ + // results is now the original array of files sorted by + // modified date + }); + + +--------------------------------------- + + +### some(arr, iterator, callback) + +__Alias:__ any + +Returns true if at least one element in the array satisfies an async test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like path.exists. Once any iterator +call returns true, the main callback is immediately called. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(result) - A callback which is called as soon as any iterator returns + true, or after all the iterator functions have finished. Result will be + either true or false depending on the values of the async tests. + +__Example__ + + async.some(['file1','file2','file3'], path.exists, function(result){ + // if result is true then at least one of the files exists + }); + +--------------------------------------- + + +### every(arr, iterator, callback) + +__Alias:__ all + +Returns true if every element in the array satisfies an async test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like path.exists. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(result) - A callback which is called after all the iterator + functions have finished. Result will be either true or false depending on + the values of the async tests. + +__Example__ + + async.every(['file1','file2','file3'], path.exists, function(result){ + // if result is true then every file exists + }); + +--------------------------------------- + + +### concat(arr, iterator, callback) + +Applies an iterator to each item in a list, concatenating the results. Returns the +concatenated list. The iterators are called in parallel, and the results are +concatenated as they return. There is no guarantee that the results array will +be returned in the original order of the arguments passed to the iterator function. + +__Arguments__ + +* arr - An array to iterate over +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed + with an error (which can be null) and an array of results. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is an array containing + the concatenated results of the iterator function. + +__Example__ + + async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){ + // files is now a list of filenames that exist in the 3 directories + }); + +--------------------------------------- + + +### concatSeries(arr, iterator, callback) + +Same as async.concat, but executes in series instead of parallel. + + +## Control Flow + + +### series(tasks, [callback]) + +Run an array of functions in series, each one running once the previous +function has completed. If any functions in the series pass an error to its +callback, no more functions are run and the callback for the series is +immediately called with the value of the error. Once the tasks have completed, +the results are passed to the final callback as an array. + +It is also possible to use an object instead of an array. Each property will be +run as a function and the results will be passed to the final callback as an object +instead of an array. This can be a more readable way of handling results from +async.series. + + +__Arguments__ + +* tasks - An array or object containing functions to run, each function is passed + a callback it must call on completion. +* callback(err, results) - An optional callback to run once all the functions + have completed. This function gets an array of all the arguments passed to + the callbacks used in the array. + +__Example__ + + async.series([ + function(callback){ + // do some stuff ... + callback(null, 'one'); + }, + function(callback){ + // do some more stuff ... + callback(null, 'two'); + }, + ], + // optional callback + function(err, results){ + // results is now equal to ['one', 'two'] + }); + + + // an example using an object instead of an array + async.series({ + one: function(callback){ + setTimeout(function(){ + callback(null, 1); + }, 200); + }, + two: function(callback){ + setTimeout(function(){ + callback(null, 2); + }, 100); + }, + }, + function(err, results) { + // results is now equal to: {one: 1, two: 2} + }); + + +--------------------------------------- + + +### parallel(tasks, [callback]) + +Run an array of functions in parallel, without waiting until the previous +function has completed. If any of the functions pass an error to its +callback, the main callback is immediately called with the value of the error. +Once the tasks have completed, the results are passed to the final callback as an +array. + +It is also possible to use an object instead of an array. Each property will be +run as a function and the results will be passed to the final callback as an object +instead of an array. This can be a more readable way of handling results from +async.parallel. + + +__Arguments__ + +* tasks - An array or object containing functions to run, each function is passed a + callback it must call on completion. +* callback(err, results) - An optional callback to run once all the functions + have completed. This function gets an array of all the arguments passed to + the callbacks used in the array. + +__Example__ + + async.parallel([ + function(callback){ + setTimeout(function(){ + callback(null, 'one'); + }, 200); + }, + function(callback){ + setTimeout(function(){ + callback(null, 'two'); + }, 100); + }, + ], + // optional callback + function(err, results){ + // the results array will equal ['one','two'] even though + // the second function had a shorter timeout. + }); + + + // an example using an object instead of an array + async.parallel({ + one: function(callback){ + setTimeout(function(){ + callback(null, 1); + }, 200); + }, + two: function(callback){ + setTimeout(function(){ + callback(null, 2); + }, 100); + }, + }, + function(err, results) { + // results is now equals to: {one: 1, two: 2} + }); + + +--------------------------------------- + + +### whilst(test, fn, callback) + +Repeatedly call fn, while test returns true. Calls the callback when stopped, +or an error occurs. + +__Arguments__ + +* test() - synchronous truth test to perform before each execution of fn. +* fn(callback) - A function to call each time the test passes. The function is + passed a callback which must be called once it has completed with an optional + error as the first argument. +* callback(err) - A callback which is called after the test fails and repeated + execution of fn has stopped. + +__Example__ + + var count = 0; + + async.whilst( + function () { return count < 5; }, + function (callback) { + count++; + setTimeout(callback, 1000); + }, + function (err) { + // 5 seconds have passed + } + ); + + +--------------------------------------- + + +### until(test, fn, callback) + +Repeatedly call fn, until test returns true. Calls the callback when stopped, +or an error occurs. + +The inverse of async.whilst. + + +--------------------------------------- + + +### waterfall(tasks, [callback]) + +Runs an array of functions in series, each passing their results to the next in +the array. However, if any of the functions pass an error to the callback, the +next function is not executed and the main callback is immediately called with +the error. + +__Arguments__ + +* tasks - An array of functions to run, each function is passed a callback it + must call on completion. +* callback(err, [results]) - An optional callback to run once all the functions + have completed. This will be passed the results of the last task's callback. + + + +__Example__ + + async.waterfall([ + function(callback){ + callback(null, 'one', 'two'); + }, + function(arg1, arg2, callback){ + callback(null, 'three'); + }, + function(arg1, callback){ + // arg1 now equals 'three' + callback(null, 'done'); + } + ], function (err, result) { + // result now equals 'done' + }); + + +--------------------------------------- + + +### queue(worker, concurrency) + +Creates a queue object with the specified concurrency. Tasks added to the +queue will be processed in parallel (up to the concurrency limit). If all +workers are in progress, the task is queued until one is available. Once +a worker has completed a task, the task's callback is called. + +__Arguments__ + +* worker(task, callback) - An asynchronous function for processing a queued + task. +* concurrency - An integer for determining how many worker functions should be + run in parallel. + +__Queue objects__ + +The queue object returned by this function has the following properties and +methods: + +* length() - a function returning the number of items waiting to be processed. +* concurrency - an integer for determining how many worker functions should be + run in parallel. This property can be changed after a queue is created to + alter the concurrency on-the-fly. +* push(task, [callback]) - add a new task to the queue, the callback is called + once the worker has finished processing the task. + instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list. +* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued +* empty - a callback that is called when the last item from the queue is given to a worker +* drain - a callback that is called when the last item from the queue has returned from the worker + +__Example__ + + // create a queue object with concurrency 2 + + var q = async.queue(function (task, callback) { + console.log('hello ' + task.name); + callback(); + }, 2); + + + // assign a callback + q.drain = function() { + console.log('all items have been processed'); + } + + // add some items to the queue + + q.push({name: 'foo'}, function (err) { + console.log('finished processing foo'); + }); + q.push({name: 'bar'}, function (err) { + console.log('finished processing bar'); + }); + + // add some items to the queue (batch-wise) + + q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) { + console.log('finished processing bar'); + }); + + +--------------------------------------- + + +### auto(tasks, [callback]) + +Determines the best order for running functions based on their requirements. +Each function can optionally depend on other functions being completed first, +and each function is run as soon as its requirements are satisfied. If any of +the functions pass an error to their callback, that function will not complete +(so any other functions depending on it will not run) and the main callback +will be called immediately with the error. Functions also receive an object +containing the results of functions which have completed so far. + +__Arguments__ + +* tasks - An object literal containing named functions or an array of + requirements, with the function itself the last item in the array. The key + used for each function or array is used when specifying requirements. The + syntax is easier to understand by looking at the example. +* callback(err, results) - An optional callback which is called when all the + tasks have been completed. The callback will receive an error as an argument + if any tasks pass an error to their callback. If all tasks complete + successfully, it will receive an object containing their results. + +__Example__ + + async.auto({ + get_data: function(callback){ + // async code to get some data + }, + make_folder: function(callback){ + // async code to create a directory to store a file in + // this is run at the same time as getting the data + }, + write_file: ['get_data', 'make_folder', function(callback){ + // once there is some data and the directory exists, + // write the data to a file in the directory + callback(null, filename); + }], + email_link: ['write_file', function(callback, results){ + // once the file is written let's email a link to it... + // results.write_file contains the filename returned by write_file. + }] + }); + +This is a fairly trivial example, but to do this using the basic parallel and +series functions would look like this: + + async.parallel([ + function(callback){ + // async code to get some data + }, + function(callback){ + // async code to create a directory to store a file in + // this is run at the same time as getting the data + } + ], + function(results){ + async.series([ + function(callback){ + // once there is some data and the directory exists, + // write the data to a file in the directory + }, + email_link: function(callback){ + // once the file is written let's email a link to it... + } + ]); + }); + +For a complicated series of async tasks using the auto function makes adding +new tasks much easier and makes the code more readable. + + +--------------------------------------- + + +### iterator(tasks) + +Creates an iterator function which calls the next function in the array, +returning a continuation to call the next one after that. Its also possible to +'peek' the next iterator by doing iterator.next(). + +This function is used internally by the async module but can be useful when +you want to manually control the flow of functions in series. + +__Arguments__ + +* tasks - An array of functions to run, each function is passed a callback it + must call on completion. + +__Example__ + + var iterator = async.iterator([ + function(){ sys.p('one'); }, + function(){ sys.p('two'); }, + function(){ sys.p('three'); } + ]); + + node> var iterator2 = iterator(); + 'one' + node> var iterator3 = iterator2(); + 'two' + node> iterator3(); + 'three' + node> var nextfn = iterator2.next(); + node> nextfn(); + 'three' + + +--------------------------------------- + + +### apply(function, arguments..) + +Creates a continuation function with some arguments already applied, a useful +shorthand when combined with other control flow functions. Any arguments +passed to the returned function are added to the arguments originally passed +to apply. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to automatically apply when the + continuation is called. + +__Example__ + + // using apply + + async.parallel([ + async.apply(fs.writeFile, 'testfile1', 'test1'), + async.apply(fs.writeFile, 'testfile2', 'test2'), + ]); + + + // the same process without using apply + + async.parallel([ + function(callback){ + fs.writeFile('testfile1', 'test1', callback); + }, + function(callback){ + fs.writeFile('testfile2', 'test2', callback); + }, + ]); + +It's possible to pass any number of additional arguments when calling the +continuation: + + node> var fn = async.apply(sys.puts, 'one'); + node> fn('two', 'three'); + one + two + three + +--------------------------------------- + + +### nextTick(callback) + +Calls the callback on a later loop around the event loop. In node.js this just +calls process.nextTick, in the browser it falls back to setTimeout(callback, 0), +which means other higher priority events may precede the execution of the callback. + +This is used internally for browser-compatibility purposes. + +__Arguments__ + +* callback - The function to call on a later loop around the event loop. + +__Example__ + + var call_order = []; + async.nextTick(function(){ + call_order.push('two'); + // call_order now equals ['one','two] + }); + call_order.push('one') + + +## Utils + + +### memoize(fn, [hasher]) + +Caches the results of an async function. When creating a hash to store function +results against, the callback is omitted from the hash and an optional hash +function can be used. + +__Arguments__ + +* fn - the function you to proxy and cache results from. +* hasher - an optional function for generating a custom hash for storing + results, it has all the arguments applied to it apart from the callback, and + must be synchronous. + +__Example__ + + var slow_fn = function (name, callback) { + // do something + callback(null, result); + }; + var fn = async.memoize(slow_fn); + + // fn can now be used as if it were slow_fn + fn('some name', function () { + // callback + }); + + +### unmemoize(fn) + +Undoes a memoized function, reverting it to the original, unmemoized +form. Comes handy in tests. + +__Arguments__ + +* fn - the memoized function + + +### log(function, arguments) + +Logs the result of an async function to the console. Only works in node.js or +in browsers that support console.log and console.error (such as FF and Chrome). +If multiple arguments are returned from the async function, console.log is +called on each argument in order. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to apply to the function. + +__Example__ + + var hello = function(name, callback){ + setTimeout(function(){ + callback(null, 'hello ' + name); + }, 1000); + }; + + node> async.log(hello, 'world'); + 'hello world' + + +--------------------------------------- + + +### dir(function, arguments) + +Logs the result of an async function to the console using console.dir to +display the properties of the resulting object. Only works in node.js or +in browsers that support console.dir and console.error (such as FF and Chrome). +If multiple arguments are returned from the async function, console.dir is +called on each argument in order. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to apply to the function. + +__Example__ + + var hello = function(name, callback){ + setTimeout(function(){ + callback(null, {hello: name}); + }, 1000); + }; + + node> async.dir(hello, 'world'); + {hello: 'world'} + + +--------------------------------------- + + +### noConflict() + +Changes the value of async back to its original value, returning a reference to the +async object. diff --git a/test/node_modules/async/index.js b/test/node_modules/async/index.js new file mode 100644 index 0000000..8e23845 --- /dev/null +++ b/test/node_modules/async/index.js @@ -0,0 +1,3 @@ +// This file is just added for convenience so this repository can be +// directly checked out into a project's deps folder +module.exports = require('./lib/async'); diff --git a/test/node_modules/async/lib/async.js b/test/node_modules/async/lib/async.js new file mode 100644 index 0000000..7cc4f5e --- /dev/null +++ b/test/node_modules/async/lib/async.js @@ -0,0 +1,692 @@ +/*global setTimeout: false, console: false */ +(function () { + + var async = {}; + + // global on the server, window in the browser + var root = this, + previous_async = root.async; + + if (typeof module !== 'undefined' && module.exports) { + module.exports = async; + } + else { + root.async = async; + } + + async.noConflict = function () { + root.async = previous_async; + return async; + }; + + //// cross-browser compatiblity functions //// + + var _forEach = function (arr, iterator) { + if (arr.forEach) { + return arr.forEach(iterator); + } + for (var i = 0; i < arr.length; i += 1) { + iterator(arr[i], i, arr); + } + }; + + var _map = function (arr, iterator) { + if (arr.map) { + return arr.map(iterator); + } + var results = []; + _forEach(arr, function (x, i, a) { + results.push(iterator(x, i, a)); + }); + return results; + }; + + var _reduce = function (arr, iterator, memo) { + if (arr.reduce) { + return arr.reduce(iterator, memo); + } + _forEach(arr, function (x, i, a) { + memo = iterator(memo, x, i, a); + }); + return memo; + }; + + var _keys = function (obj) { + if (Object.keys) { + return Object.keys(obj); + } + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + } + } + return keys; + }; + + //// exported async module functions //// + + //// nextTick implementation with browser-compatible fallback //// + if (typeof process === 'undefined' || !(process.nextTick)) { + async.nextTick = function (fn) { + setTimeout(fn, 0); + }; + } + else { + async.nextTick = process.nextTick; + } + + async.forEach = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + _forEach(arr, function (x) { + iterator(x, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(null); + } + } + }); + }); + }; + + async.forEachSeries = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + var iterate = function () { + iterator(arr[completed], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(null); + } + else { + iterate(); + } + } + }); + }; + iterate(); + }; + + async.forEachLimit = function (arr, limit, iterator, callback) { + callback = callback || function () {}; + if (!arr.length || limit <= 0) { + return callback(); + } + var completed = 0; + var started = 0; + var running = 0; + + (function replenish () { + if (completed === arr.length) { + return callback(); + } + + while (running < limit && started < arr.length) { + started += 1; + running += 1; + iterator(arr[started - 1], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + running -= 1; + if (completed === arr.length) { + callback(); + } + else { + replenish(); + } + } + }); + } + })(); + }; + + + var doParallel = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.forEach].concat(args)); + }; + }; + var doSeries = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.forEachSeries].concat(args)); + }; + }; + + + var _asyncMap = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (err, v) { + results[x.index] = v; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + }; + async.map = doParallel(_asyncMap); + async.mapSeries = doSeries(_asyncMap); + + + // reduce only has a series version, as doing reduce in parallel won't + // work in many situations. + async.reduce = function (arr, memo, iterator, callback) { + async.forEachSeries(arr, function (x, callback) { + iterator(memo, x, function (err, v) { + memo = v; + callback(err); + }); + }, function (err) { + callback(err, memo); + }); + }; + // inject alias + async.inject = async.reduce; + // foldl alias + async.foldl = async.reduce; + + async.reduceRight = function (arr, memo, iterator, callback) { + var reversed = _map(arr, function (x) { + return x; + }).reverse(); + async.reduce(reversed, memo, iterator, callback); + }; + // foldr alias + async.foldr = async.reduceRight; + + var _filter = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.filter = doParallel(_filter); + async.filterSeries = doSeries(_filter); + // select alias + async.select = async.filter; + async.selectSeries = async.filterSeries; + + var _reject = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (!v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.reject = doParallel(_reject); + async.rejectSeries = doSeries(_reject); + + var _detect = function (eachfn, arr, iterator, main_callback) { + eachfn(arr, function (x, callback) { + iterator(x, function (result) { + if (result) { + main_callback(x); + main_callback = function () {}; + } + else { + callback(); + } + }); + }, function (err) { + main_callback(); + }); + }; + async.detect = doParallel(_detect); + async.detectSeries = doSeries(_detect); + + async.some = function (arr, iterator, main_callback) { + async.forEach(arr, function (x, callback) { + iterator(x, function (v) { + if (v) { + main_callback(true); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(false); + }); + }; + // any alias + async.any = async.some; + + async.every = function (arr, iterator, main_callback) { + async.forEach(arr, function (x, callback) { + iterator(x, function (v) { + if (!v) { + main_callback(false); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(true); + }); + }; + // all alias + async.all = async.every; + + 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 { + var fn = function (left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }; + callback(null, _map(results.sort(fn), function (x) { + return x.value; + })); + } + }); + }; + + async.auto = function (tasks, callback) { + callback = callback || function () {}; + var keys = _keys(tasks); + if (!keys.length) { + return callback(null); + } + + var results = {}; + + var listeners = []; + var addListener = function (fn) { + listeners.unshift(fn); + }; + var removeListener = function (fn) { + for (var i = 0; i < listeners.length; i += 1) { + if (listeners[i] === fn) { + listeners.splice(i, 1); + return; + } + } + }; + var taskComplete = function () { + _forEach(listeners.slice(0), function (fn) { + fn(); + }); + }; + + addListener(function () { + if (_keys(results).length === keys.length) { + callback(null, results); + callback = function () {}; + } + }); + + _forEach(keys, function (k) { + var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k]; + var taskCallback = function (err) { + if (err) { + callback(err); + // stop subsequent errors hitting callback multiple times + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + taskComplete(); + } + }; + var requires = task.slice(0, Math.abs(task.length - 1)) || []; + var ready = function () { + return _reduce(requires, function (a, x) { + return (a && results.hasOwnProperty(x)); + }, true) && !results.hasOwnProperty(k); + }; + if (ready()) { + task[task.length - 1](taskCallback, results); + } + else { + var listener = function () { + if (ready()) { + removeListener(listener); + task[task.length - 1](taskCallback, results); + } + }; + addListener(listener); + } + }); + }; + + async.waterfall = function (tasks, callback) { + callback = callback || function () {}; + if (!tasks.length) { + return callback(); + } + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } + else { + args.push(callback); + } + async.nextTick(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(async.iterator(tasks))(); + }; + + async.parallel = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.map(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + async.forEach(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.series = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.mapSeries(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + async.forEachSeries(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.iterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + 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 = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + return function () { + return fn.apply( + null, args.concat(Array.prototype.slice.call(arguments)) + ); + }; + }; + + var _concat = function (eachfn, arr, fn, callback) { + var r = []; + eachfn(arr, function (x, cb) { + fn(x, function (err, y) { + r = r.concat(y || []); + cb(err); + }); + }, function (err) { + callback(err, r); + }); + }; + async.concat = doParallel(_concat); + async.concatSeries = doSeries(_concat); + + async.whilst = function (test, iterator, callback) { + if (test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.whilst(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.until = function (test, iterator, callback) { + if (!test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.until(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.queue = function (worker, concurrency) { + var workers = 0; + var q = { + tasks: [], + concurrency: concurrency, + saturated: null, + empty: null, + drain: null, + push: function (data, callback) { + if(data.constructor !== Array) { + data = [data]; + } + _forEach(data, function(task) { + q.tasks.push({ + data: task, + callback: typeof callback === 'function' ? callback : null + }); + if (q.saturated && q.tasks.length == concurrency) { + q.saturated(); + } + async.nextTick(q.process); + }); + }, + process: function () { + if (workers < q.concurrency && q.tasks.length) { + var task = q.tasks.shift(); + if(q.empty && q.tasks.length == 0) q.empty(); + workers += 1; + worker(task.data, function () { + workers -= 1; + if (task.callback) { + task.callback.apply(task, arguments); + } + if(q.drain && q.tasks.length + workers == 0) q.drain(); + q.process(); + }); + } + }, + length: function () { + return q.tasks.length; + }, + running: function () { + return workers; + } + }; + return q; + }; + + var _console_fn = function (name) { + return function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + fn.apply(null, args.concat([function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (typeof console !== 'undefined') { + if (err) { + if (console.error) { + console.error(err); + } + } + else if (console[name]) { + _forEach(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 || function (x) { + return x; + }; + var memoized = function () { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + var key = hasher.apply(null, args); + if (key in memo) { + callback.apply(null, memo[key]); + } + else if (key in queues) { + queues[key].push(callback); + } + else { + queues[key] = [callback]; + fn.apply(null, args.concat([function () { + memo[key] = arguments; + var q = queues[key]; + delete queues[key]; + for (var i = 0, l = q.length; i < l; i++) { + q[i].apply(null, arguments); + } + }])); + } + }; + memoized.unmemoized = fn; + return memoized; + }; + + async.unmemoize = function (fn) { + return function () { + return (fn.unmemoized || fn).apply(null, arguments); + }; + }; + +}()); diff --git a/test/node_modules/async/package.json b/test/node_modules/async/package.json new file mode 100644 index 0000000..6c13362 --- /dev/null +++ b/test/node_modules/async/package.json @@ -0,0 +1,34 @@ +{ + "name": "async", + "description": "Higher-order functions and common patterns for asynchronous code", + "main": "./index", + "author": { + "name": "Caolan McMahon" + }, + "version": "0.1.22", + "repository": { + "type": "git", + "url": "http://github.com/caolan/async.git" + }, + "bugs": { + "url": "http://github.com/caolan/async/issues" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/caolan/async/raw/master/LICENSE" + } + ], + "devDependencies": { + "nodeunit": ">0.0.0", + "uglify-js": "1.2.x", + "nodelint": ">0.0.0" + }, + "readme": "# Async.js\n\nAsync is a utility module which provides straight-forward, powerful functions\nfor working with asynchronous JavaScript. Although originally designed for\nuse with [node.js](http://nodejs.org), it can also be used directly in the\nbrowser.\n\nAsync provides around 20 functions that include the usual 'functional'\nsuspects (map, reduce, filter, forEach…) as well as some common patterns\nfor asynchronous control flow (parallel, series, waterfall…). All these\nfunctions assume you follow the node.js convention of providing a single\ncallback as the last argument of your async function.\n\n\n## Quick Examples\n\n async.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n });\n\n async.filter(['file1','file2','file3'], path.exists, function(results){\n // results now equals an array of the existing files\n });\n\n async.parallel([\n function(){ ... },\n function(){ ... }\n ], callback);\n\n async.series([\n function(){ ... },\n function(){ ... }\n ]);\n\nThere are many more functions available so take a look at the docs below for a\nfull list. This module aims to be comprehensive, so if you feel anything is\nmissing please create a GitHub issue for it.\n\n\n## Download\n\nReleases are available for download from\n[GitHub](http://github.com/caolan/async/downloads).\nAlternatively, you can install using Node Package Manager (npm):\n\n npm install async\n\n\n__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 17.5kb Uncompressed\n\n__Production:__ [async.min.js](https://github.com/caolan/async/raw/master/dist/async.min.js) - 1.7kb Packed and Gzipped\n\n\n## In the Browser\n\nSo far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:\n\n \n \n\n\n## Documentation\n\n### Collections\n\n* [forEach](#forEach)\n* [map](#map)\n* [filter](#filter)\n* [reject](#reject)\n* [reduce](#reduce)\n* [detect](#detect)\n* [sortBy](#sortBy)\n* [some](#some)\n* [every](#every)\n* [concat](#concat)\n\n### Control Flow\n\n* [series](#series)\n* [parallel](#parallel)\n* [whilst](#whilst)\n* [until](#until)\n* [waterfall](#waterfall)\n* [queue](#queue)\n* [auto](#auto)\n* [iterator](#iterator)\n* [apply](#apply)\n* [nextTick](#nextTick)\n\n### Utils\n\n* [memoize](#memoize)\n* [unmemoize](#unmemoize)\n* [log](#log)\n* [dir](#dir)\n* [noConflict](#noConflict)\n\n\n## Collections\n\n\n### forEach(arr, iterator, callback)\n\nApplies an iterator function to each item in an array, in parallel.\nThe iterator is called with an item from the list and a callback for when it\nhas finished. If the iterator passes an error to this callback, the main\ncallback for the forEach function is immediately called with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n // assuming openFiles is an array of file names and saveFile is a function\n // to save the modified contents of that file:\n\n async.forEach(openFiles, saveFile, function(err){\n // if any of the saves produced an error, err would equal that error\n });\n\n---------------------------------------\n\n\n### forEachSeries(arr, iterator, callback)\n\nThe same as forEach only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. This means the iterator functions will complete in order.\n\n\n---------------------------------------\n\n\n### forEachLimit(arr, limit, iterator, callback)\n\nThe same as forEach only the iterator is applied to batches of items in the\narray, in series. The next batch of iterators is only called once the current\none has completed processing.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* limit - How many items should be in each batch.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n // Assume documents is an array of JSON objects and requestApi is a\n // function that interacts with a rate-limited REST api.\n\n async.forEachLimit(documents, 20, requestApi, function(err){\n // if any of the saves produced an error, err would equal that error\n });\n---------------------------------------\n\n\n### map(arr, iterator, callback)\n\nProduces a new array of values by mapping each value in the given array through\nthe iterator function. The iterator is called with an item from the array and a\ncallback for when it has finished processing. The callback takes 2 arguments, \nan error and the transformed item from the array. If the iterator passes an\nerror to this callback, the main callback for the map function is immediately\ncalled with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order, however\nthe results array will be in the same order as the original array.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and a transformed item.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array of the\n transformed items from the original array.\n\n__Example__\n\n async.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n });\n\n---------------------------------------\n\n\n### mapSeries(arr, iterator, callback)\n\nThe same as map only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n\n---------------------------------------\n\n\n### filter(arr, iterator, callback)\n\n__Alias:__ select\n\nReturns a new array of all the values which pass an async truth test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists. This operation is\nperformed in parallel, but the results array will be in the same order as the\noriginal.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(results) - A callback which is called after all the iterator\n functions have finished.\n\n__Example__\n\n async.filter(['file1','file2','file3'], path.exists, function(results){\n // results now equals an array of the existing files\n });\n\n---------------------------------------\n\n\n### filterSeries(arr, iterator, callback)\n\n__alias:__ selectSeries\n\nThe same as filter only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n---------------------------------------\n\n\n### reject(arr, iterator, callback)\n\nThe opposite of filter. Removes values that pass an async truth test.\n\n---------------------------------------\n\n\n### rejectSeries(arr, iterator, callback)\n\nThe same as filter, only the iterator is applied to each item in the array\nin series.\n\n\n---------------------------------------\n\n\n### reduce(arr, memo, iterator, callback)\n\n__aliases:__ inject, foldl\n\nReduces a list of values into a single value using an async iterator to return\neach successive step. Memo is the initial state of the reduction. This\nfunction only operates in series. For performance reasons, it may make sense to\nsplit a call to this function into a parallel map, then use the normal\nArray.prototype.reduce on the results. This function is for situations where\neach step in the reduction needs to be async, if you can get the data before\nreducing it then its probably a good idea to do so.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* memo - The initial state of the reduction.\n* iterator(memo, item, callback) - A function applied to each item in the\n array to produce the next step in the reduction. The iterator is passed a\n callback which accepts an optional error as its first argument, and the state\n of the reduction as the second. If an error is passed to the callback, the\n reduction is stopped and the main callback is immediately called with the\n error.\n* callback(err, result) - A callback which is called after all the iterator\n functions have finished. Result is the reduced value.\n\n__Example__\n\n async.reduce([1,2,3], 0, function(memo, item, callback){\n // pointless async:\n process.nextTick(function(){\n callback(null, memo + item)\n });\n }, function(err, result){\n // result is now equal to the last value of memo, which is 6\n });\n\n---------------------------------------\n\n\n### reduceRight(arr, memo, iterator, callback)\n\n__Alias:__ foldr\n\nSame as reduce, only operates on the items in the array in reverse order.\n\n\n---------------------------------------\n\n\n### detect(arr, iterator, callback)\n\nReturns the first value in a list that passes an async truth test. The\niterator is applied in parallel, meaning the first iterator to return true will\nfire the detect callback with that result. That means the result might not be\nthe first item in the original array (in terms of order) that passes the test.\n\nIf order within the original array is important then look at detectSeries.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n the first item in the array that passes the truth test (iterator) or the\n value undefined if none passed.\n\n__Example__\n\n async.detect(['file1','file2','file3'], path.exists, function(result){\n // result now equals the first file in the list that exists\n });\n\n---------------------------------------\n\n\n### detectSeries(arr, iterator, callback)\n\nThe same as detect, only the iterator is applied to each item in the array\nin series. This means the result is always the first in the original array (in\nterms of array order) that passes the truth test.\n\n\n---------------------------------------\n\n\n### sortBy(arr, iterator, callback)\n\nSorts a list by the results of running each value through an async iterator.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and a value to use as the sort criteria.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is the items from\n the original array sorted by the values returned by the iterator calls.\n\n__Example__\n\n async.sortBy(['file1','file2','file3'], function(file, callback){\n fs.stat(file, function(err, stats){\n callback(err, stats.mtime);\n });\n }, function(err, results){\n // results is now the original array of files sorted by\n // modified date\n });\n\n\n---------------------------------------\n\n\n### some(arr, iterator, callback)\n\n__Alias:__ any\n\nReturns true if at least one element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists. Once any iterator\ncall returns true, the main callback is immediately called.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n either true or false depending on the values of the async tests.\n\n__Example__\n\n async.some(['file1','file2','file3'], path.exists, function(result){\n // if result is true then at least one of the files exists\n });\n\n---------------------------------------\n\n\n### every(arr, iterator, callback)\n\n__Alias:__ all\n\nReturns true if every element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called after all the iterator\n functions have finished. Result will be either true or false depending on\n the values of the async tests.\n\n__Example__\n\n async.every(['file1','file2','file3'], path.exists, function(result){\n // if result is true then every file exists\n });\n\n---------------------------------------\n\n\n### concat(arr, iterator, callback)\n\nApplies an iterator to each item in a list, concatenating the results. Returns the\nconcatenated list. The iterators are called in parallel, and the results are\nconcatenated as they return. There is no guarantee that the results array will\nbe returned in the original order of the arguments passed to the iterator function.\n\n__Arguments__\n\n* arr - An array to iterate over\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and an array of results.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array containing\n the concatenated results of the iterator function.\n\n__Example__\n\n async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){\n // files is now a list of filenames that exist in the 3 directories\n });\n\n---------------------------------------\n\n\n### concatSeries(arr, iterator, callback)\n\nSame as async.concat, but executes in series instead of parallel.\n\n\n## Control Flow\n\n\n### series(tasks, [callback])\n\nRun an array of functions in series, each one running once the previous\nfunction has completed. If any functions in the series pass an error to its\ncallback, no more functions are run and the callback for the series is\nimmediately called with the value of the error. Once the tasks have completed,\nthe results are passed to the final callback as an array.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.series.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed\n a callback it must call on completion.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets an array of all the arguments passed to\n the callbacks used in the array.\n\n__Example__\n\n async.series([\n function(callback){\n // do some stuff ...\n callback(null, 'one');\n },\n function(callback){\n // do some more stuff ...\n callback(null, 'two');\n },\n ],\n // optional callback\n function(err, results){\n // results is now equal to ['one', 'two']\n });\n\n\n // an example using an object instead of an array\n async.series({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n },\n },\n function(err, results) {\n // results is now equal to: {one: 1, two: 2}\n });\n\n\n---------------------------------------\n\n\n### parallel(tasks, [callback])\n\nRun an array of functions in parallel, without waiting until the previous\nfunction has completed. If any of the functions pass an error to its\ncallback, the main callback is immediately called with the value of the error.\nOnce the tasks have completed, the results are passed to the final callback as an\narray.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.parallel.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed a\n callback it must call on completion.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets an array of all the arguments passed to\n the callbacks used in the array.\n\n__Example__\n\n async.parallel([\n function(callback){\n setTimeout(function(){\n callback(null, 'one');\n }, 200);\n },\n function(callback){\n setTimeout(function(){\n callback(null, 'two');\n }, 100);\n },\n ],\n // optional callback\n function(err, results){\n // the results array will equal ['one','two'] even though\n // the second function had a shorter timeout.\n });\n\n\n // an example using an object instead of an array\n async.parallel({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n },\n },\n function(err, results) {\n // results is now equals to: {one: 1, two: 2}\n });\n\n\n---------------------------------------\n\n\n### whilst(test, fn, callback)\n\nRepeatedly call fn, while test returns true. Calls the callback when stopped,\nor an error occurs.\n\n__Arguments__\n\n* test() - synchronous truth test to perform before each execution of fn.\n* fn(callback) - A function to call each time the test passes. The function is\n passed a callback which must be called once it has completed with an optional\n error as the first argument.\n* callback(err) - A callback which is called after the test fails and repeated\n execution of fn has stopped.\n\n__Example__\n\n var count = 0;\n\n async.whilst(\n function () { return count < 5; },\n function (callback) {\n count++;\n setTimeout(callback, 1000);\n },\n function (err) {\n // 5 seconds have passed\n }\n );\n\n\n---------------------------------------\n\n\n### until(test, fn, callback)\n\nRepeatedly call fn, until test returns true. Calls the callback when stopped,\nor an error occurs.\n\nThe inverse of async.whilst.\n\n\n---------------------------------------\n\n\n### waterfall(tasks, [callback])\n\nRuns an array of functions in series, each passing their results to the next in\nthe array. However, if any of the functions pass an error to the callback, the\nnext function is not executed and the main callback is immediately called with\nthe error.\n\n__Arguments__\n\n* tasks - An array of functions to run, each function is passed a callback it\n must call on completion.\n* callback(err, [results]) - An optional callback to run once all the functions\n have completed. This will be passed the results of the last task's callback.\n\n\n\n__Example__\n\n async.waterfall([\n function(callback){\n callback(null, 'one', 'two');\n },\n function(arg1, arg2, callback){\n callback(null, 'three');\n },\n function(arg1, callback){\n // arg1 now equals 'three'\n callback(null, 'done');\n }\n ], function (err, result) {\n // result now equals 'done' \n });\n\n\n---------------------------------------\n\n\n### queue(worker, concurrency)\n\nCreates a queue object with the specified concurrency. Tasks added to the\nqueue will be processed in parallel (up to the concurrency limit). If all\nworkers are in progress, the task is queued until one is available. Once\na worker has completed a task, the task's callback is called.\n\n__Arguments__\n\n* worker(task, callback) - An asynchronous function for processing a queued\n task.\n* concurrency - An integer for determining how many worker functions should be\n run in parallel.\n\n__Queue objects__\n\nThe queue object returned by this function has the following properties and\nmethods:\n\n* length() - a function returning the number of items waiting to be processed.\n* concurrency - an integer for determining how many worker functions should be\n run in parallel. This property can be changed after a queue is created to\n alter the concurrency on-the-fly.\n* push(task, [callback]) - add a new task to the queue, the callback is called\n once the worker has finished processing the task.\n instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.\n* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued\n* empty - a callback that is called when the last item from the queue is given to a worker\n* drain - a callback that is called when the last item from the queue has returned from the worker\n\n__Example__\n\n // create a queue object with concurrency 2\n\n var q = async.queue(function (task, callback) {\n console.log('hello ' + task.name);\n callback();\n }, 2);\n\n\n // assign a callback\n q.drain = function() {\n console.log('all items have been processed');\n }\n\n // add some items to the queue\n\n q.push({name: 'foo'}, function (err) {\n console.log('finished processing foo');\n });\n q.push({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n });\n\n // add some items to the queue (batch-wise)\n\n q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {\n console.log('finished processing bar');\n });\n\n\n---------------------------------------\n\n\n### auto(tasks, [callback])\n\nDetermines the best order for running functions based on their requirements.\nEach function can optionally depend on other functions being completed first,\nand each function is run as soon as its requirements are satisfied. If any of\nthe functions pass an error to their callback, that function will not complete\n(so any other functions depending on it will not run) and the main callback\nwill be called immediately with the error. Functions also receive an object\ncontaining the results of functions which have completed so far.\n\n__Arguments__\n\n* tasks - An object literal containing named functions or an array of\n requirements, with the function itself the last item in the array. The key\n used for each function or array is used when specifying requirements. The\n syntax is easier to understand by looking at the example.\n* callback(err, results) - An optional callback which is called when all the\n tasks have been completed. The callback will receive an error as an argument\n if any tasks pass an error to their callback. If all tasks complete\n successfully, it will receive an object containing their results.\n\n__Example__\n\n async.auto({\n get_data: function(callback){\n // async code to get some data\n },\n make_folder: function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n },\n write_file: ['get_data', 'make_folder', function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n callback(null, filename);\n }],\n email_link: ['write_file', function(callback, results){\n // once the file is written let's email a link to it...\n // results.write_file contains the filename returned by write_file.\n }]\n });\n\nThis is a fairly trivial example, but to do this using the basic parallel and\nseries functions would look like this:\n\n async.parallel([\n function(callback){\n // async code to get some data\n },\n function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n }\n ],\n function(results){\n async.series([\n function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n },\n email_link: function(callback){\n // once the file is written let's email a link to it...\n }\n ]);\n });\n\nFor a complicated series of async tasks using the auto function makes adding\nnew tasks much easier and makes the code more readable.\n\n\n---------------------------------------\n\n\n### iterator(tasks)\n\nCreates an iterator function which calls the next function in the array,\nreturning a continuation to call the next one after that. Its also possible to\n'peek' the next iterator by doing iterator.next().\n\nThis function is used internally by the async module but can be useful when\nyou want to manually control the flow of functions in series.\n\n__Arguments__\n\n* tasks - An array of functions to run, each function is passed a callback it\n must call on completion.\n\n__Example__\n\n var iterator = async.iterator([\n function(){ sys.p('one'); },\n function(){ sys.p('two'); },\n function(){ sys.p('three'); }\n ]);\n\n node> var iterator2 = iterator();\n 'one'\n node> var iterator3 = iterator2();\n 'two'\n node> iterator3();\n 'three'\n node> var nextfn = iterator2.next();\n node> nextfn();\n 'three'\n\n\n---------------------------------------\n\n\n### apply(function, arguments..)\n\nCreates a continuation function with some arguments already applied, a useful\nshorthand when combined with other control flow functions. Any arguments\npassed to the returned function are added to the arguments originally passed\nto apply.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to automatically apply when the\n continuation is called.\n\n__Example__\n\n // using apply\n\n async.parallel([\n async.apply(fs.writeFile, 'testfile1', 'test1'),\n async.apply(fs.writeFile, 'testfile2', 'test2'),\n ]);\n\n\n // the same process without using apply\n\n async.parallel([\n function(callback){\n fs.writeFile('testfile1', 'test1', callback);\n },\n function(callback){\n fs.writeFile('testfile2', 'test2', callback);\n },\n ]);\n\nIt's possible to pass any number of additional arguments when calling the\ncontinuation:\n\n node> var fn = async.apply(sys.puts, 'one');\n node> fn('two', 'three');\n one\n two\n three\n\n---------------------------------------\n\n\n### nextTick(callback)\n\nCalls the callback on a later loop around the event loop. In node.js this just\ncalls process.nextTick, in the browser it falls back to setTimeout(callback, 0),\nwhich means other higher priority events may precede the execution of the callback.\n\nThis is used internally for browser-compatibility purposes.\n\n__Arguments__\n\n* callback - The function to call on a later loop around the event loop.\n\n__Example__\n\n var call_order = [];\n async.nextTick(function(){\n call_order.push('two');\n // call_order now equals ['one','two]\n });\n call_order.push('one')\n\n\n## Utils\n\n\n### memoize(fn, [hasher])\n\nCaches the results of an async function. When creating a hash to store function\nresults against, the callback is omitted from the hash and an optional hash\nfunction can be used.\n\n__Arguments__\n\n* fn - the function you to proxy and cache results from.\n* hasher - an optional function for generating a custom hash for storing\n results, it has all the arguments applied to it apart from the callback, and\n must be synchronous.\n\n__Example__\n\n var slow_fn = function (name, callback) {\n // do something\n callback(null, result);\n };\n var fn = async.memoize(slow_fn);\n\n // fn can now be used as if it were slow_fn\n fn('some name', function () {\n // callback\n });\n\n\n### unmemoize(fn)\n\nUndoes a memoized function, reverting it to the original, unmemoized\nform. Comes handy in tests.\n\n__Arguments__\n\n* fn - the memoized function\n\n\n### log(function, arguments)\n\nLogs the result of an async function to the console. Only works in node.js or\nin browsers that support console.log and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.log is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n var hello = function(name, callback){\n setTimeout(function(){\n callback(null, 'hello ' + name);\n }, 1000);\n };\n\n node> async.log(hello, 'world');\n 'hello world'\n\n\n---------------------------------------\n\n\n### dir(function, arguments)\n\nLogs the result of an async function to the console using console.dir to\ndisplay the properties of the resulting object. Only works in node.js or\nin browsers that support console.dir and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.dir is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n var hello = function(name, callback){\n setTimeout(function(){\n callback(null, {hello: name});\n }, 1000);\n };\n\n node> async.dir(hello, 'world');\n {hello: 'world'}\n\n\n---------------------------------------\n\n\n### noConflict()\n\nChanges the value of async back to its original value, returning a reference to the\nasync object.\n", + "readmeFilename": "README.md", + "_id": "async@0.1.22", + "dist": { + "shasum": "79440029ca3d7d81f698478a2a6a2aa52f0cda22" + }, + "_from": "async@0.1.22" +} diff --git a/test/node_modules/express/.npmignore b/test/node_modules/express/.npmignore new file mode 100644 index 0000000..74bd365 --- /dev/null +++ b/test/node_modules/express/.npmignore @@ -0,0 +1,7 @@ +.git* +docs/ +examples/ +support/ +test/ +testing.js +.DS_Store diff --git a/test/node_modules/express/History.md b/test/node_modules/express/History.md new file mode 100644 index 0000000..95ea466 --- /dev/null +++ b/test/node_modules/express/History.md @@ -0,0 +1,823 @@ + +2.5.11 / 2012-06-29 +================== + + * Fixed backport of req.protocol + +2.5.10 / 2012-06-15 +================== + + * Remove annoying engines field from package.json + * Backport support for trusting X-Forwarded-Proto + * use version of `package.json` for `express` command + +2.5.9/ 2012-04-02 +================== + + * Added support for PURGE request method [pbuyle] + * Fixed `express(1)` generated app `app.address()` before `listening` [mmalecki] + +2.5.8 / 2012-02-08 +================== + + * Update mkdirp dep. Closes #991 + +2.5.7 / 2012-02-06 +================== + + * Fixed `app.all` duplicate DELETE requests [mscdex] + +2.5.6 / 2012-01-13 +================== + + * Updated hamljs dev dep. Closes #953 + +2.5.5 / 2012-01-08 +================== + + * Fixed: set `filename` on cached templates [matthewleon] + +2.5.4 / 2012-01-02 +================== + + * Fixed `express(1)` eol on 0.4.x. Closes #947 + +2.5.3 / 2011-12-30 +================== + + * Fixed `req.is()` when a charset is present + +2.5.2 / 2011-12-10 +================== + + * Fixed: express(1) LF -> CRLF for windows + +2.5.1 / 2011-11-17 +================== + + * Changed: updated connect to 1.8.x + * Removed sass.js support from express(1) + +2.5.0 / 2011-10-24 +================== + + * Added ./routes dir for generated app by default + * Added npm install reminder to express(1) app gen + * Added 0.5.x support + * Removed `make test-cov` since it wont work with node 0.5.x + * Fixed express(1) public dir for windows. Closes #866 + +2.4.7 / 2011-10-05 +================== + + * Added mkdirp to express(1). Closes #795 + * Added simple _json-config_ example + * Added shorthand for the parsed request's pathname via `req.path` + * Changed connect dep to 1.7.x to fix npm issue... + * Fixed `res.redirect()` __HEAD__ support. [reported by xerox] + * Fixed `req.flash()`, only escape args + * Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie] + +2.4.6 / 2011-08-22 +================== + + * Fixed multiple param callback regression. Closes #824 [reported by TroyGoode] + +2.4.5 / 2011-08-19 +================== + + * Added support for routes to handle errors. Closes #809 + * Added `app.routes.all()`. Closes #803 + * Added "basepath" setting to work in conjunction with reverse proxies etc. + * Refactored `Route` to use a single array of callbacks + * Added support for multiple callbacks for `app.param()`. Closes #801 +Closes #805 + * Changed: removed .call(self) for route callbacks + * Dependency: `qs >= 0.3.1` + * Fixed `res.redirect()` on windows due to `join()` usage. Closes #808 + +2.4.4 / 2011-08-05 +================== + + * Fixed `res.header()` intention of a set, even when `undefined` + * Fixed `*`, value no longer required + * Fixed `res.send(204)` support. Closes #771 + +2.4.3 / 2011-07-14 +================== + + * Added docs for `status` option special-case. Closes #739 + * Fixed `options.filename`, exposing the view path to template engines + +2.4.2. / 2011-07-06 +================== + + * Revert "removed jsonp stripping" for XSS + +2.4.1 / 2011-07-06 +================== + + * Added `res.json()` JSONP support. Closes #737 + * Added _extending-templates_ example. Closes #730 + * Added "strict routing" setting for trailing slashes + * Added support for multiple envs in `app.configure()` calls. Closes #735 + * Changed: `res.send()` using `res.json()` + * Changed: when cookie `path === null` don't default it + * Changed; default cookie path to "home" setting. Closes #731 + * Removed _pids/logs_ creation from express(1) + +2.4.0 / 2011-06-28 +================== + + * Added chainable `res.status(code)` + * Added `res.json()`, an explicit version of `res.send(obj)` + * Added simple web-service example + +2.3.12 / 2011-06-22 +================== + + * \#express is now on freenode! come join! + * Added `req.get(field, param)` + * Added links to Japanese documentation, thanks @hideyukisaito! + * Added; the `express(1)` generated app outputs the env + * Added `content-negotiation` example + * Dependency: connect >= 1.5.1 < 2.0.0 + * Fixed view layout bug. Closes #720 + * Fixed; ignore body on 304. Closes #701 + +2.3.11 / 2011-06-04 +================== + + * Added `npm test` + * Removed generation of dummy test file from `express(1)` + * Fixed; `express(1)` adds express as a dep + * Fixed; prune on `prepublish` + +2.3.10 / 2011-05-27 +================== + + * Added `req.route`, exposing the current route + * Added _package.json_ generation support to `express(1)` + * Fixed call to `app.param()` function for optional params. Closes #682 + +2.3.9 / 2011-05-25 +================== + + * Fixed bug-ish with `../' in `res.partial()` calls + +2.3.8 / 2011-05-24 +================== + + * Fixed `app.options()` + +2.3.7 / 2011-05-23 +================== + + * Added route `Collection`, ex: `app.get('/user/:id').remove();` + * Added support for `app.param(fn)` to define param logic + * Removed `app.param()` support for callback with return value + * Removed module.parent check from express(1) generated app. Closes #670 + * Refactored router. Closes #639 + +2.3.6 / 2011-05-20 +================== + + * Changed; using devDependencies instead of git submodules + * Fixed redis session example + * Fixed markdown example + * Fixed view caching, should not be enabled in development + +2.3.5 / 2011-05-20 +================== + + * Added export `.view` as alias for `.View` + +2.3.4 / 2011-05-08 +================== + + * Added `./examples/say` + * Fixed `res.sendfile()` bug preventing the transfer of files with spaces + +2.3.3 / 2011-05-03 +================== + + * Added "case sensitive routes" option. + * Changed; split methods supported per rfc [slaskis] + * Fixed route-specific middleware when using the same callback function several times + +2.3.2 / 2011-04-27 +================== + + * Fixed view hints + +2.3.1 / 2011-04-26 +================== + + * Added `app.match()` as `app.match.all()` + * Added `app.lookup()` as `app.lookup.all()` + * Added `app.remove()` for `app.remove.all()` + * Added `app.remove.VERB()` + * Fixed template caching collision issue. Closes #644 + * Moved router over from connect and started refactor + +2.3.0 / 2011-04-25 +================== + + * Added options support to `res.clearCookie()` + * Added `res.helpers()` as alias of `res.locals()` + * Added; json defaults to UTF-8 with `res.send()`. Closes #632. [Daniel * Dependency `connect >= 1.4.0` + * Changed; auto set Content-Type in res.attachement [Aaron Heckmann] + * Renamed "cache views" to "view cache". Closes #628 + * Fixed caching of views when using several apps. Closes #637 + * Fixed gotcha invoking `app.param()` callbacks once per route middleware. +Closes #638 + * Fixed partial lookup precedence. Closes #631 +Shaw] + +2.2.2 / 2011-04-12 +================== + + * Added second callback support for `res.download()` connection errors + * Fixed `filename` option passing to template engine + +2.2.1 / 2011-04-04 +================== + + * Added `layout(path)` helper to change the layout within a view. Closes #610 + * Fixed `partial()` collection object support. + Previously only anything with `.length` would work. + When `.length` is present one must still be aware of holes, + however now `{ collection: {foo: 'bar'}}` is valid, exposes + `keyInCollection` and `keysInCollection`. + + * Performance improved with better view caching + * Removed `request` and `response` locals + * Changed; errorHandler page title is now `Express` instead of `Connect` + +2.2.0 / 2011-03-30 +================== + + * Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606 + * Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606 + * Added `app.VERB(path)` as alias of `app.lookup.VERB()`. + * Dependency `connect >= 1.2.0` + +2.1.1 / 2011-03-29 +================== + + * Added; expose `err.view` object when failing to locate a view + * Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann] + * Fixed; `res.send(undefined)` responds with 204 [aheckmann] + +2.1.0 / 2011-03-24 +================== + + * Added `/_?` partial lookup support. Closes #447 + * Added `request`, `response`, and `app` local variables + * Added `settings` local variable, containing the app's settings + * Added `req.flash()` exception if `req.session` is not available + * Added `res.send(bool)` support (json response) + * Fixed stylus example for latest version + * Fixed; wrap try/catch around `res.render()` + +2.0.0 / 2011-03-17 +================== + + * Fixed up index view path alternative. + * Changed; `res.locals()` without object returns the locals + +2.0.0rc3 / 2011-03-17 +================== + + * Added `res.locals(obj)` to compliment `res.local(key, val)` + * Added `res.partial()` callback support + * Fixed recursive error reporting issue in `res.render()` + +2.0.0rc2 / 2011-03-17 +================== + + * Changed; `partial()` "locals" are now optional + * Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01] + * Fixed .filename view engine option [reported by drudge] + * Fixed blog example + * Fixed `{req,res}.app` reference when mounting [Ben Weaver] + +2.0.0rc / 2011-03-14 +================== + + * Fixed; expose `HTTPSServer` constructor + * Fixed express(1) default test charset. Closes #579 [reported by secoif] + * Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP] + +2.0.0beta3 / 2011-03-09 +================== + + * Added support for `res.contentType()` literal + The original `res.contentType('.json')`, + `res.contentType('application/json')`, and `res.contentType('json')` + will work now. + * Added `res.render()` status option support back + * Added charset option for `res.render()` + * Added `.charset` support (via connect 1.0.4) + * Added view resolution hints when in development and a lookup fails + * Added layout lookup support relative to the page view. + For example while rendering `./views/user/index.jade` if you create + `./views/user/layout.jade` it will be used in favour of the root layout. + * Fixed `res.redirect()`. RFC states absolute url [reported by unlink] + * Fixed; default `res.send()` string charset to utf8 + * Removed `Partial` constructor (not currently used) + +2.0.0beta2 / 2011-03-07 +================== + + * Added res.render() `.locals` support back to aid in migration process + * Fixed flash example + +2.0.0beta / 2011-03-03 +================== + + * Added HTTPS support + * Added `res.cookie()` maxAge support + * Added `req.header()` _Referrer_ / _Referer_ special-case, either works + * Added mount support for `res.redirect()`, now respects the mount-point + * Added `union()` util, taking place of `merge(clone())` combo + * Added stylus support to express(1) generated app + * Added secret to session middleware used in examples and generated app + * Added `res.local(name, val)` for progressive view locals + * Added default param support to `req.param(name, default)` + * Added `app.disabled()` and `app.enabled()` + * Added `app.register()` support for omitting leading ".", either works + * Added `res.partial()`, using the same interface as `partial()` within a view. Closes #539 + * Added `app.param()` to map route params to async/sync logic + * Added; aliased `app.helpers()` as `app.locals()`. Closes #481 + * Added extname with no leading "." support to `res.contentType()` + * Added `cache views` setting, defaulting to enabled in "production" env + * Added index file partial resolution, eg: partial('user') may try _views/user/index.jade_. + * Added `req.accepts()` support for extensions + * Changed; `res.download()` and `res.sendfile()` now utilize Connect's + static file server `connect.static.send()`. + * Changed; replaced `connect.utils.mime()` with npm _mime_ module + * Changed; allow `req.query` to be pre-defined (via middleware or other parent + * Changed view partial resolution, now relative to parent view + * Changed view engine signature. no longer `engine.render(str, options, callback)`, now `engine.compile(str, options) -> Function`, the returned function accepts `fn(locals)`. + * Fixed `req.param()` bug returning Array.prototype methods. Closes #552 + * Fixed; using `Stream#pipe()` instead of `sys.pump()` in `res.sendfile()` + * Fixed; using _qs_ module instead of _querystring_ + * Fixed; strip unsafe chars from jsonp callbacks + * Removed "stream threshold" setting + +1.0.8 / 2011-03-01 +================== + + * Allow `req.query` to be pre-defined (via middleware or other parent app) + * "connect": ">= 0.5.0 < 1.0.0". Closes #547 + * Removed the long deprecated __EXPRESS_ENV__ support + +1.0.7 / 2011-02-07 +================== + + * Fixed `render()` setting inheritance. + Mounted apps would not inherit "view engine" + +1.0.6 / 2011-02-07 +================== + + * Fixed `view engine` setting bug when period is in dirname + +1.0.5 / 2011-02-05 +================== + + * Added secret to generated app `session()` call + +1.0.4 / 2011-02-05 +================== + + * Added `qs` dependency to _package.json_ + * Fixed namespaced `require()`s for latest connect support + +1.0.3 / 2011-01-13 +================== + + * Remove unsafe characters from JSONP callback names [Ryan Grove] + +1.0.2 / 2011-01-10 +================== + + * Removed nested require, using `connect.router` + +1.0.1 / 2010-12-29 +================== + + * Fixed for middleware stacked via `createServer()` + previously the `foo` middleware passed to `createServer(foo)` + would not have access to Express methods such as `res.send()` + or props like `req.query` etc. + +1.0.0 / 2010-11-16 +================== + + * Added; deduce partial object names from the last segment. + For example by default `partial('forum/post', postObject)` will + give you the _post_ object, providing a meaningful default. + * Added http status code string representation to `res.redirect()` body + * Added; `res.redirect()` supporting _text/plain_ and _text/html_ via __Accept__. + * Added `req.is()` to aid in content negotiation + * Added partial local inheritance [suggested by masylum]. Closes #102 + providing access to parent template locals. + * Added _-s, --session[s]_ flag to express(1) to add session related middleware + * Added _--template_ flag to express(1) to specify the + template engine to use. + * Added _--css_ flag to express(1) to specify the + stylesheet engine to use (or just plain css by default). + * Added `app.all()` support [thanks aheckmann] + * Added partial direct object support. + You may now `partial('user', user)` providing the "user" local, + vs previously `partial('user', { object: user })`. + * Added _route-separation_ example since many people question ways + to do this with CommonJS modules. Also view the _blog_ example for + an alternative. + * Performance; caching view path derived partial object names + * Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454 + * Fixed jsonp support; _text/javascript_ as per mailinglist discussion + +1.0.0rc4 / 2010-10-14 +================== + + * Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0 + * Added route-middleware support (very helpful, see the [docs](http://expressjs.com/guide.html#Route-Middleware)) + * Added _jsonp callback_ setting to enable/disable jsonp autowrapping [Dav Glass] + * Added callback query check on response.send to autowrap JSON objects for simple webservice implementations [Dav Glass] + * Added `partial()` support for array-like collections. Closes #434 + * Added support for swappable querystring parsers + * Added session usage docs. Closes #443 + * Added dynamic helper caching. Closes #439 [suggested by maritz] + * Added authentication example + * Added basic Range support to `res.sendfile()` (and `res.download()` etc) + * Changed; `express(1)` generated app using 2 spaces instead of 4 + * Default env to "development" again [aheckmann] + * Removed _context_ option is no more, use "scope" + * Fixed; exposing _./support_ libs to examples so they can run without installs + * Fixed mvc example + +1.0.0rc3 / 2010-09-20 +================== + + * Added confirmation for `express(1)` app generation. Closes #391 + * Added extending of flash formatters via `app.flashFormatters` + * Added flash formatter support. Closes #411 + * Added streaming support to `res.sendfile()` using `sys.pump()` when >= "stream threshold" + * Added _stream threshold_ setting for `res.sendfile()` + * Added `res.send()` __HEAD__ support + * Added `res.clearCookie()` + * Added `res.cookie()` + * Added `res.render()` headers option + * Added `res.redirect()` response bodies + * Added `res.render()` status option support. Closes #425 [thanks aheckmann] + * Fixed `res.sendfile()` responding with 403 on malicious path + * Fixed `res.download()` bug; when an error occurs remove _Content-Disposition_ + * Fixed; mounted apps settings now inherit from parent app [aheckmann] + * Fixed; stripping Content-Length / Content-Type when 204 + * Fixed `res.send()` 204. Closes #419 + * Fixed multiple _Set-Cookie_ headers via `res.header()`. Closes #402 + * Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo] + + +1.0.0rc2 / 2010-08-17 +================== + + * Added `app.register()` for template engine mapping. Closes #390 + * Added `res.render()` callback support as second argument (no options) + * Added callback support to `res.download()` + * Added callback support for `res.sendfile()` + * Added support for middleware access via `express.middlewareName()` vs `connect.middlewareName()` + * Added "partials" setting to docs + * Added default expresso tests to `express(1)` generated app. Closes #384 + * Fixed `res.sendfile()` error handling, defer via `next()` + * Fixed `res.render()` callback when a layout is used [thanks guillermo] + * Fixed; `make install` creating ~/.node_libraries when not present + * Fixed issue preventing error handlers from being defined anywhere. Closes #387 + +1.0.0rc / 2010-07-28 +================== + + * Added mounted hook. Closes #369 + * Added connect dependency to _package.json_ + + * Removed "reload views" setting and support code + development env never caches, production always caches. + + * Removed _param_ in route callbacks, signature is now + simply (req, res, next), previously (req, res, params, next). + Use _req.params_ for path captures, _req.query_ for GET params. + + * Fixed "home" setting + * Fixed middleware/router precedence issue. Closes #366 + * Fixed; _configure()_ callbacks called immediately. Closes #368 + +1.0.0beta2 / 2010-07-23 +================== + + * Added more examples + * Added; exporting `Server` constructor + * Added `Server#helpers()` for view locals + * Added `Server#dynamicHelpers()` for dynamic view locals. Closes #349 + * Added support for absolute view paths + * Added; _home_ setting defaults to `Server#route` for mounted apps. Closes #363 + * Added Guillermo Rauch to the contributor list + * Added support for "as" for non-collection partials. Closes #341 + * Fixed _install.sh_, ensuring _~/.node_libraries_ exists. Closes #362 [thanks jf] + * Fixed `res.render()` exceptions, now passed to `next()` when no callback is given [thanks guillermo] + * Fixed instanceof `Array` checks, now `Array.isArray()` + * Fixed express(1) expansion of public dirs. Closes #348 + * Fixed middleware precedence. Closes #345 + * Fixed view watcher, now async [thanks aheckmann] + +1.0.0beta / 2010-07-15 +================== + + * Re-write + - much faster + - much lighter + - Check [ExpressJS.com](http://expressjs.com) for migration guide and updated docs + +0.14.0 / 2010-06-15 +================== + + * Utilize relative requires + * Added Static bufferSize option [aheckmann] + * Fixed caching of view and partial subdirectories [aheckmann] + * Fixed mime.type() comments now that ".ext" is not supported + * Updated haml submodule + * Updated class submodule + * Removed bin/express + +0.13.0 / 2010-06-01 +================== + + * Added node v0.1.97 compatibility + * Added support for deleting cookies via Request#cookie('key', null) + * Updated haml submodule + * Fixed not-found page, now using using charset utf-8 + * Fixed show-exceptions page, now using using charset utf-8 + * Fixed view support due to fs.readFile Buffers + * Changed; mime.type() no longer accepts ".type" due to node extname() changes + +0.12.0 / 2010-05-22 +================== + + * Added node v0.1.96 compatibility + * Added view `helpers` export which act as additional local variables + * Updated haml submodule + * Changed ETag; removed inode, modified time only + * Fixed LF to CRLF for setting multiple cookies + * Fixed cookie complation; values are now urlencoded + * Fixed cookies parsing; accepts quoted values and url escaped cookies + +0.11.0 / 2010-05-06 +================== + + * Added support for layouts using different engines + - this.render('page.html.haml', { layout: 'super-cool-layout.html.ejs' }) + - this.render('page.html.haml', { layout: 'foo' }) // assumes 'foo.html.haml' + - this.render('page.html.haml', { layout: false }) // no layout + * Updated ext submodule + * Updated haml submodule + * Fixed EJS partial support by passing along the context. Issue #307 + +0.10.1 / 2010-05-03 +================== + + * Fixed binary uploads. + +0.10.0 / 2010-04-30 +================== + + * Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s + encoding is set to 'utf8' or 'utf-8'. + * Added "encoding" option to Request#render(). Closes #299 + * Added "dump exceptions" setting, which is enabled by default. + * Added simple ejs template engine support + * Added error reponse support for text/plain, application/json. Closes #297 + * Added callback function param to Request#error() + * Added Request#sendHead() + * Added Request#stream() + * Added support for Request#respond(304, null) for empty response bodies + * Added ETag support to Request#sendfile() + * Added options to Request#sendfile(), passed to fs.createReadStream() + * Added filename arg to Request#download() + * Performance enhanced due to pre-reversing plugins so that plugins.reverse() is not called on each request + * Performance enhanced by preventing several calls to toLowerCase() in Router#match() + * Changed; Request#sendfile() now streams + * Changed; Renamed Request#halt() to Request#respond(). Closes #289 + * Changed; Using sys.inspect() instead of JSON.encode() for error output + * Changed; run() returns the http.Server instance. Closes #298 + * Changed; Defaulting Server#host to null (INADDR_ANY) + * Changed; Logger "common" format scale of 0.4f + * Removed Logger "request" format + * Fixed; Catching ENOENT in view caching, preventing error when "views/partials" is not found + * Fixed several issues with http client + * Fixed Logger Content-Length output + * Fixed bug preventing Opera from retaining the generated session id. Closes #292 + +0.9.0 / 2010-04-14 +================== + + * Added DSL level error() route support + * Added DSL level notFound() route support + * Added Request#error() + * Added Request#notFound() + * Added Request#render() callback function. Closes #258 + * Added "max upload size" setting + * Added "magic" variables to collection partials (\_\_index\_\_, \_\_length\_\_, \_\_isFirst\_\_, \_\_isLast\_\_). Closes #254 + * Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js + * Added callback function support to Request#halt() as 3rd/4th arg + * Added preprocessing of route param wildcards using param(). Closes #251 + * Added view partial support (with collections etc) + * Fixed bug preventing falsey params (such as ?page=0). Closes #286 + * Fixed setting of multiple cookies. Closes #199 + * Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml) + * Changed; session cookie is now httpOnly + * Changed; Request is no longer global + * Changed; Event is no longer global + * Changed; "sys" module is no longer global + * Changed; moved Request#download to Static plugin where it belongs + * Changed; Request instance created before body parsing. Closes #262 + * Changed; Pre-caching views in memory when "cache view contents" is enabled. Closes #253 + * Changed; Pre-caching view partials in memory when "cache view partials" is enabled + * Updated support to node --version 0.1.90 + * Updated dependencies + * Removed set("session cookie") in favour of use(Session, { cookie: { ... }}) + * Removed utils.mixin(); use Object#mergeDeep() + +0.8.0 / 2010-03-19 +================== + + * Added coffeescript example app. Closes #242 + * Changed; cache api now async friendly. Closes #240 + * Removed deprecated 'express/static' support. Use 'express/plugins/static' + +0.7.6 / 2010-03-19 +================== + + * Added Request#isXHR. Closes #229 + * Added `make install` (for the executable) + * Added `express` executable for setting up simple app templates + * Added "GET /public/*" to Static plugin, defaulting to /public + * Added Static plugin + * Fixed; Request#render() only calls cache.get() once + * Fixed; Namespacing View caches with "view:" + * Fixed; Namespacing Static caches with "static:" + * Fixed; Both example apps now use the Static plugin + * Fixed set("views"). Closes #239 + * Fixed missing space for combined log format + * Deprecated Request#sendfile() and 'express/static' + * Removed Server#running + +0.7.5 / 2010-03-16 +================== + + * Added Request#flash() support without args, now returns all flashes + * Updated ext submodule + +0.7.4 / 2010-03-16 +================== + + * Fixed session reaper + * Changed; class.js replacing js-oo Class implementation (quite a bit faster, no browser cruft) + +0.7.3 / 2010-03-16 +================== + + * Added package.json + * Fixed requiring of haml / sass due to kiwi removal + +0.7.2 / 2010-03-16 +================== + + * Fixed GIT submodules (HAH!) + +0.7.1 / 2010-03-16 +================== + + * Changed; Express now using submodules again until a PM is adopted + * Changed; chat example using millisecond conversions from ext + +0.7.0 / 2010-03-15 +================== + + * Added Request#pass() support (finds the next matching route, or the given path) + * Added Logger plugin (default "common" format replaces CommonLogger) + * Removed Profiler plugin + * Removed CommonLogger plugin + +0.6.0 / 2010-03-11 +================== + + * Added seed.yml for kiwi package management support + * Added HTTP client query string support when method is GET. Closes #205 + + * Added support for arbitrary view engines. + For example "foo.engine.html" will now require('engine'), + the exports from this module are cached after the first require(). + + * Added async plugin support + + * Removed usage of RESTful route funcs as http client + get() etc, use http.get() and friends + + * Removed custom exceptions + +0.5.0 / 2010-03-10 +================== + + * Added ext dependency (library of js extensions) + * Removed extname() / basename() utils. Use path module + * Removed toArray() util. Use arguments.values + * Removed escapeRegexp() util. Use RegExp.escape() + * Removed process.mixin() dependency. Use utils.mixin() + * Removed Collection + * Removed ElementCollection + * Shameless self promotion of ebook "Advanced JavaScript" (http://dev-mag.com) ;) + +0.4.0 / 2010-02-11 +================== + + * Added flash() example to sample upload app + * Added high level restful http client module (express/http) + * Changed; RESTful route functions double as HTTP clients. Closes #69 + * Changed; throwing error when routes are added at runtime + * Changed; defaulting render() context to the current Request. Closes #197 + * Updated haml submodule + +0.3.0 / 2010-02-11 +================== + + * Updated haml / sass submodules. Closes #200 + * Added flash message support. Closes #64 + * Added accepts() now allows multiple args. fixes #117 + * Added support for plugins to halt. Closes #189 + * Added alternate layout support. Closes #119 + * Removed Route#run(). Closes #188 + * Fixed broken specs due to use(Cookie) missing + +0.2.1 / 2010-02-05 +================== + + * Added "plot" format option for Profiler (for gnuplot processing) + * Added request number to Profiler plugin + * Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8 + * Fixed issue with routes not firing when not files are present. Closes #184 + * Fixed process.Promise -> events.Promise + +0.2.0 / 2010-02-03 +================== + + * Added parseParam() support for name[] etc. (allows for file inputs with "multiple" attr) Closes #180 + * Added Both Cache and Session option "reapInterval" may be "reapEvery". Closes #174 + * Added expiration support to cache api with reaper. Closes #133 + * Added cache Store.Memory#reap() + * Added Cache; cache api now uses first class Cache instances + * Added abstract session Store. Closes #172 + * Changed; cache Memory.Store#get() utilizing Collection + * Renamed MemoryStore -> Store.Memory + * Fixed use() of the same plugin several time will always use latest options. Closes #176 + +0.1.0 / 2010-02-03 +================== + + * Changed; Hooks (before / after) pass request as arg as well as evaluated in their context + * Updated node support to 0.1.27 Closes #169 + * Updated dirname(__filename) -> __dirname + * Updated libxmljs support to v0.2.0 + * Added session support with memory store / reaping + * Added quick uid() helper + * Added multi-part upload support + * Added Sass.js support / submodule + * Added production env caching view contents and static files + * Added static file caching. Closes #136 + * Added cache plugin with memory stores + * Added support to StaticFile so that it works with non-textual files. + * Removed dirname() helper + * Removed several globals (now their modules must be required) + +0.0.2 / 2010-01-10 +================== + + * Added view benchmarks; currently haml vs ejs + * Added Request#attachment() specs. Closes #116 + * Added use of node's parseQuery() util. Closes #123 + * Added `make init` for submodules + * Updated Haml + * Updated sample chat app to show messages on load + * Updated libxmljs parseString -> parseHtmlString + * Fixed `make init` to work with older versions of git + * Fixed specs can now run independant specs for those who cant build deps. Closes #127 + * Fixed issues introduced by the node url module changes. Closes 126. + * Fixed two assertions failing due to Collection#keys() returning strings + * Fixed faulty Collection#toArray() spec due to keys() returning strings + * Fixed `make test` now builds libxmljs.node before testing + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/test/node_modules/express/LICENSE b/test/node_modules/express/LICENSE new file mode 100644 index 0000000..36075a3 --- /dev/null +++ b/test/node_modules/express/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2009-2011 TJ Holowaychuk + +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. \ No newline at end of file diff --git a/test/node_modules/express/Makefile b/test/node_modules/express/Makefile new file mode 100644 index 0000000..dfbfd67 --- /dev/null +++ b/test/node_modules/express/Makefile @@ -0,0 +1,29 @@ + +DOCS = $(shell find docs/*.md) +HTMLDOCS = $(DOCS:.md=.html) +TESTS = $(shell find test/*.test.js) + +test: + @NODE_ENV=test ./node_modules/.bin/expresso $(TESTS) + +docs: $(HTMLDOCS) + @ echo "... generating TOC" + @./support/toc.js docs/guide.html + +%.html: %.md + @echo "... $< -> $@" + @markdown $< \ + | cat docs/layout/head.html - docs/layout/foot.html \ + > $@ + +site: + rm -fr /tmp/docs \ + && cp -fr docs /tmp/docs \ + && git checkout gh-pages \ + && cp -fr /tmp/docs/* . \ + && echo "done" + +docclean: + rm -f docs/*.{1,html} + +.PHONY: site test docs docclean \ No newline at end of file diff --git a/test/node_modules/express/Readme.md b/test/node_modules/express/Readme.md new file mode 100644 index 0000000..d2c64c7 --- /dev/null +++ b/test/node_modules/express/Readme.md @@ -0,0 +1,145 @@ + +# Express + + Insanely fast (and small) server-side JavaScript web development framework + built on [node](http://nodejs.org) and [Connect](http://github.com/senchalabs/connect). + + var app = express.createServer(); + + app.get('/', function(req, res){ + res.send('Hello World'); + }); + + app.listen(3000); + +## Installation + + $ npm install express + +or to access the `express(1)` executable install globally: + + $ npm install -g express + +## Quick Start + + The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below: + + Create the app: + + $ npm install -g express + $ express /tmp/foo && cd /tmp/foo + + Install dependencies: + + $ npm install -d + + Start the server: + + $ node app.js + +## Features + + * Robust routing + * Redirection helpers + * Dynamic view helpers + * Content negotiation + * Focus on high performance + * View rendering and partials support + * Environment based configuration + * Session based flash notifications + * Built on [Connect](http://github.com/senchalabs/connect) + * High test coverage + * Executable for generating applications quickly + * Application level view options + +Via Connect: + + * Session support + * Cache API + * Mime helpers + * ETag support + * Persistent flash notifications + * Cookie support + * JSON-RPC + * Logging + * and _much_ more! + +## Contributors + +The following are the major contributors of Express (in no specific order). + + * TJ Holowaychuk ([visionmedia](http://github.com/visionmedia)) + * Ciaran Jessup ([ciaranj](http://github.com/ciaranj)) + * Aaron Heckmann ([aheckmann](http://github.com/aheckmann)) + * Guillermo Rauch ([guille](http://github.com/guille)) + +## More Information + + * #express on freenode + * [express-expose](http://github.com/visionmedia/express-expose) expose objects, functions, modules and more to client-side js with ease + * [express-configure](http://github.com/visionmedia/express-configuration) async configuration support + * [express-messages](http://github.com/visionmedia/express-messages) flash notification rendering helper + * [express-namespace](http://github.com/visionmedia/express-namespace) namespaced route support + * [express-params](https://github.com/visionmedia/express-params) param pre-condition functions + * [express-mongoose](https://github.com/LearnBoost/express-mongoose) plugin for easy rendering of Mongoose async Query results + * Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates + * [Google Group](http://groups.google.com/group/express-js) for discussion + * Visit the [Wiki](http://github.com/visionmedia/express/wiki) + * [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito) + * Screencast - [Introduction](http://bit.ly/eRYu0O) + * Screencast - [View Partials](http://bit.ly/dU13Fx) + * Screencast - [Route Specific Middleware](http://bit.ly/hX4IaH) + * Screencast - [Route Path Placeholder Preconditions](http://bit.ly/eNqmVs) + +## Node Compatibility + +Express 1.x is compatible with node 0.2.x and connect < 1.0. + +Express 2.x is compatible with node 0.4.x or 0.6.x, and connect 1.x + +Express 3.x (master) will be compatible with node 0.6.x and connect 2.x + +## Viewing Examples + +First install the dev dependencies to install all the example / test suite deps: + + $ npm install + +then run whichever tests you want: + + $ node examples/jade/app.js + +## Running Tests + +To run the test suite first invoke the following command within the repo, installing the development dependencies: + + $ npm install + +then run the tests: + + $ make test + +## License + +(The MIT License) + +Copyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca> + +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/test/node_modules/express/bin/express b/test/node_modules/express/bin/express new file mode 100644 index 0000000..367be59 --- /dev/null +++ b/test/node_modules/express/bin/express @@ -0,0 +1,423 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var fs = require('fs') + , os = require('os') + , exec = require('child_process').exec + , mkdirp = require('mkdirp'); + +/** + * Package information. + */ + +var pkg = JSON.parse(fs.readFileSync(__dirname + '/../package.json')); + +/** + * Framework version. + */ + +var version = pkg.version; + +/** + * Add session support. + */ + +var sessions = false; + +/** + * CSS engine to utilize. + */ + +var cssEngine; + +/** + * End-of-line code. + */ + +var eol = os.platform + ? ('win32' == os.platform() ? '\r\n' : '\n') + : '\n'; + +/** + * Template engine to utilize. + */ + +var templateEngine = 'jade'; + +/** + * Usage documentation. + */ + +var usage = '' + + '\n' + + ' Usage: express [options] [path]\n' + + '\n' + + ' Options:\n' + + ' -s, --sessions add session support\n' + + ' -t, --template add template support (jade|ejs). default=jade\n' + + ' -c, --css add stylesheet support (stylus). default=plain css\n' + + ' -v, --version output framework version\n' + + ' -h, --help output help information\n' + ; + +/** + * Routes index template. + */ + +var index = [ + '' + , '/*' + , ' * GET home page.' + , ' */' + , '' + , 'exports.index = function(req, res){' + , ' res.render(\'index\', { title: \'Express\' })' + , '};' +].join(eol); + +/** + * Jade layout template. + */ + +var jadeLayout = [ + '!!!' + , 'html' + , ' head' + , ' title= title' + , ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')' + , ' body!= body' +].join(eol); + +/** + * Jade index template. + */ + +var jadeIndex = [ + 'h1= title' + , 'p Welcome to #{title}' +].join(eol); + +/** + * EJS layout template. + */ + +var ejsLayout = [ + '' + , '' + , ' ' + , ' <%= title %>' + , ' ' + , ' ' + , ' ' + , ' <%- body %>' + , ' ' + , '' +].join(eol); + +/** + * EJS index template. + */ + +var ejsIndex = [ + '

<%= title %>

' + , '

Welcome to <%= title %>

' + ].join(eol); + +/** + * Default css template. + */ + +var css = [ + 'body {' + , ' padding: 50px;' + , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;' + , '}' + , '' + , 'a {' + , ' color: #00B7FF;' + , '}' +].join(eol); + +/** + * Default stylus template. + */ + +var stylus = [ + 'body' + , ' padding: 50px' + , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif' + , 'a' + , ' color: #00B7FF' +].join(eol); + +/** + * App template. + */ + +var app = [ + '' + , '/**' + , ' * Module dependencies.' + , ' */' + , '' + , 'var express = require(\'express\')' + , ' , routes = require(\'./routes\');' + , '' + , 'var app = module.exports = express.createServer();' + , '' + , '// Configuration' + , '' + , 'app.configure(function(){' + , ' app.set(\'views\', __dirname + \'/views\');' + , ' app.set(\'view engine\', \':TEMPLATE\');' + , ' app.use(express.bodyParser());' + , ' app.use(express.methodOverride());{sess}{css}' + , ' app.use(app.router);' + , ' app.use(express.static(__dirname + \'/public\'));' + , '});' + , '' + , 'app.configure(\'development\', function(){' + , ' app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));' + , '});' + , '' + , 'app.configure(\'production\', function(){' + , ' app.use(express.errorHandler());' + , '});' + , '' + , '// Routes' + , '' + , 'app.get(\'/\', routes.index);' + , '' + , 'app.listen(3000, function(){' + , ' console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);' + , '});' + , '' +].join(eol); + +// Parse arguments + +var args = process.argv.slice(2) + , path = '.'; + +while (args.length) { + var arg = args.shift(); + switch (arg) { + case '-h': + case '--help': + abort(usage); + break; + case '-v': + case '--version': + abort(version); + break; + case '-s': + case '--session': + case '--sessions': + sessions = true; + break; + case '-c': + case '--css': + args.length + ? (cssEngine = args.shift()) + : abort('--css requires an argument'); + break; + case '-t': + case '--template': + args.length + ? (templateEngine = args.shift()) + : abort('--template requires an argument'); + break; + default: + path = arg; + } +} + +// Generate application + +(function createApplication(path) { + emptyDirectory(path, function(empty){ + if (empty) { + createApplicationAt(path); + } else { + confirm('destination is not empty, continue? ', function(ok){ + if (ok) { + process.stdin.destroy(); + createApplicationAt(path); + } else { + abort('aborting'); + } + }); + } + }); +})(path); + +/** + * Create application at the given directory `path`. + * + * @param {String} path + */ + +function createApplicationAt(path) { + console.log(); + process.on('exit', function(){ + console.log(); + console.log(' dont forget to install dependencies:'); + console.log(' $ cd %s && npm install', path); + console.log(); + }); + + mkdir(path, function(){ + mkdir(path + '/public'); + mkdir(path + '/public/javascripts'); + mkdir(path + '/public/images'); + mkdir(path + '/public/stylesheets', function(){ + switch (cssEngine) { + case 'stylus': + write(path + '/public/stylesheets/style.styl', stylus); + break; + default: + write(path + '/public/stylesheets/style.css', css); + } + }); + + mkdir(path + '/routes', function(){ + write(path + '/routes/index.js', index); + }); + + mkdir(path + '/views', function(){ + switch (templateEngine) { + case 'ejs': + write(path + '/views/layout.ejs', ejsLayout); + write(path + '/views/index.ejs', ejsIndex); + break; + case 'jade': + write(path + '/views/layout.jade', jadeLayout); + write(path + '/views/index.jade', jadeIndex); + break; + } + }); + + // CSS Engine support + switch (cssEngine) { + case 'stylus': + app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));'); + break; + default: + app = app.replace('{css}', ''); + } + + // Session support + app = app.replace('{sess}', sessions + ? eol + ' app.use(express.cookieParser());' + eol + ' app.use(express.session({ secret: \'your secret here\' }));' + : ''); + + // Template support + app = app.replace(':TEMPLATE', templateEngine); + + // package.json + var json = '{' + eol; + json += ' "name": "application-name"' + eol; + json += ' , "version": "0.0.1"' + eol; + json += ' , "private": true' + eol; + json += ' , "dependencies": {' + eol; + json += ' "express": "' + version + '"' + eol; + if (cssEngine) json += ' , "' + cssEngine + '": ">= 0.0.1"' + eol; + if (templateEngine) json += ' , "' + templateEngine + '": ">= 0.0.1"' + eol; + json += ' }' + eol; + json += '}'; + + + write(path + '/package.json', json); + write(path + '/app.js', app); + }); +} + +/** + * Check if the given directory `path` is empty. + * + * @param {String} path + * @param {Function} fn + */ + +function emptyDirectory(path, fn) { + fs.readdir(path, function(err, files){ + if (err && 'ENOENT' != err.code) throw err; + fn(!files || !files.length); + }); +} + +/** + * echo str > path. + * + * @param {String} path + * @param {String} str + */ + +function write(path, str) { + fs.writeFile(path, str); + console.log(' \x1b[36mcreate\x1b[0m : ' + path); +} + +/** + * Prompt confirmation with the given `msg`. + * + * @param {String} msg + * @param {Function} fn + */ + +function confirm(msg, fn) { + prompt(msg, function(val){ + fn(/^ *y(es)?/i.test(val)); + }); +} + +/** + * Prompt input with the given `msg` and callback `fn`. + * + * @param {String} msg + * @param {Function} fn + */ + +function prompt(msg, fn) { + // prompt + if (' ' == msg[msg.length - 1]) { + process.stdout.write(msg); + } else { + console.log(msg); + } + + // stdin + process.stdin.setEncoding('ascii'); + process.stdin.once('data', function(data){ + fn(data); + }).resume(); +} + +/** + * Mkdir -p. + * + * @param {String} path + * @param {Function} fn + */ + +function mkdir(path, fn) { + mkdirp(path, 0755, function(err){ + if (err) throw err; + console.log(' \033[36mcreate\033[0m : ' + path); + fn && fn(); + }); +} + +/** + * Exit with the given `str`. + * + * @param {String} str + */ + +function abort(str) { + console.error(str); + process.exit(1); +} diff --git a/test/node_modules/express/index.js b/test/node_modules/express/index.js new file mode 100644 index 0000000..8d81ea7 --- /dev/null +++ b/test/node_modules/express/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/express'); \ No newline at end of file diff --git a/test/node_modules/express/lib/express.js b/test/node_modules/express/lib/express.js new file mode 100644 index 0000000..7ea451d --- /dev/null +++ b/test/node_modules/express/lib/express.js @@ -0,0 +1,79 @@ + +/*! + * Express + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var connect = require('connect') + , HTTPSServer = require('./https') + , HTTPServer = require('./http') + , Route = require('./router/route') + +/** + * Re-export connect auto-loaders. + * + * This prevents the need to `require('connect')` in order + * to access core middleware, so for example `express.logger()` instead + * of `require('connect').logger()`. + */ + +var exports = module.exports = connect.middleware; + +/** + * Framework version. + */ + +exports.version = '2.5.11'; + +/** + * Shortcut for `new Server(...)`. + * + * @param {Function} ... + * @return {Server} + * @api public + */ + +exports.createServer = function(options){ + if ('object' == typeof options) { + return new HTTPSServer(options, Array.prototype.slice.call(arguments, 1)); + } else { + return new HTTPServer(Array.prototype.slice.call(arguments)); + } +}; + +/** + * Expose constructors. + */ + +exports.HTTPServer = HTTPServer; +exports.HTTPSServer = HTTPSServer; +exports.Route = Route; + +/** + * View extensions. + */ + +exports.View = +exports.view = require('./view'); + +/** + * Response extensions. + */ + +require('./response'); + +/** + * Request extensions. + */ + +require('./request'); + +// Error handler title + +exports.errorHandler.title = 'Express'; + diff --git a/test/node_modules/express/lib/http.js b/test/node_modules/express/lib/http.js new file mode 100644 index 0000000..da2158f --- /dev/null +++ b/test/node_modules/express/lib/http.js @@ -0,0 +1,582 @@ +/*! + * Express - HTTPServer + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var qs = require('qs') + , connect = require('connect') + , router = require('./router') + , Router = require('./router') + , view = require('./view') + , toArray = require('./utils').toArray + , methods = router.methods.concat('del', 'all') + , url = require('url') + , utils = connect.utils; + +/** + * Expose `HTTPServer`. + */ + +exports = module.exports = HTTPServer; + +/** + * Server proto. + */ + +var app = HTTPServer.prototype; + +/** + * Initialize a new `HTTPServer` with optional `middleware`. + * + * @param {Array} middleware + * @api public + */ + +function HTTPServer(middleware){ + connect.HTTPServer.call(this, []); + this.init(middleware); +}; + +/** + * Inherit from `connect.HTTPServer`. + */ + +app.__proto__ = connect.HTTPServer.prototype; + +/** + * Initialize the server. + * + * @param {Array} middleware + * @api private + */ + +app.init = function(middleware){ + var self = this; + this.cache = {}; + this.settings = {}; + this.redirects = {}; + this.isCallbacks = {}; + this._locals = {}; + this.dynamicViewHelpers = {}; + this.errorHandlers = []; + + this.set('env', process.env.NODE_ENV || 'development'); + + // expose objects to each other + this.use(function(req, res, next){ + req.query = req.query || {}; + res.setHeader('X-Powered-By', 'Express'); + req.app = res.app = self; + req.res = res; + res.req = req; + req.next = next; + // assign req.query + if (req.url.indexOf('?') > 0) { + var query = url.parse(req.url).query; + req.query = qs.parse(query); + } + next(); + }); + + // apply middleware + if (middleware) middleware.forEach(self.use.bind(self)); + + // router + this.routes = new Router(this); + this.__defineGetter__('router', function(){ + this.__usedRouter = true; + return self.routes.middleware; + }); + + // default locals + this.locals({ + settings: this.settings + , app: this + }); + + // default development configuration + this.configure('development', function(){ + this.enable('hints'); + }); + + // default production configuration + this.configure('production', function(){ + this.enable('view cache'); + }); + + // register error handlers on "listening" + // so that they disregard definition position. + this.on('listening', this.registerErrorHandlers.bind(this)); + + // route manipulation methods + methods.forEach(function(method){ + self.lookup[method] = function(path){ + return self.routes.lookup(method, path); + }; + + self.match[method] = function(path){ + return self.routes.match(method, path); + }; + + self.remove[method] = function(path){ + return self.routes.lookup(method, path).remove(); + }; + }); + + // del -> delete + self.lookup.del = self.lookup.delete; + self.match.del = self.match.delete; + self.remove.del = self.remove.delete; +}; + +/** + * Remove routes matching the given `path`. + * + * @param {Route} path + * @return {Boolean} + * @api public + */ + +app.remove = function(path){ + return this.routes.lookup('all', path).remove(); +}; + +/** + * Lookup routes defined with a path + * equivalent to `path`. + * + * @param {Stirng} path + * @return {Array} + * @api public + */ + +app.lookup = function(path){ + return this.routes.lookup('all', path); +}; + +/** + * Lookup routes matching the given `url`. + * + * @param {Stirng} url + * @return {Array} + * @api public + */ + +app.match = function(url){ + return this.routes.match('all', url); +}; + +/** + * When using the vhost() middleware register error handlers. + */ + +app.onvhost = function(){ + this.registerErrorHandlers(); +}; + +/** + * Register error handlers. + * + * @return {Server} for chaining + * @api public + */ + +app.registerErrorHandlers = function(){ + this.errorHandlers.forEach(function(fn){ + this.use(function(err, req, res, next){ + fn.apply(this, arguments); + }); + }, this); + return this; +}; + +/** + * Proxy `connect.HTTPServer#use()` to apply settings to + * mounted applications. + * + * @param {String|Function|Server} route + * @param {Function|Server} middleware + * @return {Server} for chaining + * @api public + */ + +app.use = function(route, middleware){ + var app, base, handle; + + if ('string' != typeof route) { + middleware = route, route = '/'; + } + + // express app + if (middleware.handle && middleware.set) app = middleware; + + // restore .app property on req and res + if (app) { + app.route = route; + middleware = function(req, res, next) { + var orig = req.app; + app.handle(req, res, function(err){ + req.app = res.app = orig; + next(err); + }); + }; + } + + connect.HTTPServer.prototype.use.call(this, route, middleware); + + // mounted an app, invoke the hook + // and adjust some settings + if (app) { + base = this.set('basepath') || this.route; + if ('/' == base) base = ''; + base = base + (app.set('basepath') || app.route); + app.set('basepath', base); + app.parent = this; + if (app.__mounted) app.__mounted.call(app, this); + } + + return this; +}; + +/** + * Assign a callback `fn` which is called + * when this `Server` is passed to `Server#use()`. + * + * Examples: + * + * var app = express.createServer() + * , blog = express.createServer(); + * + * blog.mounted(function(parent){ + * // parent is app + * // "this" is blog + * }); + * + * app.use(blog); + * + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.mounted = function(fn){ + this.__mounted = fn; + return this; +}; + +/** + * See: view.register. + * + * @return {Server} for chaining + * @api public + */ + +app.register = function(){ + view.register.apply(this, arguments); + return this; +}; + +/** + * Register the given view helpers `obj`. This method + * can be called several times to apply additional helpers. + * + * @param {Object} obj + * @return {Server} for chaining + * @api public + */ + +app.helpers = +app.locals = function(obj){ + utils.merge(this._locals, obj); + return this; +}; + +/** + * Register the given dynamic view helpers `obj`. This method + * can be called several times to apply additional helpers. + * + * @param {Object} obj + * @return {Server} for chaining + * @api public + */ + +app.dynamicHelpers = function(obj){ + utils.merge(this.dynamicViewHelpers, obj); + return this; +}; + +/** + * Map the given param placeholder `name`(s) to the given callback(s). + * + * Param mapping is used to provide pre-conditions to routes + * which us normalized placeholders. This callback has the same + * signature as regular middleware, for example below when ":userId" + * is used this function will be invoked in an attempt to load the user. + * + * app.param('userId', function(req, res, next, id){ + * User.find(id, function(err, user){ + * if (err) { + * next(err); + * } else if (user) { + * req.user = user; + * next(); + * } else { + * next(new Error('failed to load user')); + * } + * }); + * }); + * + * Passing a single function allows you to map logic + * to the values passed to `app.param()`, for example + * this is useful to provide coercion support in a concise manner. + * + * The following example maps regular expressions to param values + * ensuring that they match, otherwise passing control to the next + * route: + * + * app.param(function(name, regexp){ + * if (regexp instanceof RegExp) { + * return function(req, res, next, val){ + * var captures; + * if (captures = regexp.exec(String(val))) { + * req.params[name] = captures; + * next(); + * } else { + * next('route'); + * } + * } + * } + * }); + * + * We can now use it as shown below, where "/commit/:commit" expects + * that the value for ":commit" is at 5 or more digits. The capture + * groups are then available as `req.params.commit` as we defined + * in the function above. + * + * app.param('commit', /^\d{5,}$/); + * + * For more of this useful functionality take a look + * at [express-params](http://github.com/visionmedia/express-params). + * + * @param {String|Array|Function} name + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.param = function(name, fn){ + var self = this + , fns = [].slice.call(arguments, 1); + + // array + if (Array.isArray(name)) { + name.forEach(function(name){ + fns.forEach(function(fn){ + self.param(name, fn); + }); + }); + // param logic + } else if ('function' == typeof name) { + this.routes.param(name); + // single + } else { + if (':' == name[0]) name = name.substr(1); + fns.forEach(function(fn){ + self.routes.param(name, fn); + }); + } + + return this; +}; + +/** + * Assign a custom exception handler callback `fn`. + * These handlers are always _last_ in the middleware stack. + * + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.error = function(fn){ + this.errorHandlers.push(fn); + return this; +}; + +/** + * Register the given callback `fn` for the given `type`. + * + * @param {String} type + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.is = function(type, fn){ + if (!fn) return this.isCallbacks[type]; + this.isCallbacks[type] = fn; + return this; +}; + +/** + * Assign `setting` to `val`, or return `setting`'s value. + * Mounted servers inherit their parent server's settings. + * + * @param {String} setting + * @param {String} val + * @return {Server|Mixed} for chaining, or the setting value + * @api public + */ + +app.set = function(setting, val){ + if (val === undefined) { + if (this.settings.hasOwnProperty(setting)) { + return this.settings[setting]; + } else if (this.parent) { + return this.parent.set(setting); + } + } else { + this.settings[setting] = val; + return this; + } +}; + +/** + * Check if `setting` is enabled. + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.enabled = function(setting){ + return !!this.set(setting); +}; + +/** + * Check if `setting` is disabled. + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.disabled = function(setting){ + return !this.set(setting); +}; + +/** + * Enable `setting`. + * + * @param {String} setting + * @return {Server} for chaining + * @api public + */ + +app.enable = function(setting){ + return this.set(setting, true); +}; + +/** + * Disable `setting`. + * + * @param {String} setting + * @return {Server} for chaining + * @api public + */ + +app.disable = function(setting){ + return this.set(setting, false); +}; + +/** + * Redirect `key` to `url`. + * + * @param {String} key + * @param {String} url + * @return {Server} for chaining + * @api public + */ + +app.redirect = function(key, url){ + this.redirects[key] = url; + return this; +}; + +/** + * Configure callback for zero or more envs, + * when no env is specified that callback will + * be invoked for all environments. Any combination + * can be used multiple times, in any order desired. + * + * Examples: + * + * app.configure(function(){ + * // executed for all envs + * }); + * + * app.configure('stage', function(){ + * // executed staging env + * }); + * + * app.configure('stage', 'production', function(){ + * // executed for stage and production + * }); + * + * @param {String} env... + * @param {Function} fn + * @return {Server} for chaining + * @api public + */ + +app.configure = function(env, fn){ + var envs = 'all' + , args = toArray(arguments); + fn = args.pop(); + if (args.length) envs = args; + if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this); + return this; +}; + +/** + * Delegate `.VERB(...)` calls to `.route(VERB, ...)`. + */ + +methods.forEach(function(method){ + app[method] = function(path){ + if (1 == arguments.length) return this.routes.lookup(method, path); + var args = [method].concat(toArray(arguments)); + if (!this.__usedRouter) this.use(this.router); + return this.routes._route.apply(this.routes, args); + } +}); + +/** + * Special-cased "all" method, applying the given route `path`, + * middleware, and callback to _every_ HTTP method. + * + * @param {String} path + * @param {Function} ... + * @return {Server} for chaining + * @api public + */ + +app.all = function(path){ + var args = arguments; + if (1 == args.length) return this.routes.lookup('all', path); + methods.forEach(function(method){ + if ('all' == method || 'del' == method) return; + app[method].apply(this, args); + }, this); + return this; +}; + +// del -> delete alias + +app.del = app.delete; + diff --git a/test/node_modules/express/lib/https.js b/test/node_modules/express/lib/https.js new file mode 100644 index 0000000..8a8c008 --- /dev/null +++ b/test/node_modules/express/lib/https.js @@ -0,0 +1,52 @@ + +/*! + * Express - HTTPSServer + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var connect = require('connect') + , HTTPServer = require('./http') + , https = require('https'); + +/** + * Expose `HTTPSServer`. + */ + +exports = module.exports = HTTPSServer; + +/** + * Server proto. + */ + +var app = HTTPSServer.prototype; + +/** + * Initialize a new `HTTPSServer` with the + * given `options`, and optional `middleware`. + * + * @param {Object} options + * @param {Array} middleware + * @api public + */ + +function HTTPSServer(options, middleware){ + connect.HTTPSServer.call(this, options, []); + this.init(middleware); +}; + +/** + * Inherit from `connect.HTTPSServer`. + */ + +app.__proto__ = connect.HTTPSServer.prototype; + +// mixin HTTPServer methods + +Object.keys(HTTPServer.prototype).forEach(function(method){ + app[method] = HTTPServer.prototype[method]; +}); diff --git a/test/node_modules/express/lib/request.js b/test/node_modules/express/lib/request.js new file mode 100644 index 0000000..4138afe --- /dev/null +++ b/test/node_modules/express/lib/request.js @@ -0,0 +1,357 @@ + +/*! + * Express - request + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , req = http.IncomingMessage.prototype + , utils = require('./utils') + , parse = require('url').parse + , mime = require('mime'); + +/** + * Default flash formatters. + * + * @type Object + */ + +var flashFormatters = exports.flashFormatters = { + s: function(val){ + return String(val); + } +}; + +/** + * Return request header or optional default. + * + * The `Referrer` header field is special-cased, + * both `Referrer` and `Referer` will yield are + * interchangeable. + * + * Examples: + * + * req.header('Content-Type'); + * // => "text/plain" + * + * req.header('content-type'); + * // => "text/plain" + * + * req.header('Accept'); + * // => undefined + * + * req.header('Accept', 'text/html'); + * // => "text/html" + * + * @param {String} name + * @param {String} defaultValue + * @return {String} + * @api public + */ + +req.header = function(name, defaultValue){ + switch (name = name.toLowerCase()) { + case 'referer': + case 'referrer': + return this.headers.referrer + || this.headers.referer + || defaultValue; + default: + return this.headers[name] || defaultValue; + } +}; + +/** + * Get `field`'s `param` value, defaulting to ''. + * + * Examples: + * + * req.get('content-disposition', 'filename'); + * // => "something.png" + * + * @param {String} field + * @param {String} param + * @return {String} + * @api public + */ + +req.get = function(field, param){ + var val = this.header(field); + if (!val) return ''; + var regexp = new RegExp(param + ' *= *(?:"([^"]+)"|([^;]+))', 'i'); + if (!regexp.exec(val)) return ''; + return RegExp.$1 || RegExp.$2; +}; + +/** + * Short-hand for `require('url').parse(req.url).pathname`. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('path', function(){ + return parse(this.url).pathname; +}); + +/** + * Check if the _Accept_ header is present, and includes the given `type`. + * + * When the _Accept_ header is not present `true` is returned. Otherwise + * the given `type` is matched by an exact match, and then subtypes. You + * may pass the subtype such as "html" which is then converted internally + * to "text/html" using the mime lookup table. + * + * Examples: + * + * // Accept: text/html + * req.accepts('html'); + * // => true + * + * // Accept: text/*; application/json + * req.accepts('html'); + * req.accepts('text/html'); + * req.accepts('text/plain'); + * req.accepts('application/json'); + * // => true + * + * req.accepts('image/png'); + * req.accepts('png'); + * // => false + * + * @param {String} type + * @return {Boolean} + * @api public + */ + +req.accepts = function(type){ + var accept = this.header('Accept'); + + // normalize extensions ".json" -> "json" + if (type && '.' == type[0]) type = type.substr(1); + + // when Accept does not exist, or contains '*/*' return true + if (!accept || ~accept.indexOf('*/*')) { + return true; + } else if (type) { + // allow "html" vs "text/html" etc + if (!~type.indexOf('/')) type = mime.lookup(type); + + // check if we have a direct match + if (~accept.indexOf(type)) return true; + + // check if we have type/* + type = type.split('/')[0] + '/*'; + return !!~accept.indexOf(type); + } else { + return false; + } +}; + +/** + * Return the value of param `name` when present or `defaultValue`. + * + * - Checks route placeholders, ex: _/user/:id_ + * - Checks query string params, ex: ?id=12 + * - Checks urlencoded body params, ex: id=12 + * + * To utilize urlencoded request bodies, `req.body` + * should be an object. This can be done by using + * the `connect.bodyParser` middleware. + * + * @param {String} name + * @param {Mixed} defaultValue + * @return {String} + * @api public + */ + +req.param = function(name, defaultValue){ + // route params like /user/:id + if (this.params && this.params.hasOwnProperty(name) && undefined !== this.params[name]) { + return this.params[name]; + } + // query string params + if (undefined !== this.query[name]) { + return this.query[name]; + } + // request body params via connect.bodyParser + if (this.body && undefined !== this.body[name]) { + return this.body[name]; + } + return defaultValue; +}; + +/** + * Queue flash `msg` of the given `type`. + * + * Examples: + * + * req.flash('info', 'email sent'); + * req.flash('error', 'email delivery failed'); + * req.flash('info', 'email re-sent'); + * // => 2 + * + * req.flash('info'); + * // => ['email sent', 'email re-sent'] + * + * req.flash('info'); + * // => [] + * + * req.flash(); + * // => { error: ['email delivery failed'], info: [] } + * + * Formatting: + * + * Flash notifications also support arbitrary formatting support. + * For example you may pass variable arguments to `req.flash()` + * and use the %s specifier to be replaced by the associated argument: + * + * req.flash('info', 'email has been sent to %s.', userName); + * + * To add custom formatters use the `exports.flashFormatters` object. + * + * @param {String} type + * @param {String} msg + * @return {Array|Object|Number} + * @api public + */ + +req.flash = function(type, msg){ + if (this.session === undefined) throw Error('req.flash() requires sessions'); + var msgs = this.session.flash = this.session.flash || {}; + if (type && msg) { + var i = 2 + , args = arguments + , formatters = this.app.flashFormatters || {}; + formatters.__proto__ = flashFormatters; + msg = utils.miniMarkdown(msg); + msg = msg.replace(/%([a-zA-Z])/g, function(_, format){ + var formatter = formatters[format]; + if (formatter) return formatter(utils.escape(args[i++])); + }); + return (msgs[type] = msgs[type] || []).push(msg); + } else if (type) { + var arr = msgs[type]; + delete msgs[type]; + return arr || []; + } else { + this.session.flash = {}; + return msgs; + } +}; + +/** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains the give mime `type`. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * req.is('html'); + * req.is('text/html'); + * // => true + * + * // When Content-Type is application/json + * req.is('json'); + * req.is('application/json'); + * // => true + * + * req.is('html'); + * // => false + * + * Ad-hoc callbacks can also be registered with Express, to perform + * assertions again the request, for example if we need an expressive + * way to check if our incoming request is an image, we can register "an image" + * callback: + * + * app.is('an image', function(req){ + * return 0 == req.headers['content-type'].indexOf('image'); + * }); + * + * Now within our route callbacks, we can use to to assert content types + * such as "image/jpeg", "image/png", etc. + * + * app.post('/image/upload', function(req, res, next){ + * if (req.is('an image')) { + * // do something + * } else { + * next(); + * } + * }); + * + * @param {String} type + * @return {Boolean} + * @api public + */ + +req.is = function(type){ + var fn = this.app.is(type); + if (fn) return fn(this); + var ct = this.headers['content-type']; + if (!ct) return false; + ct = ct.split(';')[0]; + if (!~type.indexOf('/')) type = mime.lookup(type); + if (~type.indexOf('*')) { + type = type.split('/'); + ct = ct.split('/'); + if ('*' == type[0] && type[1] == ct[1]) return true; + if ('*' == type[1] && type[0] == ct[0]) return true; + return false; + } + return !! ~ct.indexOf(type); +}; + +// Callback for isXMLHttpRequest / xhr + +function isxhr() { + return this.header('X-Requested-With', '').toLowerCase() === 'xmlhttprequest'; +} + +/** + * Check if the request was an _XMLHttpRequest_. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('isXMLHttpRequest', isxhr); +req.__defineGetter__('xhr', isxhr); + +/** + * Return the protocol string "http" or "https" + * when requested with TLS. When the "trust proxy" + * setting is enabled the "X-Forwarded-Proto" header + * field will be trusted. If you're running behind + * a reverse proxy that supplies https for you this + * may be enabled. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('protocol', function(){ + var trustProxy = this.app.set('trust proxy'); + return this.connection.encrypted + ? 'https' + : trustProxy + ? (this.header('X-Forwarded-Proto') || 'http') + : 'http'; +}); + +/** + * Short-hand for: + * + * req.protocol == 'https' + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('secure', function(){ + return 'https' == this.protocol; +}); \ No newline at end of file diff --git a/test/node_modules/express/lib/response.js b/test/node_modules/express/lib/response.js new file mode 100644 index 0000000..0174950 --- /dev/null +++ b/test/node_modules/express/lib/response.js @@ -0,0 +1,459 @@ + +/*! + * Express - response + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , http = require('http') + , path = require('path') + , connect = require('connect') + , utils = connect.utils + , parseRange = require('./utils').parseRange + , res = http.ServerResponse.prototype + , send = connect.static.send + , mime = require('mime') + , basename = path.basename + , join = path.join; + +/** + * Send a response with the given `body` and optional `headers` and `status` code. + * + * Examples: + * + * res.send(); + * res.send(new Buffer('wahoo')); + * res.send({ some: 'json' }); + * res.send('

some html

'); + * res.send('Sorry, cant find that', 404); + * res.send('text', { 'Content-Type': 'text/plain' }, 201); + * res.send(404); + * + * @param {String|Object|Number|Buffer} body or status + * @param {Object|Number} headers or status + * @param {Number} status + * @return {ServerResponse} + * @api public + */ + +res.send = function(body, headers, status){ + // allow status as second arg + if ('number' == typeof headers) { + status = headers, + headers = null; + } + + // default status + status = status || this.statusCode; + + // allow 0 args as 204 + if (!arguments.length || undefined === body) status = 204; + + // determine content type + switch (typeof body) { + case 'number': + if (!this.header('Content-Type')) { + this.contentType('.txt'); + } + body = http.STATUS_CODES[status = body]; + break; + case 'string': + if (!this.header('Content-Type')) { + this.charset = this.charset || 'utf-8'; + this.contentType('.html'); + } + break; + case 'boolean': + case 'object': + if (Buffer.isBuffer(body)) { + if (!this.header('Content-Type')) { + this.contentType('.bin'); + } + } else { + return this.json(body, headers, status); + } + break; + } + + // populate Content-Length + if (undefined !== body && !this.header('Content-Length')) { + this.header('Content-Length', Buffer.isBuffer(body) + ? body.length + : Buffer.byteLength(body)); + } + + // merge headers passed + if (headers) { + var fields = Object.keys(headers); + for (var i = 0, len = fields.length; i < len; ++i) { + var field = fields[i]; + this.header(field, headers[field]); + } + } + + // strip irrelevant headers + if (204 == status || 304 == status) { + this.removeHeader('Content-Type'); + this.removeHeader('Content-Length'); + body = ''; + } + + // respond + this.statusCode = status; + this.end('HEAD' == this.req.method ? null : body); + return this; +}; + +/** + * Send JSON response with `obj`, optional `headers`, and optional `status`. + * + * Examples: + * + * res.json(null); + * res.json({ user: 'tj' }); + * res.json('oh noes!', 500); + * res.json('I dont have that', 404); + * + * @param {Mixed} obj + * @param {Object|Number} headers or status + * @param {Number} status + * @return {ServerResponse} + * @api public + */ + +res.json = function(obj, headers, status){ + var body = JSON.stringify(obj) + , callback = this.req.query.callback + , jsonp = this.app.enabled('jsonp callback'); + + this.charset = this.charset || 'utf-8'; + this.header('Content-Type', 'application/json'); + + if (callback && jsonp) { + this.header('Content-Type', 'text/javascript'); + body = callback.replace(/[^\w$.]/g, '') + '(' + body + ');'; + } + + return this.send(body, headers, status); +}; + +/** + * Set status `code`. + * + * @param {Number} code + * @return {ServerResponse} + * @api public + */ + +res.status = function(code){ + this.statusCode = code; + return this; +}; + +/** + * Transfer the file at the given `path`. Automatically sets + * the _Content-Type_ response header field. `next()` is called + * when `path` is a directory, or when an error occurs. + * + * Options: + * + * - `maxAge` defaulting to 0 + * - `root` root directory for relative filenames + * + * @param {String} path + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +res.sendfile = function(path, options, fn){ + var next = this.req.next; + options = options || {}; + + // support function as second arg + if ('function' == typeof options) { + fn = options; + options = {}; + } + + options.path = encodeURIComponent(path); + options.callback = fn; + send(this.req, this, next, options); +}; + +/** + * Set _Content-Type_ response header passed through `mime.lookup()`. + * + * Examples: + * + * var filename = 'path/to/image.png'; + * res.contentType(filename); + * // res.headers['Content-Type'] is now "image/png" + * + * res.contentType('.html'); + * res.contentType('html'); + * res.contentType('json'); + * res.contentType('png'); + * + * @param {String} type + * @return {String} the resolved mime type + * @api public + */ + +res.contentType = function(type){ + return this.header('Content-Type', mime.lookup(type)); +}; + +/** + * Set _Content-Disposition_ header to _attachment_ with optional `filename`. + * + * @param {String} filename + * @return {ServerResponse} + * @api public + */ + +res.attachment = function(filename){ + if (filename) this.contentType(filename); + this.header('Content-Disposition', filename + ? 'attachment; filename="' + basename(filename) + '"' + : 'attachment'); + return this; +}; + +/** + * Transfer the file at the given `path`, with optional + * `filename` as an attachment and optional callback `fn(err)`, + * and optional `fn2(err)` which is invoked when an error has + * occurred after header has been sent. + * + * @param {String} path + * @param {String|Function} filename or fn + * @param {Function} fn + * @param {Function} fn2 + * @api public + */ + +res.download = function(path, filename, fn, fn2){ + var self = this; + + // support callback as second arg + if ('function' == typeof filename) { + fn2 = fn; + fn = filename; + filename = null; + } + + // transfer the file + this.attachment(filename || path).sendfile(path, function(err){ + var sentHeader = self._header; + if (err) { + if (!sentHeader) self.removeHeader('Content-Disposition'); + if (sentHeader) { + fn2 && fn2(err); + } else if (fn) { + fn(err); + } else { + self.req.next(err); + } + } else if (fn) { + fn(); + } + }); +}; + +/** + * Set or get response header `name` with optional `val`. + * + * @param {String} name + * @param {String} val + * @return {ServerResponse} for chaining + * @api public + */ + +res.header = function(name, val){ + if (1 == arguments.length) return this.getHeader(name); + this.setHeader(name, val); + return this; +}; + +/** + * Clear cookie `name`. + * + * @param {String} name + * @param {Object} options + * @api public + */ + +res.clearCookie = function(name, options){ + var opts = { expires: new Date(1) }; + this.cookie(name, '', options + ? utils.merge(options, opts) + : opts); +}; + +/** + * Set cookie `name` to `val`, with the given `options`. + * + * Options: + * + * - `maxAge` max-age in milliseconds, converted to `expires` + * - `path` defaults to the "basepath" setting which is typically "/" + * + * Examples: + * + * // "Remember Me" for 15 minutes + * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); + * + * // save as above + * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) + * + * @param {String} name + * @param {String} val + * @param {Options} options + * @api public + */ + +res.cookie = function(name, val, options){ + options = options || {}; + if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge); + if (undefined === options.path) options.path = this.app.set('basepath'); + var cookie = utils.serializeCookie(name, val, options); + this.header('Set-Cookie', cookie); +}; + +/** + * Redirect to the given `url` with optional response `status` + * defauling to 302. + * + * The given `url` can also be the name of a mapped url, for + * example by default express supports "back" which redirects + * to the _Referrer_ or _Referer_ headers or the application's + * "basepath" setting. Express also supports "basepath" out of the box, + * which can be set via `app.set('basepath', '/blog');`, and defaults + * to '/'. + * + * Redirect Mapping: + * + * To extend the redirect mapping capabilities that Express provides, + * we may use the `app.redirect()` method: + * + * app.redirect('google', 'http://google.com'); + * + * Now in a route we may call: + * + * res.redirect('google'); + * + * We may also map dynamic redirects: + * + * app.redirect('comments', function(req, res){ + * return '/post/' + req.params.id + '/comments'; + * }); + * + * So now we may do the following, and the redirect will dynamically adjust to + * the context of the request. If we called this route with _GET /post/12_ our + * redirect _Location_ would be _/post/12/comments_. + * + * app.get('/post/:id', function(req, res){ + * res.redirect('comments'); + * }); + * + * Unless an absolute `url` is given, the app's mount-point + * will be respected. For example if we redirect to `/posts`, + * and our app is mounted at `/blog` we will redirect to `/blog/posts`. + * + * @param {String} url + * @param {Number} code + * @api public + */ + +res.redirect = function(url, status){ + var app = this.app + , req = this.req + , base = app.set('basepath') || app.route + , status = status || 302 + , head = 'HEAD' == req.method + , body; + + // Setup redirect map + var map = { + back: req.header('Referrer', base) + , home: base + }; + + // Support custom redirect map + map.__proto__ = app.redirects; + + // Attempt mapped redirect + var mapped = 'function' == typeof map[url] + ? map[url](req, this) + : map[url]; + + // Perform redirect + url = mapped || url; + + // Relative + if (!~url.indexOf('://')) { + // Respect mount-point + if ('/' != base && 0 != url.indexOf(base)) url = base + url; + + // Absolute + var host = req.headers.host; + url = req.protocol + '://' + host + url; + } + + // Support text/{plain,html} by default + if (req.accepts('html')) { + body = '

' + http.STATUS_CODES[status] + '. Redirecting to ' + url + '

'; + this.header('Content-Type', 'text/html'); + } else { + body = http.STATUS_CODES[status] + '. Redirecting to ' + url; + this.header('Content-Type', 'text/plain'); + } + + // Respond + this.statusCode = status; + this.header('Location', url); + this.end(head ? null : body); +}; + +/** + * Assign the view local variable `name` to `val` or return the + * local previously assigned to `name`. + * + * @param {String} name + * @param {Mixed} val + * @return {Mixed} val + * @api public + */ + +res.local = function(name, val){ + this._locals = this._locals || {}; + return undefined === val + ? this._locals[name] + : this._locals[name] = val; +}; + +/** + * Assign several locals with the given `obj`, + * or return the locals. + * + * @param {Object} obj + * @return {Object|Undefined} + * @api public + */ + +res.locals = +res.helpers = function(obj){ + if (obj) { + for (var key in obj) { + this.local(key, obj[key]); + } + } else { + return this._locals; + } +}; diff --git a/test/node_modules/express/lib/router/collection.js b/test/node_modules/express/lib/router/collection.js new file mode 100644 index 0000000..991a9a2 --- /dev/null +++ b/test/node_modules/express/lib/router/collection.js @@ -0,0 +1,53 @@ + +/*! + * Express - router - Collection + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Expose `Collection`. + */ + +module.exports = Collection; + +/** + * Initialize a new route `Collection` + * with the given `router`. + * + * @param {Router} router + * @api private + */ + +function Collection(router) { + Array.apply(this, arguments); + this.router = router; +} + +/** + * Inherit from `Array.prototype`. + */ + +Collection.prototype.__proto__ = Array.prototype; + +/** + * Remove the routes in this collection. + * + * @return {Collection} of routes removed + * @api public + */ + +Collection.prototype.remove = function(){ + var router = this.router + , len = this.length + , ret = new Collection(this.router); + + for (var i = 0; i < len; ++i) { + if (router.remove(this[i])) { + ret.push(this[i]); + } + } + + return ret; +}; + diff --git a/test/node_modules/express/lib/router/index.js b/test/node_modules/express/lib/router/index.js new file mode 100644 index 0000000..ff1498b --- /dev/null +++ b/test/node_modules/express/lib/router/index.js @@ -0,0 +1,398 @@ + +/*! + * Express - Router + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Route = require('./route') + , Collection = require('./collection') + , utils = require('../utils') + , parse = require('url').parse + , toArray = utils.toArray; + +/** + * Expose `Router` constructor. + */ + +exports = module.exports = Router; + +/** + * Expose HTTP methods. + */ + +var methods = exports.methods = require('./methods'); + +/** + * Initialize a new `Router` with the given `app`. + * + * @param {express.HTTPServer} app + * @api private + */ + +function Router(app) { + var self = this; + this.app = app; + this.routes = {}; + this.params = {}; + this._params = []; + + this.middleware = function(req, res, next){ + self._dispatch(req, res, next); + }; +} + +/** + * Register a param callback `fn` for the given `name`. + * + * @param {String|Function} name + * @param {Function} fn + * @return {Router} for chaining + * @api public + */ + +Router.prototype.param = function(name, fn){ + // param logic + if ('function' == typeof name) { + this._params.push(name); + return; + } + + // apply param functions + var params = this._params + , len = params.length + , ret; + + for (var i = 0; i < len; ++i) { + if (ret = params[i](name, fn)) { + fn = ret; + } + } + + // ensure we end up with a + // middleware function + if ('function' != typeof fn) { + throw new Error('invalid param() call for ' + name + ', got ' + fn); + } + + (this.params[name] = this.params[name] || []).push(fn); + return this; +}; + +/** + * Return a `Collection` of all routes defined. + * + * @return {Collection} + * @api public + */ + +Router.prototype.all = function(){ + return this.find(function(){ + return true; + }); +}; + +/** + * Remove the given `route`, returns + * a bool indicating if the route was present + * or not. + * + * @param {Route} route + * @return {Boolean} + * @api public + */ + +Router.prototype.remove = function(route){ + var routes = this.routes[route.method] + , len = routes.length; + + for (var i = 0; i < len; ++i) { + if (route == routes[i]) { + routes.splice(i, 1); + return true; + } + } +}; + +/** + * Return routes with route paths matching `path`. + * + * @param {String} method + * @param {String} path + * @return {Collection} + * @api public + */ + +Router.prototype.lookup = function(method, path){ + return this.find(function(route){ + return path == route.path + && (route.method == method + || method == 'all'); + }); +}; + +/** + * Return routes with regexps that match the given `url`. + * + * @param {String} method + * @param {String} url + * @return {Collection} + * @api public + */ + +Router.prototype.match = function(method, url){ + return this.find(function(route){ + return route.match(url) + && (route.method == method + || method == 'all'); + }); +}; + +/** + * Find routes based on the return value of `fn` + * which is invoked once per route. + * + * @param {Function} fn + * @return {Collection} + * @api public + */ + +Router.prototype.find = function(fn){ + var len = methods.length + , ret = new Collection(this) + , method + , routes + , route; + + for (var i = 0; i < len; ++i) { + method = methods[i]; + routes = this.routes[method]; + if (!routes) continue; + for (var j = 0, jlen = routes.length; j < jlen; ++j) { + route = routes[j]; + if (fn(route)) ret.push(route); + } + } + + return ret; +}; + +/** + * Route dispatcher aka the route "middleware". + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @param {Function} next + * @api private + */ + +Router.prototype._dispatch = function(req, res, next){ + var params = this.params + , self = this; + + // route dispatch + (function pass(i, err){ + var paramCallbacks + , paramIndex = 0 + , paramVal + , route + , keys + , key + , ret; + + // match next route + function nextRoute(err) { + pass(req._route_index + 1, err); + } + + // match route + req.route = route = self._match(req, i); + + // implied OPTIONS + if (!route && 'OPTIONS' == req.method) return self._options(req, res); + + // no route + if (!route) return next(err); + + // we have a route + // start at param 0 + req.params = route.params; + keys = route.keys; + i = 0; + + // param callbacks + function param(err) { + paramIndex = 0; + key = keys[i++]; + paramVal = key && req.params[key.name]; + paramCallbacks = key && params[key.name]; + + try { + if ('route' == err) { + nextRoute(); + } else if (err) { + i = 0; + callbacks(err); + } else if (paramCallbacks && undefined !== paramVal) { + paramCallback(); + } else if (key) { + param(); + } else { + i = 0; + callbacks(); + } + } catch (err) { + param(err); + } + }; + + param(err); + + // single param callbacks + function paramCallback(err) { + var fn = paramCallbacks[paramIndex++]; + if (err || !fn) return param(err); + fn(req, res, paramCallback, paramVal, key.name); + } + + // invoke route callbacks + function callbacks(err) { + var fn = route.callbacks[i++]; + try { + if ('route' == err) { + nextRoute(); + } else if (err && fn) { + if (fn.length < 4) return callbacks(err); + fn(err, req, res, callbacks); + } else if (fn) { + fn(req, res, callbacks); + } else { + nextRoute(err); + } + } catch (err) { + callbacks(err); + } + } + })(0); +}; + +/** + * Respond to __OPTIONS__ method. + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @api private + */ + +Router.prototype._options = function(req, res){ + var path = parse(req.url).pathname + , body = this._optionsFor(path).join(','); + res.send(body, { Allow: body }); +}; + +/** + * Return an array of HTTP verbs or "options" for `path`. + * + * @param {String} path + * @return {Array} + * @api private + */ + +Router.prototype._optionsFor = function(path){ + var self = this; + return methods.filter(function(method){ + var routes = self.routes[method]; + if (!routes || 'options' == method) return; + for (var i = 0, len = routes.length; i < len; ++i) { + if (routes[i].match(path)) return true; + } + }).map(function(method){ + return method.toUpperCase(); + }); +}; + +/** + * Attempt to match a route for `req` + * starting from offset `i`. + * + * @param {IncomingMessage} req + * @param {Number} i + * @return {Route} + * @api private + */ + +Router.prototype._match = function(req, i){ + var method = req.method.toLowerCase() + , url = parse(req.url) + , path = url.pathname + , routes = this.routes + , captures + , route + , keys; + + // pass HEAD to GET routes + if ('head' == method) method = 'get'; + + // routes for this method + if (routes = routes[method]) { + + // matching routes + for (var len = routes.length; i < len; ++i) { + route = routes[i]; + if (captures = route.match(path)) { + keys = route.keys; + route.params = []; + + // params from capture groups + for (var j = 1, jlen = captures.length; j < jlen; ++j) { + var key = keys[j-1] + , val = 'string' == typeof captures[j] + ? decodeURIComponent(captures[j]) + : captures[j]; + if (key) { + route.params[key.name] = val; + } else { + route.params.push(val); + } + } + + // all done + req._route_index = i; + return route; + } + } + } +}; + +/** + * Route `method`, `path`, and one or more callbacks. + * + * @param {String} method + * @param {String} path + * @param {Function} callback... + * @return {Router} for chaining + * @api private + */ + +Router.prototype._route = function(method, path, callbacks){ + var app = this.app + , callbacks = utils.flatten(toArray(arguments, 2)); + + // ensure path was given + if (!path) throw new Error('app.' + method + '() requires a path'); + + // create the route + var route = new Route(method, path, callbacks, { + sensitive: app.enabled('case sensitive routes') + , strict: app.enabled('strict routing') + }); + + // add it + (this.routes[method] = this.routes[method] || []) + .push(route); + return this; +}; diff --git a/test/node_modules/express/lib/router/methods.js b/test/node_modules/express/lib/router/methods.js new file mode 100644 index 0000000..71a969a --- /dev/null +++ b/test/node_modules/express/lib/router/methods.js @@ -0,0 +1,79 @@ + +/*! + * Express - router - methods + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Hypertext Transfer Protocol -- HTTP/1.1 + * http://www.ietf.org/rfc/rfc2616.txt + */ + +var RFC2616 = ['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT']; + +/** + * HTTP Extensions for Distributed Authoring -- WEBDAV + * http://www.ietf.org/rfc/rfc2518.txt + */ + +var RFC2518 = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK']; + +/** + * Versioning Extensions to WebDAV + * http://www.ietf.org/rfc/rfc3253.txt + */ + +var RFC3253 = ['VERSION-CONTROL', 'REPORT', 'CHECKOUT', 'CHECKIN', 'UNCHECKOUT', 'MKWORKSPACE', 'UPDATE', 'LABEL', 'MERGE', 'BASELINE-CONTROL', 'MKACTIVITY']; + +/** + * Ordered Collections Protocol (WebDAV) + * http://www.ietf.org/rfc/rfc3648.txt + */ + +var RFC3648 = ['ORDERPATCH']; + +/** + * Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol + * http://www.ietf.org/rfc/rfc3744.txt + */ + +var RFC3744 = ['ACL']; + +/** + * Web Distributed Authoring and Versioning (WebDAV) SEARCH + * http://www.ietf.org/rfc/rfc5323.txt + */ + +var RFC5323 = ['SEARCH']; + +/** + * PATCH Method for HTTP + * http://www.ietf.org/rfc/rfc5789.txt + */ + +var RFC5789 = ['PATCH']; + +/** + * PURGE Method for caching reverse-proxy + * http://wiki.squid-cache.org/SquidFaq/OperatingSquid#How_can_I_purge_an_object_from_my_cache.3F + * https://www.varnish-cache.org/docs/trunk/tutorial/purging.html + */ + +var CACHE_PURGE = ['PURGE']; + +/** + * Expose the methods. + */ + +module.exports = [].concat( + RFC2616 + , RFC2518 + , RFC3253 + , RFC3648 + , RFC3744 + , RFC5323 + , RFC5789 + , CACHE_PURGE).map(function(method){ + return method.toLowerCase(); + }); diff --git a/test/node_modules/express/lib/router/route.js b/test/node_modules/express/lib/router/route.js new file mode 100644 index 0000000..7f2965c --- /dev/null +++ b/test/node_modules/express/lib/router/route.js @@ -0,0 +1,88 @@ + +/*! + * Express - router - Route + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Expose `Route`. + */ + +module.exports = Route; + +/** + * Initialize `Route` with the given HTTP `method`, `path`, + * and an array of `callbacks` and `options`. + * + * Options: + * + * - `sensitive` enable case-sensitive routes + * - `strict` enable strict matching for trailing slashes + * + * @param {String} method + * @param {String} path + * @param {Array} callbacks + * @param {Object} options. + * @api private + */ + +function Route(method, path, callbacks, options) { + options = options || {}; + this.path = path; + this.method = method; + this.callbacks = callbacks; + this.regexp = normalize(path + , this.keys = [] + , options.sensitive + , options.strict); +} + +/** + * Check if this route matches `path` and return captures made. + * + * @param {String} path + * @return {Array} + * @api private + */ + +Route.prototype.match = function(path){ + return this.regexp.exec(path); +}; + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * @param {String|RegExp} path + * @param {Array} keys + * @param {Boolean} sensitive + * @param {Boolean} strict + * @return {RegExp} + * @api private + */ + +function normalize(path, keys, sensitive, strict) { + if (path instanceof RegExp) return path; + path = path + .concat(strict ? '' : '/?') + .replace(/\/\(/g, '(?:/') + .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){ + keys.push({ name: key, optional: !! optional }); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + + (optional || ''); + }) + .replace(/([\/.])/g, '\\$1') + .replace(/\*/g, '(.*)'); + return new RegExp('^' + path + '$', sensitive ? '' : 'i'); +} diff --git a/test/node_modules/express/lib/utils.js b/test/node_modules/express/lib/utils.js new file mode 100644 index 0000000..d579f7c --- /dev/null +++ b/test/node_modules/express/lib/utils.js @@ -0,0 +1,152 @@ + +/*! + * Express - Utils + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Check if `path` looks absolute. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +exports.isAbsolute = function(path){ + if ('/' == path[0]) return true; + if (':' == path[1] && '\\' == path[2]) return true; +}; + +/** + * Merge object `b` with `a` giving precedence to + * values in object `a`. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.union = function(a, b){ + if (a && b) { + var keys = Object.keys(b) + , len = keys.length + , key; + for (var i = 0; i < len; ++i) { + key = keys[i]; + if (!a.hasOwnProperty(key)) { + a[key] = b[key]; + } + } + } + return a; +}; + +/** + * Flatten the given `arr`. + * + * @param {Array} arr + * @return {Array} + * @api private + */ + +exports.flatten = function(arr, ret){ + var ret = ret || [] + , len = arr.length; + for (var i = 0; i < len; ++i) { + if (Array.isArray(arr[i])) { + exports.flatten(arr[i], ret); + } else { + ret.push(arr[i]); + } + } + return ret; +}; + +/** + * Parse mini markdown implementation. + * The following conversions are supported, + * primarily for the "flash" middleware: + * + * _foo_ or *foo* become foo + * __foo__ or **foo** become foo + * [A](B) becomes A + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.miniMarkdown = function(str){ + return String(str) + .replace(/(__|\*\*)(.*?)\1/g, '$2') + .replace(/(_|\*)(.*?)\1/g, '$2') + .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); +}; + +/** + * Escape special characters in the given string of html. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html) { + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +}; + +/** + * Parse "Range" header `str` relative to the given file `size`. + * + * @param {Number} size + * @param {String} str + * @return {Array} + * @api private + */ + +exports.parseRange = function(size, str){ + var valid = true; + var arr = str.substr(6).split(',').map(function(range){ + var range = range.split('-') + , start = parseInt(range[0], 10) + , end = parseInt(range[1], 10); + + // -500 + if (isNaN(start)) { + start = size - end; + end = size - 1; + // 500- + } else if (isNaN(end)) { + end = size - 1; + } + + // Invalid + if (isNaN(start) || isNaN(end) || start > end) valid = false; + + return { start: start, end: end }; + }); + return valid ? arr : undefined; +}; + +/** + * Fast alternative to `Array.prototype.slice.call()`. + * + * @param {Arguments} args + * @param {Number} n + * @return {Array} + * @api public + */ + +exports.toArray = function(args, i){ + var arr = [] + , len = args.length + , i = i || 0; + for (; i < len; ++i) arr.push(args[i]); + return arr; +}; diff --git a/test/node_modules/express/lib/view.js b/test/node_modules/express/lib/view.js new file mode 100644 index 0000000..5258249 --- /dev/null +++ b/test/node_modules/express/lib/view.js @@ -0,0 +1,460 @@ + +/*! + * Express - view + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var path = require('path') + , extname = path.extname + , dirname = path.dirname + , basename = path.basename + , utils = require('connect').utils + , View = require('./view/view') + , partial = require('./view/partial') + , union = require('./utils').union + , merge = utils.merge + , http = require('http') + , res = http.ServerResponse.prototype; + +/** + * Expose constructors. + */ + +exports = module.exports = View; + +/** + * Export template engine registrar. + */ + +exports.register = View.register; + +/** + * Lookup and compile `view` with cache support by supplying + * both the `cache` object and `cid` string, + * followed by `options` passed to `exports.lookup()`. + * + * @param {String} view + * @param {Object} cache + * @param {Object} cid + * @param {Object} options + * @return {View} + * @api private + */ + +exports.compile = function(view, cache, cid, options){ + if (cache && cid && cache[cid]){ + options.filename = cache[cid].path; + return cache[cid]; + } + + // lookup + view = exports.lookup(view, options); + + // hints + if (!view.exists) { + if (options.hint) hintAtViewPaths(view.original, options); + var err = new Error('failed to locate view "' + view.original.view + '"'); + err.view = view.original; + throw err; + } + + // compile + options.filename = view.path; + view.fn = view.templateEngine.compile(view.contents, options); + cache[cid] = view; + + return view; +}; + +/** + * Lookup `view`, returning an instanceof `View`. + * + * Options: + * + * - `root` root directory path + * - `defaultEngine` default template engine + * - `parentView` parent `View` object + * - `cache` cache object + * - `cacheid` optional cache id + * + * Lookup: + * + * - partial `_` + * - any `/index` + * - non-layout `..//index` + * - any `/` + * - partial `/_` + * + * @param {String} view + * @param {Object} options + * @return {View} + * @api private + */ + +exports.lookup = function(view, options){ + var orig = view = new View(view, options) + , partial = options.isPartial + , layout = options.isLayout; + + // Try _ prefix ex: ./views/_.jade + // taking precedence over the direct path + if (partial) { + view = new View(orig.prefixPath, options); + if (!view.exists) view = orig; + } + + // Try index ex: ./views/user/index.jade + if (!layout && !view.exists) view = new View(orig.indexPath, options); + + // Try ..//index ex: ../user/index.jade + // when calling partial('user') within the same dir + if (!layout && !view.exists) view = new View(orig.upIndexPath, options); + + // Try root ex: /user.jade + if (!view.exists) view = new View(orig.rootPath, options); + + // Try root _ prefix ex: /_user.jade + if (!view.exists && partial) view = new View(view.prefixPath, options); + + view.original = orig; + return view; +}; + +/** + * Partial render helper. + * + * @api private + */ + +function renderPartial(res, view, options, parentLocals, parent){ + var collection, object, locals; + + if (options) { + // collection + if (options.collection) { + collection = options.collection; + delete options.collection; + } else if ('length' in options) { + collection = options; + options = {}; + } + + // locals + if (options.locals) { + locals = options.locals; + delete options.locals; + } + + // object + if ('Object' != options.constructor.name) { + object = options; + options = {}; + } else if (undefined != options.object) { + object = options.object; + delete options.object; + } + } else { + options = {}; + } + + // Inherit locals from parent + union(options, parentLocals); + + // Merge locals + if (locals) merge(options, locals); + + // Partials dont need layouts + options.isPartial = true; + options.layout = false; + + // Deduce name from view path + var name = options.as || partial.resolveObjectName(view); + + // Render partial + function render(){ + if (object) { + if ('string' == typeof name) { + options[name] = object; + } else if (name === global) { + merge(options, object); + } + } + return res.render(view, options, null, parent, true); + } + + // Collection support + if (collection) { + var len = collection.length + , buf = '' + , keys + , key + , val; + + options.collectionLength = len; + + if ('number' == typeof len || Array.isArray(collection)) { + for (var i = 0; i < len; ++i) { + val = collection[i]; + options.firstInCollection = i == 0; + options.indexInCollection = i; + options.lastInCollection = i == len - 1; + object = val; + buf += render(); + } + } else { + keys = Object.keys(collection); + len = keys.length; + options.collectionLength = len; + options.collectionKeys = keys; + for (var i = 0; i < len; ++i) { + key = keys[i]; + val = collection[key]; + options.keyInCollection = key; + options.firstInCollection = i == 0; + options.indexInCollection = i; + options.lastInCollection = i == len - 1; + object = val; + buf += render(); + } + } + + return buf; + } else { + return render(); + } +}; + +/** + * Render `view` partial with the given `options`. Optionally a + * callback `fn(err, str)` may be passed instead of writing to + * the socket. + * + * Options: + * + * - `object` Single object with name derived from the view (unless `as` is present) + * + * - `as` Variable name for each `collection` value, defaults to the view name. + * * as: 'something' will add the `something` local variable + * * as: this will use the collection value as the template context + * * as: global will merge the collection value's properties with `locals` + * + * - `collection` Array of objects, the name is derived from the view name itself. + * For example _video.html_ will have a object _video_ available to it. + * + * @param {String} view + * @param {Object|Array|Function} options, collection, callback, or object + * @param {Function} fn + * @return {String} + * @api public + */ + +res.partial = function(view, options, fn){ + var app = this.app + , options = options || {} + , viewEngine = app.set('view engine') + , parent = {}; + + // accept callback as second argument + if ('function' == typeof options) { + fn = options; + options = {}; + } + + // root "views" option + parent.dirname = app.set('views') || process.cwd() + '/views'; + + // utilize "view engine" option + if (viewEngine) parent.engine = viewEngine; + + // render the partial + try { + var str = renderPartial(this, view, options, null, parent); + } catch (err) { + if (fn) { + fn(err); + } else { + this.req.next(err); + } + return; + } + + // callback or transfer + if (fn) { + fn(null, str); + } else { + this.send(str); + } +}; + +/** + * Render `view` with the given `options` and optional callback `fn`. + * When a callback function is given a response will _not_ be made + * automatically, however otherwise a response of _200_ and _text/html_ is given. + * + * Options: + * + * - `scope` Template evaluation context (the value of `this`) + * - `debug` Output debugging information + * - `status` Response status code + * + * @param {String} view + * @param {Object|Function} options or callback function + * @param {Function} fn + * @api public + */ + +res.render = function(view, opts, fn, parent, sub){ + // support callback function as second arg + if ('function' == typeof opts) { + fn = opts, opts = null; + } + + try { + return this._render(view, opts, fn, parent, sub); + } catch (err) { + // callback given + if (fn) { + fn(err); + // unwind to root call to prevent multiple callbacks + } else if (sub) { + throw err; + // root template, next(err) + } else { + this.req.next(err); + } + } +}; + +// private render() + +res._render = function(view, opts, fn, parent, sub){ + var options = {} + , self = this + , app = this.app + , helpers = app._locals + , dynamicHelpers = app.dynamicViewHelpers + , viewOptions = app.set('view options') + , root = app.set('views') || process.cwd() + '/views'; + + // cache id + var cid = app.enabled('view cache') + ? view + (parent ? ':' + parent.path : '') + : false; + + // merge "view options" + if (viewOptions) merge(options, viewOptions); + + // merge res._locals + if (this._locals) merge(options, this._locals); + + // merge render() options + if (opts) merge(options, opts); + + // merge render() .locals + if (opts && opts.locals) merge(options, opts.locals); + + // status support + if (options.status) this.statusCode = options.status; + + // capture attempts + options.attempts = []; + + var partial = options.isPartial + , layout = options.layout; + + // Layout support + if (true === layout || undefined === layout) { + layout = 'layout'; + } + + // Default execution scope to a plain object + options.scope = options.scope || {}; + + // Populate view + options.parentView = parent; + + // "views" setting + options.root = root; + + // "view engine" setting + options.defaultEngine = app.set('view engine'); + + // charset option + if (options.charset) this.charset = options.charset; + + // Dynamic helper support + if (false !== options.dynamicHelpers) { + // cache + if (!this.__dynamicHelpers) { + this.__dynamicHelpers = {}; + for (var key in dynamicHelpers) { + this.__dynamicHelpers[key] = dynamicHelpers[key].call( + this.app + , this.req + , this); + } + } + + // apply + merge(options, this.__dynamicHelpers); + } + + // Merge view helpers + union(options, helpers); + + // Always expose partial() as a local + options.partial = function(path, opts){ + return renderPartial(self, path, opts, options, view); + }; + + // View lookup + options.hint = app.enabled('hints'); + view = exports.compile(view, app.cache, cid, options); + + // layout helper + options.layout = function(path){ + layout = path; + }; + + // render + var str = view.fn.call(options.scope, options); + + // layout expected + if (layout) { + options.isLayout = true; + options.layout = false; + options.body = str; + this.render(layout, options, fn, view, true); + // partial return + } else if (partial) { + return str; + // render complete, and + // callback given + } else if (fn) { + fn(null, str); + // respond + } else { + this.send(str); + } +} + +/** + * Hint at view path resolution, outputting the + * paths that Express has tried. + * + * @api private + */ + +function hintAtViewPaths(view, options) { + console.error(); + console.error('failed to locate view "' + view.view + '", tried:'); + options.attempts.forEach(function(path){ + console.error(' - %s', path); + }); + console.error(); +} diff --git a/test/node_modules/express/lib/view/partial.js b/test/node_modules/express/lib/view/partial.js new file mode 100644 index 0000000..7d2f69b --- /dev/null +++ b/test/node_modules/express/lib/view/partial.js @@ -0,0 +1,40 @@ + +/*! + * Express - view - Partial + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Memory cache. + */ + +var cache = {}; + +/** + * Resolve partial object name from the view path. + * + * Examples: + * + * "user.ejs" becomes "user" + * "forum thread.ejs" becomes "forumThread" + * "forum/thread/post.ejs" becomes "post" + * "blog-post.ejs" becomes "blogPost" + * + * @return {String} + * @api private + */ + +exports.resolveObjectName = function(view){ + return cache[view] || (cache[view] = view + .split('/') + .slice(-1)[0] + .split('.')[0] + .replace(/^_/, '') + .replace(/[^a-zA-Z0-9 ]+/g, ' ') + .split(/ +/).map(function(word, i){ + return i + ? word[0].toUpperCase() + word.substr(1) + : word; + }).join('')); +}; \ No newline at end of file diff --git a/test/node_modules/express/lib/view/view.js b/test/node_modules/express/lib/view/view.js new file mode 100644 index 0000000..7d9392c --- /dev/null +++ b/test/node_modules/express/lib/view/view.js @@ -0,0 +1,210 @@ + +/*! + * Express - View + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var path = require('path') + , utils = require('../utils') + , extname = path.extname + , dirname = path.dirname + , basename = path.basename + , fs = require('fs') + , stat = fs.statSync; + +/** + * Expose `View`. + */ + +exports = module.exports = View; + +/** + * Require cache. + */ + +var cache = {}; + +/** + * Initialize a new `View` with the given `view` path and `options`. + * + * @param {String} view + * @param {Object} options + * @api private + */ + +function View(view, options) { + options = options || {}; + this.view = view; + this.root = options.root; + this.relative = false !== options.relative; + this.defaultEngine = options.defaultEngine; + this.parent = options.parentView; + this.basename = basename(view); + this.engine = this.resolveEngine(); + this.extension = '.' + this.engine; + this.name = this.basename.replace(this.extension, ''); + this.path = this.resolvePath(); + this.dirname = dirname(this.path); + if (options.attempts) { + if (!~options.attempts.indexOf(this.path)) + options.attempts.push(this.path); + } +}; + +/** + * Check if the view path exists. + * + * @return {Boolean} + * @api public + */ + +View.prototype.__defineGetter__('exists', function(){ + try { + stat(this.path); + return true; + } catch (err) { + return false; + } +}); + +/** + * Resolve view engine. + * + * @return {String} + * @api private + */ + +View.prototype.resolveEngine = function(){ + // Explicit + if (~this.basename.indexOf('.')) return extname(this.basename).substr(1); + // Inherit from parent + if (this.parent) return this.parent.engine; + // Default + return this.defaultEngine; +}; + +/** + * Resolve view path. + * + * @return {String} + * @api private + */ + +View.prototype.resolvePath = function(){ + var path = this.view; + // Implicit engine + if (!~this.basename.indexOf('.')) path += this.extension; + // Absolute + if (utils.isAbsolute(path)) return path; + // Relative to parent + if (this.relative && this.parent) return this.parent.dirname + '/' + path; + // Relative to root + return this.root + ? this.root + '/' + path + : path; +}; + +/** + * Get view contents. This is a one-time hit, so we + * can afford to be sync. + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('contents', function(){ + return fs.readFileSync(this.path, 'utf8'); +}); + +/** + * Get template engine api, cache exports to reduce + * require() calls. + * + * @return {Object} + * @api public + */ + +View.prototype.__defineGetter__('templateEngine', function(){ + var ext = this.extension; + return cache[ext] || (cache[ext] = require(this.engine)); +}); + +/** + * Return root path alternative. + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('rootPath', function(){ + this.relative = false; + return this.resolvePath(); +}); + +/** + * Return index path alternative. + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('indexPath', function(){ + return this.dirname + + '/' + this.basename.replace(this.extension, '') + + '/index' + this.extension; +}); + +/** + * Return ..//index path alternative. + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('upIndexPath', function(){ + return this.dirname + '/../' + this.name + '/index' + this.extension; +}); + +/** + * Return _ prefix path alternative + * + * @return {String} + * @api public + */ + +View.prototype.__defineGetter__('prefixPath', function(){ + return this.dirname + '/_' + this.basename; +}); + +/** + * Register the given template engine `exports` + * as `ext`. For example we may wish to map ".html" + * files to jade: + * + * app.register('.html', require('jade')); + * + * or + * + * app.register('html', require('jade')); + * + * This is also useful for libraries that may not + * match extensions correctly. For example my haml.js + * library is installed from npm as "hamljs" so instead + * of layout.hamljs, we can register the engine as ".haml": + * + * app.register('.haml', require('haml-js')); + * + * @param {String} ext + * @param {Object} obj + * @api public + */ + +exports.register = function(ext, exports) { + if ('.' != ext[0]) ext = '.' + ext; + cache[ext] = exports; +}; diff --git a/test/node_modules/express/node_modules/connect/.npmignore b/test/node_modules/express/node_modules/connect/.npmignore new file mode 100644 index 0000000..b04a224 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/.npmignore @@ -0,0 +1,11 @@ +*.markdown +*.md +.git* +Makefile +benchmarks/ +docs/ +examples/ +install.sh +support/ +test/ +.DS_Store diff --git a/test/node_modules/express/node_modules/connect/LICENSE b/test/node_modules/express/node_modules/connect/LICENSE new file mode 100644 index 0000000..0c5d22d --- /dev/null +++ b/test/node_modules/express/node_modules/connect/LICENSE @@ -0,0 +1,24 @@ +(The MIT License) + +Copyright (c) 2010 Sencha Inc. +Copyright (c) 2011 LearnBoost +Copyright (c) 2011 TJ Holowaychuk + +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. \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/index.js b/test/node_modules/express/node_modules/connect/index.js new file mode 100644 index 0000000..7477048 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/connect'); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/cache.js b/test/node_modules/express/node_modules/connect/lib/cache.js new file mode 100644 index 0000000..4aa026e --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/cache.js @@ -0,0 +1,81 @@ + +/*! + * Connect - Cache + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Expose `Cache`. + */ + +module.exports = Cache; + +/** + * LRU cache store. + * + * @param {Number} limit + * @api private + */ + +function Cache(limit) { + this.store = {}; + this.keys = []; + this.limit = limit; +} + +/** + * Touch `key`, promoting the object. + * + * @param {String} key + * @param {Number} i + * @api private + */ + +Cache.prototype.touch = function(key, i){ + this.keys.splice(i,1); + this.keys.push(key); +}; + +/** + * Remove `key`. + * + * @param {String} key + * @api private + */ + +Cache.prototype.remove = function(key){ + delete this.store[key]; +}; + +/** + * Get the object stored for `key`. + * + * @param {String} key + * @return {Array} + * @api private + */ + +Cache.prototype.get = function(key){ + return this.store[key]; +}; + +/** + * Add a cache `key`. + * + * @param {String} key + * @return {Array} + * @api private + */ + +Cache.prototype.add = function(key){ + // initialize store + var len = this.keys.push(key); + + // limit reached, invalid LRU + if (len > this.limit) this.remove(this.keys.shift()); + + var arr = this.store[key] = []; + arr.createdAt = new Date; + return arr; +}; diff --git a/test/node_modules/express/node_modules/connect/lib/connect.js b/test/node_modules/express/node_modules/connect/lib/connect.js new file mode 100644 index 0000000..338d96f --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/connect.js @@ -0,0 +1,106 @@ + +/*! + * Connect + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var HTTPServer = require('./http').Server + , HTTPSServer = require('./https').Server + , fs = require('fs'); + +// node patches + +require('./patch'); + +// expose createServer() as the module + +exports = module.exports = createServer; + +/** + * Framework version. + */ + +exports.version = '1.9.2'; + +/** + * Initialize a new `connect.HTTPServer` with the middleware + * passed to this function. When an object is passed _first_, + * we assume these are the tls options, and return a `connect.HTTPSServer`. + * + * Examples: + * + * An example HTTP server, accepting several middleware. + * + * var server = connect.createServer( + * connect.logger() + * , connect.static(__dirname + '/public') + * ); + * + * An HTTPS server, utilizing the same middleware as above. + * + * var server = connect.createServer( + * { key: key, cert: cert } + * , connect.logger() + * , connect.static(__dirname + '/public') + * ); + * + * Alternatively with connect 1.0 we may omit `createServer()`. + * + * connect( + * connect.logger() + * , connect.static(__dirname + '/public') + * ).listen(3000); + * + * @param {Object|Function} ... + * @return {Server} + * @api public + */ + +function createServer() { + if ('object' == typeof arguments[0]) { + return new HTTPSServer(arguments[0], Array.prototype.slice.call(arguments, 1)); + } else { + return new HTTPServer(Array.prototype.slice.call(arguments)); + } +}; + +// support connect.createServer() + +exports.createServer = createServer; + +// auto-load getters + +exports.middleware = {}; + +/** + * Auto-load bundled middleware with getters. + */ + +fs.readdirSync(__dirname + '/middleware').forEach(function(filename){ + if (/\.js$/.test(filename)) { + var name = filename.substr(0, filename.lastIndexOf('.')); + exports.middleware.__defineGetter__(name, function(){ + return require('./middleware/' + name); + }); + } +}); + +// expose utils + +exports.utils = require('./utils'); + +// expose getters as first-class exports + +exports.utils.merge(exports, exports.middleware); + +// expose constructors + +exports.HTTPServer = HTTPServer; +exports.HTTPSServer = HTTPSServer; + diff --git a/test/node_modules/express/node_modules/connect/lib/http.js b/test/node_modules/express/node_modules/connect/lib/http.js new file mode 100644 index 0000000..1d35f12 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/http.js @@ -0,0 +1,218 @@ + +/*! + * Connect - HTTPServer + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , parse = require('url').parse + , assert = require('assert') + , utils = require('./utils'); + +// environment + +var env = process.env.NODE_ENV || 'development'; + +/** + * Initialize a new `Server` with the given `middleware`. + * + * Examples: + * + * var server = connect.createServer( + * connect.favicon() + * , connect.logger() + * , connect.static(__dirname + '/public') + * ); + * + * @params {Array} middleware + * @return {Server} + * @api public + */ + +var Server = exports.Server = function HTTPServer(middleware) { + this.stack = []; + middleware.forEach(function(fn){ + this.use(fn); + }, this); + http.Server.call(this, this.handle); +}; + +/** + * Inherit from `http.Server.prototype`. + */ + +Server.prototype.__proto__ = http.Server.prototype; + +/** + * Utilize the given middleware `handle` to the given `route`, + * defaulting to _/_. This "route" is the mount-point for the + * middleware, when given a value other than _/_ the middleware + * is only effective when that segment is present in the request's + * pathname. + * + * For example if we were to mount a function at _/admin_, it would + * be invoked on _/admin_, and _/admin/settings_, however it would + * not be invoked for _/_, or _/posts_. + * + * This is effectively the same as passing middleware to `connect.createServer()`, + * however provides a progressive api. + * + * Examples: + * + * var server = connect.createServer(); + * server.use(connect.favicon()); + * server.use(connect.logger()); + * server.use(connect.static(__dirname + '/public')); + * + * If we wanted to prefix static files with _/public_, we could + * "mount" the `static()` middleware: + * + * server.use('/public', connect.static(__dirname + '/public')); + * + * This api is chainable, meaning the following is valid: + * + * connect.createServer() + * .use(connect.favicon()) + * .use(connect.logger()) + * .use(connect.static(__dirname + '/public')) + * .listen(3000); + * + * @param {String|Function} route or handle + * @param {Function} handle + * @return {Server} + * @api public + */ + +Server.prototype.use = function(route, handle){ + this.route = '/'; + + // default route to '/' + if ('string' != typeof route) { + handle = route; + route = '/'; + } + + // wrap sub-apps + if ('function' == typeof handle.handle) { + var server = handle; + server.route = route; + handle = function(req, res, next) { + server.handle(req, res, next); + }; + } + + // wrap vanilla http.Servers + if (handle instanceof http.Server) { + handle = handle.listeners('request')[0]; + } + + // normalize route to not trail with slash + if ('/' == route[route.length - 1]) { + route = route.substr(0, route.length - 1); + } + + // add the middleware + this.stack.push({ route: route, handle: handle }); + + // allow chaining + return this; +}; + +/** + * Handle server requests, punting them down + * the middleware stack. + * + * @api private + */ + +Server.prototype.handle = function(req, res, out) { + var writeHead = res.writeHead + , stack = this.stack + , removed = '' + , index = 0; + + function next(err) { + var layer, path, c; + req.url = removed + req.url; + req.originalUrl = req.originalUrl || req.url; + removed = ''; + + layer = stack[index++]; + + // all done + if (!layer || res.headerSent) { + // but wait! we have a parent + if (out) return out(err); + + // error + if (err) { + var msg = 'production' == env + ? 'Internal Server Error' + : err.stack || err.toString(); + + // output to stderr in a non-test env + if ('test' != env) console.error(err.stack || err.toString()); + + // unable to respond + if (res.headerSent) return req.socket.destroy(); + + res.statusCode = 500; + res.setHeader('Content-Type', 'text/plain'); + if ('HEAD' == req.method) return res.end(); + res.end(msg); + } else { + res.statusCode = 404; + res.setHeader('Content-Type', 'text/plain'); + if ('HEAD' == req.method) return res.end(); + res.end('Cannot ' + req.method + ' ' + utils.escape(req.originalUrl)); + } + return; + } + + try { + path = parse(req.url).pathname; + if (undefined == path) path = '/'; + + // skip this layer if the route doesn't match. + if (0 != path.indexOf(layer.route)) return next(err); + + c = path[layer.route.length]; + if (c && '/' != c && '.' != c) return next(err); + + // Call the layer handler + // Trim off the part of the url that matches the route + removed = layer.route; + req.url = req.url.substr(removed.length); + + // Ensure leading slash + if ('/' != req.url[0]) req.url = '/' + req.url; + + var arity = layer.handle.length; + if (err) { + if (arity === 4) { + layer.handle(err, req, res, next); + } else { + next(err); + } + } else if (arity < 4) { + layer.handle(req, res, next); + } else { + next(); + } + } catch (e) { + if (e instanceof assert.AssertionError) { + console.error(e.stack + '\n'); + next(e); + } else { + next(e); + } + } + } + next(); +}; \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/https.js b/test/node_modules/express/node_modules/connect/lib/https.js new file mode 100644 index 0000000..9b09fa4 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/https.js @@ -0,0 +1,47 @@ + +/*! + * Connect - HTTPServer + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var HTTPServer = require('./http').Server + , https = require('https'); + +/** + * Initialize a new `Server` with the given + *`options` and `middleware`. The HTTPS api + * is identical to the [HTTP](http.html) server, + * however TLS `options` must be provided before + * passing in the optional middleware. + * + * @params {Object} options + * @params {Array} middleawre + * @return {Server} + * @api public + */ + +var Server = exports.Server = function HTTPSServer(options, middleware) { + this.stack = []; + middleware.forEach(function(fn){ + this.use(fn); + }, this); + https.Server.call(this, options, this.handle); +}; + +/** + * Inherit from `http.Server.prototype`. + */ + +Server.prototype.__proto__ = https.Server.prototype; + +// mixin HTTPServer methods + +Object.keys(HTTPServer.prototype).forEach(function(method){ + Server.prototype[method] = HTTPServer.prototype[method]; +}); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/index.js b/test/node_modules/express/node_modules/connect/lib/index.js new file mode 100644 index 0000000..77b14c3 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/index.js @@ -0,0 +1,46 @@ + +/** + * # Connect + * + * Connect is a middleware framework for node, + * shipping with over 11 bundled middleware and a rich choice of + * [3rd-party middleware](https://github.com/senchalabs/connect/wiki). + * + * Installation: + * + * $ npm install connect + * + * API: + * + * - [connect](connect.html) general + * - [http](http.html) http server + * - [https](https.html) https server + * + * Middleware: + * + * - [logger](middleware-logger.html) request logger with custom format support + * - [csrf](middleware-csrf.html) Cross-site request forgery protection + * - [basicAuth](middleware-basicAuth.html) basic http authentication + * - [bodyParser](middleware-bodyParser.html) extensible request body parser + * - [cookieParser](middleware-cookieParser.html) cookie parser + * - [session](middleware-session.html) session management support with bundled [MemoryStore](middleware-session-memory.html) + * - [compiler](middleware-compiler.html) static asset compiler (sass, less, coffee-script, etc) + * - [methodOverride](middleware-methodOverride.html) faux HTTP method support + * - [responseTime](middleware-responseTime.html) calculates response-time and exposes via X-Response-Time + * - [router](middleware-router.html) provides rich Sinatra / Express-like routing + * - [staticCache](middleware-staticCache.html) memory cache layer for the static() middleware + * - [static](middleware-static.html) streaming static file server supporting `Range` and more + * - [directory](middleware-directory.html) directory listing middleware + * - [vhost](middleware-vhost.html) virtual host sub-domain mapping middleware + * - [favicon](middleware-favicon.html) efficient favicon server (with default icon) + * - [limit](middleware-limit.html) limit the bytesize of request bodies + * - [profiler](middleware-profiler.html) request profiler reporting response-time, memory usage, etc + * - [query](middleware-query.html) automatic querystring parser, populating `req.query` + * - [errorHandler](middleware-errorHandler.html) flexible error handler + * + * Internals: + * + * - connect [utilities](utils.html) + * - node monkey [patches](patch.html) + * + */ \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js b/test/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js new file mode 100644 index 0000000..3ff472b --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js @@ -0,0 +1,93 @@ + +/*! + * Connect - basicAuth + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , unauthorized = utils.unauthorized + , badRequest = utils.badRequest; + +/** + * Enfore basic authentication by providing a `callback(user, pass)`, + * which must return `true` in order to gain access. Alternatively an async + * method is provided as well, invoking `callback(user, pass, callback)`. Populates + * `req.remoteUser`. The final alternative is simply passing username / password + * strings. + * + * Examples: + * + * connect(connect.basicAuth('username', 'password')); + * + * connect( + * connect.basicAuth(function(user, pass){ + * return 'tj' == user & 'wahoo' == pass; + * }) + * ); + * + * connect( + * connect.basicAuth(function(user, pass, fn){ + * User.authenticate({ user: user, pass: pass }, fn); + * }) + * ); + * + * @param {Function|String} callback or username + * @param {String} realm + * @api public + */ + +module.exports = function basicAuth(callback, realm) { + var username, password; + + // user / pass strings + if ('string' == typeof callback) { + username = callback; + password = realm; + if ('string' != typeof password) throw new Error('password argument required'); + realm = arguments[2]; + callback = function(user, pass){ + return user == username && pass == password; + } + } + + realm = realm || 'Authorization Required'; + + return function(req, res, next) { + var authorization = req.headers.authorization; + + if (req.remoteUser) return next(); + if (!authorization) return unauthorized(res, realm); + + var parts = authorization.split(' ') + , scheme = parts[0] + , credentials = new Buffer(parts[1], 'base64').toString().split(':'); + + if ('Basic' != scheme) return badRequest(res); + + // async + if (callback.length >= 3) { + var pause = utils.pause(req); + callback(credentials[0], credentials[1], function(err, user){ + if (err || !user) return unauthorized(res, realm); + req.remoteUser = user; + next(); + pause.resume(); + }); + // sync + } else { + if (callback(credentials[0], credentials[1])) { + req.remoteUser = credentials[0]; + next(); + } else { + unauthorized(res, realm); + } + } + } +}; + diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js b/test/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js new file mode 100644 index 0000000..a52568c --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js @@ -0,0 +1,196 @@ + +/*! + * Connect - bodyParser + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var qs = require('qs') + , formidable = require('formidable'); + +/** + * Extract the mime type from the given request's + * _Content-Type_ header. + * + * @param {IncomingMessage} req + * @return {String} + * @api private + */ + +function mime(req) { + var str = req.headers['content-type'] || ''; + return str.split(';')[0]; +} + +/** + * Parse request bodies. + * + * By default _application/json_, _application/x-www-form-urlencoded_, + * and _multipart/form-data_ are supported, however you may map `connect.bodyParser.parse[contentType]` + * to a function receiving `(req, options, callback)`. + * + * Examples: + * + * connect.createServer( + * connect.bodyParser() + * , function(req, res) { + * res.end('viewing user ' + req.body.user.name); + * } + * ); + * + * $ curl -d 'user[name]=tj' http://localhost/ + * $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://localhost/ + * + * Multipart req.files: + * + * As a security measure files are stored in a separate object, stored + * as `req.files`. This prevents attacks that may potentially alter + * filenames, and depending on the application gain access to restricted files. + * + * Multipart configuration: + * + * The `options` passed are provided to each parser function. + * The _multipart/form-data_ parser merges these with formidable's + * IncomingForm object, allowing you to tweak the upload directory, + * size limits, etc. For example you may wish to retain the file extension + * and change the upload directory: + * + * server.use(bodyParser({ uploadDir: '/www/mysite.com/uploads' })); + * + * View [node-formidable](https://github.com/felixge/node-formidable) for more information. + * + * If you wish to use formidable directly within your app, and do not + * desire this behaviour for multipart requests simply remove the + * parser: + * + * delete connect.bodyParser.parse['multipart/form-data']; + * + * Or + * + * delete express.bodyParser.parse['multipart/form-data']; + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function bodyParser(options){ + options = options || {}; + return function bodyParser(req, res, next) { + if (req.body) return next(); + req.body = {}; + + if ('GET' == req.method || 'HEAD' == req.method) return next(); + var parser = exports.parse[mime(req)]; + if (parser) { + parser(req, options, next); + } else { + next(); + } + } +}; + +/** + * Parsers. + */ + +exports.parse = {}; + +/** + * Parse application/x-www-form-urlencoded. + */ + +exports.parse['application/x-www-form-urlencoded'] = function(req, options, fn){ + var buf = ''; + req.setEncoding('utf8'); + req.on('data', function(chunk){ buf += chunk }); + req.on('end', function(){ + try { + req.body = buf.length + ? qs.parse(buf) + : {}; + fn(); + } catch (err){ + fn(err); + } + }); +}; + +/** + * Parse application/json. + */ + +exports.parse['application/json'] = function(req, options, fn){ + var buf = ''; + req.setEncoding('utf8'); + req.on('data', function(chunk){ buf += chunk }); + req.on('end', function(){ + try { + req.body = buf.length + ? JSON.parse(buf) + : {}; + fn(); + } catch (err){ + fn(err); + } + }); +}; + +/** + * Parse multipart/form-data. + * + * TODO: make multiple support optional + * TODO: revisit "error" flag if it's a formidable bug + */ + +exports.parse['multipart/form-data'] = function(req, options, fn){ + var form = new formidable.IncomingForm + , data = {} + , files = {} + , done; + + Object.keys(options).forEach(function(key){ + form[key] = options[key]; + }); + + function ondata(name, val, data){ + if (Array.isArray(data[name])) { + data[name].push(val); + } else if (data[name]) { + data[name] = [data[name], val]; + } else { + data[name] = val; + } + } + + form.on('field', function(name, val){ + ondata(name, val, data); + }); + + form.on('file', function(name, val){ + ondata(name, val, files); + }); + + form.on('error', function(err){ + fn(err); + done = true; + }); + + form.on('end', function(){ + if (done) return; + try { + req.body = qs.parse(data); + req.files = qs.parse(files); + fn(); + } catch (err) { + fn(err); + } + }); + + form.parse(req); +}; \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/compiler.js b/test/node_modules/express/node_modules/connect/lib/middleware/compiler.js new file mode 100644 index 0000000..dc4dd66 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/compiler.js @@ -0,0 +1,163 @@ + +/*! + * Connect - compiler + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , path = require('path') + , parse = require('url').parse; + +/** + * Require cache. + */ + +var cache = {}; + +/** + * Setup compiler. + * + * Options: + * + * - `src` Source directory, defaults to **CWD**. + * - `dest` Destination directory, defaults `src`. + * - `enable` Array of enabled compilers. + * + * Compilers: + * + * - `sass` Compiles sass to css + * - `less` Compiles less to css + * - `coffeescript` Compiles coffee to js + * + * @param {Object} options + * @api public + */ + +exports = module.exports = function compiler(options){ + options = options || {}; + + var srcDir = options.src || process.cwd() + , destDir = options.dest || srcDir + , enable = options.enable; + + if (!enable || enable.length === 0) { + throw new Error('compiler\'s "enable" option is not set, nothing will be compiled.'); + } + + return function compiler(req, res, next){ + if ('GET' != req.method) return next(); + var pathname = parse(req.url).pathname; + for (var i = 0, len = enable.length; i < len; ++i) { + var name = enable[i] + , compiler = compilers[name]; + if (compiler.match.test(pathname)) { + var src = (srcDir + pathname).replace(compiler.match, compiler.ext) + , dest = destDir + pathname; + + // Compare mtimes + fs.stat(src, function(err, srcStats){ + if (err) { + if ('ENOENT' == err.code) { + next(); + } else { + next(err); + } + } else { + fs.stat(dest, function(err, destStats){ + if (err) { + // Oh snap! it does not exist, compile it + if ('ENOENT' == err.code) { + compile(); + } else { + next(err); + } + } else { + // Source has changed, compile it + if (srcStats.mtime > destStats.mtime) { + compile(); + } else { + // Defer file serving + next(); + } + } + }); + } + }); + + // Compile to the destination + function compile() { + fs.readFile(src, 'utf8', function(err, str){ + if (err) { + next(err); + } else { + compiler.compile(str, function(err, str){ + if (err) { + next(err); + } else { + fs.writeFile(dest, str, 'utf8', function(err){ + next(err); + }); + } + }); + } + }); + } + return; + } + } + next(); + }; +}; + +/** + * Bundled compilers: + * + * - [sass](http://github.com/visionmedia/sass.js) to _css_ + * - [less](http://github.com/cloudhead/less.js) to _css_ + * - [coffee](http://github.com/jashkenas/coffee-script) to _js_ + */ + +var compilers = exports.compilers = { + sass: { + match: /\.css$/, + ext: '.sass', + compile: function(str, fn){ + var sass = cache.sass || (cache.sass = require('sass')); + try { + fn(null, sass.render(str)); + } catch (err) { + fn(err); + } + } + }, + less: { + match: /\.css$/, + ext: '.less', + compile: function(str, fn){ + var less = cache.less || (cache.less = require('less')); + try { + less.render(str, fn); + } catch (err) { + fn(err); + } + } + }, + coffeescript: { + match: /\.js$/, + ext: '.coffee', + compile: function(str, fn){ + var coffee = cache.coffee || (cache.coffee = require('coffee-script')); + try { + fn(null, coffee.compile(str)); + } catch (err) { + fn(err); + } + } + } +}; \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/compress.js b/test/node_modules/express/node_modules/connect/lib/middleware/compress.js new file mode 100644 index 0000000..addc5c9 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/compress.js @@ -0,0 +1,146 @@ + +/*! + * Connect - compress + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * Copyright(c) 2012 Nebojsa Sabovic + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var zlib = require('zlib'); + +/** + * Supported content-encoding methods. + */ + +exports.methods = { + gzip: zlib.createGzip + , deflate: zlib.createDeflate +}; + +/** + * Default filter function. + */ + +exports.filter = function(req, res){ + var type = res.getHeader('Content-Type') || ''; + return type.match(/json|text|javascript/); +}; + +/** + * Compress: + * + * Compress response data with gzip/deflate. + * + * Filter: + * + * A `filter` callback function may be passed to + * replace the default logic of: + * + * exports.filter = function(req, res){ + * var type = res.getHeader('Content-Type') || ''; + * return type.match(/json|text|javascript/); + * }; + * + * Options: + * + * All remaining options are passed to the gzip/deflate + * creation functions. Consult node's docs for additional details. + * + * - `chunkSize` (default: 16*1024) + * - `windowBits` + * - `level`: 0-9 where 0 is no compression, and 9 is slow but best compression + * - `memLevel`: 1-9 low is slower but uses less memory, high is fast but uses more + * - `strategy`: compression strategy + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function compress(options) { + var options = options || {} + , names = Object.keys(exports.methods) + , filter = options.filter || exports.filter; + + return function(req, res, next){ + var accept = req.headers['accept-encoding'] + , write = res.write + , writeHead = res.writeHead + , end = res.end + , stream + , method; + + // vary + res.setHeader('Vary', 'Accept-Encoding'); + + // proxy + + res.write = function(chunk, encoding){ + if (!this._header) this._implicitHeader(); + return stream + ? stream.write(chunk, encoding) + : write.call(this, chunk, encoding); + }; + + res.end = function(chunk, encoding){ + if (!this._header) { + this._implicitHeader(); + } + if (chunk) this.write(chunk, encoding); + return stream + ? stream.end() + : end.call(this); + }; + + res.writeHead = function(){ + res.writeHead = writeHead; + + // default request filter, SHOULD use identity, head + if (filter(req, res) && accept && 'HEAD' != req.method) { + // default to gzip + if ('*' == accept.trim()) { + method = 'gzip'; + } else { + for (var i = 0, len = names.length; i < len; ++i) { + if (~accept.indexOf(names[i])) { + method = names[i]; + break; + } + } + } + + // compression method + if (method) { + // compression stream + stream = exports.methods[method](options); + + // header fields + res.setHeader('Content-Encoding', method); + res.removeHeader('Content-Length'); + // WARNING: We do not strip content-length out of passed-in headers + + // compression + stream.on('data', function(chunk){ + write.call(res, chunk); + }); + + stream.on('end', function(){ + end.call(res); + }); + + stream.on('drain', function() { + res.emit('drain'); + }); + } + } + return res.writeHead.apply(this, arguments); + }; + + next(); + }; +} diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js b/test/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js new file mode 100644 index 0000000..d6b69de --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js @@ -0,0 +1,46 @@ + +/*! + * Connect - cookieParser + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('./../utils'); + +/** + * Parse _Cookie_ header and populate `req.cookies` + * with an object keyed by the cookie names. + * + * Examples: + * + * connect.createServer( + * connect.cookieParser() + * , function(req, res, next){ + * res.end(JSON.stringify(req.cookies)); + * } + * ); + * + * @return {Function} + * @api public + */ + +module.exports = function cookieParser(){ + return function cookieParser(req, res, next) { + var cookie = req.headers.cookie; + if (req.cookies) return next(); + req.cookies = {}; + if (cookie) { + try { + req.cookies = utils.parseCookie(cookie); + } catch (err) { + return next(err); + } + } + next(); + }; +}; \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/csrf.js b/test/node_modules/express/node_modules/connect/lib/middleware/csrf.js new file mode 100644 index 0000000..53347e7 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/csrf.js @@ -0,0 +1,105 @@ + +/*! + * Connect - csrf + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , crypto = require('crypto'); + +/** + * CRSF protection middleware. + * + * By default this middleware generates a token named "_csrf" + * which should be added to requests which mutate + * state, within a hidden form field, query-string etc. This + * token is validated against the visitor's `req.session._csrf` + * property which is re-generated per request. + * + * The default `value` function checks `req.body` generated + * by the `bodyParser()` middleware, `req.query` generated + * by `query()`, and the "X-CSRF-Token" header field. + * + * This middleware requires session support, thus should be added + * somewhere _below_ `session()` and `cookieParser()`. + * + * Examples: + * + * var form = '\n\ + *
\n\ + * \n\ + * \n\ + * \n\ + * \n\ + *
\n\ + * '; + * + * connect( + * connect.cookieParser() + * , connect.session({ secret: 'keyboard cat' }) + * , connect.bodyParser() + * , connect.csrf() + * + * , function(req, res, next){ + * if ('POST' != req.method) return next(); + * req.session.user = req.body.user; + * next(); + * } + * + * , function(req, res){ + * res.setHeader('Content-Type', 'text/html'); + * var body = form + * .replace('{token}', req.session._csrf) + * .replace('{user}', req.session.user && req.session.user.name || ''); + * res.end(body); + * } + * ).listen(3000); + * + * Options: + * + * - `value` a function accepting the request, returning the token + * + * @param {Object} options + * @api public + */ + +module.exports = function csrf(options) { + var options = options || {} + , value = options.value || defaultValue; + + return function(req, res, next){ + // generate CSRF token + var token = req.session._csrf || (req.session._csrf = utils.uid(24)); + + // ignore GET (for now) + if ('GET' == req.method || 'HEAD' == req.method || 'OPTIONS' == req.method) return next(); + + // determine value + var val = value(req); + + // check + if (val != token) return utils.forbidden(res); + + next(); + } +}; + +/** + * Default value function, checking the `req.body` + * and `req.query` for the CSRF token. + * + * @param {IncomingMessage} req + * @return {String} + * @api private + */ + +function defaultValue(req) { + return (req.body && req.body._csrf) + || (req.query && req.query._csrf) + || (req.headers['x-csrf-token']); +} \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/directory.js b/test/node_modules/express/node_modules/connect/lib/middleware/directory.js new file mode 100644 index 0000000..df5b5e6 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/directory.js @@ -0,0 +1,222 @@ + +/*! + * Connect - directory + * Copyright(c) 2011 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +// TODO: icon / style for directories +// TODO: arrow key navigation +// TODO: make icons extensible + +/** + * Module dependencies. + */ + +var fs = require('fs') + , parse = require('url').parse + , utils = require('../utils') + , path = require('path') + , normalize = path.normalize + , extname = path.extname + , join = path.join; + +/** + * Icon cache. + */ + +var cache = {}; + +/** + * Serve directory listings with the given `root` path. + * + * Options: + * + * - `hidden` display hidden (dot) files. Defaults to false. + * - `icons` display icons. Defaults to false. + * - `filter` Apply this filter function to files. Defaults to false. + * + * @param {String} root + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function directory(root, options){ + options = options || {}; + + // root required + if (!root) throw new Error('directory() root path required'); + var hidden = options.hidden + , icons = options.icons + , filter = options.filter + , root = normalize(root); + + return function directory(req, res, next) { + var accept = req.headers.accept || 'text/plain' + , url = parse(req.url) + , dir = decodeURIComponent(url.pathname) + , path = normalize(join(root, dir)) + , originalUrl = parse(req.originalUrl) + , originalDir = decodeURIComponent(originalUrl.pathname) + , showUp = path != root && path != root + '/'; + + // null byte(s) + if (~path.indexOf('\0')) return utils.badRequest(res); + + // malicious path + if (0 != path.indexOf(root)) return utils.forbidden(res); + + // check if we have a directory + fs.stat(path, function(err, stat){ + if (err) return 'ENOENT' == err.code + ? next() + : next(err); + + if (!stat.isDirectory()) return next(); + + // fetch files + fs.readdir(path, function(err, files){ + if (err) return next(err); + if (!hidden) files = removeHidden(files); + if (filter) files = files.filter(filter); + files.sort(); + // content-negotiation + for (var key in exports) { + if (~accept.indexOf(key) || ~accept.indexOf('*/*')) { + exports[key](req, res, files, next, originalDir, showUp, icons); + return; + } + } + utils.notAcceptable(res); + }); + }); + }; +}; + +/** + * Respond with text/html. + */ + +exports.html = function(req, res, files, next, dir, showUp, icons){ + fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){ + if (err) return next(err); + fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){ + if (err) return next(err); + if (showUp) files.unshift('..'); + str = str + .replace('{style}', style) + .replace('{files}', html(files, dir, icons)) + .replace('{directory}', dir) + .replace('{linked-path}', htmlPath(dir)); + res.setHeader('Content-Type', 'text/html'); + res.setHeader('Content-Length', str.length); + res.end(str); + }); + }); +}; + +/** + * Respond with application/json. + */ + +exports.json = function(req, res, files){ + files = JSON.stringify(files); + res.setHeader('Content-Type', 'application/json'); + res.setHeader('Content-Length', files.length); + res.end(files); +}; + +/** + * Respond with text/plain. + */ + +exports.plain = function(req, res, files){ + files = files.join('\n') + '\n'; + res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Length', files.length); + res.end(files); +}; + +/** + * Map html `dir`, returning a linked path. + */ + +function htmlPath(dir) { + var curr = []; + return dir.split('/').map(function(part){ + curr.push(part); + return '' + part + ''; + }).join(' / '); +} + +/** + * Map html `files`, returning an html unordered list. + */ + +function html(files, dir, useIcons) { + return '
    ' + files.map(function(file){ + var icon = '' + , classes = []; + + if (useIcons && '..' != file) { + icon = icons[extname(file)] || icons.default; + icon = ''; + classes.push('icon'); + } + + return '
  • ' + + icon + file + '
  • '; + + }).join('\n') + '
'; +} + +/** + * Load and cache the given `icon`. + * + * @param {String} icon + * @return {String} + * @api private + */ + +function load(icon) { + if (cache[icon]) return cache[icon]; + return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64'); +} + +/** + * Filter "hidden" `files`, aka files + * beginning with a `.`. + * + * @param {Array} files + * @return {Array} + * @api private + */ + +function removeHidden(files) { + return files.filter(function(file){ + return '.' != file[0]; + }); +} + +/** + * Icon map. + */ + +var icons = { + '.js': 'page_white_code_red.png' + , '.c': 'page_white_c.png' + , '.h': 'page_white_h.png' + , '.cc': 'page_white_cplusplus.png' + , '.php': 'page_white_php.png' + , '.rb': 'page_white_ruby.png' + , '.cpp': 'page_white_cplusplus.png' + , '.swf': 'page_white_flash.png' + , '.pdf': 'page_white_acrobat.png' + , 'default': 'page_white.png' +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js b/test/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js new file mode 100644 index 0000000..f2fc44f --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js @@ -0,0 +1,100 @@ +/*! + * Connect - errorHandler + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , url = require('url') + , fs = require('fs'); + +/** + * Flexible error handler, providing (_optional_) stack traces + * and error message responses for requests accepting text, html, + * or json. + * + * Options: + * + * - `showStack`, `stack` respond with both the error message and stack trace. Defaults to `false` + * - `showMessage`, `message`, respond with the exception message only. Defaults to `false` + * - `dumpExceptions`, `dump`, dump exceptions to stderr (without terminating the process). Defaults to `false` + * + * Text: + * + * By default, and when _text/plain_ is accepted a simple stack trace + * or error message will be returned. + * + * JSON: + * + * When _application/json_ is accepted, connect will respond with + * an object in the form of `{ "error": error }`. + * + * HTML: + * + * When accepted connect will output a nice html stack trace. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function errorHandler(options){ + options = options || {}; + + // defaults + var showStack = options.showStack || options.stack + , showMessage = options.showMessage || options.message + , dumpExceptions = options.dumpExceptions || options.dump + , formatUrl = options.formatUrl; + + return function errorHandler(err, req, res, next){ + res.statusCode = 500; + if (dumpExceptions) console.error(err.stack); + if (showStack) { + var accept = req.headers.accept || ''; + // html + if (~accept.indexOf('html')) { + fs.readFile(__dirname + '/../public/style.css', 'utf8', function(e, style){ + fs.readFile(__dirname + '/../public/error.html', 'utf8', function(e, html){ + var stack = (err.stack || '') + .split('\n').slice(1) + .map(function(v){ return '
  • ' + v + '
  • '; }).join(''); + html = html + .replace('{style}', style) + .replace('{stack}', stack) + .replace('{title}', exports.title) + .replace(/\{error\}/g, utils.escape(err.toString())); + res.setHeader('Content-Type', 'text/html'); + res.end(html); + }); + }); + // json + } else if (~accept.indexOf('json')) { + var json = JSON.stringify({ error: err }); + res.setHeader('Content-Type', 'application/json'); + res.end(json); + // plain text + } else { + res.writeHead(500, { 'Content-Type': 'text/plain' }); + res.end(err.stack); + } + } else { + var body = showMessage + ? err.toString() + : 'Internal Server Error'; + res.setHeader('Content-Type', 'text/plain'); + res.end(body); + } + }; +}; + +/** + * Template title. + */ + +exports.title = 'Connect'; \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/favicon.js b/test/node_modules/express/node_modules/connect/lib/middleware/favicon.js new file mode 100644 index 0000000..8eeafba --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/favicon.js @@ -0,0 +1,76 @@ + +/*! + * Connect - favicon + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , utils = require('../utils'); + +/** + * Favicon cache. + */ + +var icon; + +/** + * By default serves the connect favicon, or the favicon + * located by the given `path`. + * + * Options: + * + * - `maxAge` cache-control max-age directive, defaulting to 1 day + * + * Examples: + * + * connect.createServer( + * connect.favicon() + * ); + * + * connect.createServer( + * connect.favicon(__dirname + '/public/favicon.ico') + * ); + * + * @param {String} path + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function favicon(path, options){ + var options = options || {} + , path = path || __dirname + '/../public/favicon.ico' + , maxAge = options.maxAge || 86400000; + + return function favicon(req, res, next){ + if ('/favicon.ico' == req.url) { + if (icon) { + res.writeHead(200, icon.headers); + res.end(icon.body); + } else { + fs.readFile(path, function(err, buf){ + if (err) return next(err); + icon = { + headers: { + 'Content-Type': 'image/x-icon' + , 'Content-Length': buf.length + , 'ETag': '"' + utils.md5(buf) + '"' + , 'Cache-Control': 'public, max-age=' + (maxAge / 1000) + }, + body: buf + }; + res.writeHead(200, icon.headers); + res.end(icon.body); + }); + } + } else { + next(); + } + }; +}; \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/limit.js b/test/node_modules/express/node_modules/connect/lib/middleware/limit.js new file mode 100644 index 0000000..a604ffb --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/limit.js @@ -0,0 +1,80 @@ + +/*! + * Connect - limit + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Limit request bodies to the given size in `bytes`. + * + * A string representation of the bytesize may also be passed, + * for example "5mb", "200kb", "1gb", etc. + * + * Examples: + * + * var server = connect( + * connect.limit('5.5mb') + * ).listen(3000); + * + * @param {Number|String} bytes + * @return {Function} + * @api public + */ + +module.exports = function limit(bytes){ + if ('string' == typeof bytes) bytes = parse(bytes); + if ('number' != typeof bytes) throw new Error('limit() bytes required'); + return function limit(req, res, next){ + var received = 0 + , len = req.headers['content-length'] + ? parseInt(req.headers['content-length'], 10) + : null; + + // deny the request + function deny() { + req.destroy(); + } + + // self-awareness + if (req._limit) return next(); + req._limit = true; + + // limit by content-length + if (len && len > bytes) { + res.statusCode = 413; + res.end('Request Entity Too Large'); + return; + } + + // limit + req.on('data', function(chunk){ + received += chunk.length; + if (received > bytes) deny(); + }); + + next(); + }; +}; + +/** + * Parse byte `size` string. + * + * @param {String} size + * @return {Number} + * @api private + */ + +function parse(size) { + var parts = size.match(/^(\d+(?:\.\d+)?) *(kb|mb|gb)$/) + , n = parseFloat(parts[1]) + , type = parts[2]; + + var map = { + kb: 1024 + , mb: 1024 * 1024 + , gb: 1024 * 1024 * 1024 + }; + + return map[type] * n; +} \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/logger.js b/test/node_modules/express/node_modules/connect/lib/middleware/logger.js new file mode 100644 index 0000000..75cc5aa --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/logger.js @@ -0,0 +1,299 @@ + +/*! + * Connect - logger + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Log buffer. + */ + +var buf = []; + +/** + * Default log buffer duration. + */ + +var defaultBufferDuration = 1000; + +/** + * Log requests with the given `options` or a `format` string. + * + * Options: + * + * - `format` Format string, see below for tokens + * - `stream` Output stream, defaults to _stdout_ + * - `buffer` Buffer duration, defaults to 1000ms when _true_ + * - `immediate` Write log line on request instead of response (for response times) + * + * Tokens: + * + * - `:req[header]` ex: `:req[Accept]` + * - `:res[header]` ex: `:res[Content-Length]` + * - `:http-version` + * - `:response-time` + * - `:remote-addr` + * - `:date` + * - `:method` + * - `:url` + * - `:referrer` + * - `:user-agent` + * - `:status` + * + * Formats: + * + * Pre-defined formats that ship with connect: + * + * - `default` ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"' + * - `short` ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms' + * - `tiny` ':method :url :status :res[content-length] - :response-time ms' + * - `dev` concise output colored by response status for development use + * + * Examples: + * + * connect.logger() // default + * connect.logger('short') + * connect.logger('tiny') + * connect.logger('dev') + * connect.logger(':method :url - :referrer') + * connect.logger(':req[content-type] -> :res[content-type]') + * connect.logger(function(req, res){ return 'some format string' }) + * + * Defining Tokens: + * + * To define a token, simply invoke `connect.logger.token()` with the + * name and a callback function. The value returned is then available + * as ":type" in this case. + * + * connect.logger.token('type', function(req, res){ return req.headers['content-type']; }) + * + * Defining Formats: + * + * All default formats are defined this way, however it's public API as well: + * + * connect.logger.format('name', 'string or function') + * + * @param {String|Function|Object} format or options + * @return {Function} + * @api public + */ + +exports = module.exports = function logger(options) { + if ('object' == typeof options) { + options = options || {}; + } else if (options) { + options = { format: options }; + } else { + options = {}; + } + + // output on request instead of response + var immediate = options.immediate; + + // format name + var fmt = exports[options.format] || options.format || exports.default; + + // compile format + if ('function' != typeof fmt) fmt = compile(fmt); + + // options + var stream = options.stream || process.stdout + , buffer = options.buffer; + + // buffering support + if (buffer) { + var realStream = stream + , interval = 'number' == typeof buffer + ? buffer + : defaultBufferDuration; + + // flush interval + setInterval(function(){ + if (buf.length) { + realStream.write(buf.join(''), 'ascii'); + buf.length = 0; + } + }, interval); + + // swap the stream + stream = { + write: function(str){ + buf.push(str); + } + }; + } + + return function logger(req, res, next) { + req._startTime = new Date; + + // mount safety + if (req._logging) return next(); + + // flag as logging + req._logging = true; + + // immediate + if (immediate) { + var line = fmt(exports, req, res); + if (null == line) return; + stream.write(line + '\n', 'ascii'); + } else { + // proxy end to output loggging + var end = res.end; + res.end = function(chunk, encoding){ + res.end = end; + res.end(chunk, encoding); + var line = fmt(exports, req, res); + if (null == line) return; + stream.write(line + '\n', 'ascii'); + }; + } + + + next(); + }; +}; + +/** + * Compile `fmt` into a function. + * + * @param {String} fmt + * @return {Function} + * @api private + */ + +function compile(fmt) { + fmt = fmt.replace(/"/g, '\\"'); + var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){ + return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "'; + }) + '";' + return new Function('tokens, req, res', js); +}; + +/** + * Define a token function with the given `name`, + * and callback `fn(req, res)`. + * + * @param {String} name + * @param {Function} fn + * @return {Object} exports for chaining + * @api public + */ + +exports.token = function(name, fn) { + exports[name] = fn; + return this; +}; + +/** + * Define a `fmt` with the given `name`. + * + * @param {String} name + * @param {String|Function} fmt + * @return {Object} exports for chaining + * @api public + */ + +exports.format = function(name, str){ + exports[name] = str; + return this; +}; + +// default format + +exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'); + +// short format + +exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'); + +// tiny format + +exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms'); + +// dev (colored) + +exports.format('dev', function(tokens, req, res){ + var status = res.statusCode + , color = 32; + + if (status >= 500) color = 31 + else if (status >= 400) color = 33 + else if (status >= 300) color = 36; + + return '\033[90m' + req.method + + ' ' + req.originalUrl + ' ' + + '\033[' + color + 'm' + res.statusCode + + ' \033[90m' + + (new Date - req._startTime) + + 'ms\033[0m'; +}); + +// request url + +exports.token('url', function(req){ + return req.originalUrl; +}); + +// request method + +exports.token('method', function(req){ + return req.method; +}); + +// response time in milliseconds + +exports.token('response-time', function(req){ + return new Date - req._startTime; +}); + +// UTC date + +exports.token('date', function(){ + return new Date().toUTCString(); +}); + +// response status code + +exports.token('status', function(req, res){ + return res.statusCode; +}); + +// normalized referrer + +exports.token('referrer', function(req){ + return req.headers['referer'] || req.headers['referrer']; +}); + +// remote address + +exports.token('remote-addr', function(req){ + return req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)); +}); + +// HTTP version + +exports.token('http-version', function(req){ + return req.httpVersionMajor + '.' + req.httpVersionMinor; +}); + +// UA string + +exports.token('user-agent', function(req){ + return req.headers['user-agent']; +}); + +// request header + +exports.token('req', function(req, res, field){ + return req.headers[field.toLowerCase()]; +}); + +// response header + +exports.token('res', function(req, res, field){ + return (res._headers || {})[field.toLowerCase()]; +}); + diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js b/test/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js new file mode 100644 index 0000000..db4e9f3 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js @@ -0,0 +1,38 @@ + +/*! + * Connect - methodOverride + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Provides faux HTTP method support. + * + * Pass an optional `key` to use when checking for + * a method override, othewise defaults to _\_method_. + * The original method is available via `req.originalMethod`. + * + * @param {String} key + * @return {Function} + * @api public + */ + +module.exports = function methodOverride(key){ + key = key || "_method"; + return function methodOverride(req, res, next) { + req.originalMethod = req.originalMethod || req.method; + + // req.body + if (req.body && key in req.body) { + req.method = req.body[key].toUpperCase(); + delete req.body[key]; + // check X-HTTP-Method-Override + } else if (req.headers['x-http-method-override']) { + req.method = req.headers['x-http-method-override'].toUpperCase(); + } + + next(); + }; +}; + diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/profiler.js b/test/node_modules/express/node_modules/connect/lib/middleware/profiler.js new file mode 100644 index 0000000..b0b5bac --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/profiler.js @@ -0,0 +1,100 @@ + +/*! + * Connect - profiler + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Profile the duration of a request. + * + * Typically this middleware should be utilized + * _above_ all others, as it proxies the `res.end()` + * method, being first allows it to encapsulate all + * other middleware. + * + * Example Output: + * + * GET / + * response time 2ms + * memory rss 52.00kb + * memory vsize 2.07mb + * heap before 3.76mb / 8.15mb + * heap after 3.80mb / 8.15mb + * + * @api public + */ + +module.exports = function profiler(){ + return function(req, res, next){ + var end = res.end + , start = snapshot(); + + // state snapshot + function snapshot() { + return { + mem: process.memoryUsage() + , time: new Date + }; + } + + // proxy res.end() + res.end = function(data, encoding){ + res.end = end; + res.end(data, encoding); + compare(req, start, snapshot()) + }; + + next(); + } +}; + +/** + * Compare `start` / `end` snapshots. + * + * @param {IncomingRequest} req + * @param {Object} start + * @param {Object} end + * @api private + */ + +function compare(req, start, end) { + console.log(); + row(req.method, req.url); + row('response time:', (end.time - start.time) + 'ms'); + row('memory rss:', formatBytes(end.mem.rss - start.mem.rss)); + row('memory vsize:', formatBytes(end.mem.vsize - start.mem.vsize)); + row('heap before:', formatBytes(start.mem.heapUsed) + ' / ' + formatBytes(start.mem.heapTotal)); + row('heap after:', formatBytes(end.mem.heapUsed) + ' / ' + formatBytes(end.mem.heapTotal)); + console.log(); +} + +/** + * Row helper + * + * @param {String} key + * @param {String} val + * @api private + */ + +function row(key, val) { + console.log(' \033[90m%s\033[0m \033[36m%s\033[0m', key, val); +} + +/** + * Format byte-size. + * + * @param {Number} bytes + * @return {String} + * @api private + */ + +function formatBytes(bytes) { + var kb = 1024 + , mb = 1024 * kb + , gb = 1024 * mb; + if (bytes < kb) return bytes + 'b'; + if (bytes < mb) return (bytes / kb).toFixed(2) + 'kb'; + if (bytes < gb) return (bytes / mb).toFixed(2) + 'mb'; + return (bytes / gb).toFixed(2) + 'gb'; +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/query.js b/test/node_modules/express/node_modules/connect/lib/middleware/query.js new file mode 100644 index 0000000..d3b1acd --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/query.js @@ -0,0 +1,40 @@ + +/*! + * Connect - query + * Copyright(c) 2011 TJ Holowaychuk + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var qs = require('qs') + , parse = require('url').parse; + +/** + * Automatically parse the query-string when available, + * populating the `req.query` object. + * + * Examples: + * + * connect( + * connect.query() + * , function(req, res){ + * res.end(JSON.stringify(req.query)); + * } + * ).listen(3000); + * + * @return {Function} + * @api public + */ + +module.exports = function query(){ + return function query(req, res, next){ + req.query = ~req.url.indexOf('?') + ? qs.parse(parse(req.url).query) + : {}; + next(); + }; +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/responseTime.js b/test/node_modules/express/node_modules/connect/lib/middleware/responseTime.js new file mode 100644 index 0000000..2b2133a --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/responseTime.js @@ -0,0 +1,34 @@ + +/*! + * Connect - responseTime + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Adds the `X-Response-Time` header displaying the response + * duration in milliseconds. + * + * @return {Function} + * @api public + */ + +module.exports = function responseTime(){ + return function(req, res, next){ + var writeHead = res.writeHead + , start = new Date; + + if (res._responseTime) return next(); + res._responseTime = true; + + // proxy writeHead to calculate duration + res.writeHead = function(status, headers){ + var duration = new Date - start; + res.setHeader('X-Response-Time', duration + 'ms'); + res.writeHead = writeHead; + res.writeHead(status, headers); + }; + + next(); + }; +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/router.js b/test/node_modules/express/node_modules/connect/lib/middleware/router.js new file mode 100644 index 0000000..a07452e --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/router.js @@ -0,0 +1,379 @@ + +/*! + * Connect - router + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , parse = require('url').parse; + +/** + * Expose router. + */ + +exports = module.exports = router; + +/** + * Supported HTTP / WebDAV methods. + */ + +var _methods = exports.methods = [ + 'get' + , 'post' + , 'put' + , 'delete' + , 'connect' + , 'options' + , 'trace' + , 'copy' + , 'lock' + , 'mkcol' + , 'move' + , 'propfind' + , 'proppatch' + , 'unlock' + , 'report' + , 'mkactivity' + , 'checkout' + , 'merge' +]; + +/** + * Provides Sinatra and Express-like routing capabilities. + * + * Examples: + * + * connect.router(function(app){ + * app.get('/user/:id', function(req, res, next){ + * // populates req.params.id + * }); + * app.put('/user/:id', function(req, res, next){ + * // populates req.params.id + * }); + * }) + * + * @param {Function} fn + * @return {Function} + * @api public + */ + +function router(fn){ + var self = this + , methods = {} + , routes = {} + , params = {}; + + if (!fn) throw new Error('router provider requires a callback function'); + + // Generate method functions + _methods.forEach(function(method){ + methods[method] = generateMethodFunction(method.toUpperCase()); + }); + + // Alias del -> delete + methods.del = methods.delete; + + // Apply callback to all methods + methods.all = function(){ + var args = arguments; + _methods.forEach(function(name){ + methods[name].apply(this, args); + }); + return self; + }; + + // Register param callback + methods.param = function(name, fn){ + params[name] = fn; + }; + + fn.call(this, methods); + + function generateMethodFunction(name) { + var localRoutes = routes[name] = routes[name] || []; + return function(path, fn){ + var keys = [] + , middleware = []; + + // slice middleware + if (arguments.length > 2) { + middleware = Array.prototype.slice.call(arguments, 1, arguments.length); + fn = middleware.pop(); + middleware = utils.flatten(middleware); + } + + fn.middleware = middleware; + + if (!path) throw new Error(name + ' route requires a path'); + if (!fn) throw new Error(name + ' route ' + path + ' requires a callback'); + var regexp = path instanceof RegExp + ? path + : normalizePath(path, keys); + localRoutes.push({ + fn: fn + , path: regexp + , keys: keys + , orig: path + , method: name + }); + return self; + }; + } + + function router(req, res, next){ + var route + , self = this; + + (function pass(i){ + if (route = match(req, routes, i)) { + var i = 0 + , keys = route.keys; + + req.params = route.params; + + // Param preconditions + (function param(err) { + try { + var key = keys[i++] + , val = req.params[key] + , fn = params[key]; + + if ('route' == err) { + pass(req._route_index + 1); + // Error + } else if (err) { + next(err); + // Param has callback + } else if (fn) { + // Return style + if (1 == fn.length) { + req.params[key] = fn(val); + param(); + // Middleware style + } else { + fn(req, res, param, val); + } + // Finished processing params + } else if (!key) { + // route middleware + i = 0; + (function nextMiddleware(err){ + var fn = route.middleware[i++]; + if ('route' == err) { + pass(req._route_index + 1); + } else if (err) { + next(err); + } else if (fn) { + fn(req, res, nextMiddleware); + } else { + route.call(self, req, res, function(err){ + if (err) { + next(err); + } else { + pass(req._route_index + 1); + } + }); + } + })(); + // More params + } else { + param(); + } + } catch (err) { + next(err); + } + })(); + } else if ('OPTIONS' == req.method) { + options(req, res, routes); + } else { + next(); + } + })(); + }; + + router.remove = function(path, method){ + var fns = router.lookup(path, method); + fns.forEach(function(fn){ + routes[fn.method].splice(fn.index, 1); + }); + }; + + router.lookup = function(path, method, ret){ + ret = ret || []; + + // method specific lookup + if (method) { + method = method.toUpperCase(); + if (routes[method]) { + routes[method].forEach(function(route, i){ + if (path == route.orig) { + var fn = route.fn; + fn.regexp = route.path; + fn.keys = route.keys; + fn.path = route.orig; + fn.method = route.method; + fn.index = i; + ret.push(fn); + } + }); + } + // global lookup + } else { + _methods.forEach(function(method){ + router.lookup(path, method, ret); + }); + } + + return ret; + }; + + router.match = function(url, method, ret){ + var ret = ret || [] + , i = 0 + , fn + , req; + + // method specific matches + if (method) { + method = method.toUpperCase(); + req = { url: url, method: method }; + while (fn = match(req, routes, i)) { + i = req._route_index + 1; + ret.push(fn); + } + // global matches + } else { + _methods.forEach(function(method){ + router.match(url, method, ret); + }); + } + + return ret; + }; + + return router; +} + +/** + * Respond to OPTIONS. + * + * @param {ServerRequest} req + * @param {ServerResponse} req + * @param {Array} routes + * @api private + */ + +function options(req, res, routes) { + var pathname = parse(req.url).pathname + , body = optionsFor(pathname, routes).join(','); + res.writeHead(200, { + 'Content-Length': body.length + , 'Allow': body + }); + res.end(body); +} + +/** + * Return OPTIONS array for the given `path`, matching `routes`. + * + * @param {String} path + * @param {Array} routes + * @return {Array} + * @api private + */ + +function optionsFor(path, routes) { + return _methods.filter(function(method){ + var arr = routes[method.toUpperCase()]; + for (var i = 0, len = arr.length; i < len; ++i) { + if (arr[i].path.test(path)) return true; + } + }).map(function(method){ + return method.toUpperCase(); + }); +} + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * @param {String} path + * @param {Array} keys + * @return {RegExp} + * @api private + */ + +function normalizePath(path, keys) { + path = path + .concat('/?') + .replace(/\/\(/g, '(?:/') + .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){ + keys.push(key); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (format || '') + (capture || '([^/]+?)') + ')' + + (optional || ''); + }) + .replace(/([\/.])/g, '\\$1') + .replace(/\*/g, '(.+)'); + return new RegExp('^' + path + '$', 'i'); +} + +/** + * Attempt to match the given request to + * one of the routes. When successful + * a route function is returned. + * + * @param {ServerRequest} req + * @param {Object} routes + * @return {Function} + * @api private + */ + +function match(req, routes, i) { + var captures + , method = req.method + , i = i || 0; + if ('HEAD' == method) method = 'GET'; + if (routes = routes[method]) { + var url = parse(req.url) + , pathname = url.pathname; + for (var len = routes.length; i < len; ++i) { + var route = routes[i] + , fn = route.fn + , path = route.path + , keys = fn.keys = route.keys; + if (captures = path.exec(pathname)) { + fn.method = method; + fn.params = []; + for (var j = 1, len = captures.length; j < len; ++j) { + var key = keys[j-1], + val = typeof captures[j] === 'string' + ? decodeURIComponent(captures[j]) + : captures[j]; + if (key) { + fn.params[key] = val; + } else { + fn.params.push(val); + } + } + req._route_index = i; + return fn; + } + } + } +} diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/session.js b/test/node_modules/express/node_modules/connect/lib/middleware/session.js new file mode 100644 index 0000000..6616fad --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/session.js @@ -0,0 +1,345 @@ + +/*! + * Connect - session + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Session = require('./session/session') + , MemoryStore = require('./session/memory') + , Cookie = require('./session/cookie') + , Store = require('./session/store') + , utils = require('./../utils') + , parse = require('url').parse + , crypto = require('crypto'); + +// environment + +var env = process.env.NODE_ENV; + +/** + * Expose the middleware. + */ + +exports = module.exports = session; + +/** + * Expose constructors. + */ + +exports.Store = Store; +exports.Cookie = Cookie; +exports.Session = Session; +exports.MemoryStore = MemoryStore; + +/** + * Warning message for `MemoryStore` usage in production. + */ + +var warning = 'Warning: connection.session() MemoryStore is not\n' + + 'designed for a production environment, as it will leak\n' + + 'memory, and obviously only work within a single process.'; + +/** + * Default finger-printing function. + */ + +function defaultFingerprint(req) { + return ''; +}; + +/** + * Paths to ignore. + */ + +exports.ignore = []; + +/** + * Setup session store with the given `options`. + * + * Session data is _not_ saved in the cookie itself, however + * cookies are used, so we must use the [cookieParser()](middleware-cookieParser.html) + * middleware _before_ `session()`. + * + * Examples: + * + * connect.createServer( + * connect.cookieParser() + * , connect.session({ secret: 'keyboard cat' }) + * ); + * + * Options: + * + * - `key` cookie name defaulting to `connect.sid` + * - `store` Session store instance + * - `fingerprint` Custom fingerprint generating function + * - `cookie` Session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: 14400000 }` + * - `secret` Secret string used to compute hash + * + * Ignore Paths: + * + * By default `/favicon.ico` is the only ignored path, all others + * will utilize sessions, to manipulate the paths ignored, use + * `connect.session.ignore.push('/my/path')`. This works for _full_ + * pathnames only, not segments nor substrings. + * + * connect.session.ignore.push('/robots.txt'); + * + * ## req.session + * + * To store or access session data, simply use the request property `req.session`, + * which is (generally) serialized as JSON by the store, so nested objects + * are typically fine. For example below is a user-specific view counter: + * + * connect( + * connect.cookieParser() + * , connect.session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}) + * , connect.favicon() + * , function(req, res, next){ + * var sess = req.session; + * if (sess.views) { + * res.setHeader('Content-Type', 'text/html'); + * res.write('

    views: ' + sess.views + '

    '); + * res.write('

    expires in: ' + (sess.cookie.maxAge / 1000) + 's

    '); + * res.end(); + * sess.views++; + * } else { + * sess.views = 1; + * res.end('welcome to the session demo. refresh!'); + * } + * } + * ).listen(3000); + * + * ## Session#regenerate() + * + * To regenerate the session simply invoke the method, once complete + * a new SID and `Session` instance will be initialized at `req.session`. + * + * req.session.regenerate(function(err){ + * // will have a new session here + * }); + * + * ## Session#destroy() + * + * Destroys the session, removing `req.session`, will be re-generated next request. + * + * req.session.destroy(function(err){ + * // cannot access session here + * }); + * + * ## Session#reload() + * + * Reloads the session data. + * + * req.session.reload(function(err){ + * // session updated + * }); + * + * ## Session#save() + * + * Save the session. + * + * req.session.save(function(err){ + * // session saved + * }); + * + * ## Session#touch() + * + * Updates the `.maxAge`, and `.lastAccess` properties. Typically this is + * not necessary to call, as the session middleware does this for you. + * + * ## Session#cookie + * + * Each session has a unique cookie object accompany it. This allows + * you to alter the session cookie per visitor. For example we can + * set `req.session.cookie.expires` to `false` to enable the cookie + * to remain for only the duration of the user-agent. + * + * ## Session#maxAge + * + * Alternatively `req.session.cookie.maxAge` will return the time + * remaining in milliseconds, which we may also re-assign a new value + * to adjust the `.expires` property appropriately. The following + * are essentially equivalent + * + * var hour = 3600000; + * req.session.cookie.expires = new Date(Date.now() + hour); + * req.session.cookie.maxAge = hour; + * + * For example when `maxAge` is set to `60000` (one minute), and 30 seconds + * has elapsed it will return `30000` until the current request has completed, + * at which time `req.session.touch()` is called to update `req.session.lastAccess`, + * and reset `req.session.maxAge` to its original value. + * + * req.session.cookie.maxAge; + * // => 30000 + * + * Session Store Implementation: + * + * Every session store _must_ implement the following methods + * + * - `.get(sid, callback)` + * - `.set(sid, session, callback)` + * - `.destroy(sid, callback)` + * + * Recommended methods include, but are not limited to: + * + * - `.length(callback)` + * - `.clear(callback)` + * + * For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +function session(options){ + var options = options || {} + , key = options.key || 'connect.sid' + , secret = options.secret + , store = options.store || new MemoryStore + , fingerprint = options.fingerprint || defaultFingerprint + , cookie = options.cookie; + + // notify user that this store is not + // meant for a production environment + if ('production' == env && store instanceof MemoryStore) { + console.warn(warning); + } + + // ensure secret is present + if (!secret) { + throw new Error('connect.session({ secret: "string" }) required for security'); + } + + // session hashing function + store.hash = function(req, base) { + return crypto + .createHmac('sha256', secret) + .update(base + fingerprint(req)) + .digest('base64') + .replace(/=*$/, ''); + }; + + // generates the new session + store.generate = function(req){ + var base = utils.uid(24); + var sessionID = base + '.' + store.hash(req, base); + req.sessionID = sessionID; + req.session = new Session(req); + req.session.cookie = new Cookie(cookie); + }; + + return function session(req, res, next) { + // self-awareness + if (req.session) return next(); + + // parse url + var url = parse(req.url) + , path = url.pathname; + + // ignorable paths + if (~exports.ignore.indexOf(path)) return next(); + + // expose store + req.sessionStore = store; + + // proxy writeHead() to Set-Cookie + var writeHead = res.writeHead; + res.writeHead = function(status, headers){ + if (req.session) { + var cookie = req.session.cookie; + // only send secure session cookies when there is a secure connection. + // proxySecure is a custom attribute to allow for a reverse proxy + // to handle SSL connections and to communicate to connect over HTTP that + // the incoming connection is secure. + var secured = cookie.secure && (req.connection.encrypted || req.connection.proxySecure); + if (secured || !cookie.secure) { + res.setHeader('Set-Cookie', cookie.serialize(key, req.sessionID)); + } + } + + res.writeHead = writeHead; + return res.writeHead(status, headers); + }; + + // proxy end() to commit the session + var end = res.end; + res.end = function(data, encoding){ + res.end = end; + if (req.session) { + // HACK: ensure Set-Cookie for implicit writeHead() + if (!res._header) res._implicitHeader(); + req.session.resetMaxAge(); + req.session.save(function(){ + res.end(data, encoding); + }); + } else { + res.end(data, encoding); + } + }; + + // session hashing + function hash(base) { + return store.hash(req, base); + } + + // generate the session + function generate() { + store.generate(req); + } + + // get the sessionID from the cookie + req.sessionID = req.cookies[key]; + + // make a new session if the browser doesn't send a sessionID + if (!req.sessionID) { + generate(); + next(); + return; + } + + // check the fingerprint + var parts = req.sessionID.split('.'); + if (parts[1] != hash(parts[0])) { + generate(); + next(); + return; + } + + // generate the session object + var pause = utils.pause(req); + store.get(req.sessionID, function(err, sess){ + // proxy to resume() events + var _next = next; + next = function(err){ + _next(err); + pause.resume(); + } + + // error handling + if (err) { + if ('ENOENT' == err.code) { + generate(); + next(); + } else { + next(err); + } + // no session + } else if (!sess) { + generate(); + next(); + // populate req.session + } else { + store.createSession(req, sess); + next(); + } + }); + }; +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js b/test/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js new file mode 100644 index 0000000..793c2e9 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js @@ -0,0 +1,126 @@ + +/*! + * Connect - session - Cookie + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../../utils'); + +/** + * Initialize a new `Cookie` with the given `options`. + * + * @param {Object} options + * @api private + */ + +var Cookie = module.exports = function Cookie(options) { + this.path = '/'; + this.httpOnly = true; + this.maxAge = 14400000; + if (options) utils.merge(this, options); + this.originalMaxAge = undefined == this.originalMaxAge + ? this.maxAge + : this.originalMaxAge; +}; + +/** + * Prototype. + */ + +Cookie.prototype = { + + /** + * Set expires `date`. + * + * @param {Date} date + * @api public + */ + + set expires(date) { + this._expires = date; + this.originalMaxAge = this.maxAge; + }, + + /** + * Get expires `date`. + * + * @return {Date} + * @api public + */ + + get expires() { + return this._expires; + }, + + /** + * Set expires via max-age in `ms`. + * + * @param {Number} ms + * @api public + */ + + set maxAge(ms) { + this.expires = 'number' == typeof ms + ? new Date(Date.now() + ms) + : ms; + }, + + /** + * Get expires max-age in `ms`. + * + * @return {Number} + * @api public + */ + + get maxAge() { + return this.expires instanceof Date + ? this.expires.valueOf() - Date.now() + : this.expires; + }, + + /** + * Return cookie data object. + * + * @return {Object} + * @api private + */ + + get data() { + return { + originalMaxAge: this.originalMaxAge + , expires: this._expires + , secure: this.secure + , httpOnly: this.httpOnly + , domain: this.domain + , path: this.path + } + }, + + /** + * Return a serialized cookie string. + * + * @return {String} + * @api public + */ + + serialize: function(name, val){ + return utils.serializeCookie(name, val, this.data); + }, + + /** + * Return JSON representation of this cookie. + * + * @return {Object} + * @api private + */ + + toJSON: function(){ + return this.data; + } +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/session/memory.js b/test/node_modules/express/node_modules/connect/lib/middleware/session/memory.js new file mode 100644 index 0000000..ec569f5 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/session/memory.js @@ -0,0 +1,131 @@ + +/*! + * Connect - session - MemoryStore + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Store = require('./store') + , utils = require('../../utils') + , Session = require('./session'); + +/** + * Initialize a new `MemoryStore`. + * + * @api public + */ + +var MemoryStore = module.exports = function MemoryStore() { + this.sessions = {}; +}; + +/** + * Inherit from `Store.prototype`. + */ + +MemoryStore.prototype.__proto__ = Store.prototype; + +/** + * Attempt to fetch session by the given `sid`. + * + * @param {String} sid + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.get = function(sid, fn){ + var self = this; + process.nextTick(function(){ + var expires + , sess = self.sessions[sid]; + if (sess) { + sess = JSON.parse(sess); + expires = 'string' == typeof sess.cookie.expires + ? new Date(sess.cookie.expires) + : sess.cookie.expires; + if (!expires || new Date < expires) { + fn(null, sess); + } else { + self.destroy(sid, fn); + } + } else { + fn(); + } + }); +}; + +/** + * Commit the given `sess` object associated with the given `sid`. + * + * @param {String} sid + * @param {Session} sess + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.set = function(sid, sess, fn){ + var self = this; + process.nextTick(function(){ + self.sessions[sid] = JSON.stringify(sess); + fn && fn(); + }); +}; + +/** + * Destroy the session associated with the given `sid`. + * + * @param {String} sid + * @api public + */ + +MemoryStore.prototype.destroy = function(sid, fn){ + var self = this; + process.nextTick(function(){ + delete self.sessions[sid]; + fn && fn(); + }); +}; + +/** + * Invoke the given callback `fn` with all active sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.all = function(fn){ + var arr = [] + , keys = Object.keys(this.sessions); + for (var i = 0, len = keys.length; i < len; ++i) { + arr.push(this.sessions[keys[i]]); + } + fn(null, arr); +}; + +/** + * Clear all sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.clear = function(fn){ + this.sessions = {}; + fn && fn(); +}; + +/** + * Fetch number of sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.length = function(fn){ + fn(null, Object.keys(this.sessions).length); +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/session/session.js b/test/node_modules/express/node_modules/connect/lib/middleware/session/session.js new file mode 100644 index 0000000..4e7e1a6 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/session/session.js @@ -0,0 +1,137 @@ + +/*! + * Connect - session - Session + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../../utils') + , Cookie = require('./cookie'); + +/** + * Create a new `Session` with the given request and `data`. + * + * @param {IncomingRequest} req + * @param {Object} data + * @api private + */ + +var Session = module.exports = function Session(req, data) { + Object.defineProperty(this, 'req', { value: req }); + Object.defineProperty(this, 'id', { value: req.sessionID }); + if ('object' == typeof data) { + utils.merge(this, data); + } else { + this.lastAccess = Date.now(); + } +}; + +/** + * Update `.lastAccess` timestamp, + * and reset `.cookie.maxAge` to prevent + * the cookie from expiring when the + * session is still active. + * + * @return {Session} for chaining + * @api public + */ + +Session.prototype.touch = function(){ + return this + .resetLastAccess() + .resetMaxAge(); +}; + +/** + * Update `.lastAccess` timestamp. + * + * @return {Session} for chaining + * @api public + */ + +Session.prototype.resetLastAccess = function(){ + this.lastAccess = Date.now(); + return this; +}; + +/** + * Reset `.maxAge` to `.originalMaxAge`. + * + * @return {Session} for chaining + * @api public + */ + +Session.prototype.resetMaxAge = function(){ + this.cookie.maxAge = this.cookie.originalMaxAge; + return this; +}; + +/** + * Save the session data with optional callback `fn(err)`. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.save = function(fn){ + this.req.sessionStore.set(this.id, this, fn || function(){}); + return this; +}; + +/** + * Re-loads the session data _without_ altering + * the maxAge or lastAccess properties. Invokes the + * callback `fn(err)`, after which time if no exception + * has occurred the `req.session` property will be + * a new `Session` object, although representing the + * same session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.reload = function(fn){ + var req = this.req + , store = this.req.sessionStore; + store.get(this.id, function(err, sess){ + if (err) return fn(err); + if (!sess) return fn(new Error('failed to load session')); + store.createSession(req, sess); + fn(); + }); + return this; +}; + +/** + * Destroy `this` session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.destroy = function(fn){ + delete this.req.session; + this.req.sessionStore.destroy(this.id, fn); + return this; +}; + +/** + * Regenerate this request's session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.regenerate = function(fn){ + this.req.sessionStore.regenerate(this.req, fn); + return this; +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/session/store.js b/test/node_modules/express/node_modules/connect/lib/middleware/session/store.js new file mode 100644 index 0000000..6a3d47d --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/session/store.js @@ -0,0 +1,87 @@ + +/*! + * Connect - session - Store + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , Session = require('./session') + , Cookie = require('./cookie') + , utils = require('../../utils'); + +/** + * Initialize abstract `Store`. + * + * @api private + */ + +var Store = module.exports = function Store(options){}; + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Store.prototype.__proto__ = EventEmitter.prototype; + +/** + * Re-generate the given requests's session. + * + * @param {IncomingRequest} req + * @return {Function} fn + * @api public + */ + +Store.prototype.regenerate = function(req, fn){ + var self = this; + this.destroy(req.sessionID, function(err){ + self.generate(req); + fn(err); + }); +}; + +/** + * Load a `Session` instance via the given `sid` + * and invoke the callback `fn(err, sess)`. + * + * @param {String} sid + * @param {Function} fn + * @api public + */ + +Store.prototype.load = function(sid, fn){ + var self = this; + this.get(sid, function(err, sess){ + if (err) return fn(err); + if (!sess) return fn(); + var req = { sessionID: sid, sessionStore: self }; + sess = self.createSession(req, sess, false); + fn(null, sess); + }); +}; + +/** + * Create session from JSON `sess` data. + * + * @param {IncomingRequest} req + * @param {Object} sess + * @return {Session} + * @api private + */ + +Store.prototype.createSession = function(req, sess, update){ + var expires = sess.cookie.expires + , orig = sess.cookie.originalMaxAge + , update = null == update ? true : false; + sess.cookie = new Cookie(sess.cookie); + if ('string' == typeof expires) sess.cookie.expires = new Date(expires); + sess.cookie.originalMaxAge = orig; + req.session = new Session(req, sess); + if (update) req.session.resetLastAccess(); + return req.session; +}; \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/static.js b/test/node_modules/express/node_modules/connect/lib/middleware/static.js new file mode 100644 index 0000000..b9c2c86 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/static.js @@ -0,0 +1,225 @@ + +/*! + * Connect - staticProvider + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , path = require('path') + , join = path.join + , basename = path.basename + , normalize = path.normalize + , utils = require('../utils') + , Buffer = require('buffer').Buffer + , parse = require('url').parse + , mime = require('mime'); + +/** + * Static file server with the given `root` path. + * + * Examples: + * + * var oneDay = 86400000; + * + * connect( + * connect.static(__dirname + '/public') + * ).listen(3000); + * + * connect( + * connect.static(__dirname + '/public', { maxAge: oneDay }) + * ).listen(3000); + * + * Options: + * + * - `maxAge` Browser cache maxAge in milliseconds. defaults to 0 + * - `hidden` Allow transfer of hidden files. defaults to false + * - `redirect` Redirect to trailing "/" when the pathname is a dir + * + * @param {String} root + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function static(root, options){ + options = options || {}; + + // root required + if (!root) throw new Error('static() root path required'); + options.root = root; + + return function static(req, res, next) { + options.path = req.url; + options.getOnly = true; + send(req, res, next, options); + }; +}; + +/** + * Expose mime module. + */ + +exports.mime = mime; + +/** + * Respond with 416 "Requested Range Not Satisfiable" + * + * @param {ServerResponse} res + * @api private + */ + +function invalidRange(res) { + var body = 'Requested Range Not Satisfiable'; + res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Length', body.length); + res.statusCode = 416; + res.end(body); +} + +/** + * Attempt to tranfer the requseted file to `res`. + * + * @param {ServerRequest} + * @param {ServerResponse} + * @param {Function} next + * @param {Object} options + * @api private + */ + +var send = exports.send = function(req, res, next, options){ + options = options || {}; + if (!options.path) throw new Error('path required'); + + // setup + var maxAge = options.maxAge || 0 + , ranges = req.headers.range + , head = 'HEAD' == req.method + , get = 'GET' == req.method + , root = options.root ? normalize(options.root) : null + , redirect = false === options.redirect ? false : true + , getOnly = options.getOnly + , fn = options.callback + , hidden = options.hidden + , done; + + // replace next() with callback when available + if (fn) next = fn; + + // ignore non-GET requests + if (getOnly && !get && !head) return next(); + + // parse url + var url = parse(options.path) + , path = decodeURIComponent(url.pathname) + , type; + + // null byte(s) + if (~path.indexOf('\0')) return utils.badRequest(res); + + // when root is not given, consider .. malicious + if (!root && ~path.indexOf('..')) return utils.forbidden(res); + + // join / normalize from optional root dir + path = normalize(join(root, path)); + + // malicious path + if (root && 0 != path.indexOf(root)) return fn + ? fn(new Error('Forbidden')) + : utils.forbidden(res); + + // index.html support + if (normalize('/') == path[path.length - 1]) path += 'index.html'; + + // "hidden" file + if (!hidden && '.' == basename(path)[0]) return next(); + + fs.stat(path, function(err, stat){ + // mime type + type = mime.lookup(path); + + // ignore ENOENT + if (err) { + if (fn) return fn(err); + return 'ENOENT' == err.code + ? next() + : next(err); + // redirect directory in case index.html is present + } else if (stat.isDirectory()) { + if (!redirect) return next(); + res.statusCode = 301; + res.setHeader('Location', url.pathname + '/'); + res.end('Redirecting to ' + url.pathname + '/'); + return; + } + + // header fields + if (!res.getHeader('Date')) res.setHeader('Date', new Date().toUTCString()); + if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (maxAge / 1000)); + if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString()); + if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat)); + if (!res.getHeader('content-type')) { + var charset = mime.charsets.lookup(type); + res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : '')); + } + res.setHeader('Accept-Ranges', 'bytes'); + + // conditional GET support + if (utils.conditionalGET(req)) { + if (!utils.modified(req, res)) { + req.emit('static'); + return utils.notModified(res); + } + } + + var opts = {}; + var chunkSize = stat.size; + + // we have a Range request + if (ranges) { + ranges = utils.parseRange(stat.size, ranges); + // valid + if (ranges) { + // TODO: stream options + // TODO: multiple support + opts.start = ranges[0].start; + opts.end = ranges[0].end; + chunkSize = opts.end - opts.start + 1; + res.statusCode = 206; + res.setHeader('Content-Range', 'bytes ' + + opts.start + + '-' + + opts.end + + '/' + + stat.size); + // invalid + } else { + return fn + ? fn(new Error('Requested Range Not Satisfiable')) + : invalidRange(res); + } + } + + res.setHeader('Content-Length', chunkSize); + + // transfer + if (head) return res.end(); + + // stream + var stream = fs.createReadStream(path, opts); + req.emit('static', stream); + stream.pipe(res); + + // callback + if (fn) { + function callback(err) { done || fn(err); done = true } + req.on('close', callback); + stream.on('end', callback); + } + }); +}; diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/staticCache.js b/test/node_modules/express/node_modules/connect/lib/middleware/staticCache.js new file mode 100644 index 0000000..9ea8eb7 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/staticCache.js @@ -0,0 +1,175 @@ + +/*! + * Connect - staticCache + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , utils = require('../utils') + , Cache = require('../cache') + , url = require('url') + , fs = require('fs'); + +/** + * Enables a memory cache layer on top of + * the `static()` middleware, serving popular + * static files. + * + * By default a maximum of 128 objects are + * held in cache, with a max of 256k each, + * totalling ~32mb. + * + * A Least-Recently-Used (LRU) cache algo + * is implemented through the `Cache` object, + * simply rotating cache objects as they are + * hit. This means that increasingly popular + * objects maintain their positions while + * others get shoved out of the stack and + * garbage collected. + * + * Benchmarks: + * + * static(): 2700 rps + * node-static: 5300 rps + * static() + staticCache(): 7500 rps + * + * Options: + * + * - `maxObjects` max cache objects [128] + * - `maxLength` max cache object length 256kb + * + * @param {Type} name + * @return {Type} + * @api public + */ + +module.exports = function staticCache(options){ + var options = options || {} + , cache = new Cache(options.maxObjects || 128) + , maxlen = options.maxLength || 1024 * 256; + + return function staticCache(req, res, next){ + var path = url.parse(req.url).pathname + , ranges = req.headers.range + , hit = cache.get(path) + , hitCC + , uaCC + , header + , age; + + // cache static + req.on('static', function(stream){ + var headers = res._headers + , cc = utils.parseCacheControl(headers['cache-control'] || '') + , contentLength = headers['content-length'] + , hit; + + // ignore larger files + if (!contentLength || contentLength > maxlen) return; + + // dont cache items we shouldn't be + if ( cc['no-cache'] + || cc['no-store'] + || cc['private'] + || cc['must-revalidate']) return; + + // if already in cache then validate + if (hit = cache.get(path)){ + if (headers.etag == hit[0].etag) { + hit[0].date = new Date; + return; + } else { + cache.remove(path); + } + } + + // validation notifiactions don't contain a steam + if (null == stream) return; + + // add the cache object + var arr = cache.add(path); + arr.push(headers); + + // store the chunks + stream.on('data', function(chunk){ + arr.push(chunk); + }); + + // flag it as complete + stream.on('end', function(){ + arr.complete = true; + }); + }); + + // cache hit, doesnt support range requests + if (hit && hit.complete && !ranges) { + header = utils.merge({}, hit[0]); + header.Age = age = (new Date - new Date(header.date)) / 1000 | 0; + header.date = new Date().toUTCString(); + + // parse cache-controls + hitCC = utils.parseCacheControl(header['cache-control'] || ''); + uaCC = utils.parseCacheControl(req.headers['cache-control'] || ''); + + // check if we must revalidate(bypass) + if (hitCC['no-cache'] || uaCC['no-cache']) return next(); + + // check freshness of entity + if (isStale(hitCC, age) || isStale(uaCC, age)) return next(); + + // conditional GET support + if (utils.conditionalGET(req)) { + if (!utils.modified(req, res, header)) { + header['content-length'] = 0; + res.writeHead(304, header); + return res.end(); + } + } + + // HEAD support + if ('HEAD' == req.method) { + header['content-length'] = 0; + res.writeHead(200, header); + return res.end(); + } + + // respond with cache + res.writeHead(200, header); + + // backpressure + function write(i) { + var buf = hit[i]; + if (!buf) return res.end(); + if (false === res.write(buf)) { + res.once('drain', function(){ + write(++i); + }); + } else { + write(++i); + } + } + + return write(1); + } + + next(); + } +}; + +/** + * Check if cache item is stale + * + * @param {Object} cc + * @param {Number} age + * @return {Boolean} + * @api private + */ + +function isStale(cc, age) { + return cc['max-age'] && cc['max-age'] <= age; +} \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/middleware/vhost.js b/test/node_modules/express/node_modules/connect/lib/middleware/vhost.js new file mode 100644 index 0000000..913d756 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/middleware/vhost.js @@ -0,0 +1,44 @@ + +/*! + * Connect - vhost + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Setup vhost for the given `hostname` and `server`. + * + * Examples: + * + * connect( + * connect.vhost('foo.com', + * connect.createServer(...middleware...) + * ), + * connect.vhost('bar.com', + * connect.createServer(...middleware...) + * ) + * ); + * + * @param {String} hostname + * @param {Server} server + * @return {Function} + * @api public + */ + +module.exports = function vhost(hostname, server){ + if (!hostname) throw new Error('vhost hostname required'); + if (!server) throw new Error('vhost server required'); + var regexp = new RegExp('^' + hostname.replace(/[*]/g, '(.*?)') + '$'); + if (server.onvhost) server.onvhost(hostname); + return function vhost(req, res, next){ + if (!req.headers.host) return next(); + var host = req.headers.host.split(':')[0]; + if (req.subdomains = regexp.exec(host)) { + req.subdomains = req.subdomains[0].split('.').slice(0, -1); + server.emit("request", req, res); + } else { + next(); + } + }; +}; diff --git a/test/node_modules/express/node_modules/connect/lib/patch.js b/test/node_modules/express/node_modules/connect/lib/patch.js new file mode 100644 index 0000000..a6ff297 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/patch.js @@ -0,0 +1,79 @@ + +/*! + * Connect + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , res = http.OutgoingMessage.prototype; + +// original setHeader() + +var setHeader = res.setHeader; + +// original _renderHeaders() + +var _renderHeaders = res._renderHeaders; + +if (res._hasConnectPatch) return; + +/** + * Provide a public "header sent" flag + * until node does. + * + * @return {Boolean} + * @api public + */ + +res.__defineGetter__('headerSent', function(){ + return this._headerSent; +}); + +/** + * Set header `field` to `val`, special-casing + * the `Set-Cookie` field for multiple support. + * + * @param {String} field + * @param {String} val + * @api public + */ + +res.setHeader = function(field, val){ + var key = field.toLowerCase() + , prev; + + // special-case Set-Cookie + if (this._headers && 'set-cookie' == key) { + if (prev = this.getHeader(field)) { + val = Array.isArray(prev) + ? prev.concat(val) + : [prev, val]; + } + // charset + } else if ('content-type' == key && this.charset) { + val += '; charset=' + this.charset; + } + + return setHeader.call(this, field, val); +}; + +/** + * Proxy `res.end()` to expose a 'header' event, + * allowing arbitrary augmentation before the header + * fields are written to the socket. + * + * NOTE: this _only_ supports node's progressive header + * field API aka `res.setHeader()`. + */ + +res._renderHeaders = function(){ + this.emit('header'); + return _renderHeaders.call(this); +}; + +res._hasConnectPatch = true; diff --git a/test/node_modules/express/node_modules/connect/lib/public/directory.html b/test/node_modules/express/node_modules/connect/lib/public/directory.html new file mode 100644 index 0000000..15164bb --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/public/directory.html @@ -0,0 +1,75 @@ + + + listing directory {directory} + + + + + +
    +

    {linked-path}

    + {files} +
    + + \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/public/error.html b/test/node_modules/express/node_modules/connect/lib/public/error.html new file mode 100644 index 0000000..34e0df5 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/public/error.html @@ -0,0 +1,13 @@ + + + {error} + + + +
    +

    {title}

    +

    500 {error}

    +
      {stack}
    +
    + + \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/lib/public/favicon.ico b/test/node_modules/express/node_modules/connect/lib/public/favicon.ico new file mode 100644 index 0000000..895fc96 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/favicon.ico differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page.png new file mode 100644 index 0000000..03ddd79 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_add.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_add.png new file mode 100644 index 0000000..d5bfa07 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_add.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png new file mode 100644 index 0000000..89ee2da Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_code.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_code.png new file mode 100644 index 0000000..f7ea904 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_code.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png new file mode 100644 index 0000000..195dc6d Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png new file mode 100644 index 0000000..3141467 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png new file mode 100644 index 0000000..046811e Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_error.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_error.png new file mode 100644 index 0000000..f07f449 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_error.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png new file mode 100644 index 0000000..eb6158e Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_find.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_find.png new file mode 100644 index 0000000..2f19388 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_find.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png new file mode 100644 index 0000000..8e83281 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_go.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_go.png new file mode 100644 index 0000000..80fe1ed Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_go.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_green.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_green.png new file mode 100644 index 0000000..de8e003 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_green.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_key.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_key.png new file mode 100644 index 0000000..d6626cb Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_key.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png new file mode 100644 index 0000000..7e56870 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_link.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_link.png new file mode 100644 index 0000000..312eab0 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_link.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png new file mode 100644 index 0000000..246a2f0 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png new file mode 100644 index 0000000..968f073 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_red.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_red.png new file mode 100644 index 0000000..0b18247 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_red.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png new file mode 100644 index 0000000..cf347c7 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_save.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_save.png new file mode 100644 index 0000000..caea546 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_save.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white.png new file mode 100644 index 0000000..8b8b1ca Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png new file mode 100644 index 0000000..8f8095e Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png new file mode 100644 index 0000000..159b240 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png new file mode 100644 index 0000000..aa23dde Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png new file mode 100644 index 0000000..34a05cc Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png new file mode 100644 index 0000000..f501a59 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png new file mode 100644 index 0000000..848bdaf Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png new file mode 100644 index 0000000..0c76bd1 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png new file mode 100644 index 0000000..87a6914 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png new file mode 100644 index 0000000..c66011f Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png new file mode 100644 index 0000000..2b6b100 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png new file mode 100644 index 0000000..a9f31a2 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png new file mode 100644 index 0000000..a87cf84 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png new file mode 100644 index 0000000..ffb8fc9 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png new file mode 100644 index 0000000..0a7d6f4 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png new file mode 100644 index 0000000..bddba1f Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png new file mode 100644 index 0000000..af1ecaf Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png new file mode 100644 index 0000000..4cc537a Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png new file mode 100644 index 0000000..b93e776 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png new file mode 100644 index 0000000..9fc5a0a Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png new file mode 100644 index 0000000..b977d7e Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png new file mode 100644 index 0000000..5818436 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png new file mode 100644 index 0000000..5769120 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png new file mode 100644 index 0000000..8d719df Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png new file mode 100644 index 0000000..106f5aa Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png new file mode 100644 index 0000000..e4a1ecb Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png new file mode 100644 index 0000000..7e62a92 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png new file mode 100644 index 0000000..e902abb Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png new file mode 100644 index 0000000..1d2d0a4 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png new file mode 100644 index 0000000..d616484 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png new file mode 100644 index 0000000..7215d1e Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png new file mode 100644 index 0000000..bf7bd1c Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png new file mode 100644 index 0000000..f6b74cc Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png new file mode 100644 index 0000000..d3fffb6 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png new file mode 100644 index 0000000..a65bcb3 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png new file mode 100644 index 0000000..23a37b8 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png new file mode 100644 index 0000000..f907e44 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png new file mode 100644 index 0000000..5b2cbb3 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png new file mode 100644 index 0000000..7868a25 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png new file mode 100644 index 0000000..134b669 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png new file mode 100644 index 0000000..c4eff03 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png new file mode 100644 index 0000000..884ffd6 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png new file mode 100644 index 0000000..f59b7c4 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png new file mode 100644 index 0000000..44084ad Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png new file mode 100644 index 0000000..3a1441c Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png new file mode 100644 index 0000000..e770829 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png new file mode 100644 index 0000000..813f712 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png new file mode 100644 index 0000000..d9cf132 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png new file mode 100644 index 0000000..52699bf Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png new file mode 100644 index 0000000..4a05955 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png new file mode 100644 index 0000000..a0a433d Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png new file mode 100644 index 0000000..1eb8809 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png new file mode 100644 index 0000000..ae8ecbf Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png new file mode 100644 index 0000000..6ed2490 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png new file mode 100644 index 0000000..fecadd0 Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png new file mode 100644 index 0000000..fd4bbcc Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_word.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_word.png new file mode 100644 index 0000000..834cdfa Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_word.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/icons/page_world.png b/test/node_modules/express/node_modules/connect/lib/public/icons/page_world.png new file mode 100644 index 0000000..b8895dd Binary files /dev/null and b/test/node_modules/express/node_modules/connect/lib/public/icons/page_world.png differ diff --git a/test/node_modules/express/node_modules/connect/lib/public/style.css b/test/node_modules/express/node_modules/connect/lib/public/style.css new file mode 100644 index 0000000..32b6507 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/public/style.css @@ -0,0 +1,141 @@ +body { + margin: 0; + padding: 80px 100px; + font: 13px "Helvetica Neue", "Lucida Grande", "Arial"; + background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9)); + background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9); + background-repeat: no-repeat; + color: #555; + -webkit-font-smoothing: antialiased; +} +h1, h2, h3 { + margin: 0; + font-size: 22px; + color: #343434; +} +h1 em, h2 em { + padding: 0 5px; + font-weight: normal; +} +h1 { + font-size: 60px; +} +h2 { + margin-top: 10px; +} +h3 { + margin: 5px 0 10px 0; + padding-bottom: 5px; + border-bottom: 1px solid #eee; + font-size: 18px; +} +ul { + margin: 0; + padding: 0; +} +ul li { + margin: 5px 0; + padding: 3px 8px; + list-style: none; +} +ul li:hover { + cursor: pointer; + color: #2e2e2e; +} +ul li .path { + padding-left: 5px; + font-weight: bold; +} +ul li .line { + padding-right: 5px; + font-style: italic; +} +ul li:first-child .path { + padding-left: 0; +} +p { + line-height: 1.5; +} +a { + color: #555; + text-decoration: none; +} +a:hover { + color: #303030; +} +#stacktrace { + margin-top: 15px; +} +.directory h1 { + margin-bottom: 15px; + font-size: 18px; +} +ul#files { + width: 100%; + height: 500px; +} +ul#files li { + padding: 0; +} +ul#files li img { + position: absolute; + top: 5px; + left: 5px; +} +ul#files li a { + position: relative; + display: block; + margin: 1px; + width: 30%; + height: 25px; + line-height: 25px; + text-indent: 8px; + float: left; + border: 1px solid transparent; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + overflow: hidden; + text-overflow: ellipsis; +} +ul#files li a.icon { + text-indent: 25px; +} +ul#files li a:focus, +ul#files li a:hover { + outline: none; + background: rgba(255,255,255,0.65); + border: 1px solid #ececec; +} +ul#files li a.highlight { + -webkit-transition: background .4s ease-in-out; + background: #ffff4f; + border-color: #E9DC51; +} +#search { + display: block; + position: fixed; + top: 20px; + right: 20px; + width: 90px; + -webkit-transition: width ease 0.2s, opacity ease 0.4s; + -moz-transition: width ease 0.2s, opacity ease 0.4s; + -webkit-border-radius: 32px; + -moz-border-radius: 32px; + -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -webkit-font-smoothing: antialiased; + text-align: left; + font: 13px "Helvetica Neue", Arial, sans-serif; + padding: 4px 10px; + border: none; + background: transparent; + margin-bottom: 0; + outline: none; + opacity: 0.7; + color: #888; +} +#search:focus { + width: 120px; + opacity: 1.0; +} diff --git a/test/node_modules/express/node_modules/connect/lib/utils.js b/test/node_modules/express/node_modules/connect/lib/utils.js new file mode 100644 index 0000000..d0bc172 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/lib/utils.js @@ -0,0 +1,451 @@ + +/*! + * Connect - utils + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var crypto = require('crypto') + , Path = require('path') + , fs = require('fs'); + +/** + * Flatten the given `arr`. + * + * @param {Array} arr + * @return {Array} + * @api private + */ + +exports.flatten = function(arr, ret){ + var ret = ret || [] + , len = arr.length; + for (var i = 0; i < len; ++i) { + if (Array.isArray(arr[i])) { + exports.flatten(arr[i], ret); + } else { + ret.push(arr[i]); + } + } + return ret; +}; + +/** + * Return md5 hash of the given string and optional encoding, + * defaulting to hex. + * + * utils.md5('wahoo'); + * // => "e493298061761236c96b02ea6aa8a2ad" + * + * @param {String} str + * @param {String} encoding + * @return {String} + * @api public + */ + +exports.md5 = function(str, encoding){ + return crypto + .createHash('md5') + .update(str) + .digest(encoding || 'hex'); +}; + +/** + * Merge object b with object a. + * + * var a = { foo: 'bar' } + * , b = { bar: 'baz' }; + * + * utils.merge(a, b); + * // => { foo: 'bar', bar: 'baz' } + * + * @param {Object} a + * @param {Object} b + * @return {Object} + * @api public + */ + +exports.merge = function(a, b){ + if (a && b) { + for (var key in b) { + a[key] = b[key]; + } + } + return a; +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api public + */ + +exports.escape = function(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + + +/** + * Return a unique identifier with the given `len`. + * + * utils.uid(10); + * // => "FDaS435D2z" + * + * @param {Number} len + * @return {String} + * @api public + */ + +exports.uid = function(len) { + var buf = [] + , chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + , charlen = chars.length; + + for (var i = 0; i < len; ++i) { + buf.push(chars[getRandomInt(0, charlen - 1)]); + } + + return buf.join(''); +}; + +/** + * Parse the given cookie string into an object. + * + * @param {String} str + * @return {Object} + * @api public + */ + +exports.parseCookie = function(str){ + var obj = {} + , pairs = str.split(/[;,] */); + for (var i = 0, len = pairs.length; i < len; ++i) { + var pair = pairs[i] + , eqlIndex = pair.indexOf('=') + , key = pair.substr(0, eqlIndex).trim().toLowerCase() + , val = pair.substr(++eqlIndex, pair.length).trim(); + + // quoted values + if ('"' == val[0]) val = val.slice(1, -1); + + // only assign once + if (undefined == obj[key]) { + val = val.replace(/\+/g, ' '); + try { + obj[key] = decodeURIComponent(val); + } catch (err) { + if (err instanceof URIError) { + obj[key] = val; + } else { + throw err; + } + } + } + } + return obj; +}; + +/** + * Serialize the given object into a cookie string. + * + * utils.serializeCookie('name', 'tj', { httpOnly: true }) + * // => "name=tj; httpOnly" + * + * @param {String} name + * @param {String} val + * @param {Object} obj + * @return {String} + * @api public + */ + +exports.serializeCookie = function(name, val, obj){ + var pairs = [name + '=' + encodeURIComponent(val)] + , obj = obj || {}; + + if (obj.domain) pairs.push('domain=' + obj.domain); + if (obj.path) pairs.push('path=' + obj.path); + if (obj.expires) pairs.push('expires=' + obj.expires.toUTCString()); + if (obj.httpOnly) pairs.push('httpOnly'); + if (obj.secure) pairs.push('secure'); + + return pairs.join('; '); +}; + +/** + * Pause `data` and `end` events on the given `obj`. + * Middleware performing async tasks _should_ utilize + * this utility (or similar), to re-emit data once + * the async operation has completed, otherwise these + * events may be lost. + * + * var pause = utils.pause(req); + * fs.readFile(path, function(){ + * next(); + * pause.resume(); + * }); + * + * @param {Object} obj + * @return {Object} + * @api public + */ + +exports.pause = function(obj){ + var onData + , onEnd + , events = []; + + // buffer data + obj.on('data', onData = function(data, encoding){ + events.push(['data', data, encoding]); + }); + + // buffer end + obj.on('end', onEnd = function(data, encoding){ + events.push(['end', data, encoding]); + }); + + return { + end: function(){ + obj.removeListener('data', onData); + obj.removeListener('end', onEnd); + }, + resume: function(){ + this.end(); + for (var i = 0, len = events.length; i < len; ++i) { + obj.emit.apply(obj, events[i]); + } + } + }; +}; + +/** + * Check `req` and `res` to see if it has been modified. + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @return {Boolean} + * @api public + */ + +exports.modified = function(req, res, headers) { + var headers = headers || res._headers || {} + , modifiedSince = req.headers['if-modified-since'] + , lastModified = headers['last-modified'] + , noneMatch = req.headers['if-none-match'] + , etag = headers['etag']; + + if (noneMatch) noneMatch = noneMatch.split(/ *, */); + + // check If-None-Match + if (noneMatch && etag && ~noneMatch.indexOf(etag)) { + return false; + } + + // check If-Modified-Since + if (modifiedSince && lastModified) { + modifiedSince = new Date(modifiedSince); + lastModified = new Date(lastModified); + // Ignore invalid dates + if (!isNaN(modifiedSince.getTime())) { + if (lastModified <= modifiedSince) return false; + } + } + + return true; +}; + +/** + * Strip `Content-*` headers from `res`. + * + * @param {ServerResponse} res + * @api public + */ + +exports.removeContentHeaders = function(res){ + Object.keys(res._headers).forEach(function(field){ + if (0 == field.indexOf('content')) { + res.removeHeader(field); + } + }); +}; + +/** + * Check if `req` is a conditional GET request. + * + * @param {IncomingMessage} req + * @return {Boolean} + * @api public + */ + +exports.conditionalGET = function(req) { + return req.headers['if-modified-since'] + || req.headers['if-none-match']; +}; + +/** + * Respond with 403 "Forbidden". + * + * @param {ServerResponse} res + * @api public + */ + +exports.forbidden = function(res) { + var body = 'Forbidden'; + res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Length', body.length); + res.statusCode = 403; + res.end(body); +}; + +/** + * Respond with 401 "Unauthorized". + * + * @param {ServerResponse} res + * @param {String} realm + * @api public + */ + +exports.unauthorized = function(res, realm) { + res.statusCode = 401; + res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"'); + res.end('Unauthorized'); +}; + +/** + * Respond with 400 "Bad Request". + * + * @param {ServerResponse} res + * @api public + */ + +exports.badRequest = function(res) { + res.statusCode = 400; + res.end('Bad Request'); +}; + +/** + * Respond with 304 "Not Modified". + * + * @param {ServerResponse} res + * @param {Object} headers + * @api public + */ + +exports.notModified = function(res) { + exports.removeContentHeaders(res); + res.statusCode = 304; + res.end(); +}; + +/** + * Return an ETag in the form of `"-"` + * from the given `stat`. + * + * @param {Object} stat + * @return {String} + * @api public + */ + +exports.etag = function(stat) { + return '"' + stat.size + '-' + Number(stat.mtime) + '"'; +}; + +/** + * Parse "Range" header `str` relative to the given file `size`. + * + * @param {Number} size + * @param {String} str + * @return {Array} + * @api public + */ + +exports.parseRange = function(size, str){ + var valid = true; + var arr = str.substr(6).split(',').map(function(range){ + var range = range.split('-') + , start = parseInt(range[0], 10) + , end = parseInt(range[1], 10); + + // -500 + if (isNaN(start)) { + start = size - end; + end = size - 1; + // 500- + } else if (isNaN(end)) { + end = size - 1; + } + + // Invalid + if (isNaN(start) || isNaN(end) || start > end) valid = false; + + return { start: start, end: end }; + }); + return valid ? arr : undefined; +}; + +/** + * Parse the given Cache-Control `str`. + * + * @param {String} str + * @return {Object} + * @api public + */ + +exports.parseCacheControl = function(str){ + var directives = str.split(',') + , obj = {}; + + for(var i = 0, len = directives.length; i < len; i++) { + var parts = directives[i].split('=') + , key = parts.shift().trim() + , val = parseInt(parts.shift(), 10); + + obj[key] = isNaN(val) ? true : val; + } + + return obj; +}; + + +/** + * Convert array-like object to an `Array`. + * + * node-bench measured "16.5 times faster than Array.prototype.slice.call()" + * + * @param {Object} obj + * @return {Array} + * @api public + */ + +var toArray = exports.toArray = function(obj){ + var len = obj.length + , arr = new Array(len); + for (var i = 0; i < len; ++i) { + arr[i] = obj[i]; + } + return arr; +}; + +/** + * Retrun a random int, used by `utils.uid()` + * + * @param {Number} min + * @param {Number} max + * @return {Number} + * @api private + */ + +function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/.npmignore b/test/node_modules/express/node_modules/connect/node_modules/formidable/.npmignore new file mode 100644 index 0000000..4fbabb3 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/.npmignore @@ -0,0 +1,4 @@ +/test/tmp/ +*.upload +*.un~ +*.http diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/.travis.yml b/test/node_modules/express/node_modules/connect/node_modules/formidable/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/Makefile b/test/node_modules/express/node_modules/connect/node_modules/formidable/Makefile new file mode 100644 index 0000000..8945872 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/Makefile @@ -0,0 +1,14 @@ +SHELL := /bin/bash + +test: + @./test/run.js + +build: npm test + +npm: + npm install . + +clean: + rm test/tmp/* + +.PHONY: test clean build diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md b/test/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md new file mode 100644 index 0000000..a5ca104 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md @@ -0,0 +1,311 @@ +# Formidable + +[![Build Status](https://secure.travis-ci.org/felixge/node-formidable.png?branch=master)](http://travis-ci.org/felixge/node-formidable) + +## Purpose + +A node.js module for parsing form data, especially file uploads. + +## Current status + +This module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading +and encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from +a large variety of clients and is considered production-ready. + +## Features + +* Fast (~500mb/sec), non-buffering multipart parser +* Automatically writing file uploads to disk +* Low memory footprint +* Graceful error handling +* Very high test coverage + +## Changelog + +### v1.0.9 + +* Emit progress when content length header parsed (Tim Koschützki) +* Fix Readme syntax due to GitHub changes (goob) +* Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara) + +### v1.0.8 + +* Strip potentially unsafe characters when using `keepExtensions: true`. +* Switch to utest / urun for testing +* Add travis build + +### v1.0.7 + +* Remove file from package that was causing problems when installing on windows. (#102) +* Fix typos in Readme (Jason Davies). + +### v1.0.6 + +* Do not default to the default to the field name for file uploads where + filename="". + +### v1.0.5 + +* Support filename="" in multipart parts +* Explain unexpected end() errors in parser better + +**Note:** Starting with this version, formidable emits 'file' events for empty +file input fields. Previously those were incorrectly emitted as regular file +input fields with value = "". + +### v1.0.4 + +* Detect a good default tmp directory regardless of platform. (#88) + +### v1.0.3 + +* Fix problems with utf8 characters (#84) / semicolons in filenames (#58) +* Small performance improvements +* New test suite and fixture system + +### v1.0.2 + +* Exclude node\_modules folder from git +* Implement new `'aborted'` event +* Fix files in example folder to work with recent node versions +* Make gently a devDependency + +[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.1...v1.0.2) + +### v1.0.1 + +* Fix package.json to refer to proper main directory. (#68, Dean Landolt) + +[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.0...v1.0.1) + +### v1.0.0 + +* Add support for multipart boundaries that are quoted strings. (Jeff Craig) + +This marks the beginning of development on version 2.0 which will include +several architectural improvements. + +[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.11...v1.0.0) + +### v0.9.11 + +* Emit `'progress'` event when receiving data, regardless of parsing it. (Tim Koschützki) +* Use [W3C FileAPI Draft](http://dev.w3.org/2006/webapi/FileAPI/) properties for File class + +**Important:** The old property names of the File class will be removed in a +future release. + +[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.10...v0.9.11) + +### Older releases + +These releases were done before starting to maintain the above Changelog: + +* [v0.9.10](https://github.com/felixge/node-formidable/compare/v0.9.9...v0.9.10) +* [v0.9.9](https://github.com/felixge/node-formidable/compare/v0.9.8...v0.9.9) +* [v0.9.8](https://github.com/felixge/node-formidable/compare/v0.9.7...v0.9.8) +* [v0.9.7](https://github.com/felixge/node-formidable/compare/v0.9.6...v0.9.7) +* [v0.9.6](https://github.com/felixge/node-formidable/compare/v0.9.5...v0.9.6) +* [v0.9.5](https://github.com/felixge/node-formidable/compare/v0.9.4...v0.9.5) +* [v0.9.4](https://github.com/felixge/node-formidable/compare/v0.9.3...v0.9.4) +* [v0.9.3](https://github.com/felixge/node-formidable/compare/v0.9.2...v0.9.3) +* [v0.9.2](https://github.com/felixge/node-formidable/compare/v0.9.1...v0.9.2) +* [v0.9.1](https://github.com/felixge/node-formidable/compare/v0.9.0...v0.9.1) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.1.0](https://github.com/felixge/node-formidable/commits/v0.1.0) + +## Installation + +Via [npm](http://github.com/isaacs/npm): + + npm install formidable@latest + +Manually: + + git clone git://github.com/felixge/node-formidable.git formidable + vim my.js + # var formidable = require('./formidable'); + +Note: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library. + +## Example + +Parse an incoming file upload. + + var formidable = require('formidable'), + http = require('http'), + + util = require('util'); + + http.createServer(function(req, res) { + if (req.url == '/upload' && req.method.toLowerCase() == 'post') { + // parse a file upload + var form = new formidable.IncomingForm(); + form.parse(req, function(err, fields, files) { + res.writeHead(200, {'content-type': 'text/plain'}); + res.write('received upload:\n\n'); + res.end(util.inspect({fields: fields, files: files})); + }); + return; + } + + // show a file upload form + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + }).listen(80); + +## API + +### formidable.IncomingForm + +__new formidable.IncomingForm()__ + +Creates a new incoming form. + +__incomingForm.encoding = 'utf-8'__ + +The encoding to use for incoming form fields. + +__incomingForm.uploadDir = process.env.TMP || '/tmp' || process.cwd()__ + +The directory for placing file uploads in. You can move them later on using +`fs.rename()`. The default directory is picked at module load time depending on +the first existing directory from those listed above. + +__incomingForm.keepExtensions = false__ + +If you want the files written to `incomingForm.uploadDir` to include the extensions of the original files, set this property to `true`. + +__incomingForm.type__ + +Either 'multipart' or 'urlencoded' depending on the incoming request. + +__incomingForm.maxFieldsSize = 2 * 1024 * 1024__ + +Limits the amount of memory a field (not file) can allocate in bytes. +If this value is exceeded, an `'error'` event is emitted. The default +size is 2MB. + +__incomingForm.hash = false__ + +If you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`. + +__incomingForm.bytesReceived__ + +The amount of bytes received for this form so far. + +__incomingForm.bytesExpected__ + +The expected number of bytes in this form. + +__incomingForm.parse(request, [cb])__ + +Parses an incoming node.js `request` containing form data. If `cb` is provided, all fields an files are collected and passed to the callback: + + incomingForm.parse(req, function(err, fields, files) { + // ... + }); + +__incomingForm.onPart(part)__ + +You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events processing which would occur otherwise, making you fully responsible for handling the processing. + + incomingForm.onPart = function(part) { + part.addListener('data', function() { + // ... + }); + } + +If you want to use formidable to only handle certain parts for you, you can do so: + + incomingForm.onPart = function(part) { + if (!part.filename) { + // let formidable handle all non-file parts + incomingForm.handlePart(part); + } + } + +Check the code in this method for further inspiration. + +__Event: 'progress' (bytesReceived, bytesExpected)__ + +Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar. + +__Event: 'field' (name, value)__ + +Emitted whenever a field / value pair has been received. + +__Event: 'fileBegin' (name, file)__ + +Emitted whenever a new file is detected in the upload stream. Use this even if +you want to stream the file to somewhere else while buffering the upload on +the file system. + +__Event: 'file' (name, file)__ + +Emitted whenever a field / file pair has been received. `file` is an instance of `File`. + +__Event: 'error' (err)__ + +Emitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events. + +__Event: 'aborted'__ + +Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. In the future there will be a separate 'timeout' event (needs a change in the node core). + +__Event: 'end' ()__ + +Emitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response. + +### formidable.File + +__file.size = 0__ + +The size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` event), this property says how many bytes of the file have been written to disk yet. + +__file.path = null__ + +The path this file is being written to. You can modify this in the `'fileBegin'` event in +case you are unhappy with the way formidable generates a temporary path for your files. + +__file.name = null__ + +The name this file had according to the uploading client. + +__file.type = null__ + +The mime type of this file, according to the uploading client. + +__file.lastModifiedDate = null__ + +A date object (or `null`) containing the time this file was last written to. Mostly +here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/). + +__file.hash = null__ + +If hash calculation was set, you can read the hex digest out of this var. + +## License + +Formidable is licensed under the MIT license. + +## Ports + +* [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++ parser based on formidable + +## Credits + +* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/TODO b/test/node_modules/express/node_modules/connect/node_modules/formidable/TODO new file mode 100644 index 0000000..e1107f2 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/TODO @@ -0,0 +1,3 @@ +- Better bufferMaxSize handling approach +- Add tests for JSON parser pull request and merge it +- Implement QuerystringParser the same way as MultipartParser diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/benchmark/bench-multipart-parser.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/benchmark/bench-multipart-parser.js new file mode 100644 index 0000000..bff41f1 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/benchmark/bench-multipart-parser.js @@ -0,0 +1,70 @@ +require('../test/common'); +var multipartParser = require('../lib/multipart_parser'), + MultipartParser = multipartParser.MultipartParser, + parser = new MultipartParser(), + Buffer = require('buffer').Buffer, + boundary = '-----------------------------168072824752491622650073', + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + + +parser.initWithBoundary(boundary); +parser.onHeaderField = function() { + callbacks.headerField++; +}; + +parser.onHeaderValue = function() { + callbacks.headerValue++; +}; + +parser.onPartBegin = function() { + callbacks.partBegin++; +}; + +parser.onPartData = function() { + callbacks.partData++; +}; + +parser.onPartEnd = function() { + callbacks.partEnd++; +}; + +parser.onEnd = function() { + callbacks.end++; +}; + +var start = +new Date(), + nparsed = parser.write(buffer), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = new Buffer(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + } +}); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/example/post.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/example/post.js new file mode 100644 index 0000000..f6c15a6 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/example/post.js @@ -0,0 +1,43 @@ +require('../test/common'); +var http = require('http'), + util = require('util'), + formidable = require('formidable'), + server; + +server = http.createServer(function(req, res) { + if (req.url == '/') { + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + } else if (req.url == '/post') { + var form = new formidable.IncomingForm(), + fields = []; + + form + .on('error', function(err) { + res.writeHead(200, {'content-type': 'text/plain'}); + res.end('error:\n\n'+util.inspect(err)); + }) + .on('field', function(field, value) { + console.log(field, value); + fields.push([field, value]); + }) + .on('end', function() { + console.log('-> post done'); + res.writeHead(200, {'content-type': 'text/plain'}); + res.end('received fields:\n\n '+util.inspect(fields)); + }); + form.parse(req); + } else { + res.writeHead(404, {'content-type': 'text/plain'}); + res.end('404'); + } +}); +server.listen(TEST_PORT); + +console.log('listening on http://localhost:'+TEST_PORT+'/'); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/example/upload.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/example/upload.js new file mode 100644 index 0000000..050cdd9 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/example/upload.js @@ -0,0 +1,48 @@ +require('../test/common'); +var http = require('http'), + util = require('util'), + formidable = require('formidable'), + server; + +server = http.createServer(function(req, res) { + if (req.url == '/') { + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + } else if (req.url == '/upload') { + var form = new formidable.IncomingForm(), + files = [], + fields = []; + + form.uploadDir = TEST_TMP; + + form + .on('field', function(field, value) { + console.log(field, value); + fields.push([field, value]); + }) + .on('file', function(field, file) { + console.log(field, file); + files.push([field, file]); + }) + .on('end', function() { + console.log('-> upload done'); + res.writeHead(200, {'content-type': 'text/plain'}); + res.write('received fields:\n\n '+util.inspect(fields)); + res.write('\n\n'); + res.end('received files:\n\n '+util.inspect(files)); + }); + form.parse(req); + } else { + res.writeHead(404, {'content-type': 'text/plain'}); + res.end('404'); + } +}); +server.listen(TEST_PORT); + +console.log('listening on http://localhost:'+TEST_PORT+'/'); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/index.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/index.js new file mode 100644 index 0000000..be41032 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/formidable'); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/file.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/file.js new file mode 100644 index 0000000..dad8d5f --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/file.js @@ -0,0 +1,73 @@ +if (global.GENTLY) require = GENTLY.hijack(require); + +var util = require('./util'), + WriteStream = require('fs').WriteStream, + EventEmitter = require('events').EventEmitter, + crypto = require('crypto'); + +function File(properties) { + EventEmitter.call(this); + + this.size = 0; + this.path = null; + this.name = null; + this.type = null; + this.hash = null; + this.lastModifiedDate = null; + + this._writeStream = null; + + for (var key in properties) { + this[key] = properties[key]; + } + + if(typeof this.hash === 'string') { + this.hash = crypto.createHash(properties.hash); + } + + this._backwardsCompatibility(); +} +module.exports = File; +util.inherits(File, EventEmitter); + +// @todo Next release: Show error messages when accessing these +File.prototype._backwardsCompatibility = function() { + var self = this; + this.__defineGetter__('length', function() { + return self.size; + }); + this.__defineGetter__('filename', function() { + return self.name; + }); + this.__defineGetter__('mime', function() { + return self.type; + }); +}; + +File.prototype.open = function() { + this._writeStream = new WriteStream(this.path); +}; + +File.prototype.write = function(buffer, cb) { + var self = this; + this._writeStream.write(buffer, function() { + if(self.hash) { + self.hash.update(buffer); + } + self.lastModifiedDate = new Date(); + self.size += buffer.length; + self.emit('progress', self.size); + cb(); + }); +}; + +File.prototype.end = function(cb) { + var self = this; + this._writeStream.end(function() { + if(self.hash) { + self.hash = self.hash.digest('hex'); + } + self.emit('end'); + cb(); + }); +}; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js new file mode 100644 index 0000000..060eac2 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js @@ -0,0 +1,384 @@ +if (global.GENTLY) require = GENTLY.hijack(require); + +var fs = require('fs'); +var util = require('./util'), + path = require('path'), + File = require('./file'), + MultipartParser = require('./multipart_parser').MultipartParser, + QuerystringParser = require('./querystring_parser').QuerystringParser, + StringDecoder = require('string_decoder').StringDecoder, + EventEmitter = require('events').EventEmitter, + Stream = require('stream').Stream; + +function IncomingForm(opts) { + if (!(this instanceof IncomingForm)) return new IncomingForm; + EventEmitter.call(this); + + opts=opts||{}; + + this.error = null; + this.ended = false; + + this.maxFieldsSize = opts.maxFieldsSize || 2 * 1024 * 1024; + this.keepExtensions = opts.keepExtensions || false; + this.uploadDir = opts.uploadDir || IncomingForm.UPLOAD_DIR; + this.encoding = opts.encoding || 'utf-8'; + this.headers = null; + this.type = null; + this.hash = false; + + this.bytesReceived = null; + this.bytesExpected = null; + + this._parser = null; + this._flushing = 0; + this._fieldsSize = 0; +}; +util.inherits(IncomingForm, EventEmitter); +exports.IncomingForm = IncomingForm; + +IncomingForm.UPLOAD_DIR = (function() { + var dirs = [process.env.TMP, '/tmp', process.cwd()]; + for (var i = 0; i < dirs.length; i++) { + var dir = dirs[i]; + var isDirectory = false; + + try { + isDirectory = fs.statSync(dir).isDirectory(); + } catch (e) {} + + if (isDirectory) return dir; + } +})(); + +IncomingForm.prototype.parse = function(req, cb) { + this.pause = function() { + try { + req.pause(); + } catch (err) { + // the stream was destroyed + if (!this.ended) { + // before it was completed, crash & burn + this._error(err); + } + return false; + } + return true; + }; + + this.resume = function() { + try { + req.resume(); + } catch (err) { + // the stream was destroyed + if (!this.ended) { + // before it was completed, crash & burn + this._error(err); + } + return false; + } + + return true; + }; + + this.writeHeaders(req.headers); + + var self = this; + req + .on('error', function(err) { + self._error(err); + }) + .on('aborted', function() { + self.emit('aborted'); + }) + .on('data', function(buffer) { + self.write(buffer); + }) + .on('end', function() { + if (self.error) { + return; + } + + var err = self._parser.end(); + if (err) { + self._error(err); + } + }); + + if (cb) { + var fields = {}, files = {}; + this + .on('field', function(name, value) { + fields[name] = value; + }) + .on('file', function(name, file) { + files[name] = file; + }) + .on('error', function(err) { + cb(err, fields, files); + }) + .on('end', function() { + cb(null, fields, files); + }); + } + + return this; +}; + +IncomingForm.prototype.writeHeaders = function(headers) { + this.headers = headers; + this._parseContentLength(); + this._parseContentType(); +}; + +IncomingForm.prototype.write = function(buffer) { + if (!this._parser) { + this._error(new Error('unintialized parser')); + return; + } + + this.bytesReceived += buffer.length; + this.emit('progress', this.bytesReceived, this.bytesExpected); + + var bytesParsed = this._parser.write(buffer); + if (bytesParsed !== buffer.length) { + this._error(new Error('parser error, '+bytesParsed+' of '+buffer.length+' bytes parsed')); + } + + return bytesParsed; +}; + +IncomingForm.prototype.pause = function() { + // this does nothing, unless overwritten in IncomingForm.parse + return false; +}; + +IncomingForm.prototype.resume = function() { + // this does nothing, unless overwritten in IncomingForm.parse + return false; +}; + +IncomingForm.prototype.onPart = function(part) { + // this method can be overwritten by the user + this.handlePart(part); +}; + +IncomingForm.prototype.handlePart = function(part) { + var self = this; + + if (part.filename === undefined) { + var value = '' + , decoder = new StringDecoder(this.encoding); + + part.on('data', function(buffer) { + self._fieldsSize += buffer.length; + if (self._fieldsSize > self.maxFieldsSize) { + self._error(new Error('maxFieldsSize exceeded, received '+self._fieldsSize+' bytes of field data')); + return; + } + value += decoder.write(buffer); + }); + + part.on('end', function() { + self.emit('field', part.name, value); + }); + return; + } + + this._flushing++; + + var file = new File({ + path: this._uploadPath(part.filename), + name: part.filename, + type: part.mime, + hash: self.hash + }); + + this.emit('fileBegin', part.name, file); + + file.open(); + + part.on('data', function(buffer) { + self.pause(); + file.write(buffer, function() { + self.resume(); + }); + }); + + part.on('end', function() { + file.end(function() { + self._flushing--; + self.emit('file', part.name, file); + self._maybeEnd(); + }); + }); +}; + +IncomingForm.prototype._parseContentType = function() { + if (!this.headers['content-type']) { + this._error(new Error('bad content-type header, no content-type')); + return; + } + + if (this.headers['content-type'].match(/urlencoded/i)) { + this._initUrlencoded(); + return; + } + + if (this.headers['content-type'].match(/multipart/i)) { + var m; + if (m = this.headers['content-type'].match(/boundary=(?:"([^"]+)"|([^;]+))/i)) { + this._initMultipart(m[1] || m[2]); + } else { + this._error(new Error('bad content-type header, no multipart boundary')); + } + return; + } + + this._error(new Error('bad content-type header, unknown content-type: '+this.headers['content-type'])); +}; + +IncomingForm.prototype._error = function(err) { + if (this.error) { + return; + } + + this.error = err; + this.pause(); + this.emit('error', err); +}; + +IncomingForm.prototype._parseContentLength = function() { + if (this.headers['content-length']) { + this.bytesReceived = 0; + this.bytesExpected = parseInt(this.headers['content-length'], 10); + this.emit('progress', this.bytesReceived, this.bytesExpected); + } +}; + +IncomingForm.prototype._newParser = function() { + return new MultipartParser(); +}; + +IncomingForm.prototype._initMultipart = function(boundary) { + this.type = 'multipart'; + + var parser = new MultipartParser(), + self = this, + headerField, + headerValue, + part; + + parser.initWithBoundary(boundary); + + parser.onPartBegin = function() { + part = new Stream(); + part.readable = true; + part.headers = {}; + part.name = null; + part.filename = null; + part.mime = null; + headerField = ''; + headerValue = ''; + }; + + parser.onHeaderField = function(b, start, end) { + headerField += b.toString(self.encoding, start, end); + }; + + parser.onHeaderValue = function(b, start, end) { + headerValue += b.toString(self.encoding, start, end); + }; + + parser.onHeaderEnd = function() { + headerField = headerField.toLowerCase(); + part.headers[headerField] = headerValue; + + var m; + if (headerField == 'content-disposition') { + if (m = headerValue.match(/name="([^"]+)"/i)) { + part.name = m[1]; + } + + part.filename = self._fileName(headerValue); + } else if (headerField == 'content-type') { + part.mime = headerValue; + } + + headerField = ''; + headerValue = ''; + }; + + parser.onHeadersEnd = function() { + self.onPart(part); + }; + + parser.onPartData = function(b, start, end) { + part.emit('data', b.slice(start, end)); + }; + + parser.onPartEnd = function() { + part.emit('end'); + }; + + parser.onEnd = function() { + self.ended = true; + self._maybeEnd(); + }; + + this._parser = parser; +}; + +IncomingForm.prototype._fileName = function(headerValue) { + var m = headerValue.match(/filename="(.*?)"($|; )/i) + if (!m) return; + + var filename = m[1].substr(m[1].lastIndexOf('\\') + 1); + filename = filename.replace(/%22/g, '"'); + filename = filename.replace(/&#([\d]{4});/g, function(m, code) { + return String.fromCharCode(code); + }); + return filename; +}; + +IncomingForm.prototype._initUrlencoded = function() { + this.type = 'urlencoded'; + + var parser = new QuerystringParser() + , self = this; + + parser.onField = function(key, val) { + self.emit('field', key, val); + }; + + parser.onEnd = function() { + self.ended = true; + self._maybeEnd(); + }; + + this._parser = parser; +}; + +IncomingForm.prototype._uploadPath = function(filename) { + var name = ''; + for (var i = 0; i < 32; i++) { + name += Math.floor(Math.random() * 16).toString(16); + } + + if (this.keepExtensions) { + var ext = path.extname(filename); + ext = ext.replace(/(\.[a-z0-9]+).*/, '$1') + + name += ext; + } + + return path.join(this.uploadDir, name); +}; + +IncomingForm.prototype._maybeEnd = function() { + if (!this.ended || this._flushing) { + return; + } + + this.emit('end'); +}; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/index.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/index.js new file mode 100644 index 0000000..7a6e3e1 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/index.js @@ -0,0 +1,3 @@ +var IncomingForm = require('./incoming_form').IncomingForm; +IncomingForm.IncomingForm = IncomingForm; +module.exports = IncomingForm; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/multipart_parser.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/multipart_parser.js new file mode 100644 index 0000000..9ca567c --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/multipart_parser.js @@ -0,0 +1,312 @@ +var Buffer = require('buffer').Buffer, + s = 0, + S = + { PARSER_UNINITIALIZED: s++, + START: s++, + START_BOUNDARY: s++, + HEADER_FIELD_START: s++, + HEADER_FIELD: s++, + HEADER_VALUE_START: s++, + HEADER_VALUE: s++, + HEADER_VALUE_ALMOST_DONE: s++, + HEADERS_ALMOST_DONE: s++, + PART_DATA_START: s++, + PART_DATA: s++, + PART_END: s++, + END: s++, + }, + + f = 1, + F = + { PART_BOUNDARY: f, + LAST_BOUNDARY: f *= 2, + }, + + LF = 10, + CR = 13, + SPACE = 32, + HYPHEN = 45, + COLON = 58, + A = 97, + Z = 122, + + lower = function(c) { + return c | 0x20; + }; + +for (var s in S) { + exports[s] = S[s]; +} + +function MultipartParser() { + this.boundary = null; + this.boundaryChars = null; + this.lookbehind = null; + this.state = S.PARSER_UNINITIALIZED; + + this.index = null; + this.flags = 0; +}; +exports.MultipartParser = MultipartParser; + +MultipartParser.stateToString = function(stateNumber) { + for (var state in S) { + var number = S[state]; + if (number === stateNumber) return state; + } +}; + +MultipartParser.prototype.initWithBoundary = function(str) { + this.boundary = new Buffer(str.length+4); + this.boundary.write('\r\n--', 'ascii', 0); + this.boundary.write(str, 'ascii', 4); + this.lookbehind = new Buffer(this.boundary.length+8); + this.state = S.START; + + this.boundaryChars = {}; + for (var i = 0; i < this.boundary.length; i++) { + this.boundaryChars[this.boundary[i]] = true; + } +}; + +MultipartParser.prototype.write = function(buffer) { + var self = this, + i = 0, + len = buffer.length, + prevIndex = this.index, + index = this.index, + state = this.state, + flags = this.flags, + lookbehind = this.lookbehind, + boundary = this.boundary, + boundaryChars = this.boundaryChars, + boundaryLength = this.boundary.length, + boundaryEnd = boundaryLength - 1, + bufferLength = buffer.length, + c, + cl, + + mark = function(name) { + self[name+'Mark'] = i; + }, + clear = function(name) { + delete self[name+'Mark']; + }, + callback = function(name, buffer, start, end) { + if (start !== undefined && start === end) { + return; + } + + var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1); + if (callbackSymbol in self) { + self[callbackSymbol](buffer, start, end); + } + }, + dataCallback = function(name, clear) { + var markSymbol = name+'Mark'; + if (!(markSymbol in self)) { + return; + } + + if (!clear) { + callback(name, buffer, self[markSymbol], buffer.length); + self[markSymbol] = 0; + } else { + callback(name, buffer, self[markSymbol], i); + delete self[markSymbol]; + } + }; + + for (i = 0; i < len; i++) { + c = buffer[i]; + switch (state) { + case S.PARSER_UNINITIALIZED: + return i; + case S.START: + index = 0; + state = S.START_BOUNDARY; + case S.START_BOUNDARY: + if (index == boundary.length - 2) { + if (c != CR) { + return i; + } + index++; + break; + } else if (index - 1 == boundary.length - 2) { + if (c != LF) { + return i; + } + index = 0; + callback('partBegin'); + state = S.HEADER_FIELD_START; + break; + } + + if (c != boundary[index+2]) { + return i; + } + index++; + break; + case S.HEADER_FIELD_START: + state = S.HEADER_FIELD; + mark('headerField'); + index = 0; + case S.HEADER_FIELD: + if (c == CR) { + clear('headerField'); + state = S.HEADERS_ALMOST_DONE; + break; + } + + index++; + if (c == HYPHEN) { + break; + } + + if (c == COLON) { + if (index == 1) { + // empty header field + return i; + } + dataCallback('headerField', true); + state = S.HEADER_VALUE_START; + break; + } + + cl = lower(c); + if (cl < A || cl > Z) { + return i; + } + break; + case S.HEADER_VALUE_START: + if (c == SPACE) { + break; + } + + mark('headerValue'); + state = S.HEADER_VALUE; + case S.HEADER_VALUE: + if (c == CR) { + dataCallback('headerValue', true); + callback('headerEnd'); + state = S.HEADER_VALUE_ALMOST_DONE; + } + break; + case S.HEADER_VALUE_ALMOST_DONE: + if (c != LF) { + return i; + } + state = S.HEADER_FIELD_START; + break; + case S.HEADERS_ALMOST_DONE: + if (c != LF) { + return i; + } + + callback('headersEnd'); + state = S.PART_DATA_START; + break; + case S.PART_DATA_START: + state = S.PART_DATA + mark('partData'); + case S.PART_DATA: + prevIndex = index; + + if (index == 0) { + // boyer-moore derrived algorithm to safely skip non-boundary data + i += boundaryEnd; + while (i < bufferLength && !(buffer[i] in boundaryChars)) { + i += boundaryLength; + } + i -= boundaryEnd; + c = buffer[i]; + } + + if (index < boundary.length) { + if (boundary[index] == c) { + if (index == 0) { + dataCallback('partData', true); + } + index++; + } else { + index = 0; + } + } else if (index == boundary.length) { + index++; + if (c == CR) { + // CR = part boundary + flags |= F.PART_BOUNDARY; + } else if (c == HYPHEN) { + // HYPHEN = end boundary + flags |= F.LAST_BOUNDARY; + } else { + index = 0; + } + } else if (index - 1 == boundary.length) { + if (flags & F.PART_BOUNDARY) { + index = 0; + if (c == LF) { + // unset the PART_BOUNDARY flag + flags &= ~F.PART_BOUNDARY; + callback('partEnd'); + callback('partBegin'); + state = S.HEADER_FIELD_START; + break; + } + } else if (flags & F.LAST_BOUNDARY) { + if (c == HYPHEN) { + callback('partEnd'); + callback('end'); + state = S.END; + } else { + index = 0; + } + } else { + index = 0; + } + } + + if (index > 0) { + // when matching a possible boundary, keep a lookbehind reference + // in case it turns out to be a false lead + lookbehind[index-1] = c; + } else if (prevIndex > 0) { + // if our boundary turned out to be rubbish, the captured lookbehind + // belongs to partData + callback('partData', lookbehind, 0, prevIndex); + prevIndex = 0; + mark('partData'); + + // reconsider the current character even so it interrupted the sequence + // it could be the beginning of a new sequence + i--; + } + + break; + case S.END: + break; + default: + return i; + } + } + + dataCallback('headerField'); + dataCallback('headerValue'); + dataCallback('partData'); + + this.index = index; + this.state = state; + this.flags = flags; + + return len; +}; + +MultipartParser.prototype.end = function() { + if (this.state != S.END) { + return new Error('MultipartParser.end(): stream ended unexpectedly: ' + this.explain()); + } +}; + +MultipartParser.prototype.explain = function() { + return 'state = ' + MultipartParser.stateToString(this.state); +}; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/querystring_parser.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/querystring_parser.js new file mode 100644 index 0000000..63f109e --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/querystring_parser.js @@ -0,0 +1,25 @@ +if (global.GENTLY) require = GENTLY.hijack(require); + +// This is a buffering parser, not quite as nice as the multipart one. +// If I find time I'll rewrite this to be fully streaming as well +var querystring = require('querystring'); + +function QuerystringParser() { + this.buffer = ''; +}; +exports.QuerystringParser = QuerystringParser; + +QuerystringParser.prototype.write = function(buffer) { + this.buffer += buffer.toString('ascii'); + return buffer.length; +}; + +QuerystringParser.prototype.end = function() { + var fields = querystring.parse(this.buffer); + for (var field in fields) { + this.onField(field, fields[field]); + } + this.buffer = ''; + + this.onEnd(); +}; \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/util.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/util.js new file mode 100644 index 0000000..e9493e9 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/lib/util.js @@ -0,0 +1,6 @@ +// Backwards compatibility ... +try { + module.exports = require('util'); +} catch (e) { + module.exports = require('sys'); +} diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Makefile b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Makefile new file mode 100644 index 0000000..01f7140 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Makefile @@ -0,0 +1,4 @@ +test: + @find test/simple/test-*.js | xargs -n 1 -t node + +.PHONY: test \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Readme.md b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Readme.md new file mode 100644 index 0000000..f8f0c66 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Readme.md @@ -0,0 +1,167 @@ +# Gently + +## Purpose + +A node.js module that helps with stubbing and behavior verification. It allows you to test the most remote and nested corners of your code while keeping being fully unobtrusive. + +## Features + +* Overwrite and stub individual object functions +* Verify that all expected calls have been made in the expected order +* Restore stubbed functions to their original behavior +* Detect object / class names from obj.constructor.name and obj.toString() +* Hijack any required module function or class constructor + +## Installation + +Via [npm](http://github.com/isaacs/npm): + + npm install gently@latest + +## Example + +Make sure your dog is working properly: + + function Dog() {} + + Dog.prototype.seeCat = function() { + this.bark('whuf, whuf'); + this.run(); + } + + Dog.prototype.bark = function(bark) { + require('sys').puts(bark); + } + + var gently = new (require('gently')) + , assert = require('assert') + , dog = new Dog(); + + gently.expect(dog, 'bark', function(bark) { + assert.equal(bark, 'whuf, whuf'); + }); + gently.expect(dog, 'run'); + + dog.seeCat(); + +You can also easily test event emitters with this, for example a simple sequence of 2 events emitted by `fs.WriteStream`: + + var gently = new (require('gently')) + , stream = new (require('fs').WriteStream)('my_file.txt'); + + gently.expect(stream, 'emit', function(event) { + assert.equal(event, 'open'); + }); + + gently.expect(stream, 'emit', function(event) { + assert.equal(event, 'drain'); + }); + +For a full read world example, check out this test case: [test-incoming-form.js](http://github.com/felixge/node-formidable/blob/master/test/simple/test-incoming-form.js) (in [node-formdiable](http://github.com/felixge/node-formidable)). + +## API + +### Gently + +#### new Gently() + +Creates a new gently instance. It listens to the process `'exit'` event to make sure all expectations have been verified. + +#### gently.expect(obj, method, [[count], stubFn]) + +Creates an expectation for an objects method to be called. You can optionally specify the call `count` you are expecting, as well as `stubFn` function that will run instead of the original function. + +Returns a reference to the function that is getting overwritten. + +#### gently.expect([count], stubFn) + +Returns a function that is supposed to be executed `count` times, delegating any calls to the provided `stubFn` function. Naming your stubFn closure will help to properly diagnose errors that are being thrown: + + childProcess.exec('ls', gently.expect(function lsCallback(code) { + assert.equal(0, code); + })); + +#### gently.restore(obj, method) + +Restores an object method that has been previously overwritten using `gently.expect()`. + +#### gently.hijack(realRequire) + +Returns a new require functions that catches a reference to all required modules into `gently.hijacked`. + +To use this function, include a line like this in your `'my-module.js'`. + + if (global.GENTLY) require = GENTLY.hijack(require); + + var sys = require('sys'); + exports.hello = function() { + sys.log('world'); + }; + +Now you can write a test for the module above: + + var gently = global.GENTLY = new (require('gently')) + , myModule = require('./my-module'); + + gently.expect(gently.hijacked.sys, 'log', function(str) { + assert.equal(str, 'world'); + }); + + myModule.hello(); + +#### gently.stub(location, [exportsName]) + +Returns a stub class that will be used instead of the real class from the module at `location` with the given `exportsName`. + +This allows to test an OOP version of the previous example, where `'my-module.js'`. + + if (global.GENTLY) require = GENTLY.hijack(require); + + var World = require('./world'); + + exports.hello = function() { + var world = new World(); + world.hello(); + } + +And `world.js` looks like this: + + var sys = require('sys'); + + function World() { + + } + module.exports = World; + + World.prototype.hello = function() { + sys.log('world'); + }; + +Testing `'my-module.js'` can now easily be accomplished: + + var gently = global.GENTLY = new (require('gently')) + , WorldStub = gently.stub('./world') + , myModule = require('./my-module') + , WORLD; + + gently.expect(WorldStub, 'new', function() { + WORLD = this; + }); + + gently.expect(WORLD, 'hello'); + + myModule.hello(); + +#### gently.hijacked + +An object that holds the references to all hijacked modules. + +#### gently.verify([msg]) + +Verifies that all expectations of this gently instance have been satisfied. If not called manually, this method is called when the process `'exit'` event is fired. + +If `msg` is given, it will appear in any error that might be thrown. + +## License + +Gently is licensed under the MIT license. \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/dog.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/dog.js new file mode 100644 index 0000000..022fae0 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/dog.js @@ -0,0 +1,22 @@ +require('../test/common'); +function Dog() {} + +Dog.prototype.seeCat = function() { + this.bark('whuf, whuf'); + this.run(); +} + +Dog.prototype.bark = function(bark) { + require('sys').puts(bark); +} + +var gently = new (require('gently')) + , assert = require('assert') + , dog = new Dog(); + +gently.expect(dog, 'bark', function(bark) { + assert.equal(bark, 'whuf, whuf'); +}); +gently.expect(dog, 'run'); + +dog.seeCat(); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/event_emitter.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/event_emitter.js new file mode 100644 index 0000000..7def134 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/event_emitter.js @@ -0,0 +1,11 @@ +require('../test/common'); +var gently = new (require('gently')) + , stream = new (require('fs').WriteStream)('my_file.txt'); + +gently.expect(stream, 'emit', function(event) { + assert.equal(event, 'open'); +}); + +gently.expect(stream, 'emit', function(event) { + assert.equal(event, 'drain'); +}); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/index.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/index.js new file mode 100644 index 0000000..69122bd --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/gently'); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/gently.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/gently.js new file mode 100644 index 0000000..8af0e1e --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/gently.js @@ -0,0 +1,184 @@ +var path = require('path'); + +function Gently() { + this.expectations = []; + this.hijacked = {}; + + var self = this; + process.addListener('exit', function() { + self.verify('process exit'); + }); +}; +module.exports = Gently; + +Gently.prototype.stub = function(location, exportsName) { + function Stub() { + return Stub['new'].apply(this, arguments); + }; + + Stub['new'] = function () {}; + + var stubName = 'require('+JSON.stringify(location)+')'; + if (exportsName) { + stubName += '.'+exportsName; + } + + Stub.prototype.toString = Stub.toString = function() { + return stubName; + }; + + var exports = this.hijacked[location] || {}; + if (exportsName) { + exports[exportsName] = Stub; + } else { + exports = Stub; + } + + this.hijacked[location] = exports; + return Stub; +}; + +Gently.prototype.hijack = function(realRequire) { + var self = this; + return function(location) { + return self.hijacked[location] = (self.hijacked[location]) + ? self.hijacked[location] + : realRequire(location); + }; +}; + +Gently.prototype.expect = function(obj, method, count, stubFn) { + if (typeof obj != 'function' && typeof obj != 'object' && typeof obj != 'number') { + throw new Error + ( 'Bad 1st argument for gently.expect(), ' + + 'object, function, or number expected, got: '+(typeof obj) + ); + } else if (typeof obj == 'function' && (typeof method != 'string')) { + // expect(stubFn) interface + stubFn = obj; + obj = null; + method = null; + count = 1; + } else if (typeof method == 'function') { + // expect(count, stubFn) interface + count = obj; + stubFn = method; + obj = null; + method = null; + } else if (typeof count == 'function') { + // expect(obj, method, stubFn) interface + stubFn = count; + count = 1; + } else if (count === undefined) { + // expect(obj, method) interface + count = 1; + } + + var name = this._name(obj, method, stubFn); + this.expectations.push({obj: obj, method: method, stubFn: stubFn, name: name, count: count}); + + var self = this; + function delegate() { + return self._stubFn(this, obj, method, name, Array.prototype.slice.call(arguments)); + } + + if (!obj) { + return delegate; + } + + var original = (obj[method]) + ? obj[method]._original || obj[method] + : undefined; + + obj[method] = delegate; + return obj[method]._original = original; +}; + +Gently.prototype.restore = function(obj, method) { + if (!obj[method] || !obj[method]._original) { + throw new Error(this._name(obj, method)+' is not gently stubbed'); + } + obj[method] = obj[method]._original; +}; + +Gently.prototype.verify = function(msg) { + if (!this.expectations.length) { + return; + } + + var validExpectations = []; + for (var i = 0, l = this.expectations.length; i < l; i++) { + var expectation = this.expectations[i]; + + if (expectation.count > 0) { + validExpectations.push(expectation); + } + } + + this.expectations = []; // reset so that no duplicate verification attempts are made + + if (!validExpectations.length) { + return; + } + + var expectation = validExpectations[0]; + + throw new Error + ( 'Expected call to '+expectation.name+' did not happen' + + ( (msg) + ? ' ('+msg+')' + : '' + ) + ); +}; + +Gently.prototype._stubFn = function(self, obj, method, name, args) { + var expectation = this.expectations[0], obj, method; + + if (!expectation) { + throw new Error('Unexpected call to '+name+', no call was expected'); + } + + if (expectation.obj !== obj || expectation.method !== method) { + throw new Error('Unexpected call to '+name+', expected call to '+ expectation.name); + } + + expectation.count -= 1; + if (expectation.count === 0) { + this.expectations.shift(); + + // autorestore original if its not a closure + // and no more expectations on that object + var has_more_expectations = this.expectations.reduce(function (memo, expectation) { + return memo || (expectation.obj === obj && expectation.method === method); + }, false); + if (obj !== null && method !== null && !has_more_expectations) { + if (typeof obj[method]._original !== 'undefined') { + obj[method] = obj[method]._original; + delete obj[method]._original; + } else { + delete obj[method]; + } + } + } + + if (expectation.stubFn) { + return expectation.stubFn.apply(self, args); + } +}; + +Gently.prototype._name = function(obj, method, stubFn) { + if (obj) { + var objectName = obj.toString(); + if (objectName == '[object Object]' && obj.constructor.name) { + objectName = '['+obj.constructor.name+']'; + } + return (objectName)+'.'+method+'()'; + } + + if (stubFn.name) { + return stubFn.name+'()'; + } + + return '>> '+stubFn.toString()+' <<'; +}; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/index.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/index.js new file mode 100644 index 0000000..64c1977 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/index.js @@ -0,0 +1 @@ +module.exports = require('./gently'); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/package.json b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/package.json new file mode 100644 index 0000000..9c1b7a0 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/package.json @@ -0,0 +1,14 @@ +{ + "name": "gently", + "version": "0.9.2", + "directories": { + "lib": "./lib/gently" + }, + "main": "./lib/gently/index", + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": "*" + }, + "optionalDependencies": {} +} diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/common.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/common.js new file mode 100644 index 0000000..978b5c5 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/common.js @@ -0,0 +1,8 @@ +var path = require('path') + , sys = require('sys'); + +require.paths.unshift(path.dirname(__dirname)+'/lib'); + +global.puts = sys.puts; +global.p = function() {sys.error(sys.inspect.apply(null, arguments))};; +global.assert = require('assert'); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/simple/test-gently.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/simple/test-gently.js new file mode 100644 index 0000000..4f8fe2d --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/simple/test-gently.js @@ -0,0 +1,348 @@ +require('../common'); +var Gently = require('gently') + , gently; + +function test(test) { + process.removeAllListeners('exit'); + gently = new Gently(); + test(); +} + +test(function constructor() { + assert.deepEqual(gently.expectations, []); + assert.deepEqual(gently.hijacked, {}); + assert.equal(gently.constructor.name, 'Gently'); +}); + +test(function expectBadArgs() { + var BAD_ARG = 'oh no'; + try { + gently.expect(BAD_ARG); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Bad 1st argument for gently.expect(), object, function, or number expected, got: '+(typeof BAD_ARG)); + } +}); + +test(function expectObjMethod() { + var OBJ = {}, NAME = 'foobar'; + OBJ.foo = function(x) { + return x; + }; + + gently._name = function() { + return NAME; + }; + + var original = OBJ.foo + , stubFn = function() {}; + + (function testAddOne() { + assert.strictEqual(gently.expect(OBJ, 'foo', stubFn), original); + + assert.equal(gently.expectations.length, 1); + var expectation = gently.expectations[0]; + assert.strictEqual(expectation.obj, OBJ); + assert.strictEqual(expectation.method, 'foo'); + assert.strictEqual(expectation.stubFn, stubFn); + assert.strictEqual(expectation.name, NAME); + assert.strictEqual(OBJ.foo._original, original); + })(); + + (function testAddTwo() { + gently.expect(OBJ, 'foo', 2, stubFn); + assert.equal(gently.expectations.length, 2); + assert.strictEqual(OBJ.foo._original, original); + })(); + + (function testAddOneWithoutMock() { + gently.expect(OBJ, 'foo'); + assert.equal(gently.expectations.length, 3); + })(); + + var stubFnCalled = 0, SELF = {}; + gently._stubFn = function(self, obj, method, name, args) { + stubFnCalled++; + assert.strictEqual(self, SELF); + assert.strictEqual(obj, OBJ); + assert.strictEqual(method, 'foo'); + assert.strictEqual(name, NAME); + assert.deepEqual(args, [1, 2]); + return 23; + }; + assert.equal(OBJ.foo.apply(SELF, [1, 2]), 23); + assert.equal(stubFnCalled, 1); +}); + +test(function expectClosure() { + var NAME = 'MY CLOSURE'; + function closureFn() {}; + + gently._name = function() { + return NAME; + }; + + var fn = gently.expect(closureFn); + assert.equal(gently.expectations.length, 1); + var expectation = gently.expectations[0]; + assert.strictEqual(expectation.obj, null); + assert.strictEqual(expectation.method, null); + assert.strictEqual(expectation.stubFn, closureFn); + assert.strictEqual(expectation.name, NAME); + + var stubFnCalled = 0, SELF = {}; + gently._stubFn = function(self, obj, method, name, args) { + stubFnCalled++; + assert.strictEqual(self, SELF); + assert.strictEqual(obj, null); + assert.strictEqual(method, null); + assert.strictEqual(name, NAME); + assert.deepEqual(args, [1, 2]); + return 23; + }; + assert.equal(fn.apply(SELF, [1, 2]), 23); + assert.equal(stubFnCalled, 1); +}); + +test(function expectClosureCount() { + var stubFnCalled = 0; + function closureFn() {stubFnCalled++}; + + var fn = gently.expect(2, closureFn); + assert.equal(gently.expectations.length, 1); + fn(); + assert.equal(gently.expectations.length, 1); + fn(); + assert.equal(stubFnCalled, 2); +}); + +test(function restore() { + var OBJ = {}, NAME = '[my object].myFn()'; + OBJ.foo = function(x) { + return x; + }; + + gently._name = function() { + return NAME; + }; + + var original = OBJ.foo; + gently.expect(OBJ, 'foo'); + gently.restore(OBJ, 'foo'); + assert.strictEqual(OBJ.foo, original); + + (function testError() { + try { + gently.restore(OBJ, 'foo'); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, NAME+' is not gently stubbed'); + } + })(); +}); + +test(function _stubFn() { + var OBJ1 = {toString: function() {return '[OBJ 1]'}} + , OBJ2 = {toString: function() {return '[OBJ 2]'}, foo: function () {return 'bar';}} + , SELF = {}; + + gently.expect(OBJ1, 'foo', function(x) { + assert.strictEqual(this, SELF); + return x * 2; + }); + + assert.equal(gently._stubFn(SELF, OBJ1, 'foo', 'dummy_name', [5]), 10); + + (function testAutorestore() { + assert.equal(OBJ2.foo(), 'bar'); + + gently.expect(OBJ2, 'foo', function() { + return 'stubbed foo'; + }); + + gently.expect(OBJ2, 'foo', function() { + return "didn't restore yet"; + }); + + assert.equal(gently._stubFn(SELF, OBJ2, 'foo', 'dummy_name', []), 'stubbed foo'); + assert.equal(gently._stubFn(SELF, OBJ2, 'foo', 'dummy_name', []), "didn't restore yet"); + assert.equal(OBJ2.foo(), 'bar'); + assert.deepEqual(gently.expectations, []); + })(); + + (function testNoMoreCallExpected() { + try { + gently._stubFn(SELF, OBJ1, 'foo', 'dummy_name', [5]); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Unexpected call to dummy_name, no call was expected'); + } + })(); + + (function testDifferentCallExpected() { + gently.expect(OBJ2, 'bar'); + try { + gently._stubFn(SELF, OBJ1, 'foo', 'dummy_name', [5]); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Unexpected call to dummy_name, expected call to '+gently._name(OBJ2, 'bar')); + } + + assert.equal(gently.expectations.length, 1); + })(); + + (function testNoMockCallback() { + OBJ2.bar(); + assert.equal(gently.expectations.length, 0); + })(); +}); + +test(function stub() { + var LOCATION = './my_class'; + + (function testRegular() { + var Stub = gently.stub(LOCATION); + assert.ok(Stub instanceof Function); + assert.strictEqual(gently.hijacked[LOCATION], Stub); + assert.ok(Stub['new'] instanceof Function); + assert.equal(Stub.toString(), 'require('+JSON.stringify(LOCATION)+')'); + + (function testConstructor() { + var newCalled = 0 + , STUB + , ARGS = ['foo', 'bar']; + + Stub['new'] = function(a, b) { + assert.equal(a, ARGS[0]); + assert.equal(b, ARGS[1]); + newCalled++; + STUB = this; + }; + + var stub = new Stub(ARGS[0], ARGS[1]); + assert.strictEqual(stub, STUB); + assert.equal(newCalled, 1); + assert.equal(stub.toString(), 'require('+JSON.stringify(LOCATION)+')'); + })(); + + (function testUseReturnValueAsInstance() { + var R = {}; + + Stub['new'] = function() { + return R; + }; + + var stub = new Stub(); + assert.strictEqual(stub, R); + + })(); + })(); + + var EXPORTS_NAME = 'MyClass'; + test(function testExportsName() { + var Stub = gently.stub(LOCATION, EXPORTS_NAME); + assert.strictEqual(gently.hijacked[LOCATION][EXPORTS_NAME], Stub); + assert.equal(Stub.toString(), 'require('+JSON.stringify(LOCATION)+').'+EXPORTS_NAME); + + (function testConstructor() { + var stub = new Stub(); + assert.equal(Stub.toString(), 'require('+JSON.stringify(LOCATION)+').'+EXPORTS_NAME); + })(); + }); +}); + +test(function hijack() { + var LOCATION = './foo' + , REQUIRE_CALLS = 0 + , EXPORTS = {} + , REQUIRE = function() { + REQUIRE_CALLS++; + return EXPORTS; + }; + + var hijackedRequire = gently.hijack(REQUIRE); + hijackedRequire(LOCATION); + assert.strictEqual(gently.hijacked[LOCATION], EXPORTS); + + assert.equal(REQUIRE_CALLS, 1); + + // make sure we are caching the hijacked module + hijackedRequire(LOCATION); + assert.equal(REQUIRE_CALLS, 1); +}); + +test(function verify() { + var OBJ = {toString: function() {return '[OBJ]'}}; + gently.verify(); + + gently.expect(OBJ, 'foo'); + try { + gently.verify(); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Expected call to [OBJ].foo() did not happen'); + } + + try { + gently.verify('foo'); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Expected call to [OBJ].foo() did not happen (foo)'); + } +}); + +test(function processExit() { + var verifyCalled = 0; + gently.verify = function(msg) { + verifyCalled++; + assert.equal(msg, 'process exit'); + }; + + process.emit('exit'); + assert.equal(verifyCalled, 1); +}); + +test(function _name() { + (function testNamedClass() { + function Foo() {}; + var foo = new Foo(); + assert.equal(gently._name(foo, 'bar'), '[Foo].bar()'); + })(); + + (function testToStringPreference() { + function Foo() {}; + Foo.prototype.toString = function() { + return '[Superman 123]'; + }; + var foo = new Foo(); + assert.equal(gently._name(foo, 'bar'), '[Superman 123].bar()'); + })(); + + (function testUnamedClass() { + var Foo = function() {}; + var foo = new Foo(); + assert.equal(gently._name(foo, 'bar'), foo.toString()+'.bar()'); + })(); + + (function testNamedClosure() { + function myClosure() {}; + assert.equal(gently._name(null, null, myClosure), myClosure.name+'()'); + })(); + + (function testUnamedClosure() { + var myClosure = function() {2+2 == 5}; + assert.equal(gently._name(null, null, myClosure), '>> '+myClosure.toString()+' <<'); + })(); +}); + +test(function verifyExpectNone() { + var OBJ = {toString: function() {return '[OBJ]'}}; + gently.verify(); + + gently.expect(OBJ, 'foo', 0); + try { + gently.verify(); + } catch (e) { + assert.fail('Exception should not have been thrown'); + } +}); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/package.json b/test/node_modules/express/node_modules/connect/node_modules/formidable/package.json new file mode 100644 index 0000000..bddf510 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/package.json @@ -0,0 +1,28 @@ +{ + "name": "formidable", + "version": "1.0.11", + "dependencies": {}, + "devDependencies": { + "gently": "0.8.0", + "findit": "0.1.1", + "hashish": "0.0.4", + "urun": "0.0.4", + "utest": "0.0.3" + }, + "directories": { + "lib": "./lib" + }, + "main": "./lib/index", + "scripts": { + "test": "make test" + }, + "engines": { + "node": "*" + }, + "optionalDependencies": {}, + "readme": "# Formidable\n\n[![Build Status](https://secure.travis-ci.org/felixge/node-formidable.png?branch=master)](http://travis-ci.org/felixge/node-formidable)\n\n## Purpose\n\nA node.js module for parsing form data, especially file uploads.\n\n## Current status\n\nThis module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading\nand encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from\na large variety of clients and is considered production-ready.\n\n## Features\n\n* Fast (~500mb/sec), non-buffering multipart parser\n* Automatically writing file uploads to disk\n* Low memory footprint\n* Graceful error handling\n* Very high test coverage\n\n## Changelog\n\n### v1.0.9\n\n* Emit progress when content length header parsed (Tim Koschützki)\n* Fix Readme syntax due to GitHub changes (goob)\n* Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara)\n\n### v1.0.8\n\n* Strip potentially unsafe characters when using `keepExtensions: true`.\n* Switch to utest / urun for testing\n* Add travis build\n\n### v1.0.7\n\n* Remove file from package that was causing problems when installing on windows. (#102)\n* Fix typos in Readme (Jason Davies).\n\n### v1.0.6\n\n* Do not default to the default to the field name for file uploads where\n filename=\"\".\n\n### v1.0.5\n\n* Support filename=\"\" in multipart parts\n* Explain unexpected end() errors in parser better\n\n**Note:** Starting with this version, formidable emits 'file' events for empty\nfile input fields. Previously those were incorrectly emitted as regular file\ninput fields with value = \"\".\n\n### v1.0.4\n\n* Detect a good default tmp directory regardless of platform. (#88)\n\n### v1.0.3\n\n* Fix problems with utf8 characters (#84) / semicolons in filenames (#58)\n* Small performance improvements\n* New test suite and fixture system\n\n### v1.0.2\n\n* Exclude node\\_modules folder from git\n* Implement new `'aborted'` event\n* Fix files in example folder to work with recent node versions\n* Make gently a devDependency\n\n[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.1...v1.0.2)\n\n### v1.0.1\n\n* Fix package.json to refer to proper main directory. (#68, Dean Landolt)\n\n[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.0...v1.0.1)\n\n### v1.0.0\n\n* Add support for multipart boundaries that are quoted strings. (Jeff Craig)\n\nThis marks the beginning of development on version 2.0 which will include\nseveral architectural improvements.\n\n[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.11...v1.0.0)\n\n### v0.9.11\n\n* Emit `'progress'` event when receiving data, regardless of parsing it. (Tim Koschützki)\n* Use [W3C FileAPI Draft](http://dev.w3.org/2006/webapi/FileAPI/) properties for File class\n\n**Important:** The old property names of the File class will be removed in a\nfuture release.\n\n[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.10...v0.9.11)\n\n### Older releases\n\nThese releases were done before starting to maintain the above Changelog:\n\n* [v0.9.10](https://github.com/felixge/node-formidable/compare/v0.9.9...v0.9.10)\n* [v0.9.9](https://github.com/felixge/node-formidable/compare/v0.9.8...v0.9.9)\n* [v0.9.8](https://github.com/felixge/node-formidable/compare/v0.9.7...v0.9.8)\n* [v0.9.7](https://github.com/felixge/node-formidable/compare/v0.9.6...v0.9.7)\n* [v0.9.6](https://github.com/felixge/node-formidable/compare/v0.9.5...v0.9.6)\n* [v0.9.5](https://github.com/felixge/node-formidable/compare/v0.9.4...v0.9.5)\n* [v0.9.4](https://github.com/felixge/node-formidable/compare/v0.9.3...v0.9.4)\n* [v0.9.3](https://github.com/felixge/node-formidable/compare/v0.9.2...v0.9.3)\n* [v0.9.2](https://github.com/felixge/node-formidable/compare/v0.9.1...v0.9.2)\n* [v0.9.1](https://github.com/felixge/node-formidable/compare/v0.9.0...v0.9.1)\n* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)\n* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)\n* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)\n* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)\n* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)\n* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)\n* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)\n* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)\n* [v0.1.0](https://github.com/felixge/node-formidable/commits/v0.1.0)\n\n## Installation\n\nVia [npm](http://github.com/isaacs/npm):\n\n npm install formidable@latest\n\nManually:\n\n git clone git://github.com/felixge/node-formidable.git formidable\n vim my.js\n # var formidable = require('./formidable');\n\nNote: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library.\n\n## Example\n\nParse an incoming file upload.\n\n var formidable = require('formidable'),\n http = require('http'),\n\n util = require('util');\n\n http.createServer(function(req, res) {\n if (req.url == '/upload' && req.method.toLowerCase() == 'post') {\n // parse a file upload\n var form = new formidable.IncomingForm();\n form.parse(req, function(err, fields, files) {\n res.writeHead(200, {'content-type': 'text/plain'});\n res.write('received upload:\\n\\n');\n res.end(util.inspect({fields: fields, files: files}));\n });\n return;\n }\n\n // show a file upload form\n res.writeHead(200, {'content-type': 'text/html'});\n res.end(\n '
    '+\n '
    '+\n '
    '+\n ''+\n '
    '\n );\n }).listen(80);\n\n## API\n\n### formidable.IncomingForm\n\n__new formidable.IncomingForm()__\n\nCreates a new incoming form.\n\n__incomingForm.encoding = 'utf-8'__\n\nThe encoding to use for incoming form fields.\n\n__incomingForm.uploadDir = process.env.TMP || '/tmp' || process.cwd()__\n\nThe directory for placing file uploads in. You can move them later on using\n`fs.rename()`. The default directory is picked at module load time depending on\nthe first existing directory from those listed above.\n\n__incomingForm.keepExtensions = false__\n\nIf you want the files written to `incomingForm.uploadDir` to include the extensions of the original files, set this property to `true`.\n\n__incomingForm.type__\n\nEither 'multipart' or 'urlencoded' depending on the incoming request.\n\n__incomingForm.maxFieldsSize = 2 * 1024 * 1024__\n\nLimits the amount of memory a field (not file) can allocate in bytes.\nIf this value is exceeded, an `'error'` event is emitted. The default\nsize is 2MB.\n\n__incomingForm.hash = false__\n\nIf you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`.\n\n__incomingForm.bytesReceived__\n\nThe amount of bytes received for this form so far.\n\n__incomingForm.bytesExpected__\n\nThe expected number of bytes in this form.\n\n__incomingForm.parse(request, [cb])__\n\nParses an incoming node.js `request` containing form data. If `cb` is provided, all fields an files are collected and passed to the callback:\n\n incomingForm.parse(req, function(err, fields, files) {\n // ...\n });\n\n__incomingForm.onPart(part)__\n\nYou may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events processing which would occur otherwise, making you fully responsible for handling the processing.\n\n incomingForm.onPart = function(part) {\n part.addListener('data', function() {\n // ...\n });\n }\n\nIf you want to use formidable to only handle certain parts for you, you can do so:\n\n incomingForm.onPart = function(part) {\n if (!part.filename) {\n // let formidable handle all non-file parts\n incomingForm.handlePart(part);\n }\n }\n\nCheck the code in this method for further inspiration.\n\n__Event: 'progress' (bytesReceived, bytesExpected)__\n\nEmitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.\n\n__Event: 'field' (name, value)__\n\nEmitted whenever a field / value pair has been received.\n\n__Event: 'fileBegin' (name, file)__\n\nEmitted whenever a new file is detected in the upload stream. Use this even if\nyou want to stream the file to somewhere else while buffering the upload on\nthe file system.\n\n__Event: 'file' (name, file)__\n\nEmitted whenever a field / file pair has been received. `file` is an instance of `File`.\n\n__Event: 'error' (err)__\n\nEmitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events.\n\n__Event: 'aborted'__\n\nEmitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. In the future there will be a separate 'timeout' event (needs a change in the node core).\n\n__Event: 'end' ()__\n\nEmitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.\n\n### formidable.File\n\n__file.size = 0__\n\nThe size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` event), this property says how many bytes of the file have been written to disk yet.\n\n__file.path = null__\n\nThe path this file is being written to. You can modify this in the `'fileBegin'` event in\ncase you are unhappy with the way formidable generates a temporary path for your files.\n\n__file.name = null__\n\nThe name this file had according to the uploading client.\n\n__file.type = null__\n\nThe mime type of this file, according to the uploading client.\n\n__file.lastModifiedDate = null__\n\nA date object (or `null`) containing the time this file was last written to. Mostly\nhere for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).\n\n__file.hash = null__\n\nIf hash calculation was set, you can read the hex digest out of this var.\n\n## License\n\nFormidable is licensed under the MIT license.\n\n## Ports\n\n* [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++ parser based on formidable\n\n## Credits\n\n* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js\n", + "readmeFilename": "Readme.md", + "_id": "formidable@1.0.11", + "description": "[![Build Status](https://secure.travis-ci.org/felixge/node-formidable.png?branch=master)](http://travis-ci.org/felixge/node-formidable)", + "_from": "formidable@1.0.x" +} diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/common.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/common.js new file mode 100644 index 0000000..eb432ad --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/common.js @@ -0,0 +1,19 @@ +var mysql = require('..'); +var path = require('path'); + +var root = path.join(__dirname, '../'); +exports.dir = { + root : root, + lib : root + '/lib', + fixture : root + '/test/fixture', + tmp : root + '/test/tmp', +}; + +exports.port = 13532; + +exports.formidable = require('..'); +exports.assert = require('assert'); + +exports.require = function(lib) { + return require(exports.dir.lib + '/' + lib); +}; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/funkyfilename.txt b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/funkyfilename.txt new file mode 100644 index 0000000..e7a4785 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/funkyfilename.txt @@ -0,0 +1 @@ +I am a text file with a funky name! diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/plain.txt b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/plain.txt new file mode 100644 index 0000000..9b6903e --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/plain.txt @@ -0,0 +1 @@ +I am a plain text file diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/info.md b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/info.md new file mode 100644 index 0000000..3c9dbe3 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/info.md @@ -0,0 +1,3 @@ +* Opera does not allow submitting this file, it shows a warning to the + user that the file could not be found instead. Tested in 9.8, 11.51 on OSX. + Reported to Opera on 08.09.2011 (tracking email DSK-346009@bugs.opera.com). diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/no-filename.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/no-filename.js new file mode 100644 index 0000000..0bae449 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/no-filename.js @@ -0,0 +1,3 @@ +module.exports['generic.http'] = [ + {type: 'file', name: 'upload', filename: '', fixture: 'plain.txt'}, +]; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/special-chars-in-filename.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/special-chars-in-filename.js new file mode 100644 index 0000000..eb76fdc --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/special-chars-in-filename.js @@ -0,0 +1,21 @@ +var properFilename = 'funkyfilename.txt'; + +function expect(filename) { + return [ + {type: 'field', name: 'title', value: 'Weird filename'}, + {type: 'file', name: 'upload', filename: filename, fixture: properFilename}, + ]; +}; + +var webkit = " ? % * | \" < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"; +var ffOrIe = " ? % * | \" < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"; + +module.exports = { + 'osx-chrome-13.http' : expect(webkit), + 'osx-firefox-3.6.http' : expect(ffOrIe), + 'osx-safari-5.http' : expect(webkit), + 'xp-chrome-12.http' : expect(webkit), + 'xp-ie-7.http' : expect(ffOrIe), + 'xp-ie-8.http' : expect(ffOrIe), + 'xp-safari-5.http' : expect(webkit), +}; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multipart.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multipart.js new file mode 100644 index 0000000..a476169 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multipart.js @@ -0,0 +1,72 @@ +exports['rfc1867'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--\r\n', + parts: + [ { headers: { + 'content-disposition': 'form-data; name="field1"', + }, + data: 'Joe Blow\r\nalmost tricked you!', + }, + { headers: { + 'content-disposition': 'form-data; name="pics"; filename="file1.txt"', + 'Content-Type': 'text/plain', + }, + data: '... contents of file1.txt ...\r', + } + ] + }; + +exports['noTrailing\r\n'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--', + parts: + [ { headers: { + 'content-disposition': 'form-data; name="field1"', + }, + data: 'Joe Blow\r\nalmost tricked you!', + }, + { headers: { + 'content-disposition': 'form-data; name="pics"; filename="file1.txt"', + 'Content-Type': 'text/plain', + }, + data: '... contents of file1.txt ...\r', + } + ] + }; + +exports['emptyHeader'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + ': foo\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--\r\n', + expectError: true, + }; diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-fixtures.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-fixtures.js new file mode 100644 index 0000000..66ad259 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-fixtures.js @@ -0,0 +1,89 @@ +var hashish = require('hashish'); +var fs = require('fs'); +var findit = require('findit'); +var path = require('path'); +var http = require('http'); +var net = require('net'); +var assert = require('assert'); + +var common = require('../common'); +var formidable = common.formidable; + +var server = http.createServer(); +server.listen(common.port, findFixtures); + +function findFixtures() { + var fixtures = []; + findit + .sync(common.dir.fixture + '/js') + .forEach(function(jsPath) { + if (!/\.js$/.test(jsPath)) return; + + var group = path.basename(jsPath, '.js'); + hashish.forEach(require(jsPath), function(fixture, name) { + fixtures.push({ + name : group + '/' + name, + fixture : fixture, + }); + }); + }); + + testNext(fixtures); +} + +function testNext(fixtures) { + var fixture = fixtures.shift(); + if (!fixture) return server.close(); + + var name = fixture.name; + var fixture = fixture.fixture; + + uploadFixture(name, function(err, parts) { + if (err) throw err; + + fixture.forEach(function(expectedPart, i) { + var parsedPart = parts[i]; + assert.equal(parsedPart.type, expectedPart.type); + assert.equal(parsedPart.name, expectedPart.name); + + if (parsedPart.type === 'file') { + var filename = parsedPart.value.name; + assert.equal(filename, expectedPart.filename); + } + }); + + testNext(fixtures); + }); +}; + +function uploadFixture(name, cb) { + server.once('request', function(req, res) { + var form = new formidable.IncomingForm(); + form.uploadDir = common.dir.tmp; + form.parse(req); + + function callback() { + var realCallback = cb; + cb = function() {}; + realCallback.apply(null, arguments); + } + + var parts = []; + form + .on('error', callback) + .on('fileBegin', function(name, value) { + parts.push({type: 'file', name: name, value: value}); + }) + .on('field', function(name, value) { + parts.push({type: 'field', name: name, value: value}); + }) + .on('end', function() { + callback(null, parts); + }); + }); + + var socket = net.createConnection(common.port); + var file = fs.createReadStream(common.dir.fixture + '/http/' + name); + + file.pipe(socket); +} diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/common.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/common.js new file mode 100644 index 0000000..2b98598 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/common.js @@ -0,0 +1,24 @@ +var path = require('path'), + fs = require('fs'); + +try { + global.Gently = require('gently'); +} catch (e) { + throw new Error('this test suite requires node-gently'); +} + +exports.lib = path.join(__dirname, '../../lib'); + +global.GENTLY = new Gently(); + +global.assert = require('assert'); +global.TEST_PORT = 13532; +global.TEST_FIXTURES = path.join(__dirname, '../fixture'); +global.TEST_TMP = path.join(__dirname, '../tmp'); + +// Stupid new feature in node that complains about gently attaching too many +// listeners to process 'exit'. This is a workaround until I can think of a +// better way to deal with this. +if (process.setMaxListeners) { + process.setMaxListeners(10000); +} diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/integration/test-multipart-parser.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/integration/test-multipart-parser.js new file mode 100644 index 0000000..75232aa --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/integration/test-multipart-parser.js @@ -0,0 +1,80 @@ +var common = require('../common'); +var CHUNK_LENGTH = 10, + multipartParser = require(common.lib + '/multipart_parser'), + MultipartParser = multipartParser.MultipartParser, + parser = new MultipartParser(), + fixtures = require(TEST_FIXTURES + '/multipart'), + Buffer = require('buffer').Buffer; + +Object.keys(fixtures).forEach(function(name) { + var fixture = fixtures[name], + buffer = new Buffer(Buffer.byteLength(fixture.raw, 'binary')), + offset = 0, + chunk, + nparsed, + + parts = [], + part = null, + headerField, + headerValue, + endCalled = ''; + + parser.initWithBoundary(fixture.boundary); + parser.onPartBegin = function() { + part = {headers: {}, data: ''}; + parts.push(part); + headerField = ''; + headerValue = ''; + }; + + parser.onHeaderField = function(b, start, end) { + headerField += b.toString('ascii', start, end); + }; + + parser.onHeaderValue = function(b, start, end) { + headerValue += b.toString('ascii', start, end); + } + + parser.onHeaderEnd = function() { + part.headers[headerField] = headerValue; + headerField = ''; + headerValue = ''; + }; + + parser.onPartData = function(b, start, end) { + var str = b.toString('ascii', start, end); + part.data += b.slice(start, end); + } + + parser.onEnd = function() { + endCalled = true; + } + + buffer.write(fixture.raw, 'binary', 0); + + while (offset < buffer.length) { + if (offset + CHUNK_LENGTH < buffer.length) { + chunk = buffer.slice(offset, offset+CHUNK_LENGTH); + } else { + chunk = buffer.slice(offset, buffer.length); + } + offset = offset + CHUNK_LENGTH; + + nparsed = parser.write(chunk); + if (nparsed != chunk.length) { + if (fixture.expectError) { + return; + } + puts('-- ERROR --'); + p(chunk.toString('ascii')); + throw new Error(chunk.length+' bytes written, but only '+nparsed+' bytes parsed!'); + } + } + + if (fixture.expectError) { + throw new Error('expected parse error did not happen'); + } + + assert.ok(endCalled); + assert.deepEqual(parts, fixture.parts); +}); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-file.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-file.js new file mode 100644 index 0000000..52ceedb --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-file.js @@ -0,0 +1,104 @@ +var common = require('../common'); +var WriteStreamStub = GENTLY.stub('fs', 'WriteStream'); + +var File = require(common.lib + '/file'), + EventEmitter = require('events').EventEmitter, + file, + gently; + +function test(test) { + gently = new Gently(); + file = new File(); + test(); + gently.verify(test.name); +} + +test(function constructor() { + assert.ok(file instanceof EventEmitter); + assert.strictEqual(file.size, 0); + assert.strictEqual(file.path, null); + assert.strictEqual(file.name, null); + assert.strictEqual(file.type, null); + assert.strictEqual(file.lastModifiedDate, null); + + assert.strictEqual(file._writeStream, null); + + (function testSetProperties() { + var file2 = new File({foo: 'bar'}); + assert.equal(file2.foo, 'bar'); + })(); +}); + +test(function open() { + var WRITE_STREAM; + file.path = '/foo'; + + gently.expect(WriteStreamStub, 'new', function (path) { + WRITE_STREAM = this; + assert.strictEqual(path, file.path); + }); + + file.open(); + assert.strictEqual(file._writeStream, WRITE_STREAM); +}); + +test(function write() { + var BUFFER = {length: 10}, + CB_STUB, + CB = function() { + CB_STUB.apply(this, arguments); + }; + + file._writeStream = {}; + + gently.expect(file._writeStream, 'write', function (buffer, cb) { + assert.strictEqual(buffer, BUFFER); + + gently.expect(file, 'emit', function (event, bytesWritten) { + assert.ok(file.lastModifiedDate instanceof Date); + assert.equal(event, 'progress'); + assert.equal(bytesWritten, file.size); + }); + + CB_STUB = gently.expect(function writeCb() { + assert.equal(file.size, 10); + }); + + cb(); + + gently.expect(file, 'emit', function (event, bytesWritten) { + assert.equal(event, 'progress'); + assert.equal(bytesWritten, file.size); + }); + + CB_STUB = gently.expect(function writeCb() { + assert.equal(file.size, 20); + }); + + cb(); + }); + + file.write(BUFFER, CB); +}); + +test(function end() { + var CB_STUB, + CB = function() { + CB_STUB.apply(this, arguments); + }; + + file._writeStream = {}; + + gently.expect(file._writeStream, 'end', function (cb) { + gently.expect(file, 'emit', function (event) { + assert.equal(event, 'end'); + }); + + CB_STUB = gently.expect(function endCb() { + }); + + cb(); + }); + + file.end(CB); +}); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js new file mode 100644 index 0000000..84de439 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js @@ -0,0 +1,727 @@ +var common = require('../common'); +var MultipartParserStub = GENTLY.stub('./multipart_parser', 'MultipartParser'), + QuerystringParserStub = GENTLY.stub('./querystring_parser', 'QuerystringParser'), + EventEmitterStub = GENTLY.stub('events', 'EventEmitter'), + StreamStub = GENTLY.stub('stream', 'Stream'), + FileStub = GENTLY.stub('./file'); + +var formidable = require(common.lib + '/index'), + IncomingForm = formidable.IncomingForm, + events = require('events'), + fs = require('fs'), + path = require('path'), + Buffer = require('buffer').Buffer, + fixtures = require(TEST_FIXTURES + '/multipart'), + form, + gently; + +function test(test) { + gently = new Gently(); + gently.expect(EventEmitterStub, 'call'); + form = new IncomingForm(); + test(); + gently.verify(test.name); +} + +test(function constructor() { + assert.strictEqual(form.error, null); + assert.strictEqual(form.ended, false); + assert.strictEqual(form.type, null); + assert.strictEqual(form.headers, null); + assert.strictEqual(form.keepExtensions, false); + assert.strictEqual(form.uploadDir, '/tmp'); + assert.strictEqual(form.encoding, 'utf-8'); + assert.strictEqual(form.bytesReceived, null); + assert.strictEqual(form.bytesExpected, null); + assert.strictEqual(form.maxFieldsSize, 2 * 1024 * 1024); + assert.strictEqual(form._parser, null); + assert.strictEqual(form._flushing, 0); + assert.strictEqual(form._fieldsSize, 0); + assert.ok(form instanceof EventEmitterStub); + assert.equal(form.constructor.name, 'IncomingForm'); + + (function testSimpleConstructor() { + gently.expect(EventEmitterStub, 'call'); + var form = IncomingForm(); + assert.ok(form instanceof IncomingForm); + })(); + + (function testSimpleConstructorShortcut() { + gently.expect(EventEmitterStub, 'call'); + var form = formidable(); + assert.ok(form instanceof IncomingForm); + })(); +}); + +test(function parse() { + var REQ = {headers: {}} + , emit = {}; + + gently.expect(form, 'writeHeaders', function(headers) { + assert.strictEqual(headers, REQ.headers); + }); + + var events = ['error', 'aborted', 'data', 'end']; + gently.expect(REQ, 'on', events.length, function(event, fn) { + assert.equal(event, events.shift()); + emit[event] = fn; + return this; + }); + + form.parse(REQ); + + (function testPause() { + gently.expect(REQ, 'pause'); + assert.strictEqual(form.pause(), true); + })(); + + (function testPauseCriticalException() { + form.ended = false; + + var ERR = new Error('dasdsa'); + gently.expect(REQ, 'pause', function() { + throw ERR; + }); + + gently.expect(form, '_error', function(err) { + assert.strictEqual(err, ERR); + }); + + assert.strictEqual(form.pause(), false); + })(); + + (function testPauseHarmlessException() { + form.ended = true; + + var ERR = new Error('dasdsa'); + gently.expect(REQ, 'pause', function() { + throw ERR; + }); + + assert.strictEqual(form.pause(), false); + })(); + + (function testResume() { + gently.expect(REQ, 'resume'); + assert.strictEqual(form.resume(), true); + })(); + + (function testResumeCriticalException() { + form.ended = false; + + var ERR = new Error('dasdsa'); + gently.expect(REQ, 'resume', function() { + throw ERR; + }); + + gently.expect(form, '_error', function(err) { + assert.strictEqual(err, ERR); + }); + + assert.strictEqual(form.resume(), false); + })(); + + (function testResumeHarmlessException() { + form.ended = true; + + var ERR = new Error('dasdsa'); + gently.expect(REQ, 'resume', function() { + throw ERR; + }); + + assert.strictEqual(form.resume(), false); + })(); + + (function testEmitError() { + var ERR = new Error('something bad happened'); + gently.expect(form, '_error',function(err) { + assert.strictEqual(err, ERR); + }); + emit.error(ERR); + })(); + + (function testEmitAborted() { + gently.expect(form, 'emit',function(event) { + assert.equal(event, 'aborted'); + }); + + emit.aborted(); + })(); + + + (function testEmitData() { + var BUFFER = [1, 2, 3]; + gently.expect(form, 'write', function(buffer) { + assert.strictEqual(buffer, BUFFER); + }); + emit.data(BUFFER); + })(); + + (function testEmitEnd() { + form._parser = {}; + + (function testWithError() { + var ERR = new Error('haha'); + gently.expect(form._parser, 'end', function() { + return ERR; + }); + + gently.expect(form, '_error', function(err) { + assert.strictEqual(err, ERR); + }); + + emit.end(); + })(); + + (function testWithoutError() { + gently.expect(form._parser, 'end'); + emit.end(); + })(); + + (function testAfterError() { + form.error = true; + emit.end(); + })(); + })(); + + (function testWithCallback() { + gently.expect(EventEmitterStub, 'call'); + var form = new IncomingForm(), + REQ = {headers: {}}, + parseCalled = 0; + + gently.expect(form, 'writeHeaders'); + gently.expect(REQ, 'on', 4, function() { + return this; + }); + + gently.expect(form, 'on', 4, function(event, fn) { + if (event == 'field') { + fn('field1', 'foo'); + fn('field1', 'bar'); + fn('field2', 'nice'); + } + + if (event == 'file') { + fn('file1', '1'); + fn('file1', '2'); + fn('file2', '3'); + } + + if (event == 'end') { + fn(); + } + return this; + }); + + form.parse(REQ, gently.expect(function parseCbOk(err, fields, files) { + assert.deepEqual(fields, {field1: 'bar', field2: 'nice'}); + assert.deepEqual(files, {file1: '2', file2: '3'}); + })); + + gently.expect(form, 'writeHeaders'); + gently.expect(REQ, 'on', 4, function() { + return this; + }); + + var ERR = new Error('test'); + gently.expect(form, 'on', 3, function(event, fn) { + if (event == 'field') { + fn('foo', 'bar'); + } + + if (event == 'error') { + fn(ERR); + gently.expect(form, 'on'); + } + return this; + }); + + form.parse(REQ, gently.expect(function parseCbErr(err, fields, files) { + assert.strictEqual(err, ERR); + assert.deepEqual(fields, {foo: 'bar'}); + })); + })(); +}); + +test(function pause() { + assert.strictEqual(form.pause(), false); +}); + +test(function resume() { + assert.strictEqual(form.resume(), false); +}); + + +test(function writeHeaders() { + var HEADERS = {}; + gently.expect(form, '_parseContentLength'); + gently.expect(form, '_parseContentType'); + + form.writeHeaders(HEADERS); + assert.strictEqual(form.headers, HEADERS); +}); + +test(function write() { + var parser = {}, + BUFFER = [1, 2, 3]; + + form._parser = parser; + form.bytesExpected = 523423; + + (function testBasic() { + gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) { + assert.equal(event, 'progress'); + assert.equal(bytesReceived, BUFFER.length); + assert.equal(bytesExpected, form.bytesExpected); + }); + + gently.expect(parser, 'write', function(buffer) { + assert.strictEqual(buffer, BUFFER); + return buffer.length; + }); + + assert.equal(form.write(BUFFER), BUFFER.length); + assert.equal(form.bytesReceived, BUFFER.length); + })(); + + (function testParserError() { + gently.expect(form, 'emit'); + + gently.expect(parser, 'write', function(buffer) { + assert.strictEqual(buffer, BUFFER); + return buffer.length - 1; + }); + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/parser error/i)); + }); + + assert.equal(form.write(BUFFER), BUFFER.length - 1); + assert.equal(form.bytesReceived, BUFFER.length + BUFFER.length); + })(); + + (function testUninitialized() { + delete form._parser; + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/unintialized parser/i)); + }); + form.write(BUFFER); + })(); +}); + +test(function parseContentType() { + var HEADERS = {}; + + form.headers = {'content-type': 'application/x-www-form-urlencoded'}; + gently.expect(form, '_initUrlencoded'); + form._parseContentType(); + + // accept anything that has 'urlencoded' in it + form.headers = {'content-type': 'broken-client/urlencoded-stupid'}; + gently.expect(form, '_initUrlencoded'); + form._parseContentType(); + + var BOUNDARY = '---------------------------57814261102167618332366269'; + form.headers = {'content-type': 'multipart/form-data; boundary='+BOUNDARY}; + + gently.expect(form, '_initMultipart', function(boundary) { + assert.equal(boundary, BOUNDARY); + }); + form._parseContentType(); + + (function testQuotedBoundary() { + form.headers = {'content-type': 'multipart/form-data; boundary="' + BOUNDARY + '"'}; + + gently.expect(form, '_initMultipart', function(boundary) { + assert.equal(boundary, BOUNDARY); + }); + form._parseContentType(); + })(); + + (function testNoBoundary() { + form.headers = {'content-type': 'multipart/form-data'}; + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/no multipart boundary/i)); + }); + form._parseContentType(); + })(); + + (function testNoContentType() { + form.headers = {}; + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/no content-type/i)); + }); + form._parseContentType(); + })(); + + (function testUnknownContentType() { + form.headers = {'content-type': 'invalid'}; + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/unknown content-type/i)); + }); + form._parseContentType(); + })(); +}); + +test(function parseContentLength() { + var HEADERS = {}; + + form.headers = {}; + form._parseContentLength(); + assert.strictEqual(form.bytesReceived, null); + assert.strictEqual(form.bytesExpected, null); + + form.headers['content-length'] = '8'; + gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) { + assert.equal(event, 'progress'); + assert.equal(bytesReceived, 0); + assert.equal(bytesExpected, 8); + }); + form._parseContentLength(); + assert.strictEqual(form.bytesReceived, 0); + assert.strictEqual(form.bytesExpected, 8); + + // JS can be evil, lets make sure we are not + form.headers['content-length'] = '08'; + gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) { + assert.equal(event, 'progress'); + assert.equal(bytesReceived, 0); + assert.equal(bytesExpected, 8); + }); + form._parseContentLength(); + assert.strictEqual(form.bytesExpected, 8); +}); + +test(function _initMultipart() { + var BOUNDARY = '123', + PARSER; + + gently.expect(MultipartParserStub, 'new', function() { + PARSER = this; + }); + + gently.expect(MultipartParserStub.prototype, 'initWithBoundary', function(boundary) { + assert.equal(boundary, BOUNDARY); + }); + + form._initMultipart(BOUNDARY); + assert.equal(form.type, 'multipart'); + assert.strictEqual(form._parser, PARSER); + + (function testRegularField() { + var PART; + gently.expect(StreamStub, 'new', function() { + PART = this; + }); + + gently.expect(form, 'onPart', function(part) { + assert.strictEqual(part, PART); + assert.deepEqual + ( part.headers + , { 'content-disposition': 'form-data; name="field1"' + , 'foo': 'bar' + } + ); + assert.equal(part.name, 'field1'); + + var strings = ['hello', ' world']; + gently.expect(part, 'emit', 2, function(event, b) { + assert.equal(event, 'data'); + assert.equal(b.toString(), strings.shift()); + }); + + gently.expect(part, 'emit', function(event, b) { + assert.equal(event, 'end'); + }); + }); + + PARSER.onPartBegin(); + PARSER.onHeaderField(new Buffer('content-disposition'), 0, 10); + PARSER.onHeaderField(new Buffer('content-disposition'), 10, 19); + PARSER.onHeaderValue(new Buffer('form-data; name="field1"'), 0, 14); + PARSER.onHeaderValue(new Buffer('form-data; name="field1"'), 14, 24); + PARSER.onHeaderEnd(); + PARSER.onHeaderField(new Buffer('foo'), 0, 3); + PARSER.onHeaderValue(new Buffer('bar'), 0, 3); + PARSER.onHeaderEnd(); + PARSER.onHeadersEnd(); + PARSER.onPartData(new Buffer('hello world'), 0, 5); + PARSER.onPartData(new Buffer('hello world'), 5, 11); + PARSER.onPartEnd(); + })(); + + (function testFileField() { + var PART; + gently.expect(StreamStub, 'new', function() { + PART = this; + }); + + gently.expect(form, 'onPart', function(part) { + assert.deepEqual + ( part.headers + , { 'content-disposition': 'form-data; name="field2"; filename="C:\\Documents and Settings\\IE\\Must\\Die\\Sun"et.jpg"' + , 'content-type': 'text/plain' + } + ); + assert.equal(part.name, 'field2'); + assert.equal(part.filename, 'Sun"et.jpg'); + assert.equal(part.mime, 'text/plain'); + + gently.expect(part, 'emit', function(event, b) { + assert.equal(event, 'data'); + assert.equal(b.toString(), '... contents of file1.txt ...'); + }); + + gently.expect(part, 'emit', function(event, b) { + assert.equal(event, 'end'); + }); + }); + + PARSER.onPartBegin(); + PARSER.onHeaderField(new Buffer('content-disposition'), 0, 19); + PARSER.onHeaderValue(new Buffer('form-data; name="field2"; filename="C:\\Documents and Settings\\IE\\Must\\Die\\Sun"et.jpg"'), 0, 85); + PARSER.onHeaderEnd(); + PARSER.onHeaderField(new Buffer('Content-Type'), 0, 12); + PARSER.onHeaderValue(new Buffer('text/plain'), 0, 10); + PARSER.onHeaderEnd(); + PARSER.onHeadersEnd(); + PARSER.onPartData(new Buffer('... contents of file1.txt ...'), 0, 29); + PARSER.onPartEnd(); + })(); + + (function testEnd() { + gently.expect(form, '_maybeEnd'); + PARSER.onEnd(); + assert.ok(form.ended); + })(); +}); + +test(function _fileName() { + // TODO + return; +}); + +test(function _initUrlencoded() { + var PARSER; + + gently.expect(QuerystringParserStub, 'new', function() { + PARSER = this; + }); + + form._initUrlencoded(); + assert.equal(form.type, 'urlencoded'); + assert.strictEqual(form._parser, PARSER); + + (function testOnField() { + var KEY = 'KEY', VAL = 'VAL'; + gently.expect(form, 'emit', function(field, key, val) { + assert.equal(field, 'field'); + assert.equal(key, KEY); + assert.equal(val, VAL); + }); + + PARSER.onField(KEY, VAL); + })(); + + (function testOnEnd() { + gently.expect(form, '_maybeEnd'); + + PARSER.onEnd(); + assert.equal(form.ended, true); + })(); +}); + +test(function _error() { + var ERR = new Error('bla'); + + gently.expect(form, 'pause'); + gently.expect(form, 'emit', function(event, err) { + assert.equal(event, 'error'); + assert.strictEqual(err, ERR); + }); + + form._error(ERR); + assert.strictEqual(form.error, ERR); + + // make sure _error only does its thing once + form._error(ERR); +}); + +test(function onPart() { + var PART = {}; + gently.expect(form, 'handlePart', function(part) { + assert.strictEqual(part, PART); + }); + + form.onPart(PART); +}); + +test(function handlePart() { + (function testUtf8Field() { + var PART = new events.EventEmitter(); + PART.name = 'my_field'; + + gently.expect(form, 'emit', function(event, field, value) { + assert.equal(event, 'field'); + assert.equal(field, 'my_field'); + assert.equal(value, 'hello world: €'); + }); + + form.handlePart(PART); + PART.emit('data', new Buffer('hello')); + PART.emit('data', new Buffer(' world: ')); + PART.emit('data', new Buffer([0xE2])); + PART.emit('data', new Buffer([0x82, 0xAC])); + PART.emit('end'); + })(); + + (function testBinaryField() { + var PART = new events.EventEmitter(); + PART.name = 'my_field2'; + + gently.expect(form, 'emit', function(event, field, value) { + assert.equal(event, 'field'); + assert.equal(field, 'my_field2'); + assert.equal(value, 'hello world: '+new Buffer([0xE2, 0x82, 0xAC]).toString('binary')); + }); + + form.encoding = 'binary'; + form.handlePart(PART); + PART.emit('data', new Buffer('hello')); + PART.emit('data', new Buffer(' world: ')); + PART.emit('data', new Buffer([0xE2])); + PART.emit('data', new Buffer([0x82, 0xAC])); + PART.emit('end'); + })(); + + (function testFieldSize() { + form.maxFieldsSize = 8; + var PART = new events.EventEmitter(); + PART.name = 'my_field'; + + gently.expect(form, '_error', function(err) { + assert.equal(err.message, 'maxFieldsSize exceeded, received 9 bytes of field data'); + }); + + form.handlePart(PART); + form._fieldsSize = 1; + PART.emit('data', new Buffer(7)); + PART.emit('data', new Buffer(1)); + })(); + + (function testFilePart() { + var PART = new events.EventEmitter(), + FILE = new events.EventEmitter(), + PATH = '/foo/bar'; + + PART.name = 'my_file'; + PART.filename = 'sweet.txt'; + PART.mime = 'sweet.txt'; + + gently.expect(form, '_uploadPath', function(filename) { + assert.equal(filename, PART.filename); + return PATH; + }); + + gently.expect(FileStub, 'new', function(properties) { + assert.equal(properties.path, PATH); + assert.equal(properties.name, PART.filename); + assert.equal(properties.type, PART.mime); + FILE = this; + + gently.expect(form, 'emit', function (event, field, file) { + assert.equal(event, 'fileBegin'); + assert.strictEqual(field, PART.name); + assert.strictEqual(file, FILE); + }); + + gently.expect(FILE, 'open'); + }); + + form.handlePart(PART); + assert.equal(form._flushing, 1); + + var BUFFER; + gently.expect(form, 'pause'); + gently.expect(FILE, 'write', function(buffer, cb) { + assert.strictEqual(buffer, BUFFER); + gently.expect(form, 'resume'); + // @todo handle cb(new Err) + cb(); + }); + + PART.emit('data', BUFFER = new Buffer('test')); + + gently.expect(FILE, 'end', function(cb) { + gently.expect(form, 'emit', function(event, field, file) { + assert.equal(event, 'file'); + assert.strictEqual(file, FILE); + }); + + gently.expect(form, '_maybeEnd'); + + cb(); + assert.equal(form._flushing, 0); + }); + + PART.emit('end'); + })(); +}); + +test(function _uploadPath() { + (function testUniqueId() { + var UUID_A, UUID_B; + gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, uuid) { + assert.equal(uploadDir, form.uploadDir); + UUID_A = uuid; + }); + form._uploadPath(); + + gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, uuid) { + UUID_B = uuid; + }); + form._uploadPath(); + + assert.notEqual(UUID_A, UUID_B); + })(); + + (function testFileExtension() { + form.keepExtensions = true; + var FILENAME = 'foo.jpg', + EXT = '.bar'; + + gently.expect(GENTLY.hijacked.path, 'extname', function(filename) { + assert.equal(filename, FILENAME); + gently.restore(path, 'extname'); + + return EXT; + }); + + gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, name) { + assert.equal(path.extname(name), EXT); + }); + form._uploadPath(FILENAME); + })(); +}); + +test(function _maybeEnd() { + gently.expect(form, 'emit', 0); + form._maybeEnd(); + + form.ended = true; + form._flushing = 1; + form._maybeEnd(); + + gently.expect(form, 'emit', function(event) { + assert.equal(event, 'end'); + }); + + form.ended = true; + form._flushing = 0; + form._maybeEnd(); +}); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-multipart-parser.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-multipart-parser.js new file mode 100644 index 0000000..d8dc968 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-multipart-parser.js @@ -0,0 +1,50 @@ +var common = require('../common'); +var multipartParser = require(common.lib + '/multipart_parser'), + MultipartParser = multipartParser.MultipartParser, + events = require('events'), + Buffer = require('buffer').Buffer, + parser; + +function test(test) { + parser = new MultipartParser(); + test(); +} + +test(function constructor() { + assert.equal(parser.boundary, null); + assert.equal(parser.state, 0); + assert.equal(parser.flags, 0); + assert.equal(parser.boundaryChars, null); + assert.equal(parser.index, null); + assert.equal(parser.lookbehind, null); + assert.equal(parser.constructor.name, 'MultipartParser'); +}); + +test(function initWithBoundary() { + var boundary = 'abc'; + parser.initWithBoundary(boundary); + assert.deepEqual(Array.prototype.slice.call(parser.boundary), [13, 10, 45, 45, 97, 98, 99]); + assert.equal(parser.state, multipartParser.START); + + assert.deepEqual(parser.boundaryChars, {10: true, 13: true, 45: true, 97: true, 98: true, 99: true}); +}); + +test(function parserError() { + var boundary = 'abc', + buffer = new Buffer(5); + + parser.initWithBoundary(boundary); + buffer.write('--ad', 'ascii', 0); + assert.equal(parser.write(buffer), 3); +}); + +test(function end() { + (function testError() { + assert.equal(parser.end().message, 'MultipartParser.end(): stream ended unexpectedly: ' + parser.explain()); + })(); + + (function testRegular() { + parser.state = multipartParser.END; + assert.strictEqual(parser.end(), undefined); + })(); +}); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-querystring-parser.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-querystring-parser.js new file mode 100644 index 0000000..54d3e2d --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-querystring-parser.js @@ -0,0 +1,45 @@ +var common = require('../common'); +var QuerystringParser = require(common.lib + '/querystring_parser').QuerystringParser, + Buffer = require('buffer').Buffer, + gently, + parser; + +function test(test) { + gently = new Gently(); + parser = new QuerystringParser(); + test(); + gently.verify(test.name); +} + +test(function constructor() { + assert.equal(parser.buffer, ''); + assert.equal(parser.constructor.name, 'QuerystringParser'); +}); + +test(function write() { + var a = new Buffer('a=1'); + assert.equal(parser.write(a), a.length); + + var b = new Buffer('&b=2'); + parser.write(b); + assert.equal(parser.buffer, a + b); +}); + +test(function end() { + var FIELDS = {a: ['b', {c: 'd'}], e: 'f'}; + + gently.expect(GENTLY.hijacked.querystring, 'parse', function(str) { + assert.equal(str, parser.buffer); + return FIELDS; + }); + + gently.expect(parser, 'onField', Object.keys(FIELDS).length, function(key, val) { + assert.deepEqual(FIELDS[key], val); + }); + + gently.expect(parser, 'onEnd'); + + parser.buffer = 'my buffer'; + parser.end(); + assert.equal(parser.buffer, ''); +}); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/system/test-multi-video-upload.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/system/test-multi-video-upload.js new file mode 100644 index 0000000..479e46d --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/system/test-multi-video-upload.js @@ -0,0 +1,75 @@ +var common = require('../common'); +var BOUNDARY = '---------------------------10102754414578508781458777923', + FIXTURE = TEST_FIXTURES+'/multi_video.upload', + fs = require('fs'), + util = require(common.lib + '/util'), + http = require('http'), + formidable = require(common.lib + '/index'), + server = http.createServer(); + +server.on('request', function(req, res) { + var form = new formidable.IncomingForm(), + uploads = {}; + + form.uploadDir = TEST_TMP; + form.hash = 'sha1'; + form.parse(req); + + form + .on('fileBegin', function(field, file) { + assert.equal(field, 'upload'); + + var tracker = {file: file, progress: [], ended: false}; + uploads[file.filename] = tracker; + file + .on('progress', function(bytesReceived) { + tracker.progress.push(bytesReceived); + assert.equal(bytesReceived, file.length); + }) + .on('end', function() { + tracker.ended = true; + }); + }) + .on('field', function(field, value) { + assert.equal(field, 'title'); + assert.equal(value, ''); + }) + .on('file', function(field, file) { + assert.equal(field, 'upload'); + assert.strictEqual(uploads[file.filename].file, file); + }) + .on('end', function() { + assert.ok(uploads['shortest_video.flv']); + assert.ok(uploads['shortest_video.flv'].ended); + assert.ok(uploads['shortest_video.flv'].progress.length > 3); + assert.equal(uploads['shortest_video.flv'].file.hash, 'd6a17616c7143d1b1438ceeef6836d1a09186b3a'); + assert.equal(uploads['shortest_video.flv'].progress.slice(-1), uploads['shortest_video.flv'].file.length); + assert.ok(uploads['shortest_video.mp4']); + assert.ok(uploads['shortest_video.mp4'].ended); + assert.ok(uploads['shortest_video.mp4'].progress.length > 3); + assert.equal(uploads['shortest_video.mp4'].file.hash, '937dfd4db263f4887ceae19341dcc8d63bcd557f'); + + server.close(); + res.writeHead(200); + res.end('good'); + }); +}); + +server.listen(TEST_PORT, function() { + var client = http.createClient(TEST_PORT), + stat = fs.statSync(FIXTURE), + headers = { + 'content-type': 'multipart/form-data; boundary='+BOUNDARY, + 'content-length': stat.size, + } + request = client.request('POST', '/', headers), + fixture = new fs.ReadStream(FIXTURE); + + fixture + .on('data', function(b) { + request.write(b); + }) + .on('end', function() { + request.end(); + }); +}); diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/run.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/run.js new file mode 100644 index 0000000..50b2361 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/run.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('urun')(__dirname) diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-incoming-form.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-incoming-form.js new file mode 100644 index 0000000..fe2ac1c --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-incoming-form.js @@ -0,0 +1,63 @@ +var common = require('../common'); +var test = require('utest'); +var assert = common.assert; +var IncomingForm = common.require('incoming_form').IncomingForm; +var path = require('path'); + +var form; +test('IncomingForm', { + before: function() { + form = new IncomingForm(); + }, + + '#_fileName with regular characters': function() { + var filename = 'foo.txt'; + assert.equal(form._fileName(makeHeader(filename)), 'foo.txt'); + }, + + '#_fileName with unescaped quote': function() { + var filename = 'my".txt'; + assert.equal(form._fileName(makeHeader(filename)), 'my".txt'); + }, + + '#_fileName with escaped quote': function() { + var filename = 'my%22.txt'; + assert.equal(form._fileName(makeHeader(filename)), 'my".txt'); + }, + + '#_fileName with bad quote and additional sub-header': function() { + var filename = 'my".txt'; + var header = makeHeader(filename) + '; foo="bar"'; + assert.equal(form._fileName(header), filename); + }, + + '#_fileName with semicolon': function() { + var filename = 'my;.txt'; + assert.equal(form._fileName(makeHeader(filename)), 'my;.txt'); + }, + + '#_fileName with utf8 character': function() { + var filename = 'my☃.txt'; + assert.equal(form._fileName(makeHeader(filename)), 'my☃.txt'); + }, + + '#_uploadPath strips harmful characters from extension when keepExtensions': function() { + form.keepExtensions = true; + + var ext = path.extname(form._uploadPath('fine.jpg?foo=bar')); + assert.equal(ext, '.jpg'); + + var ext = path.extname(form._uploadPath('fine?foo=bar')); + assert.equal(ext, ''); + + var ext = path.extname(form._uploadPath('super.cr2+dsad')); + assert.equal(ext, '.cr2'); + + var ext = path.extname(form._uploadPath('super.bar')); + assert.equal(ext, '.bar'); + }, +}); + +function makeHeader(filename) { + return 'Content-Disposition: form-data; name="upload"; filename="' + filename + '"'; +} diff --git a/test/node_modules/express/node_modules/connect/node_modules/formidable/tool/record.js b/test/node_modules/express/node_modules/connect/node_modules/formidable/tool/record.js new file mode 100644 index 0000000..9f1cef8 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/node_modules/formidable/tool/record.js @@ -0,0 +1,47 @@ +var http = require('http'); +var fs = require('fs'); +var connections = 0; + +var server = http.createServer(function(req, res) { + var socket = req.socket; + console.log('Request: %s %s -> %s', req.method, req.url, socket.filename); + + req.on('end', function() { + if (req.url !== '/') { + res.end(JSON.stringify({ + method: req.method, + url: req.url, + filename: socket.filename, + })); + return; + } + + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + }); +}); + +server.on('connection', function(socket) { + connections++; + + socket.id = connections; + socket.filename = 'connection-' + socket.id + '.http'; + socket.file = fs.createWriteStream(socket.filename); + socket.pipe(socket.file); + + console.log('--> %s', socket.filename); + socket.on('close', function() { + console.log('<-- %s', socket.filename); + }); +}); + +var port = process.env.PORT || 8080; +server.listen(port, function() { + console.log('Recording connections on port %s', port); +}); diff --git a/test/node_modules/express/node_modules/connect/package.json b/test/node_modules/express/node_modules/connect/package.json new file mode 100644 index 0000000..72a7abd --- /dev/null +++ b/test/node_modules/express/node_modules/connect/package.json @@ -0,0 +1,42 @@ +{ + "name": "connect", + "version": "1.9.2", + "description": "High performance middleware framework", + "keywords": [ + "framework", + "web", + "middleware", + "connect", + "rack" + ], + "repository": { + "type": "git", + "url": "git://github.com/senchalabs/connect" + }, + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "dependencies": { + "qs": ">= 0.4.0", + "mime": ">= 0.0.1", + "formidable": "1.0.x" + }, + "devDependencies": { + "expresso": "0.9.2", + "koala": "0.1.2", + "less": "1.1.1", + "sass": "0.5.0", + "markdown": "0.2.1", + "ejs": "0.4.3", + "should": "0.3.2" + }, + "publishConfig": { + "tag": "1.8" + }, + "main": "index", + "_id": "connect@1.9.2", + "readme": "ERROR: No README.md file found!", + "_from": "connect@1.x" +} diff --git a/test/node_modules/express/node_modules/connect/test.js b/test/node_modules/express/node_modules/connect/test.js new file mode 100644 index 0000000..106ddd1 --- /dev/null +++ b/test/node_modules/express/node_modules/connect/test.js @@ -0,0 +1,15 @@ + +/** + * Module dependencies. + */ + +var connect = require('./'); + +var app = connect() + .use(connect.logger('dev')) + .use(function(req, res){ + var body = Array(3222).join('hey'); + res.setHeader('Content-Length', body.length); + res.end(body); + }) + .listen(3000); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/mime/LICENSE b/test/node_modules/express/node_modules/mime/LICENSE new file mode 100644 index 0000000..451fc45 --- /dev/null +++ b/test/node_modules/express/node_modules/mime/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Benjamin Thomas, Robert Kieffer + +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/test/node_modules/express/node_modules/mime/README.md b/test/node_modules/express/node_modules/mime/README.md new file mode 100644 index 0000000..a157de1 --- /dev/null +++ b/test/node_modules/express/node_modules/mime/README.md @@ -0,0 +1,50 @@ +# mime + +Support for mapping between file extensions and MIME types. This module uses the latest version of the Apache "mime.types" file (maps over 620 types to 800+ extensions). It is also trivially easy to add your own types and extensions, should you need to do that. + +## Install + +Install with [npm](http://github.com/isaacs/npm): + + npm install mime + +## API - Queries + +### mime.lookup(path) +Get the mime type associated with a file. This is method is case-insensitive. Everything in path up to and including the last '/' or '.' is ignored, so you can pass it paths, filenames, or extensions, like so: + + var mime = require('mime'); + + mime.lookup('/path/to/file.txt'); // => 'text/plain' + mime.lookup('file.txt'); // => 'text/plain' + mime.lookup('.txt'); // => 'text/plain' + mime.lookup('htm'); // => 'text/html' + +### mime.extension(type) - lookup the default extension for type + + mime.extension('text/html'); // => 'html' + mime.extension('application/octet-stream'); // => 'bin' + +### mime.charsets.lookup() - map mime-type to charset + + mime.charsets.lookup('text/plain'); // => 'UTF-8' + +(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.) + +## API - Customizing + +The following APIs allow you to add your own type mappings within your project. If you feel a type should be included as part of node-mime, see [requesting new types](https://github.com/bentomas/node-mime/wiki/Requesting-New-Types). +### mime.define() - Add custom mime/extension mappings + + mime.define({ + 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'], + 'application/x-my-type': ['x-mt', 'x-mtt'], + // etc ... + }); + + mime.lookup('x-sft'); // => 'text/x-some-format' + mime.extension('text/x-some-format'); // => 'x-sf' + +### mime.load(filepath) - Load mappings from an Apache ".types" format file + + mime.load('./my_project.types'); diff --git a/test/node_modules/express/node_modules/mime/mime.js b/test/node_modules/express/node_modules/mime/mime.js new file mode 100644 index 0000000..5fac753 --- /dev/null +++ b/test/node_modules/express/node_modules/mime/mime.js @@ -0,0 +1,92 @@ +var path = require('path'), + fs = require('fs'); + +var mime = module.exports = { + /** Map of extension to mime type */ + types: {}, + + /** Map of mime type to extension */ + extensions :{}, + + /** + * Define mimetype -> extension mappings. Each key is a mime-type that maps + * to an array of extensions associated with the type. The first extension is + * used as the default extension for the type. + * + * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']}); + * + * @param map (Object) type definitions + */ + define: function(map) { + for (var type in map) { + var exts = map[type]; + + for (var i = 0; i < exts.length; i++) { + mime.types[exts[i]] = type; + } + + // Default extension is the first one we encounter + if (!mime.extensions[type]) { + mime.extensions[type] = exts[0]; + } + } + }, + + /** + * Load an Apache2-style ".types" file + * + * This may be called multiple times (it's expected). Where files declare + * overlapping types/extensions, the last file wins. + * + * @param file (String) path of file to load. + */ + load: function(file) { + // Read file and split into lines + var map = {}, + content = fs.readFileSync(file, 'ascii'), + lines = content.split(/[\r\n]+/); + + lines.forEach(function(line, lineno) { + // Clean up whitespace/comments, and split into fields + var fields = line.replace(/\s*#.*|^\s*|\s*$/g, '').split(/\s+/); + map[fields.shift()] = fields; + }); + + mime.define(map); + }, + + /** + * Lookup a mime type based on extension + */ + lookup: function(path, fallback) { + var ext = path.replace(/.*[\.\/]/, '').toLowerCase(); + return mime.types[ext] || fallback || mime.default_type; + }, + + /** + * Return file extension associated with a mime type + */ + extension: function(mimeType) { + return mime.extensions[mimeType]; + }, + + /** + * Lookup a charset based on mime type. + */ + charsets: { + lookup: function (mimeType, fallback) { + // Assume text types are utf8. Modify mime logic as needed. + return (/^text\//).test(mimeType) ? 'UTF-8' : fallback; + } + } +}; + +// Load our local copy of +// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +mime.load(path.join(__dirname, 'types/mime.types')); + +// Overlay enhancements submitted by the node.js community +mime.load(path.join(__dirname, 'types/node.types')); + +// Set the default type +mime.default_type = mime.types.bin; diff --git a/test/node_modules/express/node_modules/mime/package.json b/test/node_modules/express/node_modules/mime/package.json new file mode 100644 index 0000000..b41b3ea --- /dev/null +++ b/test/node_modules/express/node_modules/mime/package.json @@ -0,0 +1,34 @@ +{ + "author": { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + }, + "contributors": [ + { + "name": "Benjamin Thomas", + "email": "benjamin@benjaminthomas.org", + "url": "http://github.com/bentomas" + } + ], + "dependencies": {}, + "description": "A comprehensive library for mime-type mapping", + "devDependencies": { + "async_testing": "" + }, + "keywords": [ + "util", + "mime" + ], + "main": "mime.js", + "name": "mime", + "repository": { + "url": "http://github.com/bentomas/node-mime", + "type": "git" + }, + "version": "1.2.4", + "readme": "# mime\n\nSupport for mapping between file extensions and MIME types. This module uses the latest version of the Apache \"mime.types\" file (maps over 620 types to 800+ extensions). It is also trivially easy to add your own types and extensions, should you need to do that.\n\n## Install\n\nInstall with [npm](http://github.com/isaacs/npm):\n\n npm install mime\n\n## API - Queries\n\n### mime.lookup(path)\nGet the mime type associated with a file. This is method is case-insensitive. Everything in path up to and including the last '/' or '.' is ignored, so you can pass it paths, filenames, or extensions, like so:\n\n var mime = require('mime');\n\n mime.lookup('/path/to/file.txt'); // => 'text/plain'\n mime.lookup('file.txt'); // => 'text/plain'\n mime.lookup('.txt'); // => 'text/plain'\n mime.lookup('htm'); // => 'text/html'\n\n### mime.extension(type) - lookup the default extension for type\n\n mime.extension('text/html'); // => 'html'\n mime.extension('application/octet-stream'); // => 'bin'\n\n### mime.charsets.lookup() - map mime-type to charset\n\n mime.charsets.lookup('text/plain'); // => 'UTF-8'\n\n(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.)\n\n## API - Customizing\n\nThe following APIs allow you to add your own type mappings within your project. If you feel a type should be included as part of node-mime, see [requesting new types](https://github.com/bentomas/node-mime/wiki/Requesting-New-Types).\n### mime.define() - Add custom mime/extension mappings\n\n mime.define({\n 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'],\n 'application/x-my-type': ['x-mt', 'x-mtt'],\n // etc ...\n });\n\n mime.lookup('x-sft'); // => 'text/x-some-format'\n mime.extension('text/x-some-format'); // => 'x-sf'\n\n### mime.load(filepath) - Load mappings from an Apache \".types\" format file\n\n mime.load('./my_project.types');\n", + "readmeFilename": "README.md", + "_id": "mime@1.2.4", + "_from": "mime@1.2.4" +} diff --git a/test/node_modules/express/node_modules/mime/test.js b/test/node_modules/express/node_modules/mime/test.js new file mode 100644 index 0000000..b904895 --- /dev/null +++ b/test/node_modules/express/node_modules/mime/test.js @@ -0,0 +1,79 @@ +/** + * Requires the async_testing module + * + * Usage: node test.js + */ +var mime = require('./mime'); +exports["test mime lookup"] = function(test) { + // easy + test.equal('text/plain', mime.lookup('text.txt')); + + // hidden file or multiple periods + test.equal('text/plain', mime.lookup('.text.txt')); + + // just an extension + test.equal('text/plain', mime.lookup('.txt')); + + // just an extension without a dot + test.equal('text/plain', mime.lookup('txt')); + + // default + test.equal('application/octet-stream', mime.lookup('text.nope')); + + // fallback + test.equal('fallback', mime.lookup('text.fallback', 'fallback')); + + test.finish(); +}; + +exports["test extension lookup"] = function(test) { + // easy + test.equal('txt', mime.extension(mime.types.text)); + test.equal('html', mime.extension(mime.types.htm)); + test.equal('bin', mime.extension('application/octet-stream')); + + test.finish(); +}; + +exports["test mime lookup uppercase"] = function(test) { + // easy + test.equal('text/plain', mime.lookup('TEXT.TXT')); + + // just an extension + test.equal('text/plain', mime.lookup('.TXT')); + + // just an extension without a dot + test.equal('text/plain', mime.lookup('TXT')); + + // default + test.equal('application/octet-stream', mime.lookup('TEXT.NOPE')); + + // fallback + test.equal('fallback', mime.lookup('TEXT.FALLBACK', 'fallback')); + + test.finish(); +}; + +exports["test custom types"] = function(test) { + test.equal('application/octet-stream', mime.lookup('file.buffer')); + test.equal('audio/mp4', mime.lookup('file.m4a')); + + test.finish(); +}; + +exports["test charset lookup"] = function(test) { + // easy + test.equal('UTF-8', mime.charsets.lookup('text/plain')); + + // none + test.ok(typeof mime.charsets.lookup(mime.types.js) == 'undefined'); + + // fallback + test.equal('fallback', mime.charsets.lookup('application/octet-stream', 'fallback')); + + test.finish(); +}; + +if (module == require.main) { + require('async_testing').run(__filename, process.ARGV); +} diff --git a/test/node_modules/express/node_modules/mime/types/mime.types b/test/node_modules/express/node_modules/mime/types/mime.types new file mode 100644 index 0000000..6a90929 --- /dev/null +++ b/test/node_modules/express/node_modules/mime/types/mime.types @@ -0,0 +1,1479 @@ +# This file maps Internet media types to unique file extension(s). +# Although created for httpd, this file is used by many software systems +# and has been placed in the public domain for unlimited redisribution. +# +# The table below contains both registered and (common) unregistered types. +# A type that has no unique extension can be ignored -- they are listed +# here to guide configurations toward known types and to make it easier to +# identify "new" types. File extensions are also commonly used to indicate +# content languages and encodings, so choose them carefully. +# +# Internet media types should be registered as described in RFC 4288. +# The registry is at . +# +# MIME type (lowercased) Extensions +# ============================================ ========== +# application/1d-interleaved-parityfec +# application/3gpp-ims+xml +# application/activemessage +application/andrew-inset ez +# application/applefile +application/applixware aw +application/atom+xml atom +application/atomcat+xml atomcat +# application/atomicmail +application/atomsvc+xml atomsvc +# application/auth-policy+xml +# application/batch-smtp +# application/beep+xml +# application/cals-1840 +application/ccxml+xml ccxml +application/cdmi-capability cdmia +application/cdmi-container cdmic +application/cdmi-domain cdmid +application/cdmi-object cdmio +application/cdmi-queue cdmiq +# application/cea-2018+xml +# application/cellml+xml +# application/cfw +# application/cnrp+xml +# application/commonground +# application/conference-info+xml +# application/cpl+xml +# application/csta+xml +# application/cstadata+xml +application/cu-seeme cu +# application/cybercash +application/davmount+xml davmount +# application/dca-rft +# application/dec-dx +# application/dialog-info+xml +# application/dicom +# application/dns +# application/dskpp+xml +application/dssc+der dssc +application/dssc+xml xdssc +# application/dvcs +application/ecmascript ecma +# application/edi-consent +# application/edi-x12 +# application/edifact +application/emma+xml emma +# application/epp+xml +application/epub+zip epub +# application/eshop +# application/example +application/exi exi +# application/fastinfoset +# application/fastsoap +# application/fits +application/font-tdpfr pfr +# application/framework-attributes+xml +# application/h224 +# application/held+xml +# application/http +application/hyperstudio stk +# application/ibe-key-request+xml +# application/ibe-pkg-reply+xml +# application/ibe-pp-data +# application/iges +# application/im-iscomposing+xml +# application/index +# application/index.cmd +# application/index.obj +# application/index.response +# application/index.vnd +# application/iotp +application/ipfix ipfix +# application/ipp +# application/isup +application/java-archive jar +application/java-serialized-object ser +application/java-vm class +application/javascript js +application/json json +# application/kpml-request+xml +# application/kpml-response+xml +application/lost+xml lostxml +application/mac-binhex40 hqx +application/mac-compactpro cpt +# application/macwriteii +application/mads+xml mads +application/marc mrc +application/marcxml+xml mrcx +application/mathematica ma nb mb +# application/mathml-content+xml +# application/mathml-presentation+xml +application/mathml+xml mathml +# application/mbms-associated-procedure-description+xml +# application/mbms-deregister+xml +# application/mbms-envelope+xml +# application/mbms-msk+xml +# application/mbms-msk-response+xml +# application/mbms-protection-description+xml +# application/mbms-reception-report+xml +# application/mbms-register+xml +# application/mbms-register-response+xml +# application/mbms-user-service-description+xml +application/mbox mbox +# application/media_control+xml +application/mediaservercontrol+xml mscml +application/metalink4+xml meta4 +application/mets+xml mets +# application/mikey +application/mods+xml mods +# application/moss-keys +# application/moss-signature +# application/mosskey-data +# application/mosskey-request +application/mp21 m21 mp21 +application/mp4 mp4s +# application/mpeg4-generic +# application/mpeg4-iod +# application/mpeg4-iod-xmt +# application/msc-ivr+xml +# application/msc-mixer+xml +application/msword doc dot +application/mxf mxf +# application/nasdata +# application/news-checkgroups +# application/news-groupinfo +# application/news-transmission +# application/nss +# application/ocsp-request +# application/ocsp-response +application/octet-stream bin dms lha lrf lzh so iso dmg dist distz pkg bpk dump elc deploy +application/oda oda +application/oebps-package+xml opf +application/ogg ogx +application/onenote onetoc onetoc2 onetmp onepkg +# application/parityfec +application/patch-ops-error+xml xer +application/pdf pdf +application/pgp-encrypted pgp +# application/pgp-keys +application/pgp-signature asc sig +application/pics-rules prf +# application/pidf+xml +# application/pidf-diff+xml +application/pkcs10 p10 +application/pkcs7-mime p7m p7c +application/pkcs7-signature p7s +application/pkcs8 p8 +application/pkix-attr-cert ac +application/pkix-cert cer +application/pkix-crl crl +application/pkix-pkipath pkipath +application/pkixcmp pki +application/pls+xml pls +# application/poc-settings+xml +application/postscript ai eps ps +# application/prs.alvestrand.titrax-sheet +application/prs.cww cww +# application/prs.nprend +# application/prs.plucker +# application/prs.rdf-xml-crypt +# application/prs.xsf+xml +application/pskc+xml pskcxml +# application/qsig +application/rdf+xml rdf +application/reginfo+xml rif +application/relax-ng-compact-syntax rnc +# application/remote-printing +application/resource-lists+xml rl +application/resource-lists-diff+xml rld +# application/riscos +# application/rlmi+xml +application/rls-services+xml rs +application/rsd+xml rsd +application/rss+xml rss +application/rtf rtf +# application/rtx +# application/samlassertion+xml +# application/samlmetadata+xml +application/sbml+xml sbml +application/scvp-cv-request scq +application/scvp-cv-response scs +application/scvp-vp-request spq +application/scvp-vp-response spp +application/sdp sdp +# application/set-payment +application/set-payment-initiation setpay +# application/set-registration +application/set-registration-initiation setreg +# application/sgml +# application/sgml-open-catalog +application/shf+xml shf +# application/sieve +# application/simple-filter+xml +# application/simple-message-summary +# application/simplesymbolcontainer +# application/slate +# application/smil +application/smil+xml smi smil +# application/soap+fastinfoset +# application/soap+xml +application/sparql-query rq +application/sparql-results+xml srx +# application/spirits-event+xml +application/srgs gram +application/srgs+xml grxml +application/sru+xml sru +application/ssml+xml ssml +# application/tamp-apex-update +# application/tamp-apex-update-confirm +# application/tamp-community-update +# application/tamp-community-update-confirm +# application/tamp-error +# application/tamp-sequence-adjust +# application/tamp-sequence-adjust-confirm +# application/tamp-status-query +# application/tamp-status-response +# application/tamp-update +# application/tamp-update-confirm +application/tei+xml tei teicorpus +application/thraud+xml tfi +# application/timestamp-query +# application/timestamp-reply +application/timestamped-data tsd +# application/tve-trigger +# application/ulpfec +# application/vemmi +# application/vividence.scriptfile +# application/vnd.3gpp.bsf+xml +application/vnd.3gpp.pic-bw-large plb +application/vnd.3gpp.pic-bw-small psb +application/vnd.3gpp.pic-bw-var pvb +# application/vnd.3gpp.sms +# application/vnd.3gpp2.bcmcsinfo+xml +# application/vnd.3gpp2.sms +application/vnd.3gpp2.tcap tcap +application/vnd.3m.post-it-notes pwn +application/vnd.accpac.simply.aso aso +application/vnd.accpac.simply.imp imp +application/vnd.acucobol acu +application/vnd.acucorp atc acutc +application/vnd.adobe.air-application-installer-package+zip air +application/vnd.adobe.fxp fxp fxpl +# application/vnd.adobe.partial-upload +application/vnd.adobe.xdp+xml xdp +application/vnd.adobe.xfdf xfdf +# application/vnd.aether.imp +# application/vnd.ah-barcode +application/vnd.ahead.space ahead +application/vnd.airzip.filesecure.azf azf +application/vnd.airzip.filesecure.azs azs +application/vnd.amazon.ebook azw +application/vnd.americandynamics.acc acc +application/vnd.amiga.ami ami +# application/vnd.amundsen.maze+xml +application/vnd.android.package-archive apk +application/vnd.anser-web-certificate-issue-initiation cii +application/vnd.anser-web-funds-transfer-initiation fti +application/vnd.antix.game-component atx +application/vnd.apple.installer+xml mpkg +application/vnd.apple.mpegurl m3u8 +# application/vnd.arastra.swi +application/vnd.aristanetworks.swi swi +application/vnd.audiograph aep +# application/vnd.autopackage +# application/vnd.avistar+xml +application/vnd.blueice.multipass mpm +# application/vnd.bluetooth.ep.oob +application/vnd.bmi bmi +application/vnd.businessobjects rep +# application/vnd.cab-jscript +# application/vnd.canon-cpdl +# application/vnd.canon-lips +# application/vnd.cendio.thinlinc.clientconf +application/vnd.chemdraw+xml cdxml +application/vnd.chipnuts.karaoke-mmd mmd +application/vnd.cinderella cdy +# application/vnd.cirpack.isdn-ext +application/vnd.claymore cla +application/vnd.cloanto.rp9 rp9 +application/vnd.clonk.c4group c4g c4d c4f c4p c4u +application/vnd.cluetrust.cartomobile-config c11amc +application/vnd.cluetrust.cartomobile-config-pkg c11amz +# application/vnd.commerce-battelle +application/vnd.commonspace csp +application/vnd.contact.cmsg cdbcmsg +application/vnd.cosmocaller cmc +application/vnd.crick.clicker clkx +application/vnd.crick.clicker.keyboard clkk +application/vnd.crick.clicker.palette clkp +application/vnd.crick.clicker.template clkt +application/vnd.crick.clicker.wordbank clkw +application/vnd.criticaltools.wbs+xml wbs +application/vnd.ctc-posml pml +# application/vnd.ctct.ws+xml +# application/vnd.cups-pdf +# application/vnd.cups-postscript +application/vnd.cups-ppd ppd +# application/vnd.cups-raster +# application/vnd.cups-raw +application/vnd.curl.car car +application/vnd.curl.pcurl pcurl +# application/vnd.cybank +application/vnd.data-vision.rdz rdz +application/vnd.dece.data uvf uvvf uvd uvvd +application/vnd.dece.ttml+xml uvt uvvt +application/vnd.dece.unspecified uvx uvvx +application/vnd.denovo.fcselayout-link fe_launch +# application/vnd.dir-bi.plate-dl-nosuffix +application/vnd.dna dna +application/vnd.dolby.mlp mlp +# application/vnd.dolby.mobile.1 +# application/vnd.dolby.mobile.2 +application/vnd.dpgraph dpg +application/vnd.dreamfactory dfac +application/vnd.dvb.ait ait +# application/vnd.dvb.dvbj +# application/vnd.dvb.esgcontainer +# application/vnd.dvb.ipdcdftnotifaccess +# application/vnd.dvb.ipdcesgaccess +# application/vnd.dvb.ipdcesgaccess2 +# application/vnd.dvb.ipdcesgpdd +# application/vnd.dvb.ipdcroaming +# application/vnd.dvb.iptv.alfec-base +# application/vnd.dvb.iptv.alfec-enhancement +# application/vnd.dvb.notif-aggregate-root+xml +# application/vnd.dvb.notif-container+xml +# application/vnd.dvb.notif-generic+xml +# application/vnd.dvb.notif-ia-msglist+xml +# application/vnd.dvb.notif-ia-registration-request+xml +# application/vnd.dvb.notif-ia-registration-response+xml +# application/vnd.dvb.notif-init+xml +# application/vnd.dvb.pfr +application/vnd.dvb.service svc +# application/vnd.dxr +application/vnd.dynageo geo +# application/vnd.easykaraoke.cdgdownload +# application/vnd.ecdis-update +application/vnd.ecowin.chart mag +# application/vnd.ecowin.filerequest +# application/vnd.ecowin.fileupdate +# application/vnd.ecowin.series +# application/vnd.ecowin.seriesrequest +# application/vnd.ecowin.seriesupdate +# application/vnd.emclient.accessrequest+xml +application/vnd.enliven nml +application/vnd.epson.esf esf +application/vnd.epson.msf msf +application/vnd.epson.quickanime qam +application/vnd.epson.salt slt +application/vnd.epson.ssf ssf +# application/vnd.ericsson.quickcall +application/vnd.eszigno3+xml es3 et3 +# application/vnd.etsi.aoc+xml +# application/vnd.etsi.cug+xml +# application/vnd.etsi.iptvcommand+xml +# application/vnd.etsi.iptvdiscovery+xml +# application/vnd.etsi.iptvprofile+xml +# application/vnd.etsi.iptvsad-bc+xml +# application/vnd.etsi.iptvsad-cod+xml +# application/vnd.etsi.iptvsad-npvr+xml +# application/vnd.etsi.iptvservice+xml +# application/vnd.etsi.iptvsync+xml +# application/vnd.etsi.iptvueprofile+xml +# application/vnd.etsi.mcid+xml +# application/vnd.etsi.overload-control-policy-dataset+xml +# application/vnd.etsi.sci+xml +# application/vnd.etsi.simservs+xml +# application/vnd.etsi.tsl+xml +# application/vnd.etsi.tsl.der +# application/vnd.eudora.data +application/vnd.ezpix-album ez2 +application/vnd.ezpix-package ez3 +# application/vnd.f-secure.mobile +application/vnd.fdf fdf +application/vnd.fdsn.mseed mseed +application/vnd.fdsn.seed seed dataless +# application/vnd.ffsns +# application/vnd.fints +application/vnd.flographit gph +application/vnd.fluxtime.clip ftc +# application/vnd.font-fontforge-sfd +application/vnd.framemaker fm frame maker book +application/vnd.frogans.fnc fnc +application/vnd.frogans.ltf ltf +application/vnd.fsc.weblaunch fsc +application/vnd.fujitsu.oasys oas +application/vnd.fujitsu.oasys2 oa2 +application/vnd.fujitsu.oasys3 oa3 +application/vnd.fujitsu.oasysgp fg5 +application/vnd.fujitsu.oasysprs bh2 +# application/vnd.fujixerox.art-ex +# application/vnd.fujixerox.art4 +# application/vnd.fujixerox.hbpl +application/vnd.fujixerox.ddd ddd +application/vnd.fujixerox.docuworks xdw +application/vnd.fujixerox.docuworks.binder xbd +# application/vnd.fut-misnet +application/vnd.fuzzysheet fzs +application/vnd.genomatix.tuxedo txd +# application/vnd.geocube+xml +application/vnd.geogebra.file ggb +application/vnd.geogebra.tool ggt +application/vnd.geometry-explorer gex gre +application/vnd.geonext gxt +application/vnd.geoplan g2w +application/vnd.geospace g3w +# application/vnd.globalplatform.card-content-mgt +# application/vnd.globalplatform.card-content-mgt-response +application/vnd.gmx gmx +application/vnd.google-earth.kml+xml kml +application/vnd.google-earth.kmz kmz +application/vnd.grafeq gqf gqs +# application/vnd.gridmp +application/vnd.groove-account gac +application/vnd.groove-help ghf +application/vnd.groove-identity-message gim +application/vnd.groove-injector grv +application/vnd.groove-tool-message gtm +application/vnd.groove-tool-template tpl +application/vnd.groove-vcard vcg +application/vnd.hal+xml hal +application/vnd.handheld-entertainment+xml zmm +application/vnd.hbci hbci +# application/vnd.hcl-bireports +application/vnd.hhe.lesson-player les +application/vnd.hp-hpgl hpgl +application/vnd.hp-hpid hpid +application/vnd.hp-hps hps +application/vnd.hp-jlyt jlt +application/vnd.hp-pcl pcl +application/vnd.hp-pclxl pclxl +# application/vnd.httphone +application/vnd.hydrostatix.sof-data sfd-hdstx +application/vnd.hzn-3d-crossword x3d +# application/vnd.ibm.afplinedata +# application/vnd.ibm.electronic-media +application/vnd.ibm.minipay mpy +application/vnd.ibm.modcap afp listafp list3820 +application/vnd.ibm.rights-management irm +application/vnd.ibm.secure-container sc +application/vnd.iccprofile icc icm +application/vnd.igloader igl +application/vnd.immervision-ivp ivp +application/vnd.immervision-ivu ivu +# application/vnd.informedcontrol.rms+xml +# application/vnd.informix-visionary +# application/vnd.infotech.project +# application/vnd.infotech.project+xml +application/vnd.insors.igm igm +application/vnd.intercon.formnet xpw xpx +application/vnd.intergeo i2g +# application/vnd.intertrust.digibox +# application/vnd.intertrust.nncp +application/vnd.intu.qbo qbo +application/vnd.intu.qfx qfx +# application/vnd.iptc.g2.conceptitem+xml +# application/vnd.iptc.g2.knowledgeitem+xml +# application/vnd.iptc.g2.newsitem+xml +# application/vnd.iptc.g2.packageitem+xml +application/vnd.ipunplugged.rcprofile rcprofile +application/vnd.irepository.package+xml irp +application/vnd.is-xpr xpr +application/vnd.isac.fcs fcs +application/vnd.jam jam +# application/vnd.japannet-directory-service +# application/vnd.japannet-jpnstore-wakeup +# application/vnd.japannet-payment-wakeup +# application/vnd.japannet-registration +# application/vnd.japannet-registration-wakeup +# application/vnd.japannet-setstore-wakeup +# application/vnd.japannet-verification +# application/vnd.japannet-verification-wakeup +application/vnd.jcp.javame.midlet-rms rms +application/vnd.jisp jisp +application/vnd.joost.joda-archive joda +application/vnd.kahootz ktz ktr +application/vnd.kde.karbon karbon +application/vnd.kde.kchart chrt +application/vnd.kde.kformula kfo +application/vnd.kde.kivio flw +application/vnd.kde.kontour kon +application/vnd.kde.kpresenter kpr kpt +application/vnd.kde.kspread ksp +application/vnd.kde.kword kwd kwt +application/vnd.kenameaapp htke +application/vnd.kidspiration kia +application/vnd.kinar kne knp +application/vnd.koan skp skd skt skm +application/vnd.kodak-descriptor sse +application/vnd.las.las+xml lasxml +# application/vnd.liberty-request+xml +application/vnd.llamagraphics.life-balance.desktop lbd +application/vnd.llamagraphics.life-balance.exchange+xml lbe +application/vnd.lotus-1-2-3 123 +application/vnd.lotus-approach apr +application/vnd.lotus-freelance pre +application/vnd.lotus-notes nsf +application/vnd.lotus-organizer org +application/vnd.lotus-screencam scm +application/vnd.lotus-wordpro lwp +application/vnd.macports.portpkg portpkg +# application/vnd.marlin.drm.actiontoken+xml +# application/vnd.marlin.drm.conftoken+xml +# application/vnd.marlin.drm.license+xml +# application/vnd.marlin.drm.mdcf +application/vnd.mcd mcd +application/vnd.medcalcdata mc1 +application/vnd.mediastation.cdkey cdkey +# application/vnd.meridian-slingshot +application/vnd.mfer mwf +application/vnd.mfmp mfm +application/vnd.micrografx.flo flo +application/vnd.micrografx.igx igx +application/vnd.mif mif +# application/vnd.minisoft-hp3000-save +# application/vnd.mitsubishi.misty-guard.trustweb +application/vnd.mobius.daf daf +application/vnd.mobius.dis dis +application/vnd.mobius.mbk mbk +application/vnd.mobius.mqy mqy +application/vnd.mobius.msl msl +application/vnd.mobius.plc plc +application/vnd.mobius.txf txf +application/vnd.mophun.application mpn +application/vnd.mophun.certificate mpc +# application/vnd.motorola.flexsuite +# application/vnd.motorola.flexsuite.adsi +# application/vnd.motorola.flexsuite.fis +# application/vnd.motorola.flexsuite.gotap +# application/vnd.motorola.flexsuite.kmr +# application/vnd.motorola.flexsuite.ttc +# application/vnd.motorola.flexsuite.wem +# application/vnd.motorola.iprm +application/vnd.mozilla.xul+xml xul +application/vnd.ms-artgalry cil +# application/vnd.ms-asf +application/vnd.ms-cab-compressed cab +application/vnd.ms-excel xls xlm xla xlc xlt xlw +application/vnd.ms-excel.addin.macroenabled.12 xlam +application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb +application/vnd.ms-excel.sheet.macroenabled.12 xlsm +application/vnd.ms-excel.template.macroenabled.12 xltm +application/vnd.ms-fontobject eot +application/vnd.ms-htmlhelp chm +application/vnd.ms-ims ims +application/vnd.ms-lrm lrm +# application/vnd.ms-office.activex+xml +application/vnd.ms-officetheme thmx +application/vnd.ms-pki.seccat cat +application/vnd.ms-pki.stl stl +# application/vnd.ms-playready.initiator+xml +application/vnd.ms-powerpoint ppt pps pot +application/vnd.ms-powerpoint.addin.macroenabled.12 ppam +application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm +application/vnd.ms-powerpoint.slide.macroenabled.12 sldm +application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm +application/vnd.ms-powerpoint.template.macroenabled.12 potm +application/vnd.ms-project mpp mpt +# application/vnd.ms-tnef +# application/vnd.ms-wmdrm.lic-chlg-req +# application/vnd.ms-wmdrm.lic-resp +# application/vnd.ms-wmdrm.meter-chlg-req +# application/vnd.ms-wmdrm.meter-resp +application/vnd.ms-word.document.macroenabled.12 docm +application/vnd.ms-word.template.macroenabled.12 dotm +application/vnd.ms-works wps wks wcm wdb +application/vnd.ms-wpl wpl +application/vnd.ms-xpsdocument xps +application/vnd.mseq mseq +# application/vnd.msign +# application/vnd.multiad.creator +# application/vnd.multiad.creator.cif +# application/vnd.music-niff +application/vnd.musician mus +application/vnd.muvee.style msty +# application/vnd.ncd.control +# application/vnd.ncd.reference +# application/vnd.nervana +# application/vnd.netfpx +application/vnd.neurolanguage.nlu nlu +application/vnd.noblenet-directory nnd +application/vnd.noblenet-sealer nns +application/vnd.noblenet-web nnw +# application/vnd.nokia.catalogs +# application/vnd.nokia.conml+wbxml +# application/vnd.nokia.conml+xml +# application/vnd.nokia.isds-radio-presets +# application/vnd.nokia.iptv.config+xml +# application/vnd.nokia.landmark+wbxml +# application/vnd.nokia.landmark+xml +# application/vnd.nokia.landmarkcollection+xml +# application/vnd.nokia.n-gage.ac+xml +application/vnd.nokia.n-gage.data ngdat +application/vnd.nokia.n-gage.symbian.install n-gage +# application/vnd.nokia.ncd +# application/vnd.nokia.pcd+wbxml +# application/vnd.nokia.pcd+xml +application/vnd.nokia.radio-preset rpst +application/vnd.nokia.radio-presets rpss +application/vnd.novadigm.edm edm +application/vnd.novadigm.edx edx +application/vnd.novadigm.ext ext +# application/vnd.ntt-local.file-transfer +# application/vnd.ntt-local.sip-ta_remote +# application/vnd.ntt-local.sip-ta_tcp_stream +application/vnd.oasis.opendocument.chart odc +application/vnd.oasis.opendocument.chart-template otc +application/vnd.oasis.opendocument.database odb +application/vnd.oasis.opendocument.formula odf +application/vnd.oasis.opendocument.formula-template odft +application/vnd.oasis.opendocument.graphics odg +application/vnd.oasis.opendocument.graphics-template otg +application/vnd.oasis.opendocument.image odi +application/vnd.oasis.opendocument.image-template oti +application/vnd.oasis.opendocument.presentation odp +application/vnd.oasis.opendocument.presentation-template otp +application/vnd.oasis.opendocument.spreadsheet ods +application/vnd.oasis.opendocument.spreadsheet-template ots +application/vnd.oasis.opendocument.text odt +application/vnd.oasis.opendocument.text-master odm +application/vnd.oasis.opendocument.text-template ott +application/vnd.oasis.opendocument.text-web oth +# application/vnd.obn +# application/vnd.oipf.contentaccessdownload+xml +# application/vnd.oipf.contentaccessstreaming+xml +# application/vnd.oipf.cspg-hexbinary +# application/vnd.oipf.dae.svg+xml +# application/vnd.oipf.dae.xhtml+xml +# application/vnd.oipf.mippvcontrolmessage+xml +# application/vnd.oipf.pae.gem +# application/vnd.oipf.spdiscovery+xml +# application/vnd.oipf.spdlist+xml +# application/vnd.oipf.ueprofile+xml +# application/vnd.oipf.userprofile+xml +application/vnd.olpc-sugar xo +# application/vnd.oma-scws-config +# application/vnd.oma-scws-http-request +# application/vnd.oma-scws-http-response +# application/vnd.oma.bcast.associated-procedure-parameter+xml +# application/vnd.oma.bcast.drm-trigger+xml +# application/vnd.oma.bcast.imd+xml +# application/vnd.oma.bcast.ltkm +# application/vnd.oma.bcast.notification+xml +# application/vnd.oma.bcast.provisioningtrigger +# application/vnd.oma.bcast.sgboot +# application/vnd.oma.bcast.sgdd+xml +# application/vnd.oma.bcast.sgdu +# application/vnd.oma.bcast.simple-symbol-container +# application/vnd.oma.bcast.smartcard-trigger+xml +# application/vnd.oma.bcast.sprov+xml +# application/vnd.oma.bcast.stkm +# application/vnd.oma.cab-address-book+xml +# application/vnd.oma.cab-pcc+xml +# application/vnd.oma.dcd +# application/vnd.oma.dcdc +application/vnd.oma.dd2+xml dd2 +# application/vnd.oma.drm.risd+xml +# application/vnd.oma.group-usage-list+xml +# application/vnd.oma.poc.detailed-progress-report+xml +# application/vnd.oma.poc.final-report+xml +# application/vnd.oma.poc.groups+xml +# application/vnd.oma.poc.invocation-descriptor+xml +# application/vnd.oma.poc.optimized-progress-report+xml +# application/vnd.oma.push +# application/vnd.oma.scidm.messages+xml +# application/vnd.oma.xcap-directory+xml +# application/vnd.omads-email+xml +# application/vnd.omads-file+xml +# application/vnd.omads-folder+xml +# application/vnd.omaloc-supl-init +application/vnd.openofficeorg.extension oxt +# application/vnd.openxmlformats-officedocument.custom-properties+xml +# application/vnd.openxmlformats-officedocument.customxmlproperties+xml +# application/vnd.openxmlformats-officedocument.drawing+xml +# application/vnd.openxmlformats-officedocument.drawingml.chart+xml +# application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml +# application/vnd.openxmlformats-officedocument.extended-properties+xml +# application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml +# application/vnd.openxmlformats-officedocument.presentationml.comments+xml +# application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml +# application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml +# application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml +application/vnd.openxmlformats-officedocument.presentationml.presentation pptx +# application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.presprops+xml +application/vnd.openxmlformats-officedocument.presentationml.slide sldx +# application/vnd.openxmlformats-officedocument.presentationml.slide+xml +# application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml +# application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml +application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx +# application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml +# application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml +# application/vnd.openxmlformats-officedocument.presentationml.tags+xml +application/vnd.openxmlformats-officedocument.presentationml.template potx +# application/vnd.openxmlformats-officedocument.presentationml.template.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx +# application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml +application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx +# application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml +# application/vnd.openxmlformats-officedocument.theme+xml +# application/vnd.openxmlformats-officedocument.themeoverride+xml +# application/vnd.openxmlformats-officedocument.vmldrawing +# application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml +application/vnd.openxmlformats-officedocument.wordprocessingml.document docx +# application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml +application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx +# application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml +# application/vnd.openxmlformats-package.core-properties+xml +# application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml +# application/vnd.openxmlformats-package.relationships+xml +# application/vnd.quobject-quoxdocument +# application/vnd.osa.netdeploy +application/vnd.osgeo.mapguide.package mgp +# application/vnd.osgi.bundle +application/vnd.osgi.dp dp +# application/vnd.otps.ct-kip+xml +application/vnd.palm pdb pqa oprc +# application/vnd.paos.xml +application/vnd.pawaafile paw +application/vnd.pg.format str +application/vnd.pg.osasli ei6 +# application/vnd.piaccess.application-licence +application/vnd.picsel efif +application/vnd.pmi.widget wg +# application/vnd.poc.group-advertisement+xml +application/vnd.pocketlearn plf +application/vnd.powerbuilder6 pbd +# application/vnd.powerbuilder6-s +# application/vnd.powerbuilder7 +# application/vnd.powerbuilder7-s +# application/vnd.powerbuilder75 +# application/vnd.powerbuilder75-s +# application/vnd.preminet +application/vnd.previewsystems.box box +application/vnd.proteus.magazine mgz +application/vnd.publishare-delta-tree qps +application/vnd.pvi.ptid1 ptid +# application/vnd.pwg-multiplexed +# application/vnd.pwg-xhtml-print+xml +# application/vnd.qualcomm.brew-app-res +application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb +# application/vnd.radisys.moml+xml +# application/vnd.radisys.msml+xml +# application/vnd.radisys.msml-audit+xml +# application/vnd.radisys.msml-audit-conf+xml +# application/vnd.radisys.msml-audit-conn+xml +# application/vnd.radisys.msml-audit-dialog+xml +# application/vnd.radisys.msml-audit-stream+xml +# application/vnd.radisys.msml-conf+xml +# application/vnd.radisys.msml-dialog+xml +# application/vnd.radisys.msml-dialog-base+xml +# application/vnd.radisys.msml-dialog-fax-detect+xml +# application/vnd.radisys.msml-dialog-fax-sendrecv+xml +# application/vnd.radisys.msml-dialog-group+xml +# application/vnd.radisys.msml-dialog-speech+xml +# application/vnd.radisys.msml-dialog-transform+xml +# application/vnd.rainstor.data +# application/vnd.rapid +application/vnd.realvnc.bed bed +application/vnd.recordare.musicxml mxl +application/vnd.recordare.musicxml+xml musicxml +# application/vnd.renlearn.rlprint +application/vnd.rig.cryptonote cryptonote +application/vnd.rim.cod cod +application/vnd.rn-realmedia rm +application/vnd.route66.link66+xml link66 +# application/vnd.ruckus.download +# application/vnd.s3sms +application/vnd.sailingtracker.track st +# application/vnd.sbm.cid +# application/vnd.sbm.mid2 +# application/vnd.scribus +# application/vnd.sealed.3df +# application/vnd.sealed.csf +# application/vnd.sealed.doc +# application/vnd.sealed.eml +# application/vnd.sealed.mht +# application/vnd.sealed.net +# application/vnd.sealed.ppt +# application/vnd.sealed.tiff +# application/vnd.sealed.xls +# application/vnd.sealedmedia.softseal.html +# application/vnd.sealedmedia.softseal.pdf +application/vnd.seemail see +application/vnd.sema sema +application/vnd.semd semd +application/vnd.semf semf +application/vnd.shana.informed.formdata ifm +application/vnd.shana.informed.formtemplate itp +application/vnd.shana.informed.interchange iif +application/vnd.shana.informed.package ipk +application/vnd.simtech-mindmapper twd twds +application/vnd.smaf mmf +# application/vnd.smart.notebook +application/vnd.smart.teacher teacher +# application/vnd.software602.filler.form+xml +# application/vnd.software602.filler.form-xml-zip +application/vnd.solent.sdkm+xml sdkm sdkd +application/vnd.spotfire.dxp dxp +application/vnd.spotfire.sfs sfs +# application/vnd.sss-cod +# application/vnd.sss-dtf +# application/vnd.sss-ntf +application/vnd.stardivision.calc sdc +application/vnd.stardivision.draw sda +application/vnd.stardivision.impress sdd +application/vnd.stardivision.math smf +application/vnd.stardivision.writer sdw vor +application/vnd.stardivision.writer-global sgl +application/vnd.stepmania.stepchart sm +# application/vnd.street-stream +application/vnd.sun.xml.calc sxc +application/vnd.sun.xml.calc.template stc +application/vnd.sun.xml.draw sxd +application/vnd.sun.xml.draw.template std +application/vnd.sun.xml.impress sxi +application/vnd.sun.xml.impress.template sti +application/vnd.sun.xml.math sxm +application/vnd.sun.xml.writer sxw +application/vnd.sun.xml.writer.global sxg +application/vnd.sun.xml.writer.template stw +# application/vnd.sun.wadl+xml +application/vnd.sus-calendar sus susp +application/vnd.svd svd +# application/vnd.swiftview-ics +application/vnd.symbian.install sis sisx +application/vnd.syncml+xml xsm +application/vnd.syncml.dm+wbxml bdm +application/vnd.syncml.dm+xml xdm +# application/vnd.syncml.dm.notification +# application/vnd.syncml.ds.notification +application/vnd.tao.intent-module-archive tao +application/vnd.tmobile-livetv tmo +application/vnd.trid.tpt tpt +application/vnd.triscape.mxs mxs +application/vnd.trueapp tra +# application/vnd.truedoc +# application/vnd.ubisoft.webplayer +application/vnd.ufdl ufd ufdl +application/vnd.uiq.theme utz +application/vnd.umajin umj +application/vnd.unity unityweb +application/vnd.uoml+xml uoml +# application/vnd.uplanet.alert +# application/vnd.uplanet.alert-wbxml +# application/vnd.uplanet.bearer-choice +# application/vnd.uplanet.bearer-choice-wbxml +# application/vnd.uplanet.cacheop +# application/vnd.uplanet.cacheop-wbxml +# application/vnd.uplanet.channel +# application/vnd.uplanet.channel-wbxml +# application/vnd.uplanet.list +# application/vnd.uplanet.list-wbxml +# application/vnd.uplanet.listcmd +# application/vnd.uplanet.listcmd-wbxml +# application/vnd.uplanet.signal +application/vnd.vcx vcx +# application/vnd.vd-study +# application/vnd.vectorworks +# application/vnd.verimatrix.vcas +# application/vnd.vidsoft.vidconference +application/vnd.visio vsd vst vss vsw +application/vnd.visionary vis +# application/vnd.vividence.scriptfile +application/vnd.vsf vsf +# application/vnd.wap.sic +# application/vnd.wap.slc +application/vnd.wap.wbxml wbxml +application/vnd.wap.wmlc wmlc +application/vnd.wap.wmlscriptc wmlsc +application/vnd.webturbo wtb +# application/vnd.wfa.wsc +# application/vnd.wmc +# application/vnd.wmf.bootstrap +# application/vnd.wolfram.mathematica +# application/vnd.wolfram.mathematica.package +application/vnd.wolfram.player nbp +application/vnd.wordperfect wpd +application/vnd.wqd wqd +# application/vnd.wrq-hp3000-labelled +application/vnd.wt.stf stf +# application/vnd.wv.csp+wbxml +# application/vnd.wv.csp+xml +# application/vnd.wv.ssp+xml +application/vnd.xara xar +application/vnd.xfdl xfdl +# application/vnd.xfdl.webform +# application/vnd.xmi+xml +# application/vnd.xmpie.cpkg +# application/vnd.xmpie.dpkg +# application/vnd.xmpie.plan +# application/vnd.xmpie.ppkg +# application/vnd.xmpie.xlim +application/vnd.yamaha.hv-dic hvd +application/vnd.yamaha.hv-script hvs +application/vnd.yamaha.hv-voice hvp +application/vnd.yamaha.openscoreformat osf +application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg +# application/vnd.yamaha.remote-setup +application/vnd.yamaha.smaf-audio saf +application/vnd.yamaha.smaf-phrase spf +# application/vnd.yamaha.tunnel-udpencap +application/vnd.yellowriver-custom-menu cmp +application/vnd.zul zir zirz +application/vnd.zzazz.deck+xml zaz +application/voicexml+xml vxml +# application/vq-rtcpxr +# application/watcherinfo+xml +# application/whoispp-query +# application/whoispp-response +application/widget wgt +application/winhlp hlp +# application/wita +# application/wordperfect5.1 +application/wsdl+xml wsdl +application/wspolicy+xml wspolicy +application/x-7z-compressed 7z +application/x-abiword abw +application/x-ace-compressed ace +application/x-authorware-bin aab x32 u32 vox +application/x-authorware-map aam +application/x-authorware-seg aas +application/x-bcpio bcpio +application/x-bittorrent torrent +application/x-bzip bz +application/x-bzip2 bz2 boz +application/x-cdlink vcd +application/x-chat chat +application/x-chess-pgn pgn +# application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-debian-package deb udeb +application/x-director dir dcr dxr cst cct cxt w3d fgd swa +application/x-doom wad +application/x-dtbncx+xml ncx +application/x-dtbook+xml dtb +application/x-dtbresource+xml res +application/x-dvi dvi +application/x-font-bdf bdf +# application/x-font-dos +# application/x-font-framemaker +application/x-font-ghostscript gsf +# application/x-font-libgrx +application/x-font-linux-psf psf +application/x-font-otf otf +application/x-font-pcf pcf +application/x-font-snf snf +# application/x-font-speedo +# application/x-font-sunos-news +application/x-font-ttf ttf ttc +application/x-font-type1 pfa pfb pfm afm +application/x-font-woff woff +# application/x-font-vfont +application/x-futuresplash spl +application/x-gnumeric gnumeric +application/x-gtar gtar +# application/x-gzip +application/x-hdf hdf +application/x-java-jnlp-file jnlp +application/x-latex latex +application/x-mobipocket-ebook prc mobi +application/x-ms-application application +application/x-ms-wmd wmd +application/x-ms-wmz wmz +application/x-ms-xbap xbap +application/x-msaccess mdb +application/x-msbinder obd +application/x-mscardfile crd +application/x-msclip clp +application/x-msdownload exe dll com bat msi +application/x-msmediaview mvb m13 m14 +application/x-msmetafile wmf +application/x-msmoney mny +application/x-mspublisher pub +application/x-msschedule scd +application/x-msterminal trm +application/x-mswrite wri +application/x-netcdf nc cdf +application/x-pkcs12 p12 pfx +application/x-pkcs7-certificates p7b spc +application/x-pkcs7-certreqresp p7r +application/x-rar-compressed rar +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-silverlight-app xap +application/x-stuffit sit +application/x-stuffitx sitx +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-tex-tfm tfm +application/x-texinfo texinfo texi +application/x-ustar ustar +application/x-wais-source src +application/x-x509-ca-cert der crt +application/x-xfig fig +application/x-xpinstall xpi +# application/x400-bp +# application/xcap-att+xml +# application/xcap-caps+xml +application/xcap-diff+xml xdf +# application/xcap-el+xml +# application/xcap-error+xml +# application/xcap-ns+xml +# application/xcon-conference-info-diff+xml +# application/xcon-conference-info+xml +application/xenc+xml xenc +application/xhtml+xml xhtml xht +# application/xhtml-voice+xml +application/xml xml xsl +application/xml-dtd dtd +# application/xml-external-parsed-entity +# application/xmpp+xml +application/xop+xml xop +application/xslt+xml xslt +application/xspf+xml xspf +application/xv+xml mxml xhvml xvml xvm +application/yang yang +application/yin+xml yin +application/zip zip +# audio/1d-interleaved-parityfec +# audio/32kadpcm +# audio/3gpp +# audio/3gpp2 +# audio/ac3 +audio/adpcm adp +# audio/amr +# audio/amr-wb +# audio/amr-wb+ +# audio/asc +# audio/atrac-advanced-lossless +# audio/atrac-x +# audio/atrac3 +audio/basic au snd +# audio/bv16 +# audio/bv32 +# audio/clearmode +# audio/cn +# audio/dat12 +# audio/dls +# audio/dsr-es201108 +# audio/dsr-es202050 +# audio/dsr-es202211 +# audio/dsr-es202212 +# audio/dvi4 +# audio/eac3 +# audio/evrc +# audio/evrc-qcp +# audio/evrc0 +# audio/evrc1 +# audio/evrcb +# audio/evrcb0 +# audio/evrcb1 +# audio/evrcwb +# audio/evrcwb0 +# audio/evrcwb1 +# audio/example +# audio/g719 +# audio/g722 +# audio/g7221 +# audio/g723 +# audio/g726-16 +# audio/g726-24 +# audio/g726-32 +# audio/g726-40 +# audio/g728 +# audio/g729 +# audio/g7291 +# audio/g729d +# audio/g729e +# audio/gsm +# audio/gsm-efr +# audio/gsm-hr-08 +# audio/ilbc +# audio/l16 +# audio/l20 +# audio/l24 +# audio/l8 +# audio/lpc +audio/midi mid midi kar rmi +# audio/mobile-xmf +audio/mp4 mp4a +# audio/mp4a-latm +# audio/mpa +# audio/mpa-robust +audio/mpeg mpga mp2 mp2a mp3 m2a m3a +# audio/mpeg4-generic +audio/ogg oga ogg spx +# audio/parityfec +# audio/pcma +# audio/pcma-wb +# audio/pcmu-wb +# audio/pcmu +# audio/prs.sid +# audio/qcelp +# audio/red +# audio/rtp-enc-aescm128 +# audio/rtp-midi +# audio/rtx +# audio/smv +# audio/smv0 +# audio/smv-qcp +# audio/sp-midi +# audio/speex +# audio/t140c +# audio/t38 +# audio/telephone-event +# audio/tone +# audio/uemclip +# audio/ulpfec +# audio/vdvi +# audio/vmr-wb +# audio/vnd.3gpp.iufp +# audio/vnd.4sb +# audio/vnd.audiokoz +# audio/vnd.celp +# audio/vnd.cisco.nse +# audio/vnd.cmles.radio-events +# audio/vnd.cns.anp1 +# audio/vnd.cns.inf1 +audio/vnd.dece.audio uva uvva +audio/vnd.digital-winds eol +# audio/vnd.dlna.adts +# audio/vnd.dolby.heaac.1 +# audio/vnd.dolby.heaac.2 +# audio/vnd.dolby.mlp +# audio/vnd.dolby.mps +# audio/vnd.dolby.pl2 +# audio/vnd.dolby.pl2x +# audio/vnd.dolby.pl2z +# audio/vnd.dolby.pulse.1 +audio/vnd.dra dra +audio/vnd.dts dts +audio/vnd.dts.hd dtshd +# audio/vnd.everad.plj +# audio/vnd.hns.audio +audio/vnd.lucent.voice lvp +audio/vnd.ms-playready.media.pya pya +# audio/vnd.nokia.mobile-xmf +# audio/vnd.nortel.vbk +audio/vnd.nuera.ecelp4800 ecelp4800 +audio/vnd.nuera.ecelp7470 ecelp7470 +audio/vnd.nuera.ecelp9600 ecelp9600 +# audio/vnd.octel.sbc +# audio/vnd.qcelp +# audio/vnd.rhetorex.32kadpcm +audio/vnd.rip rip +# audio/vnd.sealedmedia.softseal.mpeg +# audio/vnd.vmx.cvsd +# audio/vorbis +# audio/vorbis-config +audio/webm weba +audio/x-aac aac +audio/x-aiff aif aiff aifc +audio/x-mpegurl m3u +audio/x-ms-wax wax +audio/x-ms-wma wma +audio/x-pn-realaudio ram ra +audio/x-pn-realaudio-plugin rmp +audio/x-wav wav +chemical/x-cdx cdx +chemical/x-cif cif +chemical/x-cmdf cmdf +chemical/x-cml cml +chemical/x-csml csml +# chemical/x-pdb +chemical/x-xyz xyz +image/bmp bmp +image/cgm cgm +# image/example +# image/fits +image/g3fax g3 +image/gif gif +image/ief ief +# image/jp2 +image/jpeg jpeg jpg jpe +# image/jpm +# image/jpx +image/ktx ktx +# image/naplps +image/png png +image/prs.btif btif +# image/prs.pti +image/svg+xml svg svgz +# image/t38 +image/tiff tiff tif +# image/tiff-fx +image/vnd.adobe.photoshop psd +# image/vnd.cns.inf2 +image/vnd.dece.graphic uvi uvvi uvg uvvg +image/vnd.dvb.subtitle sub +image/vnd.djvu djvu djv +image/vnd.dwg dwg +image/vnd.dxf dxf +image/vnd.fastbidsheet fbs +image/vnd.fpx fpx +image/vnd.fst fst +image/vnd.fujixerox.edmics-mmr mmr +image/vnd.fujixerox.edmics-rlc rlc +# image/vnd.globalgraphics.pgb +# image/vnd.microsoft.icon +# image/vnd.mix +image/vnd.ms-modi mdi +image/vnd.net-fpx npx +# image/vnd.radiance +# image/vnd.sealed.png +# image/vnd.sealedmedia.softseal.gif +# image/vnd.sealedmedia.softseal.jpg +# image/vnd.svf +image/vnd.wap.wbmp wbmp +image/vnd.xiff xif +image/webp webp +image/x-cmu-raster ras +image/x-cmx cmx +image/x-freehand fh fhc fh4 fh5 fh7 +image/x-icon ico +image/x-pcx pcx +image/x-pict pic pct +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +# message/cpim +# message/delivery-status +# message/disposition-notification +# message/example +# message/external-body +# message/feedback-report +# message/global +# message/global-delivery-status +# message/global-disposition-notification +# message/global-headers +# message/http +# message/imdn+xml +# message/news +# message/partial +message/rfc822 eml mime +# message/s-http +# message/sip +# message/sipfrag +# message/tracking-status +# message/vnd.si.simp +# model/example +model/iges igs iges +model/mesh msh mesh silo +model/vnd.collada+xml dae +model/vnd.dwf dwf +# model/vnd.flatland.3dml +model/vnd.gdl gdl +# model/vnd.gs-gdl +# model/vnd.gs.gdl +model/vnd.gtw gtw +# model/vnd.moml+xml +model/vnd.mts mts +# model/vnd.parasolid.transmit.binary +# model/vnd.parasolid.transmit.text +model/vnd.vtu vtu +model/vrml wrl vrml +# multipart/alternative +# multipart/appledouble +# multipart/byteranges +# multipart/digest +# multipart/encrypted +# multipart/example +# multipart/form-data +# multipart/header-set +# multipart/mixed +# multipart/parallel +# multipart/related +# multipart/report +# multipart/signed +# multipart/voice-message +# text/1d-interleaved-parityfec +text/calendar ics ifb +text/css css +text/csv csv +# text/directory +# text/dns +# text/ecmascript +# text/enriched +# text/example +text/html html htm +# text/javascript +text/n3 n3 +# text/parityfec +text/plain txt text conf def list log in +# text/prs.fallenstein.rst +text/prs.lines.tag dsc +# text/vnd.radisys.msml-basic-layout +# text/red +# text/rfc822-headers +text/richtext rtx +# text/rtf +# text/rtp-enc-aescm128 +# text/rtx +text/sgml sgml sgm +# text/t140 +text/tab-separated-values tsv +text/troff t tr roff man me ms +text/turtle ttl +# text/ulpfec +text/uri-list uri uris urls +# text/vnd.abc +text/vnd.curl curl +text/vnd.curl.dcurl dcurl +text/vnd.curl.scurl scurl +text/vnd.curl.mcurl mcurl +# text/vnd.dmclientscript +# text/vnd.esmertec.theme-descriptor +text/vnd.fly fly +text/vnd.fmi.flexstor flx +text/vnd.graphviz gv +text/vnd.in3d.3dml 3dml +text/vnd.in3d.spot spot +# text/vnd.iptc.newsml +# text/vnd.iptc.nitf +# text/vnd.latex-z +# text/vnd.motorola.reflex +# text/vnd.ms-mediapackage +# text/vnd.net2phone.commcenter.command +# text/vnd.si.uricatalogue +text/vnd.sun.j2me.app-descriptor jad +# text/vnd.trolltech.linguist +# text/vnd.wap.si +# text/vnd.wap.sl +text/vnd.wap.wml wml +text/vnd.wap.wmlscript wmls +text/x-asm s asm +text/x-c c cc cxx cpp h hh dic +text/x-fortran f for f77 f90 +text/x-pascal p pas +text/x-java-source java +text/x-setext etx +text/x-uuencode uu +text/x-vcalendar vcs +text/x-vcard vcf +# text/xml +# text/xml-external-parsed-entity +# video/1d-interleaved-parityfec +video/3gpp 3gp +# video/3gpp-tt +video/3gpp2 3g2 +# video/bmpeg +# video/bt656 +# video/celb +# video/dv +# video/example +video/h261 h261 +video/h263 h263 +# video/h263-1998 +# video/h263-2000 +video/h264 h264 +# video/h264-rcdo +# video/h264-svc +video/jpeg jpgv +# video/jpeg2000 +video/jpm jpm jpgm +video/mj2 mj2 mjp2 +# video/mp1s +# video/mp2p +# video/mp2t +video/mp4 mp4 mp4v mpg4 +# video/mp4v-es +video/mpeg mpeg mpg mpe m1v m2v +# video/mpeg4-generic +# video/mpv +# video/nv +video/ogg ogv +# video/parityfec +# video/pointer +video/quicktime qt mov +# video/raw +# video/rtp-enc-aescm128 +# video/rtx +# video/smpte292m +# video/ulpfec +# video/vc1 +# video/vnd.cctv +video/vnd.dece.hd uvh uvvh +video/vnd.dece.mobile uvm uvvm +# video/vnd.dece.mp4 +video/vnd.dece.pd uvp uvvp +video/vnd.dece.sd uvs uvvs +video/vnd.dece.video uvv uvvv +# video/vnd.directv.mpeg +# video/vnd.directv.mpeg-tts +# video/vnd.dlna.mpeg-tts +video/vnd.fvt fvt +# video/vnd.hns.video +# video/vnd.iptvforum.1dparityfec-1010 +# video/vnd.iptvforum.1dparityfec-2005 +# video/vnd.iptvforum.2dparityfec-1010 +# video/vnd.iptvforum.2dparityfec-2005 +# video/vnd.iptvforum.ttsavc +# video/vnd.iptvforum.ttsmpeg2 +# video/vnd.motorola.video +# video/vnd.motorola.videop +video/vnd.mpegurl mxu m4u +video/vnd.ms-playready.media.pyv pyv +# video/vnd.nokia.interleaved-multimedia +# video/vnd.nokia.videovoip +# video/vnd.objectvideo +# video/vnd.sealed.mpeg1 +# video/vnd.sealed.mpeg4 +# video/vnd.sealed.swf +# video/vnd.sealedmedia.softseal.mov +video/vnd.uvvu.mp4 uvu uvvu +video/vnd.vivo viv +video/webm webm +video/x-f4v f4v +video/x-fli fli +video/x-flv flv +video/x-m4v m4v +video/x-ms-asf asf asx +video/x-ms-wm wm +video/x-ms-wmv wmv +video/x-ms-wmx wmx +video/x-ms-wvx wvx +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice diff --git a/test/node_modules/express/node_modules/mime/types/node.types b/test/node_modules/express/node_modules/mime/types/node.types new file mode 100644 index 0000000..fdabaa4 --- /dev/null +++ b/test/node_modules/express/node_modules/mime/types/node.types @@ -0,0 +1,43 @@ +# What: Google Chrome Extension +# Why: To allow apps to (work) be served with the right content type header. +# http://codereview.chromium.org/2830017 +# Added by: niftylettuce +application/x-chrome-extension crx + +# What: OTF Message Silencer +# Why: To silence the "Resource interpreted as font but transferred with MIME +# type font/otf" message that occurs in Google Chrome +# Added by: niftylettuce +font/opentype otf + +# What: HTC support +# Why: To properly render .htc files such as CSS3PIE +# Added by: niftylettuce +text/x-component htc + +# What: HTML5 application cache manifest +# Why: De-facto standard. Required by Mozilla browser when serving HTML5 apps +# per https://developer.mozilla.org/en/offline_resources_in_firefox +# Added by: louisremi +text/cache-manifest appcache manifest + +# What: node binary buffer format +# Why: semi-standard extension w/in the node community +# Added by: tootallnate +application/octet-stream buffer + +# What: The "protected" MP-4 formats used by iTunes. +# Why: Required for streaming music to browsers (?) +# Added by: broofa +application/mp4 m4p +audio/mp4 m4a + +# What: Music playlist format (http://en.wikipedia.org/wiki/M3U) +# Why: See https://github.com/bentomas/node-mime/pull/6 +# Added by: mjrusso +application/x-mpegURL m3u8 + +# What: Video format, Part of RFC1890 +# Why: See https://github.com/bentomas/node-mime/pull/6 +# Added by: mjrusso +video/MP2T ts diff --git a/test/node_modules/express/node_modules/mkdirp/.gitignore.orig b/test/node_modules/express/node_modules/mkdirp/.gitignore.orig new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/.gitignore.orig @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/test/node_modules/express/node_modules/mkdirp/.gitignore.rej b/test/node_modules/express/node_modules/mkdirp/.gitignore.rej new file mode 100644 index 0000000..69244ff --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/.gitignore.rej @@ -0,0 +1,5 @@ +--- /dev/null ++++ .gitignore +@@ -0,0 +1,2 @@ ++node_modules/ ++npm-debug.log \ No newline at end of file diff --git a/test/node_modules/express/node_modules/mkdirp/.npmignore b/test/node_modules/express/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/test/node_modules/express/node_modules/mkdirp/LICENSE b/test/node_modules/express/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +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/test/node_modules/express/node_modules/mkdirp/README.markdown b/test/node_modules/express/node_modules/mkdirp/README.markdown new file mode 100644 index 0000000..b4dd75f --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/README.markdown @@ -0,0 +1,54 @@ +mkdirp +====== + +Like `mkdir -p`, but in node.js! + +example +======= + +pow.js +------ + var mkdirp = require('mkdirp'); + + mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') + }); + +Output + pow! + +And now /tmp/foo/bar/baz exists, huzzah! + +methods +======= + +var mkdirp = require('mkdirp'); + +mkdirp(dir, mode, cb) +--------------------- + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +mkdirp.sync(dir, mode) +---------------------- + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +install +======= + +With [npm](http://npmjs.org) do: + + npm install mkdirp + +license +======= + +MIT/X11 diff --git a/test/node_modules/express/node_modules/mkdirp/examples/pow.js b/test/node_modules/express/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/test/node_modules/express/node_modules/mkdirp/examples/pow.js.orig b/test/node_modules/express/node_modules/mkdirp/examples/pow.js.orig new file mode 100644 index 0000000..7741462 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/examples/pow.js.orig @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', 0755, function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/test/node_modules/express/node_modules/mkdirp/examples/pow.js.rej b/test/node_modules/express/node_modules/mkdirp/examples/pow.js.rej new file mode 100644 index 0000000..81e7f43 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/examples/pow.js.rej @@ -0,0 +1,19 @@ +--- examples/pow.js ++++ examples/pow.js +@@ -1,6 +1,15 @@ +-var mkdirp = require('mkdirp').mkdirp; ++var mkdirp = require('../').mkdirp, ++ mkdirpSync = require('../').mkdirpSync; + + mkdirp('/tmp/foo/bar/baz', 0755, function (err) { + if (err) console.error(err) + else console.log('pow!') + }); ++ ++try { ++ mkdirpSync('/tmp/bar/foo/baz', 0755); ++ console.log('double pow!'); ++} ++catch (ex) { ++ console.log(ex); ++} \ No newline at end of file diff --git a/test/node_modules/express/node_modules/mkdirp/index.js b/test/node_modules/express/node_modules/mkdirp/index.js new file mode 100644 index 0000000..25f43ad --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/index.js @@ -0,0 +1,79 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, mode, f) { + if (typeof mode === 'function' || mode === undefined) { + f = mode; + mode = 0777 & (~process.umask()); + } + + var cb = f || function () {}; + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + fs.mkdir(p, mode, function (er) { + if (!er) return cb(); + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), mode, function (er) { + if (er) cb(er); + else mkdirP(p, mode, cb); + }); + break; + + case 'EEXIST': + fs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original EEXIST be the failure reason. + if (er2 || !stat.isDirectory()) cb(er) + else cb(); + }); + break; + + default: + cb(er); + break; + } + }); +} + +mkdirP.sync = function sync (p, mode) { + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + try { + fs.mkdirSync(p, mode) + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + var err1 = sync(path.dirname(p), mode) + if (err1) throw err1; + else return sync(p, mode); + break; + + case 'EEXIST' : + var stat; + try { + stat = fs.statSync(p); + } + catch (err1) { + throw err0 + } + if (!stat.isDirectory()) throw err0; + else return null; + break; + default : + throw err0 + break; + } + } + + return null; +}; diff --git a/test/node_modules/express/node_modules/mkdirp/package.json b/test/node_modules/express/node_modules/mkdirp/package.json new file mode 100644 index 0000000..f2aeb8b --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/package.json @@ -0,0 +1,33 @@ +{ + "name": "mkdirp", + "description": "Recursively mkdir, like `mkdir -p`", + "version": "0.3.0", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "main": "./index", + "keywords": [ + "mkdir", + "directory" + ], + "repository": { + "type": "git", + "url": "http://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "devDependencies": { + "tap": "0.0.x" + }, + "license": "MIT/X11", + "engines": { + "node": "*" + }, + "readme": "mkdirp\n======\n\nLike `mkdir -p`, but in node.js!\n\nexample\n=======\n\npow.js\n------\n var mkdirp = require('mkdirp');\n \n mkdirp('/tmp/foo/bar/baz', function (err) {\n if (err) console.error(err)\n else console.log('pow!')\n });\n\nOutput\n pow!\n\nAnd now /tmp/foo/bar/baz exists, huzzah!\n\nmethods\n=======\n\nvar mkdirp = require('mkdirp');\n\nmkdirp(dir, mode, cb)\n---------------------\n\nCreate a new directory and any necessary subdirectories at `dir` with octal\npermission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\nmkdirp.sync(dir, mode)\n----------------------\n\nSynchronously create a new directory and any necessary subdirectories at `dir`\nwith octal permission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\ninstall\n=======\n\nWith [npm](http://npmjs.org) do:\n\n npm install mkdirp\n\nlicense\n=======\n\nMIT/X11\n", + "readmeFilename": "README.markdown", + "_id": "mkdirp@0.3.0", + "_from": "mkdirp@0.3.0" +} diff --git a/test/node_modules/express/node_modules/mkdirp/test/chmod.js b/test/node_modules/express/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/clobber.js b/test/node_modules/express/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/mkdirp.js b/test/node_modules/express/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..b07cd70 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('woo', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/perm.js b/test/node_modules/express/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..23a7abb --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/perm.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/perm_sync.js b/test/node_modules/express/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..f685f60 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,39 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); + +test('sync root perm', function (t) { + t.plan(1); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/race.js b/test/node_modules/express/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..96a0447 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/race.js @@ -0,0 +1,41 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('race', function (t) { + t.plan(4); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + } + }) + }) + }); + } +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/rel.js b/test/node_modules/express/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..7985824 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/rel.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('rel', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/sync.js b/test/node_modules/express/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..e0e389d --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/sync.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + var err = mkdirp.sync(file, 0755); + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/umask.js b/test/node_modules/express/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..64ccafe --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/umask.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/test/node_modules/express/node_modules/mkdirp/test/umask_sync.js b/test/node_modules/express/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..83cba56 --- /dev/null +++ b/test/node_modules/express/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,27 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + var err = mkdirp.sync(file); + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) +}); diff --git a/test/node_modules/express/node_modules/qs/.gitmodules b/test/node_modules/express/node_modules/qs/.gitmodules new file mode 100644 index 0000000..49e31da --- /dev/null +++ b/test/node_modules/express/node_modules/qs/.gitmodules @@ -0,0 +1,6 @@ +[submodule "support/expresso"] + path = support/expresso + url = git://github.com/visionmedia/expresso.git +[submodule "support/should"] + path = support/should + url = git://github.com/visionmedia/should.js.git diff --git a/test/node_modules/express/node_modules/qs/.npmignore b/test/node_modules/express/node_modules/qs/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/test/node_modules/express/node_modules/qs/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/test/node_modules/express/node_modules/qs/.travis.yml b/test/node_modules/express/node_modules/qs/.travis.yml new file mode 100644 index 0000000..2c0a8f6 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.4 \ No newline at end of file diff --git a/test/node_modules/express/node_modules/qs/History.md b/test/node_modules/express/node_modules/qs/History.md new file mode 100644 index 0000000..3eaf7df --- /dev/null +++ b/test/node_modules/express/node_modules/qs/History.md @@ -0,0 +1,73 @@ + +0.4.2 / 2012-02-08 +================== + + * Fixed: ensure objects are created when appropriate not arrays [aheckmann] + +0.4.1 / 2012-01-26 +================== + + * Fixed stringify()ing numbers. Closes #23 + +0.4.0 / 2011-11-21 +================== + + * Allow parsing of an existing object (for `bodyParser()`) [jackyz] + * Replaced expresso with mocha + +0.3.2 / 2011-11-08 +================== + + * Fixed global variable leak + +0.3.1 / 2011-08-17 +================== + + * Added `try/catch` around malformed uri components + * Add test coverage for Array native method bleed-though + +0.3.0 / 2011-07-19 +================== + + * Allow `array[index]` and `object[property]` syntaxes [Aria Stewart] + +0.2.0 / 2011-06-29 +================== + + * Added `qs.stringify()` [Cory Forsyth] + +0.1.0 / 2011-04-13 +================== + + * Added jQuery-ish array support + +0.0.7 / 2011-03-13 +================== + + * Fixed; handle empty string and `== null` in `qs.parse()` [dmit] + allows for convenient `qs.parse(url.parse(str).query)` + +0.0.6 / 2011-02-14 +================== + + * Fixed; support for implicit arrays + +0.0.4 / 2011-02-09 +================== + + * Fixed `+` as a space + +0.0.3 / 2011-02-08 +================== + + * Fixed case when right-hand value contains "]" + +0.0.2 / 2011-02-07 +================== + + * Fixed "=" presence in key + +0.0.1 / 2011-02-07 +================== + + * Initial release \ No newline at end of file diff --git a/test/node_modules/express/node_modules/qs/Makefile b/test/node_modules/express/node_modules/qs/Makefile new file mode 100644 index 0000000..e4df837 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/Makefile @@ -0,0 +1,5 @@ + +test: + @./node_modules/.bin/mocha + +.PHONY: test \ No newline at end of file diff --git a/test/node_modules/express/node_modules/qs/Readme.md b/test/node_modules/express/node_modules/qs/Readme.md new file mode 100644 index 0000000..db0d145 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/Readme.md @@ -0,0 +1,54 @@ +# node-querystring + + query string parser for node supporting nesting, as it was removed from `0.3.x`, so this library provides the previous and commonly desired behaviour (and twice as fast). Used by [express](http://expressjs.com), [connect](http://senchalabs.github.com/connect) and others. + +## Installation + + $ npm install qs + +## Examples + +```js +var qs = require('qs'); + +qs.parse('user[name][first]=Tobi&user[email]=tobi@learnboost.com'); +// => { user: { name: { first: 'Tobi' }, email: 'tobi@learnboost.com' } } + +qs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }}) +// => user[name]=Tobi&user[email]=tobi%40learnboost.com +``` + +## Testing + +Install dev dependencies: + + $ npm install -d + +and execute: + + $ make test + +## License + +(The MIT License) + +Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca> + +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. \ No newline at end of file diff --git a/test/node_modules/express/node_modules/qs/benchmark.js b/test/node_modules/express/node_modules/qs/benchmark.js new file mode 100644 index 0000000..97e2c93 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/benchmark.js @@ -0,0 +1,17 @@ + +var qs = require('./'); + +var times = 100000 + , start = new Date + , n = times; + +console.log('times: %d', times); + +while (n--) qs.parse('foo=bar'); +console.log('simple: %dms', new Date - start); + +var start = new Date + , n = times; + +while (n--) qs.parse('user[name][first]=tj&user[name][last]=holowaychuk'); +console.log('nested: %dms', new Date - start); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/qs/examples.js b/test/node_modules/express/node_modules/qs/examples.js new file mode 100644 index 0000000..27617b7 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/examples.js @@ -0,0 +1,51 @@ + +/** + * Module dependencies. + */ + +var qs = require('./'); + +var obj = qs.parse('foo'); +console.log(obj) + +var obj = qs.parse('foo=bar=baz'); +console.log(obj) + +var obj = qs.parse('users[]'); +console.log(obj) + +var obj = qs.parse('name=tj&email=tj@vision-media.ca'); +console.log(obj) + +var obj = qs.parse('users[]=tj&users[]=tobi&users[]=jane'); +console.log(obj) + +var obj = qs.parse('user[name][first]=tj&user[name][last]=holowaychuk'); +console.log(obj) + +var obj = qs.parse('users[][name][first]=tj&users[][name][last]=holowaychuk'); +console.log(obj) + +var obj = qs.parse('a=a&a=b&a=c'); +console.log(obj) + +var obj = qs.parse('user[tj]=tj&user[tj]=TJ'); +console.log(obj) + +var obj = qs.parse('user[names]=tj&user[names]=TJ&user[names]=Tyler'); +console.log(obj) + +var obj = qs.parse('user[name][first]=tj&user[name][first]=TJ'); +console.log(obj) + +var obj = qs.parse('user[0]=tj&user[1]=TJ'); +console.log(obj) + +var obj = qs.parse('user[0]=tj&user[]=TJ'); +console.log(obj) + +var obj = qs.parse('user[0]=tj&user[foo]=TJ'); +console.log(obj) + +var str = qs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }}); +console.log(str); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/qs/index.js b/test/node_modules/express/node_modules/qs/index.js new file mode 100644 index 0000000..d177d20 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/querystring'); \ No newline at end of file diff --git a/test/node_modules/express/node_modules/qs/lib/querystring.js b/test/node_modules/express/node_modules/qs/lib/querystring.js new file mode 100644 index 0000000..6c72712 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/lib/querystring.js @@ -0,0 +1,264 @@ + +/*! + * querystring + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Library version. + */ + +exports.version = '0.4.2'; + +/** + * Object#toString() ref for stringify(). + */ + +var toString = Object.prototype.toString; + +/** + * Cache non-integer test regexp. + */ + +var isint = /^[0-9]+$/; + +function promote(parent, key) { + if (parent[key].length == 0) return parent[key] = {}; + var t = {}; + for (var i in parent[key]) t[i] = parent[key][i]; + parent[key] = t; + return t; +} + +function parse(parts, parent, key, val) { + var part = parts.shift(); + // end + if (!part) { + if (Array.isArray(parent[key])) { + parent[key].push(val); + } else if ('object' == typeof parent[key]) { + parent[key] = val; + } else if ('undefined' == typeof parent[key]) { + parent[key] = val; + } else { + parent[key] = [parent[key], val]; + } + // array + } else { + var obj = parent[key] = parent[key] || []; + if (']' == part) { + if (Array.isArray(obj)) { + if ('' != val) obj.push(val); + } else if ('object' == typeof obj) { + obj[Object.keys(obj).length] = val; + } else { + obj = parent[key] = [parent[key], val]; + } + // prop + } else if (~part.indexOf(']')) { + part = part.substr(0, part.length - 1); + if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key); + parse(parts, obj, part, val); + // key + } else { + if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key); + parse(parts, obj, part, val); + } + } +} + +/** + * Merge parent key/val pair. + */ + +function merge(parent, key, val){ + if (~key.indexOf(']')) { + var parts = key.split('[') + , len = parts.length + , last = len - 1; + parse(parts, parent, 'base', val); + // optimize + } else { + if (!isint.test(key) && Array.isArray(parent.base)) { + var t = {}; + for (var k in parent.base) t[k] = parent.base[k]; + parent.base = t; + } + set(parent.base, key, val); + } + + return parent; +} + +/** + * Parse the given obj. + */ + +function parseObject(obj){ + var ret = { base: {} }; + Object.keys(obj).forEach(function(name){ + merge(ret, name, obj[name]); + }); + return ret.base; +} + +/** + * Parse the given str. + */ + +function parseString(str){ + return String(str) + .split('&') + .reduce(function(ret, pair){ + try{ + pair = decodeURIComponent(pair.replace(/\+/g, ' ')); + } catch(e) { + // ignore + } + + var eql = pair.indexOf('=') + , brace = lastBraceInKey(pair) + , key = pair.substr(0, brace || eql) + , val = pair.substr(brace || eql, pair.length) + , val = val.substr(val.indexOf('=') + 1, val.length); + + // ?foo + if ('' == key) key = pair, val = ''; + + return merge(ret, key, val); + }, { base: {} }).base; +} + +/** + * Parse the given query `str` or `obj`, returning an object. + * + * @param {String} str | {Object} obj + * @return {Object} + * @api public + */ + +exports.parse = function(str){ + if (null == str || '' == str) return {}; + return 'object' == typeof str + ? parseObject(str) + : parseString(str); +}; + +/** + * Turn the given `obj` into a query string + * + * @param {Object} obj + * @return {String} + * @api public + */ + +var stringify = exports.stringify = function(obj, prefix) { + if (Array.isArray(obj)) { + return stringifyArray(obj, prefix); + } else if ('[object Object]' == toString.call(obj)) { + return stringifyObject(obj, prefix); + } else if ('string' == typeof obj) { + return stringifyString(obj, prefix); + } else { + return prefix + '=' + obj; + } +}; + +/** + * Stringify the given `str`. + * + * @param {String} str + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyString(str, prefix) { + if (!prefix) throw new TypeError('stringify expects an object'); + return prefix + '=' + encodeURIComponent(str); +} + +/** + * Stringify the given `arr`. + * + * @param {Array} arr + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyArray(arr, prefix) { + var ret = []; + if (!prefix) throw new TypeError('stringify expects an object'); + for (var i = 0; i < arr.length; i++) { + ret.push(stringify(arr[i], prefix + '[]')); + } + return ret.join('&'); +} + +/** + * Stringify the given `obj`. + * + * @param {Object} obj + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyObject(obj, prefix) { + var ret = [] + , keys = Object.keys(obj) + , key; + + for (var i = 0, len = keys.length; i < len; ++i) { + key = keys[i]; + ret.push(stringify(obj[key], prefix + ? prefix + '[' + encodeURIComponent(key) + ']' + : encodeURIComponent(key))); + } + + return ret.join('&'); +} + +/** + * Set `obj`'s `key` to `val` respecting + * the weird and wonderful syntax of a qs, + * where "foo=bar&foo=baz" becomes an array. + * + * @param {Object} obj + * @param {String} key + * @param {String} val + * @api private + */ + +function set(obj, key, val) { + var v = obj[key]; + if (undefined === v) { + obj[key] = val; + } else if (Array.isArray(v)) { + v.push(val); + } else { + obj[key] = [v, val]; + } +} + +/** + * Locate last brace in `str` within the key. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function lastBraceInKey(str) { + var len = str.length + , brace + , c; + for (var i = 0; i < len; ++i) { + c = str[i]; + if (']' == c) brace = false; + if ('[' == c) brace = true; + if ('=' == c && !brace) return i; + } +} diff --git a/test/node_modules/express/node_modules/qs/package.json b/test/node_modules/express/node_modules/qs/package.json new file mode 100644 index 0000000..8bb9ea5 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/package.json @@ -0,0 +1,26 @@ +{ + "name": "qs", + "description": "querystring parser", + "version": "0.4.2", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/node-querystring.git" + }, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "main": "index", + "engines": { + "node": "*" + }, + "readme": "# node-querystring\n\n query string parser for node supporting nesting, as it was removed from `0.3.x`, so this library provides the previous and commonly desired behaviour (and twice as fast). Used by [express](http://expressjs.com), [connect](http://senchalabs.github.com/connect) and others.\n\n## Installation\n\n $ npm install qs\n\n## Examples\n\n```js\nvar qs = require('qs');\n\nqs.parse('user[name][first]=Tobi&user[email]=tobi@learnboost.com');\n// => { user: { name: { first: 'Tobi' }, email: 'tobi@learnboost.com' } }\n\nqs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }})\n// => user[name]=Tobi&user[email]=tobi%40learnboost.com\n```\n\n## Testing\n\nInstall dev dependencies:\n\n $ npm install -d\n\nand execute:\n\n $ make test\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "readmeFilename": "Readme.md", + "_id": "qs@0.4.2", + "_from": "qs@0.4.x" +} diff --git a/test/node_modules/express/node_modules/qs/test/mocha.opts b/test/node_modules/express/node_modules/qs/test/mocha.opts new file mode 100644 index 0000000..521cbb2 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/test/mocha.opts @@ -0,0 +1,2 @@ +--require should +--ui exports diff --git a/test/node_modules/express/node_modules/qs/test/parse.js b/test/node_modules/express/node_modules/qs/test/parse.js new file mode 100644 index 0000000..f219e27 --- /dev/null +++ b/test/node_modules/express/node_modules/qs/test/parse.js @@ -0,0 +1,167 @@ + +/** + * Module dependencies. + */ + +var qs = require('../'); + +module.exports = { + 'test basics': function(){ + qs.parse('0=foo').should.eql({ '0': 'foo' }); + + qs.parse('foo=c++') + .should.eql({ foo: 'c ' }); + + qs.parse('a[>=]=23') + .should.eql({ a: { '>=': '23' }}); + + qs.parse('a[<=>]==23') + .should.eql({ a: { '<=>': '=23' }}); + + qs.parse('a[==]=23') + .should.eql({ a: { '==': '23' }}); + + qs.parse('foo') + .should.eql({ foo: '' }); + + qs.parse('foo=bar') + .should.eql({ foo: 'bar' }); + + qs.parse('foo%3Dbar=baz') + .should.eql({ foo: 'bar=baz' }); + + qs.parse(' foo = bar = baz ') + .should.eql({ ' foo ': ' bar = baz ' }); + + qs.parse('foo=bar=baz') + .should.eql({ foo: 'bar=baz' }); + + qs.parse('foo=bar&bar=baz') + .should.eql({ foo: 'bar', bar: 'baz' }); + + qs.parse('foo=bar&baz') + .should.eql({ foo: 'bar', baz: '' }); + + qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World') + .should.eql({ + cht: 'p3' + , chd: 't:60,40' + , chs: '250x100' + , chl: 'Hello|World' + }); + }, + + 'test nesting': function(){ + qs.parse('ops[>=]=25') + .should.eql({ ops: { '>=': '25' }}); + + qs.parse('user[name]=tj') + .should.eql({ user: { name: 'tj' }}); + + qs.parse('user[name][first]=tj&user[name][last]=holowaychuk') + .should.eql({ user: { name: { first: 'tj', last: 'holowaychuk' }}}); + }, + + 'test escaping': function(){ + qs.parse('foo=foo%20bar') + .should.eql({ foo: 'foo bar' }); + }, + + 'test arrays': function(){ + qs.parse('images[]') + .should.eql({ images: [] }); + + qs.parse('user[]=tj') + .should.eql({ user: ['tj'] }); + + qs.parse('user[]=tj&user[]=tobi&user[]=jane') + .should.eql({ user: ['tj', 'tobi', 'jane'] }); + + qs.parse('user[names][]=tj&user[names][]=tyler') + .should.eql({ user: { names: ['tj', 'tyler'] }}); + + qs.parse('user[names][]=tj&user[names][]=tyler&user[email]=tj@vision-media.ca') + .should.eql({ user: { names: ['tj', 'tyler'], email: 'tj@vision-media.ca' }}); + + qs.parse('items=a&items=b') + .should.eql({ items: ['a', 'b'] }); + + qs.parse('user[names]=tj&user[names]=holowaychuk&user[names]=TJ') + .should.eql({ user: { names: ['tj', 'holowaychuk', 'TJ'] }}); + + qs.parse('user[name][first]=tj&user[name][first]=TJ') + .should.eql({ user: { name: { first: ['tj', 'TJ'] }}}); + + var o = qs.parse('existing[fcbaebfecc][name][last]=tj') + o.should.eql({ existing: { 'fcbaebfecc': { name: { last: 'tj' }}}}) + Array.isArray(o.existing).should.be.false; + }, + + 'test right-hand brackets': function(){ + qs.parse('pets=["tobi"]') + .should.eql({ pets: '["tobi"]' }); + + qs.parse('operators=[">=", "<="]') + .should.eql({ operators: '[">=", "<="]' }); + + qs.parse('op[>=]=[1,2,3]') + .should.eql({ op: { '>=': '[1,2,3]' }}); + + qs.parse('op[>=]=[1,2,3]&op[=]=[[[[1]]]]') + .should.eql({ op: { '>=': '[1,2,3]', '=': '[[[[1]]]]' }}); + }, + + 'test duplicates': function(){ + qs.parse('items=bar&items=baz&items=raz') + .should.eql({ items: ['bar', 'baz', 'raz'] }); + }, + + 'test empty': function(){ + qs.parse('').should.eql({}); + qs.parse(undefined).should.eql({}); + qs.parse(null).should.eql({}); + }, + + 'test arrays with indexes': function(){ + qs.parse('foo[0]=bar&foo[1]=baz').should.eql({ foo: ['bar', 'baz'] }); + qs.parse('foo[1]=bar&foo[0]=baz').should.eql({ foo: ['baz', 'bar'] }); + qs.parse('foo[base64]=RAWR').should.eql({ foo: { base64: 'RAWR' }}); + qs.parse('foo[64base]=RAWR').should.eql({ foo: { '64base': 'RAWR' }}); + }, + + 'test arrays becoming objects': function(){ + qs.parse('foo[0]=bar&foo[bad]=baz').should.eql({ foo: { 0: "bar", bad: "baz" }}); + qs.parse('foo[bad]=baz&foo[0]=bar').should.eql({ foo: { 0: "bar", bad: "baz" }}); + }, + + 'test bleed-through of Array native properties/methods': function(){ + Array.prototype.protoProperty = true; + Array.prototype.protoFunction = function () {}; + qs.parse('foo=bar').should.eql({ foo: 'bar' }); + }, + + 'test malformed uri': function(){ + qs.parse('{%:%}').should.eql({ '{%:%}': '' }); + qs.parse('foo=%:%}').should.eql({ 'foo': '%:%}' }); + }, + + 'test semi-parsed': function(){ + qs.parse({ 'user[name]': 'tobi' }) + .should.eql({ user: { name: 'tobi' }}); + + qs.parse({ 'user[name]': 'tobi', 'user[email][main]': 'tobi@lb.com' }) + .should.eql({ user: { name: 'tobi', email: { main: 'tobi@lb.com' } }}); + } + + // 'test complex': function(){ + // qs.parse('users[][name][first]=tj&users[foo]=bar') + // .should.eql({ + // users: [ { name: 'tj' }, { name: 'tobi' }, { foo: 'bar' }] + // }); + // + // qs.parse('users[][name][first]=tj&users[][name][first]=tobi') + // .should.eql({ + // users: [ { name: 'tj' }, { name: 'tobi' }] + // }); + // } +}; diff --git a/test/node_modules/express/node_modules/qs/test/stringify.js b/test/node_modules/express/node_modules/qs/test/stringify.js new file mode 100644 index 0000000..c2195cb --- /dev/null +++ b/test/node_modules/express/node_modules/qs/test/stringify.js @@ -0,0 +1,103 @@ + +/** + * Module dependencies. + */ + +var qs = require('../') + , should = require('should') + , str_identities = { + 'basics': [ + { str: 'foo=bar', obj: {'foo' : 'bar'}}, + { str: 'foo=%22bar%22', obj: {'foo' : '\"bar\"'}}, + { str: 'foo=', obj: {'foo': ''}}, + { str: 'foo=1&bar=2', obj: {'foo' : '1', 'bar' : '2'}}, + { str: 'my%20weird%20field=q1!2%22\'w%245%267%2Fz8)%3F', obj: {'my weird field': "q1!2\"'w$5&7/z8)?"}}, + { str: 'foo%3Dbaz=bar', obj: {'foo=baz': 'bar'}}, + { str: 'foo=bar&bar=baz', obj: {foo: 'bar', bar: 'baz'}} + ], + 'escaping': [ + { str: 'foo=foo%20bar', obj: {foo: 'foo bar'}}, + { str: 'cht=p3&chd=t%3A60%2C40&chs=250x100&chl=Hello%7CWorld', obj: { + cht: 'p3' + , chd: 't:60,40' + , chs: '250x100' + , chl: 'Hello|World' + }} + ], + 'nested': [ + { str: 'foo[]=bar&foo[]=quux', obj: {'foo' : ['bar', 'quux']}}, + { str: 'foo[]=bar', obj: {foo: ['bar']}}, + { str: 'foo[]=1&foo[]=2', obj: {'foo' : ['1', '2']}}, + { str: 'foo=bar&baz[]=1&baz[]=2&baz[]=3', obj: {'foo' : 'bar', 'baz' : ['1', '2', '3']}}, + { str: 'foo[]=bar&baz[]=1&baz[]=2&baz[]=3', obj: {'foo' : ['bar'], 'baz' : ['1', '2', '3']}}, + { str: 'x[y][z]=1', obj: {'x' : {'y' : {'z' : '1'}}}}, + { str: 'x[y][z][]=1', obj: {'x' : {'y' : {'z' : ['1']}}}}, + { str: 'x[y][z]=2', obj: {'x' : {'y' : {'z' : '2'}}}}, + { str: 'x[y][z][]=1&x[y][z][]=2', obj: {'x' : {'y' : {'z' : ['1', '2']}}}}, + { str: 'x[y][][z]=1', obj: {'x' : {'y' : [{'z' : '1'}]}}}, + { str: 'x[y][][z][]=1', obj: {'x' : {'y' : [{'z' : ['1']}]}}}, + { str: 'x[y][][z]=1&x[y][][w]=2', obj: {'x' : {'y' : [{'z' : '1', 'w' : '2'}]}}}, + { str: 'x[y][][v][w]=1', obj: {'x' : {'y' : [{'v' : {'w' : '1'}}]}}}, + { str: 'x[y][][z]=1&x[y][][v][w]=2', obj: {'x' : {'y' : [{'z' : '1', 'v' : {'w' : '2'}}]}}}, + { str: 'x[y][][z]=1&x[y][][z]=2', obj: {'x' : {'y' : [{'z' : '1'}, {'z' : '2'}]}}}, + { str: 'x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3', obj: {'x' : {'y' : [{'z' : '1', 'w' : 'a'}, {'z' : '2', 'w' : '3'}]}}}, + { str: 'user[name][first]=tj&user[name][last]=holowaychuk', obj: { user: { name: { first: 'tj', last: 'holowaychuk' }}}} + ], + 'errors': [ + { obj: 'foo=bar', message: 'stringify expects an object' }, + { obj: ['foo', 'bar'], message: 'stringify expects an object' } + ], + 'numbers': [ + { str: 'limit[]=1&limit[]=2&limit[]=3', obj: { limit: [1, 2, '3'] }}, + { str: 'limit=1', obj: { limit: 1 }} + ] + }; + + +// Assert error +function err(fn, msg){ + var err; + try { + fn(); + } catch (e) { + should.equal(e.message, msg); + return; + } + throw new Error('no exception thrown, expected "' + msg + '"'); +} + +function test(type) { + var str, obj; + for (var i = 0; i < str_identities[type].length; i++) { + str = str_identities[type][i].str; + obj = str_identities[type][i].obj; + qs.stringify(obj).should.eql(str); + } +} + +module.exports = { + 'test basics': function() { + test('basics'); + }, + + 'test escaping': function() { + test('escaping'); + }, + + 'test nested': function() { + test('nested'); + }, + + 'test numbers': function(){ + test('numbers'); + }, + + 'test errors': function() { + var obj, message; + for (var i = 0; i < str_identities['errors'].length; i++) { + message = str_identities['errors'][i].message; + obj = str_identities['errors'][i].obj; + err(function(){ qs.stringify(obj) }, message); + } + } +}; \ No newline at end of file diff --git a/test/node_modules/express/package.json b/test/node_modules/express/package.json new file mode 100644 index 0000000..ab497b1 --- /dev/null +++ b/test/node_modules/express/package.json @@ -0,0 +1,71 @@ +{ + "name": "express", + "description": "Sinatra inspired web development framework", + "version": "2.5.11", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "Aaron Heckmann", + "email": "aaron.heckmann+github@gmail.com" + }, + { + "name": "Ciaran Jessup", + "email": "ciaranj@gmail.com" + }, + { + "name": "Guillermo Rauch", + "email": "rauchg@gmail.com" + } + ], + "dependencies": { + "connect": "1.x", + "mime": "1.2.4", + "qs": "0.4.x", + "mkdirp": "0.3.0" + }, + "devDependencies": { + "connect-form": "0.2.1", + "ejs": "0.4.2", + "expresso": "0.9.2", + "hamljs": "0.6.x", + "jade": "0.16.2", + "stylus": "0.13.0", + "should": "0.3.2", + "express-messages": "0.0.2", + "node-markdown": ">= 0.0.1", + "connect-redis": ">= 0.0.1" + }, + "keywords": [ + "framework", + "sinatra", + "web", + "rest", + "restful" + ], + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/express" + }, + "main": "index", + "bin": { + "express": "./bin/express" + }, + "scripts": { + "test": "make test", + "prepublish": "npm prune" + }, + "readme": "\n# Express\n \n Insanely fast (and small) server-side JavaScript web development framework\n built on [node](http://nodejs.org) and [Connect](http://github.com/senchalabs/connect).\n \n var app = express.createServer();\n \n app.get('/', function(req, res){\n res.send('Hello World');\n });\n \n app.listen(3000);\n\n## Installation\n\n $ npm install express\n\nor to access the `express(1)` executable install globally:\n\n $ npm install -g express\n\n## Quick Start\n\n The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:\n\n Create the app:\n\n $ npm install -g express\n $ express /tmp/foo && cd /tmp/foo\n\n Install dependencies:\n\n $ npm install -d\n\n Start the server:\n\n $ node app.js\n\n## Features\n\n * Robust routing\n * Redirection helpers\n * Dynamic view helpers\n * Content negotiation\n * Focus on high performance\n * View rendering and partials support\n * Environment based configuration\n * Session based flash notifications\n * Built on [Connect](http://github.com/senchalabs/connect)\n * High test coverage\n * Executable for generating applications quickly\n * Application level view options\n\nVia Connect:\n\n * Session support\n * Cache API\n * Mime helpers\n * ETag support\n * Persistent flash notifications\n * Cookie support\n * JSON-RPC\n * Logging\n * and _much_ more!\n\n## Contributors\n\nThe following are the major contributors of Express (in no specific order).\n\n * TJ Holowaychuk ([visionmedia](http://github.com/visionmedia))\n * Ciaran Jessup ([ciaranj](http://github.com/ciaranj))\n * Aaron Heckmann ([aheckmann](http://github.com/aheckmann))\n * Guillermo Rauch ([guille](http://github.com/guille))\n\n## More Information\n\n * #express on freenode\n * [express-expose](http://github.com/visionmedia/express-expose) expose objects, functions, modules and more to client-side js with ease\n * [express-configure](http://github.com/visionmedia/express-configuration) async configuration support\n * [express-messages](http://github.com/visionmedia/express-messages) flash notification rendering helper\n * [express-namespace](http://github.com/visionmedia/express-namespace) namespaced route support\n * [express-params](https://github.com/visionmedia/express-params) param pre-condition functions\n * [express-mongoose](https://github.com/LearnBoost/express-mongoose) plugin for easy rendering of Mongoose async Query results\n * Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates\n * [Google Group](http://groups.google.com/group/express-js) for discussion\n * Visit the [Wiki](http://github.com/visionmedia/express/wiki)\n * [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito)\n * Screencast - [Introduction](http://bit.ly/eRYu0O)\n * Screencast - [View Partials](http://bit.ly/dU13Fx)\n * Screencast - [Route Specific Middleware](http://bit.ly/hX4IaH)\n * Screencast - [Route Path Placeholder Preconditions](http://bit.ly/eNqmVs)\n\n## Node Compatibility\n\nExpress 1.x is compatible with node 0.2.x and connect < 1.0.\n\nExpress 2.x is compatible with node 0.4.x or 0.6.x, and connect 1.x\n\nExpress 3.x (master) will be compatible with node 0.6.x and connect 2.x\n\n## Viewing Examples\n\nFirst install the dev dependencies to install all the example / test suite deps:\n\n $ npm install\n\nthen run whichever tests you want:\n\n $ node examples/jade/app.js\n\n## Running Tests\n\nTo run the test suite first invoke the following command within the repo, installing the development dependencies:\n\n $ npm install\n\nthen run the tests:\n\n $ make test\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "Readme.md", + "_id": "express@2.5.11", + "dist": { + "shasum": "6bd0457244aed17e9619e779d06db4d4742867c3" + }, + "_from": "express@2.5.11" +} diff --git a/test/node_modules/express/test.js b/test/node_modules/express/test.js new file mode 100644 index 0000000..1f5ac01 --- /dev/null +++ b/test/node_modules/express/test.js @@ -0,0 +1,12 @@ + +var express = require('./'); +var app = express.createServer(); + +app.use(express.logger('dev')); + +app.get('/', function(req, res){ + console.log(req.protocol); + res.send('hello'); +}); + +app.listen(3000); diff --git a/test/node_modules/jade/.npmignore b/test/node_modules/jade/.npmignore new file mode 100644 index 0000000..fdc7b89 --- /dev/null +++ b/test/node_modules/jade/.npmignore @@ -0,0 +1,14 @@ +test +support +benchmarks +examples +lib-cov +coverage.html +.gitmodules +.travis.yml +History.md +Makefile +test/ +support/ +benchmarks/ +examples/ diff --git a/test/node_modules/jade/LICENSE b/test/node_modules/jade/LICENSE new file mode 100644 index 0000000..8ad0e0d --- /dev/null +++ b/test/node_modules/jade/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2009-2010 TJ Holowaychuk + +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. \ No newline at end of file diff --git a/test/node_modules/jade/Readme.md b/test/node_modules/jade/Readme.md new file mode 100644 index 0000000..9f629d9 --- /dev/null +++ b/test/node_modules/jade/Readme.md @@ -0,0 +1,1298 @@ + [![Build Status](https://secure.travis-ci.org/visionmedia/jade.png)](http://travis-ci.org/visionmedia/jade) + +# Jade - template engine + + Jade is a high performance template engine heavily influenced by [Haml](http://haml-lang.com) + and implemented with JavaScript for [node](http://nodejs.org). For discussion join the [Google Group](http://groups.google.com/group/jadejs). + +## Test drive + + You can test drive Jade online [here](http://naltatis.github.com/jade-syntax-docs). + +## README Contents + +- [Features](#a1) +- [Implementations](#a2) +- [Installation](#a3) +- [Browser Support](#a4) +- [Public API](#a5) +- [Syntax](#a6) + - [Line Endings](#a6-1) + - [Tags](#a6-2) + - [Tag Text](#a6-3) + - [Comments](#a6-4) + - [Block Comments](#a6-5) + - [Nesting](#a6-6) + - [Block Expansion](#a6-7) + - [Case](#a6-8) + - [Attributes](#a6-9) + - [HTML](#a6-10) + - [Doctypes](#a6-11) +- [Filters](#a7) +- [Code](#a8) +- [Iteration](#a9) +- [Conditionals](#a10) +- [Template inheritance](#a11) +- [Block append / prepend](#a12) +- [Includes](#a13) +- [Mixins](#a14) +- [Generated Output](#a15) +- [Example Makefile](#a16) +- [jade(1)](#a17) +- [Tutorials](#a18) +- [License](#a19) + + +## Features + + - client-side support + - great readability + - flexible indentation + - block-expansion + - mixins + - static includes + - attribute interpolation + - code is escaped by default for security + - contextual error reporting at compile & run time + - executable for compiling jade templates via the command line + - html 5 mode (the default doctype) + - optional memory caching + - combine dynamic and static tag classes + - parse tree manipulation via _filters_ + - template inheritance + - block append / prepend + - supports [Express JS](http://expressjs.com) out of the box + - transparent iteration over objects, arrays, and even non-enumerables via `each` + - block comments + - no tag prefix + - filters + - :stylus must have [stylus](http://github.com/LearnBoost/stylus) installed + - :less must have [less.js](http://github.com/cloudhead/less.js) installed + - :markdown must have [markdown-js](http://github.com/evilstreak/markdown-js), [node-discount](http://github.com/visionmedia/node-discount), or [marked](http://github.com/chjj/marked) installed + - :cdata + - :coffeescript must have [coffee-script](http://jashkenas.github.com/coffee-script/) installed + - [Emacs Mode](https://github.com/brianc/jade-mode) + - [Vim Syntax](https://github.com/digitaltoad/vim-jade) + - [TextMate Bundle](http://github.com/miksago/jade-tmbundle) + - [Coda/SubEtha syntax Mode](https://github.com/aaronmccall/jade.mode) + - [Screencasts](http://tjholowaychuk.com/post/1004255394/jade-screencast-template-engine-for-nodejs) + - [html2jade](https://github.com/donpark/html2jade) converter + + +## Implementations + + - [php](http://github.com/everzet/jade.php) + - [scala](http://scalate.fusesource.org/versions/snapshot/documentation/scaml-reference.html) + - [ruby](http://github.com/stonean/slim) + - [python](https://github.com/SyrusAkbary/pyjade) + - [java](https://github.com/neuland/jade4j) + + +## Installation + +via npm: + +```bash +$ npm install jade +``` + + +## Browser Support + + To compile jade to a single file compatible for client-side use simply execute: + +```bash +$ make jade.js +``` + + Alternatively, if uglifyjs is installed via npm (`npm install uglify-js`) you may execute the following which will create both files. However each release builds these for you. + +```bash +$ make jade.min.js +``` + + By default Jade instruments templates with line number statements such as `__.lineno = 3` for debugging purposes. When used in a browser it's useful to minimize this boiler plate, you can do so by passing the option `{ compileDebug: false }`. The following template + +```jade +p Hello #{name} +``` + + Can then be as small as the following generated function: + +```js +function anonymous(locals, attrs, escape, rethrow) { + var buf = []; + with (locals || {}) { + var interp; + buf.push('\n

    Hello ' + escape((interp = name) == null ? '' : interp) + '\n

    '); + } + return buf.join(""); +} +``` + + Through the use of Jade's `./runtime.js` you may utilize these pre-compiled templates on the client-side _without_ Jade itself, all you need is the associated utility functions (in runtime.js), which are then available as `jade.attrs`, `jade.escape` etc. To enable this you should pass `{ client: true }` to `jade.compile()` to tell Jade to reference the helper functions + via `jade.attrs`, `jade.escape` etc. + +```js +function anonymous(locals, attrs, escape, rethrow) { + var attrs = jade.attrs, escape = jade.escape, rethrow = jade.rethrow; + var buf = []; + with (locals || {}) { + var interp; + buf.push('\n

    Hello ' + escape((interp = name) == null ? '' : interp) + '\n

    '); + } + return buf.join(""); +} +``` + +
    +## Public API + +```js +var jade = require('jade'); + +// Compile a function +var fn = jade.compile('string of jade', options); +fn(locals); +``` + +### Options + + - `self` Use a `self` namespace to hold the locals _(false by default)_ + - `locals` Local variable object + - `filename` Used in exceptions, and required when using includes + - `debug` Outputs tokens and function body generated + - `compiler` Compiler to replace jade's default + - `compileDebug` When `false` no debug instrumentation is compiled + - `pretty` Add pretty-indentation whitespace to output _(false by default)_ + + +## Syntax + + +### Line Endings + +**CRLF** and **CR** are converted to **LF** before parsing. + + +### Tags + +A tag is simply a leading word: + +```jade +html +``` + +for example is converted to `` + +tags can also have ids: + +```jade +div#container +``` + +which would render `
    ` + +how about some classes? + +```jade +div.user-details +``` + +renders `
    ` + +multiple classes? _and_ an id? sure: + +```jade +div#foo.bar.baz +``` + +renders `
    ` + +div div div sure is annoying, how about: + +```jade +#foo +.bar +``` + +which is syntactic sugar for what we have already been doing, and outputs: + +```html +
    +``` + +
    +### Tag Text + +Simply place some content after the tag: + +```jade +p wahoo! +``` + +renders `

    wahoo!

    `. + +well cool, but how about large bodies of text: + +```jade +p + | foo bar baz + | rawr rawr + | super cool + | go jade go +``` + +renders `

    foo bar baz rawr.....

    ` + +interpolation? yup! both types of text can utilize interpolation, +if we passed `{ name: 'tj', email: 'tj@vision-media.ca' }` to the compiled function we can do the following: + +```jade +#user #{name} <#{email}> +``` + +outputs `
    tj <tj@vision-media.ca>
    ` + +Actually want `#{}` for some reason? escape it! + +```jade +p \#{something} +``` + +now we have `

    #{something}

    ` + +We can also utilize the unescaped variant `!{html}`, so the following +will result in a literal script tag: + +```jade +- var html = "" +| !{html} +``` + +Nested tags that also contain text can optionally use a text block: + +```jade +label + | Username: + input(name='user[name]') +``` + +or immediate tag text: + +```jade +label Username: + input(name='user[name]') +``` + +Tags that accept _only_ text such as `script` and `style` do not +need the leading `|` character, for example: + +```jade +html + head + title Example + script + if (foo) { + bar(); + } else { + baz(); + } +``` + +Once again as an alternative, we may use a trailing `.` to indicate a text block, for example: + +```jade +p. + foo asdf + asdf + asdfasdfaf + asdf + asd. +``` + +outputs: + +```html +

    foo asdf +asdf + asdfasdfaf + asdf +asd. +

    +``` + +This however differs from a trailing `.` followed by a space, which although is ignored by the Jade parser, tells Jade that this period is a literal: + +```jade +p . +``` + +outputs: + +```html +

    .

    +``` + +It should be noted that text blocks should be doubled escaped. For example if you desire the following output. + +```html +

    foo\bar

    +``` + +use: + +```jade +p. + foo\\bar +``` + +
    +### Comments + +Single line comments currently look the same as JavaScript comments, +aka `//` and must be placed on their own line: + +```jade +// just some paragraphs +p foo +p bar +``` + +would output + +```html + +

    foo

    +

    bar

    +``` + +Jade also supports unbuffered comments, by simply adding a hyphen: + +```jade +//- will not output within markup +p foo +p bar +``` + +outputting + +```html +

    foo

    +

    bar

    +``` + +
    +### Block Comments + + A block comment is legal as well: + +```jade +body + // + #content + h1 Example +``` + +outputting + +```html + + + +``` + +Jade supports conditional-comments as well, for example: + +```jade +head + //if lt IE 8 + script(src='/ie-sucks.js') +``` + +outputs: + +```html + + + +``` + + +### Nesting + + Jade supports nesting to define the tags in a natural way: + +```jade +ul + li.first + a(href='#') foo + li + a(href='#') bar + li.last + a(href='#') baz +``` + + +### Block Expansion + + Block expansion allows you to create terse single-line nested tags, + the following example is equivalent to the nesting example above. + +```jade +ul + li.first: a(href='#') foo + li: a(href='#') bar + li.last: a(href='#') baz +``` + + +### Case + + The case statement takes the following form: + +```jade +html + body + friends = 10 + case friends + when 0 + p you have no friends + when 1 + p you have a friend + default + p you have #{friends} friends +``` + + Block expansion may also be used: + +```jade +friends = 5 + +html + body + case friends + when 0: p you have no friends + when 1: p you have a friend + default: p you have #{friends} friends +``` + + +### Attributes + +Jade currently supports `(` and `)` as attribute delimiters. + +```jade +a(href='/login', title='View login page') Login +``` + +When a value is `undefined` or `null` the attribute is _not_ added, +so this is fine, it will not compile `something="null"`. + +```jade +div(something=null) +``` + +Boolean attributes are also supported: + +```jade +input(type="checkbox", checked) +``` + +Boolean attributes with code will only output the attribute when `true`: + +```jade +input(type="checkbox", checked=someValue) +``` + +Multiple lines work too: + +```jade +input(type='checkbox', + name='agreement', + checked) +``` + +Multiple lines without the comma work fine: + +```jade +input(type='checkbox' + name='agreement' + checked) +``` + +Funky whitespace? fine: + +```jade +input( + type='checkbox' + name='agreement' + checked) +``` + +Colons work: + +```jade +rss(xmlns:atom="atom") +``` + +Suppose we have the `user` local `{ id: 12, name: 'tobi' }` +and we wish to create an anchor tag with `href` pointing to "/user/12" +we could use regular javascript concatenation: + +```jade +a(href='/user/' + user.id)= user.name +``` + +or we could use jade's interpolation, which I added because everyone +using Ruby or CoffeeScript seems to think this is legal js..: + +```jade +a(href='/user/#{user.id}')= user.name +``` + +The `class` attribute is special-cased when an array is given, +allowing you to pass an array such as `bodyClasses = ['user', 'authenticated']` directly: + +```jade +body(class=bodyClasses) +``` + + +### HTML + + Inline html is fine, we can use the pipe syntax to + write arbitrary text, in this case some html: + +```jade +html + body + |

    Title

    + |

    foo bar baz

    +``` + + Or we can use the trailing `.` to indicate to Jade that we + only want text in this block, allowing us to omit the pipes: + +```jade +html + body. +

    Title

    +

    foo bar baz

    +``` + + Both of these examples yield the same result: + +```html +

    Title

    +

    foo bar baz

    + +``` + + The same rule applies for anywhere you can have text + in jade, raw html is fine: + +```jade +html + body + h1 User #{name} +``` + +
    +### Doctypes + +To add a doctype simply use `!!!`, or `doctype` followed by an optional value: + +```jade +!!! +``` + +or + +```jade +doctype +``` + +Will output the _html 5_ doctype, however: + +```jade +!!! transitional +``` + +Will output the _transitional_ doctype. + +Doctypes are case-insensitive, so the following are equivalent: + +```jade +doctype Basic +doctype basic +``` + +it's also possible to simply pass a doctype literal: + +```jade +doctype html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN +``` + +yielding: + +```html + +``` + +Below are the doctypes defined by default, which can easily be extended: + +```js +var doctypes = exports.doctypes = { + '5': '', + 'default': '', + 'xml': '', + 'transitional': '', + 'strict': '', + 'frameset': '', + '1.1': '', + 'basic': '', + 'mobile': '' +}; +``` + +To alter the default simply change: + +```js +jade.doctypes.default = 'whatever you want'; +``` + + +## Filters + +Filters are prefixed with `:`, for example `:markdown` and +pass the following block of text to an arbitrary function for processing. View the _features_ +at the top of this document for available filters. + +```jade +body + :markdown + Woah! jade _and_ markdown, very **cool** + we can even link to [stuff](http://google.com) +``` + +Renders: + +```html +

    Woah! jade and markdown, very cool we can even link to stuff

    +``` + + +## Code + +Jade currently supports three classifications of executable code. The first +is prefixed by `-`, and is not buffered: + +```jade +- var foo = 'bar'; +``` + +This can be used for conditionals, or iteration: + +```jade +- for (var key in obj) + p= obj[key] +``` + +Due to Jade's buffering techniques the following is valid as well: + +```jade +- if (foo) + ul + li yay + li foo + li worked +- else + p oh no! didnt work +``` + +Hell, even verbose iteration: + +```jade +- if (items.length) + ul + - items.forEach(function(item){ + li= item + - }) +``` + +Anything you want! + +Next up we have _escaped_ buffered code, which is used to +buffer a return value, which is prefixed by `=`: + +```jade +- var foo = 'bar' += foo +h1= foo +``` + +Which outputs `bar

    bar

    `. Code buffered by `=` is escaped +by default for security, however to output unescaped return values +you may use `!=`: + +```jade +p!= aVarContainingMoreHTML +``` + + Jade also has designer-friendly variants, making the literal JavaScript + more expressive and declarative. For example the following assignments + are equivalent, and the expression is still regular javascript: + +```jade +- var foo = 'foo ' + 'bar' +foo = 'foo ' + 'bar' +``` + + Likewise Jade has first-class `if`, `else if`, `else`, `until`, `while`, `unless` among others, however you must remember that the expressions are still regular javascript: + +```jade +if foo == 'bar' + ul + li yay + li foo + li worked +else + p oh no! didnt work +``` + +
    +## Iteration + + Along with vanilla JavaScript Jade also supports a subset of + constructs that allow you to create more designer-friendly templates, + one of these constructs is `each`, taking the form: + +```jade +each VAL[, KEY] in OBJ +``` + +An example iterating over an array: + +```jade +- var items = ["one", "two", "three"] +each item in items + li= item +``` + +outputs: + +```html +
  • one
  • +
  • two
  • +
  • three
  • +``` + +iterating an array with index: + +```jade +items = ["one", "two", "three"] +each item, i in items + li #{item}: #{i} +``` + +outputs: + +```html +
  • one: 0
  • +
  • two: 1
  • +
  • three: 2
  • +``` + +iterating an object's keys and values: + +```jade +obj = { foo: 'bar' } +each val, key in obj + li #{key}: #{val} +``` + +would output `
  • foo: bar
  • ` + +Internally Jade converts these statements to regular +JavaScript loops such as `users.forEach(function(user){`, +so lexical scope and nesting applies as it would with regular +JavaScript: + +```jade +each user in users + each role in user.roles + li= role +``` + + You may also use `for` if you prefer: + +```jade +for user in users + for role in user.roles + li= role +``` + +
    +## Conditionals + + Jade conditionals are equivalent to those using the code (`-`) prefix, + however allow you to ditch parenthesis to become more designer friendly, + however keep in mind the expression given is _regular_ JavaScript: + +```jade +for user in users + if user.role == 'admin' + p #{user.name} is an admin + else + p= user.name +``` + + is equivalent to the following using vanilla JavaScript literals: + +```jade +for user in users + - if (user.role == 'admin') + p #{user.name} is an admin + - else + p= user.name +``` + + Jade also provides have `unless` which is equivalent to `if (!(expr))`: + +```jade +for user in users + unless user.isAnonymous + p + | Click to view + a(href='/users/' + user.id)= user.name +``` + + +## Template inheritance + + Jade supports template inheritance via the `block` and `extends` keywords. A block is simply a "block" of Jade that may be replaced within a child template, this process is recursive. To activate template inheritance in Express 2.x you must add: `app.set('view options', { layout: false });`. + + Jade blocks can provide default content if desired, however optional as shown below by `block scripts`, `block content`, and `block foot`. + +```jade +html + head + h1 My Site - #{title} + block scripts + script(src='/jquery.js') + body + block content + block foot + #footer + p some footer content +``` + + Now to extend the layout, simply create a new file and use the `extends` directive as shown below, giving the path (with or without the .jade extension). You may now define one or more blocks that will override the parent block content, note that here the `foot` block is _not_ redefined and will output "some footer content". + +```jade +extends layout + +block scripts + script(src='/jquery.js') + script(src='/pets.js') + +block content + h1= title + each pet in pets + include pet +``` + + It's also possible to override a block to provide additional blocks, as shown in the following example where `content` now exposes a `sidebar` and `primary` block for overriding, or the child template could override `content` all together. + +```jade +extends regular-layout + +block content + .sidebar + block sidebar + p nothing + .primary + block primary + p nothing +``` + + +## Block append / prepend + + Jade allows you to _replace_ (default), _prepend_, or _append_ blocks. Suppose for example you have default scripts in a "head" block that you wish to utilize on _every_ page, you might do this: + +```jade +html + head + block head + script(src='/vendor/jquery.js') + script(src='/vendor/caustic.js') + body + block content +``` + + Now suppose you have a page of your application for a JavaScript game, you want some game related scripts as well as these defaults, you can simply `append` the block: + +```jade +extends layout + +block append head + script(src='/vendor/three.js') + script(src='/game.js') +``` + + When using `block append` or `block prepend` the `block` is optional: + +```jade +extends layout + +append head + script(src='/vendor/three.js') + script(src='/game.js') +``` + + +## Includes + + Includes allow you to statically include chunks of Jade, + or other content like css, or html which lives in separate files. The classical example is including a header and footer. Suppose we have the following directory structure: + + ./layout.jade + ./includes/ + ./head.jade + ./foot.jade + +and the following _layout.jade_: + +```jade +html + include includes/head + body + h1 My Site + p Welcome to my super amazing site. + include includes/foot +``` + +both includes _includes/head_ and _includes/foot_ are +read relative to the `filename` option given to _layout.jade_, +which should be an absolute path to this file, however Express does this for you. Include then parses these files, and injects the AST produced to render what you would expect: + +```html + + + My Site + + + +

    My Site

    +

    Welcome to my super lame site.

    + + + +``` + + As mentioned `include` can be used to include other content + such as html or css. By providing an extension Jade will not + assume that the file is Jade source and will include it as + a literal: + +```jade +html + body + include content.html +``` + + Include directives may also accept a block, in which case the + the given block will be appended to the _last_ block defined + in the file. For example if `head.jade` contains: + +```jade +head + script(src='/jquery.js') +``` + + We may append values by providing a block to `include head` + as shown below, adding the two scripts. + +```jade +html + include head + script(src='/foo.js') + script(src='/bar.js') + body + h1 test +``` + + You may also `yield` within an included template, allowing you to explicitly mark where the block given to `include` will be placed. Suppose for example you wish to prepend scripts rather than append, you might do the following: + +```jade +head + yield + script(src='/jquery.js') + script(src='/jquery.ui.js') +``` + + Since included Jade is parsed and literally merges the AST, lexically scoped variables function as if the included Jade was written right in the same file. This means `include` may be used as sort of partial, for example suppose we have `user.jade` which utilizes a `user` variable. + +```jade +h1= user.name +p= user.occupation +``` + +We could then simply `include user` while iterating users, and since the `user` variable is already defined within the loop the included template will have access to it. + +```jade +users = [{ name: 'Tobi', occupation: 'Ferret' }] + +each user in users + .user + include user +``` + +yielding: + +```html +
    +

    Tobi

    +

    Ferret

    +
    +``` + +If we wanted to expose a different variable name as `user` since `user.jade` references that name, we could simply define a new variable as shown here with `user = person`: + +```jade +each person in users + .user + user = person + include user +``` + +
    +## Mixins + + Mixins are converted to regular JavaScript functions in + the compiled template that Jade constructs. Mixins may + take arguments, though not required: + +```jade +mixin list + ul + li foo + li bar + li baz +``` + + Utilizing a mixin without args looks similar, just without a block: + +```jade +h2 Groceries +mixin list +``` + + Mixins may take one or more arguments as well, the arguments + are regular javascripts expressions, so for example the following: + +```jade +mixin pets(pets) + ul.pets + - each pet in pets + li= pet + +mixin profile(user) + .user + h2= user.name + mixin pets(user.pets) +``` + + Would yield something similar to the following html: + +```html +
    +

    tj

    +
      +
    • tobi
    • +
    • loki
    • +
    • jane
    • +
    • manny
    • +
    +
    +``` + +
    +## Generated Output + + Suppose we have the following Jade: + +```jade +- var title = 'yay' +h1.title #{title} +p Just an example +``` + + When the `compileDebug` option is not explicitly `false`, Jade + will compile the function instrumented with `__.lineno = n;`, which + in the event of an exception is passed to `rethrow()` which constructs + a useful message relative to the initial Jade input. + +```js +function anonymous(locals) { + var __ = { lineno: 1, input: "- var title = 'yay'\nh1.title #{title}\np Just an example", filename: "testing/test.js" }; + var rethrow = jade.rethrow; + try { + var attrs = jade.attrs, escape = jade.escape; + var buf = []; + with (locals || {}) { + var interp; + __.lineno = 1; + var title = 'yay' + __.lineno = 2; + buf.push(''); + buf.push('' + escape((interp = title) == null ? '' : interp) + ''); + buf.push(''); + __.lineno = 3; + buf.push('

    '); + buf.push('Just an example'); + buf.push('

    '); + } + return buf.join(""); + } catch (err) { + rethrow(err, __.input, __.filename, __.lineno); + } +} +``` + +When the `compileDebug` option _is_ explicitly `false`, this instrumentation +is stripped, which is very helpful for light-weight client-side templates. Combining Jade's options with the `./runtime.js` file in this repo allows you +to toString() compiled templates and avoid running the entire Jade library on +the client, increasing performance, and decreasing the amount of JavaScript +required. + +```js +function anonymous(locals) { + var attrs = jade.attrs, escape = jade.escape; + var buf = []; + with (locals || {}) { + var interp; + var title = 'yay' + buf.push(''); + buf.push('' + escape((interp = title) == null ? '' : interp) + ''); + buf.push(''); + buf.push('

    '); + buf.push('Just an example'); + buf.push('

    '); + } + return buf.join(""); +} +``` + +
    +## Example Makefile + + Below is an example Makefile used to compile _pages/*.jade_ + into _pages/*.html_ files by simply executing `make`. + +```make +JADE = $(shell find pages/*.jade) +HTML = $(JADE:.jade=.html) + +all: $(HTML) + +%.html: %.jade + jade < $< --path $< > $@ + +clean: + rm -f $(HTML) + +.PHONY: clean +``` + +this can be combined with the `watch(1)` command to produce +a watcher-like behaviour: + +```bash +$ watch make +``` + + +## jade(1) + +``` + +Usage: jade [options] [dir|file ...] + +Options: + + -h, --help output usage information + -V, --version output the version number + -o, --obj javascript options object + -O, --out output the compiled html to + -p, --path filename used to resolve includes + -P, --pretty compile pretty html output + -c, --client compile for client-side runtime.js + -D, --no-debug compile without debugging (smaller functions) + +Examples: + + # translate jade the templates dir + $ jade templates + + # create {foo,bar}.html + $ jade {foo,bar}.jade + + # jade over stdio + $ jade < my.jade > my.html + + # jade over stdio + $ echo "h1 Jade!" | jade + + # foo, bar dirs rendering to /tmp + $ jade foo bar --out /tmp + +``` + + +## Tutorials + + - cssdeck interactive [Jade syntax tutorial](http://cssdeck.com/labs/learning-the-jade-templating-engine-syntax) + - cssdeck interactive [Jade logic tutorial](http://cssdeck.com/labs/jade-templating-tutorial-codecast-part-2) + - in [Japanese](http://blog.craftgear.net/4f501e97c1347ec934000001/title/10%E5%88%86%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8Bjade%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3) + + +## License + +(The MIT License) + +Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca> + +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/test/node_modules/jade/Readme_zh-cn.md b/test/node_modules/jade/Readme_zh-cn.md new file mode 100644 index 0000000..cb90608 --- /dev/null +++ b/test/node_modules/jade/Readme_zh-cn.md @@ -0,0 +1,921 @@ +# Jade - 模板引擎 + + Jade 是一个高性能的模板引擎,它深受[Haml](http://haml-lang.com)影响,它是用javascript实现的,并且可以供[node](http://nodejs.org)使用. + + 翻译:[草依山](http://jser.me)   [翻译反馈](http://weibo.com/1826461472/z9jriDdmB#pl_profile_nav)  [Fork me](https://github.com/jserme/jade/) + +## 特性 + + - 客户端支持 + - 代码高可读 + - 灵活的缩进 + - 块展开 + - 混合 + - 静态包含 + - 属性改写 + - 安全,默认代码是转义的 + - 运行时和编译时上下文错误报告 + - 命令行下编译jade模板 + - html 5 模式 (使用 _!!! 5_ 文档类型) + - 在内存中缓存(可选) + - 合并动态和静态标签类 + - 可以通过 _filters_ 修改树 + - 模板继承 + - 原生支持 [Express JS](http://expressjs.com) + - 通过 `each` 枚举对象、数组甚至是不能枚举的对象 + - 块注释 + - 没有前缀的标签 + - AST filters + - 过滤器 + - :sass 必须已经安装[sass.js](http://github.com/visionmedia/sass.js) + - :less 必须已经安装[less.js](http://github.com/cloudhead/less.js) + - :markdown 必须已经安装[markdown-js](http://github.com/evilstreak/markdown-js) 或者[node-discount](http://github.com/visionmedia/node-discount) + - :cdata + - :coffeescript 必须已经安装[coffee-script](http://jashkenas.github.com/coffee-script/) + - [Vim Syntax](https://github.com/digitaltoad/vim-jade) + - [TextMate Bundle](http://github.com/miksago/jade-tmbundle) + - [Screencasts](http://tjholowaychuk.com/post/1004255394/jade-screencast-template-engine-for-nodejs) + - [html2jade](https://github.com/donpark/html2jade) 转换器 + +## 其它实现 + + - [php](http://github.com/everzet/jade.php) + - [scala](http://scalate.fusesource.org/versions/snapshot/documentation/scaml-reference.html) + - [ruby](http://github.com/stonean/slim) + +## 安装 + +通过 npm: + + npm install jade + +## 浏览器支持 + + 把jade编译为一个可供浏览器使用的单文件,只需要简单的执行: + + $ make jade.js + + 如果你已经安装了uglifyjs (`npm install uglify-js`),你可以执行下面的命令它会生成所有的文件。其实每一个正式版本里都帮你做了这事。 + + $ make jade.min.js + + 默认情况下,为了方便调试Jade会把模板组织成带有形如 `__.lineno = 3` 的行号的形式。 + 在浏览器里使用的时候,你可以通过传递一个选项`{ compileDebug: false }`来去掉这个。 + 下面的模板 + + p Hello #{name} + + 会被翻译成下面的函数: + +```js +function anonymous(locals, attrs, escape, rethrow) { + var buf = []; + with (locals || {}) { + var interp; + buf.push('\n

    Hello ' + escape((interp = name) == null ? '' : interp) + '\n

    '); + } + return buf.join(""); +} +``` + + 通过使用Jade的 `./runtime.js`你可以在浏览器使用这些预编译的模板而不需要使用Jade, 你只需要使用runtime.js里的工具函数, 它们会放在`jade.attrs`, `jade.escape` 这些里。 把选项 `{ client: true }` 传递给 `jade.compile()`, Jade 会把这些帮助函数的引用放在`jade.attrs`, `jade.escape`. + +```js +function anonymous(locals, attrs, escape, rethrow) { + var attrs = jade.attrs, escape = jade.escape, rethrow = jade.rethrow; + var buf = []; + with (locals || {}) { + var interp; + buf.push('\n

    Hello ' + escape((interp = name) == null ? '' : interp) + '\n

    '); + } + return buf.join(""); +} +``` + +## 公开API + +```javascript + var jade = require('jade'); + + // Compile a function + var fn = jade.compile('string of jade', options); + fn(locals); +``` + +### 选项 + + - `self` 使用`self` 命名空间来持有本地变量. _默认为false_ + - `locals` 本地变量对象 + - `filename` 异常发生时使用,includes时必需 + - `debug` 输出token和翻译后的函数体 + - `compiler` 替换掉jade默认的编译器 + - `compileDebug` `false`的时候调试的结构不会被输出 + +## 语法 + +### 行结束标志 + +**CRLF** 和 **CR** 会在编译之前被转换为 **LF** + +### 标签 + +标签就是一个简单的单词: + + html + +它会被转换为 `` + +标签也是可以有id的: + + div#container + +它会被转换为 `
    ` + +怎么加类呢? + + div.user-details + +转换为 `
    ` + +多个类? 和id? 也是可以搞定的: + + div#foo.bar.baz + +转换为 `
    ` + +不停的div div div 很讨厌啊 , 可以这样: + + #foo + .bar + +这个算是我们的语法糖,它已经被很好的支持了,上面的会输出: + + `
    ` + +### 标签文本 + +只需要简单的把内容放在标签之后: + + p wahoo! + +它会被渲染为 `

    wahoo!

    `. + +很帅吧,但是大段的文本怎么办呢: + + p + | foo bar baz + | rawr rawr + | super cool + | go jade go + +渲染为 `

    foo bar baz rawr.....

    ` + +怎么和数据结合起来? 所有类型的文本展示都可以和数据结合起来,如果我们把`{ name: 'tj', email: 'tj@vision-media.ca' }` 传给编译函数,下面是模板上的写法: + + #user #{name} <#{email}> + +它会被渲染为 `
    tj <tj@vision-media.ca>
    ` + +当就是要输出`#{}` 的时候怎么办? 转义一下! + + p \#{something} + +它会输出`

    #{something}

    ` + +同样可以使用非转义的变量`!{html}`, 下面的模板将直接输出一个script标签 + + - var html = "" + | !{html} + +内联标签同样可以使用文本块来包含文本: + + label + | Username: + input(name='user[name]') + +或者直接使用标签文本: + + label Username: + input(name='user[name]') + +_只_包含文本的标签,比如`script`, `style`, 和 `textarea` 不需要前缀`|` 字符, 比如: + + html + head + title Example + script + if (foo) { + bar(); + } else { + baz(); + } + +这里还有一种选择,可以使用'.' 来开始一段文本块,比如: + + p. + foo asdf + asdf + asdfasdfaf + asdf + asd. + +会被渲染为: + +

    foo asdf + asdf + asdfasdfaf + asdf + asd + . +

    + +这和带一个空格的 '.' 是不一样的, 带空格的会被Jade的解析器忽略,当作一个普通的文字: + + p . + +渲染为: + +

    .

    + + +需要注意的是广西块需要两次转义。比如想要输出下面的文本: + +

    foo\bar

    + +使用: + + p. + foo\\bar + +### 注释 + +单行注释和JavaScript里是一样的,通过"//"来开始,并且必须单独一行: + + // just some paragraphs + p foo + p bar + +渲染为: + + +

    foo

    +

    bar

    + +Jade 同样支持不输出的注释,加一个短横线就行了: + + //- will not output within markup + p foo + p bar + +渲染为: + +

    foo

    +

    bar

    + +### 块注释 + + 块注释也是支持的: + + body + // + #content + h1 Example + +渲染为: + + + + + +Jade 同样很好的支持了条件注释: + + body + //if IE + a(href='http://www.mozilla.com/en-US/firefox/') Get Firefox + + +渲染为: + + + + + +### 内联 + + Jade 支持以自然的方式定义标签嵌套: + + ul + li.first + a(href='#') foo + li + a(href='#') bar + li.last + a(href='#') baz + +### 块展开 + + 块展开可以帮助你在一行内创建嵌套的标签,下面的例子和上面的是一样的: + + ul + li.first: a(href='#') foo + li: a(href='#') bar + li.last: a(href='#') baz + + +### 属性 + +Jade 现在支持使用'(' 和 ')' 作为属性分隔符 + + a(href='/login', title='View login page') Login + +当一个值是 `undefined` 或者 `null` 属性_不_会被加上, +所以呢,它不会编译出 'something="null"'. + + div(something=null) + +Boolean 属性也是支持的: + + input(type="checkbox", checked) + +使用代码的Boolean 属性只有当属性为`true`时才会输出: + + input(type="checkbox", checked=someValue) + +多行同样也是可用的: + + input(type='checkbox', + name='agreement', + checked) + +多行的时候可以不加逗号: + + input(type='checkbox' + name='agreement' + checked) + +加点空格,格式好看一点?同样支持 + + input( + type='checkbox' + name='agreement' + checked) + +冒号也是支持的: + + rss(xmlns:atom="atom") + +假如我有一个`user` 对象 `{ id: 12, name: 'tobi' }` +我们希望创建一个指向"/user/12"的链接 `href`, 我们可以使用普通的javascript字符串连接,如下: + + a(href='/user/' + user.id)= user.name + +或者我们使用jade的修改方式,这个我想很多使用Ruby或者 CoffeeScript的人会看起来像普通的js..: + + a(href='/user/#{user.id}')= user.name + +`class`属性是一个特殊的属性,你可以直接传递一个数组,比如`bodyClasses = ['user', 'authenticated']` : + + body(class=bodyClasses) + +### HTML + + 内联的html是可以的,我们可以使用管道定义一段文本 : + +``` +html + body + |

    Title

    + |

    foo bar baz

    +``` + + 或者我们可以使用`.` 来告诉Jade我们需要一段文本: + +``` +html + body. +

    Title

    +

    foo bar baz

    +``` + + 上面的两个例子都会渲染成相同的结果: + +``` +

    Title

    +

    foo bar baz

    + +``` + + 这条规则适应于在jade里的任何文本: + +``` +html + body + h1 User #{name} +``` + +### Doctypes + +添加文档类型只需要简单的使用 `!!!`, 或者 `doctype` 跟上下面的可选项: + + !!! + +会渲染出 _transitional_ 文档类型, 或者: + + !!! 5 + +or + + !!! html + +or + + doctype html + +doctypes 是大小写不敏感的, 所以下面两个是一样的: + + doctype Basic + doctype basic + +当然也是可以直接传递一段文档类型的文本: + + doctype html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN + +渲染后: + + + +会输出 _html 5_ 文档类型. 下面的默认的文档类型,可以很简单的扩展: + +```javascript + var doctypes = exports.doctypes = { + '5': '', + 'xml': '', + 'default': '', + 'transitional': '', + 'strict': '', + 'frameset': '', + '1.1': '', + 'basic': '', + 'mobile': '' + }; +``` + +通过下面的代码可以很简单的改变默认的文档类型: + +```javascript + jade.doctypes.default = 'whatever you want'; +``` + +## 过滤器 + +过滤器前缀 `:`, 比如 `:markdown` 会把下面块里的文本交给专门的函数进行处理。查看顶部 _特性_ 里有哪些可用的过滤器。 + + body + :markdown + Woah! jade _and_ markdown, very **cool** + we can even link to [stuff](http://google.com) + +渲染为: + +

    Woah! jade and markdown, very cool we can even link to stuff

    + +## 代码 + +Jade目前支持三种类型的可执行代码。第一种是前缀`-`, 这是不会被输出的: + + - var foo = 'bar'; + +这可以用在条件语句或者循环中: + + - for (var key in obj) + p= obj[key] + +由于Jade的缓存技术,下面的代码也是可以的: + + - if (foo) + ul + li yay + li foo + li worked + - else + p oh no! didnt work + +哈哈,甚至是很长的循环也是可以的: + + - if (items.length) + ul + - items.forEach(function(item){ + li= item + - }) + +所以你想要的! + +下一步我们要_转义_输出的代码,比如我们返回一个值,只要前缀一个`=`: + + - var foo = 'bar' + = foo + h1= foo + +它会渲染为`bar

    bar

    `. 为了安全起见,使用`=`输出的代码默认是转义的,如果想直接输出不转义的值可以使用`!=`: + + p!= aVarContainingMoreHTML + +Jade 同样是设计师友好的,它可以使javascript更直接更富表现力。比如下面的赋值语句是相等的,同时表达式还是通常的javascript: + + - var foo = 'foo ' + 'bar' + foo = 'foo ' + 'bar' + +Jade会把 `if`, `else if`, `else`, `until`, `while`, `unless`同别的优先对待, 但是你得记住它们还是普通的javascript: + + if foo == 'bar' + ul + li yay + li foo + li worked + else + p oh no! didnt work + +## 循环 + +尽管已经支持JavaScript原生代码,Jade还是支持了一些特殊的标签,它们可以让模板更加易于理解,其中之一就是`each`, 这种形式: + + each VAL[, KEY] in OBJ + +一个遍历数组的例子 : + + - var items = ["one", "two", "three"] + each item in items + li= item + +渲染为: + +
  • one
  • +
  • two
  • +
  • three
  • + +遍历一个数组同时带上索引: + + items = ["one", "two", "three"] + each item, i in items + li #{item}: #{i} + +渲染为: + +
  • one: 0
  • +
  • two: 1
  • +
  • three: 2
  • + +遍历一个数组的键值: + + obj = { foo: 'bar' } + each val, key in obj + li #{key}: #{val} + +将会渲染为:`
  • foo: bar
  • ` + +Jade在内部会把这些语句转换成原生的JavaScript语句,就像使用 `users.forEach(function(user){`, +词法作用域和嵌套会像在普通的JavaScript中一样: + + each user in users + each role in user.roles + li= role + +如果你喜欢,也可以使用`for` : + + for user in users + for role in user.roles + li= role + +## 条件语句 + +Jade 条件语句和使用了(`-`) 前缀的JavaScript语句是一致的,然后它允许你不使用圆括号,这样会看上去对设计师更友好一点, +同时要在心里记住这个表达式渲染出的是_常规_Javascript: + + for user in users + if user.role == 'admin' + p #{user.name} is an admin + else + p= user.name + +和下面的使用了常规JavaScript的代码是相等的: + + for user in users + - if (user.role == 'admin') + p #{user.name} is an admin + - else + p= user.name + +Jade 同时支持`unless`, 这和`if (!(expr))`是等价的: + + for user in users + unless user.isAnonymous + p + | Click to view + a(href='/users/' + user.id)= user.name + +## 模板继承 + + Jade 支持通过 `block` 和 `extends` 关键字来实现模板继承。 一个块就是一个Jade的"block" ,它将在子模板中实现,同时是支持递归的。 + + Jade 块如果没有内容,Jade会添加默认内容,下面的代码默认会输出`block scripts`, `block content`, 和 `block foot`. + +``` +html + head + h1 My Site - #{title} + block scripts + script(src='/jquery.js') + body + block content + block foot + #footer + p some footer content +``` + + 现在我们来继承这个布局,简单创建一个新文件,像下面那样直接使用`extends`,给定路径(可以选择带.jade扩展名或者不带). 你可以定义一个或者更多的块来覆盖父级块内容, 注意到这里的`foot`块_没有_定义,所以它还会输出父级的"some footer content"。 + +``` +extends extend-layout + +block scripts + script(src='/jquery.js') + script(src='/pets.js') + +block content + h1= title + each pet in pets + include pet +``` + + 同样可以在一个子块里添加块,就像下面实现的块`content`里又定义了两个可以被实现的块`sidebar`和`primary`,或者子模板直接实现`content`。 + +``` +extends regular-layout + +block content + .sidebar + block sidebar + p nothing + .primary + block primary + p nothing +``` + +## 包含 + + Includes 允许你静态包含一段Jade, 或者别的存放在单个文件中的东西比如css, html。 非常常见的例子是包含头部和页脚。 假设我们有一个下面目录结构的文件夹: + + ./layout.jade + ./includes/ + ./head.jade + ./tail.jade + +下面是 _layout.jade_ 的内容: + + html + include includes/head + body + h1 My Site + p Welcome to my super amazing site. + include includes/foot + +这两个包含 _includes/head_ 和 _includes/foot_ 都会读取相对于给 _layout.jade_ 参数`filename` 的路径的文件, 这是一个绝对路径,不用担心Express帮你搞定这些了。Include 会解析这些文件,并且插入到已经生成的语法树中,然后渲染为你期待的内容: + +```html + + + My Site + + + +

    My Site

    +

    Welcome to my super lame site.

    + + + +``` + + 前面已经提到,`include` 可以包含比如html或者css这样的内容。给定一个扩展名后,Jade不会把这个文件当作一个Jade源代码,并且会把它当作一个普通文本包含进来: + +``` +html + body + include content.html +``` + + Include 也可以接受块内容,给定的块将会附加到包含文件 _最后_ 的块里。 举个例子,`head.jade` 包含下面的内容: + + + ``` +head + script(src='/jquery.js') +``` + + 我们可以像下面给`include head`添加内容, 这里是添加两个脚本. + +``` +html + include head + script(src='/foo.js') + script(src='/bar.js') + body + h1 test +``` + + +## Mixins + + Mixins在编译的模板里会被Jade转换为普通的JavaScript函数。 Mixins 可以还参数,但不是必需的: + + mixin list + ul + li foo + li bar + li baz + + 使用不带参数的mixin看上去非常简单,在一个块外: + + h2 Groceries + mixin list + + Mixins 也可以带一个或者多个参数,参数就是普通的javascripts表达式,比如下面的例子: + + mixin pets(pets) + ul.pets + - each pet in pets + li= pet + + mixin profile(user) + .user + h2= user.name + mixin pets(user.pets) + + 会输出像下面的html: + +```html +
    +

    tj

    +
      +
    • tobi
    • +
    • loki
    • +
    • jane
    • +
    • manny
    • +
    +
    +``` + +## 产生输出 + + 假设我们有下面的Jade源码: + +``` +- var title = 'yay' +h1.title #{title} +p Just an example +``` + + 当 `compileDebug` 选项不是`false`, Jade 会编译时会把函数里加上 `__.lineno = n;`, 这个参数会在编译出错时传递给`rethrow()`, 而这个函数会在Jade初始输出时给出一个有用的错误信息。 + +```js +function anonymous(locals) { + var __ = { lineno: 1, input: "- var title = 'yay'\nh1.title #{title}\np Just an example", filename: "testing/test.js" }; + var rethrow = jade.rethrow; + try { + var attrs = jade.attrs, escape = jade.escape; + var buf = []; + with (locals || {}) { + var interp; + __.lineno = 1; + var title = 'yay' + __.lineno = 2; + buf.push(''); + buf.push('' + escape((interp = title) == null ? '' : interp) + ''); + buf.push(''); + __.lineno = 3; + buf.push('

    '); + buf.push('Just an example'); + buf.push('

    '); + } + return buf.join(""); + } catch (err) { + rethrow(err, __.input, __.filename, __.lineno); + } +} +``` + +当`compileDebug` 参数是`false`, 这个参数会被去掉,这样对于轻量级的浏览器端模板是非常有用的。结合Jade的参数和当前源码库里的 `./runtime.js` 文件,你可以通过toString()来编译模板而不需要在浏览器端运行整个Jade库,这样可以提高性能,也可以减少载入的JavaScript数量。 + +```js +function anonymous(locals) { + var attrs = jade.attrs, escape = jade.escape; + var buf = []; + with (locals || {}) { + var interp; + var title = 'yay' + buf.push(''); + buf.push('' + escape((interp = title) == null ? '' : interp) + ''); + buf.push(''); + buf.push('

    '); + buf.push('Just an example'); + buf.push('

    '); + } + return buf.join(""); +} +``` + +## Makefile的一个例子 + + 通过执行`make`, 下面的Makefile例子可以把 _pages/*.jade_ 编译为 _pages/*.html_ 。 + +```make +JADE = $(shell find pages/*.jade) +HTML = $(JADE:.jade=.html) + +all: $(HTML) + +%.html: %.jade + jade < $< --path $< > $@ + +clean: + rm -f $(HTML) + +.PHONY: clean +``` + +这个可以和`watch(1)` 命令起来产生像下面的行为: + + $ watch make + +## 命令行的jade(1) + +``` + +使用: jade [options] [dir|file ...] + +选项: + + -h, --help 输出帮助信息 + -v, --version 输出版本号 + -o, --obj javascript选项 + -O, --out 输出编译后的html到 + -p, --path 在处理stdio时,查找包含文件时的查找路径 + +Examples: + + # 编译整个目录 + $ jade templates + + # 生成 {foo,bar}.html + $ jade {foo,bar}.jade + + # 在标准IO下使用jade + $ jade < my.jade > my.html + + # 在标准IO下使用jade, 同时指定用于查找包含的文件 + $ jade < my.jade -p my.jade > my.html + + # 在标准IO下使用jade + $ echo "h1 Jade!" | jade + + # foo, bar 目录渲染到 /tmp + $ jade foo bar --out /tmp + +``` + +## License + +(The MIT License) + +Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca> + +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/test/node_modules/jade/bin/jade b/test/node_modules/jade/bin/jade new file mode 100644 index 0000000..8f1c816 --- /dev/null +++ b/test/node_modules/jade/bin/jade @@ -0,0 +1,168 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var fs = require('fs') + , program = require('commander') + , path = require('path') + , basename = path.basename + , dirname = path.dirname + , resolve = path.resolve + , exists = fs.existsSync || path.existsSync + , join = path.join + , mkdirp = require('mkdirp') + , jade = require('../'); + +// jade options + +var options = {}; + +// options + +program + .version(jade.version) + .usage('[options] [dir|file ...]') + .option('-o, --obj ', 'javascript options object') + .option('-O, --out ', 'output the compiled html to ') + .option('-p, --path ', 'filename used to resolve includes') + .option('-P, --pretty', 'compile pretty html output') + .option('-c, --client', 'compile function for client-side runtime.js') + .option('-D, --no-debug', 'compile without debugging (smaller functions)') + .option('-w, --watch', 'watch files for changes and automatically re-render') + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' # translate jade the templates dir'); + console.log(' $ jade templates'); + console.log(''); + console.log(' # create {foo,bar}.html'); + console.log(' $ jade {foo,bar}.jade'); + console.log(''); + console.log(' # jade over stdio'); + console.log(' $ jade < my.jade > my.html'); + console.log(''); + console.log(' # jade over stdio'); + console.log(' $ echo "h1 Jade!" | jade'); + console.log(''); + console.log(' # foo, bar dirs rendering to /tmp'); + console.log(' $ jade foo bar --out /tmp '); + console.log(''); +}); + +program.parse(process.argv); + +// options given, parse them + +if (program.obj) { + if (exists(program.obj)) { + options = JSON.parse(fs.readFileSync(program.obj)); + } else { + options = eval('(' + program.obj + ')'); + } +} + +// --filename + +if (program.path) options.filename = program.path; + +// --no-debug + +options.compileDebug = program.debug; + +// --client + +options.client = program.client; + +// --pretty + +options.pretty = program.pretty; + +// --watch + +options.watch = program.watch; + +// left-over args are file paths + +var files = program.args; + +// compile files + +if (files.length) { + console.log(); + files.forEach(renderFile); + if (options.watch) { + files.forEach(function (file) { + fs.watchFile(file, {interval: 100}, function (curr, prev) { + if (curr.mtime > prev.mtime) renderFile(file); + }); + }); + } + process.on('exit', function () { + console.log(); + }); +// stdio +} else { + stdin(); +} + +/** + * Compile from stdin. + */ + +function stdin() { + var buf = ''; + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(chunk){ buf += chunk; }); + process.stdin.on('end', function(){ + var fn = jade.compile(buf, options); + var output = options.client + ? fn.toString() + : fn(options); + process.stdout.write(output); + }).resume(); +} + +/** + * Process the given path, compiling the jade files found. + * Always walk the subdirectories. + */ + +function renderFile(path) { + var re = /\.jade$/; + fs.lstat(path, function(err, stat) { + if (err) throw err; + // Found jade file + if (stat.isFile() && re.test(path)) { + fs.readFile(path, 'utf8', function(err, str){ + if (err) throw err; + options.filename = path; + var fn = jade.compile(str, options); + var extname = options.client ? '.js' : '.html'; + path = path.replace(re, extname); + if (program.out) path = join(program.out, basename(path)); + var dir = resolve(dirname(path)); + mkdirp(dir, 0755, function(err){ + if (err) throw err; + var output = options.client + ? fn.toString() + : fn(options); + fs.writeFile(path, output, function(err){ + if (err) throw err; + console.log(' \033[90mrendered \033[36m%s\033[0m', path); + }); + }); + }); + // Found directory + } else if (stat.isDirectory()) { + fs.readdir(path, function(err, files) { + if (err) throw err; + files.map(function(filename) { + return path + '/' + filename; + }).forEach(renderFile); + }); + } + }); +} diff --git a/test/node_modules/jade/index.js b/test/node_modules/jade/index.js new file mode 100644 index 0000000..8ad059f --- /dev/null +++ b/test/node_modules/jade/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.JADE_COV + ? require('./lib-cov/jade') + : require('./lib/jade'); \ No newline at end of file diff --git a/test/node_modules/jade/jade.js b/test/node_modules/jade/jade.js new file mode 100644 index 0000000..ad22ec3 --- /dev/null +++ b/test/node_modules/jade/jade.js @@ -0,0 +1,3654 @@ +(function() { + +// CommonJS require() + +function require(p){ + var path = require.resolve(p) + , mod = require.modules[path]; + if (!mod) throw new Error('failed to require "' + p + '"'); + if (!mod.exports) { + mod.exports = {}; + mod.call(mod.exports, mod, mod.exports, require.relative(path)); + } + return mod.exports; + } + +require.modules = {}; + +require.resolve = function (path){ + var orig = path + , reg = path + '.js' + , index = path + '/index.js'; + return require.modules[reg] && reg + || require.modules[index] && index + || orig; + }; + +require.register = function (path, fn){ + require.modules[path] = fn; + }; + +require.relative = function (parent) { + return function(p){ + if ('.' != p.charAt(0)) return require(p); + + var path = parent.split('/') + , segs = p.split('/'); + path.pop(); + + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if ('..' == seg) path.pop(); + else if ('.' != seg) path.push(seg); + } + + return require(path.join('/')); + }; + }; + + +require.register("compiler.js", function(module, exports, require){ + +/*! + * Jade - Compiler + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var nodes = require('./nodes') + , filters = require('./filters') + , doctypes = require('./doctypes') + , selfClosing = require('./self-closing') + , runtime = require('./runtime') + , utils = require('./utils'); + + + if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } + } + + if (!String.prototype.trimLeft) { + String.prototype.trimLeft = function(){ + return this.replace(/^\s+/, ''); + } + } + + + +/** + * Initialize `Compiler` with the given `node`. + * + * @param {Node} node + * @param {Object} options + * @api public + */ + +var Compiler = module.exports = function Compiler(node, options) { + this.options = options = options || {}; + this.node = node; + this.hasCompiledDoctype = false; + this.hasCompiledTag = false; + this.pp = options.pretty || false; + this.debug = false !== options.compileDebug; + this.indents = 0; + this.parentIndents = 0; + if (options.doctype) this.setDoctype(options.doctype); +}; + +/** + * Compiler prototype. + */ + +Compiler.prototype = { + + /** + * Compile parse tree to JavaScript. + * + * @api public + */ + + compile: function(){ + this.buf = ['var interp;']; + if (this.pp) this.buf.push("var __indent = [];"); + this.lastBufferedIdx = -1; + this.visit(this.node); + return this.buf.join('\n'); + }, + + /** + * Sets the default doctype `name`. Sets terse mode to `true` when + * html 5 is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {string} name + * @api public + */ + + setDoctype: function(name){ + name = (name && name.toLowerCase()) || 'default'; + this.doctype = doctypes[name] || ''; + this.terse = this.doctype.toLowerCase() == ''; + this.xml = 0 == this.doctype.indexOf(' 1 && !escape && block.nodes[0].isText && block.nodes[1].isText) + this.prettyIndent(1, true); + + for (var i = 0; i < len; ++i) { + // Pretty print text + if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText) + this.prettyIndent(1, false); + + this.visit(block.nodes[i]); + // Multiple text nodes are separated by newlines + if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText) + this.buffer('\\n'); + } + }, + + /** + * Visit `doctype`. Sets terse mode to `true` when html 5 + * is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {Doctype} doctype + * @api public + */ + + visitDoctype: function(doctype){ + if (doctype && (doctype.val || !this.doctype)) { + this.setDoctype(doctype.val || 'default'); + } + + if (this.doctype) this.buffer(this.doctype); + this.hasCompiledDoctype = true; + }, + + /** + * Visit `mixin`, generating a function that + * may be called within the template. + * + * @param {Mixin} mixin + * @api public + */ + + visitMixin: function(mixin){ + var name = mixin.name.replace(/-/g, '_') + '_mixin' + , args = mixin.args || '' + , block = mixin.block + , attrs = mixin.attrs + , pp = this.pp; + + if (mixin.call) { + if (pp) this.buf.push("__indent.push('" + Array(this.indents + 1).join(' ') + "');") + if (block || attrs.length) { + + this.buf.push(name + '.call({'); + + if (block) { + this.buf.push('block: function(){'); + + // Render block with no indents, dynamically added when rendered + this.parentIndents++; + var _indents = this.indents; + this.indents = 0; + this.visit(mixin.block); + this.indents = _indents; + this.parentIndents--; + + if (attrs.length) { + this.buf.push('},'); + } else { + this.buf.push('}'); + } + } + + if (attrs.length) { + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push('attributes: merge({' + val.buf + + '}, attributes), escaped: merge(' + val.escaped + ', escaped, true)'); + } else { + this.buf.push('attributes: {' + val.buf + '}, escaped: ' + val.escaped); + } + } + + if (args) { + this.buf.push('}, ' + args + ');'); + } else { + this.buf.push('});'); + } + + } else { + this.buf.push(name + '(' + args + ');'); + } + if (pp) this.buf.push("__indent.pop();") + } else { + this.buf.push('var ' + name + ' = function(' + args + '){'); + this.buf.push('var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};'); + this.parentIndents++; + this.visit(block); + this.parentIndents--; + this.buf.push('};'); + } + }, + + /** + * Visit `tag` buffering tag markup, generating + * attributes, visiting the `tag`'s code and block. + * + * @param {Tag} tag + * @api public + */ + + visitTag: function(tag){ + this.indents++; + var name = tag.name + , pp = this.pp; + + if (tag.buffer) name = "' + (" + name + ") + '"; + + if (!this.hasCompiledTag) { + if (!this.hasCompiledDoctype && 'html' == name) { + this.visitDoctype(); + } + this.hasCompiledTag = true; + } + + // pretty print + if (pp && !tag.isInline()) + this.prettyIndent(0, true); + + if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) { + this.buffer('<' + name); + this.visitAttributes(tag.attrs); + this.terse + ? this.buffer('>') + : this.buffer('/>'); + } else { + // Optimize attributes buffering + if (tag.attrs.length) { + this.buffer('<' + name); + if (tag.attrs.length) this.visitAttributes(tag.attrs); + this.buffer('>'); + } else { + this.buffer('<' + name + '>'); + } + if (tag.code) this.visitCode(tag.code); + this.escape = 'pre' == tag.name; + this.visit(tag.block); + + // pretty print + if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline()) + this.prettyIndent(0, true); + + this.buffer(''); + } + this.indents--; + }, + + /** + * Visit `filter`, throwing when the filter does not exist. + * + * @param {Filter} filter + * @api public + */ + + visitFilter: function(filter){ + var fn = filters[filter.name]; + + // unknown filter + if (!fn) { + if (filter.isASTFilter) { + throw new Error('unknown ast filter "' + filter.name + ':"'); + } else { + throw new Error('unknown filter ":' + filter.name + '"'); + } + } + + if (filter.isASTFilter) { + this.buf.push(fn(filter.block, this, filter.attrs)); + } else { + var text = filter.block.nodes.map(function(node){ return node.val }).join('\n'); + filter.attrs = filter.attrs || {}; + filter.attrs.filename = this.options.filename; + this.buffer(utils.text(fn(text, filter.attrs))); + } + }, + + /** + * Visit `text` node. + * + * @param {Text} text + * @api public + */ + + visitText: function(text){ + text = utils.text(text.val.replace(/\\/g, '_SLASH_')); + if (this.escape) text = escape(text); + text = text.replace(/_SLASH_/g, '\\\\'); + this.buffer(text); + }, + + /** + * Visit a `comment`, only buffering when the buffer flag is set. + * + * @param {Comment} comment + * @api public + */ + + visitComment: function(comment){ + if (!comment.buffer) return; + if (this.pp) this.prettyIndent(1, true); + this.buffer(''); + }, + + /** + * Visit a `BlockComment`. + * + * @param {Comment} comment + * @api public + */ + + visitBlockComment: function(comment){ + if (!comment.buffer) return; + if (0 == comment.val.trim().indexOf('if')) { + this.buffer(''); + } else { + this.buffer(''); + } + }, + + /** + * Visit `code`, respecting buffer / escape flags. + * If the code is followed by a block, wrap it in + * a self-calling function. + * + * @param {Code} code + * @api public + */ + + visitCode: function(code){ + // Wrap code blocks with {}. + // we only wrap unbuffered code blocks ATM + // since they are usually flow control + + // Buffer code + if (code.buffer) { + var val = code.val.trimLeft(); + this.buf.push('var __val__ = ' + val); + val = 'null == __val__ ? "" : __val__'; + if (code.escape) val = 'escape(' + val + ')'; + this.buf.push("buf.push(" + val + ");"); + } else { + this.buf.push(code.val); + } + + // Block support + if (code.block) { + if (!code.buffer) this.buf.push('{'); + this.visit(code.block); + if (!code.buffer) this.buf.push('}'); + } + }, + + /** + * Visit `each` block. + * + * @param {Each} each + * @api public + */ + + visitEach: function(each){ + this.buf.push('' + + '// iterate ' + each.obj + '\n' + + ';(function(){\n' + + ' if (\'number\' == typeof ' + each.obj + '.length) {\n'); + + if (each.alternative) { + this.buf.push(' if (' + each.obj + '.length) {'); + } + + this.buf.push('' + + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push(' }\n'); + + if (each.alternative) { + this.buf.push(' } else {'); + this.visit(each.alternative); + this.buf.push(' }'); + } + + this.buf.push('' + + ' } else {\n' + + ' var $$l = 0;\n' + + ' for (var ' + each.key + ' in ' + each.obj + ') {\n' + + ' $$l++;' + + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push(' }\n'); + + this.buf.push(' }\n'); + if (each.alternative) { + this.buf.push(' if ($$l === 0) {'); + this.visit(each.alternative); + this.buf.push(' }'); + } + this.buf.push(' }\n}).call(this);\n'); + }, + + /** + * Visit `attrs`. + * + * @param {Array} attrs + * @api public + */ + + visitAttributes: function(attrs){ + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push("buf.push(attrs(merge({ " + val.buf + + " }, attributes), merge(" + val.escaped + ", escaped, true)));"); + } else if (val.constant) { + eval('var buf={' + val.buf + '};'); + this.buffer(runtime.attrs(buf, JSON.parse(val.escaped)), true); + } else { + this.buf.push("buf.push(attrs({ " + val.buf + " }, " + val.escaped + "));"); + } + }, + + /** + * Compile attributes. + */ + + attrs: function(attrs){ + var buf = [] + , classes = [] + , escaped = {} + , constant = attrs.every(function(attr){ return isConstant(attr.val) }) + , inherits = false; + + if (this.terse) buf.push('terse: true'); + + attrs.forEach(function(attr){ + if (attr.name == 'attributes') return inherits = true; + escaped[attr.name] = attr.escaped; + if (attr.name == 'class') { + classes.push('(' + attr.val + ')'); + } else { + var pair = "'" + attr.name + "':(" + attr.val + ')'; + buf.push(pair); + } + }); + + if (classes.length) { + classes = classes.join(" + ' ' + "); + buf.push("class: " + classes); + } + + return { + buf: buf.join(', ').replace('class:', '"class":'), + escaped: JSON.stringify(escaped), + inherits: inherits, + constant: constant + }; + } +}; + +/** + * Check if expression can be evaluated to a constant + * + * @param {String} expression + * @return {Boolean} + * @api private + */ + +function isConstant(val){ + // Check strings/literals + if (/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val)) + return true; + + // Check numbers + if (!isNaN(Number(val))) + return true; + + // Check arrays + var matches; + if (matches = /^ *\[(.*)\] *$/.exec(val)) + return matches[1].split(',').every(isConstant); + + return false; +} + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +function escape(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +}); // module: compiler.js + +require.register("doctypes.js", function(module, exports, require){ + +/*! + * Jade - doctypes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + '5': '' + , 'default': '' + , 'xml': '' + , 'transitional': '' + , 'strict': '' + , 'frameset': '' + , '1.1': '' + , 'basic': '' + , 'mobile': '' +}; +}); // module: doctypes.js + +require.register("filters.js", function(module, exports, require){ + +/*! + * Jade - filters + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + + /** + * Wrap text with CDATA block. + */ + + cdata: function(str){ + return ''; + }, + + /** + * Transform sass to css, wrapped in style tags. + */ + + sass: function(str){ + str = str.replace(/\\n/g, '\n'); + var sass = require('sass').render(str).replace(/\n/g, '\\n'); + return ''; + }, + + /** + * Transform stylus to css, wrapped in style tags. + */ + + stylus: function(str, options){ + var ret; + str = str.replace(/\\n/g, '\n'); + var stylus = require('stylus'); + stylus(str, options).render(function(err, css){ + if (err) throw err; + ret = css.replace(/\n/g, '\\n'); + }); + return ''; + }, + + /** + * Transform less to css, wrapped in style tags. + */ + + less: function(str){ + var ret; + str = str.replace(/\\n/g, '\n'); + require('less').render(str, function(err, css){ + if (err) throw err; + ret = ''; + }); + return ret; + }, + + /** + * Transform markdown to html. + */ + + markdown: function(str){ + var md; + + // support markdown / discount + try { + md = require('markdown'); + } catch (err){ + try { + md = require('discount'); + } catch (err) { + try { + md = require('markdown-js'); + } catch (err) { + try { + md = require('marked'); + } catch (err) { + throw new + Error('Cannot find markdown library, install markdown, discount, or marked.'); + } + } + } + } + + str = str.replace(/\\n/g, '\n'); + return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'''); + }, + + /** + * Transform coffeescript to javascript. + */ + + coffeescript: function(str){ + var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); + return ''; + } +}; + +}); // module: filters.js + +require.register("inline-tags.js", function(module, exports, require){ + +/*! + * Jade - inline tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'a' + , 'abbr' + , 'acronym' + , 'b' + , 'br' + , 'code' + , 'em' + , 'font' + , 'i' + , 'img' + , 'ins' + , 'kbd' + , 'map' + , 'samp' + , 'small' + , 'span' + , 'strong' + , 'sub' + , 'sup' +]; +}); // module: inline-tags.js + +require.register("jade.js", function(module, exports, require){ +/*! + * Jade + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Parser = require('./parser') + , Lexer = require('./lexer') + , Compiler = require('./compiler') + , runtime = require('./runtime') + +/** + * Library version. + */ + +exports.version = '0.27.6'; + +/** + * Expose self closing tags. + */ + +exports.selfClosing = require('./self-closing'); + +/** + * Default supported doctypes. + */ + +exports.doctypes = require('./doctypes'); + +/** + * Text filters. + */ + +exports.filters = require('./filters'); + +/** + * Utilities. + */ + +exports.utils = require('./utils'); + +/** + * Expose `Compiler`. + */ + +exports.Compiler = Compiler; + +/** + * Expose `Parser`. + */ + +exports.Parser = Parser; + +/** + * Expose `Lexer`. + */ + +exports.Lexer = Lexer; + +/** + * Nodes. + */ + +exports.nodes = require('./nodes'); + +/** + * Jade runtime helpers. + */ + +exports.runtime = runtime; + +/** + * Template function cache. + */ + +exports.cache = {}; + +/** + * Parse the given `str` of jade and return a function body. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ + +function parse(str, options){ + try { + // Parse + var parser = new Parser(str, options.filename, options); + + // Compile + var compiler = new (options.compiler || Compiler)(parser.parse(), options) + , js = compiler.compile(); + + // Debug compiler + if (options.debug) { + console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' ')); + } + + return '' + + 'var buf = [];\n' + + (options.self + ? 'var self = locals || {};\n' + js + : 'with (locals || {}) {\n' + js + '\n}\n') + + 'return buf.join("");'; + } catch (err) { + parser = parser.context(); + runtime.rethrow(err, parser.filename, parser.lexer.lineno); + } +} + +/** + * Strip any UTF-8 BOM off of the start of `str`, if it exists. + * + * @param {String} str + * @return {String} + * @api private + */ + +function stripBOM(str){ + return 0xFEFF == str.charCodeAt(0) + ? str.substring(1) + : str; +} + +/** + * Compile a `Function` representation of the given jade `str`. + * + * Options: + * + * - `compileDebug` when `false` debugging code is stripped from the compiled template + * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()` + * for use with the Jade client-side runtime.js + * + * @param {String} str + * @param {Options} options + * @return {Function} + * @api public + */ + +exports.compile = function(str, options){ + var options = options || {} + , client = options.client + , filename = options.filename + ? JSON.stringify(options.filename) + : 'undefined' + , fn; + + str = stripBOM(String(str)); + + if (options.compileDebug !== false) { + fn = [ + 'var __jade = [{ lineno: 1, filename: ' + filename + ' }];' + , 'try {' + , parse(str, options) + , '} catch (err) {' + , ' rethrow(err, __jade[0].filename, __jade[0].lineno);' + , '}' + ].join('\n'); + } else { + fn = parse(str, options); + } + + if (client) { + fn = 'attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n' + fn; + } + + fn = new Function('locals, attrs, escape, rethrow, merge', fn); + + if (client) return fn; + + return function(locals){ + return fn(locals, runtime.attrs, runtime.escape, runtime.rethrow, runtime.merge); + }; +}; + +/** + * Render the given `str` of jade and invoke + * the callback `fn(err, str)`. + * + * Options: + * + * - `cache` enable template caching + * - `filename` filename required for `include` / `extends` and caching + * + * @param {String} str + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +exports.render = function(str, options, fn){ + // swap args + if ('function' == typeof options) { + fn = options, options = {}; + } + + // cache requires .filename + if (options.cache && !options.filename) { + return fn(new Error('the "filename" option is required for caching')); + } + + try { + var path = options.filename; + var tmpl = options.cache + ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) + : exports.compile(str, options); + fn(null, tmpl(options)); + } catch (err) { + fn(err); + } +}; + +/** + * Render a Jade file at the given `path` and callback `fn(err, str)`. + * + * @param {String} path + * @param {Object|Function} options or callback + * @param {Function} fn + * @api public + */ + +exports.renderFile = function(path, options, fn){ + var key = path + ':string'; + + if ('function' == typeof options) { + fn = options, options = {}; + } + + try { + options.filename = path; + var str = options.cache + ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) + : fs.readFileSync(path, 'utf8'); + exports.render(str, options, fn); + } catch (err) { + fn(err); + } +}; + +/** + * Express support. + */ + +exports.__express = exports.renderFile; + +}); // module: jade.js + +require.register("lexer.js", function(module, exports, require){ +/*! + * Jade - Lexer + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +var utils = require('./utils'); + +/** + * Initialize `Lexer` with the given `str`. + * + * Options: + * + * - `colons` allow colons for attr delimiters + * + * @param {String} str + * @param {Object} options + * @api private + */ + +var Lexer = module.exports = function Lexer(str, options) { + options = options || {}; + this.input = str.replace(/\r\n|\r/g, '\n'); + this.colons = options.colons; + this.deferredTokens = []; + this.lastIndents = 0; + this.lineno = 1; + this.stash = []; + this.indentStack = []; + this.indentRe = null; + this.pipeless = false; +}; + +/** + * Lexer prototype. + */ + +Lexer.prototype = { + + /** + * Construct a token with the given `type` and `val`. + * + * @param {String} type + * @param {String} val + * @return {Object} + * @api private + */ + + tok: function(type, val){ + return { + type: type + , line: this.lineno + , val: val + } + }, + + /** + * Consume the given `len` of input. + * + * @param {Number} len + * @api private + */ + + consume: function(len){ + this.input = this.input.substr(len); + }, + + /** + * Scan for `type` with the given `regexp`. + * + * @param {String} type + * @param {RegExp} regexp + * @return {Object} + * @api private + */ + + scan: function(regexp, type){ + var captures; + if (captures = regexp.exec(this.input)) { + this.consume(captures[0].length); + return this.tok(type, captures[1]); + } + }, + + /** + * Defer the given `tok`. + * + * @param {Object} tok + * @api private + */ + + defer: function(tok){ + this.deferredTokens.push(tok); + }, + + /** + * Lookahead `n` tokens. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + var fetch = n - this.stash.length; + while (fetch-- > 0) this.stash.push(this.next()); + return this.stash[--n]; + }, + + /** + * Return the indexOf `start` / `end` delimiters. + * + * @param {String} start + * @param {String} end + * @return {Number} + * @api private + */ + + indexOfDelimiters: function(start, end){ + var str = this.input + , nstart = 0 + , nend = 0 + , pos = 0; + for (var i = 0, len = str.length; i < len; ++i) { + if (start == str.charAt(i)) { + ++nstart; + } else if (end == str.charAt(i)) { + if (++nend == nstart) { + pos = i; + break; + } + } + } + return pos; + }, + + /** + * Stashed token. + */ + + stashed: function() { + return this.stash.length + && this.stash.shift(); + }, + + /** + * Deferred token. + */ + + deferred: function() { + return this.deferredTokens.length + && this.deferredTokens.shift(); + }, + + /** + * end-of-source. + */ + + eos: function() { + if (this.input.length) return; + if (this.indentStack.length) { + this.indentStack.shift(); + return this.tok('outdent'); + } else { + return this.tok('eos'); + } + }, + + /** + * Blank line. + */ + + blank: function() { + var captures; + if (captures = /^\n *\n/.exec(this.input)) { + this.consume(captures[0].length - 1); + + ++this.lineno; + if (this.pipeless) return this.tok('text', ''); + return this.next(); + } + }, + + /** + * Comment. + */ + + comment: function() { + var captures; + if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('comment', captures[2]); + tok.buffer = '-' != captures[1]; + return tok; + } + }, + + /** + * Interpolated tag. + */ + + interpolation: function() { + var captures; + if (captures = /^#\{(.*?)\}/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('interpolation', captures[1]); + } + }, + + /** + * Tag. + */ + + tag: function() { + var captures; + if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) { + this.consume(captures[0].length); + var tok, name = captures[1]; + if (':' == name[name.length - 1]) { + name = name.slice(0, -1); + tok = this.tok('tag', name); + this.defer(this.tok(':')); + while (' ' == this.input[0]) this.input = this.input.substr(1); + } else { + tok = this.tok('tag', name); + } + tok.selfClosing = !! captures[2]; + return tok; + } + }, + + /** + * Filter. + */ + + filter: function() { + return this.scan(/^:(\w+)/, 'filter'); + }, + + /** + * Doctype. + */ + + doctype: function() { + return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype'); + }, + + /** + * Id. + */ + + id: function() { + return this.scan(/^#([\w-]+)/, 'id'); + }, + + /** + * Class. + */ + + className: function() { + return this.scan(/^\.([\w-]+)/, 'class'); + }, + + /** + * Text. + */ + + text: function() { + return this.scan(/^(?:\| ?| ?)?([^\n]+)/, 'text'); + }, + + /** + * Extends. + */ + + "extends": function() { + return this.scan(/^extends? +([^\n]+)/, 'extends'); + }, + + /** + * Block prepend. + */ + + prepend: function() { + var captures; + if (captures = /^prepend +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'prepend' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block append. + */ + + append: function() { + var captures; + if (captures = /^append +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'append' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block. + */ + + block: function() { + var captures; + if (captures = /^block\b *(?:(prepend|append) +)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = captures[1] || 'replace' + , name = captures[2] + , tok = this.tok('block', name); + + tok.mode = mode; + return tok; + } + }, + + /** + * Yield. + */ + + yield: function() { + return this.scan(/^yield */, 'yield'); + }, + + /** + * Include. + */ + + include: function() { + return this.scan(/^include +([^\n]+)/, 'include'); + }, + + /** + * Case. + */ + + "case": function() { + return this.scan(/^case +([^\n]+)/, 'case'); + }, + + /** + * When. + */ + + when: function() { + return this.scan(/^when +([^:\n]+)/, 'when'); + }, + + /** + * Default. + */ + + "default": function() { + return this.scan(/^default */, 'default'); + }, + + /** + * Assignment. + */ + + assignment: function() { + var captures; + if (captures = /^(\w+) += *([^;\n]+)( *;? *)/.exec(this.input)) { + this.consume(captures[0].length); + var name = captures[1] + , val = captures[2]; + return this.tok('code', 'var ' + name + ' = (' + val + ');'); + } + }, + + /** + * Call mixin. + */ + + call: function(){ + var captures; + if (captures = /^\+([-\w]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('call', captures[1]); + + // Check for args (not attributes) + if (captures = /^ *\((.*?)\)/.exec(this.input)) { + if (!/^ *[-\w]+ *=/.test(captures[1])) { + this.consume(captures[0].length); + tok.args = captures[1]; + } + } + + return tok; + } + }, + + /** + * Mixin. + */ + + mixin: function(){ + var captures; + if (captures = /^mixin +([-\w]+)(?: *\((.*)\))?/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('mixin', captures[1]); + tok.args = captures[2]; + return tok; + } + }, + + /** + * Conditional. + */ + + conditional: function() { + var captures; + if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var type = captures[1] + , js = captures[2]; + + switch (type) { + case 'if': js = 'if (' + js + ')'; break; + case 'unless': js = 'if (!(' + js + '))'; break; + case 'else if': js = 'else if (' + js + ')'; break; + case 'else': js = 'else'; break; + } + + return this.tok('code', js); + } + }, + + /** + * While. + */ + + "while": function() { + var captures; + if (captures = /^while +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('code', 'while (' + captures[1] + ')'); + } + }, + + /** + * Each. + */ + + each: function() { + var captures; + if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('each', captures[1]); + tok.key = captures[2] || '$index'; + tok.code = captures[3]; + return tok; + } + }, + + /** + * Code. + */ + + code: function() { + var captures; + if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var flags = captures[1]; + captures[1] = captures[2]; + var tok = this.tok('code', captures[1]); + tok.escape = flags.charAt(0) === '='; + tok.buffer = flags.charAt(0) === '=' || flags.charAt(1) === '='; + return tok; + } + }, + + /** + * Attributes. + */ + + attrs: function() { + if ('(' == this.input.charAt(0)) { + var index = this.indexOfDelimiters('(', ')') + , str = this.input.substr(1, index-1) + , tok = this.tok('attrs') + , len = str.length + , colons = this.colons + , states = ['key'] + , escapedAttr + , key = '' + , val = '' + , quote + , c + , p; + + function state(){ + return states[states.length - 1]; + } + + function interpolate(attr) { + return attr.replace(/(\\)?#\{([^}]+)\}/g, function(_, escape, expr){ + return escape + ? _ + : quote + " + (" + expr + ") + " + quote; + }); + } + + this.consume(index + 1); + tok.attrs = {}; + tok.escaped = {}; + + function parse(c) { + var real = c; + // TODO: remove when people fix ":" + if (colons && ':' == c) c = '='; + switch (c) { + case ',': + case '\n': + switch (state()) { + case 'expr': + case 'array': + case 'string': + case 'object': + val += c; + break; + default: + states.push('key'); + val = val.trim(); + key = key.trim(); + if ('' == key) return; + key = key.replace(/^['"]|['"]$/g, '').replace('!', ''); + tok.escaped[key] = escapedAttr; + tok.attrs[key] = '' == val + ? true + : interpolate(val); + key = val = ''; + } + break; + case '=': + switch (state()) { + case 'key char': + key += real; + break; + case 'val': + case 'expr': + case 'array': + case 'string': + case 'object': + val += real; + break; + default: + escapedAttr = '!' != p; + states.push('val'); + } + break; + case '(': + if ('val' == state() + || 'expr' == state()) states.push('expr'); + val += c; + break; + case ')': + if ('expr' == state() + || 'val' == state()) states.pop(); + val += c; + break; + case '{': + if ('val' == state()) states.push('object'); + val += c; + break; + case '}': + if ('object' == state()) states.pop(); + val += c; + break; + case '[': + if ('val' == state()) states.push('array'); + val += c; + break; + case ']': + if ('array' == state()) states.pop(); + val += c; + break; + case '"': + case "'": + switch (state()) { + case 'key': + states.push('key char'); + break; + case 'key char': + states.pop(); + break; + case 'string': + if (c == quote) states.pop(); + val += c; + break; + default: + states.push('string'); + val += c; + quote = c; + } + break; + case '': + break; + default: + switch (state()) { + case 'key': + case 'key char': + key += c; + break; + default: + val += c; + } + } + p = c; + } + + for (var i = 0; i < len; ++i) { + parse(str.charAt(i)); + } + + parse(','); + + if ('/' == this.input.charAt(0)) { + this.consume(1); + tok.selfClosing = true; + } + + return tok; + } + }, + + /** + * Indent | Outdent | Newline. + */ + + indent: function() { + var captures, re; + + // established regexp + if (this.indentRe) { + captures = this.indentRe.exec(this.input); + // determine regexp + } else { + // tabs + re = /^\n(\t*) */; + captures = re.exec(this.input); + + // spaces + if (captures && !captures[1].length) { + re = /^\n( *)/; + captures = re.exec(this.input); + } + + // established + if (captures && captures[1].length) this.indentRe = re; + } + + if (captures) { + var tok + , indents = captures[1].length; + + ++this.lineno; + this.consume(indents + 1); + + if (' ' == this.input[0] || '\t' == this.input[0]) { + throw new Error('Invalid indentation, you can use tabs or spaces but not both'); + } + + // blank line + if ('\n' == this.input[0]) return this.tok('newline'); + + // outdent + if (this.indentStack.length && indents < this.indentStack[0]) { + while (this.indentStack.length && this.indentStack[0] > indents) { + this.stash.push(this.tok('outdent')); + this.indentStack.shift(); + } + tok = this.stash.pop(); + // indent + } else if (indents && indents != this.indentStack[0]) { + this.indentStack.unshift(indents); + tok = this.tok('indent', indents); + // newline + } else { + tok = this.tok('newline'); + } + + return tok; + } + }, + + /** + * Pipe-less text consumed only when + * pipeless is true; + */ + + pipelessText: function() { + if (this.pipeless) { + if ('\n' == this.input[0]) return; + var i = this.input.indexOf('\n'); + if (-1 == i) i = this.input.length; + var str = this.input.substr(0, i); + this.consume(str.length); + return this.tok('text', str); + } + }, + + /** + * ':' + */ + + colon: function() { + return this.scan(/^: */, ':'); + }, + + /** + * Return the next token object, or those + * previously stashed by lookahead. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.stashed() + || this.next(); + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + next: function() { + return this.deferred() + || this.blank() + || this.eos() + || this.pipelessText() + || this.yield() + || this.doctype() + || this.interpolation() + || this["case"]() + || this.when() + || this["default"]() + || this["extends"]() + || this.append() + || this.prepend() + || this.block() + || this.include() + || this.mixin() + || this.call() + || this.conditional() + || this.each() + || this["while"]() + || this.assignment() + || this.tag() + || this.filter() + || this.code() + || this.id() + || this.className() + || this.attrs() + || this.indent() + || this.comment() + || this.colon() + || this.text(); + } +}; + +}); // module: lexer.js + +require.register("nodes/attrs.js", function(module, exports, require){ + +/*! + * Jade - nodes - Attrs + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'), + Block = require('./block'); + +/** + * Initialize a `Attrs` node. + * + * @api public + */ + +var Attrs = module.exports = function Attrs() { + this.attrs = []; +}; + +/** + * Inherit from `Node`. + */ + +Attrs.prototype = new Node; +Attrs.prototype.constructor = Attrs; + + +/** + * Set attribute `name` to `val`, keep in mind these become + * part of a raw js object literal, so to quote a value you must + * '"quote me"', otherwise or example 'user.name' is literal JavaScript. + * + * @param {String} name + * @param {String} val + * @param {Boolean} escaped + * @return {Tag} for chaining + * @api public + */ + +Attrs.prototype.setAttribute = function(name, val, escaped){ + this.attrs.push({ name: name, val: val, escaped: escaped }); + return this; +}; + +/** + * Remove attribute `name` when present. + * + * @param {String} name + * @api public + */ + +Attrs.prototype.removeAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + delete this.attrs[i]; + } + } +}; + +/** + * Get attribute value by `name`. + * + * @param {String} name + * @return {String} + * @api public + */ + +Attrs.prototype.getAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + return this.attrs[i].val; + } + } +}; + +}); // module: nodes/attrs.js + +require.register("nodes/block-comment.js", function(module, exports, require){ + +/*! + * Jade - nodes - BlockComment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `BlockComment` with the given `block`. + * + * @param {String} val + * @param {Block} block + * @param {Boolean} buffer + * @api public + */ + +var BlockComment = module.exports = function BlockComment(val, block, buffer) { + this.block = block; + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +BlockComment.prototype = new Node; +BlockComment.prototype.constructor = BlockComment; + +}); // module: nodes/block-comment.js + +require.register("nodes/block.js", function(module, exports, require){ + +/*! + * Jade - nodes - Block + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Block` with an optional `node`. + * + * @param {Node} node + * @api public + */ + +var Block = module.exports = function Block(node){ + this.nodes = []; + if (node) this.push(node); +}; + +/** + * Inherit from `Node`. + */ + +Block.prototype = new Node; +Block.prototype.constructor = Block; + + +/** + * Block flag. + */ + +Block.prototype.isBlock = true; + +/** + * Replace the nodes in `other` with the nodes + * in `this` block. + * + * @param {Block} other + * @api private + */ + +Block.prototype.replace = function(other){ + other.nodes = this.nodes; +}; + +/** + * Pust the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.push = function(node){ + return this.nodes.push(node); +}; + +/** + * Check if this block is empty. + * + * @return {Boolean} + * @api public + */ + +Block.prototype.isEmpty = function(){ + return 0 == this.nodes.length; +}; + +/** + * Unshift the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.unshift = function(node){ + return this.nodes.unshift(node); +}; + +/** + * Return the "last" block, or the first `yield` node. + * + * @return {Block} + * @api private + */ + +Block.prototype.includeBlock = function(){ + var ret = this + , node; + + for (var i = 0, len = this.nodes.length; i < len; ++i) { + node = this.nodes[i]; + if (node.yield) return node; + else if (node.textOnly) continue; + else if (node.includeBlock) ret = node.includeBlock(); + else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock(); + if (ret.yield) return ret; + } + + return ret; +}; + +/** + * Return a clone of this block. + * + * @return {Block} + * @api private + */ + +Block.prototype.clone = function(){ + var clone = new Block; + for (var i = 0, len = this.nodes.length; i < len; ++i) { + clone.push(this.nodes[i].clone()); + } + return clone; +}; + + +}); // module: nodes/block.js + +require.register("nodes/case.js", function(module, exports, require){ + +/*! + * Jade - nodes - Case + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Case` with `expr`. + * + * @param {String} expr + * @api public + */ + +var Case = exports = module.exports = function Case(expr, block){ + this.expr = expr; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Case.prototype = new Node; +Case.prototype.constructor = Case; + + +var When = exports.When = function When(expr, block){ + this.expr = expr; + this.block = block; + this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +When.prototype = new Node; +When.prototype.constructor = When; + + + +}); // module: nodes/case.js + +require.register("nodes/code.js", function(module, exports, require){ + +/*! + * Jade - nodes - Code + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Code` node with the given code `val`. + * Code may also be optionally buffered and escaped. + * + * @param {String} val + * @param {Boolean} buffer + * @param {Boolean} escape + * @api public + */ + +var Code = module.exports = function Code(val, buffer, escape) { + this.val = val; + this.buffer = buffer; + this.escape = escape; + if (val.match(/^ *else/)) this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +Code.prototype = new Node; +Code.prototype.constructor = Code; + +}); // module: nodes/code.js + +require.register("nodes/comment.js", function(module, exports, require){ + +/*! + * Jade - nodes - Comment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Comment` with the given `val`, optionally `buffer`, + * otherwise the comment may render in the output. + * + * @param {String} val + * @param {Boolean} buffer + * @api public + */ + +var Comment = module.exports = function Comment(val, buffer) { + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +Comment.prototype = new Node; +Comment.prototype.constructor = Comment; + +}); // module: nodes/comment.js + +require.register("nodes/doctype.js", function(module, exports, require){ + +/*! + * Jade - nodes - Doctype + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Doctype` with the given `val`. + * + * @param {String} val + * @api public + */ + +var Doctype = module.exports = function Doctype(val) { + this.val = val; +}; + +/** + * Inherit from `Node`. + */ + +Doctype.prototype = new Node; +Doctype.prototype.constructor = Doctype; + +}); // module: nodes/doctype.js + +require.register("nodes/each.js", function(module, exports, require){ + +/*! + * Jade - nodes - Each + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize an `Each` node, representing iteration + * + * @param {String} obj + * @param {String} val + * @param {String} key + * @param {Block} block + * @api public + */ + +var Each = module.exports = function Each(obj, val, key, block) { + this.obj = obj; + this.val = val; + this.key = key; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Each.prototype = new Node; +Each.prototype.constructor = Each; + +}); // module: nodes/each.js + +require.register("nodes/filter.js", function(module, exports, require){ + +/*! + * Jade - nodes - Filter + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node') + , Block = require('./block'); + +/** + * Initialize a `Filter` node with the given + * filter `name` and `block`. + * + * @param {String} name + * @param {Block|Node} block + * @api public + */ + +var Filter = module.exports = function Filter(name, block, attrs) { + this.name = name; + this.block = block; + this.attrs = attrs; + this.isASTFilter = !block.nodes.every(function(node){ return node.isText }); +}; + +/** + * Inherit from `Node`. + */ + +Filter.prototype = new Node; +Filter.prototype.constructor = Filter; + +}); // module: nodes/filter.js + +require.register("nodes/index.js", function(module, exports, require){ + +/*! + * Jade - nodes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +exports.Node = require('./node'); +exports.Tag = require('./tag'); +exports.Code = require('./code'); +exports.Each = require('./each'); +exports.Case = require('./case'); +exports.Text = require('./text'); +exports.Block = require('./block'); +exports.Mixin = require('./mixin'); +exports.Filter = require('./filter'); +exports.Comment = require('./comment'); +exports.Literal = require('./literal'); +exports.BlockComment = require('./block-comment'); +exports.Doctype = require('./doctype'); + +}); // module: nodes/index.js + +require.register("nodes/literal.js", function(module, exports, require){ + +/*! + * Jade - nodes - Literal + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Literal` node with the given `str. + * + * @param {String} str + * @api public + */ + +var Literal = module.exports = function Literal(str) { + this.str = str + .replace(/\\/g, "\\\\") + .replace(/\n|\r\n/g, "\\n") + .replace(/'/g, "\\'"); +}; + +/** + * Inherit from `Node`. + */ + +Literal.prototype = new Node; +Literal.prototype.constructor = Literal; + + +}); // module: nodes/literal.js + +require.register("nodes/mixin.js", function(module, exports, require){ + +/*! + * Jade - nodes - Mixin + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'); + +/** + * Initialize a new `Mixin` with `name` and `block`. + * + * @param {String} name + * @param {String} args + * @param {Block} block + * @api public + */ + +var Mixin = module.exports = function Mixin(name, args, block, call){ + this.name = name; + this.args = args; + this.block = block; + this.attrs = []; + this.call = call; +}; + +/** + * Inherit from `Attrs`. + */ + +Mixin.prototype = new Attrs; +Mixin.prototype.constructor = Mixin; + + + +}); // module: nodes/mixin.js + +require.register("nodes/node.js", function(module, exports, require){ + +/*! + * Jade - nodes - Node + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize a `Node`. + * + * @api public + */ + +var Node = module.exports = function Node(){}; + +/** + * Clone this node (return itself) + * + * @return {Node} + * @api private + */ + +Node.prototype.clone = function(){ + return this; +}; + +}); // module: nodes/node.js + +require.register("nodes/tag.js", function(module, exports, require){ + +/*! + * Jade - nodes - Tag + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'), + Block = require('./block'), + inlineTags = require('../inline-tags'); + +/** + * Initialize a `Tag` node with the given tag `name` and optional `block`. + * + * @param {String} name + * @param {Block} block + * @api public + */ + +var Tag = module.exports = function Tag(name, block) { + this.name = name; + this.attrs = []; + this.block = block || new Block; +}; + +/** + * Inherit from `Attrs`. + */ + +Tag.prototype = new Attrs; +Tag.prototype.constructor = Tag; + + +/** + * Clone this tag. + * + * @return {Tag} + * @api private + */ + +Tag.prototype.clone = function(){ + var clone = new Tag(this.name, this.block.clone()); + clone.line = this.line; + clone.attrs = this.attrs; + clone.textOnly = this.textOnly; + return clone; +}; + +/** + * Check if this tag is an inline tag. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.isInline = function(){ + return ~inlineTags.indexOf(this.name); +}; + +/** + * Check if this tag's contents can be inlined. Used for pretty printing. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.canInline = function(){ + var nodes = this.block.nodes; + + function isInline(node){ + // Recurse if the node is a block + if (node.isBlock) return node.nodes.every(isInline); + return node.isText || (node.isInline && node.isInline()); + } + + // Empty tag + if (!nodes.length) return true; + + // Text-only or inline-only tag + if (1 == nodes.length) return isInline(nodes[0]); + + // Multi-line inline-only tag + if (this.block.nodes.every(isInline)) { + for (var i = 1, len = nodes.length; i < len; ++i) { + if (nodes[i-1].isText && nodes[i].isText) + return false; + } + return true; + } + + // Mixed tag + return false; +}; +}); // module: nodes/tag.js + +require.register("nodes/text.js", function(module, exports, require){ + +/*! + * Jade - nodes - Text + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Text` node with optional `line`. + * + * @param {String} line + * @api public + */ + +var Text = module.exports = function Text(line) { + this.val = ''; + if ('string' == typeof line) this.val = line; +}; + +/** + * Inherit from `Node`. + */ + +Text.prototype = new Node; +Text.prototype.constructor = Text; + + +/** + * Flag as text. + */ + +Text.prototype.isText = true; +}); // module: nodes/text.js + +require.register("parser.js", function(module, exports, require){ + +/*! + * Jade - Parser + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Lexer = require('./lexer') + , nodes = require('./nodes') + , utils = require('./utils'); + +/** + * Initialize `Parser` with the given input `str` and `filename`. + * + * @param {String} str + * @param {String} filename + * @param {Object} options + * @api public + */ + +var Parser = exports = module.exports = function Parser(str, filename, options){ + this.input = str; + this.lexer = new Lexer(str, options); + this.filename = filename; + this.blocks = {}; + this.mixins = {}; + this.options = options; + this.contexts = [this]; +}; + +/** + * Tags that may not contain tags. + */ + +var textOnly = exports.textOnly = ['script', 'style']; + +/** + * Parser prototype. + */ + +Parser.prototype = { + + /** + * Push `parser` onto the context stack, + * or pop and return a `Parser`. + */ + + context: function(parser){ + if (parser) { + this.contexts.push(parser); + } else { + return this.contexts.pop(); + } + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.lexer.advance(); + }, + + /** + * Skip `n` tokens. + * + * @param {Number} n + * @api private + */ + + skip: function(n){ + while (n--) this.advance(); + }, + + /** + * Single token lookahead. + * + * @return {Object} + * @api private + */ + + peek: function() { + return this.lookahead(1); + }, + + /** + * Return lexer lineno. + * + * @return {Number} + * @api private + */ + + line: function() { + return this.lexer.lineno; + }, + + /** + * `n` token lookahead. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + return this.lexer.lookahead(n); + }, + + /** + * Parse input returning a string of js for evaluation. + * + * @return {String} + * @api public + */ + + parse: function(){ + var block = new nodes.Block, parser; + block.line = this.line(); + + while ('eos' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + + if (parser = this.extending) { + this.context(parser); + var ast = parser.parse(); + this.context(); + // hoist mixins + for (var name in this.mixins) + ast.unshift(this.mixins[name]); + return ast; + } + + return block; + }, + + /** + * Expect the given type, or throw an exception. + * + * @param {String} type + * @api private + */ + + expect: function(type){ + if (this.peek().type === type) { + return this.advance(); + } else { + throw new Error('expected "' + type + '", but got "' + this.peek().type + '"'); + } + }, + + /** + * Accept the given `type`. + * + * @param {String} type + * @api private + */ + + accept: function(type){ + if (this.peek().type === type) { + return this.advance(); + } + }, + + /** + * tag + * | doctype + * | mixin + * | include + * | filter + * | comment + * | text + * | each + * | code + * | yield + * | id + * | class + * | interpolation + */ + + parseExpr: function(){ + switch (this.peek().type) { + case 'tag': + return this.parseTag(); + case 'mixin': + return this.parseMixin(); + case 'block': + return this.parseBlock(); + case 'case': + return this.parseCase(); + case 'when': + return this.parseWhen(); + case 'default': + return this.parseDefault(); + case 'extends': + return this.parseExtends(); + case 'include': + return this.parseInclude(); + case 'doctype': + return this.parseDoctype(); + case 'filter': + return this.parseFilter(); + case 'comment': + return this.parseComment(); + case 'text': + return this.parseText(); + case 'each': + return this.parseEach(); + case 'code': + return this.parseCode(); + case 'call': + return this.parseCall(); + case 'interpolation': + return this.parseInterpolation(); + case 'yield': + this.advance(); + var block = new nodes.Block; + block.yield = true; + return block; + case 'id': + case 'class': + var tok = this.advance(); + this.lexer.defer(this.lexer.tok('tag', 'div')); + this.lexer.defer(tok); + return this.parseExpr(); + default: + throw new Error('unexpected token "' + this.peek().type + '"'); + } + }, + + /** + * Text + */ + + parseText: function(){ + var tok = this.expect('text') + , node = new nodes.Text(tok.val); + node.line = this.line(); + return node; + }, + + /** + * ':' expr + * | block + */ + + parseBlockExpansion: function(){ + if (':' == this.peek().type) { + this.advance(); + return new nodes.Block(this.parseExpr()); + } else { + return this.block(); + } + }, + + /** + * case + */ + + parseCase: function(){ + var val = this.expect('case').val + , node = new nodes.Case(val); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * when + */ + + parseWhen: function(){ + var val = this.expect('when').val + return new nodes.Case.When(val, this.parseBlockExpansion()); + }, + + /** + * default + */ + + parseDefault: function(){ + this.expect('default'); + return new nodes.Case.When('default', this.parseBlockExpansion()); + }, + + /** + * code + */ + + parseCode: function(){ + var tok = this.expect('code') + , node = new nodes.Code(tok.val, tok.buffer, tok.escape) + , block + , i = 1; + node.line = this.line(); + while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i; + block = 'indent' == this.lookahead(i).type; + if (block) { + this.skip(i-1); + node.block = this.block(); + } + return node; + }, + + /** + * comment + */ + + parseComment: function(){ + var tok = this.expect('comment') + , node; + + if ('indent' == this.peek().type) { + node = new nodes.BlockComment(tok.val, this.block(), tok.buffer); + } else { + node = new nodes.Comment(tok.val, tok.buffer); + } + + node.line = this.line(); + return node; + }, + + /** + * doctype + */ + + parseDoctype: function(){ + var tok = this.expect('doctype') + , node = new nodes.Doctype(tok.val); + node.line = this.line(); + return node; + }, + + /** + * filter attrs? text-block + */ + + parseFilter: function(){ + var block + , tok = this.expect('filter') + , attrs = this.accept('attrs'); + + this.lexer.pipeless = true; + block = this.parseTextBlock(); + this.lexer.pipeless = false; + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * tag ':' attrs? block + */ + + parseASTFilter: function(){ + var block + , tok = this.expect('tag') + , attrs = this.accept('attrs'); + + this.expect(':'); + block = this.block(); + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * each block + */ + + parseEach: function(){ + var tok = this.expect('each') + , node = new nodes.Each(tok.code, tok.val, tok.key); + node.line = this.line(); + node.block = this.block(); + if (this.peek().type == 'code' && this.peek().val == 'else') { + this.advance(); + node.alternative = this.block(); + } + return node; + }, + + /** + * 'extends' name + */ + + parseExtends: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + if (!this.filename) + throw new Error('the "filename" option is required to extend templates'); + + var path = this.expect('extends').val.trim() + , dir = dirname(this.filename); + + var path = join(dir, path + '.jade') + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + + parser.blocks = this.blocks; + parser.contexts = this.contexts; + this.extending = parser; + + // TODO: null node + return new nodes.Literal(''); + }, + + /** + * 'block' name block + */ + + parseBlock: function(){ + var block = this.expect('block') + , mode = block.mode + , name = block.val.trim(); + + block = 'indent' == this.peek().type + ? this.block() + : new nodes.Block(new nodes.Literal('')); + + var prev = this.blocks[name]; + + if (prev) { + switch (prev.mode) { + case 'append': + block.nodes = block.nodes.concat(prev.nodes); + prev = block; + break; + case 'prepend': + block.nodes = prev.nodes.concat(block.nodes); + prev = block; + break; + } + } + + block.mode = mode; + return this.blocks[name] = prev || block; + }, + + /** + * include block? + */ + + parseInclude: function(){ + var path = require('path') + , fs = require('fs') + , dirname = path.dirname + , basename = path.basename + , join = path.join; + + var path = this.expect('include').val.trim() + , dir = dirname(this.filename); + + if (!this.filename) + throw new Error('the "filename" option is required to use includes'); + + // no extension + if (!~basename(path).indexOf('.')) { + path += '.jade'; + } + + // non-jade + if ('.jade' != path.substr(-5)) { + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8'); + return new nodes.Literal(str); + } + + var path = join(dir, path) + , str = fs.readFileSync(path, 'utf8') + , parser = new Parser(str, path, this.options); + parser.blocks = utils.merge({}, this.blocks); + parser.mixins = this.mixins; + + this.context(parser); + var ast = parser.parse(); + this.context(); + ast.filename = path; + + if ('indent' == this.peek().type) { + ast.includeBlock().push(this.block()); + } + + return ast; + }, + + /** + * call ident block + */ + + parseCall: function(){ + var tok = this.expect('call') + , name = tok.val + , args = tok.args + , mixin = new nodes.Mixin(name, args, new nodes.Block, true); + + this.tag(mixin); + if (mixin.block.isEmpty()) mixin.block = null; + return mixin; + }, + + /** + * mixin block + */ + + parseMixin: function(){ + var tok = this.expect('mixin') + , name = tok.val + , args = tok.args + , mixin; + + // definition + if ('indent' == this.peek().type) { + mixin = new nodes.Mixin(name, args, this.block(), false); + this.mixins[name] = mixin; + return mixin; + // call + } else { + return new nodes.Mixin(name, args, null, true); + } + }, + + /** + * indent (text | newline)* outdent + */ + + parseTextBlock: function(){ + var block = new nodes.Block; + block.line = this.line(); + var spaces = this.expect('indent').val; + if (null == this._spaces) this._spaces = spaces; + var indent = Array(spaces - this._spaces + 1).join(' '); + while ('outdent' != this.peek().type) { + switch (this.peek().type) { + case 'newline': + this.advance(); + break; + case 'indent': + this.parseTextBlock().nodes.forEach(function(node){ + block.push(node); + }); + break; + default: + var text = new nodes.Text(indent + this.advance().val); + text.line = this.line(); + block.push(text); + } + } + + if (spaces == this._spaces) this._spaces = null; + this.expect('outdent'); + return block; + }, + + /** + * indent expr* outdent + */ + + block: function(){ + var block = new nodes.Block; + block.line = this.line(); + this.expect('indent'); + while ('outdent' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + this.expect('outdent'); + return block; + }, + + /** + * interpolation (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseInterpolation: function(){ + var tok = this.advance(); + var tag = new nodes.Tag(tok.val); + tag.buffer = true; + return this.tag(tag); + }, + + /** + * tag (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseTag: function(){ + // ast-filter look-ahead + var i = 2; + if ('attrs' == this.lookahead(i).type) ++i; + if (':' == this.lookahead(i).type) { + if ('indent' == this.lookahead(++i).type) { + return this.parseASTFilter(); + } + } + + var tok = this.advance() + , tag = new nodes.Tag(tok.val); + + tag.selfClosing = tok.selfClosing; + + return this.tag(tag); + }, + + /** + * Parse tag. + */ + + tag: function(tag){ + var dot; + + tag.line = this.line(); + + // (attrs | class | id)* + out: + while (true) { + switch (this.peek().type) { + case 'id': + case 'class': + var tok = this.advance(); + tag.setAttribute(tok.type, "'" + tok.val + "'"); + continue; + case 'attrs': + var tok = this.advance() + , obj = tok.attrs + , escaped = tok.escaped + , names = Object.keys(obj); + + if (tok.selfClosing) tag.selfClosing = true; + + for (var i = 0, len = names.length; i < len; ++i) { + var name = names[i] + , val = obj[name]; + tag.setAttribute(name, val, escaped[name]); + } + continue; + default: + break out; + } + } + + // check immediate '.' + if ('.' == this.peek().val) { + dot = tag.textOnly = true; + this.advance(); + } + + // (text | code | ':')? + switch (this.peek().type) { + case 'text': + tag.block.push(this.parseText()); + break; + case 'code': + tag.code = this.parseCode(); + break; + case ':': + this.advance(); + tag.block = new nodes.Block; + tag.block.push(this.parseExpr()); + break; + } + + // newline* + while ('newline' == this.peek().type) this.advance(); + + tag.textOnly = tag.textOnly || ~textOnly.indexOf(tag.name); + + // script special-case + if ('script' == tag.name) { + var type = tag.getAttribute('type'); + if (!dot && type && 'text/javascript' != type.replace(/^['"]|['"]$/g, '')) { + tag.textOnly = false; + } + } + + // block? + if ('indent' == this.peek().type) { + if (tag.textOnly) { + this.lexer.pipeless = true; + tag.block = this.parseTextBlock(); + this.lexer.pipeless = false; + } else { + var block = this.block(); + if (tag.block) { + for (var i = 0, len = block.nodes.length; i < len; ++i) { + tag.block.push(block.nodes[i]); + } + } else { + tag.block = block; + } + } + } + + return tag; + } +}; + +}); // module: parser.js + +require.register("runtime.js", function(module, exports, require){ + +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; + +}); // module: runtime.js + +require.register("self-closing.js", function(module, exports, require){ + +/*! + * Jade - self closing tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'meta' + , 'img' + , 'link' + , 'input' + , 'source' + , 'area' + , 'base' + , 'col' + , 'br' + , 'hr' +]; +}); // module: self-closing.js + +require.register("utils.js", function(module, exports, require){ + +/*! + * Jade - utils + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Convert interpolation in the given string to JavaScript. + * + * @param {String} str + * @return {String} + * @api private + */ + +var interpolate = exports.interpolate = function(str){ + return str.replace(/(_SLASH_)?([#!]){(.*?)}/g, function(str, escape, flag, code){ + code = code + .replace(/\\'/g, "'") + .replace(/_SLASH_/g, '\\'); + + return escape + ? str.slice(7) + : "' + " + + ('!' == flag ? '' : 'escape') + + "((interp = " + code + + ") == null ? '' : interp) + '"; + }); +}; + +/** + * Escape single quotes in `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +var escape = exports.escape = function(str) { + return str.replace(/'/g, "\\'"); +}; + +/** + * Interpolate, and escape the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.text = function(str){ + return interpolate(escape(str)); +}; + +/** + * Merge `b` into `a`. + * + * @param {Object} a + * @param {Object} b + * @return {Object} + * @api public + */ + +exports.merge = function(a, b) { + for (var key in b) a[key] = b[key]; + return a; +}; + + +}); // module: utils.js + +window.jade = require("jade"); +})(); diff --git a/test/node_modules/jade/jade.md b/test/node_modules/jade/jade.md new file mode 100644 index 0000000..051dc03 --- /dev/null +++ b/test/node_modules/jade/jade.md @@ -0,0 +1,510 @@ + +# Jade + + The jade template engine for node.js + +## Synopsis + + jade [-h|--help] [-v|--version] [-o|--obj STR] + [-O|--out DIR] [-p|--path PATH] [-P|--pretty] + [-c|--client] [-D|--no-debug] + +## Examples + + translate jade the templates dir + + $ jade templates + + create {foo,bar}.html + + $ jade {foo,bar}.jade + + jade over stdio + + $ jade < my.jade > my.html + + jade over s + + $ echo "h1 Jade!" | jade + + foo, bar dirs rendering to /tmp + + $ jade foo bar --out /tmp + + compile client-side templates without debugging + instrumentation, making the output javascript + very light-weight. This requires runtime.js + in your projects. + + $ jade --client --no-debug < my.jade + +## Tags + + Tags are simply nested via whitespace, closing + tags defined for you. These indents are called "blocks". + + ul + li + a Foo + li + a Bar + + You may have several tags in one "block": + + ul + li + a Foo + a Bar + a Baz + +## Self-closing Tags + + Some tags are flagged as self-closing by default, such + as `meta`, `link`, and so on. To explicitly self-close + a tag simply append the `/` character: + + foo/ + foo(bar='baz')/ + + Would yield: + + + + +## Attributes + + Tag attributes look similar to HTML, however + the values are regular JavaScript, here are + some examples: + + a(href='google.com') Google + a(class='button', href='google.com') Google + + As mentioned the attribute values are just JavaScript, + this means ternary operations and other JavaScript expressions + work just fine: + + body(class=user.authenticated ? 'authenticated' : 'anonymous') + a(href=user.website || 'http://google.com') + + Multiple lines work too: + + input(type='checkbox', + name='agreement', + checked) + + Multiple lines without the comma work fine: + + input(type='checkbox' + name='agreement' + checked) + + Funky whitespace? fine: + + input( + type='checkbox' + name='agreement' + checked) + +## Boolean attributes + + Boolean attributes are mirrored by Jade, and accept + bools, aka _true_ or _false_. When no value is specified + _true_ is assumed. For example: + + input(type="checkbox", checked) + // => "" + + For example if the checkbox was for an agreement, perhaps `user.agreed` + was _true_ the following would also output 'checked="checked"': + + input(type="checkbox", checked=user.agreed) + +## Class attributes + + The _class_ attribute accepts an array of classes, + this can be handy when generated from a javascript + function etc: + + classes = ['foo', 'bar', 'baz'] + a(class=classes) + // => "" + +## Class literal + + Classes may be defined using a ".CLASSNAME" syntax: + + .button + // => "
    " + + Or chained: + + .large.button + // => "
    " + + The previous defaulted to divs, however you + may also specify the tag type: + + h1.title My Title + // => "

    My Title

    " + +## Id literal + + Much like the class literal there's an id literal: + + #user-1 + // => "
    " + + Again we may specify the tag as well: + + ul#menu + li: a(href='/home') Home + li: a(href='/store') Store + li: a(href='/contact') Contact + + Finally all of these may be used in any combination, + the following are all valid tags: + + a.button#contact(style: 'color: red') Contact + a.button(style: 'color: red')#contact Contact + a(style: 'color: red').button#contact Contact + +## Block expansion + + Jade supports the concept of "block expansion", in which + using a trailing ":" after a tag will inject a block: + + ul + li: a Foo + li: a Bar + li: a Baz + +## Text + + Arbitrary text may follow tags: + + p Welcome to my site + + yields: + +

    Welcome to my site

    + +## Pipe text + + Another form of text is "pipe" text. Pipes act + as the text margin for large bodies of text. + + p + | This is a large + | body of text for + | this tag. + | + | Nothing too + | exciting. + + yields: + +

    This is a large + body of text for + this tag. + + Nothing too + exciting. +

    + + Using pipes we can also specify regular Jade tags + within the text: + + p + | Click to visit + a(href='http://google.com') Google + | if you want. + +## Text only tags + + As an alternative to pipe text you may add + a trailing "." to indicate that the block + contains nothing but plain-text, no tags: + + p. + This is a large + body of text for + this tag. + + Nothing too + exciting. + + Some tags are text-only by default, for example + _script_, _textarea_, and _style_ tags do not + contain nested HTML so Jade implies the trailing ".": + + script + if (foo) { + bar(); + } + + style + body { + padding: 50px; + font: 14px Helvetica; + } + +## Template script tags + + Sometimes it's useful to define HTML in script + tags using Jade, typically for client-side templates. + + To do this simply give the _script_ tag an arbitrary + _type_ attribute such as _text/x-template_: + + script(type='text/template') + h1 Look! + p Jade still works in here! + +## Interpolation + + Both plain-text and piped-text support interpolation, + which comes in two forms, escapes and non-escaped. The + following will output the _user.name_ in the paragraph + but HTML within it will be escaped to prevent XSS attacks: + + p Welcome #{user.name} + + The following syntax is identical however it will _not_ escape + HTML, and should only be used with strings that you trust: + + p Welcome !{user.name} + +## Inline HTML + + Sometimes constructing small inline snippets of HTML + in Jade can be annoying, luckily we can add plain + HTML as well: + + p Welcome #{user.name} + +## Code + + To buffer output with Jade simply use _=_ at the beginning + of a line or after a tag. This method escapes any HTML + present in the string. + + p= user.description + + To buffer output unescaped use the _!=_ variant, but again + be careful of XSS. + + p!= user.description + + The final way to mess with JavaScript code in Jade is the unbuffered + _-_, which can be used for conditionals, defining variables etc: + + - var user = { description: 'foo bar baz' } + #user + - if (user.description) { + h2 Description + p.description= user.description + - } + + When compiled blocks are wrapped in anonymous functions, so the + following is also valid, without braces: + + - var user = { description: 'foo bar baz' } + #user + - if (user.description) + h2 Description + p.description= user.description + + If you really want you could even use `.forEach()` and others: + + - users.forEach(function(user){ + .user + h2= user.name + p User #{user.name} is #{user.age} years old + - }) + + Taking this further Jade provides some syntax for conditionals, + iteration, switch statements etc. Let's look at those next! + +## Assignment + + Jade's first-class assignment is simple, simply use the _=_ + operator and Jade will _var_ it for you. The following are equivalent: + + - var user = { name: 'tobi' } + user = { name: 'tobi' } + +## Conditionals + + Jade's first-class conditional syntax allows for optional + parenthesis, and you may now omit the leading _-_ otherwise + it's identical, still just regular javascript: + + user = { description: 'foo bar baz' } + #user + if user.description + h2 Description + p.description= user.description + + Jade provides the negated version, _unless_ as well, the following + are equivalent: + + - if (!(user.isAnonymous)) + p You're logged in as #{user.name} + + unless user.isAnonymous + p You're logged in as #{user.name} + +## Iteration + + JavaScript's _for_ loops don't look very declarative, so Jade + also provides its own _for_ loop construct, aliased as _each_: + + for user in users + .user + h2= user.name + p user #{user.name} is #{user.age} year old + + As mentioned _each_ is identical: + + each user in users + .user + h2= user.name + + If necessary the index is available as well: + + for user, i in users + .user(class='user-#{i}') + h2= user.name + + Remember, it's just JavaScript: + + ul#letters + for letter in ['a', 'b', 'c'] + li= letter + +## Mixins + + Mixins provide a way to define jade "functions" which "mix in" + their contents when called. This is useful for abstracting + out large fragments of Jade. + + The simplest possible mixin which accepts no arguments might + look like this: + + mixin hello + p Hello + + You use a mixin by placing `+` before the name: + + +hello + + For something a little more dynamic, mixins can take + arguments, the mixin itself is converted to a javascript + function internally: + + mixin hello(user) + p Hello #{user} + + +hello('Tobi') + + Yields: + +

    Hello Tobi

    + + Mixins may optionally take blocks, when a block is passed + its contents becomes the implicit `block` argument. For + example here is a mixin passed a block, and also invoked + without passing a block: + + mixin article(title) + .article + .article-wrapper + h1= title + if block + block + else + p No content provided + + +article('Hello world') + + +article('Hello world') + p This is my + p Amazing article + + yields: + +
    +
    +

    Hello world

    +

    No content provided

    +
    +
    + +
    +
    +

    Hello world

    +

    This is my

    +

    Amazing article

    +
    +
    + + Mixins can even take attributes, just like a tag. When + attributes are passed they become the implicit `attributes` + argument. Individual attributes can be accessed just like + normal object properties: + + mixin centered + .centered(class=attributes.class) + block + + +centered.bold Hello world + + +centered.red + p This is my + p Amazing article + + yields: + +
    Hello world
    +
    +

    This is my

    +

    Amazing article

    +
    + + If you use `attributes` directly, *all* passed attributes + get used: + + mixin link + a.menu(attributes) + block + + +link.highlight(href='#top') Top + +link#sec1.plain(href='#section1') Section 1 + +link#sec2.plain(href='#section2') Section 2 + + yields: + + Top + Section 1 + Section 2 + + If you pass arguments, they must directly follow the mixin: + + mixin list(arr) + if block + .title + block + ul(attributes) + each item in arr + li= item + + +list(['foo', 'bar', 'baz'])(id='myList', class='bold') + + yields: + +
      +
    • foo
    • +
    • bar
    • +
    • baz
    • +
    diff --git a/test/node_modules/jade/jade.min.js b/test/node_modules/jade/jade.min.js new file mode 100644 index 0000000..93f3b3d --- /dev/null +++ b/test/node_modules/jade/jade.min.js @@ -0,0 +1,2 @@ +(function(){function require(p){var path=require.resolve(p),mod=require.modules[path];if(!mod)throw new Error('failed to require "'+p+'"');return mod.exports||(mod.exports={},mod.call(mod.exports,mod,mod.exports,require.relative(path))),mod.exports}require.modules={},require.resolve=function(path){var orig=path,reg=path+".js",index=path+"/index.js";return require.modules[reg]&®||require.modules[index]&&index||orig},require.register=function(path,fn){require.modules[path]=fn},require.relative=function(parent){return function(p){if("."!=p.charAt(0))return require(p);var path=parent.split("/"),segs=p.split("/");path.pop();for(var i=0;i/g,">").replace(/"/g,""")}var nodes=require("./nodes"),filters=require("./filters"),doctypes=require("./doctypes"),selfClosing=require("./self-closing"),runtime=require("./runtime"),utils=require("./utils");Object.keys||(Object.keys=function(obj){var arr=[];for(var key in obj)obj.hasOwnProperty(key)&&arr.push(key);return arr}),String.prototype.trimLeft||(String.prototype.trimLeft=function(){return this.replace(/^\s+/,"")});var Compiler=module.exports=function Compiler(node,options){this.options=options=options||{},this.node=node,this.hasCompiledDoctype=!1,this.hasCompiledTag=!1,this.pp=options.pretty||!1,this.debug=!1!==options.compileDebug,this.indents=0,this.parentIndents=0,options.doctype&&this.setDoctype(options.doctype)};Compiler.prototype={compile:function(){return this.buf=["var interp;"],this.pp&&this.buf.push("var __indent = [];"),this.lastBufferedIdx=-1,this.visit(this.node),this.buf.join("\n")},setDoctype:function(name){name=name&&name.toLowerCase()||"default",this.doctype=doctypes[name]||"",this.terse=this.doctype.toLowerCase()=="",this.xml=0==this.doctype.indexOf("1&&!escape&&block.nodes[0].isText&&block.nodes[1].isText&&this.prettyIndent(1,!0);for(var i=0;i0&&!escape&&block.nodes[i].isText&&block.nodes[i-1].isText&&this.prettyIndent(1,!1),this.visit(block.nodes[i]),block.nodes[i+1]&&block.nodes[i].isText&&block.nodes[i+1].isText&&this.buffer("\\n")},visitDoctype:function(doctype){doctype&&(doctype.val||!this.doctype)&&this.setDoctype(doctype.val||"default"),this.doctype&&this.buffer(this.doctype),this.hasCompiledDoctype=!0},visitMixin:function(mixin){var name=mixin.name.replace(/-/g,"_")+"_mixin",args=mixin.args||"",block=mixin.block,attrs=mixin.attrs,pp=this.pp;if(mixin.call){pp&&this.buf.push("__indent.push('"+Array(this.indents+1).join(" ")+"');");if(block||attrs.length){this.buf.push(name+".call({");if(block){this.buf.push("block: function(){"),this.parentIndents++;var _indents=this.indents;this.indents=0,this.visit(mixin.block),this.indents=_indents,this.parentIndents--,attrs.length?this.buf.push("},"):this.buf.push("}")}if(attrs.length){var val=this.attrs(attrs);val.inherits?this.buf.push("attributes: merge({"+val.buf+"}, attributes), escaped: merge("+val.escaped+", escaped, true)"):this.buf.push("attributes: {"+val.buf+"}, escaped: "+val.escaped)}args?this.buf.push("}, "+args+");"):this.buf.push("});")}else this.buf.push(name+"("+args+");");pp&&this.buf.push("__indent.pop();")}else this.buf.push("var "+name+" = function("+args+"){"),this.buf.push("var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};"),this.parentIndents++,this.visit(block),this.parentIndents--,this.buf.push("};")},visitTag:function(tag){this.indents++;var name=tag.name,pp=this.pp;tag.buffer&&(name="' + ("+name+") + '"),this.hasCompiledTag||(!this.hasCompiledDoctype&&"html"==name&&this.visitDoctype(),this.hasCompiledTag=!0),pp&&!tag.isInline()&&this.prettyIndent(0,!0),(~selfClosing.indexOf(name)||tag.selfClosing)&&!this.xml?(this.buffer("<"+name),this.visitAttributes(tag.attrs),this.terse?this.buffer(">"):this.buffer("/>")):(tag.attrs.length?(this.buffer("<"+name),tag.attrs.length&&this.visitAttributes(tag.attrs),this.buffer(">")):this.buffer("<"+name+">"),tag.code&&this.visitCode(tag.code),this.escape="pre"==tag.name,this.visit(tag.block),pp&&!tag.isInline()&&"pre"!=tag.name&&!tag.canInline()&&this.prettyIndent(0,!0),this.buffer("")),this.indents--},visitFilter:function(filter){var fn=filters[filter.name];if(!fn)throw filter.isASTFilter?new Error('unknown ast filter "'+filter.name+':"'):new Error('unknown filter ":'+filter.name+'"');if(filter.isASTFilter)this.buf.push(fn(filter.block,this,filter.attrs));else{var text=filter.block.nodes.map(function(node){return node.val}).join("\n");filter.attrs=filter.attrs||{},filter.attrs.filename=this.options.filename,this.buffer(utils.text(fn(text,filter.attrs)))}},visitText:function(text){text=utils.text(text.val.replace(/\\/g,"_SLASH_")),this.escape&&(text=escape(text)),text=text.replace(/_SLASH_/g,"\\\\"),this.buffer(text)},visitComment:function(comment){if(!comment.buffer)return;this.pp&&this.prettyIndent(1,!0),this.buffer("")},visitBlockComment:function(comment){if(!comment.buffer)return;0==comment.val.trim().indexOf("if")?(this.buffer("")):(this.buffer(""))},visitCode:function(code){if(code.buffer){var val=code.val.trimLeft();this.buf.push("var __val__ = "+val),val='null == __val__ ? "" : __val__',code.escape&&(val="escape("+val+")"),this.buf.push("buf.push("+val+");")}else this.buf.push(code.val);code.block&&(code.buffer||this.buf.push("{"),this.visit(code.block),code.buffer||this.buf.push("}"))},visitEach:function(each){this.buf.push("// iterate "+each.obj+"\n"+";(function(){\n"+" if ('number' == typeof "+each.obj+".length) {\n"),each.alternative&&this.buf.push(" if ("+each.obj+".length) {"),this.buf.push(" for (var "+each.key+" = 0, $$l = "+each.obj+".length; "+each.key+" < $$l; "+each.key+"++) {\n"+" var "+each.val+" = "+each.obj+"["+each.key+"];\n"),this.visit(each.block),this.buf.push(" }\n"),each.alternative&&(this.buf.push(" } else {"),this.visit(each.alternative),this.buf.push(" }")),this.buf.push(" } else {\n var $$l = 0;\n for (var "+each.key+" in "+each.obj+") {\n"+" $$l++;"+" if ("+each.obj+".hasOwnProperty("+each.key+")){"+" var "+each.val+" = "+each.obj+"["+each.key+"];\n"),this.visit(each.block),this.buf.push(" }\n"),this.buf.push(" }\n"),each.alternative&&(this.buf.push(" if ($$l === 0) {"),this.visit(each.alternative),this.buf.push(" }")),this.buf.push(" }\n}).call(this);\n")},visitAttributes:function(attrs){var val=this.attrs(attrs);val.inherits?this.buf.push("buf.push(attrs(merge({ "+val.buf+" }, attributes), merge("+val.escaped+", escaped, true)));"):val.constant?(eval("var buf={"+val.buf+"};"),this.buffer(runtime.attrs(buf,JSON.parse(val.escaped)),!0)):this.buf.push("buf.push(attrs({ "+val.buf+" }, "+val.escaped+"));")},attrs:function(attrs){var buf=[],classes=[],escaped={},constant=attrs.every(function(attr){return isConstant(attr.val)}),inherits=!1;return this.terse&&buf.push("terse: true"),attrs.forEach(function(attr){if(attr.name=="attributes")return inherits=!0;escaped[attr.name]=attr.escaped;if(attr.name=="class")classes.push("("+attr.val+")");else{var pair="'"+attr.name+"':("+attr.val+")";buf.push(pair)}}),classes.length&&(classes=classes.join(" + ' ' + "),buf.push("class: "+classes)),{buf:buf.join(", ").replace("class:",'"class":'),escaped:JSON.stringify(escaped),inherits:inherits,constant:constant}}}}),require.register("doctypes.js",function(module,exports,require){module.exports={5:"","default":"",xml:'',transitional:'',strict:'',frameset:'',1.1:'',basic:'',mobile:''}}),require.register("filters.js",function(module,exports,require){module.exports={cdata:function(str){return""},sass:function(str){str=str.replace(/\\n/g,"\n");var sass=require("sass").render(str).replace(/\n/g,"\\n");return'"},stylus:function(str,options){var ret;str=str.replace(/\\n/g,"\n");var stylus=require("stylus");return stylus(str,options).render(function(err,css){if(err)throw err;ret=css.replace(/\n/g,"\\n")}),'"},less:function(str){var ret;return str=str.replace(/\\n/g,"\n"),require("less").render(str,function(err,css){if(err)throw err;ret='"}),ret},markdown:function(str){var md;try{md=require("markdown")}catch(err){try{md=require("discount")}catch(err){try{md=require("markdown-js")}catch(err){try{md=require("marked")}catch(err){throw new Error("Cannot find markdown library, install markdown, discount, or marked.")}}}}return str=str.replace(/\\n/g,"\n"),md.parse(str).replace(/\n/g,"\\n").replace(/'/g,"'")},coffeescript:function(str){var js=require("coffee-script").compile(str).replace(/\\/g,"\\\\").replace(/\n/g,"\\n");return'"}}}),require.register("inline-tags.js",function(module,exports,require){module.exports=["a","abbr","acronym","b","br","code","em","font","i","img","ins","kbd","map","samp","small","span","strong","sub","sup"]}),require.register("jade.js",function(module,exports,require){function parse(str,options){try{var parser=new Parser(str,options.filename,options),compiler=new(options.compiler||Compiler)(parser.parse(),options),js=compiler.compile();return options.debug&&console.error("\nCompiled Function:\n\n%s",js.replace(/^/gm," ")),"var buf = [];\n"+(options.self?"var self = locals || {};\n"+js:"with (locals || {}) {\n"+js+"\n}\n")+'return buf.join("");'}catch(err){parser=parser.context(),runtime.rethrow(err,parser.filename,parser.lexer.lineno)}}function stripBOM(str){return 65279==str.charCodeAt(0)?str.substring(1):str}var Parser=require("./parser"),Lexer=require("./lexer"),Compiler=require("./compiler"),runtime=require("./runtime");exports.version="0.27.6",exports.selfClosing=require("./self-closing"),exports.doctypes=require("./doctypes"),exports.filters=require("./filters"),exports.utils=require("./utils"),exports.Compiler=Compiler,exports.Parser=Parser,exports.Lexer=Lexer,exports.nodes=require("./nodes"),exports.runtime=runtime,exports.cache={},exports.compile=function(str,options){var options=options||{},client=options.client,filename=options.filename?JSON.stringify(options.filename):"undefined",fn;return str=stripBOM(String(str)),options.compileDebug!==!1?fn=["var __jade = [{ lineno: 1, filename: "+filename+" }];","try {",parse(str,options),"} catch (err) {"," rethrow(err, __jade[0].filename, __jade[0].lineno);","}"].join("\n"):fn=parse(str,options),client&&(fn="attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n"+fn),fn=new Function("locals, attrs, escape, rethrow, merge",fn),client?fn:function(locals){return fn(locals,runtime.attrs,runtime.escape,runtime.rethrow,runtime.merge)}},exports.render=function(str,options,fn){"function"==typeof options&&(fn=options,options={});if(options.cache&&!options.filename)return fn(new Error('the "filename" option is required for caching'));try{var path=options.filename,tmpl=options.cache?exports.cache[path]||(exports.cache[path]=exports.compile(str,options)):exports.compile(str,options);fn(null,tmpl(options))}catch(err){fn(err)}},exports.renderFile=function(path,options,fn){var key=path+":string";"function"==typeof options&&(fn=options,options={});try{options.filename=path;var str=options.cache?exports.cache[key]||(exports.cache[key]=fs.readFileSync(path,"utf8")):fs.readFileSync(path,"utf8");exports.render(str,options,fn)}catch(err){fn(err)}},exports.__express=exports.renderFile}),require.register("lexer.js",function(module,exports,require){var utils=require("./utils"),Lexer=module.exports=function Lexer(str,options){options=options||{},this.input=str.replace(/\r\n|\r/g,"\n"),this.colons=options.colons,this.deferredTokens=[],this.lastIndents=0,this.lineno=1,this.stash=[],this.indentStack=[],this.indentRe=null,this.pipeless=!1};Lexer.prototype={tok:function(type,val){return{type:type,line:this.lineno,val:val}},consume:function(len){this.input=this.input.substr(len)},scan:function(regexp,type){var captures;if(captures=regexp.exec(this.input))return this.consume(captures[0].length),this.tok(type,captures[1])},defer:function(tok){this.deferredTokens.push(tok)},lookahead:function(n){var fetch=n-this.stash.length;while(fetch-->0)this.stash.push(this.next());return this.stash[--n]},indexOfDelimiters:function(start,end){var str=this.input,nstart=0,nend=0,pos=0;for(var i=0,len=str.length;iindents)this.stash.push(this.tok("outdent")),this.indentStack.shift();tok=this.stash.pop()}else indents&&indents!=this.indentStack[0]?(this.indentStack.unshift(indents),tok=this.tok("indent",indents)):tok=this.tok("newline");return tok}},pipelessText:function(){if(this.pipeless){if("\n"==this.input[0])return;var i=this.input.indexOf("\n");-1==i&&(i=this.input.length);var str=this.input.substr(0,i);return this.consume(str.length),this.tok("text",str)}},colon:function(){return this.scan(/^: */,":")},advance:function(){return this.stashed()||this.next()},next:function(){return this.deferred()||this.blank()||this.eos()||this.pipelessText()||this.yield()||this.doctype()||this.interpolation()||this["case"]()||this.when()||this["default"]()||this["extends"]()||this.append()||this.prepend()||this.block()||this.include()||this.mixin()||this.call()||this.conditional()||this.each()||this["while"]()||this.assignment()||this.tag()||this.filter()||this.code()||this.id()||this.className()||this.attrs()||this.indent()||this.comment()||this.colon()||this.text()}}}),require.register("nodes/attrs.js",function(module,exports,require){var Node=require("./node"),Block=require("./block"),Attrs=module.exports=function Attrs(){this.attrs=[]};Attrs.prototype=new Node,Attrs.prototype.constructor=Attrs,Attrs.prototype.setAttribute=function(name,val,escaped){return this.attrs.push({name:name,val:val,escaped:escaped}),this},Attrs.prototype.removeAttribute=function(name){for(var i=0,len=this.attrs.length;i/g,">").replace(/"/g,""")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err}}),require.register("self-closing.js",function(module,exports,require){module.exports=["meta","img","link","input","source","area","base","col","br","hr"]}),require.register("utils.js",function(module,exports,require){var interpolate=exports.interpolate=function(str){return str.replace(/(_SLASH_)?([#!]){(.*?)}/g,function(str,escape,flag,code){return code=code.replace(/\\'/g,"'").replace(/_SLASH_/g,"\\"),escape?str.slice(7):"' + "+("!"==flag?"":"escape")+"((interp = "+code+") == null ? '' : interp) + '"})},escape=exports.escape=function(str){return str.replace(/'/g,"\\'")};exports.text=function(str){return interpolate(escape(str))},exports.merge=function(a,b){for(var key in b)a[key]=b[key];return a}}),window.jade=require("jade")})(); \ No newline at end of file diff --git a/test/node_modules/jade/lib/compiler.js b/test/node_modules/jade/lib/compiler.js new file mode 100644 index 0000000..bb56142 --- /dev/null +++ b/test/node_modules/jade/lib/compiler.js @@ -0,0 +1,655 @@ + +/*! + * Jade - Compiler + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var nodes = require('./nodes') + , filters = require('./filters') + , doctypes = require('./doctypes') + , selfClosing = require('./self-closing') + , runtime = require('./runtime') + , utils = require('./utils'); + +// if browser +// +// if (!Object.keys) { +// Object.keys = function(obj){ +// var arr = []; +// for (var key in obj) { +// if (obj.hasOwnProperty(key)) { +// arr.push(key); +// } +// } +// return arr; +// } +// } +// +// if (!String.prototype.trimLeft) { +// String.prototype.trimLeft = function(){ +// return this.replace(/^\s+/, ''); +// } +// } +// +// end + + +/** + * Initialize `Compiler` with the given `node`. + * + * @param {Node} node + * @param {Object} options + * @api public + */ + +var Compiler = module.exports = function Compiler(node, options) { + this.options = options = options || {}; + this.node = node; + this.hasCompiledDoctype = false; + this.hasCompiledTag = false; + this.pp = options.pretty || false; + this.debug = false !== options.compileDebug; + this.indents = 0; + this.parentIndents = 0; + if (options.doctype) this.setDoctype(options.doctype); +}; + +/** + * Compiler prototype. + */ + +Compiler.prototype = { + + /** + * Compile parse tree to JavaScript. + * + * @api public + */ + + compile: function(){ + this.buf = ['var interp;']; + if (this.pp) this.buf.push("var __indent = [];"); + this.lastBufferedIdx = -1; + this.visit(this.node); + return this.buf.join('\n'); + }, + + /** + * Sets the default doctype `name`. Sets terse mode to `true` when + * html 5 is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {string} name + * @api public + */ + + setDoctype: function(name){ + name = (name && name.toLowerCase()) || 'default'; + this.doctype = doctypes[name] || ''; + this.terse = this.doctype.toLowerCase() == ''; + this.xml = 0 == this.doctype.indexOf(' 1 && !escape && block.nodes[0].isText && block.nodes[1].isText) + this.prettyIndent(1, true); + + for (var i = 0; i < len; ++i) { + // Pretty print text + if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText) + this.prettyIndent(1, false); + + this.visit(block.nodes[i]); + // Multiple text nodes are separated by newlines + if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText) + this.buffer('\\n'); + } + }, + + /** + * Visit `doctype`. Sets terse mode to `true` when html 5 + * is used, causing self-closing tags to end with ">" vs "/>", + * and boolean attributes are not mirrored. + * + * @param {Doctype} doctype + * @api public + */ + + visitDoctype: function(doctype){ + if (doctype && (doctype.val || !this.doctype)) { + this.setDoctype(doctype.val || 'default'); + } + + if (this.doctype) this.buffer(this.doctype); + this.hasCompiledDoctype = true; + }, + + /** + * Visit `mixin`, generating a function that + * may be called within the template. + * + * @param {Mixin} mixin + * @api public + */ + + visitMixin: function(mixin){ + var name = mixin.name.replace(/-/g, '_') + '_mixin' + , args = mixin.args || '' + , block = mixin.block + , attrs = mixin.attrs + , pp = this.pp; + + if (mixin.call) { + if (pp) this.buf.push("__indent.push('" + Array(this.indents + 1).join(' ') + "');") + if (block || attrs.length) { + + this.buf.push(name + '.call({'); + + if (block) { + this.buf.push('block: function(){'); + + // Render block with no indents, dynamically added when rendered + this.parentIndents++; + var _indents = this.indents; + this.indents = 0; + this.visit(mixin.block); + this.indents = _indents; + this.parentIndents--; + + if (attrs.length) { + this.buf.push('},'); + } else { + this.buf.push('}'); + } + } + + if (attrs.length) { + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push('attributes: merge({' + val.buf + + '}, attributes), escaped: merge(' + val.escaped + ', escaped, true)'); + } else { + this.buf.push('attributes: {' + val.buf + '}, escaped: ' + val.escaped); + } + } + + if (args) { + this.buf.push('}, ' + args + ');'); + } else { + this.buf.push('});'); + } + + } else { + this.buf.push(name + '(' + args + ');'); + } + if (pp) this.buf.push("__indent.pop();") + } else { + this.buf.push('var ' + name + ' = function(' + args + '){'); + this.buf.push('var block = this.block, attributes = this.attributes || {}, escaped = this.escaped || {};'); + this.parentIndents++; + this.visit(block); + this.parentIndents--; + this.buf.push('};'); + } + }, + + /** + * Visit `tag` buffering tag markup, generating + * attributes, visiting the `tag`'s code and block. + * + * @param {Tag} tag + * @api public + */ + + visitTag: function(tag){ + this.indents++; + var name = tag.name + , pp = this.pp; + + if (tag.buffer) name = "' + (" + name + ") + '"; + + if (!this.hasCompiledTag) { + if (!this.hasCompiledDoctype && 'html' == name) { + this.visitDoctype(); + } + this.hasCompiledTag = true; + } + + // pretty print + if (pp && !tag.isInline()) + this.prettyIndent(0, true); + + if ((~selfClosing.indexOf(name) || tag.selfClosing) && !this.xml) { + this.buffer('<' + name); + this.visitAttributes(tag.attrs); + this.terse + ? this.buffer('>') + : this.buffer('/>'); + } else { + // Optimize attributes buffering + if (tag.attrs.length) { + this.buffer('<' + name); + if (tag.attrs.length) this.visitAttributes(tag.attrs); + this.buffer('>'); + } else { + this.buffer('<' + name + '>'); + } + if (tag.code) this.visitCode(tag.code); + this.escape = 'pre' == tag.name; + this.visit(tag.block); + + // pretty print + if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline()) + this.prettyIndent(0, true); + + this.buffer(''); + } + this.indents--; + }, + + /** + * Visit `filter`, throwing when the filter does not exist. + * + * @param {Filter} filter + * @api public + */ + + visitFilter: function(filter){ + var fn = filters[filter.name]; + + // unknown filter + if (!fn) throw new Error('unknown filter ":' + filter.name + '"'); + + var text = filter.block.nodes.map( + function(node){ return node.val; } + ).join('\n'); + filter.attrs = filter.attrs || {}; + filter.attrs.filename = this.options.filename; + this.buffer(utils.text(fn(text, filter.attrs))); + }, + + /** + * Visit `text` node. + * + * @param {Text} text + * @api public + */ + + visitText: function(text){ + text = utils.text(text.val.replace(/\\/g, '_SLASH_')); + if (this.escape) text = escape(text); + text = text.replace(/_SLASH_/g, '\\\\'); + this.buffer(text); + }, + + /** + * Visit a `comment`, only buffering when the buffer flag is set. + * + * @param {Comment} comment + * @api public + */ + + visitComment: function(comment){ + if (!comment.buffer) return; + if (this.pp) this.prettyIndent(1, true); + this.buffer(''); + }, + + /** + * Visit a `BlockComment`. + * + * @param {Comment} comment + * @api public + */ + + visitBlockComment: function(comment){ + if (!comment.buffer) return; + if (0 == comment.val.trim().indexOf('if')) { + this.buffer(''); + } else { + this.buffer(''); + } + }, + + /** + * Visit `code`, respecting buffer / escape flags. + * If the code is followed by a block, wrap it in + * a self-calling function. + * + * @param {Code} code + * @api public + */ + + visitCode: function(code){ + // Wrap code blocks with {}. + // we only wrap unbuffered code blocks ATM + // since they are usually flow control + + // Buffer code + if (code.buffer) { + var val = code.val.trimLeft(); + this.buf.push('var __val__ = ' + val); + val = 'null == __val__ ? "" : __val__'; + if (code.escape) val = 'escape(' + val + ')'; + this.buf.push("buf.push(" + val + ");"); + } else { + this.buf.push(code.val); + } + + // Block support + if (code.block) { + if (!code.buffer) this.buf.push('{'); + this.visit(code.block); + if (!code.buffer) this.buf.push('}'); + } + }, + + /** + * Visit `each` block. + * + * @param {Each} each + * @api public + */ + + visitEach: function(each){ + this.buf.push('' + + '// iterate ' + each.obj + '\n' + + ';(function(){\n' + + ' if (\'number\' == typeof ' + each.obj + '.length) {\n'); + + if (each.alternative) { + this.buf.push(' if (' + each.obj + '.length) {'); + } + + this.buf.push('' + + ' for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n' + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + this.buf.push(' }\n'); + + if (each.alternative) { + this.buf.push(' } else {'); + this.visit(each.alternative); + this.buf.push(' }'); + } + + this.buf.push('' + + ' } else {\n' + + ' var $$l = 0;\n' + + ' for (var ' + each.key + ' in ' + each.obj + ') {\n' + + ' $$l++;' + // if browser + // + ' if (' + each.obj + '.hasOwnProperty(' + each.key + ')){' + // end + + ' var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n'); + + this.visit(each.block); + + // if browser + // this.buf.push(' }\n'); + // end + + this.buf.push(' }\n'); + if (each.alternative) { + this.buf.push(' if ($$l === 0) {'); + this.visit(each.alternative); + this.buf.push(' }'); + } + this.buf.push(' }\n}).call(this);\n'); + }, + + /** + * Visit `attrs`. + * + * @param {Array} attrs + * @api public + */ + + visitAttributes: function(attrs){ + var val = this.attrs(attrs); + if (val.inherits) { + this.buf.push("buf.push(attrs(merge({ " + val.buf + + " }, attributes), merge(" + val.escaped + ", escaped, true)));"); + } else if (val.constant) { + eval('var buf={' + val.buf + '};'); + this.buffer(runtime.attrs(buf, JSON.parse(val.escaped)), true); + } else { + this.buf.push("buf.push(attrs({ " + val.buf + " }, " + val.escaped + "));"); + } + }, + + /** + * Compile attributes. + */ + + attrs: function(attrs){ + var buf = [] + , classes = [] + , escaped = {} + , constant = attrs.every(function(attr){ return isConstant(attr.val) }) + , inherits = false; + + if (this.terse) buf.push('terse: true'); + + attrs.forEach(function(attr){ + if (attr.name == 'attributes') return inherits = true; + escaped[attr.name] = attr.escaped; + if (attr.name == 'class') { + classes.push('(' + attr.val + ')'); + } else { + var pair = "'" + attr.name + "':(" + attr.val + ')'; + buf.push(pair); + } + }); + + if (classes.length) { + classes = classes.join(" + ' ' + "); + buf.push('"class": ' + classes); + } + + return { + buf: buf.join(', '), + escaped: JSON.stringify(escaped), + inherits: inherits, + constant: constant + }; + } +}; + +/** + * Check if expression can be evaluated to a constant + * + * @param {String} expression + * @return {Boolean} + * @api private + */ + +function isConstant(val){ + // Check strings/literals + if (/^ *("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|true|false|null|undefined) *$/i.test(val)) + return true; + + // Check numbers + if (!isNaN(Number(val))) + return true; + + // Check arrays + var matches; + if (matches = /^ *\[(.*)\] *$/.exec(val)) + return matches[1].split(',').every(isConstant); + + return false; +} + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +function escape(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} diff --git a/test/node_modules/jade/lib/doctypes.js b/test/node_modules/jade/lib/doctypes.js new file mode 100644 index 0000000..e87ca1e --- /dev/null +++ b/test/node_modules/jade/lib/doctypes.js @@ -0,0 +1,18 @@ + +/*! + * Jade - doctypes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = { + '5': '' + , 'default': '' + , 'xml': '' + , 'transitional': '' + , 'strict': '' + , 'frameset': '' + , '1.1': '' + , 'basic': '' + , 'mobile': '' +}; \ No newline at end of file diff --git a/test/node_modules/jade/lib/filters.js b/test/node_modules/jade/lib/filters.js new file mode 100644 index 0000000..d633559 --- /dev/null +++ b/test/node_modules/jade/lib/filters.js @@ -0,0 +1,105 @@ + +/*! + * Jade - filters + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Wrap text with CDATA block. + */ + +exports.cdata = function(str){ + return ''; +}; + +/** + * Wrap text in script tags. + */ + +exports.js = function(str){ + return ''; +}; + +/** + * Wrap text in style tags. + */ + +exports.css = function(str){ + return ''; +}; + +/** + * Transform stylus to css, wrapped in style tags. + */ + +exports.stylus = function(str, options){ + var ret; + str = str.replace(/\\n/g, '\n'); + var stylus = require('stylus'); + stylus(str, options).render(function(err, css){ + if (err) throw err; + ret = css.replace(/\n/g, '\\n'); + }); + return ''; +}; + +/** + * Transform less to css, wrapped in style tags. + */ + +exports.less = function(str){ + var ret; + str = str.replace(/\\n/g, '\n'); + require('less').render(str, function(err, css){ + if (err) throw err; + ret = ''; + }); + return ret; +}; + +/** + * Transform markdown to html. + */ + +exports.markdown = function(str){ + var md; + + // support markdown / discount + try { + md = require('markdown'); + } catch (err){ + try { + md = require('discount'); + } catch (err) { + try { + md = require('markdown-js'); + } catch (err) { + try { + md = require('marked'); + } catch (err) { + throw new + Error('Cannot find markdown library, install markdown, discount, or marked.'); + } + } + } + } + + str = str.replace(/\\n/g, '\n'); + return md.parse(str).replace(/\n/g, '\\n').replace(/'/g,'''); +}; + +/** + * Transform coffeescript to javascript. + */ + +exports.coffeescript = function(str){ + var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); + return ''; +}; + +// aliases + +exports.md = exports.markdown; +exports.styl = exports.stylus; +exports.coffee = exports.coffeescript; diff --git a/test/node_modules/jade/lib/inline-tags.js b/test/node_modules/jade/lib/inline-tags.js new file mode 100644 index 0000000..491de0b --- /dev/null +++ b/test/node_modules/jade/lib/inline-tags.js @@ -0,0 +1,28 @@ + +/*! + * Jade - inline tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'a' + , 'abbr' + , 'acronym' + , 'b' + , 'br' + , 'code' + , 'em' + , 'font' + , 'i' + , 'img' + , 'ins' + , 'kbd' + , 'map' + , 'samp' + , 'small' + , 'span' + , 'strong' + , 'sub' + , 'sup' +]; \ No newline at end of file diff --git a/test/node_modules/jade/lib/jade.js b/test/node_modules/jade/lib/jade.js new file mode 100644 index 0000000..09d61e6 --- /dev/null +++ b/test/node_modules/jade/lib/jade.js @@ -0,0 +1,253 @@ +/*! + * Jade + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Parser = require('./parser') + , Lexer = require('./lexer') + , Compiler = require('./compiler') + , runtime = require('./runtime') +// if node + , fs = require('fs'); +// end + +/** + * Library version. + */ + +exports.version = '0.28.0'; + +/** + * Expose self closing tags. + */ + +exports.selfClosing = require('./self-closing'); + +/** + * Default supported doctypes. + */ + +exports.doctypes = require('./doctypes'); + +/** + * Text filters. + */ + +exports.filters = require('./filters'); + +/** + * Utilities. + */ + +exports.utils = require('./utils'); + +/** + * Expose `Compiler`. + */ + +exports.Compiler = Compiler; + +/** + * Expose `Parser`. + */ + +exports.Parser = Parser; + +/** + * Expose `Lexer`. + */ + +exports.Lexer = Lexer; + +/** + * Nodes. + */ + +exports.nodes = require('./nodes'); + +/** + * Jade runtime helpers. + */ + +exports.runtime = runtime; + +/** + * Template function cache. + */ + +exports.cache = {}; + +/** + * Parse the given `str` of jade and return a function body. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ + +function parse(str, options){ + try { + // Parse + var parser = new Parser(str, options.filename, options); + + // Compile + var compiler = new (options.compiler || Compiler)(parser.parse(), options) + , js = compiler.compile(); + + // Debug compiler + if (options.debug) { + console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' ')); + } + + return '' + + 'var buf = [];\n' + + (options.self + ? 'var self = locals || {};\n' + js + : 'with (locals || {}) {\n' + js + '\n}\n') + + 'return buf.join("");'; + } catch (err) { + parser = parser.context(); + runtime.rethrow(err, parser.filename, parser.lexer.lineno); + } +} + +/** + * Strip any UTF-8 BOM off of the start of `str`, if it exists. + * + * @param {String} str + * @return {String} + * @api private + */ + +function stripBOM(str){ + return 0xFEFF == str.charCodeAt(0) + ? str.substring(1) + : str; +} + +/** + * Compile a `Function` representation of the given jade `str`. + * + * Options: + * + * - `compileDebug` when `false` debugging code is stripped from the compiled template + * - `client` when `true` the helper functions `escape()` etc will reference `jade.escape()` + * for use with the Jade client-side runtime.js + * + * @param {String} str + * @param {Options} options + * @return {Function} + * @api public + */ + +exports.compile = function(str, options){ + var options = options || {} + , client = options.client + , filename = options.filename + ? JSON.stringify(options.filename) + : 'undefined' + , fn; + + str = stripBOM(String(str)); + + if (options.compileDebug !== false) { + fn = [ + 'var __jade = [{ lineno: 1, filename: ' + filename + ' }];' + , 'try {' + , parse(str, options) + , '} catch (err) {' + , ' rethrow(err, __jade[0].filename, __jade[0].lineno);' + , '}' + ].join('\n'); + } else { + fn = parse(str, options); + } + + if (client) { + fn = 'attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;\n' + fn; + } + + fn = new Function('locals, attrs, escape, rethrow, merge', fn); + + if (client) return fn; + + return function(locals){ + return fn(locals, runtime.attrs, runtime.escape, runtime.rethrow, runtime.merge); + }; +}; + +/** + * Render the given `str` of jade and invoke + * the callback `fn(err, str)`. + * + * Options: + * + * - `cache` enable template caching + * - `filename` filename required for `include` / `extends` and caching + * + * @param {String} str + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +exports.render = function(str, options, fn){ + // swap args + if ('function' == typeof options) { + fn = options, options = {}; + } + + // cache requires .filename + if (options.cache && !options.filename) { + return fn(new Error('the "filename" option is required for caching')); + } + + try { + var path = options.filename; + var tmpl = options.cache + ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) + : exports.compile(str, options); + fn(null, tmpl(options)); + } catch (err) { + fn(err); + } +}; + +/** + * Render a Jade file at the given `path` and callback `fn(err, str)`. + * + * @param {String} path + * @param {Object|Function} options or callback + * @param {Function} fn + * @api public + */ + +exports.renderFile = function(path, options, fn){ + var key = path + ':string'; + + if ('function' == typeof options) { + fn = options, options = {}; + } + + try { + options.filename = path; + var str = options.cache + ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) + : fs.readFileSync(path, 'utf8'); + exports.render(str, options, fn); + } catch (err) { + fn(err); + } +}; + +/** + * Express support. + */ + +exports.__express = exports.renderFile; diff --git a/test/node_modules/jade/lib/lexer.js b/test/node_modules/jade/lib/lexer.js new file mode 100644 index 0000000..1e0bfff --- /dev/null +++ b/test/node_modules/jade/lib/lexer.js @@ -0,0 +1,775 @@ +/*! + * Jade - Lexer + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +var utils = require('./utils'); + +/** + * Initialize `Lexer` with the given `str`. + * + * Options: + * + * - `colons` allow colons for attr delimiters + * + * @param {String} str + * @param {Object} options + * @api private + */ + +var Lexer = module.exports = function Lexer(str, options) { + options = options || {}; + this.input = str.replace(/\r\n|\r/g, '\n'); + this.colons = options.colons; + this.deferredTokens = []; + this.lastIndents = 0; + this.lineno = 1; + this.stash = []; + this.indentStack = []; + this.indentRe = null; + this.pipeless = false; +}; + +/** + * Lexer prototype. + */ + +Lexer.prototype = { + + /** + * Construct a token with the given `type` and `val`. + * + * @param {String} type + * @param {String} val + * @return {Object} + * @api private + */ + + tok: function(type, val){ + return { + type: type + , line: this.lineno + , val: val + } + }, + + /** + * Consume the given `len` of input. + * + * @param {Number} len + * @api private + */ + + consume: function(len){ + this.input = this.input.substr(len); + }, + + /** + * Scan for `type` with the given `regexp`. + * + * @param {String} type + * @param {RegExp} regexp + * @return {Object} + * @api private + */ + + scan: function(regexp, type){ + var captures; + if (captures = regexp.exec(this.input)) { + this.consume(captures[0].length); + return this.tok(type, captures[1]); + } + }, + + /** + * Defer the given `tok`. + * + * @param {Object} tok + * @api private + */ + + defer: function(tok){ + this.deferredTokens.push(tok); + }, + + /** + * Lookahead `n` tokens. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + var fetch = n - this.stash.length; + while (fetch-- > 0) this.stash.push(this.next()); + return this.stash[--n]; + }, + + /** + * Return the indexOf `start` / `end` delimiters. + * + * @param {String} start + * @param {String} end + * @return {Number} + * @api private + */ + + indexOfDelimiters: function(start, end){ + var str = this.input + , nstart = 0 + , nend = 0 + , pos = 0; + for (var i = 0, len = str.length; i < len; ++i) { + if (start == str.charAt(i)) { + ++nstart; + } else if (end == str.charAt(i)) { + if (++nend == nstart) { + pos = i; + break; + } + } + } + return pos; + }, + + /** + * Stashed token. + */ + + stashed: function() { + return this.stash.length + && this.stash.shift(); + }, + + /** + * Deferred token. + */ + + deferred: function() { + return this.deferredTokens.length + && this.deferredTokens.shift(); + }, + + /** + * end-of-source. + */ + + eos: function() { + if (this.input.length) return; + if (this.indentStack.length) { + this.indentStack.shift(); + return this.tok('outdent'); + } else { + return this.tok('eos'); + } + }, + + /** + * Blank line. + */ + + blank: function() { + var captures; + if (captures = /^\n *\n/.exec(this.input)) { + this.consume(captures[0].length - 1); + ++this.lineno; + if (this.pipeless) return this.tok('text', ''); + return this.next(); + } + }, + + /** + * Comment. + */ + + comment: function() { + var captures; + if (captures = /^ *\/\/(-)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('comment', captures[2]); + tok.buffer = '-' != captures[1]; + return tok; + } + }, + + /** + * Interpolated tag. + */ + + interpolation: function() { + var captures; + if (captures = /^#\{(.*?)\}/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('interpolation', captures[1]); + } + }, + + /** + * Tag. + */ + + tag: function() { + var captures; + if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) { + this.consume(captures[0].length); + var tok, name = captures[1]; + if (':' == name[name.length - 1]) { + name = name.slice(0, -1); + tok = this.tok('tag', name); + this.defer(this.tok(':')); + while (' ' == this.input[0]) this.input = this.input.substr(1); + } else { + tok = this.tok('tag', name); + } + tok.selfClosing = !! captures[2]; + return tok; + } + }, + + /** + * Filter. + */ + + filter: function() { + return this.scan(/^:(\w+)/, 'filter'); + }, + + /** + * Doctype. + */ + + doctype: function() { + return this.scan(/^(?:!!!|doctype) *([^\n]+)?/, 'doctype'); + }, + + /** + * Id. + */ + + id: function() { + return this.scan(/^#([\w-]+)/, 'id'); + }, + + /** + * Class. + */ + + className: function() { + return this.scan(/^\.([\w-]+)/, 'class'); + }, + + /** + * Text. + */ + + text: function() { + return this.scan(/^(?:\| ?| ?)?([^\n]+)/, 'text'); + }, + + /** + * Extends. + */ + + "extends": function() { + return this.scan(/^extends? +([^\n]+)/, 'extends'); + }, + + /** + * Block prepend. + */ + + prepend: function() { + var captures; + if (captures = /^prepend +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'prepend' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block append. + */ + + append: function() { + var captures; + if (captures = /^append +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = 'append' + , name = captures[1] + , tok = this.tok('block', name); + tok.mode = mode; + return tok; + } + }, + + /** + * Block. + */ + + block: function() { + var captures; + if (captures = /^block\b *(?:(prepend|append) +)?([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var mode = captures[1] || 'replace' + , name = captures[2] + , tok = this.tok('block', name); + + tok.mode = mode; + return tok; + } + }, + + /** + * Yield. + */ + + yield: function() { + return this.scan(/^yield */, 'yield'); + }, + + /** + * Include. + */ + + include: function() { + return this.scan(/^include +([^\n]+)/, 'include'); + }, + + /** + * Case. + */ + + "case": function() { + return this.scan(/^case +([^\n]+)/, 'case'); + }, + + /** + * When. + */ + + when: function() { + return this.scan(/^when +([^:\n]+)/, 'when'); + }, + + /** + * Default. + */ + + "default": function() { + return this.scan(/^default */, 'default'); + }, + + /** + * Assignment. + */ + + assignment: function() { + var captures; + if (captures = /^(\w+) += *([^;\n]+)( *;? *)/.exec(this.input)) { + this.consume(captures[0].length); + var name = captures[1] + , val = captures[2]; + return this.tok('code', 'var ' + name + ' = (' + val + ');'); + } + }, + + /** + * Call mixin. + */ + + call: function(){ + var captures; + if (captures = /^\+([-\w]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('call', captures[1]); + + // Check for args (not attributes) + if (captures = /^ *\((.*?)\)/.exec(this.input)) { + if (!/^ *[-\w]+ *=/.test(captures[1])) { + this.consume(captures[0].length); + tok.args = captures[1]; + } + } + + return tok; + } + }, + + /** + * Mixin. + */ + + mixin: function(){ + var captures; + if (captures = /^mixin +([-\w]+)(?: *\((.*)\))?/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('mixin', captures[1]); + tok.args = captures[2]; + return tok; + } + }, + + /** + * Conditional. + */ + + conditional: function() { + var captures; + if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) { + this.consume(captures[0].length); + var type = captures[1] + , js = captures[2]; + + switch (type) { + case 'if': js = 'if (' + js + ')'; break; + case 'unless': js = 'if (!(' + js + '))'; break; + case 'else if': js = 'else if (' + js + ')'; break; + case 'else': js = 'else'; break; + } + + return this.tok('code', js); + } + }, + + /** + * While. + */ + + "while": function() { + var captures; + if (captures = /^while +([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + return this.tok('code', 'while (' + captures[1] + ')'); + } + }, + + /** + * Each. + */ + + each: function() { + var captures; + if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in *([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var tok = this.tok('each', captures[1]); + tok.key = captures[2] || '$index'; + tok.code = captures[3]; + return tok; + } + }, + + /** + * Code. + */ + + code: function() { + var captures; + if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) { + this.consume(captures[0].length); + var flags = captures[1]; + captures[1] = captures[2]; + var tok = this.tok('code', captures[1]); + tok.escape = flags.charAt(0) === '='; + tok.buffer = flags.charAt(0) === '=' || flags.charAt(1) === '='; + return tok; + } + }, + + /** + * Attributes. + */ + + attrs: function() { + if ('(' == this.input.charAt(0)) { + var index = this.indexOfDelimiters('(', ')') + , str = this.input.substr(1, index-1) + , tok = this.tok('attrs') + , len = str.length + , colons = this.colons + , states = ['key'] + , escapedAttr + , key = '' + , val = '' + , quote + , c + , p; + + function state(){ + return states[states.length - 1]; + } + + function interpolate(attr) { + return attr.replace(/(\\)?#\{([^}]+)\}/g, function(_, escape, expr){ + return escape + ? _ + : quote + " + (" + expr + ") + " + quote; + }); + } + + this.consume(index + 1); + tok.attrs = {}; + tok.escaped = {}; + + function parse(c) { + var real = c; + // TODO: remove when people fix ":" + if (colons && ':' == c) c = '='; + switch (c) { + case ',': + case '\n': + switch (state()) { + case 'expr': + case 'array': + case 'string': + case 'object': + val += c; + break; + default: + states.push('key'); + val = val.trim(); + key = key.trim(); + if ('' == key) return; + key = key.replace(/^['"]|['"]$/g, '').replace('!', ''); + tok.escaped[key] = escapedAttr; + tok.attrs[key] = '' == val + ? true + : interpolate(val); + key = val = ''; + } + break; + case '=': + switch (state()) { + case 'key char': + key += real; + break; + case 'val': + case 'expr': + case 'array': + case 'string': + case 'object': + val += real; + break; + default: + escapedAttr = '!' != p; + states.push('val'); + } + break; + case '(': + if ('val' == state() + || 'expr' == state()) states.push('expr'); + val += c; + break; + case ')': + if ('expr' == state() + || 'val' == state()) states.pop(); + val += c; + break; + case '{': + if ('val' == state()) states.push('object'); + val += c; + break; + case '}': + if ('object' == state()) states.pop(); + val += c; + break; + case '[': + if ('val' == state()) states.push('array'); + val += c; + break; + case ']': + if ('array' == state()) states.pop(); + val += c; + break; + case '"': + case "'": + switch (state()) { + case 'key': + states.push('key char'); + break; + case 'key char': + states.pop(); + break; + case 'string': + if (c == quote) states.pop(); + val += c; + break; + default: + states.push('string'); + val += c; + quote = c; + } + break; + case '': + break; + default: + switch (state()) { + case 'key': + case 'key char': + key += c; + break; + default: + val += c; + } + } + p = c; + } + + for (var i = 0; i < len; ++i) { + parse(str.charAt(i)); + } + + parse(','); + + if ('/' == this.input.charAt(0)) { + this.consume(1); + tok.selfClosing = true; + } + + return tok; + } + }, + + /** + * Indent | Outdent | Newline. + */ + + indent: function() { + var captures, re; + + // established regexp + if (this.indentRe) { + captures = this.indentRe.exec(this.input); + // determine regexp + } else { + // tabs + re = /^\n(\t*) */; + captures = re.exec(this.input); + + // spaces + if (captures && !captures[1].length) { + re = /^\n( *)/; + captures = re.exec(this.input); + } + + // established + if (captures && captures[1].length) this.indentRe = re; + } + + if (captures) { + var tok + , indents = captures[1].length; + + ++this.lineno; + this.consume(indents + 1); + + if (' ' == this.input[0] || '\t' == this.input[0]) { + throw new Error('Invalid indentation, you can use tabs or spaces but not both'); + } + + // blank line + if ('\n' == this.input[0]) return this.tok('newline'); + + // outdent + if (this.indentStack.length && indents < this.indentStack[0]) { + while (this.indentStack.length && this.indentStack[0] > indents) { + this.stash.push(this.tok('outdent')); + this.indentStack.shift(); + } + tok = this.stash.pop(); + // indent + } else if (indents && indents != this.indentStack[0]) { + this.indentStack.unshift(indents); + tok = this.tok('indent', indents); + // newline + } else { + tok = this.tok('newline'); + } + + return tok; + } + }, + + /** + * Pipe-less text consumed only when + * pipeless is true; + */ + + pipelessText: function() { + if (this.pipeless) { + if ('\n' == this.input[0]) return; + var i = this.input.indexOf('\n'); + if (-1 == i) i = this.input.length; + var str = this.input.substr(0, i); + this.consume(str.length); + return this.tok('text', str); + } + }, + + /** + * ':' + */ + + colon: function() { + return this.scan(/^: */, ':'); + }, + + /** + * Return the next token object, or those + * previously stashed by lookahead. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.stashed() + || this.next(); + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + next: function() { + return this.deferred() + || this.blank() + || this.eos() + || this.pipelessText() + || this.yield() + || this.doctype() + || this.interpolation() + || this["case"]() + || this.when() + || this["default"]() + || this["extends"]() + || this.append() + || this.prepend() + || this.block() + || this.include() + || this.mixin() + || this.call() + || this.conditional() + || this.each() + || this["while"]() + || this.assignment() + || this.tag() + || this.filter() + || this.code() + || this.id() + || this.className() + || this.attrs() + || this.indent() + || this.comment() + || this.colon() + || this.text(); + } +}; diff --git a/test/node_modules/jade/lib/nodes/attrs.js b/test/node_modules/jade/lib/nodes/attrs.js new file mode 100644 index 0000000..5de9b59 --- /dev/null +++ b/test/node_modules/jade/lib/nodes/attrs.js @@ -0,0 +1,77 @@ + +/*! + * Jade - nodes - Attrs + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'), + Block = require('./block'); + +/** + * Initialize a `Attrs` node. + * + * @api public + */ + +var Attrs = module.exports = function Attrs() { + this.attrs = []; +}; + +/** + * Inherit from `Node`. + */ + +Attrs.prototype.__proto__ = Node.prototype; + +/** + * Set attribute `name` to `val`, keep in mind these become + * part of a raw js object literal, so to quote a value you must + * '"quote me"', otherwise or example 'user.name' is literal JavaScript. + * + * @param {String} name + * @param {String} val + * @param {Boolean} escaped + * @return {Tag} for chaining + * @api public + */ + +Attrs.prototype.setAttribute = function(name, val, escaped){ + this.attrs.push({ name: name, val: val, escaped: escaped }); + return this; +}; + +/** + * Remove attribute `name` when present. + * + * @param {String} name + * @api public + */ + +Attrs.prototype.removeAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + delete this.attrs[i]; + } + } +}; + +/** + * Get attribute value by `name`. + * + * @param {String} name + * @return {String} + * @api public + */ + +Attrs.prototype.getAttribute = function(name){ + for (var i = 0, len = this.attrs.length; i < len; ++i) { + if (this.attrs[i] && this.attrs[i].name == name) { + return this.attrs[i].val; + } + } +}; diff --git a/test/node_modules/jade/lib/nodes/block-comment.js b/test/node_modules/jade/lib/nodes/block-comment.js new file mode 100644 index 0000000..4f41e4a --- /dev/null +++ b/test/node_modules/jade/lib/nodes/block-comment.js @@ -0,0 +1,33 @@ + +/*! + * Jade - nodes - BlockComment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `BlockComment` with the given `block`. + * + * @param {String} val + * @param {Block} block + * @param {Boolean} buffer + * @api public + */ + +var BlockComment = module.exports = function BlockComment(val, block, buffer) { + this.block = block; + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +BlockComment.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/test/node_modules/jade/lib/nodes/block.js b/test/node_modules/jade/lib/nodes/block.js new file mode 100644 index 0000000..6bb18c9 --- /dev/null +++ b/test/node_modules/jade/lib/nodes/block.js @@ -0,0 +1,122 @@ + +/*! + * Jade - nodes - Block + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Block` with an optional `node`. + * + * @param {Node} node + * @api public + */ + +var Block = module.exports = function Block(node){ + this.nodes = []; + if (node) this.push(node); +}; + +/** + * Inherit from `Node`. + */ + +Block.prototype.__proto__ = Node.prototype; + +/** + * Block flag. + */ + +Block.prototype.isBlock = true; + +/** + * Replace the nodes in `other` with the nodes + * in `this` block. + * + * @param {Block} other + * @api private + */ + +Block.prototype.replace = function(other){ + other.nodes = this.nodes; +}; + +/** + * Pust the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.push = function(node){ + return this.nodes.push(node); +}; + +/** + * Check if this block is empty. + * + * @return {Boolean} + * @api public + */ + +Block.prototype.isEmpty = function(){ + return 0 == this.nodes.length; +}; + +/** + * Unshift the given `node`. + * + * @param {Node} node + * @return {Number} + * @api public + */ + +Block.prototype.unshift = function(node){ + return this.nodes.unshift(node); +}; + +/** + * Return the "last" block, or the first `yield` node. + * + * @return {Block} + * @api private + */ + +Block.prototype.includeBlock = function(){ + var ret = this + , node; + + for (var i = 0, len = this.nodes.length; i < len; ++i) { + node = this.nodes[i]; + if (node.yield) return node; + else if (node.textOnly) continue; + else if (node.includeBlock) ret = node.includeBlock(); + else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock(); + if (ret.yield) return ret; + } + + return ret; +}; + +/** + * Return a clone of this block. + * + * @return {Block} + * @api private + */ + +Block.prototype.clone = function(){ + var clone = new Block; + for (var i = 0, len = this.nodes.length; i < len; ++i) { + clone.push(this.nodes[i].clone()); + } + return clone; +}; + diff --git a/test/node_modules/jade/lib/nodes/case.js b/test/node_modules/jade/lib/nodes/case.js new file mode 100644 index 0000000..08ff033 --- /dev/null +++ b/test/node_modules/jade/lib/nodes/case.js @@ -0,0 +1,43 @@ + +/*! + * Jade - nodes - Case + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a new `Case` with `expr`. + * + * @param {String} expr + * @api public + */ + +var Case = exports = module.exports = function Case(expr, block){ + this.expr = expr; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Case.prototype.__proto__ = Node.prototype; + +var When = exports.When = function When(expr, block){ + this.expr = expr; + this.block = block; + this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +When.prototype.__proto__ = Node.prototype; + diff --git a/test/node_modules/jade/lib/nodes/code.js b/test/node_modules/jade/lib/nodes/code.js new file mode 100644 index 0000000..babc675 --- /dev/null +++ b/test/node_modules/jade/lib/nodes/code.js @@ -0,0 +1,35 @@ + +/*! + * Jade - nodes - Code + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Code` node with the given code `val`. + * Code may also be optionally buffered and escaped. + * + * @param {String} val + * @param {Boolean} buffer + * @param {Boolean} escape + * @api public + */ + +var Code = module.exports = function Code(val, buffer, escape) { + this.val = val; + this.buffer = buffer; + this.escape = escape; + if (val.match(/^ *else/)) this.debug = false; +}; + +/** + * Inherit from `Node`. + */ + +Code.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/test/node_modules/jade/lib/nodes/comment.js b/test/node_modules/jade/lib/nodes/comment.js new file mode 100644 index 0000000..2e1469e --- /dev/null +++ b/test/node_modules/jade/lib/nodes/comment.js @@ -0,0 +1,32 @@ + +/*! + * Jade - nodes - Comment + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Comment` with the given `val`, optionally `buffer`, + * otherwise the comment may render in the output. + * + * @param {String} val + * @param {Boolean} buffer + * @api public + */ + +var Comment = module.exports = function Comment(val, buffer) { + this.val = val; + this.buffer = buffer; +}; + +/** + * Inherit from `Node`. + */ + +Comment.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/test/node_modules/jade/lib/nodes/doctype.js b/test/node_modules/jade/lib/nodes/doctype.js new file mode 100644 index 0000000..b8f33e5 --- /dev/null +++ b/test/node_modules/jade/lib/nodes/doctype.js @@ -0,0 +1,29 @@ + +/*! + * Jade - nodes - Doctype + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Doctype` with the given `val`. + * + * @param {String} val + * @api public + */ + +var Doctype = module.exports = function Doctype(val) { + this.val = val; +}; + +/** + * Inherit from `Node`. + */ + +Doctype.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/test/node_modules/jade/lib/nodes/each.js b/test/node_modules/jade/lib/nodes/each.js new file mode 100644 index 0000000..f54101f --- /dev/null +++ b/test/node_modules/jade/lib/nodes/each.js @@ -0,0 +1,35 @@ + +/*! + * Jade - nodes - Each + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize an `Each` node, representing iteration + * + * @param {String} obj + * @param {String} val + * @param {String} key + * @param {Block} block + * @api public + */ + +var Each = module.exports = function Each(obj, val, key, block) { + this.obj = obj; + this.val = val; + this.key = key; + this.block = block; +}; + +/** + * Inherit from `Node`. + */ + +Each.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/test/node_modules/jade/lib/nodes/filter.js b/test/node_modules/jade/lib/nodes/filter.js new file mode 100644 index 0000000..0d7ff6e --- /dev/null +++ b/test/node_modules/jade/lib/nodes/filter.js @@ -0,0 +1,34 @@ + +/*! + * Jade - nodes - Filter + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node') + , Block = require('./block'); + +/** + * Initialize a `Filter` node with the given + * filter `name` and `block`. + * + * @param {String} name + * @param {Block|Node} block + * @api public + */ + +var Filter = module.exports = function Filter(name, block, attrs) { + this.name = name; + this.block = block; + this.attrs = attrs; +}; + +/** + * Inherit from `Node`. + */ + +Filter.prototype.__proto__ = Node.prototype; \ No newline at end of file diff --git a/test/node_modules/jade/lib/nodes/index.js b/test/node_modules/jade/lib/nodes/index.js new file mode 100644 index 0000000..386ad2f --- /dev/null +++ b/test/node_modules/jade/lib/nodes/index.js @@ -0,0 +1,20 @@ + +/*! + * Jade - nodes + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +exports.Node = require('./node'); +exports.Tag = require('./tag'); +exports.Code = require('./code'); +exports.Each = require('./each'); +exports.Case = require('./case'); +exports.Text = require('./text'); +exports.Block = require('./block'); +exports.Mixin = require('./mixin'); +exports.Filter = require('./filter'); +exports.Comment = require('./comment'); +exports.Literal = require('./literal'); +exports.BlockComment = require('./block-comment'); +exports.Doctype = require('./doctype'); diff --git a/test/node_modules/jade/lib/nodes/literal.js b/test/node_modules/jade/lib/nodes/literal.js new file mode 100644 index 0000000..fde586b --- /dev/null +++ b/test/node_modules/jade/lib/nodes/literal.js @@ -0,0 +1,32 @@ + +/*! + * Jade - nodes - Literal + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Literal` node with the given `str. + * + * @param {String} str + * @api public + */ + +var Literal = module.exports = function Literal(str) { + this.str = str + .replace(/\\/g, "\\\\") + .replace(/\n|\r\n/g, "\\n") + .replace(/'/g, "\\'"); +}; + +/** + * Inherit from `Node`. + */ + +Literal.prototype.__proto__ = Node.prototype; diff --git a/test/node_modules/jade/lib/nodes/mixin.js b/test/node_modules/jade/lib/nodes/mixin.js new file mode 100644 index 0000000..8407bc7 --- /dev/null +++ b/test/node_modules/jade/lib/nodes/mixin.js @@ -0,0 +1,36 @@ + +/*! + * Jade - nodes - Mixin + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'); + +/** + * Initialize a new `Mixin` with `name` and `block`. + * + * @param {String} name + * @param {String} args + * @param {Block} block + * @api public + */ + +var Mixin = module.exports = function Mixin(name, args, block, call){ + this.name = name; + this.args = args; + this.block = block; + this.attrs = []; + this.call = call; +}; + +/** + * Inherit from `Attrs`. + */ + +Mixin.prototype.__proto__ = Attrs.prototype; + diff --git a/test/node_modules/jade/lib/nodes/node.js b/test/node_modules/jade/lib/nodes/node.js new file mode 100644 index 0000000..e98f042 --- /dev/null +++ b/test/node_modules/jade/lib/nodes/node.js @@ -0,0 +1,25 @@ + +/*! + * Jade - nodes - Node + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Initialize a `Node`. + * + * @api public + */ + +var Node = module.exports = function Node(){}; + +/** + * Clone this node (return itself) + * + * @return {Node} + * @api private + */ + +Node.prototype.clone = function(){ + return this; +}; diff --git a/test/node_modules/jade/lib/nodes/tag.js b/test/node_modules/jade/lib/nodes/tag.js new file mode 100644 index 0000000..4b6728a --- /dev/null +++ b/test/node_modules/jade/lib/nodes/tag.js @@ -0,0 +1,95 @@ + +/*! + * Jade - nodes - Tag + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Attrs = require('./attrs'), + Block = require('./block'), + inlineTags = require('../inline-tags'); + +/** + * Initialize a `Tag` node with the given tag `name` and optional `block`. + * + * @param {String} name + * @param {Block} block + * @api public + */ + +var Tag = module.exports = function Tag(name, block) { + this.name = name; + this.attrs = []; + this.block = block || new Block; +}; + +/** + * Inherit from `Attrs`. + */ + +Tag.prototype.__proto__ = Attrs.prototype; + +/** + * Clone this tag. + * + * @return {Tag} + * @api private + */ + +Tag.prototype.clone = function(){ + var clone = new Tag(this.name, this.block.clone()); + clone.line = this.line; + clone.attrs = this.attrs; + clone.textOnly = this.textOnly; + return clone; +}; + +/** + * Check if this tag is an inline tag. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.isInline = function(){ + return ~inlineTags.indexOf(this.name); +}; + +/** + * Check if this tag's contents can be inlined. Used for pretty printing. + * + * @return {Boolean} + * @api private + */ + +Tag.prototype.canInline = function(){ + var nodes = this.block.nodes; + + function isInline(node){ + // Recurse if the node is a block + if (node.isBlock) return node.nodes.every(isInline); + return node.isText || (node.isInline && node.isInline()); + } + + // Empty tag + if (!nodes.length) return true; + + // Text-only or inline-only tag + if (1 == nodes.length) return isInline(nodes[0]); + + // Multi-line inline-only tag + if (this.block.nodes.every(isInline)) { + for (var i = 1, len = nodes.length; i < len; ++i) { + if (nodes[i-1].isText && nodes[i].isText) + return false; + } + return true; + } + + // Mixed tag + return false; +}; \ No newline at end of file diff --git a/test/node_modules/jade/lib/nodes/text.js b/test/node_modules/jade/lib/nodes/text.js new file mode 100644 index 0000000..3b5dd55 --- /dev/null +++ b/test/node_modules/jade/lib/nodes/text.js @@ -0,0 +1,36 @@ + +/*! + * Jade - nodes - Text + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Node = require('./node'); + +/** + * Initialize a `Text` node with optional `line`. + * + * @param {String} line + * @api public + */ + +var Text = module.exports = function Text(line) { + this.val = ''; + if ('string' == typeof line) this.val = line; +}; + +/** + * Inherit from `Node`. + */ + +Text.prototype.__proto__ = Node.prototype; + +/** + * Flag as text. + */ + +Text.prototype.isText = true; \ No newline at end of file diff --git a/test/node_modules/jade/lib/parser.js b/test/node_modules/jade/lib/parser.js new file mode 100644 index 0000000..5039c90 --- /dev/null +++ b/test/node_modules/jade/lib/parser.js @@ -0,0 +1,699 @@ + +/*! + * Jade - Parser + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Lexer = require('./lexer') + , nodes = require('./nodes') + , utils = require('./utils') + , filters = require('./filters') + , path = require('path') + , extname = path.extname; + +/** + * Initialize `Parser` with the given input `str` and `filename`. + * + * @param {String} str + * @param {String} filename + * @param {Object} options + * @api public + */ + +var Parser = exports = module.exports = function Parser(str, filename, options){ + this.input = str; + this.lexer = new Lexer(str, options); + this.filename = filename; + this.blocks = {}; + this.mixins = {}; + this.options = options; + this.contexts = [this]; +}; + +/** + * Tags that may not contain tags. + */ + +var textOnly = exports.textOnly = ['script', 'style']; + +/** + * Parser prototype. + */ + +Parser.prototype = { + + /** + * Push `parser` onto the context stack, + * or pop and return a `Parser`. + */ + + context: function(parser){ + if (parser) { + this.contexts.push(parser); + } else { + return this.contexts.pop(); + } + }, + + /** + * Return the next token object. + * + * @return {Object} + * @api private + */ + + advance: function(){ + return this.lexer.advance(); + }, + + /** + * Skip `n` tokens. + * + * @param {Number} n + * @api private + */ + + skip: function(n){ + while (n--) this.advance(); + }, + + /** + * Single token lookahead. + * + * @return {Object} + * @api private + */ + + peek: function() { + return this.lookahead(1); + }, + + /** + * Return lexer lineno. + * + * @return {Number} + * @api private + */ + + line: function() { + return this.lexer.lineno; + }, + + /** + * `n` token lookahead. + * + * @param {Number} n + * @return {Object} + * @api private + */ + + lookahead: function(n){ + return this.lexer.lookahead(n); + }, + + /** + * Parse input returning a string of js for evaluation. + * + * @return {String} + * @api public + */ + + parse: function(){ + var block = new nodes.Block, parser; + block.line = this.line(); + + while ('eos' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + + if (parser = this.extending) { + this.context(parser); + var ast = parser.parse(); + this.context(); + // hoist mixins + for (var name in this.mixins) + ast.unshift(this.mixins[name]); + return ast; + } + + return block; + }, + + /** + * Expect the given type, or throw an exception. + * + * @param {String} type + * @api private + */ + + expect: function(type){ + if (this.peek().type === type) { + return this.advance(); + } else { + throw new Error('expected "' + type + '", but got "' + this.peek().type + '"'); + } + }, + + /** + * Accept the given `type`. + * + * @param {String} type + * @api private + */ + + accept: function(type){ + if (this.peek().type === type) { + return this.advance(); + } + }, + + /** + * tag + * | doctype + * | mixin + * | include + * | filter + * | comment + * | text + * | each + * | code + * | yield + * | id + * | class + * | interpolation + */ + + parseExpr: function(){ + switch (this.peek().type) { + case 'tag': + return this.parseTag(); + case 'mixin': + return this.parseMixin(); + case 'block': + return this.parseBlock(); + case 'case': + return this.parseCase(); + case 'when': + return this.parseWhen(); + case 'default': + return this.parseDefault(); + case 'extends': + return this.parseExtends(); + case 'include': + return this.parseInclude(); + case 'doctype': + return this.parseDoctype(); + case 'filter': + return this.parseFilter(); + case 'comment': + return this.parseComment(); + case 'text': + return this.parseText(); + case 'each': + return this.parseEach(); + case 'code': + return this.parseCode(); + case 'call': + return this.parseCall(); + case 'interpolation': + return this.parseInterpolation(); + case 'yield': + this.advance(); + var block = new nodes.Block; + block.yield = true; + return block; + case 'id': + case 'class': + var tok = this.advance(); + this.lexer.defer(this.lexer.tok('tag', 'div')); + this.lexer.defer(tok); + return this.parseExpr(); + default: + throw new Error('unexpected token "' + this.peek().type + '"'); + } + }, + + /** + * Text + */ + + parseText: function(){ + var tok = this.expect('text'); + var node = new nodes.Text(tok.val); + node.line = this.line(); + return node; + }, + + /** + * ':' expr + * | block + */ + + parseBlockExpansion: function(){ + if (':' == this.peek().type) { + this.advance(); + return new nodes.Block(this.parseExpr()); + } else { + return this.block(); + } + }, + + /** + * case + */ + + parseCase: function(){ + var val = this.expect('case').val; + var node = new nodes.Case(val); + node.line = this.line(); + node.block = this.block(); + return node; + }, + + /** + * when + */ + + parseWhen: function(){ + var val = this.expect('when').val + return new nodes.Case.When(val, this.parseBlockExpansion()); + }, + + /** + * default + */ + + parseDefault: function(){ + this.expect('default'); + return new nodes.Case.When('default', this.parseBlockExpansion()); + }, + + /** + * code + */ + + parseCode: function(){ + var tok = this.expect('code'); + var node = new nodes.Code(tok.val, tok.buffer, tok.escape); + var block; + var i = 1; + node.line = this.line(); + while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i; + block = 'indent' == this.lookahead(i).type; + if (block) { + this.skip(i-1); + node.block = this.block(); + } + return node; + }, + + /** + * comment + */ + + parseComment: function(){ + var tok = this.expect('comment'); + var node; + + if ('indent' == this.peek().type) { + node = new nodes.BlockComment(tok.val, this.block(), tok.buffer); + } else { + node = new nodes.Comment(tok.val, tok.buffer); + } + + node.line = this.line(); + return node; + }, + + /** + * doctype + */ + + parseDoctype: function(){ + var tok = this.expect('doctype'); + var node = new nodes.Doctype(tok.val); + node.line = this.line(); + return node; + }, + + /** + * filter attrs? text-block + */ + + parseFilter: function(){ + var tok = this.expect('filter'); + var attrs = this.accept('attrs'); + var block; + + this.lexer.pipeless = true; + block = this.parseTextBlock(); + this.lexer.pipeless = false; + + var node = new nodes.Filter(tok.val, block, attrs && attrs.attrs); + node.line = this.line(); + return node; + }, + + /** + * each block + */ + + parseEach: function(){ + var tok = this.expect('each'); + var node = new nodes.Each(tok.code, tok.val, tok.key); + node.line = this.line(); + node.block = this.block(); + if (this.peek().type == 'code' && this.peek().val == 'else') { + this.advance(); + node.alternative = this.block(); + } + return node; + }, + + /** + * 'extends' name + */ + + parseExtends: function(){ + var path = require('path'); + var fs = require('fs'); + var dirname = path.dirname; + var basename = path.basename; + var join = path.join; + + if (!this.filename) + throw new Error('the "filename" option is required to extend templates'); + + var path = this.expect('extends').val.trim(); + var dir = dirname(this.filename); + + var path = join(dir, path + '.jade'); + var str = fs.readFileSync(path, 'utf8'); + var parser = new Parser(str, path, this.options); + + parser.blocks = this.blocks; + parser.contexts = this.contexts; + this.extending = parser; + + // TODO: null node + return new nodes.Literal(''); + }, + + /** + * 'block' name block + */ + + parseBlock: function(){ + var block = this.expect('block'); + var mode = block.mode; + var name = block.val.trim(); + + block = 'indent' == this.peek().type + ? this.block() + : new nodes.Block(new nodes.Literal('')); + + var prev = this.blocks[name]; + + if (prev) { + switch (prev.mode) { + case 'append': + block.nodes = block.nodes.concat(prev.nodes); + prev = block; + break; + case 'prepend': + block.nodes = prev.nodes.concat(block.nodes); + prev = block; + break; + } + } + + block.mode = mode; + return this.blocks[name] = prev || block; + }, + + /** + * include block? + */ + + parseInclude: function(){ + var path = require('path'); + var fs = require('fs'); + var dirname = path.dirname; + var basename = path.basename; + var join = path.join; + + var path = this.expect('include').val.trim(); + var dir = dirname(this.filename); + + if (!this.filename) + throw new Error('the "filename" option is required to use includes'); + + // no extension + if (!~basename(path).indexOf('.')) { + path += '.jade'; + } + + // non-jade + if ('.jade' != path.substr(-5)) { + var path = join(dir, path); + var str = fs.readFileSync(path, 'utf8'); + var ext = extname(path).slice(1); + var filter = filters[ext]; + if (filter) str = filter(str, { filename: path }).replace(/\\n/g, '\n'); + return new nodes.Literal(str); + } + + var path = join(dir, path); + var str = fs.readFileSync(path, 'utf8'); + var parser = new Parser(str, path, this.options); + parser.blocks = utils.merge({}, this.blocks); + parser.mixins = this.mixins; + + this.context(parser); + var ast = parser.parse(); + this.context(); + ast.filename = path; + + if ('indent' == this.peek().type) { + ast.includeBlock().push(this.block()); + } + + return ast; + }, + + /** + * call ident block + */ + + parseCall: function(){ + var tok = this.expect('call'); + var name = tok.val; + var args = tok.args; + var mixin = new nodes.Mixin(name, args, new nodes.Block, true); + + this.tag(mixin); + if (mixin.block.isEmpty()) mixin.block = null; + return mixin; + }, + + /** + * mixin block + */ + + parseMixin: function(){ + var tok = this.expect('mixin'); + var name = tok.val; + var args = tok.args; + var mixin; + + // definition + if ('indent' == this.peek().type) { + mixin = new nodes.Mixin(name, args, this.block(), false); + this.mixins[name] = mixin; + return mixin; + // call + } else { + return new nodes.Mixin(name, args, null, true); + } + }, + + /** + * indent (text | newline)* outdent + */ + + parseTextBlock: function(){ + var block = new nodes.Block; + block.line = this.line(); + var spaces = this.expect('indent').val; + if (null == this._spaces) this._spaces = spaces; + var indent = Array(spaces - this._spaces + 1).join(' '); + while ('outdent' != this.peek().type) { + switch (this.peek().type) { + case 'newline': + this.advance(); + break; + case 'indent': + this.parseTextBlock().nodes.forEach(function(node){ + block.push(node); + }); + break; + default: + var text = new nodes.Text(indent + this.advance().val); + text.line = this.line(); + block.push(text); + } + } + + if (spaces == this._spaces) this._spaces = null; + this.expect('outdent'); + return block; + }, + + /** + * indent expr* outdent + */ + + block: function(){ + var block = new nodes.Block; + block.line = this.line(); + this.expect('indent'); + while ('outdent' != this.peek().type) { + if ('newline' == this.peek().type) { + this.advance(); + } else { + block.push(this.parseExpr()); + } + } + this.expect('outdent'); + return block; + }, + + /** + * interpolation (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseInterpolation: function(){ + var tok = this.advance(); + var tag = new nodes.Tag(tok.val); + tag.buffer = true; + return this.tag(tag); + }, + + /** + * tag (attrs | class | id)* (text | code | ':')? newline* block? + */ + + parseTag: function(){ + // ast-filter look-ahead + var i = 2; + if ('attrs' == this.lookahead(i).type) ++i; + + var tok = this.advance(); + var tag = new nodes.Tag(tok.val); + + tag.selfClosing = tok.selfClosing; + + return this.tag(tag); + }, + + /** + * Parse tag. + */ + + tag: function(tag){ + var dot; + + tag.line = this.line(); + + // (attrs | class | id)* + out: + while (true) { + switch (this.peek().type) { + case 'id': + case 'class': + var tok = this.advance(); + tag.setAttribute(tok.type, "'" + tok.val + "'"); + continue; + case 'attrs': + var tok = this.advance() + , obj = tok.attrs + , escaped = tok.escaped + , names = Object.keys(obj); + + if (tok.selfClosing) tag.selfClosing = true; + + for (var i = 0, len = names.length; i < len; ++i) { + var name = names[i] + , val = obj[name]; + tag.setAttribute(name, val, escaped[name]); + } + continue; + default: + break out; + } + } + + // check immediate '.' + if ('.' == this.peek().val) { + dot = tag.textOnly = true; + this.advance(); + } + + // (text | code | ':')? + switch (this.peek().type) { + case 'text': + tag.block.push(this.parseText()); + break; + case 'code': + tag.code = this.parseCode(); + break; + case ':': + this.advance(); + tag.block = new nodes.Block; + tag.block.push(this.parseExpr()); + break; + } + + // newline* + while ('newline' == this.peek().type) this.advance(); + + tag.textOnly = tag.textOnly || ~textOnly.indexOf(tag.name); + + // script special-case + if ('script' == tag.name) { + var type = tag.getAttribute('type'); + if (!dot && type && 'text/javascript' != type.replace(/^['"]|['"]$/g, '')) { + tag.textOnly = false; + } + } + + // block? + if ('indent' == this.peek().type) { + if (tag.textOnly) { + this.lexer.pipeless = true; + tag.block = this.parseTextBlock(); + this.lexer.pipeless = false; + } else { + var block = this.block(); + if (tag.block) { + for (var i = 0, len = block.nodes.length; i < len; ++i) { + tag.block.push(block.nodes[i]); + } + } else { + tag.block = block; + } + } + } + + return tag; + } +}; diff --git a/test/node_modules/jade/lib/runtime.js b/test/node_modules/jade/lib/runtime.js new file mode 100644 index 0000000..fb711f5 --- /dev/null +++ b/test/node_modules/jade/lib/runtime.js @@ -0,0 +1,174 @@ + +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; diff --git a/test/node_modules/jade/lib/self-closing.js b/test/node_modules/jade/lib/self-closing.js new file mode 100644 index 0000000..0548771 --- /dev/null +++ b/test/node_modules/jade/lib/self-closing.js @@ -0,0 +1,19 @@ + +/*! + * Jade - self closing tags + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +module.exports = [ + 'meta' + , 'img' + , 'link' + , 'input' + , 'source' + , 'area' + , 'base' + , 'col' + , 'br' + , 'hr' +]; \ No newline at end of file diff --git a/test/node_modules/jade/lib/utils.js b/test/node_modules/jade/lib/utils.js new file mode 100644 index 0000000..ca4a7fa --- /dev/null +++ b/test/node_modules/jade/lib/utils.js @@ -0,0 +1,68 @@ + +/*! + * Jade - utils + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Convert interpolation in the given string to JavaScript. + * + * @param {String} str + * @return {String} + * @api private + */ + +var interpolate = exports.interpolate = function(str){ + return str.replace(/(_SLASH_)?([#!]){(.*?)}/g, function(str, escape, flag, code){ + code = code + .replace(/\\'/g, "'") + .replace(/_SLASH_/g, '\\'); + + return escape + ? str.slice(7) + : "' + " + + ('!' == flag ? '' : 'escape') + + "((interp = " + code + + ") == null ? '' : interp) + '"; + }); +}; + +/** + * Escape single quotes in `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +var escape = exports.escape = function(str) { + return str.replace(/'/g, "\\'"); +}; + +/** + * Interpolate, and escape the given `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.text = function(str){ + return interpolate(escape(str)); +}; + +/** + * Merge `b` into `a`. + * + * @param {Object} a + * @param {Object} b + * @return {Object} + * @api public + */ + +exports.merge = function(a, b) { + for (var key in b) a[key] = b[key]; + return a; +}; + diff --git a/test/node_modules/jade/node_modules/.bin/cake b/test/node_modules/jade/node_modules/.bin/cake new file mode 100644 index 0000000..da84bdc --- /dev/null +++ b/test/node_modules/jade/node_modules/.bin/cake @@ -0,0 +1,15 @@ +#!/bin/sh +basedir=`dirname "$0"` + +case `uname` in + *CYGWIN*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + "$basedir/node" "$basedir/../coffee-script/bin/cake" "$@" + ret=$? +else + node "$basedir/../coffee-script/bin/cake" "$@" + ret=$? +fi +exit $ret diff --git a/test/node_modules/jade/node_modules/.bin/cake.cmd b/test/node_modules/jade/node_modules/.bin/cake.cmd new file mode 100644 index 0000000..269da0a --- /dev/null +++ b/test/node_modules/jade/node_modules/.bin/cake.cmd @@ -0,0 +1,6 @@ +:: Created by npm, please don't edit manually. +@IF EXIST "%~dp0\node.exe" ( + "%~dp0\node.exe" "%~dp0\..\coffee-script\bin\cake" %* +) ELSE ( + node "%~dp0\..\coffee-script\bin\cake" %* +) \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/.bin/coffee b/test/node_modules/jade/node_modules/.bin/coffee new file mode 100644 index 0000000..04d88bf --- /dev/null +++ b/test/node_modules/jade/node_modules/.bin/coffee @@ -0,0 +1,15 @@ +#!/bin/sh +basedir=`dirname "$0"` + +case `uname` in + *CYGWIN*) basedir=`cygpath -w "$basedir"`;; +esac + +if [ -x "$basedir/node" ]; then + "$basedir/node" "$basedir/../coffee-script/bin/coffee" "$@" + ret=$? +else + node "$basedir/../coffee-script/bin/coffee" "$@" + ret=$? +fi +exit $ret diff --git a/test/node_modules/jade/node_modules/.bin/coffee.cmd b/test/node_modules/jade/node_modules/.bin/coffee.cmd new file mode 100644 index 0000000..6a162b9 --- /dev/null +++ b/test/node_modules/jade/node_modules/.bin/coffee.cmd @@ -0,0 +1,6 @@ +:: Created by npm, please don't edit manually. +@IF EXIST "%~dp0\node.exe" ( + "%~dp0\node.exe" "%~dp0\..\coffee-script\bin\coffee" %* +) ELSE ( + node "%~dp0\..\coffee-script\bin\coffee" %* +) \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/coffee-script/.npmignore b/test/node_modules/jade/node_modules/coffee-script/.npmignore new file mode 100644 index 0000000..21e430d --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/.npmignore @@ -0,0 +1,11 @@ +*.coffee +*.html +.DS_Store +.git* +Cakefile +documentation/ +examples/ +extras/coffee-script.js +raw/ +src/ +test/ diff --git a/test/node_modules/jade/node_modules/coffee-script/CNAME b/test/node_modules/jade/node_modules/coffee-script/CNAME new file mode 100644 index 0000000..faadabe --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/CNAME @@ -0,0 +1 @@ +coffeescript.org \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/coffee-script/CONTRIBUTING.md b/test/node_modules/jade/node_modules/coffee-script/CONTRIBUTING.md new file mode 100644 index 0000000..6390c68 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/CONTRIBUTING.md @@ -0,0 +1,9 @@ +## How to contribute to CoffeeScript + +* Before you open a ticket or send a pull request, [search](https://github.com/jashkenas/coffee-script/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one. + +* Before sending a pull request for a feature, be sure to have [tests](https://github.com/jashkenas/coffee-script/tree/master/test). + +* Use the same coding style as the rest of the [codebase](https://github.com/jashkenas/coffee-script/tree/master/src). If you're just getting started with CoffeeScript, there's a nice [style guide](https://github.com/polarmobile/coffeescript-style-guide). + +* In your pull request, do not add documentation to `index.html` or re-build the minified `coffee-script.js` file. We'll do those things before cutting a new release. \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/coffee-script/LICENSE b/test/node_modules/jade/node_modules/coffee-script/LICENSE new file mode 100644 index 0000000..dbe6b4e --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2009-2012 Jeremy Ashkenas + +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. \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/coffee-script/README b/test/node_modules/jade/node_modules/coffee-script/README new file mode 100644 index 0000000..69ee6f4 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/README @@ -0,0 +1,51 @@ + + { + } } { + { { } } + } }{ { + { }{ } } _____ __ __ + ( }{ }{ { ) / ____| / _|/ _| + .- { { } { }} -. | | ___ | |_| |_ ___ ___ + ( ( } { } { } } ) | | / _ \| _| _/ _ \/ _ \ + |`-..________ ..-'| | |___| (_) | | | || __/ __/ + | | \_____\___/|_| |_| \___|\___| + | ;--. + | (__ \ _____ _ _ + | | ) ) / ____| (_) | | + | |/ / | (___ ___ _ __ _ _ __ | |_ + | ( / \___ \ / __| '__| | '_ \| __| + | |/ ____) | (__| | | | |_) | |_ + | | |_____/ \___|_| |_| .__/ \__| + `-.._________..-' | | + |_| + + + CoffeeScript is a little language that compiles into JavaScript. + + Install Node.js, and then the CoffeeScript compiler: + sudo bin/cake install + + Or, if you have the Node Package Manager installed: + npm install -g coffee-script + (Leave off the -g if you don't wish to install globally.) + + Execute a script: + coffee /path/to/script.coffee + + Compile a script: + coffee -c /path/to/script.coffee + + For documentation, usage, and examples, see: + http://coffeescript.org/ + + To suggest a feature, report a bug, or general discussion: + http://github.com/jashkenas/coffee-script/issues/ + + If you'd like to chat, drop by #coffeescript on Freenode IRC, + or on webchat.freenode.net. + + The source repository: + git://github.com/jashkenas/coffee-script.git + + All contributors are listed here: + http://github.com/jashkenas/coffee-script/contributors diff --git a/test/node_modules/jade/node_modules/coffee-script/Rakefile b/test/node_modules/jade/node_modules/coffee-script/Rakefile new file mode 100644 index 0000000..dfb85da --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/Rakefile @@ -0,0 +1,78 @@ +require 'rubygems' +require 'erb' +require 'fileutils' +require 'rake/testtask' +require 'json' + +desc "Build the documentation page" +task :doc do + source = 'documentation/index.html.erb' + child = fork { exec "bin/coffee -bcw -o documentation/js documentation/coffee/*.coffee" } + at_exit { Process.kill("INT", child) } + Signal.trap("INT") { exit } + loop do + mtime = File.stat(source).mtime + if !@mtime || mtime > @mtime + rendered = ERB.new(File.read(source)).result(binding) + File.open('index.html', 'w+') {|f| f.write(rendered) } + end + @mtime = mtime + sleep 1 + end +end + +desc "Build coffee-script-source gem" +task :gem do + require 'rubygems' + require 'rubygems/package' + + gemspec = Gem::Specification.new do |s| + s.name = 'coffee-script-source' + s.version = JSON.parse(File.read('package.json'))["version"] + s.date = Time.now.strftime("%Y-%m-%d") + + s.homepage = "http://jashkenas.github.com/coffee-script/" + s.summary = "The CoffeeScript Compiler" + s.description = <<-EOS + CoffeeScript is a little language that compiles into JavaScript. + Underneath all of those embarrassing braces and semicolons, + JavaScript has always had a gorgeous object model at its heart. + CoffeeScript is an attempt to expose the good parts of JavaScript + in a simple way. + EOS + + s.files = [ + 'lib/coffee_script/coffee-script.js', + 'lib/coffee_script/source.rb' + ] + + s.authors = ['Jeremy Ashkenas'] + s.email = 'jashkenas@gmail.com' + s.rubyforge_project = 'coffee-script-source' + end + + file = File.open("coffee-script-source.gem", "w") + Gem::Package.open(file, 'w') do |pkg| + pkg.metadata = gemspec.to_yaml + + path = "lib/coffee_script/source.rb" + contents = <<-ERUBY +module CoffeeScript + module Source + def self.bundled_path + File.expand_path("../coffee-script.js", __FILE__) + end + end +end + ERUBY + pkg.add_file_simple(path, 0644, contents.size) do |tar_io| + tar_io.write(contents) + end + + contents = File.read("extras/coffee-script.js") + path = "lib/coffee_script/coffee-script.js" + pkg.add_file_simple(path, 0644, contents.size) do |tar_io| + tar_io.write(contents) + end + end +end diff --git a/test/node_modules/jade/node_modules/coffee-script/bin/cake b/test/node_modules/jade/node_modules/coffee-script/bin/cake new file mode 100644 index 0000000..5965f4e --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/bin/cake @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +var path = require('path'); +var fs = require('fs'); +var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); + +require(lib + '/coffee-script/cake').run(); diff --git a/test/node_modules/jade/node_modules/coffee-script/bin/coffee b/test/node_modules/jade/node_modules/coffee-script/bin/coffee new file mode 100644 index 0000000..3d1d71c --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/bin/coffee @@ -0,0 +1,7 @@ +#!/usr/bin/env node + +var path = require('path'); +var fs = require('fs'); +var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); + +require(lib + '/coffee-script/command').run(); diff --git a/test/node_modules/jade/node_modules/coffee-script/extras/jsl.conf b/test/node_modules/jade/node_modules/coffee-script/extras/jsl.conf new file mode 100644 index 0000000..1190da5 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/extras/jsl.conf @@ -0,0 +1,44 @@ +# JavaScriptLint configuration file for CoffeeScript. + ++no_return_value # function {0} does not always return a value ++duplicate_formal # duplicate formal argument {0} +-equal_as_assign # test for equality (==) mistyped as assignment (=)?{0} ++var_hides_arg # variable {0} hides argument ++redeclared_var # redeclaration of {0} {1} +-anon_no_return_value # anonymous function does not always return a value ++missing_semicolon # missing semicolon ++meaningless_block # meaningless block; curly braces have no impact +-comma_separated_stmts # multiple statements separated by commas (use semicolons?) ++unreachable_code # unreachable code ++missing_break # missing break statement +-missing_break_for_last_case # missing break statement for last case in switch +-comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) +-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement +-useless_void # use of the void type may be unnecessary (void is always undefined) ++multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs ++use_of_label # use of label +-block_without_braces # block statement without curly braces ++leading_decimal_point # leading decimal point may indicate a number or an object member ++trailing_decimal_point # trailing decimal point may indicate a number or an object member ++octal_number # leading zeros make an octal number ++nested_comment # nested comment ++misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma ++ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement ++empty_statement # empty statement or extra semicolon +-missing_option_explicit # the "option explicit" control comment is missing ++partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag ++dup_option_explicit # duplicate "option explicit" control comment ++useless_assign # useless assignment ++ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity ++ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent) +-missing_default_case # missing default case in switch statement ++duplicate_case_in_switch # duplicate case in switch statements ++default_not_at_end # the default case is not at the end of the switch statement ++legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax ++jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax ++useless_comparison # useless comparison; comparing identical expressions ++with_statement # with statement hides undeclared variables; use temporary variable instead ++trailing_comma_in_array # extra comma is not recommended in array initializers ++assign_to_function_call # assignment to a function call ++parseint_missing_radix # parseInt missing radix parameter ++lambda_assign_requires_semicolon diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/browser.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/browser.js new file mode 100644 index 0000000..045156f --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/browser.js @@ -0,0 +1,92 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var CoffeeScript, runScripts; + + CoffeeScript = require('./coffee-script'); + + CoffeeScript.require = require; + + CoffeeScript["eval"] = function(code, options) { + var _ref; + if (options == null) { + options = {}; + } + if ((_ref = options.bare) == null) { + options.bare = true; + } + return eval(CoffeeScript.compile(code, options)); + }; + + CoffeeScript.run = function(code, options) { + if (options == null) { + options = {}; + } + options.bare = true; + return Function(CoffeeScript.compile(code, options))(); + }; + + if (typeof window === "undefined" || window === null) { + return; + } + + CoffeeScript.load = function(url, callback) { + var xhr; + xhr = window.ActiveXObject ? new window.ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest(); + xhr.open('GET', url, true); + if ('overrideMimeType' in xhr) { + xhr.overrideMimeType('text/plain'); + } + xhr.onreadystatechange = function() { + var _ref; + if (xhr.readyState === 4) { + if ((_ref = xhr.status) === 0 || _ref === 200) { + CoffeeScript.run(xhr.responseText); + } else { + throw new Error("Could not load " + url); + } + if (callback) { + return callback(); + } + } + }; + return xhr.send(null); + }; + + runScripts = function() { + var coffees, execute, index, length, s, scripts; + scripts = document.getElementsByTagName('script'); + coffees = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = scripts.length; _i < _len; _i++) { + s = scripts[_i]; + if (s.type === 'text/coffeescript') { + _results.push(s); + } + } + return _results; + })(); + index = 0; + length = coffees.length; + (execute = function() { + var script; + script = coffees[index++]; + if ((script != null ? script.type : void 0) === 'text/coffeescript') { + if (script.src) { + return CoffeeScript.load(script.src, execute); + } else { + CoffeeScript.run(script.innerHTML); + return execute(); + } + } + })(); + return null; + }; + + if (window.addEventListener) { + addEventListener('DOMContentLoaded', runScripts, false); + } else { + attachEvent('onload', runScripts); + } + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/cake.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/cake.js new file mode 100644 index 0000000..0e7f1cc --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/cake.js @@ -0,0 +1,113 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var CoffeeScript, cakefileDirectory, existsSync, fatalError, fs, helpers, missingTask, oparse, options, optparse, path, printTasks, switches, tasks; + + fs = require('fs'); + + path = require('path'); + + helpers = require('./helpers'); + + optparse = require('./optparse'); + + CoffeeScript = require('./coffee-script'); + + existsSync = fs.existsSync || path.existsSync; + + tasks = {}; + + options = {}; + + switches = []; + + oparse = null; + + helpers.extend(global, { + task: function(name, description, action) { + var _ref; + if (!action) { + _ref = [description, action], action = _ref[0], description = _ref[1]; + } + return tasks[name] = { + name: name, + description: description, + action: action + }; + }, + option: function(letter, flag, description) { + return switches.push([letter, flag, description]); + }, + invoke: function(name) { + if (!tasks[name]) { + missingTask(name); + } + return tasks[name].action(options); + } + }); + + exports.run = function() { + var arg, args, _i, _len, _ref, _results; + global.__originalDirname = fs.realpathSync('.'); + process.chdir(cakefileDirectory(__originalDirname)); + args = process.argv.slice(2); + CoffeeScript.run(fs.readFileSync('Cakefile').toString(), { + filename: 'Cakefile' + }); + oparse = new optparse.OptionParser(switches); + if (!args.length) { + return printTasks(); + } + try { + options = oparse.parse(args); + } catch (e) { + return fatalError("" + e); + } + _ref = options["arguments"]; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + arg = _ref[_i]; + _results.push(invoke(arg)); + } + return _results; + }; + + printTasks = function() { + var cakefilePath, desc, name, relative, spaces, task; + relative = path.relative || path.resolve; + cakefilePath = path.join(relative(__originalDirname, process.cwd()), 'Cakefile'); + console.log("" + cakefilePath + " defines the following tasks:\n"); + for (name in tasks) { + task = tasks[name]; + spaces = 20 - name.length; + spaces = spaces > 0 ? Array(spaces + 1).join(' ') : ''; + desc = task.description ? "# " + task.description : ''; + console.log("cake " + name + spaces + " " + desc); + } + if (switches.length) { + return console.log(oparse.help()); + } + }; + + fatalError = function(message) { + console.error(message + '\n'); + console.log('To see a list of all tasks/options, run "cake"'); + return process.exit(1); + }; + + missingTask = function(task) { + return fatalError("No such task: " + task); + }; + + cakefileDirectory = function(dir) { + var parent; + if (existsSync(path.join(dir, 'Cakefile'))) { + return dir; + } + parent = path.normalize(path.join(dir, '..')); + if (parent !== dir) { + return cakefileDirectory(parent); + } + throw new Error("Cakefile not found in " + (process.cwd())); + }; + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/coffee-script.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/coffee-script.js new file mode 100644 index 0000000..7ee55cd --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/coffee-script.js @@ -0,0 +1,171 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Lexer, RESERVED, compile, fs, lexer, parser, path, stripBOM, vm, _ref, + __hasProp = {}.hasOwnProperty; + + fs = require('fs'); + + path = require('path'); + + _ref = require('./lexer'), Lexer = _ref.Lexer, RESERVED = _ref.RESERVED; + + parser = require('./parser').parser; + + vm = require('vm'); + + stripBOM = function(content) { + if (content.charCodeAt(0) === 0xFEFF) { + return content.substring(1); + } else { + return content; + } + }; + + if (require.extensions) { + require.extensions['.coffee'] = function(module, filename) { + var content; + content = compile(stripBOM(fs.readFileSync(filename, 'utf8')), { + filename: filename + }); + return module._compile(content, filename); + }; + } + + exports.VERSION = '1.4.0'; + + exports.RESERVED = RESERVED; + + exports.helpers = require('./helpers'); + + exports.compile = compile = function(code, options) { + var header, js, merge; + if (options == null) { + options = {}; + } + merge = exports.helpers.merge; + try { + js = (parser.parse(lexer.tokenize(code))).compile(options); + if (!options.header) { + return js; + } + } catch (err) { + if (options.filename) { + err.message = "In " + options.filename + ", " + err.message; + } + throw err; + } + header = "Generated by CoffeeScript " + this.VERSION; + return "// " + header + "\n" + js; + }; + + exports.tokens = function(code, options) { + return lexer.tokenize(code, options); + }; + + exports.nodes = function(source, options) { + if (typeof source === 'string') { + return parser.parse(lexer.tokenize(source, options)); + } else { + return parser.parse(source); + } + }; + + exports.run = function(code, options) { + var mainModule; + if (options == null) { + options = {}; + } + mainModule = require.main; + mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : '.'; + mainModule.moduleCache && (mainModule.moduleCache = {}); + mainModule.paths = require('module')._nodeModulePaths(path.dirname(fs.realpathSync(options.filename))); + if (path.extname(mainModule.filename) !== '.coffee' || require.extensions) { + return mainModule._compile(compile(code, options), mainModule.filename); + } else { + return mainModule._compile(code, mainModule.filename); + } + }; + + exports["eval"] = function(code, options) { + var Module, Script, js, k, o, r, sandbox, v, _i, _len, _module, _ref1, _ref2, _require; + if (options == null) { + options = {}; + } + if (!(code = code.trim())) { + return; + } + Script = vm.Script; + if (Script) { + if (options.sandbox != null) { + if (options.sandbox instanceof Script.createContext().constructor) { + sandbox = options.sandbox; + } else { + sandbox = Script.createContext(); + _ref1 = options.sandbox; + for (k in _ref1) { + if (!__hasProp.call(_ref1, k)) continue; + v = _ref1[k]; + sandbox[k] = v; + } + } + sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox; + } else { + sandbox = global; + } + sandbox.__filename = options.filename || 'eval'; + sandbox.__dirname = path.dirname(sandbox.__filename); + if (!(sandbox !== global || sandbox.module || sandbox.require)) { + Module = require('module'); + sandbox.module = _module = new Module(options.modulename || 'eval'); + sandbox.require = _require = function(path) { + return Module._load(path, _module, true); + }; + _module.filename = sandbox.__filename; + _ref2 = Object.getOwnPropertyNames(require); + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + r = _ref2[_i]; + if (r !== 'paths') { + _require[r] = require[r]; + } + } + _require.paths = _module.paths = Module._nodeModulePaths(process.cwd()); + _require.resolve = function(request) { + return Module._resolveFilename(request, _module); + }; + } + } + o = {}; + for (k in options) { + if (!__hasProp.call(options, k)) continue; + v = options[k]; + o[k] = v; + } + o.bare = true; + js = compile(code, o); + if (sandbox === global) { + return vm.runInThisContext(js); + } else { + return vm.runInContext(js, sandbox); + } + }; + + lexer = new Lexer; + + parser.lexer = { + lex: function() { + var tag, _ref1; + _ref1 = this.tokens[this.pos++] || [''], tag = _ref1[0], this.yytext = _ref1[1], this.yylineno = _ref1[2]; + return tag; + }, + setInput: function(tokens) { + this.tokens = tokens; + return this.pos = 0; + }, + upcomingInput: function() { + return ""; + } + }; + + parser.yy = require('./nodes'); + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/command.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/command.js new file mode 100644 index 0000000..b0bcfe1 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/command.js @@ -0,0 +1,502 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var BANNER, CoffeeScript, EventEmitter, SWITCHES, compileJoin, compileOptions, compilePath, compileScript, compileStdio, exec, exists, forkNode, fs, helpers, hidden, joinTimeout, lint, loadRequires, notSources, optionParser, optparse, opts, outputPath, parseOptions, path, printLine, printTokens, printWarn, removeSource, sourceCode, sources, spawn, timeLog, unwatchDir, usage, version, wait, watch, watchDir, watchers, writeJs, _ref; + + fs = require('fs'); + + path = require('path'); + + helpers = require('./helpers'); + + optparse = require('./optparse'); + + CoffeeScript = require('./coffee-script'); + + _ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec; + + EventEmitter = require('events').EventEmitter; + + exists = fs.exists || path.exists; + + helpers.extend(CoffeeScript, new EventEmitter); + + printLine = function(line) { + return process.stdout.write(line + '\n'); + }; + + printWarn = function(line) { + return process.stderr.write(line + '\n'); + }; + + hidden = function(file) { + return /^\.|~$/.test(file); + }; + + BANNER = 'Usage: coffee [options] path/to/script.coffee -- [args]\n\nIf called without options, `coffee` will run your script.'; + + SWITCHES = [['-b', '--bare', 'compile without a top-level function wrapper'], ['-c', '--compile', 'compile to JavaScript and save as .js files'], ['-e', '--eval', 'pass a string from the command line as input'], ['-h', '--help', 'display this help message'], ['-i', '--interactive', 'run an interactive CoffeeScript REPL'], ['-j', '--join [FILE]', 'concatenate the source CoffeeScript before compiling'], ['-l', '--lint', 'pipe the compiled JavaScript through JavaScript Lint'], ['-n', '--nodes', 'print out the parse tree that the parser produces'], ['--nodejs [ARGS]', 'pass options directly to the "node" binary'], ['-o', '--output [DIR]', 'set the output directory for compiled JavaScript'], ['-p', '--print', 'print out the compiled JavaScript'], ['-r', '--require [FILE*]', 'require a library before executing your script'], ['-s', '--stdio', 'listen for and compile scripts over stdio'], ['-t', '--tokens', 'print out the tokens that the lexer/rewriter produce'], ['-v', '--version', 'display the version number'], ['-w', '--watch', 'watch scripts for changes and rerun commands']]; + + opts = {}; + + sources = []; + + sourceCode = []; + + notSources = {}; + + watchers = {}; + + optionParser = null; + + exports.run = function() { + var literals, source, _i, _len, _results; + parseOptions(); + if (opts.nodejs) { + return forkNode(); + } + if (opts.help) { + return usage(); + } + if (opts.version) { + return version(); + } + if (opts.require) { + loadRequires(); + } + if (opts.interactive) { + return require('./repl'); + } + if (opts.watch && !fs.watch) { + return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + "."); + } + if (opts.stdio) { + return compileStdio(); + } + if (opts["eval"]) { + return compileScript(null, sources[0]); + } + if (!sources.length) { + return require('./repl'); + } + literals = opts.run ? sources.splice(1) : []; + process.argv = process.argv.slice(0, 2).concat(literals); + process.argv[0] = 'coffee'; + process.execPath = require.main.filename; + _results = []; + for (_i = 0, _len = sources.length; _i < _len; _i++) { + source = sources[_i]; + _results.push(compilePath(source, true, path.normalize(source))); + } + return _results; + }; + + compilePath = function(source, topLevel, base) { + return fs.stat(source, function(err, stats) { + if (err && err.code !== 'ENOENT') { + throw err; + } + if ((err != null ? err.code : void 0) === 'ENOENT') { + if (topLevel && source.slice(-7) !== '.coffee') { + source = sources[sources.indexOf(source)] = "" + source + ".coffee"; + return compilePath(source, topLevel, base); + } + if (topLevel) { + console.error("File not found: " + source); + process.exit(1); + } + return; + } + if (stats.isDirectory()) { + if (opts.watch) { + watchDir(source, base); + } + return fs.readdir(source, function(err, files) { + var file, index, _ref1, _ref2; + if (err && err.code !== 'ENOENT') { + throw err; + } + if ((err != null ? err.code : void 0) === 'ENOENT') { + return; + } + index = sources.indexOf(source); + files = files.filter(function(file) { + return !hidden(file); + }); + [].splice.apply(sources, [index, index - index + 1].concat(_ref1 = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + _results.push(path.join(source, file)); + } + return _results; + })())), _ref1; + [].splice.apply(sourceCode, [index, index - index + 1].concat(_ref2 = files.map(function() { + return null; + }))), _ref2; + return files.forEach(function(file) { + return compilePath(path.join(source, file), false, base); + }); + }); + } else if (topLevel || path.extname(source) === '.coffee') { + if (opts.watch) { + watch(source, base); + } + return fs.readFile(source, function(err, code) { + if (err && err.code !== 'ENOENT') { + throw err; + } + if ((err != null ? err.code : void 0) === 'ENOENT') { + return; + } + return compileScript(source, code.toString(), base); + }); + } else { + notSources[source] = true; + return removeSource(source, base); + } + }); + }; + + compileScript = function(file, input, base) { + var o, options, t, task; + o = opts; + options = compileOptions(file); + try { + t = task = { + file: file, + input: input, + options: options + }; + CoffeeScript.emit('compile', task); + if (o.tokens) { + return printTokens(CoffeeScript.tokens(t.input)); + } else if (o.nodes) { + return printLine(CoffeeScript.nodes(t.input).toString().trim()); + } else if (o.run) { + return CoffeeScript.run(t.input, t.options); + } else if (o.join && t.file !== o.join) { + sourceCode[sources.indexOf(t.file)] = t.input; + return compileJoin(); + } else { + t.output = CoffeeScript.compile(t.input, t.options); + CoffeeScript.emit('success', task); + if (o.print) { + return printLine(t.output.trim()); + } else if (o.compile) { + return writeJs(t.file, t.output, base); + } else if (o.lint) { + return lint(t.file, t.output); + } + } + } catch (err) { + CoffeeScript.emit('failure', err, task); + if (CoffeeScript.listeners('failure').length) { + return; + } + if (o.watch) { + return printLine(err.message + '\x07'); + } + printWarn(err instanceof Error && err.stack || ("ERROR: " + err)); + return process.exit(1); + } + }; + + compileStdio = function() { + var code, stdin; + code = ''; + stdin = process.openStdin(); + stdin.on('data', function(buffer) { + if (buffer) { + return code += buffer.toString(); + } + }); + return stdin.on('end', function() { + return compileScript(null, code); + }); + }; + + joinTimeout = null; + + compileJoin = function() { + if (!opts.join) { + return; + } + if (!sourceCode.some(function(code) { + return code === null; + })) { + clearTimeout(joinTimeout); + return joinTimeout = wait(100, function() { + return compileScript(opts.join, sourceCode.join('\n'), opts.join); + }); + } + }; + + loadRequires = function() { + var realFilename, req, _i, _len, _ref1; + realFilename = module.filename; + module.filename = '.'; + _ref1 = opts.require; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + req = _ref1[_i]; + require(req); + } + return module.filename = realFilename; + }; + + watch = function(source, base) { + var compile, compileTimeout, prevStats, rewatch, watchErr, watcher; + prevStats = null; + compileTimeout = null; + watchErr = function(e) { + if (e.code === 'ENOENT') { + if (sources.indexOf(source) === -1) { + return; + } + try { + rewatch(); + return compile(); + } catch (e) { + removeSource(source, base, true); + return compileJoin(); + } + } else { + throw e; + } + }; + compile = function() { + clearTimeout(compileTimeout); + return compileTimeout = wait(25, function() { + return fs.stat(source, function(err, stats) { + if (err) { + return watchErr(err); + } + if (prevStats && stats.size === prevStats.size && stats.mtime.getTime() === prevStats.mtime.getTime()) { + return rewatch(); + } + prevStats = stats; + return fs.readFile(source, function(err, code) { + if (err) { + return watchErr(err); + } + compileScript(source, code.toString(), base); + return rewatch(); + }); + }); + }); + }; + try { + watcher = fs.watch(source, compile); + } catch (e) { + watchErr(e); + } + return rewatch = function() { + if (watcher != null) { + watcher.close(); + } + return watcher = fs.watch(source, compile); + }; + }; + + watchDir = function(source, base) { + var readdirTimeout, watcher; + readdirTimeout = null; + try { + return watcher = fs.watch(source, function() { + clearTimeout(readdirTimeout); + return readdirTimeout = wait(25, function() { + return fs.readdir(source, function(err, files) { + var file, _i, _len, _results; + if (err) { + if (err.code !== 'ENOENT') { + throw err; + } + watcher.close(); + return unwatchDir(source, base); + } + _results = []; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + if (!(!hidden(file) && !notSources[file])) { + continue; + } + file = path.join(source, file); + if (sources.some(function(s) { + return s.indexOf(file) >= 0; + })) { + continue; + } + sources.push(file); + sourceCode.push(null); + _results.push(compilePath(file, false, base)); + } + return _results; + }); + }); + }); + } catch (e) { + if (e.code !== 'ENOENT') { + throw e; + } + } + }; + + unwatchDir = function(source, base) { + var file, prevSources, toRemove, _i, _len; + prevSources = sources.slice(0); + toRemove = (function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = sources.length; _i < _len; _i++) { + file = sources[_i]; + if (file.indexOf(source) >= 0) { + _results.push(file); + } + } + return _results; + })(); + for (_i = 0, _len = toRemove.length; _i < _len; _i++) { + file = toRemove[_i]; + removeSource(file, base, true); + } + if (!sources.some(function(s, i) { + return prevSources[i] !== s; + })) { + return; + } + return compileJoin(); + }; + + removeSource = function(source, base, removeJs) { + var index, jsPath; + index = sources.indexOf(source); + sources.splice(index, 1); + sourceCode.splice(index, 1); + if (removeJs && !opts.join) { + jsPath = outputPath(source, base); + return exists(jsPath, function(itExists) { + if (itExists) { + return fs.unlink(jsPath, function(err) { + if (err && err.code !== 'ENOENT') { + throw err; + } + return timeLog("removed " + source); + }); + } + }); + } + }; + + outputPath = function(source, base) { + var baseDir, dir, filename, srcDir; + filename = path.basename(source, path.extname(source)) + '.js'; + srcDir = path.dirname(source); + baseDir = base === '.' ? srcDir : srcDir.substring(base.length); + dir = opts.output ? path.join(opts.output, baseDir) : srcDir; + return path.join(dir, filename); + }; + + writeJs = function(source, js, base) { + var compile, jsDir, jsPath; + jsPath = outputPath(source, base); + jsDir = path.dirname(jsPath); + compile = function() { + if (js.length <= 0) { + js = ' '; + } + return fs.writeFile(jsPath, js, function(err) { + if (err) { + return printLine(err.message); + } else if (opts.compile && opts.watch) { + return timeLog("compiled " + source); + } + }); + }; + return exists(jsDir, function(itExists) { + if (itExists) { + return compile(); + } else { + return exec("mkdir -p " + jsDir, compile); + } + }); + }; + + wait = function(milliseconds, func) { + return setTimeout(func, milliseconds); + }; + + timeLog = function(message) { + return console.log("" + ((new Date).toLocaleTimeString()) + " - " + message); + }; + + lint = function(file, js) { + var conf, jsl, printIt; + printIt = function(buffer) { + return printLine(file + ':\t' + buffer.toString().trim()); + }; + conf = __dirname + '/../../extras/jsl.conf'; + jsl = spawn('jsl', ['-nologo', '-stdin', '-conf', conf]); + jsl.stdout.on('data', printIt); + jsl.stderr.on('data', printIt); + jsl.stdin.write(js); + return jsl.stdin.end(); + }; + + printTokens = function(tokens) { + var strings, tag, token, value; + strings = (function() { + var _i, _len, _ref1, _results; + _results = []; + for (_i = 0, _len = tokens.length; _i < _len; _i++) { + token = tokens[_i]; + _ref1 = [token[0], token[1].toString().replace(/\n/, '\\n')], tag = _ref1[0], value = _ref1[1]; + _results.push("[" + tag + " " + value + "]"); + } + return _results; + })(); + return printLine(strings.join(' ')); + }; + + parseOptions = function() { + var i, o, source, _i, _len; + optionParser = new optparse.OptionParser(SWITCHES, BANNER); + o = opts = optionParser.parse(process.argv.slice(2)); + o.compile || (o.compile = !!o.output); + o.run = !(o.compile || o.print || o.lint); + o.print = !!(o.print || (o["eval"] || o.stdio && o.compile)); + sources = o["arguments"]; + for (i = _i = 0, _len = sources.length; _i < _len; i = ++_i) { + source = sources[i]; + sourceCode[i] = null; + } + }; + + compileOptions = function(filename) { + return { + filename: filename, + bare: opts.bare, + header: opts.compile + }; + }; + + forkNode = function() { + var args, nodeArgs; + nodeArgs = opts.nodejs.split(/\s+/); + args = process.argv.slice(1); + args.splice(args.indexOf('--nodejs'), 2); + return spawn(process.execPath, nodeArgs.concat(args), { + cwd: process.cwd(), + env: process.env, + customFds: [0, 1, 2] + }); + }; + + usage = function() { + return printLine((new optparse.OptionParser(SWITCHES, BANNER)).help()); + }; + + version = function() { + return printLine("CoffeeScript version " + CoffeeScript.VERSION); + }; + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/grammar.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/grammar.js new file mode 100644 index 0000000..e3513e5 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/grammar.js @@ -0,0 +1,606 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap; + + Parser = require('jison').Parser; + + unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/; + + o = function(patternString, action, options) { + var match; + patternString = patternString.replace(/\s{2,}/g, ' '); + if (!action) { + return [patternString, '$$ = $1;', options]; + } + action = (match = unwrap.exec(action)) ? match[1] : "(" + action + "())"; + action = action.replace(/\bnew /g, '$&yy.'); + action = action.replace(/\b(?:Block\.wrap|extend)\b/g, 'yy.$&'); + return [patternString, "$$ = " + action + ";", options]; + }; + + grammar = { + Root: [ + o('', function() { + return new Block; + }), o('Body'), o('Block TERMINATOR') + ], + Body: [ + o('Line', function() { + return Block.wrap([$1]); + }), o('Body TERMINATOR Line', function() { + return $1.push($3); + }), o('Body TERMINATOR') + ], + Line: [o('Expression'), o('Statement')], + Statement: [ + o('Return'), o('Comment'), o('STATEMENT', function() { + return new Literal($1); + }) + ], + Expression: [o('Value'), o('Invocation'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw')], + Block: [ + o('INDENT OUTDENT', function() { + return new Block; + }), o('INDENT Body OUTDENT', function() { + return $2; + }) + ], + Identifier: [ + o('IDENTIFIER', function() { + return new Literal($1); + }) + ], + AlphaNumeric: [ + o('NUMBER', function() { + return new Literal($1); + }), o('STRING', function() { + return new Literal($1); + }) + ], + Literal: [ + o('AlphaNumeric'), o('JS', function() { + return new Literal($1); + }), o('REGEX', function() { + return new Literal($1); + }), o('DEBUGGER', function() { + return new Literal($1); + }), o('UNDEFINED', function() { + return new Undefined; + }), o('NULL', function() { + return new Null; + }), o('BOOL', function() { + return new Bool($1); + }) + ], + Assign: [ + o('Assignable = Expression', function() { + return new Assign($1, $3); + }), o('Assignable = TERMINATOR Expression', function() { + return new Assign($1, $4); + }), o('Assignable = INDENT Expression OUTDENT', function() { + return new Assign($1, $4); + }) + ], + AssignObj: [ + o('ObjAssignable', function() { + return new Value($1); + }), o('ObjAssignable : Expression', function() { + return new Assign(new Value($1), $3, 'object'); + }), o('ObjAssignable :\ + INDENT Expression OUTDENT', function() { + return new Assign(new Value($1), $4, 'object'); + }), o('Comment') + ], + ObjAssignable: [o('Identifier'), o('AlphaNumeric'), o('ThisProperty')], + Return: [ + o('RETURN Expression', function() { + return new Return($2); + }), o('RETURN', function() { + return new Return; + }) + ], + Comment: [ + o('HERECOMMENT', function() { + return new Comment($1); + }) + ], + Code: [ + o('PARAM_START ParamList PARAM_END FuncGlyph Block', function() { + return new Code($2, $5, $4); + }), o('FuncGlyph Block', function() { + return new Code([], $2, $1); + }) + ], + FuncGlyph: [ + o('->', function() { + return 'func'; + }), o('=>', function() { + return 'boundfunc'; + }) + ], + OptComma: [o(''), o(',')], + ParamList: [ + o('', function() { + return []; + }), o('Param', function() { + return [$1]; + }), o('ParamList , Param', function() { + return $1.concat($3); + }), o('ParamList OptComma TERMINATOR Param', function() { + return $1.concat($4); + }), o('ParamList OptComma INDENT ParamList OptComma OUTDENT', function() { + return $1.concat($4); + }) + ], + Param: [ + o('ParamVar', function() { + return new Param($1); + }), o('ParamVar ...', function() { + return new Param($1, null, true); + }), o('ParamVar = Expression', function() { + return new Param($1, $3); + }) + ], + ParamVar: [o('Identifier'), o('ThisProperty'), o('Array'), o('Object')], + Splat: [ + o('Expression ...', function() { + return new Splat($1); + }) + ], + SimpleAssignable: [ + o('Identifier', function() { + return new Value($1); + }), o('Value Accessor', function() { + return $1.add($2); + }), o('Invocation Accessor', function() { + return new Value($1, [].concat($2)); + }), o('ThisProperty') + ], + Assignable: [ + o('SimpleAssignable'), o('Array', function() { + return new Value($1); + }), o('Object', function() { + return new Value($1); + }) + ], + Value: [ + o('Assignable'), o('Literal', function() { + return new Value($1); + }), o('Parenthetical', function() { + return new Value($1); + }), o('Range', function() { + return new Value($1); + }), o('This') + ], + Accessor: [ + o('. Identifier', function() { + return new Access($2); + }), o('?. Identifier', function() { + return new Access($2, 'soak'); + }), o(':: Identifier', function() { + return [new Access(new Literal('prototype')), new Access($2)]; + }), o('::', function() { + return new Access(new Literal('prototype')); + }), o('Index') + ], + Index: [ + o('INDEX_START IndexValue INDEX_END', function() { + return $2; + }), o('INDEX_SOAK Index', function() { + return extend($2, { + soak: true + }); + }) + ], + IndexValue: [ + o('Expression', function() { + return new Index($1); + }), o('Slice', function() { + return new Slice($1); + }) + ], + Object: [ + o('{ AssignList OptComma }', function() { + return new Obj($2, $1.generated); + }) + ], + AssignList: [ + o('', function() { + return []; + }), o('AssignObj', function() { + return [$1]; + }), o('AssignList , AssignObj', function() { + return $1.concat($3); + }), o('AssignList OptComma TERMINATOR AssignObj', function() { + return $1.concat($4); + }), o('AssignList OptComma INDENT AssignList OptComma OUTDENT', function() { + return $1.concat($4); + }) + ], + Class: [ + o('CLASS', function() { + return new Class; + }), o('CLASS Block', function() { + return new Class(null, null, $2); + }), o('CLASS EXTENDS Expression', function() { + return new Class(null, $3); + }), o('CLASS EXTENDS Expression Block', function() { + return new Class(null, $3, $4); + }), o('CLASS SimpleAssignable', function() { + return new Class($2); + }), o('CLASS SimpleAssignable Block', function() { + return new Class($2, null, $3); + }), o('CLASS SimpleAssignable EXTENDS Expression', function() { + return new Class($2, $4); + }), o('CLASS SimpleAssignable EXTENDS Expression Block', function() { + return new Class($2, $4, $5); + }) + ], + Invocation: [ + o('Value OptFuncExist Arguments', function() { + return new Call($1, $3, $2); + }), o('Invocation OptFuncExist Arguments', function() { + return new Call($1, $3, $2); + }), o('SUPER', function() { + return new Call('super', [new Splat(new Literal('arguments'))]); + }), o('SUPER Arguments', function() { + return new Call('super', $2); + }) + ], + OptFuncExist: [ + o('', function() { + return false; + }), o('FUNC_EXIST', function() { + return true; + }) + ], + Arguments: [ + o('CALL_START CALL_END', function() { + return []; + }), o('CALL_START ArgList OptComma CALL_END', function() { + return $2; + }) + ], + This: [ + o('THIS', function() { + return new Value(new Literal('this')); + }), o('@', function() { + return new Value(new Literal('this')); + }) + ], + ThisProperty: [ + o('@ Identifier', function() { + return new Value(new Literal('this'), [new Access($2)], 'this'); + }) + ], + Array: [ + o('[ ]', function() { + return new Arr([]); + }), o('[ ArgList OptComma ]', function() { + return new Arr($2); + }) + ], + RangeDots: [ + o('..', function() { + return 'inclusive'; + }), o('...', function() { + return 'exclusive'; + }) + ], + Range: [ + o('[ Expression RangeDots Expression ]', function() { + return new Range($2, $4, $3); + }) + ], + Slice: [ + o('Expression RangeDots Expression', function() { + return new Range($1, $3, $2); + }), o('Expression RangeDots', function() { + return new Range($1, null, $2); + }), o('RangeDots Expression', function() { + return new Range(null, $2, $1); + }), o('RangeDots', function() { + return new Range(null, null, $1); + }) + ], + ArgList: [ + o('Arg', function() { + return [$1]; + }), o('ArgList , Arg', function() { + return $1.concat($3); + }), o('ArgList OptComma TERMINATOR Arg', function() { + return $1.concat($4); + }), o('INDENT ArgList OptComma OUTDENT', function() { + return $2; + }), o('ArgList OptComma INDENT ArgList OptComma OUTDENT', function() { + return $1.concat($4); + }) + ], + Arg: [o('Expression'), o('Splat')], + SimpleArgs: [ + o('Expression'), o('SimpleArgs , Expression', function() { + return [].concat($1, $3); + }) + ], + Try: [ + o('TRY Block', function() { + return new Try($2); + }), o('TRY Block Catch', function() { + return new Try($2, $3[0], $3[1]); + }), o('TRY Block FINALLY Block', function() { + return new Try($2, null, null, $4); + }), o('TRY Block Catch FINALLY Block', function() { + return new Try($2, $3[0], $3[1], $5); + }) + ], + Catch: [ + o('CATCH Identifier Block', function() { + return [$2, $3]; + }) + ], + Throw: [ + o('THROW Expression', function() { + return new Throw($2); + }) + ], + Parenthetical: [ + o('( Body )', function() { + return new Parens($2); + }), o('( INDENT Body OUTDENT )', function() { + return new Parens($3); + }) + ], + WhileSource: [ + o('WHILE Expression', function() { + return new While($2); + }), o('WHILE Expression WHEN Expression', function() { + return new While($2, { + guard: $4 + }); + }), o('UNTIL Expression', function() { + return new While($2, { + invert: true + }); + }), o('UNTIL Expression WHEN Expression', function() { + return new While($2, { + invert: true, + guard: $4 + }); + }) + ], + While: [ + o('WhileSource Block', function() { + return $1.addBody($2); + }), o('Statement WhileSource', function() { + return $2.addBody(Block.wrap([$1])); + }), o('Expression WhileSource', function() { + return $2.addBody(Block.wrap([$1])); + }), o('Loop', function() { + return $1; + }) + ], + Loop: [ + o('LOOP Block', function() { + return new While(new Literal('true')).addBody($2); + }), o('LOOP Expression', function() { + return new While(new Literal('true')).addBody(Block.wrap([$2])); + }) + ], + For: [ + o('Statement ForBody', function() { + return new For($1, $2); + }), o('Expression ForBody', function() { + return new For($1, $2); + }), o('ForBody Block', function() { + return new For($2, $1); + }) + ], + ForBody: [ + o('FOR Range', function() { + return { + source: new Value($2) + }; + }), o('ForStart ForSource', function() { + $2.own = $1.own; + $2.name = $1[0]; + $2.index = $1[1]; + return $2; + }) + ], + ForStart: [ + o('FOR ForVariables', function() { + return $2; + }), o('FOR OWN ForVariables', function() { + $3.own = true; + return $3; + }) + ], + ForValue: [ + o('Identifier'), o('ThisProperty'), o('Array', function() { + return new Value($1); + }), o('Object', function() { + return new Value($1); + }) + ], + ForVariables: [ + o('ForValue', function() { + return [$1]; + }), o('ForValue , ForValue', function() { + return [$1, $3]; + }) + ], + ForSource: [ + o('FORIN Expression', function() { + return { + source: $2 + }; + }), o('FOROF Expression', function() { + return { + source: $2, + object: true + }; + }), o('FORIN Expression WHEN Expression', function() { + return { + source: $2, + guard: $4 + }; + }), o('FOROF Expression WHEN Expression', function() { + return { + source: $2, + guard: $4, + object: true + }; + }), o('FORIN Expression BY Expression', function() { + return { + source: $2, + step: $4 + }; + }), o('FORIN Expression WHEN Expression BY Expression', function() { + return { + source: $2, + guard: $4, + step: $6 + }; + }), o('FORIN Expression BY Expression WHEN Expression', function() { + return { + source: $2, + step: $4, + guard: $6 + }; + }) + ], + Switch: [ + o('SWITCH Expression INDENT Whens OUTDENT', function() { + return new Switch($2, $4); + }), o('SWITCH Expression INDENT Whens ELSE Block OUTDENT', function() { + return new Switch($2, $4, $6); + }), o('SWITCH INDENT Whens OUTDENT', function() { + return new Switch(null, $3); + }), o('SWITCH INDENT Whens ELSE Block OUTDENT', function() { + return new Switch(null, $3, $5); + }) + ], + Whens: [ + o('When'), o('Whens When', function() { + return $1.concat($2); + }) + ], + When: [ + o('LEADING_WHEN SimpleArgs Block', function() { + return [[$2, $3]]; + }), o('LEADING_WHEN SimpleArgs Block TERMINATOR', function() { + return [[$2, $3]]; + }) + ], + IfBlock: [ + o('IF Expression Block', function() { + return new If($2, $3, { + type: $1 + }); + }), o('IfBlock ELSE IF Expression Block', function() { + return $1.addElse(new If($4, $5, { + type: $3 + })); + }) + ], + If: [ + o('IfBlock'), o('IfBlock ELSE Block', function() { + return $1.addElse($3); + }), o('Statement POST_IF Expression', function() { + return new If($3, Block.wrap([$1]), { + type: $2, + statement: true + }); + }), o('Expression POST_IF Expression', function() { + return new If($3, Block.wrap([$1]), { + type: $2, + statement: true + }); + }) + ], + Operation: [ + o('UNARY Expression', function() { + return new Op($1, $2); + }), o('- Expression', (function() { + return new Op('-', $2); + }), { + prec: 'UNARY' + }), o('+ Expression', (function() { + return new Op('+', $2); + }), { + prec: 'UNARY' + }), o('-- SimpleAssignable', function() { + return new Op('--', $2); + }), o('++ SimpleAssignable', function() { + return new Op('++', $2); + }), o('SimpleAssignable --', function() { + return new Op('--', $1, null, true); + }), o('SimpleAssignable ++', function() { + return new Op('++', $1, null, true); + }), o('Expression ?', function() { + return new Existence($1); + }), o('Expression + Expression', function() { + return new Op('+', $1, $3); + }), o('Expression - Expression', function() { + return new Op('-', $1, $3); + }), o('Expression MATH Expression', function() { + return new Op($2, $1, $3); + }), o('Expression SHIFT Expression', function() { + return new Op($2, $1, $3); + }), o('Expression COMPARE Expression', function() { + return new Op($2, $1, $3); + }), o('Expression LOGIC Expression', function() { + return new Op($2, $1, $3); + }), o('Expression RELATION Expression', function() { + if ($2.charAt(0) === '!') { + return new Op($2.slice(1), $1, $3).invert(); + } else { + return new Op($2, $1, $3); + } + }), o('SimpleAssignable COMPOUND_ASSIGN\ + Expression', function() { + return new Assign($1, $3, $2); + }), o('SimpleAssignable COMPOUND_ASSIGN\ + INDENT Expression OUTDENT', function() { + return new Assign($1, $4, $2); + }), o('SimpleAssignable EXTENDS Expression', function() { + return new Extends($1, $3); + }) + ] + }; + + operators = [['left', '.', '?.', '::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF']]; + + tokens = []; + + for (name in grammar) { + alternatives = grammar[name]; + grammar[name] = (function() { + var _i, _j, _len, _len1, _ref, _results; + _results = []; + for (_i = 0, _len = alternatives.length; _i < _len; _i++) { + alt = alternatives[_i]; + _ref = alt[0].split(' '); + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + token = _ref[_j]; + if (!grammar[token]) { + tokens.push(token); + } + } + if (name === 'Root') { + alt[1] = "return " + alt[1]; + } + _results.push(alt); + } + return _results; + })(); + } + + exports.parser = new Parser({ + tokens: tokens.join(' '), + bnf: grammar, + operators: operators.reverse(), + startSymbol: 'Root' + }); + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/helpers.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/helpers.js new file mode 100644 index 0000000..f9929f7 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/helpers.js @@ -0,0 +1,88 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var extend, flatten, _ref; + + exports.starts = function(string, literal, start) { + return literal === string.substr(start, literal.length); + }; + + exports.ends = function(string, literal, back) { + var len; + len = literal.length; + return literal === string.substr(string.length - len - (back || 0), len); + }; + + exports.compact = function(array) { + var item, _i, _len, _results; + _results = []; + for (_i = 0, _len = array.length; _i < _len; _i++) { + item = array[_i]; + if (item) { + _results.push(item); + } + } + return _results; + }; + + exports.count = function(string, substr) { + var num, pos; + num = pos = 0; + if (!substr.length) { + return 1 / 0; + } + while (pos = 1 + string.indexOf(substr, pos)) { + num++; + } + return num; + }; + + exports.merge = function(options, overrides) { + return extend(extend({}, options), overrides); + }; + + extend = exports.extend = function(object, properties) { + var key, val; + for (key in properties) { + val = properties[key]; + object[key] = val; + } + return object; + }; + + exports.flatten = flatten = function(array) { + var element, flattened, _i, _len; + flattened = []; + for (_i = 0, _len = array.length; _i < _len; _i++) { + element = array[_i]; + if (element instanceof Array) { + flattened = flattened.concat(flatten(element)); + } else { + flattened.push(element); + } + } + return flattened; + }; + + exports.del = function(obj, key) { + var val; + val = obj[key]; + delete obj[key]; + return val; + }; + + exports.last = function(array, back) { + return array[array.length - (back || 0) - 1]; + }; + + exports.some = (_ref = Array.prototype.some) != null ? _ref : function(fn) { + var e, _i, _len; + for (_i = 0, _len = this.length; _i < _len; _i++) { + e = this[_i]; + if (fn(e)) { + return true; + } + } + return false; + }; + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/index.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/index.js new file mode 100644 index 0000000..b1c1765 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/index.js @@ -0,0 +1,11 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var key, val, _ref; + + _ref = require('./coffee-script'); + for (key in _ref) { + val = _ref[key]; + exports[key] = val; + } + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/lexer.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/lexer.js new file mode 100644 index 0000000..1b777cb --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/lexer.js @@ -0,0 +1,788 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, key, last, starts, _ref, _ref1, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + _ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES; + + _ref1 = require('./helpers'), count = _ref1.count, starts = _ref1.starts, compact = _ref1.compact, last = _ref1.last; + + exports.Lexer = Lexer = (function() { + + function Lexer() {} + + Lexer.prototype.tokenize = function(code, opts) { + var i, tag; + if (opts == null) { + opts = {}; + } + if (WHITESPACE.test(code)) { + code = "\n" + code; + } + code = code.replace(/\r/g, '').replace(TRAILING_SPACES, ''); + this.code = code; + this.line = opts.line || 0; + this.indent = 0; + this.indebt = 0; + this.outdebt = 0; + this.indents = []; + this.ends = []; + this.tokens = []; + i = 0; + while (this.chunk = code.slice(i)) { + i += this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.heredocToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken(); + } + this.closeIndentation(); + if (tag = this.ends.pop()) { + this.error("missing " + tag); + } + if (opts.rewrite === false) { + return this.tokens; + } + return (new Rewriter).rewrite(this.tokens); + }; + + Lexer.prototype.identifierToken = function() { + var colon, forcedIdentifier, id, input, match, prev, tag, _ref2, _ref3; + if (!(match = IDENTIFIER.exec(this.chunk))) { + return 0; + } + input = match[0], id = match[1], colon = match[2]; + if (id === 'own' && this.tag() === 'FOR') { + this.token('OWN', id); + return id.length; + } + forcedIdentifier = colon || (prev = last(this.tokens)) && (((_ref2 = prev[0]) === '.' || _ref2 === '?.' || _ref2 === '::') || !prev.spaced && prev[0] === '@'); + tag = 'IDENTIFIER'; + if (!forcedIdentifier && (__indexOf.call(JS_KEYWORDS, id) >= 0 || __indexOf.call(COFFEE_KEYWORDS, id) >= 0)) { + tag = id.toUpperCase(); + if (tag === 'WHEN' && (_ref3 = this.tag(), __indexOf.call(LINE_BREAK, _ref3) >= 0)) { + tag = 'LEADING_WHEN'; + } else if (tag === 'FOR') { + this.seenFor = true; + } else if (tag === 'UNLESS') { + tag = 'IF'; + } else if (__indexOf.call(UNARY, tag) >= 0) { + tag = 'UNARY'; + } else if (__indexOf.call(RELATION, tag) >= 0) { + if (tag !== 'INSTANCEOF' && this.seenFor) { + tag = 'FOR' + tag; + this.seenFor = false; + } else { + tag = 'RELATION'; + if (this.value() === '!') { + this.tokens.pop(); + id = '!' + id; + } + } + } + } + if (__indexOf.call(JS_FORBIDDEN, id) >= 0) { + if (forcedIdentifier) { + tag = 'IDENTIFIER'; + id = new String(id); + id.reserved = true; + } else if (__indexOf.call(RESERVED, id) >= 0) { + this.error("reserved word \"" + id + "\""); + } + } + if (!forcedIdentifier) { + if (__indexOf.call(COFFEE_ALIASES, id) >= 0) { + id = COFFEE_ALIAS_MAP[id]; + } + tag = (function() { + switch (id) { + case '!': + return 'UNARY'; + case '==': + case '!=': + return 'COMPARE'; + case '&&': + case '||': + return 'LOGIC'; + case 'true': + case 'false': + return 'BOOL'; + case 'break': + case 'continue': + return 'STATEMENT'; + default: + return tag; + } + })(); + } + this.token(tag, id); + if (colon) { + this.token(':', ':'); + } + return input.length; + }; + + Lexer.prototype.numberToken = function() { + var binaryLiteral, lexedLength, match, number, octalLiteral; + if (!(match = NUMBER.exec(this.chunk))) { + return 0; + } + number = match[0]; + if (/^0[BOX]/.test(number)) { + this.error("radix prefix '" + number + "' must be lowercase"); + } else if (/E/.test(number) && !/^0x/.test(number)) { + this.error("exponential notation '" + number + "' must be indicated with a lowercase 'e'"); + } else if (/^0\d*[89]/.test(number)) { + this.error("decimal literal '" + number + "' must not be prefixed with '0'"); + } else if (/^0\d+/.test(number)) { + this.error("octal literal '" + number + "' must be prefixed with '0o'"); + } + lexedLength = number.length; + if (octalLiteral = /^0o([0-7]+)/.exec(number)) { + number = '0x' + (parseInt(octalLiteral[1], 8)).toString(16); + } + if (binaryLiteral = /^0b([01]+)/.exec(number)) { + number = '0x' + (parseInt(binaryLiteral[1], 2)).toString(16); + } + this.token('NUMBER', number); + return lexedLength; + }; + + Lexer.prototype.stringToken = function() { + var match, octalEsc, string; + switch (this.chunk.charAt(0)) { + case "'": + if (!(match = SIMPLESTR.exec(this.chunk))) { + return 0; + } + this.token('STRING', (string = match[0]).replace(MULTILINER, '\\\n')); + break; + case '"': + if (!(string = this.balancedString(this.chunk, '"'))) { + return 0; + } + if (0 < string.indexOf('#{', 1)) { + this.interpolateString(string.slice(1, -1)); + } else { + this.token('STRING', this.escapeLines(string)); + } + break; + default: + return 0; + } + if (octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test(string)) { + this.error("octal escape sequences " + string + " are not allowed"); + } + this.line += count(string, '\n'); + return string.length; + }; + + Lexer.prototype.heredocToken = function() { + var doc, heredoc, match, quote; + if (!(match = HEREDOC.exec(this.chunk))) { + return 0; + } + heredoc = match[0]; + quote = heredoc.charAt(0); + doc = this.sanitizeHeredoc(match[2], { + quote: quote, + indent: null + }); + if (quote === '"' && 0 <= doc.indexOf('#{')) { + this.interpolateString(doc, { + heredoc: true + }); + } else { + this.token('STRING', this.makeString(doc, quote, true)); + } + this.line += count(heredoc, '\n'); + return heredoc.length; + }; + + Lexer.prototype.commentToken = function() { + var comment, here, match; + if (!(match = this.chunk.match(COMMENT))) { + return 0; + } + comment = match[0], here = match[1]; + if (here) { + this.token('HERECOMMENT', this.sanitizeHeredoc(here, { + herecomment: true, + indent: Array(this.indent + 1).join(' ') + })); + } + this.line += count(comment, '\n'); + return comment.length; + }; + + Lexer.prototype.jsToken = function() { + var match, script; + if (!(this.chunk.charAt(0) === '`' && (match = JSTOKEN.exec(this.chunk)))) { + return 0; + } + this.token('JS', (script = match[0]).slice(1, -1)); + this.line += count(script, '\n'); + return script.length; + }; + + Lexer.prototype.regexToken = function() { + var flags, length, match, prev, regex, _ref2, _ref3; + if (this.chunk.charAt(0) !== '/') { + return 0; + } + if (match = HEREGEX.exec(this.chunk)) { + length = this.heregexToken(match); + this.line += count(match[0], '\n'); + return length; + } + prev = last(this.tokens); + if (prev && (_ref2 = prev[0], __indexOf.call((prev.spaced ? NOT_REGEX : NOT_SPACED_REGEX), _ref2) >= 0)) { + return 0; + } + if (!(match = REGEX.exec(this.chunk))) { + return 0; + } + _ref3 = match, match = _ref3[0], regex = _ref3[1], flags = _ref3[2]; + if (regex.slice(0, 2) === '/*') { + this.error('regular expressions cannot begin with `*`'); + } + if (regex === '//') { + regex = '/(?:)/'; + } + this.token('REGEX', "" + regex + flags); + return match.length; + }; + + Lexer.prototype.heregexToken = function(match) { + var body, flags, heregex, re, tag, tokens, value, _i, _len, _ref2, _ref3, _ref4, _ref5; + heregex = match[0], body = match[1], flags = match[2]; + if (0 > body.indexOf('#{')) { + re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/'); + if (re.match(/^\*/)) { + this.error('regular expressions cannot begin with `*`'); + } + this.token('REGEX', "/" + (re || '(?:)') + "/" + flags); + return heregex.length; + } + this.token('IDENTIFIER', 'RegExp'); + this.tokens.push(['CALL_START', '(']); + tokens = []; + _ref2 = this.interpolateString(body, { + regex: true + }); + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + _ref3 = _ref2[_i], tag = _ref3[0], value = _ref3[1]; + if (tag === 'TOKENS') { + tokens.push.apply(tokens, value); + } else { + if (!(value = value.replace(HEREGEX_OMIT, ''))) { + continue; + } + value = value.replace(/\\/g, '\\\\'); + tokens.push(['STRING', this.makeString(value, '"', true)]); + } + tokens.push(['+', '+']); + } + tokens.pop(); + if (((_ref4 = tokens[0]) != null ? _ref4[0] : void 0) !== 'STRING') { + this.tokens.push(['STRING', '""'], ['+', '+']); + } + (_ref5 = this.tokens).push.apply(_ref5, tokens); + if (flags) { + this.tokens.push([',', ','], ['STRING', '"' + flags + '"']); + } + this.token(')', ')'); + return heregex.length; + }; + + Lexer.prototype.lineToken = function() { + var diff, indent, match, noNewlines, size; + if (!(match = MULTI_DENT.exec(this.chunk))) { + return 0; + } + indent = match[0]; + this.line += count(indent, '\n'); + this.seenFor = false; + size = indent.length - 1 - indent.lastIndexOf('\n'); + noNewlines = this.unfinished(); + if (size - this.indebt === this.indent) { + if (noNewlines) { + this.suppressNewlines(); + } else { + this.newlineToken(); + } + return indent.length; + } + if (size > this.indent) { + if (noNewlines) { + this.indebt = size - this.indent; + this.suppressNewlines(); + return indent.length; + } + diff = size - this.indent + this.outdebt; + this.token('INDENT', diff); + this.indents.push(diff); + this.ends.push('OUTDENT'); + this.outdebt = this.indebt = 0; + } else { + this.indebt = 0; + this.outdentToken(this.indent - size, noNewlines); + } + this.indent = size; + return indent.length; + }; + + Lexer.prototype.outdentToken = function(moveOut, noNewlines) { + var dent, len; + while (moveOut > 0) { + len = this.indents.length - 1; + if (this.indents[len] === void 0) { + moveOut = 0; + } else if (this.indents[len] === this.outdebt) { + moveOut -= this.outdebt; + this.outdebt = 0; + } else if (this.indents[len] < this.outdebt) { + this.outdebt -= this.indents[len]; + moveOut -= this.indents[len]; + } else { + dent = this.indents.pop() - this.outdebt; + moveOut -= dent; + this.outdebt = 0; + this.pair('OUTDENT'); + this.token('OUTDENT', dent); + } + } + if (dent) { + this.outdebt -= moveOut; + } + while (this.value() === ';') { + this.tokens.pop(); + } + if (!(this.tag() === 'TERMINATOR' || noNewlines)) { + this.token('TERMINATOR', '\n'); + } + return this; + }; + + Lexer.prototype.whitespaceToken = function() { + var match, nline, prev; + if (!((match = WHITESPACE.exec(this.chunk)) || (nline = this.chunk.charAt(0) === '\n'))) { + return 0; + } + prev = last(this.tokens); + if (prev) { + prev[match ? 'spaced' : 'newLine'] = true; + } + if (match) { + return match[0].length; + } else { + return 0; + } + }; + + Lexer.prototype.newlineToken = function() { + while (this.value() === ';') { + this.tokens.pop(); + } + if (this.tag() !== 'TERMINATOR') { + this.token('TERMINATOR', '\n'); + } + return this; + }; + + Lexer.prototype.suppressNewlines = function() { + if (this.value() === '\\') { + this.tokens.pop(); + } + return this; + }; + + Lexer.prototype.literalToken = function() { + var match, prev, tag, value, _ref2, _ref3, _ref4, _ref5; + if (match = OPERATOR.exec(this.chunk)) { + value = match[0]; + if (CODE.test(value)) { + this.tagParameters(); + } + } else { + value = this.chunk.charAt(0); + } + tag = value; + prev = last(this.tokens); + if (value === '=' && prev) { + if (!prev[1].reserved && (_ref2 = prev[1], __indexOf.call(JS_FORBIDDEN, _ref2) >= 0)) { + this.error("reserved word \"" + (this.value()) + "\" can't be assigned"); + } + if ((_ref3 = prev[1]) === '||' || _ref3 === '&&') { + prev[0] = 'COMPOUND_ASSIGN'; + prev[1] += '='; + return value.length; + } + } + if (value === ';') { + this.seenFor = false; + tag = 'TERMINATOR'; + } else if (__indexOf.call(MATH, value) >= 0) { + tag = 'MATH'; + } else if (__indexOf.call(COMPARE, value) >= 0) { + tag = 'COMPARE'; + } else if (__indexOf.call(COMPOUND_ASSIGN, value) >= 0) { + tag = 'COMPOUND_ASSIGN'; + } else if (__indexOf.call(UNARY, value) >= 0) { + tag = 'UNARY'; + } else if (__indexOf.call(SHIFT, value) >= 0) { + tag = 'SHIFT'; + } else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) { + tag = 'LOGIC'; + } else if (prev && !prev.spaced) { + if (value === '(' && (_ref4 = prev[0], __indexOf.call(CALLABLE, _ref4) >= 0)) { + if (prev[0] === '?') { + prev[0] = 'FUNC_EXIST'; + } + tag = 'CALL_START'; + } else if (value === '[' && (_ref5 = prev[0], __indexOf.call(INDEXABLE, _ref5) >= 0)) { + tag = 'INDEX_START'; + switch (prev[0]) { + case '?': + prev[0] = 'INDEX_SOAK'; + } + } + } + switch (value) { + case '(': + case '{': + case '[': + this.ends.push(INVERSES[value]); + break; + case ')': + case '}': + case ']': + this.pair(value); + } + this.token(tag, value); + return value.length; + }; + + Lexer.prototype.sanitizeHeredoc = function(doc, options) { + var attempt, herecomment, indent, match, _ref2; + indent = options.indent, herecomment = options.herecomment; + if (herecomment) { + if (HEREDOC_ILLEGAL.test(doc)) { + this.error("block comment cannot contain \"*/\", starting"); + } + if (doc.indexOf('\n') <= 0) { + return doc; + } + } else { + while (match = HEREDOC_INDENT.exec(doc)) { + attempt = match[1]; + if (indent === null || (0 < (_ref2 = attempt.length) && _ref2 < indent.length)) { + indent = attempt; + } + } + } + if (indent) { + doc = doc.replace(RegExp("\\n" + indent, "g"), '\n'); + } + if (!herecomment) { + doc = doc.replace(/^\n/, ''); + } + return doc; + }; + + Lexer.prototype.tagParameters = function() { + var i, stack, tok, tokens; + if (this.tag() !== ')') { + return this; + } + stack = []; + tokens = this.tokens; + i = tokens.length; + tokens[--i][0] = 'PARAM_END'; + while (tok = tokens[--i]) { + switch (tok[0]) { + case ')': + stack.push(tok); + break; + case '(': + case 'CALL_START': + if (stack.length) { + stack.pop(); + } else if (tok[0] === '(') { + tok[0] = 'PARAM_START'; + return this; + } else { + return this; + } + } + } + return this; + }; + + Lexer.prototype.closeIndentation = function() { + return this.outdentToken(this.indent); + }; + + Lexer.prototype.balancedString = function(str, end) { + var continueCount, i, letter, match, prev, stack, _i, _ref2; + continueCount = 0; + stack = [end]; + for (i = _i = 1, _ref2 = str.length; 1 <= _ref2 ? _i < _ref2 : _i > _ref2; i = 1 <= _ref2 ? ++_i : --_i) { + if (continueCount) { + --continueCount; + continue; + } + switch (letter = str.charAt(i)) { + case '\\': + ++continueCount; + continue; + case end: + stack.pop(); + if (!stack.length) { + return str.slice(0, +i + 1 || 9e9); + } + end = stack[stack.length - 1]; + continue; + } + if (end === '}' && (letter === '"' || letter === "'")) { + stack.push(end = letter); + } else if (end === '}' && letter === '/' && (match = HEREGEX.exec(str.slice(i)) || REGEX.exec(str.slice(i)))) { + continueCount += match[0].length - 1; + } else if (end === '}' && letter === '{') { + stack.push(end = '}'); + } else if (end === '"' && prev === '#' && letter === '{') { + stack.push(end = '}'); + } + prev = letter; + } + return this.error("missing " + (stack.pop()) + ", starting"); + }; + + Lexer.prototype.interpolateString = function(str, options) { + var expr, heredoc, i, inner, interpolated, len, letter, nested, pi, regex, tag, tokens, value, _i, _len, _ref2, _ref3, _ref4; + if (options == null) { + options = {}; + } + heredoc = options.heredoc, regex = options.regex; + tokens = []; + pi = 0; + i = -1; + while (letter = str.charAt(i += 1)) { + if (letter === '\\') { + i += 1; + continue; + } + if (!(letter === '#' && str.charAt(i + 1) === '{' && (expr = this.balancedString(str.slice(i + 1), '}')))) { + continue; + } + if (pi < i) { + tokens.push(['NEOSTRING', str.slice(pi, i)]); + } + inner = expr.slice(1, -1); + if (inner.length) { + nested = new Lexer().tokenize(inner, { + line: this.line, + rewrite: false + }); + nested.pop(); + if (((_ref2 = nested[0]) != null ? _ref2[0] : void 0) === 'TERMINATOR') { + nested.shift(); + } + if (len = nested.length) { + if (len > 1) { + nested.unshift(['(', '(', this.line]); + nested.push([')', ')', this.line]); + } + tokens.push(['TOKENS', nested]); + } + } + i += expr.length; + pi = i + 1; + } + if ((i > pi && pi < str.length)) { + tokens.push(['NEOSTRING', str.slice(pi)]); + } + if (regex) { + return tokens; + } + if (!tokens.length) { + return this.token('STRING', '""'); + } + if (tokens[0][0] !== 'NEOSTRING') { + tokens.unshift(['', '']); + } + if (interpolated = tokens.length > 1) { + this.token('(', '('); + } + for (i = _i = 0, _len = tokens.length; _i < _len; i = ++_i) { + _ref3 = tokens[i], tag = _ref3[0], value = _ref3[1]; + if (i) { + this.token('+', '+'); + } + if (tag === 'TOKENS') { + (_ref4 = this.tokens).push.apply(_ref4, value); + } else { + this.token('STRING', this.makeString(value, '"', heredoc)); + } + } + if (interpolated) { + this.token(')', ')'); + } + return tokens; + }; + + Lexer.prototype.pair = function(tag) { + var size, wanted; + if (tag !== (wanted = last(this.ends))) { + if ('OUTDENT' !== wanted) { + this.error("unmatched " + tag); + } + this.indent -= size = last(this.indents); + this.outdentToken(size, true); + return this.pair(tag); + } + return this.ends.pop(); + }; + + Lexer.prototype.token = function(tag, value) { + return this.tokens.push([tag, value, this.line]); + }; + + Lexer.prototype.tag = function(index, tag) { + var tok; + return (tok = last(this.tokens, index)) && (tag ? tok[0] = tag : tok[0]); + }; + + Lexer.prototype.value = function(index, val) { + var tok; + return (tok = last(this.tokens, index)) && (val ? tok[1] = val : tok[1]); + }; + + Lexer.prototype.unfinished = function() { + var _ref2; + return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS'); + }; + + Lexer.prototype.escapeLines = function(str, heredoc) { + return str.replace(MULTILINER, heredoc ? '\\n' : ''); + }; + + Lexer.prototype.makeString = function(body, quote, heredoc) { + if (!body) { + return quote + quote; + } + body = body.replace(/\\([\s\S])/g, function(match, contents) { + if (contents === '\n' || contents === quote) { + return contents; + } else { + return match; + } + }); + body = body.replace(RegExp("" + quote, "g"), '\\$&'); + return quote + this.escapeLines(body, heredoc) + quote; + }; + + Lexer.prototype.error = function(message) { + throw SyntaxError("" + message + " on line " + (this.line + 1)); + }; + + return Lexer; + + })(); + + JS_KEYWORDS = ['true', 'false', 'null', 'this', 'new', 'delete', 'typeof', 'in', 'instanceof', 'return', 'throw', 'break', 'continue', 'debugger', 'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally', 'class', 'extends', 'super']; + + COFFEE_KEYWORDS = ['undefined', 'then', 'unless', 'until', 'loop', 'of', 'by', 'when']; + + COFFEE_ALIAS_MAP = { + and: '&&', + or: '||', + is: '==', + isnt: '!=', + not: '!', + yes: 'true', + no: 'false', + on: 'true', + off: 'false' + }; + + COFFEE_ALIASES = (function() { + var _results; + _results = []; + for (key in COFFEE_ALIAS_MAP) { + _results.push(key); + } + return _results; + })(); + + COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat(COFFEE_ALIASES); + + RESERVED = ['case', 'default', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice', '__bind', '__indexOf', 'implements', 'interface', 'package', 'private', 'protected', 'public', 'static', 'yield']; + + STRICT_PROSCRIBED = ['arguments', 'eval']; + + JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED); + + exports.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED); + + exports.STRICT_PROSCRIBED = STRICT_PROSCRIBED; + + IDENTIFIER = /^([$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)([^\n\S]*:(?!:))?/; + + NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i; + + HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/; + + OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/; + + WHITESPACE = /^[^\n\S]+/; + + COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/; + + CODE = /^[-=]>/; + + MULTI_DENT = /^(?:\n[^\n\S]*)+/; + + SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/; + + JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/; + + REGEX = /^(\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)([imgy]{0,4})(?!\w)/; + + HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?!\w)/; + + HEREGEX_OMIT = /\s+(?:#.*)?/g; + + MULTILINER = /\n/g; + + HEREDOC_INDENT = /\n+([^\n\S]*)/g; + + HEREDOC_ILLEGAL = /\*\//; + + LINE_CONTINUER = /^\s*(?:,|\??\.(?![.\d])|::)/; + + TRAILING_SPACES = /\s+$/; + + COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']; + + UNARY = ['!', '~', 'NEW', 'TYPEOF', 'DELETE', 'DO']; + + LOGIC = ['&&', '||', '&', '|', '^']; + + SHIFT = ['<<', '>>', '>>>']; + + COMPARE = ['==', '!=', '<', '>', '<=', '>=']; + + MATH = ['*', '/', '%']; + + RELATION = ['IN', 'OF', 'INSTANCEOF']; + + BOOL = ['TRUE', 'FALSE']; + + NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', 'NULL', 'UNDEFINED', '++', '--', ']']; + + NOT_SPACED_REGEX = NOT_REGEX.concat(')', '}', 'THIS', 'IDENTIFIER', 'STRING'); + + CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']; + + INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL', 'NULL', 'UNDEFINED'); + + LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']; + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/nodes.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/nodes.js new file mode 100644 index 0000000..f8e0777 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/nodes.js @@ -0,0 +1,2971 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, some, starts, unfoldSoak, utility, _ref, _ref1, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + Scope = require('./scope').Scope; + + _ref = require('./lexer'), RESERVED = _ref.RESERVED, STRICT_PROSCRIBED = _ref.STRICT_PROSCRIBED; + + _ref1 = require('./helpers'), compact = _ref1.compact, flatten = _ref1.flatten, extend = _ref1.extend, merge = _ref1.merge, del = _ref1.del, starts = _ref1.starts, ends = _ref1.ends, last = _ref1.last, some = _ref1.some; + + exports.extend = extend; + + YES = function() { + return true; + }; + + NO = function() { + return false; + }; + + THIS = function() { + return this; + }; + + NEGATE = function() { + this.negated = !this.negated; + return this; + }; + + exports.Base = Base = (function() { + + function Base() {} + + Base.prototype.compile = function(o, lvl) { + var node; + o = extend({}, o); + if (lvl) { + o.level = lvl; + } + node = this.unfoldSoak(o) || this; + node.tab = o.indent; + if (o.level === LEVEL_TOP || !node.isStatement(o)) { + return node.compileNode(o); + } else { + return node.compileClosure(o); + } + }; + + Base.prototype.compileClosure = function(o) { + if (this.jumps()) { + throw SyntaxError('cannot use a pure statement in an expression.'); + } + o.sharedScope = true; + return Closure.wrap(this).compileNode(o); + }; + + Base.prototype.cache = function(o, level, reused) { + var ref, sub; + if (!this.isComplex()) { + ref = level ? this.compile(o, level) : this; + return [ref, ref]; + } else { + ref = new Literal(reused || o.scope.freeVariable('ref')); + sub = new Assign(ref, this); + if (level) { + return [sub.compile(o, level), ref.value]; + } else { + return [sub, ref]; + } + } + }; + + Base.prototype.compileLoopReference = function(o, name) { + var src, tmp; + src = tmp = this.compile(o, LEVEL_LIST); + if (!((-Infinity < +src && +src < Infinity) || IDENTIFIER.test(src) && o.scope.check(src, true))) { + src = "" + (tmp = o.scope.freeVariable(name)) + " = " + src; + } + return [src, tmp]; + }; + + Base.prototype.makeReturn = function(res) { + var me; + me = this.unwrapAll(); + if (res) { + return new Call(new Literal("" + res + ".push"), [me]); + } else { + return new Return(me); + } + }; + + Base.prototype.contains = function(pred) { + var contains; + contains = false; + this.traverseChildren(false, function(node) { + if (pred(node)) { + contains = true; + return false; + } + }); + return contains; + }; + + Base.prototype.containsType = function(type) { + return this instanceof type || this.contains(function(node) { + return node instanceof type; + }); + }; + + Base.prototype.lastNonComment = function(list) { + var i; + i = list.length; + while (i--) { + if (!(list[i] instanceof Comment)) { + return list[i]; + } + } + return null; + }; + + Base.prototype.toString = function(idt, name) { + var tree; + if (idt == null) { + idt = ''; + } + if (name == null) { + name = this.constructor.name; + } + tree = '\n' + idt + name; + if (this.soak) { + tree += '?'; + } + this.eachChild(function(node) { + return tree += node.toString(idt + TAB); + }); + return tree; + }; + + Base.prototype.eachChild = function(func) { + var attr, child, _i, _j, _len, _len1, _ref2, _ref3; + if (!this.children) { + return this; + } + _ref2 = this.children; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + attr = _ref2[_i]; + if (this[attr]) { + _ref3 = flatten([this[attr]]); + for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { + child = _ref3[_j]; + if (func(child) === false) { + return this; + } + } + } + } + return this; + }; + + Base.prototype.traverseChildren = function(crossScope, func) { + return this.eachChild(function(child) { + if (func(child) === false) { + return false; + } + return child.traverseChildren(crossScope, func); + }); + }; + + Base.prototype.invert = function() { + return new Op('!', this); + }; + + Base.prototype.unwrapAll = function() { + var node; + node = this; + while (node !== (node = node.unwrap())) { + continue; + } + return node; + }; + + Base.prototype.children = []; + + Base.prototype.isStatement = NO; + + Base.prototype.jumps = NO; + + Base.prototype.isComplex = YES; + + Base.prototype.isChainable = NO; + + Base.prototype.isAssignable = NO; + + Base.prototype.unwrap = THIS; + + Base.prototype.unfoldSoak = NO; + + Base.prototype.assigns = NO; + + return Base; + + })(); + + exports.Block = Block = (function(_super) { + + __extends(Block, _super); + + function Block(nodes) { + this.expressions = compact(flatten(nodes || [])); + } + + Block.prototype.children = ['expressions']; + + Block.prototype.push = function(node) { + this.expressions.push(node); + return this; + }; + + Block.prototype.pop = function() { + return this.expressions.pop(); + }; + + Block.prototype.unshift = function(node) { + this.expressions.unshift(node); + return this; + }; + + Block.prototype.unwrap = function() { + if (this.expressions.length === 1) { + return this.expressions[0]; + } else { + return this; + } + }; + + Block.prototype.isEmpty = function() { + return !this.expressions.length; + }; + + Block.prototype.isStatement = function(o) { + var exp, _i, _len, _ref2; + _ref2 = this.expressions; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + exp = _ref2[_i]; + if (exp.isStatement(o)) { + return true; + } + } + return false; + }; + + Block.prototype.jumps = function(o) { + var exp, _i, _len, _ref2; + _ref2 = this.expressions; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + exp = _ref2[_i]; + if (exp.jumps(o)) { + return exp; + } + } + }; + + Block.prototype.makeReturn = function(res) { + var expr, len; + len = this.expressions.length; + while (len--) { + expr = this.expressions[len]; + if (!(expr instanceof Comment)) { + this.expressions[len] = expr.makeReturn(res); + if (expr instanceof Return && !expr.expression) { + this.expressions.splice(len, 1); + } + break; + } + } + return this; + }; + + Block.prototype.compile = function(o, level) { + if (o == null) { + o = {}; + } + if (o.scope) { + return Block.__super__.compile.call(this, o, level); + } else { + return this.compileRoot(o); + } + }; + + Block.prototype.compileNode = function(o) { + var code, codes, node, top, _i, _len, _ref2; + this.tab = o.indent; + top = o.level === LEVEL_TOP; + codes = []; + _ref2 = this.expressions; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + node = _ref2[_i]; + node = node.unwrapAll(); + node = node.unfoldSoak(o) || node; + if (node instanceof Block) { + codes.push(node.compileNode(o)); + } else if (top) { + node.front = true; + code = node.compile(o); + if (!node.isStatement(o)) { + code = "" + this.tab + code + ";"; + if (node instanceof Literal) { + code = "" + code + "\n"; + } + } + codes.push(code); + } else { + codes.push(node.compile(o, LEVEL_LIST)); + } + } + if (top) { + if (this.spaced) { + return "\n" + (codes.join('\n\n')) + "\n"; + } else { + return codes.join('\n'); + } + } + code = codes.join(', ') || 'void 0'; + if (codes.length > 1 && o.level >= LEVEL_LIST) { + return "(" + code + ")"; + } else { + return code; + } + }; + + Block.prototype.compileRoot = function(o) { + var code, exp, i, prelude, preludeExps, rest; + o.indent = o.bare ? '' : TAB; + o.scope = new Scope(null, this, null); + o.level = LEVEL_TOP; + this.spaced = true; + prelude = ""; + if (!o.bare) { + preludeExps = (function() { + var _i, _len, _ref2, _results; + _ref2 = this.expressions; + _results = []; + for (i = _i = 0, _len = _ref2.length; _i < _len; i = ++_i) { + exp = _ref2[i]; + if (!(exp.unwrap() instanceof Comment)) { + break; + } + _results.push(exp); + } + return _results; + }).call(this); + rest = this.expressions.slice(preludeExps.length); + this.expressions = preludeExps; + if (preludeExps.length) { + prelude = "" + (this.compileNode(merge(o, { + indent: '' + }))) + "\n"; + } + this.expressions = rest; + } + code = this.compileWithDeclarations(o); + if (o.bare) { + return code; + } + return "" + prelude + "(function() {\n" + code + "\n}).call(this);\n"; + }; + + Block.prototype.compileWithDeclarations = function(o) { + var assigns, code, declars, exp, i, post, rest, scope, spaced, _i, _len, _ref2, _ref3, _ref4; + code = post = ''; + _ref2 = this.expressions; + for (i = _i = 0, _len = _ref2.length; _i < _len; i = ++_i) { + exp = _ref2[i]; + exp = exp.unwrap(); + if (!(exp instanceof Comment || exp instanceof Literal)) { + break; + } + } + o = merge(o, { + level: LEVEL_TOP + }); + if (i) { + rest = this.expressions.splice(i, 9e9); + _ref3 = [this.spaced, false], spaced = _ref3[0], this.spaced = _ref3[1]; + _ref4 = [this.compileNode(o), spaced], code = _ref4[0], this.spaced = _ref4[1]; + this.expressions = rest; + } + post = this.compileNode(o); + scope = o.scope; + if (scope.expressions === this) { + declars = o.scope.hasDeclarations(); + assigns = scope.hasAssignments; + if (declars || assigns) { + if (i) { + code += '\n'; + } + code += "" + this.tab + "var "; + if (declars) { + code += scope.declaredVariables().join(', '); + } + if (assigns) { + if (declars) { + code += ",\n" + (this.tab + TAB); + } + code += scope.assignedVariables().join(",\n" + (this.tab + TAB)); + } + code += ';\n'; + } + } + return code + post; + }; + + Block.wrap = function(nodes) { + if (nodes.length === 1 && nodes[0] instanceof Block) { + return nodes[0]; + } + return new Block(nodes); + }; + + return Block; + + })(Base); + + exports.Literal = Literal = (function(_super) { + + __extends(Literal, _super); + + function Literal(value) { + this.value = value; + } + + Literal.prototype.makeReturn = function() { + if (this.isStatement()) { + return this; + } else { + return Literal.__super__.makeReturn.apply(this, arguments); + } + }; + + Literal.prototype.isAssignable = function() { + return IDENTIFIER.test(this.value); + }; + + Literal.prototype.isStatement = function() { + var _ref2; + return (_ref2 = this.value) === 'break' || _ref2 === 'continue' || _ref2 === 'debugger'; + }; + + Literal.prototype.isComplex = NO; + + Literal.prototype.assigns = function(name) { + return name === this.value; + }; + + Literal.prototype.jumps = function(o) { + if (this.value === 'break' && !((o != null ? o.loop : void 0) || (o != null ? o.block : void 0))) { + return this; + } + if (this.value === 'continue' && !(o != null ? o.loop : void 0)) { + return this; + } + }; + + Literal.prototype.compileNode = function(o) { + var code, _ref2; + code = this.value === 'this' ? ((_ref2 = o.scope.method) != null ? _ref2.bound : void 0) ? o.scope.method.context : this.value : this.value.reserved ? "\"" + this.value + "\"" : this.value; + if (this.isStatement()) { + return "" + this.tab + code + ";"; + } else { + return code; + } + }; + + Literal.prototype.toString = function() { + return ' "' + this.value + '"'; + }; + + return Literal; + + })(Base); + + exports.Undefined = (function(_super) { + + __extends(Undefined, _super); + + function Undefined() { + return Undefined.__super__.constructor.apply(this, arguments); + } + + Undefined.prototype.isAssignable = NO; + + Undefined.prototype.isComplex = NO; + + Undefined.prototype.compileNode = function(o) { + if (o.level >= LEVEL_ACCESS) { + return '(void 0)'; + } else { + return 'void 0'; + } + }; + + return Undefined; + + })(Base); + + exports.Null = (function(_super) { + + __extends(Null, _super); + + function Null() { + return Null.__super__.constructor.apply(this, arguments); + } + + Null.prototype.isAssignable = NO; + + Null.prototype.isComplex = NO; + + Null.prototype.compileNode = function() { + return "null"; + }; + + return Null; + + })(Base); + + exports.Bool = (function(_super) { + + __extends(Bool, _super); + + Bool.prototype.isAssignable = NO; + + Bool.prototype.isComplex = NO; + + Bool.prototype.compileNode = function() { + return this.val; + }; + + function Bool(val) { + this.val = val; + } + + return Bool; + + })(Base); + + exports.Return = Return = (function(_super) { + + __extends(Return, _super); + + function Return(expr) { + if (expr && !expr.unwrap().isUndefined) { + this.expression = expr; + } + } + + Return.prototype.children = ['expression']; + + Return.prototype.isStatement = YES; + + Return.prototype.makeReturn = THIS; + + Return.prototype.jumps = THIS; + + Return.prototype.compile = function(o, level) { + var expr, _ref2; + expr = (_ref2 = this.expression) != null ? _ref2.makeReturn() : void 0; + if (expr && !(expr instanceof Return)) { + return expr.compile(o, level); + } else { + return Return.__super__.compile.call(this, o, level); + } + }; + + Return.prototype.compileNode = function(o) { + return this.tab + ("return" + [this.expression ? " " + (this.expression.compile(o, LEVEL_PAREN)) : void 0] + ";"); + }; + + return Return; + + })(Base); + + exports.Value = Value = (function(_super) { + + __extends(Value, _super); + + function Value(base, props, tag) { + if (!props && base instanceof Value) { + return base; + } + this.base = base; + this.properties = props || []; + if (tag) { + this[tag] = true; + } + return this; + } + + Value.prototype.children = ['base', 'properties']; + + Value.prototype.add = function(props) { + this.properties = this.properties.concat(props); + return this; + }; + + Value.prototype.hasProperties = function() { + return !!this.properties.length; + }; + + Value.prototype.isArray = function() { + return !this.properties.length && this.base instanceof Arr; + }; + + Value.prototype.isComplex = function() { + return this.hasProperties() || this.base.isComplex(); + }; + + Value.prototype.isAssignable = function() { + return this.hasProperties() || this.base.isAssignable(); + }; + + Value.prototype.isSimpleNumber = function() { + return this.base instanceof Literal && SIMPLENUM.test(this.base.value); + }; + + Value.prototype.isString = function() { + return this.base instanceof Literal && IS_STRING.test(this.base.value); + }; + + Value.prototype.isAtomic = function() { + var node, _i, _len, _ref2; + _ref2 = this.properties.concat(this.base); + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + node = _ref2[_i]; + if (node.soak || node instanceof Call) { + return false; + } + } + return true; + }; + + Value.prototype.isStatement = function(o) { + return !this.properties.length && this.base.isStatement(o); + }; + + Value.prototype.assigns = function(name) { + return !this.properties.length && this.base.assigns(name); + }; + + Value.prototype.jumps = function(o) { + return !this.properties.length && this.base.jumps(o); + }; + + Value.prototype.isObject = function(onlyGenerated) { + if (this.properties.length) { + return false; + } + return (this.base instanceof Obj) && (!onlyGenerated || this.base.generated); + }; + + Value.prototype.isSplice = function() { + return last(this.properties) instanceof Slice; + }; + + Value.prototype.unwrap = function() { + if (this.properties.length) { + return this; + } else { + return this.base; + } + }; + + Value.prototype.cacheReference = function(o) { + var base, bref, name, nref; + name = last(this.properties); + if (this.properties.length < 2 && !this.base.isComplex() && !(name != null ? name.isComplex() : void 0)) { + return [this, this]; + } + base = new Value(this.base, this.properties.slice(0, -1)); + if (base.isComplex()) { + bref = new Literal(o.scope.freeVariable('base')); + base = new Value(new Parens(new Assign(bref, base))); + } + if (!name) { + return [base, bref]; + } + if (name.isComplex()) { + nref = new Literal(o.scope.freeVariable('name')); + name = new Index(new Assign(nref, name.index)); + nref = new Index(nref); + } + return [base.add(name), new Value(bref || base.base, [nref || name])]; + }; + + Value.prototype.compileNode = function(o) { + var code, prop, props, _i, _len; + this.base.front = this.front; + props = this.properties; + code = this.base.compile(o, props.length ? LEVEL_ACCESS : null); + if ((this.base instanceof Parens || props.length) && SIMPLENUM.test(code)) { + code = "" + code + "."; + } + for (_i = 0, _len = props.length; _i < _len; _i++) { + prop = props[_i]; + code += prop.compile(o); + } + return code; + }; + + Value.prototype.unfoldSoak = function(o) { + var result, + _this = this; + if (this.unfoldedSoak != null) { + return this.unfoldedSoak; + } + result = (function() { + var fst, i, ifn, prop, ref, snd, _i, _len, _ref2; + if (ifn = _this.base.unfoldSoak(o)) { + Array.prototype.push.apply(ifn.body.properties, _this.properties); + return ifn; + } + _ref2 = _this.properties; + for (i = _i = 0, _len = _ref2.length; _i < _len; i = ++_i) { + prop = _ref2[i]; + if (!prop.soak) { + continue; + } + prop.soak = false; + fst = new Value(_this.base, _this.properties.slice(0, i)); + snd = new Value(_this.base, _this.properties.slice(i)); + if (fst.isComplex()) { + ref = new Literal(o.scope.freeVariable('ref')); + fst = new Parens(new Assign(ref, fst)); + snd.base = ref; + } + return new If(new Existence(fst), snd, { + soak: true + }); + } + return null; + })(); + return this.unfoldedSoak = result || false; + }; + + return Value; + + })(Base); + + exports.Comment = Comment = (function(_super) { + + __extends(Comment, _super); + + function Comment(comment) { + this.comment = comment; + } + + Comment.prototype.isStatement = YES; + + Comment.prototype.makeReturn = THIS; + + Comment.prototype.compileNode = function(o, level) { + var code; + code = '/*' + multident(this.comment, this.tab) + ("\n" + this.tab + "*/\n"); + if ((level || o.level) === LEVEL_TOP) { + code = o.indent + code; + } + return code; + }; + + return Comment; + + })(Base); + + exports.Call = Call = (function(_super) { + + __extends(Call, _super); + + function Call(variable, args, soak) { + this.args = args != null ? args : []; + this.soak = soak; + this.isNew = false; + this.isSuper = variable === 'super'; + this.variable = this.isSuper ? null : variable; + } + + Call.prototype.children = ['variable', 'args']; + + Call.prototype.newInstance = function() { + var base, _ref2; + base = ((_ref2 = this.variable) != null ? _ref2.base : void 0) || this.variable; + if (base instanceof Call && !base.isNew) { + base.newInstance(); + } else { + this.isNew = true; + } + return this; + }; + + Call.prototype.superReference = function(o) { + var accesses, method, name; + method = o.scope.namedMethod(); + if (!method) { + throw SyntaxError('cannot call super outside of a function.'); + } + name = method.name; + if (name == null) { + throw SyntaxError('cannot call super on an anonymous function.'); + } + if (method.klass) { + accesses = [new Access(new Literal('__super__'))]; + if (method["static"]) { + accesses.push(new Access(new Literal('constructor'))); + } + accesses.push(new Access(new Literal(name))); + return (new Value(new Literal(method.klass), accesses)).compile(o); + } else { + return "" + name + ".__super__.constructor"; + } + }; + + Call.prototype.superThis = function(o) { + var method; + method = o.scope.method; + return (method && !method.klass && method.context) || "this"; + }; + + Call.prototype.unfoldSoak = function(o) { + var call, ifn, left, list, rite, _i, _len, _ref2, _ref3; + if (this.soak) { + if (this.variable) { + if (ifn = unfoldSoak(o, this, 'variable')) { + return ifn; + } + _ref2 = new Value(this.variable).cacheReference(o), left = _ref2[0], rite = _ref2[1]; + } else { + left = new Literal(this.superReference(o)); + rite = new Value(left); + } + rite = new Call(rite, this.args); + rite.isNew = this.isNew; + left = new Literal("typeof " + (left.compile(o)) + " === \"function\""); + return new If(left, new Value(rite), { + soak: true + }); + } + call = this; + list = []; + while (true) { + if (call.variable instanceof Call) { + list.push(call); + call = call.variable; + continue; + } + if (!(call.variable instanceof Value)) { + break; + } + list.push(call); + if (!((call = call.variable.base) instanceof Call)) { + break; + } + } + _ref3 = list.reverse(); + for (_i = 0, _len = _ref3.length; _i < _len; _i++) { + call = _ref3[_i]; + if (ifn) { + if (call.variable instanceof Call) { + call.variable = ifn; + } else { + call.variable.base = ifn; + } + } + ifn = unfoldSoak(o, call, 'variable'); + } + return ifn; + }; + + Call.prototype.filterImplicitObjects = function(list) { + var node, nodes, obj, prop, properties, _i, _j, _len, _len1, _ref2; + nodes = []; + for (_i = 0, _len = list.length; _i < _len; _i++) { + node = list[_i]; + if (!((typeof node.isObject === "function" ? node.isObject() : void 0) && node.base.generated)) { + nodes.push(node); + continue; + } + obj = null; + _ref2 = node.base.properties; + for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { + prop = _ref2[_j]; + if (prop instanceof Assign || prop instanceof Comment) { + if (!obj) { + nodes.push(obj = new Obj(properties = [], true)); + } + properties.push(prop); + } else { + nodes.push(prop); + obj = null; + } + } + } + return nodes; + }; + + Call.prototype.compileNode = function(o) { + var arg, args, code, _ref2; + if ((_ref2 = this.variable) != null) { + _ref2.front = this.front; + } + if (code = Splat.compileSplattedArray(o, this.args, true)) { + return this.compileSplat(o, code); + } + args = this.filterImplicitObjects(this.args); + args = ((function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = args.length; _i < _len; _i++) { + arg = args[_i]; + _results.push(arg.compile(o, LEVEL_LIST)); + } + return _results; + })()).join(', '); + if (this.isSuper) { + return this.superReference(o) + (".call(" + (this.superThis(o)) + (args && ', ' + args) + ")"); + } else { + return (this.isNew ? 'new ' : '') + this.variable.compile(o, LEVEL_ACCESS) + ("(" + args + ")"); + } + }; + + Call.prototype.compileSuper = function(args, o) { + return "" + (this.superReference(o)) + ".call(" + (this.superThis(o)) + (args.length ? ', ' : '') + args + ")"; + }; + + Call.prototype.compileSplat = function(o, splatArgs) { + var base, fun, idt, name, ref; + if (this.isSuper) { + return "" + (this.superReference(o)) + ".apply(" + (this.superThis(o)) + ", " + splatArgs + ")"; + } + if (this.isNew) { + idt = this.tab + TAB; + return "(function(func, args, ctor) {\n" + idt + "ctor.prototype = func.prototype;\n" + idt + "var child = new ctor, result = func.apply(child, args);\n" + idt + "return Object(result) === result ? result : child;\n" + this.tab + "})(" + (this.variable.compile(o, LEVEL_LIST)) + ", " + splatArgs + ", function(){})"; + } + base = new Value(this.variable); + if ((name = base.properties.pop()) && base.isComplex()) { + ref = o.scope.freeVariable('ref'); + fun = "(" + ref + " = " + (base.compile(o, LEVEL_LIST)) + ")" + (name.compile(o)); + } else { + fun = base.compile(o, LEVEL_ACCESS); + if (SIMPLENUM.test(fun)) { + fun = "(" + fun + ")"; + } + if (name) { + ref = fun; + fun += name.compile(o); + } else { + ref = 'null'; + } + } + return "" + fun + ".apply(" + ref + ", " + splatArgs + ")"; + }; + + return Call; + + })(Base); + + exports.Extends = Extends = (function(_super) { + + __extends(Extends, _super); + + function Extends(child, parent) { + this.child = child; + this.parent = parent; + } + + Extends.prototype.children = ['child', 'parent']; + + Extends.prototype.compile = function(o) { + return new Call(new Value(new Literal(utility('extends'))), [this.child, this.parent]).compile(o); + }; + + return Extends; + + })(Base); + + exports.Access = Access = (function(_super) { + + __extends(Access, _super); + + function Access(name, tag) { + this.name = name; + this.name.asKey = true; + this.soak = tag === 'soak'; + } + + Access.prototype.children = ['name']; + + Access.prototype.compile = function(o) { + var name; + name = this.name.compile(o); + if (IDENTIFIER.test(name)) { + return "." + name; + } else { + return "[" + name + "]"; + } + }; + + Access.prototype.isComplex = NO; + + return Access; + + })(Base); + + exports.Index = Index = (function(_super) { + + __extends(Index, _super); + + function Index(index) { + this.index = index; + } + + Index.prototype.children = ['index']; + + Index.prototype.compile = function(o) { + return "[" + (this.index.compile(o, LEVEL_PAREN)) + "]"; + }; + + Index.prototype.isComplex = function() { + return this.index.isComplex(); + }; + + return Index; + + })(Base); + + exports.Range = Range = (function(_super) { + + __extends(Range, _super); + + Range.prototype.children = ['from', 'to']; + + function Range(from, to, tag) { + this.from = from; + this.to = to; + this.exclusive = tag === 'exclusive'; + this.equals = this.exclusive ? '' : '='; + } + + Range.prototype.compileVariables = function(o) { + var step, _ref2, _ref3, _ref4, _ref5; + o = merge(o, { + top: true + }); + _ref2 = this.from.cache(o, LEVEL_LIST), this.fromC = _ref2[0], this.fromVar = _ref2[1]; + _ref3 = this.to.cache(o, LEVEL_LIST), this.toC = _ref3[0], this.toVar = _ref3[1]; + if (step = del(o, 'step')) { + _ref4 = step.cache(o, LEVEL_LIST), this.step = _ref4[0], this.stepVar = _ref4[1]; + } + _ref5 = [this.fromVar.match(SIMPLENUM), this.toVar.match(SIMPLENUM)], this.fromNum = _ref5[0], this.toNum = _ref5[1]; + if (this.stepVar) { + return this.stepNum = this.stepVar.match(SIMPLENUM); + } + }; + + Range.prototype.compileNode = function(o) { + var cond, condPart, from, gt, idx, idxName, known, lt, namedIndex, stepPart, to, varPart, _ref2, _ref3; + if (!this.fromVar) { + this.compileVariables(o); + } + if (!o.index) { + return this.compileArray(o); + } + known = this.fromNum && this.toNum; + idx = del(o, 'index'); + idxName = del(o, 'name'); + namedIndex = idxName && idxName !== idx; + varPart = "" + idx + " = " + this.fromC; + if (this.toC !== this.toVar) { + varPart += ", " + this.toC; + } + if (this.step !== this.stepVar) { + varPart += ", " + this.step; + } + _ref2 = ["" + idx + " <" + this.equals, "" + idx + " >" + this.equals], lt = _ref2[0], gt = _ref2[1]; + condPart = this.stepNum ? +this.stepNum > 0 ? "" + lt + " " + this.toVar : "" + gt + " " + this.toVar : known ? ((_ref3 = [+this.fromNum, +this.toNum], from = _ref3[0], to = _ref3[1], _ref3), from <= to ? "" + lt + " " + to : "" + gt + " " + to) : (cond = "" + this.fromVar + " <= " + this.toVar, "" + cond + " ? " + lt + " " + this.toVar + " : " + gt + " " + this.toVar); + stepPart = this.stepVar ? "" + idx + " += " + this.stepVar : known ? namedIndex ? from <= to ? "++" + idx : "--" + idx : from <= to ? "" + idx + "++" : "" + idx + "--" : namedIndex ? "" + cond + " ? ++" + idx + " : --" + idx : "" + cond + " ? " + idx + "++ : " + idx + "--"; + if (namedIndex) { + varPart = "" + idxName + " = " + varPart; + } + if (namedIndex) { + stepPart = "" + idxName + " = " + stepPart; + } + return "" + varPart + "; " + condPart + "; " + stepPart; + }; + + Range.prototype.compileArray = function(o) { + var args, body, cond, hasArgs, i, idt, post, pre, range, result, vars, _i, _ref2, _ref3, _results; + if (this.fromNum && this.toNum && Math.abs(this.fromNum - this.toNum) <= 20) { + range = (function() { + _results = []; + for (var _i = _ref2 = +this.fromNum, _ref3 = +this.toNum; _ref2 <= _ref3 ? _i <= _ref3 : _i >= _ref3; _ref2 <= _ref3 ? _i++ : _i--){ _results.push(_i); } + return _results; + }).apply(this); + if (this.exclusive) { + range.pop(); + } + return "[" + (range.join(', ')) + "]"; + } + idt = this.tab + TAB; + i = o.scope.freeVariable('i'); + result = o.scope.freeVariable('results'); + pre = "\n" + idt + result + " = [];"; + if (this.fromNum && this.toNum) { + o.index = i; + body = this.compileNode(o); + } else { + vars = ("" + i + " = " + this.fromC) + (this.toC !== this.toVar ? ", " + this.toC : ''); + cond = "" + this.fromVar + " <= " + this.toVar; + body = "var " + vars + "; " + cond + " ? " + i + " <" + this.equals + " " + this.toVar + " : " + i + " >" + this.equals + " " + this.toVar + "; " + cond + " ? " + i + "++ : " + i + "--"; + } + post = "{ " + result + ".push(" + i + "); }\n" + idt + "return " + result + ";\n" + o.indent; + hasArgs = function(node) { + return node != null ? node.contains(function(n) { + return n instanceof Literal && n.value === 'arguments' && !n.asKey; + }) : void 0; + }; + if (hasArgs(this.from) || hasArgs(this.to)) { + args = ', arguments'; + } + return "(function() {" + pre + "\n" + idt + "for (" + body + ")" + post + "}).apply(this" + (args != null ? args : '') + ")"; + }; + + return Range; + + })(Base); + + exports.Slice = Slice = (function(_super) { + + __extends(Slice, _super); + + Slice.prototype.children = ['range']; + + function Slice(range) { + this.range = range; + Slice.__super__.constructor.call(this); + } + + Slice.prototype.compileNode = function(o) { + var compiled, from, fromStr, to, toStr, _ref2; + _ref2 = this.range, to = _ref2.to, from = _ref2.from; + fromStr = from && from.compile(o, LEVEL_PAREN) || '0'; + compiled = to && to.compile(o, LEVEL_PAREN); + if (to && !(!this.range.exclusive && +compiled === -1)) { + toStr = ', ' + (this.range.exclusive ? compiled : SIMPLENUM.test(compiled) ? "" + (+compiled + 1) : (compiled = to.compile(o, LEVEL_ACCESS), "+" + compiled + " + 1 || 9e9")); + } + return ".slice(" + fromStr + (toStr || '') + ")"; + }; + + return Slice; + + })(Base); + + exports.Obj = Obj = (function(_super) { + + __extends(Obj, _super); + + function Obj(props, generated) { + this.generated = generated != null ? generated : false; + this.objects = this.properties = props || []; + } + + Obj.prototype.children = ['properties']; + + Obj.prototype.compileNode = function(o) { + var i, idt, indent, join, lastNoncom, node, obj, prop, props, _i, _len; + props = this.properties; + if (!props.length) { + return (this.front ? '({})' : '{}'); + } + if (this.generated) { + for (_i = 0, _len = props.length; _i < _len; _i++) { + node = props[_i]; + if (node instanceof Value) { + throw new Error('cannot have an implicit value in an implicit object'); + } + } + } + idt = o.indent += TAB; + lastNoncom = this.lastNonComment(this.properties); + props = (function() { + var _j, _len1, _results; + _results = []; + for (i = _j = 0, _len1 = props.length; _j < _len1; i = ++_j) { + prop = props[i]; + join = i === props.length - 1 ? '' : prop === lastNoncom || prop instanceof Comment ? '\n' : ',\n'; + indent = prop instanceof Comment ? '' : idt; + if (prop instanceof Value && prop["this"]) { + prop = new Assign(prop.properties[0].name, prop, 'object'); + } + if (!(prop instanceof Comment)) { + if (!(prop instanceof Assign)) { + prop = new Assign(prop, prop, 'object'); + } + (prop.variable.base || prop.variable).asKey = true; + } + _results.push(indent + prop.compile(o, LEVEL_TOP) + join); + } + return _results; + })(); + props = props.join(''); + obj = "{" + (props && '\n' + props + '\n' + this.tab) + "}"; + if (this.front) { + return "(" + obj + ")"; + } else { + return obj; + } + }; + + Obj.prototype.assigns = function(name) { + var prop, _i, _len, _ref2; + _ref2 = this.properties; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + prop = _ref2[_i]; + if (prop.assigns(name)) { + return true; + } + } + return false; + }; + + return Obj; + + })(Base); + + exports.Arr = Arr = (function(_super) { + + __extends(Arr, _super); + + function Arr(objs) { + this.objects = objs || []; + } + + Arr.prototype.children = ['objects']; + + Arr.prototype.filterImplicitObjects = Call.prototype.filterImplicitObjects; + + Arr.prototype.compileNode = function(o) { + var code, obj, objs; + if (!this.objects.length) { + return '[]'; + } + o.indent += TAB; + objs = this.filterImplicitObjects(this.objects); + if (code = Splat.compileSplattedArray(o, objs)) { + return code; + } + code = ((function() { + var _i, _len, _results; + _results = []; + for (_i = 0, _len = objs.length; _i < _len; _i++) { + obj = objs[_i]; + _results.push(obj.compile(o, LEVEL_LIST)); + } + return _results; + })()).join(', '); + if (code.indexOf('\n') >= 0) { + return "[\n" + o.indent + code + "\n" + this.tab + "]"; + } else { + return "[" + code + "]"; + } + }; + + Arr.prototype.assigns = function(name) { + var obj, _i, _len, _ref2; + _ref2 = this.objects; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + obj = _ref2[_i]; + if (obj.assigns(name)) { + return true; + } + } + return false; + }; + + return Arr; + + })(Base); + + exports.Class = Class = (function(_super) { + + __extends(Class, _super); + + function Class(variable, parent, body) { + this.variable = variable; + this.parent = parent; + this.body = body != null ? body : new Block; + this.boundFuncs = []; + this.body.classBody = true; + } + + Class.prototype.children = ['variable', 'parent', 'body']; + + Class.prototype.determineName = function() { + var decl, tail; + if (!this.variable) { + return null; + } + decl = (tail = last(this.variable.properties)) ? tail instanceof Access && tail.name.value : this.variable.base.value; + if (__indexOf.call(STRICT_PROSCRIBED, decl) >= 0) { + throw SyntaxError("variable name may not be " + decl); + } + return decl && (decl = IDENTIFIER.test(decl) && decl); + }; + + Class.prototype.setContext = function(name) { + return this.body.traverseChildren(false, function(node) { + if (node.classBody) { + return false; + } + if (node instanceof Literal && node.value === 'this') { + return node.value = name; + } else if (node instanceof Code) { + node.klass = name; + if (node.bound) { + return node.context = name; + } + } + }); + }; + + Class.prototype.addBoundFunctions = function(o) { + var bvar, lhs, _i, _len, _ref2, _results; + if (this.boundFuncs.length) { + _ref2 = this.boundFuncs; + _results = []; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + bvar = _ref2[_i]; + lhs = (new Value(new Literal("this"), [new Access(bvar)])).compile(o); + _results.push(this.ctor.body.unshift(new Literal("" + lhs + " = " + (utility('bind')) + "(" + lhs + ", this)"))); + } + return _results; + } + }; + + Class.prototype.addProperties = function(node, name, o) { + var assign, base, exprs, func, props; + props = node.base.properties.slice(0); + exprs = (function() { + var _results; + _results = []; + while (assign = props.shift()) { + if (assign instanceof Assign) { + base = assign.variable.base; + delete assign.context; + func = assign.value; + if (base.value === 'constructor') { + if (this.ctor) { + throw new Error('cannot define more than one constructor in a class'); + } + if (func.bound) { + throw new Error('cannot define a constructor as a bound function'); + } + if (func instanceof Code) { + assign = this.ctor = func; + } else { + this.externalCtor = o.scope.freeVariable('class'); + assign = new Assign(new Literal(this.externalCtor), func); + } + } else { + if (assign.variable["this"]) { + func["static"] = true; + if (func.bound) { + func.context = name; + } + } else { + assign.variable = new Value(new Literal(name), [new Access(new Literal('prototype')), new Access(base)]); + if (func instanceof Code && func.bound) { + this.boundFuncs.push(base); + func.bound = false; + } + } + } + } + _results.push(assign); + } + return _results; + }).call(this); + return compact(exprs); + }; + + Class.prototype.walkBody = function(name, o) { + var _this = this; + return this.traverseChildren(false, function(child) { + var exps, i, node, _i, _len, _ref2; + if (child instanceof Class) { + return false; + } + if (child instanceof Block) { + _ref2 = exps = child.expressions; + for (i = _i = 0, _len = _ref2.length; _i < _len; i = ++_i) { + node = _ref2[i]; + if (node instanceof Value && node.isObject(true)) { + exps[i] = _this.addProperties(node, name, o); + } + } + return child.expressions = exps = flatten(exps); + } + }); + }; + + Class.prototype.hoistDirectivePrologue = function() { + var expressions, index, node; + index = 0; + expressions = this.body.expressions; + while ((node = expressions[index]) && node instanceof Comment || node instanceof Value && node.isString()) { + ++index; + } + return this.directives = expressions.splice(0, index); + }; + + Class.prototype.ensureConstructor = function(name) { + if (!this.ctor) { + this.ctor = new Code; + if (this.parent) { + this.ctor.body.push(new Literal("" + name + ".__super__.constructor.apply(this, arguments)")); + } + if (this.externalCtor) { + this.ctor.body.push(new Literal("" + this.externalCtor + ".apply(this, arguments)")); + } + this.ctor.body.makeReturn(); + this.body.expressions.unshift(this.ctor); + } + this.ctor.ctor = this.ctor.name = name; + this.ctor.klass = null; + return this.ctor.noReturn = true; + }; + + Class.prototype.compileNode = function(o) { + var call, decl, klass, lname, name, params, _ref2; + decl = this.determineName(); + name = decl || '_Class'; + if (name.reserved) { + name = "_" + name; + } + lname = new Literal(name); + this.hoistDirectivePrologue(); + this.setContext(name); + this.walkBody(name, o); + this.ensureConstructor(name); + this.body.spaced = true; + if (!(this.ctor instanceof Code)) { + this.body.expressions.unshift(this.ctor); + } + this.body.expressions.push(lname); + (_ref2 = this.body.expressions).unshift.apply(_ref2, this.directives); + this.addBoundFunctions(o); + call = Closure.wrap(this.body); + if (this.parent) { + this.superClass = new Literal(o.scope.freeVariable('super', false)); + this.body.expressions.unshift(new Extends(lname, this.superClass)); + call.args.push(this.parent); + params = call.variable.params || call.variable.base.params; + params.push(new Param(this.superClass)); + } + klass = new Parens(call, true); + if (this.variable) { + klass = new Assign(this.variable, klass); + } + return klass.compile(o); + }; + + return Class; + + })(Base); + + exports.Assign = Assign = (function(_super) { + + __extends(Assign, _super); + + function Assign(variable, value, context, options) { + var forbidden, name, _ref2; + this.variable = variable; + this.value = value; + this.context = context; + this.param = options && options.param; + this.subpattern = options && options.subpattern; + forbidden = (_ref2 = (name = this.variable.unwrapAll().value), __indexOf.call(STRICT_PROSCRIBED, _ref2) >= 0); + if (forbidden && this.context !== 'object') { + throw SyntaxError("variable name may not be \"" + name + "\""); + } + } + + Assign.prototype.children = ['variable', 'value']; + + Assign.prototype.isStatement = function(o) { + return (o != null ? o.level : void 0) === LEVEL_TOP && (this.context != null) && __indexOf.call(this.context, "?") >= 0; + }; + + Assign.prototype.assigns = function(name) { + return this[this.context === 'object' ? 'value' : 'variable'].assigns(name); + }; + + Assign.prototype.unfoldSoak = function(o) { + return unfoldSoak(o, this, 'variable'); + }; + + Assign.prototype.compileNode = function(o) { + var isValue, match, name, val, varBase, _ref2, _ref3, _ref4, _ref5; + if (isValue = this.variable instanceof Value) { + if (this.variable.isArray() || this.variable.isObject()) { + return this.compilePatternMatch(o); + } + if (this.variable.isSplice()) { + return this.compileSplice(o); + } + if ((_ref2 = this.context) === '||=' || _ref2 === '&&=' || _ref2 === '?=') { + return this.compileConditional(o); + } + } + name = this.variable.compile(o, LEVEL_LIST); + if (!this.context) { + if (!(varBase = this.variable.unwrapAll()).isAssignable()) { + throw SyntaxError("\"" + (this.variable.compile(o)) + "\" cannot be assigned."); + } + if (!(typeof varBase.hasProperties === "function" ? varBase.hasProperties() : void 0)) { + if (this.param) { + o.scope.add(name, 'var'); + } else { + o.scope.find(name); + } + } + } + if (this.value instanceof Code && (match = METHOD_DEF.exec(name))) { + if (match[1]) { + this.value.klass = match[1]; + } + this.value.name = (_ref3 = (_ref4 = (_ref5 = match[2]) != null ? _ref5 : match[3]) != null ? _ref4 : match[4]) != null ? _ref3 : match[5]; + } + val = this.value.compile(o, LEVEL_LIST); + if (this.context === 'object') { + return "" + name + ": " + val; + } + val = name + (" " + (this.context || '=') + " ") + val; + if (o.level <= LEVEL_LIST) { + return val; + } else { + return "(" + val + ")"; + } + }; + + Assign.prototype.compilePatternMatch = function(o) { + var acc, assigns, code, i, idx, isObject, ivar, name, obj, objects, olen, ref, rest, splat, top, val, value, vvar, _i, _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8; + top = o.level === LEVEL_TOP; + value = this.value; + objects = this.variable.base.objects; + if (!(olen = objects.length)) { + code = value.compile(o); + if (o.level >= LEVEL_OP) { + return "(" + code + ")"; + } else { + return code; + } + } + isObject = this.variable.isObject(); + if (top && olen === 1 && !((obj = objects[0]) instanceof Splat)) { + if (obj instanceof Assign) { + _ref2 = obj, (_ref3 = _ref2.variable, idx = _ref3.base), obj = _ref2.value; + } else { + if (obj.base instanceof Parens) { + _ref4 = new Value(obj.unwrapAll()).cacheReference(o), obj = _ref4[0], idx = _ref4[1]; + } else { + idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new Literal(0); + } + } + acc = IDENTIFIER.test(idx.unwrap().value || 0); + value = new Value(value); + value.properties.push(new (acc ? Access : Index)(idx)); + if (_ref5 = obj.unwrap().value, __indexOf.call(RESERVED, _ref5) >= 0) { + throw new SyntaxError("assignment to a reserved word: " + (obj.compile(o)) + " = " + (value.compile(o))); + } + return new Assign(obj, value, null, { + param: this.param + }).compile(o, LEVEL_TOP); + } + vvar = value.compile(o, LEVEL_LIST); + assigns = []; + splat = false; + if (!IDENTIFIER.test(vvar) || this.variable.assigns(vvar)) { + assigns.push("" + (ref = o.scope.freeVariable('ref')) + " = " + vvar); + vvar = ref; + } + for (i = _i = 0, _len = objects.length; _i < _len; i = ++_i) { + obj = objects[i]; + idx = i; + if (isObject) { + if (obj instanceof Assign) { + _ref6 = obj, (_ref7 = _ref6.variable, idx = _ref7.base), obj = _ref6.value; + } else { + if (obj.base instanceof Parens) { + _ref8 = new Value(obj.unwrapAll()).cacheReference(o), obj = _ref8[0], idx = _ref8[1]; + } else { + idx = obj["this"] ? obj.properties[0].name : obj; + } + } + } + if (!splat && obj instanceof Splat) { + name = obj.name.unwrap().value; + obj = obj.unwrap(); + val = "" + olen + " <= " + vvar + ".length ? " + (utility('slice')) + ".call(" + vvar + ", " + i; + if (rest = olen - i - 1) { + ivar = o.scope.freeVariable('i'); + val += ", " + ivar + " = " + vvar + ".length - " + rest + ") : (" + ivar + " = " + i + ", [])"; + } else { + val += ") : []"; + } + val = new Literal(val); + splat = "" + ivar + "++"; + } else { + name = obj.unwrap().value; + if (obj instanceof Splat) { + obj = obj.name.compile(o); + throw new SyntaxError("multiple splats are disallowed in an assignment: " + obj + "..."); + } + if (typeof idx === 'number') { + idx = new Literal(splat || idx); + acc = false; + } else { + acc = isObject && IDENTIFIER.test(idx.unwrap().value || 0); + } + val = new Value(new Literal(vvar), [new (acc ? Access : Index)(idx)]); + } + if ((name != null) && __indexOf.call(RESERVED, name) >= 0) { + throw new SyntaxError("assignment to a reserved word: " + (obj.compile(o)) + " = " + (val.compile(o))); + } + assigns.push(new Assign(obj, val, null, { + param: this.param, + subpattern: true + }).compile(o, LEVEL_LIST)); + } + if (!(top || this.subpattern)) { + assigns.push(vvar); + } + code = assigns.join(', '); + if (o.level < LEVEL_LIST) { + return code; + } else { + return "(" + code + ")"; + } + }; + + Assign.prototype.compileConditional = function(o) { + var left, right, _ref2; + _ref2 = this.variable.cacheReference(o), left = _ref2[0], right = _ref2[1]; + if (!left.properties.length && left.base instanceof Literal && left.base.value !== "this" && !o.scope.check(left.base.value)) { + throw new Error("the variable \"" + left.base.value + "\" can't be assigned with " + this.context + " because it has not been defined."); + } + if (__indexOf.call(this.context, "?") >= 0) { + o.isExistentialEquals = true; + } + return new Op(this.context.slice(0, -1), left, new Assign(right, this.value, '=')).compile(o); + }; + + Assign.prototype.compileSplice = function(o) { + var code, exclusive, from, fromDecl, fromRef, name, to, valDef, valRef, _ref2, _ref3, _ref4; + _ref2 = this.variable.properties.pop().range, from = _ref2.from, to = _ref2.to, exclusive = _ref2.exclusive; + name = this.variable.compile(o); + _ref3 = (from != null ? from.cache(o, LEVEL_OP) : void 0) || ['0', '0'], fromDecl = _ref3[0], fromRef = _ref3[1]; + if (to) { + if ((from != null ? from.isSimpleNumber() : void 0) && to.isSimpleNumber()) { + to = +to.compile(o) - +fromRef; + if (!exclusive) { + to += 1; + } + } else { + to = to.compile(o, LEVEL_ACCESS) + ' - ' + fromRef; + if (!exclusive) { + to += ' + 1'; + } + } + } else { + to = "9e9"; + } + _ref4 = this.value.cache(o, LEVEL_LIST), valDef = _ref4[0], valRef = _ref4[1]; + code = "[].splice.apply(" + name + ", [" + fromDecl + ", " + to + "].concat(" + valDef + ")), " + valRef; + if (o.level > LEVEL_TOP) { + return "(" + code + ")"; + } else { + return code; + } + }; + + return Assign; + + })(Base); + + exports.Code = Code = (function(_super) { + + __extends(Code, _super); + + function Code(params, body, tag) { + this.params = params || []; + this.body = body || new Block; + this.bound = tag === 'boundfunc'; + if (this.bound) { + this.context = '_this'; + } + } + + Code.prototype.children = ['params', 'body']; + + Code.prototype.isStatement = function() { + return !!this.ctor; + }; + + Code.prototype.jumps = NO; + + Code.prototype.compileNode = function(o) { + var code, exprs, i, idt, lit, name, p, param, params, ref, splats, uniqs, val, wasEmpty, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _m, _n, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8; + o.scope = new Scope(o.scope, this.body, this); + o.scope.shared = del(o, 'sharedScope'); + o.indent += TAB; + delete o.bare; + delete o.isExistentialEquals; + params = []; + exprs = []; + _ref2 = this.paramNames(); + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + name = _ref2[_i]; + if (!o.scope.check(name)) { + o.scope.parameter(name); + } + } + _ref3 = this.params; + for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) { + param = _ref3[_j]; + if (!param.splat) { + continue; + } + _ref4 = this.params; + for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) { + p = _ref4[_k].name; + if (p["this"]) { + p = p.properties[0].name; + } + if (p.value) { + o.scope.add(p.value, 'var', true); + } + } + splats = new Assign(new Value(new Arr((function() { + var _l, _len3, _ref5, _results; + _ref5 = this.params; + _results = []; + for (_l = 0, _len3 = _ref5.length; _l < _len3; _l++) { + p = _ref5[_l]; + _results.push(p.asReference(o)); + } + return _results; + }).call(this))), new Value(new Literal('arguments'))); + break; + } + _ref5 = this.params; + for (_l = 0, _len3 = _ref5.length; _l < _len3; _l++) { + param = _ref5[_l]; + if (param.isComplex()) { + val = ref = param.asReference(o); + if (param.value) { + val = new Op('?', ref, param.value); + } + exprs.push(new Assign(new Value(param.name), val, '=', { + param: true + })); + } else { + ref = param; + if (param.value) { + lit = new Literal(ref.name.value + ' == null'); + val = new Assign(new Value(param.name), param.value, '='); + exprs.push(new If(lit, val)); + } + } + if (!splats) { + params.push(ref); + } + } + wasEmpty = this.body.isEmpty(); + if (splats) { + exprs.unshift(splats); + } + if (exprs.length) { + (_ref6 = this.body.expressions).unshift.apply(_ref6, exprs); + } + for (i = _m = 0, _len4 = params.length; _m < _len4; i = ++_m) { + p = params[i]; + o.scope.parameter(params[i] = p.compile(o)); + } + uniqs = []; + _ref7 = this.paramNames(); + for (_n = 0, _len5 = _ref7.length; _n < _len5; _n++) { + name = _ref7[_n]; + if (__indexOf.call(uniqs, name) >= 0) { + throw SyntaxError("multiple parameters named '" + name + "'"); + } + uniqs.push(name); + } + if (!(wasEmpty || this.noReturn)) { + this.body.makeReturn(); + } + if (this.bound) { + if ((_ref8 = o.scope.parent.method) != null ? _ref8.bound : void 0) { + this.bound = this.context = o.scope.parent.method.context; + } else if (!this["static"]) { + o.scope.parent.assign('_this', 'this'); + } + } + idt = o.indent; + code = 'function'; + if (this.ctor) { + code += ' ' + this.name; + } + code += '(' + params.join(', ') + ') {'; + if (!this.body.isEmpty()) { + code += "\n" + (this.body.compileWithDeclarations(o)) + "\n" + this.tab; + } + code += '}'; + if (this.ctor) { + return this.tab + code; + } + if (this.front || (o.level >= LEVEL_ACCESS)) { + return "(" + code + ")"; + } else { + return code; + } + }; + + Code.prototype.paramNames = function() { + var names, param, _i, _len, _ref2; + names = []; + _ref2 = this.params; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + param = _ref2[_i]; + names.push.apply(names, param.names()); + } + return names; + }; + + Code.prototype.traverseChildren = function(crossScope, func) { + if (crossScope) { + return Code.__super__.traverseChildren.call(this, crossScope, func); + } + }; + + return Code; + + })(Base); + + exports.Param = Param = (function(_super) { + + __extends(Param, _super); + + function Param(name, value, splat) { + var _ref2; + this.name = name; + this.value = value; + this.splat = splat; + if (_ref2 = (name = this.name.unwrapAll().value), __indexOf.call(STRICT_PROSCRIBED, _ref2) >= 0) { + throw SyntaxError("parameter name \"" + name + "\" is not allowed"); + } + } + + Param.prototype.children = ['name', 'value']; + + Param.prototype.compile = function(o) { + return this.name.compile(o, LEVEL_LIST); + }; + + Param.prototype.asReference = function(o) { + var node; + if (this.reference) { + return this.reference; + } + node = this.name; + if (node["this"]) { + node = node.properties[0].name; + if (node.value.reserved) { + node = new Literal(o.scope.freeVariable(node.value)); + } + } else if (node.isComplex()) { + node = new Literal(o.scope.freeVariable('arg')); + } + node = new Value(node); + if (this.splat) { + node = new Splat(node); + } + return this.reference = node; + }; + + Param.prototype.isComplex = function() { + return this.name.isComplex(); + }; + + Param.prototype.names = function(name) { + var atParam, names, obj, _i, _len, _ref2; + if (name == null) { + name = this.name; + } + atParam = function(obj) { + var value; + value = obj.properties[0].name.value; + if (value.reserved) { + return []; + } else { + return [value]; + } + }; + if (name instanceof Literal) { + return [name.value]; + } + if (name instanceof Value) { + return atParam(name); + } + names = []; + _ref2 = name.objects; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + obj = _ref2[_i]; + if (obj instanceof Assign) { + names.push(obj.value.unwrap().value); + } else if (obj instanceof Splat) { + names.push(obj.name.unwrap().value); + } else if (obj instanceof Value) { + if (obj.isArray() || obj.isObject()) { + names.push.apply(names, this.names(obj.base)); + } else if (obj["this"]) { + names.push.apply(names, atParam(obj)); + } else { + names.push(obj.base.value); + } + } else { + throw SyntaxError("illegal parameter " + (obj.compile())); + } + } + return names; + }; + + return Param; + + })(Base); + + exports.Splat = Splat = (function(_super) { + + __extends(Splat, _super); + + Splat.prototype.children = ['name']; + + Splat.prototype.isAssignable = YES; + + function Splat(name) { + this.name = name.compile ? name : new Literal(name); + } + + Splat.prototype.assigns = function(name) { + return this.name.assigns(name); + }; + + Splat.prototype.compile = function(o) { + if (this.index != null) { + return this.compileParam(o); + } else { + return this.name.compile(o); + } + }; + + Splat.prototype.unwrap = function() { + return this.name; + }; + + Splat.compileSplattedArray = function(o, list, apply) { + var args, base, code, i, index, node, _i, _len; + index = -1; + while ((node = list[++index]) && !(node instanceof Splat)) { + continue; + } + if (index >= list.length) { + return ''; + } + if (list.length === 1) { + code = list[0].compile(o, LEVEL_LIST); + if (apply) { + return code; + } + return "" + (utility('slice')) + ".call(" + code + ")"; + } + args = list.slice(index); + for (i = _i = 0, _len = args.length; _i < _len; i = ++_i) { + node = args[i]; + code = node.compile(o, LEVEL_LIST); + args[i] = node instanceof Splat ? "" + (utility('slice')) + ".call(" + code + ")" : "[" + code + "]"; + } + if (index === 0) { + return args[0] + (".concat(" + (args.slice(1).join(', ')) + ")"); + } + base = (function() { + var _j, _len1, _ref2, _results; + _ref2 = list.slice(0, index); + _results = []; + for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) { + node = _ref2[_j]; + _results.push(node.compile(o, LEVEL_LIST)); + } + return _results; + })(); + return "[" + (base.join(', ')) + "].concat(" + (args.join(', ')) + ")"; + }; + + return Splat; + + })(Base); + + exports.While = While = (function(_super) { + + __extends(While, _super); + + function While(condition, options) { + this.condition = (options != null ? options.invert : void 0) ? condition.invert() : condition; + this.guard = options != null ? options.guard : void 0; + } + + While.prototype.children = ['condition', 'guard', 'body']; + + While.prototype.isStatement = YES; + + While.prototype.makeReturn = function(res) { + if (res) { + return While.__super__.makeReturn.apply(this, arguments); + } else { + this.returns = !this.jumps({ + loop: true + }); + return this; + } + }; + + While.prototype.addBody = function(body) { + this.body = body; + return this; + }; + + While.prototype.jumps = function() { + var expressions, node, _i, _len; + expressions = this.body.expressions; + if (!expressions.length) { + return false; + } + for (_i = 0, _len = expressions.length; _i < _len; _i++) { + node = expressions[_i]; + if (node.jumps({ + loop: true + })) { + return node; + } + } + return false; + }; + + While.prototype.compileNode = function(o) { + var body, code, rvar, set; + o.indent += TAB; + set = ''; + body = this.body; + if (body.isEmpty()) { + body = ''; + } else { + if (this.returns) { + body.makeReturn(rvar = o.scope.freeVariable('results')); + set = "" + this.tab + rvar + " = [];\n"; + } + if (this.guard) { + if (body.expressions.length > 1) { + body.expressions.unshift(new If((new Parens(this.guard)).invert(), new Literal("continue"))); + } else { + if (this.guard) { + body = Block.wrap([new If(this.guard, body)]); + } + } + } + body = "\n" + (body.compile(o, LEVEL_TOP)) + "\n" + this.tab; + } + code = set + this.tab + ("while (" + (this.condition.compile(o, LEVEL_PAREN)) + ") {" + body + "}"); + if (this.returns) { + code += "\n" + this.tab + "return " + rvar + ";"; + } + return code; + }; + + return While; + + })(Base); + + exports.Op = Op = (function(_super) { + var CONVERSIONS, INVERSIONS; + + __extends(Op, _super); + + function Op(op, first, second, flip) { + if (op === 'in') { + return new In(first, second); + } + if (op === 'do') { + return this.generateDo(first); + } + if (op === 'new') { + if (first instanceof Call && !first["do"] && !first.isNew) { + return first.newInstance(); + } + if (first instanceof Code && first.bound || first["do"]) { + first = new Parens(first); + } + } + this.operator = CONVERSIONS[op] || op; + this.first = first; + this.second = second; + this.flip = !!flip; + return this; + } + + CONVERSIONS = { + '==': '===', + '!=': '!==', + 'of': 'in' + }; + + INVERSIONS = { + '!==': '===', + '===': '!==' + }; + + Op.prototype.children = ['first', 'second']; + + Op.prototype.isSimpleNumber = NO; + + Op.prototype.isUnary = function() { + return !this.second; + }; + + Op.prototype.isComplex = function() { + var _ref2; + return !(this.isUnary() && ((_ref2 = this.operator) === '+' || _ref2 === '-')) || this.first.isComplex(); + }; + + Op.prototype.isChainable = function() { + var _ref2; + return (_ref2 = this.operator) === '<' || _ref2 === '>' || _ref2 === '>=' || _ref2 === '<=' || _ref2 === '===' || _ref2 === '!=='; + }; + + Op.prototype.invert = function() { + var allInvertable, curr, fst, op, _ref2; + if (this.isChainable() && this.first.isChainable()) { + allInvertable = true; + curr = this; + while (curr && curr.operator) { + allInvertable && (allInvertable = curr.operator in INVERSIONS); + curr = curr.first; + } + if (!allInvertable) { + return new Parens(this).invert(); + } + curr = this; + while (curr && curr.operator) { + curr.invert = !curr.invert; + curr.operator = INVERSIONS[curr.operator]; + curr = curr.first; + } + return this; + } else if (op = INVERSIONS[this.operator]) { + this.operator = op; + if (this.first.unwrap() instanceof Op) { + this.first.invert(); + } + return this; + } else if (this.second) { + return new Parens(this).invert(); + } else if (this.operator === '!' && (fst = this.first.unwrap()) instanceof Op && ((_ref2 = fst.operator) === '!' || _ref2 === 'in' || _ref2 === 'instanceof')) { + return fst; + } else { + return new Op('!', this); + } + }; + + Op.prototype.unfoldSoak = function(o) { + var _ref2; + return ((_ref2 = this.operator) === '++' || _ref2 === '--' || _ref2 === 'delete') && unfoldSoak(o, this, 'first'); + }; + + Op.prototype.generateDo = function(exp) { + var call, func, param, passedParams, ref, _i, _len, _ref2; + passedParams = []; + func = exp instanceof Assign && (ref = exp.value.unwrap()) instanceof Code ? ref : exp; + _ref2 = func.params || []; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + param = _ref2[_i]; + if (param.value) { + passedParams.push(param.value); + delete param.value; + } else { + passedParams.push(param); + } + } + call = new Call(exp, passedParams); + call["do"] = true; + return call; + }; + + Op.prototype.compileNode = function(o) { + var code, isChain, _ref2, _ref3; + isChain = this.isChainable() && this.first.isChainable(); + if (!isChain) { + this.first.front = this.front; + } + if (this.operator === 'delete' && o.scope.check(this.first.unwrapAll().value)) { + throw SyntaxError('delete operand may not be argument or var'); + } + if (((_ref2 = this.operator) === '--' || _ref2 === '++') && (_ref3 = this.first.unwrapAll().value, __indexOf.call(STRICT_PROSCRIBED, _ref3) >= 0)) { + throw SyntaxError('prefix increment/decrement may not have eval or arguments operand'); + } + if (this.isUnary()) { + return this.compileUnary(o); + } + if (isChain) { + return this.compileChain(o); + } + if (this.operator === '?') { + return this.compileExistence(o); + } + code = this.first.compile(o, LEVEL_OP) + ' ' + this.operator + ' ' + this.second.compile(o, LEVEL_OP); + if (o.level <= LEVEL_OP) { + return code; + } else { + return "(" + code + ")"; + } + }; + + Op.prototype.compileChain = function(o) { + var code, fst, shared, _ref2; + _ref2 = this.first.second.cache(o), this.first.second = _ref2[0], shared = _ref2[1]; + fst = this.first.compile(o, LEVEL_OP); + code = "" + fst + " " + (this.invert ? '&&' : '||') + " " + (shared.compile(o)) + " " + this.operator + " " + (this.second.compile(o, LEVEL_OP)); + return "(" + code + ")"; + }; + + Op.prototype.compileExistence = function(o) { + var fst, ref; + if (this.first.isComplex()) { + ref = new Literal(o.scope.freeVariable('ref')); + fst = new Parens(new Assign(ref, this.first)); + } else { + fst = this.first; + ref = fst; + } + return new If(new Existence(fst), ref, { + type: 'if' + }).addElse(this.second).compile(o); + }; + + Op.prototype.compileUnary = function(o) { + var op, parts, plusMinus; + if (o.level >= LEVEL_ACCESS) { + return (new Parens(this)).compile(o); + } + parts = [op = this.operator]; + plusMinus = op === '+' || op === '-'; + if ((op === 'new' || op === 'typeof' || op === 'delete') || plusMinus && this.first instanceof Op && this.first.operator === op) { + parts.push(' '); + } + if ((plusMinus && this.first instanceof Op) || (op === 'new' && this.first.isStatement(o))) { + this.first = new Parens(this.first); + } + parts.push(this.first.compile(o, LEVEL_OP)); + if (this.flip) { + parts.reverse(); + } + return parts.join(''); + }; + + Op.prototype.toString = function(idt) { + return Op.__super__.toString.call(this, idt, this.constructor.name + ' ' + this.operator); + }; + + return Op; + + })(Base); + + exports.In = In = (function(_super) { + + __extends(In, _super); + + function In(object, array) { + this.object = object; + this.array = array; + } + + In.prototype.children = ['object', 'array']; + + In.prototype.invert = NEGATE; + + In.prototype.compileNode = function(o) { + var hasSplat, obj, _i, _len, _ref2; + if (this.array instanceof Value && this.array.isArray()) { + _ref2 = this.array.base.objects; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + obj = _ref2[_i]; + if (!(obj instanceof Splat)) { + continue; + } + hasSplat = true; + break; + } + if (!hasSplat) { + return this.compileOrTest(o); + } + } + return this.compileLoopTest(o); + }; + + In.prototype.compileOrTest = function(o) { + var cmp, cnj, i, item, ref, sub, tests, _ref2, _ref3; + if (this.array.base.objects.length === 0) { + return "" + (!!this.negated); + } + _ref2 = this.object.cache(o, LEVEL_OP), sub = _ref2[0], ref = _ref2[1]; + _ref3 = this.negated ? [' !== ', ' && '] : [' === ', ' || '], cmp = _ref3[0], cnj = _ref3[1]; + tests = (function() { + var _i, _len, _ref4, _results; + _ref4 = this.array.base.objects; + _results = []; + for (i = _i = 0, _len = _ref4.length; _i < _len; i = ++_i) { + item = _ref4[i]; + _results.push((i ? ref : sub) + cmp + item.compile(o, LEVEL_ACCESS)); + } + return _results; + }).call(this); + tests = tests.join(cnj); + if (o.level < LEVEL_OP) { + return tests; + } else { + return "(" + tests + ")"; + } + }; + + In.prototype.compileLoopTest = function(o) { + var code, ref, sub, _ref2; + _ref2 = this.object.cache(o, LEVEL_LIST), sub = _ref2[0], ref = _ref2[1]; + code = utility('indexOf') + (".call(" + (this.array.compile(o, LEVEL_LIST)) + ", " + ref + ") ") + (this.negated ? '< 0' : '>= 0'); + if (sub === ref) { + return code; + } + code = sub + ', ' + code; + if (o.level < LEVEL_LIST) { + return code; + } else { + return "(" + code + ")"; + } + }; + + In.prototype.toString = function(idt) { + return In.__super__.toString.call(this, idt, this.constructor.name + (this.negated ? '!' : '')); + }; + + return In; + + })(Base); + + exports.Try = Try = (function(_super) { + + __extends(Try, _super); + + function Try(attempt, error, recovery, ensure) { + this.attempt = attempt; + this.error = error; + this.recovery = recovery; + this.ensure = ensure; + } + + Try.prototype.children = ['attempt', 'recovery', 'ensure']; + + Try.prototype.isStatement = YES; + + Try.prototype.jumps = function(o) { + var _ref2; + return this.attempt.jumps(o) || ((_ref2 = this.recovery) != null ? _ref2.jumps(o) : void 0); + }; + + Try.prototype.makeReturn = function(res) { + if (this.attempt) { + this.attempt = this.attempt.makeReturn(res); + } + if (this.recovery) { + this.recovery = this.recovery.makeReturn(res); + } + return this; + }; + + Try.prototype.compileNode = function(o) { + var catchPart, ensurePart, errorPart, tryPart; + o.indent += TAB; + errorPart = this.error ? " (" + (this.error.compile(o)) + ") " : ' '; + tryPart = this.attempt.compile(o, LEVEL_TOP); + catchPart = (function() { + var _ref2; + if (this.recovery) { + if (_ref2 = this.error.value, __indexOf.call(STRICT_PROSCRIBED, _ref2) >= 0) { + throw SyntaxError("catch variable may not be \"" + this.error.value + "\""); + } + if (!o.scope.check(this.error.value)) { + o.scope.add(this.error.value, 'param'); + } + return " catch" + errorPart + "{\n" + (this.recovery.compile(o, LEVEL_TOP)) + "\n" + this.tab + "}"; + } else if (!(this.ensure || this.recovery)) { + return ' catch (_error) {}'; + } + }).call(this); + ensurePart = this.ensure ? " finally {\n" + (this.ensure.compile(o, LEVEL_TOP)) + "\n" + this.tab + "}" : ''; + return "" + this.tab + "try {\n" + tryPart + "\n" + this.tab + "}" + (catchPart || '') + ensurePart; + }; + + return Try; + + })(Base); + + exports.Throw = Throw = (function(_super) { + + __extends(Throw, _super); + + function Throw(expression) { + this.expression = expression; + } + + Throw.prototype.children = ['expression']; + + Throw.prototype.isStatement = YES; + + Throw.prototype.jumps = NO; + + Throw.prototype.makeReturn = THIS; + + Throw.prototype.compileNode = function(o) { + return this.tab + ("throw " + (this.expression.compile(o)) + ";"); + }; + + return Throw; + + })(Base); + + exports.Existence = Existence = (function(_super) { + + __extends(Existence, _super); + + function Existence(expression) { + this.expression = expression; + } + + Existence.prototype.children = ['expression']; + + Existence.prototype.invert = NEGATE; + + Existence.prototype.compileNode = function(o) { + var cmp, cnj, code, _ref2; + this.expression.front = this.front; + code = this.expression.compile(o, LEVEL_OP); + if (IDENTIFIER.test(code) && !o.scope.check(code)) { + _ref2 = this.negated ? ['===', '||'] : ['!==', '&&'], cmp = _ref2[0], cnj = _ref2[1]; + code = "typeof " + code + " " + cmp + " \"undefined\" " + cnj + " " + code + " " + cmp + " null"; + } else { + code = "" + code + " " + (this.negated ? '==' : '!=') + " null"; + } + if (o.level <= LEVEL_COND) { + return code; + } else { + return "(" + code + ")"; + } + }; + + return Existence; + + })(Base); + + exports.Parens = Parens = (function(_super) { + + __extends(Parens, _super); + + function Parens(body) { + this.body = body; + } + + Parens.prototype.children = ['body']; + + Parens.prototype.unwrap = function() { + return this.body; + }; + + Parens.prototype.isComplex = function() { + return this.body.isComplex(); + }; + + Parens.prototype.compileNode = function(o) { + var bare, code, expr; + expr = this.body.unwrap(); + if (expr instanceof Value && expr.isAtomic()) { + expr.front = this.front; + return expr.compile(o); + } + code = expr.compile(o, LEVEL_PAREN); + bare = o.level < LEVEL_OP && (expr instanceof Op || expr instanceof Call || (expr instanceof For && expr.returns)); + if (bare) { + return code; + } else { + return "(" + code + ")"; + } + }; + + return Parens; + + })(Base); + + exports.For = For = (function(_super) { + + __extends(For, _super); + + function For(body, source) { + var _ref2; + this.source = source.source, this.guard = source.guard, this.step = source.step, this.name = source.name, this.index = source.index; + this.body = Block.wrap([body]); + this.own = !!source.own; + this.object = !!source.object; + if (this.object) { + _ref2 = [this.index, this.name], this.name = _ref2[0], this.index = _ref2[1]; + } + if (this.index instanceof Value) { + throw SyntaxError('index cannot be a pattern matching expression'); + } + this.range = this.source instanceof Value && this.source.base instanceof Range && !this.source.properties.length; + this.pattern = this.name instanceof Value; + if (this.range && this.index) { + throw SyntaxError('indexes do not apply to range loops'); + } + if (this.range && this.pattern) { + throw SyntaxError('cannot pattern match over range loops'); + } + this.returns = false; + } + + For.prototype.children = ['body', 'source', 'guard', 'step']; + + For.prototype.compileNode = function(o) { + var body, defPart, forPart, forVarPart, guardPart, idt1, index, ivar, kvar, kvarAssign, lastJumps, lvar, name, namePart, ref, resultPart, returnResult, rvar, scope, source, stepPart, stepvar, svar, varPart, _ref2; + body = Block.wrap([this.body]); + lastJumps = (_ref2 = last(body.expressions)) != null ? _ref2.jumps() : void 0; + if (lastJumps && lastJumps instanceof Return) { + this.returns = false; + } + source = this.range ? this.source.base : this.source; + scope = o.scope; + name = this.name && this.name.compile(o, LEVEL_LIST); + index = this.index && this.index.compile(o, LEVEL_LIST); + if (name && !this.pattern) { + scope.find(name); + } + if (index) { + scope.find(index); + } + if (this.returns) { + rvar = scope.freeVariable('results'); + } + ivar = (this.object && index) || scope.freeVariable('i'); + kvar = (this.range && name) || index || ivar; + kvarAssign = kvar !== ivar ? "" + kvar + " = " : ""; + if (this.step && !this.range) { + stepvar = scope.freeVariable("step"); + } + if (this.pattern) { + name = ivar; + } + varPart = ''; + guardPart = ''; + defPart = ''; + idt1 = this.tab + TAB; + if (this.range) { + forPart = source.compile(merge(o, { + index: ivar, + name: name, + step: this.step + })); + } else { + svar = this.source.compile(o, LEVEL_LIST); + if ((name || this.own) && !IDENTIFIER.test(svar)) { + defPart = "" + this.tab + (ref = scope.freeVariable('ref')) + " = " + svar + ";\n"; + svar = ref; + } + if (name && !this.pattern) { + namePart = "" + name + " = " + svar + "[" + kvar + "]"; + } + if (!this.object) { + lvar = scope.freeVariable('len'); + forVarPart = "" + kvarAssign + ivar + " = 0, " + lvar + " = " + svar + ".length"; + if (this.step) { + forVarPart += ", " + stepvar + " = " + (this.step.compile(o, LEVEL_OP)); + } + stepPart = "" + kvarAssign + (this.step ? "" + ivar + " += " + stepvar : (kvar !== ivar ? "++" + ivar : "" + ivar + "++")); + forPart = "" + forVarPart + "; " + ivar + " < " + lvar + "; " + stepPart; + } + } + if (this.returns) { + resultPart = "" + this.tab + rvar + " = [];\n"; + returnResult = "\n" + this.tab + "return " + rvar + ";"; + body.makeReturn(rvar); + } + if (this.guard) { + if (body.expressions.length > 1) { + body.expressions.unshift(new If((new Parens(this.guard)).invert(), new Literal("continue"))); + } else { + if (this.guard) { + body = Block.wrap([new If(this.guard, body)]); + } + } + } + if (this.pattern) { + body.expressions.unshift(new Assign(this.name, new Literal("" + svar + "[" + kvar + "]"))); + } + defPart += this.pluckDirectCall(o, body); + if (namePart) { + varPart = "\n" + idt1 + namePart + ";"; + } + if (this.object) { + forPart = "" + kvar + " in " + svar; + if (this.own) { + guardPart = "\n" + idt1 + "if (!" + (utility('hasProp')) + ".call(" + svar + ", " + kvar + ")) continue;"; + } + } + body = body.compile(merge(o, { + indent: idt1 + }), LEVEL_TOP); + if (body) { + body = '\n' + body + '\n'; + } + return "" + defPart + (resultPart || '') + this.tab + "for (" + forPart + ") {" + guardPart + varPart + body + this.tab + "}" + (returnResult || ''); + }; + + For.prototype.pluckDirectCall = function(o, body) { + var base, defs, expr, fn, idx, ref, val, _i, _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7; + defs = ''; + _ref2 = body.expressions; + for (idx = _i = 0, _len = _ref2.length; _i < _len; idx = ++_i) { + expr = _ref2[idx]; + expr = expr.unwrapAll(); + if (!(expr instanceof Call)) { + continue; + } + val = expr.variable.unwrapAll(); + if (!((val instanceof Code) || (val instanceof Value && ((_ref3 = val.base) != null ? _ref3.unwrapAll() : void 0) instanceof Code && val.properties.length === 1 && ((_ref4 = (_ref5 = val.properties[0].name) != null ? _ref5.value : void 0) === 'call' || _ref4 === 'apply')))) { + continue; + } + fn = ((_ref6 = val.base) != null ? _ref6.unwrapAll() : void 0) || val; + ref = new Literal(o.scope.freeVariable('fn')); + base = new Value(ref); + if (val.base) { + _ref7 = [base, val], val.base = _ref7[0], base = _ref7[1]; + } + body.expressions[idx] = new Call(base, expr.args); + defs += this.tab + new Assign(ref, fn).compile(o, LEVEL_TOP) + ';\n'; + } + return defs; + }; + + return For; + + })(While); + + exports.Switch = Switch = (function(_super) { + + __extends(Switch, _super); + + function Switch(subject, cases, otherwise) { + this.subject = subject; + this.cases = cases; + this.otherwise = otherwise; + } + + Switch.prototype.children = ['subject', 'cases', 'otherwise']; + + Switch.prototype.isStatement = YES; + + Switch.prototype.jumps = function(o) { + var block, conds, _i, _len, _ref2, _ref3, _ref4; + if (o == null) { + o = { + block: true + }; + } + _ref2 = this.cases; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + _ref3 = _ref2[_i], conds = _ref3[0], block = _ref3[1]; + if (block.jumps(o)) { + return block; + } + } + return (_ref4 = this.otherwise) != null ? _ref4.jumps(o) : void 0; + }; + + Switch.prototype.makeReturn = function(res) { + var pair, _i, _len, _ref2, _ref3; + _ref2 = this.cases; + for (_i = 0, _len = _ref2.length; _i < _len; _i++) { + pair = _ref2[_i]; + pair[1].makeReturn(res); + } + if (res) { + this.otherwise || (this.otherwise = new Block([new Literal('void 0')])); + } + if ((_ref3 = this.otherwise) != null) { + _ref3.makeReturn(res); + } + return this; + }; + + Switch.prototype.compileNode = function(o) { + var block, body, code, cond, conditions, expr, i, idt1, idt2, _i, _j, _len, _len1, _ref2, _ref3, _ref4, _ref5; + idt1 = o.indent + TAB; + idt2 = o.indent = idt1 + TAB; + code = this.tab + ("switch (" + (((_ref2 = this.subject) != null ? _ref2.compile(o, LEVEL_PAREN) : void 0) || false) + ") {\n"); + _ref3 = this.cases; + for (i = _i = 0, _len = _ref3.length; _i < _len; i = ++_i) { + _ref4 = _ref3[i], conditions = _ref4[0], block = _ref4[1]; + _ref5 = flatten([conditions]); + for (_j = 0, _len1 = _ref5.length; _j < _len1; _j++) { + cond = _ref5[_j]; + if (!this.subject) { + cond = cond.invert(); + } + code += idt1 + ("case " + (cond.compile(o, LEVEL_PAREN)) + ":\n"); + } + if (body = block.compile(o, LEVEL_TOP)) { + code += body + '\n'; + } + if (i === this.cases.length - 1 && !this.otherwise) { + break; + } + expr = this.lastNonComment(block.expressions); + if (expr instanceof Return || (expr instanceof Literal && expr.jumps() && expr.value !== 'debugger')) { + continue; + } + code += idt2 + 'break;\n'; + } + if (this.otherwise && this.otherwise.expressions.length) { + code += idt1 + ("default:\n" + (this.otherwise.compile(o, LEVEL_TOP)) + "\n"); + } + return code + this.tab + '}'; + }; + + return Switch; + + })(Base); + + exports.If = If = (function(_super) { + + __extends(If, _super); + + function If(condition, body, options) { + this.body = body; + if (options == null) { + options = {}; + } + this.condition = options.type === 'unless' ? condition.invert() : condition; + this.elseBody = null; + this.isChain = false; + this.soak = options.soak; + } + + If.prototype.children = ['condition', 'body', 'elseBody']; + + If.prototype.bodyNode = function() { + var _ref2; + return (_ref2 = this.body) != null ? _ref2.unwrap() : void 0; + }; + + If.prototype.elseBodyNode = function() { + var _ref2; + return (_ref2 = this.elseBody) != null ? _ref2.unwrap() : void 0; + }; + + If.prototype.addElse = function(elseBody) { + if (this.isChain) { + this.elseBodyNode().addElse(elseBody); + } else { + this.isChain = elseBody instanceof If; + this.elseBody = this.ensureBlock(elseBody); + } + return this; + }; + + If.prototype.isStatement = function(o) { + var _ref2; + return (o != null ? o.level : void 0) === LEVEL_TOP || this.bodyNode().isStatement(o) || ((_ref2 = this.elseBodyNode()) != null ? _ref2.isStatement(o) : void 0); + }; + + If.prototype.jumps = function(o) { + var _ref2; + return this.body.jumps(o) || ((_ref2 = this.elseBody) != null ? _ref2.jumps(o) : void 0); + }; + + If.prototype.compileNode = function(o) { + if (this.isStatement(o)) { + return this.compileStatement(o); + } else { + return this.compileExpression(o); + } + }; + + If.prototype.makeReturn = function(res) { + if (res) { + this.elseBody || (this.elseBody = new Block([new Literal('void 0')])); + } + this.body && (this.body = new Block([this.body.makeReturn(res)])); + this.elseBody && (this.elseBody = new Block([this.elseBody.makeReturn(res)])); + return this; + }; + + If.prototype.ensureBlock = function(node) { + if (node instanceof Block) { + return node; + } else { + return new Block([node]); + } + }; + + If.prototype.compileStatement = function(o) { + var body, child, cond, exeq, ifPart; + child = del(o, 'chainChild'); + exeq = del(o, 'isExistentialEquals'); + if (exeq) { + return new If(this.condition.invert(), this.elseBodyNode(), { + type: 'if' + }).compile(o); + } + cond = this.condition.compile(o, LEVEL_PAREN); + o.indent += TAB; + body = this.ensureBlock(this.body); + ifPart = "if (" + cond + ") {\n" + (body.compile(o)) + "\n" + this.tab + "}"; + if (!child) { + ifPart = this.tab + ifPart; + } + if (!this.elseBody) { + return ifPart; + } + return ifPart + ' else ' + (this.isChain ? (o.indent = this.tab, o.chainChild = true, this.elseBody.unwrap().compile(o, LEVEL_TOP)) : "{\n" + (this.elseBody.compile(o, LEVEL_TOP)) + "\n" + this.tab + "}"); + }; + + If.prototype.compileExpression = function(o) { + var alt, body, code, cond; + cond = this.condition.compile(o, LEVEL_COND); + body = this.bodyNode().compile(o, LEVEL_LIST); + alt = this.elseBodyNode() ? this.elseBodyNode().compile(o, LEVEL_LIST) : 'void 0'; + code = "" + cond + " ? " + body + " : " + alt; + if (o.level >= LEVEL_COND) { + return "(" + code + ")"; + } else { + return code; + } + }; + + If.prototype.unfoldSoak = function() { + return this.soak && this; + }; + + return If; + + })(Base); + + Closure = { + wrap: function(expressions, statement, noReturn) { + var args, call, func, mentionsArgs, meth; + if (expressions.jumps()) { + return expressions; + } + func = new Code([], Block.wrap([expressions])); + args = []; + if ((mentionsArgs = expressions.contains(this.literalArgs)) || expressions.contains(this.literalThis)) { + meth = new Literal(mentionsArgs ? 'apply' : 'call'); + args = [new Literal('this')]; + if (mentionsArgs) { + args.push(new Literal('arguments')); + } + func = new Value(func, [new Access(meth)]); + } + func.noReturn = noReturn; + call = new Call(func, args); + if (statement) { + return Block.wrap([call]); + } else { + return call; + } + }, + literalArgs: function(node) { + return node instanceof Literal && node.value === 'arguments' && !node.asKey; + }, + literalThis: function(node) { + return (node instanceof Literal && node.value === 'this' && !node.asKey) || (node instanceof Code && node.bound) || (node instanceof Call && node.isSuper); + } + }; + + unfoldSoak = function(o, parent, name) { + var ifn; + if (!(ifn = parent[name].unfoldSoak(o))) { + return; + } + parent[name] = ifn.body; + ifn.body = new Value(parent); + return ifn; + }; + + UTILITIES = { + "extends": function() { + return "function(child, parent) { for (var key in parent) { if (" + (utility('hasProp')) + ".call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }"; + }, + bind: function() { + return 'function(fn, me){ return function(){ return fn.apply(me, arguments); }; }'; + }, + indexOf: function() { + return "[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }"; + }, + hasProp: function() { + return '{}.hasOwnProperty'; + }, + slice: function() { + return '[].slice'; + } + }; + + LEVEL_TOP = 1; + + LEVEL_PAREN = 2; + + LEVEL_LIST = 3; + + LEVEL_COND = 4; + + LEVEL_OP = 5; + + LEVEL_ACCESS = 6; + + TAB = ' '; + + IDENTIFIER_STR = "[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*"; + + IDENTIFIER = RegExp("^" + IDENTIFIER_STR + "$"); + + SIMPLENUM = /^[+-]?\d+$/; + + METHOD_DEF = RegExp("^(?:(" + IDENTIFIER_STR + ")\\.prototype(?:\\.(" + IDENTIFIER_STR + ")|\\[(\"(?:[^\\\\\"\\r\\n]|\\\\.)*\"|'(?:[^\\\\'\\r\\n]|\\\\.)*')\\]|\\[(0x[\\da-fA-F]+|\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\]))|(" + IDENTIFIER_STR + ")$"); + + IS_STRING = /^['"]/; + + utility = function(name) { + var ref; + ref = "__" + name; + Scope.root.assign(ref, UTILITIES[name]()); + return ref; + }; + + multident = function(code, tab) { + code = code.replace(/\n/g, '$&' + tab); + return code.replace(/\s+$/, ''); + }; + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/optparse.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/optparse.js new file mode 100644 index 0000000..d9bab41 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/optparse.js @@ -0,0 +1,138 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments; + + exports.OptionParser = OptionParser = (function() { + + function OptionParser(rules, banner) { + this.banner = banner; + this.rules = buildRules(rules); + } + + OptionParser.prototype.parse = function(args) { + var arg, i, isOption, matchedRule, options, originalArgs, pos, rule, seenNonOptionArg, skippingArgument, value, _i, _j, _len, _len1, _ref; + options = { + "arguments": [] + }; + skippingArgument = false; + originalArgs = args; + args = normalizeArguments(args); + for (i = _i = 0, _len = args.length; _i < _len; i = ++_i) { + arg = args[i]; + if (skippingArgument) { + skippingArgument = false; + continue; + } + if (arg === '--') { + pos = originalArgs.indexOf('--'); + options["arguments"] = options["arguments"].concat(originalArgs.slice(pos + 1)); + break; + } + isOption = !!(arg.match(LONG_FLAG) || arg.match(SHORT_FLAG)); + seenNonOptionArg = options["arguments"].length > 0; + if (!seenNonOptionArg) { + matchedRule = false; + _ref = this.rules; + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + rule = _ref[_j]; + if (rule.shortFlag === arg || rule.longFlag === arg) { + value = true; + if (rule.hasArgument) { + skippingArgument = true; + value = args[i + 1]; + } + options[rule.name] = rule.isList ? (options[rule.name] || []).concat(value) : value; + matchedRule = true; + break; + } + } + if (isOption && !matchedRule) { + throw new Error("unrecognized option: " + arg); + } + } + if (seenNonOptionArg || !isOption) { + options["arguments"].push(arg); + } + } + return options; + }; + + OptionParser.prototype.help = function() { + var letPart, lines, rule, spaces, _i, _len, _ref; + lines = []; + if (this.banner) { + lines.unshift("" + this.banner + "\n"); + } + _ref = this.rules; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + rule = _ref[_i]; + spaces = 15 - rule.longFlag.length; + spaces = spaces > 0 ? Array(spaces + 1).join(' ') : ''; + letPart = rule.shortFlag ? rule.shortFlag + ', ' : ' '; + lines.push(' ' + letPart + rule.longFlag + spaces + rule.description); + } + return "\n" + (lines.join('\n')) + "\n"; + }; + + return OptionParser; + + })(); + + LONG_FLAG = /^(--\w[\w\-]*)/; + + SHORT_FLAG = /^(-\w)$/; + + MULTI_FLAG = /^-(\w{2,})/; + + OPTIONAL = /\[(\w+(\*?))\]/; + + buildRules = function(rules) { + var tuple, _i, _len, _results; + _results = []; + for (_i = 0, _len = rules.length; _i < _len; _i++) { + tuple = rules[_i]; + if (tuple.length < 3) { + tuple.unshift(null); + } + _results.push(buildRule.apply(null, tuple)); + } + return _results; + }; + + buildRule = function(shortFlag, longFlag, description, options) { + var match; + if (options == null) { + options = {}; + } + match = longFlag.match(OPTIONAL); + longFlag = longFlag.match(LONG_FLAG)[1]; + return { + name: longFlag.substr(2), + shortFlag: shortFlag, + longFlag: longFlag, + description: description, + hasArgument: !!(match && match[1]), + isList: !!(match && match[2]) + }; + }; + + normalizeArguments = function(args) { + var arg, l, match, result, _i, _j, _len, _len1, _ref; + args = args.slice(0); + result = []; + for (_i = 0, _len = args.length; _i < _len; _i++) { + arg = args[_i]; + if (match = arg.match(MULTI_FLAG)) { + _ref = match[1].split(''); + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + l = _ref[_j]; + result.push('-' + l); + } + } else { + result.push(arg); + } + } + return result; + }; + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/parser.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/parser.js new file mode 100644 index 0000000..f049903 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/parser.js @@ -0,0 +1,683 @@ +/* Jison generated parser */ +var parser = (function(){ +var parser = {trace: function trace() { }, +yy: {}, +symbols_: {"error":2,"Root":3,"Body":4,"Block":5,"TERMINATOR":6,"Line":7,"Expression":8,"Statement":9,"Return":10,"Comment":11,"STATEMENT":12,"Value":13,"Invocation":14,"Code":15,"Operation":16,"Assign":17,"If":18,"Try":19,"While":20,"For":21,"Switch":22,"Class":23,"Throw":24,"INDENT":25,"OUTDENT":26,"Identifier":27,"IDENTIFIER":28,"AlphaNumeric":29,"NUMBER":30,"STRING":31,"Literal":32,"JS":33,"REGEX":34,"DEBUGGER":35,"UNDEFINED":36,"NULL":37,"BOOL":38,"Assignable":39,"=":40,"AssignObj":41,"ObjAssignable":42,":":43,"ThisProperty":44,"RETURN":45,"HERECOMMENT":46,"PARAM_START":47,"ParamList":48,"PARAM_END":49,"FuncGlyph":50,"->":51,"=>":52,"OptComma":53,",":54,"Param":55,"ParamVar":56,"...":57,"Array":58,"Object":59,"Splat":60,"SimpleAssignable":61,"Accessor":62,"Parenthetical":63,"Range":64,"This":65,".":66,"?.":67,"::":68,"Index":69,"INDEX_START":70,"IndexValue":71,"INDEX_END":72,"INDEX_SOAK":73,"Slice":74,"{":75,"AssignList":76,"}":77,"CLASS":78,"EXTENDS":79,"OptFuncExist":80,"Arguments":81,"SUPER":82,"FUNC_EXIST":83,"CALL_START":84,"CALL_END":85,"ArgList":86,"THIS":87,"@":88,"[":89,"]":90,"RangeDots":91,"..":92,"Arg":93,"SimpleArgs":94,"TRY":95,"Catch":96,"FINALLY":97,"CATCH":98,"THROW":99,"(":100,")":101,"WhileSource":102,"WHILE":103,"WHEN":104,"UNTIL":105,"Loop":106,"LOOP":107,"ForBody":108,"FOR":109,"ForStart":110,"ForSource":111,"ForVariables":112,"OWN":113,"ForValue":114,"FORIN":115,"FOROF":116,"BY":117,"SWITCH":118,"Whens":119,"ELSE":120,"When":121,"LEADING_WHEN":122,"IfBlock":123,"IF":124,"POST_IF":125,"UNARY":126,"-":127,"+":128,"--":129,"++":130,"?":131,"MATH":132,"SHIFT":133,"COMPARE":134,"LOGIC":135,"RELATION":136,"COMPOUND_ASSIGN":137,"$accept":0,"$end":1}, +terminals_: {2:"error",6:"TERMINATOR",12:"STATEMENT",25:"INDENT",26:"OUTDENT",28:"IDENTIFIER",30:"NUMBER",31:"STRING",33:"JS",34:"REGEX",35:"DEBUGGER",36:"UNDEFINED",37:"NULL",38:"BOOL",40:"=",43:":",45:"RETURN",46:"HERECOMMENT",47:"PARAM_START",49:"PARAM_END",51:"->",52:"=>",54:",",57:"...",66:".",67:"?.",68:"::",70:"INDEX_START",72:"INDEX_END",73:"INDEX_SOAK",75:"{",77:"}",78:"CLASS",79:"EXTENDS",82:"SUPER",83:"FUNC_EXIST",84:"CALL_START",85:"CALL_END",87:"THIS",88:"@",89:"[",90:"]",92:"..",95:"TRY",97:"FINALLY",98:"CATCH",99:"THROW",100:"(",101:")",103:"WHILE",104:"WHEN",105:"UNTIL",107:"LOOP",109:"FOR",113:"OWN",115:"FORIN",116:"FOROF",117:"BY",118:"SWITCH",120:"ELSE",122:"LEADING_WHEN",124:"IF",125:"POST_IF",126:"UNARY",127:"-",128:"+",129:"--",130:"++",131:"?",132:"MATH",133:"SHIFT",134:"COMPARE",135:"LOGIC",136:"RELATION",137:"COMPOUND_ASSIGN"}, +productions_: [0,[3,0],[3,1],[3,2],[4,1],[4,3],[4,2],[7,1],[7,1],[9,1],[9,1],[9,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[5,2],[5,3],[27,1],[29,1],[29,1],[32,1],[32,1],[32,1],[32,1],[32,1],[32,1],[32,1],[17,3],[17,4],[17,5],[41,1],[41,3],[41,5],[41,1],[42,1],[42,1],[42,1],[10,2],[10,1],[11,1],[15,5],[15,2],[50,1],[50,1],[53,0],[53,1],[48,0],[48,1],[48,3],[48,4],[48,6],[55,1],[55,2],[55,3],[56,1],[56,1],[56,1],[56,1],[60,2],[61,1],[61,2],[61,2],[61,1],[39,1],[39,1],[39,1],[13,1],[13,1],[13,1],[13,1],[13,1],[62,2],[62,2],[62,2],[62,1],[62,1],[69,3],[69,2],[71,1],[71,1],[59,4],[76,0],[76,1],[76,3],[76,4],[76,6],[23,1],[23,2],[23,3],[23,4],[23,2],[23,3],[23,4],[23,5],[14,3],[14,3],[14,1],[14,2],[80,0],[80,1],[81,2],[81,4],[65,1],[65,1],[44,2],[58,2],[58,4],[91,1],[91,1],[64,5],[74,3],[74,2],[74,2],[74,1],[86,1],[86,3],[86,4],[86,4],[86,6],[93,1],[93,1],[94,1],[94,3],[19,2],[19,3],[19,4],[19,5],[96,3],[24,2],[63,3],[63,5],[102,2],[102,4],[102,2],[102,4],[20,2],[20,2],[20,2],[20,1],[106,2],[106,2],[21,2],[21,2],[21,2],[108,2],[108,2],[110,2],[110,3],[114,1],[114,1],[114,1],[114,1],[112,1],[112,3],[111,2],[111,2],[111,4],[111,4],[111,4],[111,6],[111,6],[22,5],[22,7],[22,4],[22,6],[119,1],[119,2],[121,3],[121,4],[123,3],[123,5],[18,1],[18,3],[18,3],[18,3],[16,2],[16,2],[16,2],[16,2],[16,2],[16,2],[16,2],[16,2],[16,3],[16,3],[16,3],[16,3],[16,3],[16,3],[16,3],[16,3],[16,5],[16,3]], +performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { + +var $0 = $$.length - 1; +switch (yystate) { +case 1:return this.$ = new yy.Block; +break; +case 2:return this.$ = $$[$0]; +break; +case 3:return this.$ = $$[$0-1]; +break; +case 4:this.$ = yy.Block.wrap([$$[$0]]); +break; +case 5:this.$ = $$[$0-2].push($$[$0]); +break; +case 6:this.$ = $$[$0-1]; +break; +case 7:this.$ = $$[$0]; +break; +case 8:this.$ = $$[$0]; +break; +case 9:this.$ = $$[$0]; +break; +case 10:this.$ = $$[$0]; +break; +case 11:this.$ = new yy.Literal($$[$0]); +break; +case 12:this.$ = $$[$0]; +break; +case 13:this.$ = $$[$0]; +break; +case 14:this.$ = $$[$0]; +break; +case 15:this.$ = $$[$0]; +break; +case 16:this.$ = $$[$0]; +break; +case 17:this.$ = $$[$0]; +break; +case 18:this.$ = $$[$0]; +break; +case 19:this.$ = $$[$0]; +break; +case 20:this.$ = $$[$0]; +break; +case 21:this.$ = $$[$0]; +break; +case 22:this.$ = $$[$0]; +break; +case 23:this.$ = $$[$0]; +break; +case 24:this.$ = new yy.Block; +break; +case 25:this.$ = $$[$0-1]; +break; +case 26:this.$ = new yy.Literal($$[$0]); +break; +case 27:this.$ = new yy.Literal($$[$0]); +break; +case 28:this.$ = new yy.Literal($$[$0]); +break; +case 29:this.$ = $$[$0]; +break; +case 30:this.$ = new yy.Literal($$[$0]); +break; +case 31:this.$ = new yy.Literal($$[$0]); +break; +case 32:this.$ = new yy.Literal($$[$0]); +break; +case 33:this.$ = new yy.Undefined; +break; +case 34:this.$ = new yy.Null; +break; +case 35:this.$ = new yy.Bool($$[$0]); +break; +case 36:this.$ = new yy.Assign($$[$0-2], $$[$0]); +break; +case 37:this.$ = new yy.Assign($$[$0-3], $$[$0]); +break; +case 38:this.$ = new yy.Assign($$[$0-4], $$[$0-1]); +break; +case 39:this.$ = new yy.Value($$[$0]); +break; +case 40:this.$ = new yy.Assign(new yy.Value($$[$0-2]), $$[$0], 'object'); +break; +case 41:this.$ = new yy.Assign(new yy.Value($$[$0-4]), $$[$0-1], 'object'); +break; +case 42:this.$ = $$[$0]; +break; +case 43:this.$ = $$[$0]; +break; +case 44:this.$ = $$[$0]; +break; +case 45:this.$ = $$[$0]; +break; +case 46:this.$ = new yy.Return($$[$0]); +break; +case 47:this.$ = new yy.Return; +break; +case 48:this.$ = new yy.Comment($$[$0]); +break; +case 49:this.$ = new yy.Code($$[$0-3], $$[$0], $$[$0-1]); +break; +case 50:this.$ = new yy.Code([], $$[$0], $$[$0-1]); +break; +case 51:this.$ = 'func'; +break; +case 52:this.$ = 'boundfunc'; +break; +case 53:this.$ = $$[$0]; +break; +case 54:this.$ = $$[$0]; +break; +case 55:this.$ = []; +break; +case 56:this.$ = [$$[$0]]; +break; +case 57:this.$ = $$[$0-2].concat($$[$0]); +break; +case 58:this.$ = $$[$0-3].concat($$[$0]); +break; +case 59:this.$ = $$[$0-5].concat($$[$0-2]); +break; +case 60:this.$ = new yy.Param($$[$0]); +break; +case 61:this.$ = new yy.Param($$[$0-1], null, true); +break; +case 62:this.$ = new yy.Param($$[$0-2], $$[$0]); +break; +case 63:this.$ = $$[$0]; +break; +case 64:this.$ = $$[$0]; +break; +case 65:this.$ = $$[$0]; +break; +case 66:this.$ = $$[$0]; +break; +case 67:this.$ = new yy.Splat($$[$0-1]); +break; +case 68:this.$ = new yy.Value($$[$0]); +break; +case 69:this.$ = $$[$0-1].add($$[$0]); +break; +case 70:this.$ = new yy.Value($$[$0-1], [].concat($$[$0])); +break; +case 71:this.$ = $$[$0]; +break; +case 72:this.$ = $$[$0]; +break; +case 73:this.$ = new yy.Value($$[$0]); +break; +case 74:this.$ = new yy.Value($$[$0]); +break; +case 75:this.$ = $$[$0]; +break; +case 76:this.$ = new yy.Value($$[$0]); +break; +case 77:this.$ = new yy.Value($$[$0]); +break; +case 78:this.$ = new yy.Value($$[$0]); +break; +case 79:this.$ = $$[$0]; +break; +case 80:this.$ = new yy.Access($$[$0]); +break; +case 81:this.$ = new yy.Access($$[$0], 'soak'); +break; +case 82:this.$ = [new yy.Access(new yy.Literal('prototype')), new yy.Access($$[$0])]; +break; +case 83:this.$ = new yy.Access(new yy.Literal('prototype')); +break; +case 84:this.$ = $$[$0]; +break; +case 85:this.$ = $$[$0-1]; +break; +case 86:this.$ = yy.extend($$[$0], { + soak: true + }); +break; +case 87:this.$ = new yy.Index($$[$0]); +break; +case 88:this.$ = new yy.Slice($$[$0]); +break; +case 89:this.$ = new yy.Obj($$[$0-2], $$[$0-3].generated); +break; +case 90:this.$ = []; +break; +case 91:this.$ = [$$[$0]]; +break; +case 92:this.$ = $$[$0-2].concat($$[$0]); +break; +case 93:this.$ = $$[$0-3].concat($$[$0]); +break; +case 94:this.$ = $$[$0-5].concat($$[$0-2]); +break; +case 95:this.$ = new yy.Class; +break; +case 96:this.$ = new yy.Class(null, null, $$[$0]); +break; +case 97:this.$ = new yy.Class(null, $$[$0]); +break; +case 98:this.$ = new yy.Class(null, $$[$0-1], $$[$0]); +break; +case 99:this.$ = new yy.Class($$[$0]); +break; +case 100:this.$ = new yy.Class($$[$0-1], null, $$[$0]); +break; +case 101:this.$ = new yy.Class($$[$0-2], $$[$0]); +break; +case 102:this.$ = new yy.Class($$[$0-3], $$[$0-1], $$[$0]); +break; +case 103:this.$ = new yy.Call($$[$0-2], $$[$0], $$[$0-1]); +break; +case 104:this.$ = new yy.Call($$[$0-2], $$[$0], $$[$0-1]); +break; +case 105:this.$ = new yy.Call('super', [new yy.Splat(new yy.Literal('arguments'))]); +break; +case 106:this.$ = new yy.Call('super', $$[$0]); +break; +case 107:this.$ = false; +break; +case 108:this.$ = true; +break; +case 109:this.$ = []; +break; +case 110:this.$ = $$[$0-2]; +break; +case 111:this.$ = new yy.Value(new yy.Literal('this')); +break; +case 112:this.$ = new yy.Value(new yy.Literal('this')); +break; +case 113:this.$ = new yy.Value(new yy.Literal('this'), [new yy.Access($$[$0])], 'this'); +break; +case 114:this.$ = new yy.Arr([]); +break; +case 115:this.$ = new yy.Arr($$[$0-2]); +break; +case 116:this.$ = 'inclusive'; +break; +case 117:this.$ = 'exclusive'; +break; +case 118:this.$ = new yy.Range($$[$0-3], $$[$0-1], $$[$0-2]); +break; +case 119:this.$ = new yy.Range($$[$0-2], $$[$0], $$[$0-1]); +break; +case 120:this.$ = new yy.Range($$[$0-1], null, $$[$0]); +break; +case 121:this.$ = new yy.Range(null, $$[$0], $$[$0-1]); +break; +case 122:this.$ = new yy.Range(null, null, $$[$0]); +break; +case 123:this.$ = [$$[$0]]; +break; +case 124:this.$ = $$[$0-2].concat($$[$0]); +break; +case 125:this.$ = $$[$0-3].concat($$[$0]); +break; +case 126:this.$ = $$[$0-2]; +break; +case 127:this.$ = $$[$0-5].concat($$[$0-2]); +break; +case 128:this.$ = $$[$0]; +break; +case 129:this.$ = $$[$0]; +break; +case 130:this.$ = $$[$0]; +break; +case 131:this.$ = [].concat($$[$0-2], $$[$0]); +break; +case 132:this.$ = new yy.Try($$[$0]); +break; +case 133:this.$ = new yy.Try($$[$0-1], $$[$0][0], $$[$0][1]); +break; +case 134:this.$ = new yy.Try($$[$0-2], null, null, $$[$0]); +break; +case 135:this.$ = new yy.Try($$[$0-3], $$[$0-2][0], $$[$0-2][1], $$[$0]); +break; +case 136:this.$ = [$$[$0-1], $$[$0]]; +break; +case 137:this.$ = new yy.Throw($$[$0]); +break; +case 138:this.$ = new yy.Parens($$[$0-1]); +break; +case 139:this.$ = new yy.Parens($$[$0-2]); +break; +case 140:this.$ = new yy.While($$[$0]); +break; +case 141:this.$ = new yy.While($$[$0-2], { + guard: $$[$0] + }); +break; +case 142:this.$ = new yy.While($$[$0], { + invert: true + }); +break; +case 143:this.$ = new yy.While($$[$0-2], { + invert: true, + guard: $$[$0] + }); +break; +case 144:this.$ = $$[$0-1].addBody($$[$0]); +break; +case 145:this.$ = $$[$0].addBody(yy.Block.wrap([$$[$0-1]])); +break; +case 146:this.$ = $$[$0].addBody(yy.Block.wrap([$$[$0-1]])); +break; +case 147:this.$ = $$[$0]; +break; +case 148:this.$ = new yy.While(new yy.Literal('true')).addBody($$[$0]); +break; +case 149:this.$ = new yy.While(new yy.Literal('true')).addBody(yy.Block.wrap([$$[$0]])); +break; +case 150:this.$ = new yy.For($$[$0-1], $$[$0]); +break; +case 151:this.$ = new yy.For($$[$0-1], $$[$0]); +break; +case 152:this.$ = new yy.For($$[$0], $$[$0-1]); +break; +case 153:this.$ = { + source: new yy.Value($$[$0]) + }; +break; +case 154:this.$ = (function () { + $$[$0].own = $$[$0-1].own; + $$[$0].name = $$[$0-1][0]; + $$[$0].index = $$[$0-1][1]; + return $$[$0]; + }()); +break; +case 155:this.$ = $$[$0]; +break; +case 156:this.$ = (function () { + $$[$0].own = true; + return $$[$0]; + }()); +break; +case 157:this.$ = $$[$0]; +break; +case 158:this.$ = $$[$0]; +break; +case 159:this.$ = new yy.Value($$[$0]); +break; +case 160:this.$ = new yy.Value($$[$0]); +break; +case 161:this.$ = [$$[$0]]; +break; +case 162:this.$ = [$$[$0-2], $$[$0]]; +break; +case 163:this.$ = { + source: $$[$0] + }; +break; +case 164:this.$ = { + source: $$[$0], + object: true + }; +break; +case 165:this.$ = { + source: $$[$0-2], + guard: $$[$0] + }; +break; +case 166:this.$ = { + source: $$[$0-2], + guard: $$[$0], + object: true + }; +break; +case 167:this.$ = { + source: $$[$0-2], + step: $$[$0] + }; +break; +case 168:this.$ = { + source: $$[$0-4], + guard: $$[$0-2], + step: $$[$0] + }; +break; +case 169:this.$ = { + source: $$[$0-4], + step: $$[$0-2], + guard: $$[$0] + }; +break; +case 170:this.$ = new yy.Switch($$[$0-3], $$[$0-1]); +break; +case 171:this.$ = new yy.Switch($$[$0-5], $$[$0-3], $$[$0-1]); +break; +case 172:this.$ = new yy.Switch(null, $$[$0-1]); +break; +case 173:this.$ = new yy.Switch(null, $$[$0-3], $$[$0-1]); +break; +case 174:this.$ = $$[$0]; +break; +case 175:this.$ = $$[$0-1].concat($$[$0]); +break; +case 176:this.$ = [[$$[$0-1], $$[$0]]]; +break; +case 177:this.$ = [[$$[$0-2], $$[$0-1]]]; +break; +case 178:this.$ = new yy.If($$[$0-1], $$[$0], { + type: $$[$0-2] + }); +break; +case 179:this.$ = $$[$0-4].addElse(new yy.If($$[$0-1], $$[$0], { + type: $$[$0-2] + })); +break; +case 180:this.$ = $$[$0]; +break; +case 181:this.$ = $$[$0-2].addElse($$[$0]); +break; +case 182:this.$ = new yy.If($$[$0], yy.Block.wrap([$$[$0-2]]), { + type: $$[$0-1], + statement: true + }); +break; +case 183:this.$ = new yy.If($$[$0], yy.Block.wrap([$$[$0-2]]), { + type: $$[$0-1], + statement: true + }); +break; +case 184:this.$ = new yy.Op($$[$0-1], $$[$0]); +break; +case 185:this.$ = new yy.Op('-', $$[$0]); +break; +case 186:this.$ = new yy.Op('+', $$[$0]); +break; +case 187:this.$ = new yy.Op('--', $$[$0]); +break; +case 188:this.$ = new yy.Op('++', $$[$0]); +break; +case 189:this.$ = new yy.Op('--', $$[$0-1], null, true); +break; +case 190:this.$ = new yy.Op('++', $$[$0-1], null, true); +break; +case 191:this.$ = new yy.Existence($$[$0-1]); +break; +case 192:this.$ = new yy.Op('+', $$[$0-2], $$[$0]); +break; +case 193:this.$ = new yy.Op('-', $$[$0-2], $$[$0]); +break; +case 194:this.$ = new yy.Op($$[$0-1], $$[$0-2], $$[$0]); +break; +case 195:this.$ = new yy.Op($$[$0-1], $$[$0-2], $$[$0]); +break; +case 196:this.$ = new yy.Op($$[$0-1], $$[$0-2], $$[$0]); +break; +case 197:this.$ = new yy.Op($$[$0-1], $$[$0-2], $$[$0]); +break; +case 198:this.$ = (function () { + if ($$[$0-1].charAt(0) === '!') { + return new yy.Op($$[$0-1].slice(1), $$[$0-2], $$[$0]).invert(); + } else { + return new yy.Op($$[$0-1], $$[$0-2], $$[$0]); + } + }()); +break; +case 199:this.$ = new yy.Assign($$[$0-2], $$[$0], $$[$0-1]); +break; +case 200:this.$ = new yy.Assign($$[$0-4], $$[$0-1], $$[$0-3]); +break; +case 201:this.$ = new yy.Extends($$[$0-2], $$[$0]); +break; +} +}, +table: [{1:[2,1],3:1,4:2,5:3,7:4,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,5],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[3]},{1:[2,2],6:[1,74]},{6:[1,75]},{1:[2,4],6:[2,4],26:[2,4],101:[2,4]},{4:77,7:4,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,26:[1,76],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,7],6:[2,7],26:[2,7],101:[2,7],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,8],6:[2,8],26:[2,8],101:[2,8],102:90,103:[1,65],105:[1,66],108:91,109:[1,68],110:69,125:[1,89]},{1:[2,12],6:[2,12],25:[2,12],26:[2,12],49:[2,12],54:[2,12],57:[2,12],62:93,66:[1,95],67:[1,96],68:[1,97],69:98,70:[1,99],72:[2,12],73:[1,100],77:[2,12],80:92,83:[1,94],84:[2,107],85:[2,12],90:[2,12],92:[2,12],101:[2,12],103:[2,12],104:[2,12],105:[2,12],109:[2,12],117:[2,12],125:[2,12],127:[2,12],128:[2,12],131:[2,12],132:[2,12],133:[2,12],134:[2,12],135:[2,12],136:[2,12]},{1:[2,13],6:[2,13],25:[2,13],26:[2,13],49:[2,13],54:[2,13],57:[2,13],62:102,66:[1,95],67:[1,96],68:[1,97],69:98,70:[1,99],72:[2,13],73:[1,100],77:[2,13],80:101,83:[1,94],84:[2,107],85:[2,13],90:[2,13],92:[2,13],101:[2,13],103:[2,13],104:[2,13],105:[2,13],109:[2,13],117:[2,13],125:[2,13],127:[2,13],128:[2,13],131:[2,13],132:[2,13],133:[2,13],134:[2,13],135:[2,13],136:[2,13]},{1:[2,14],6:[2,14],25:[2,14],26:[2,14],49:[2,14],54:[2,14],57:[2,14],72:[2,14],77:[2,14],85:[2,14],90:[2,14],92:[2,14],101:[2,14],103:[2,14],104:[2,14],105:[2,14],109:[2,14],117:[2,14],125:[2,14],127:[2,14],128:[2,14],131:[2,14],132:[2,14],133:[2,14],134:[2,14],135:[2,14],136:[2,14]},{1:[2,15],6:[2,15],25:[2,15],26:[2,15],49:[2,15],54:[2,15],57:[2,15],72:[2,15],77:[2,15],85:[2,15],90:[2,15],92:[2,15],101:[2,15],103:[2,15],104:[2,15],105:[2,15],109:[2,15],117:[2,15],125:[2,15],127:[2,15],128:[2,15],131:[2,15],132:[2,15],133:[2,15],134:[2,15],135:[2,15],136:[2,15]},{1:[2,16],6:[2,16],25:[2,16],26:[2,16],49:[2,16],54:[2,16],57:[2,16],72:[2,16],77:[2,16],85:[2,16],90:[2,16],92:[2,16],101:[2,16],103:[2,16],104:[2,16],105:[2,16],109:[2,16],117:[2,16],125:[2,16],127:[2,16],128:[2,16],131:[2,16],132:[2,16],133:[2,16],134:[2,16],135:[2,16],136:[2,16]},{1:[2,17],6:[2,17],25:[2,17],26:[2,17],49:[2,17],54:[2,17],57:[2,17],72:[2,17],77:[2,17],85:[2,17],90:[2,17],92:[2,17],101:[2,17],103:[2,17],104:[2,17],105:[2,17],109:[2,17],117:[2,17],125:[2,17],127:[2,17],128:[2,17],131:[2,17],132:[2,17],133:[2,17],134:[2,17],135:[2,17],136:[2,17]},{1:[2,18],6:[2,18],25:[2,18],26:[2,18],49:[2,18],54:[2,18],57:[2,18],72:[2,18],77:[2,18],85:[2,18],90:[2,18],92:[2,18],101:[2,18],103:[2,18],104:[2,18],105:[2,18],109:[2,18],117:[2,18],125:[2,18],127:[2,18],128:[2,18],131:[2,18],132:[2,18],133:[2,18],134:[2,18],135:[2,18],136:[2,18]},{1:[2,19],6:[2,19],25:[2,19],26:[2,19],49:[2,19],54:[2,19],57:[2,19],72:[2,19],77:[2,19],85:[2,19],90:[2,19],92:[2,19],101:[2,19],103:[2,19],104:[2,19],105:[2,19],109:[2,19],117:[2,19],125:[2,19],127:[2,19],128:[2,19],131:[2,19],132:[2,19],133:[2,19],134:[2,19],135:[2,19],136:[2,19]},{1:[2,20],6:[2,20],25:[2,20],26:[2,20],49:[2,20],54:[2,20],57:[2,20],72:[2,20],77:[2,20],85:[2,20],90:[2,20],92:[2,20],101:[2,20],103:[2,20],104:[2,20],105:[2,20],109:[2,20],117:[2,20],125:[2,20],127:[2,20],128:[2,20],131:[2,20],132:[2,20],133:[2,20],134:[2,20],135:[2,20],136:[2,20]},{1:[2,21],6:[2,21],25:[2,21],26:[2,21],49:[2,21],54:[2,21],57:[2,21],72:[2,21],77:[2,21],85:[2,21],90:[2,21],92:[2,21],101:[2,21],103:[2,21],104:[2,21],105:[2,21],109:[2,21],117:[2,21],125:[2,21],127:[2,21],128:[2,21],131:[2,21],132:[2,21],133:[2,21],134:[2,21],135:[2,21],136:[2,21]},{1:[2,22],6:[2,22],25:[2,22],26:[2,22],49:[2,22],54:[2,22],57:[2,22],72:[2,22],77:[2,22],85:[2,22],90:[2,22],92:[2,22],101:[2,22],103:[2,22],104:[2,22],105:[2,22],109:[2,22],117:[2,22],125:[2,22],127:[2,22],128:[2,22],131:[2,22],132:[2,22],133:[2,22],134:[2,22],135:[2,22],136:[2,22]},{1:[2,23],6:[2,23],25:[2,23],26:[2,23],49:[2,23],54:[2,23],57:[2,23],72:[2,23],77:[2,23],85:[2,23],90:[2,23],92:[2,23],101:[2,23],103:[2,23],104:[2,23],105:[2,23],109:[2,23],117:[2,23],125:[2,23],127:[2,23],128:[2,23],131:[2,23],132:[2,23],133:[2,23],134:[2,23],135:[2,23],136:[2,23]},{1:[2,9],6:[2,9],26:[2,9],101:[2,9],103:[2,9],105:[2,9],109:[2,9],125:[2,9]},{1:[2,10],6:[2,10],26:[2,10],101:[2,10],103:[2,10],105:[2,10],109:[2,10],125:[2,10]},{1:[2,11],6:[2,11],26:[2,11],101:[2,11],103:[2,11],105:[2,11],109:[2,11],125:[2,11]},{1:[2,75],6:[2,75],25:[2,75],26:[2,75],40:[1,103],49:[2,75],54:[2,75],57:[2,75],66:[2,75],67:[2,75],68:[2,75],70:[2,75],72:[2,75],73:[2,75],77:[2,75],83:[2,75],84:[2,75],85:[2,75],90:[2,75],92:[2,75],101:[2,75],103:[2,75],104:[2,75],105:[2,75],109:[2,75],117:[2,75],125:[2,75],127:[2,75],128:[2,75],131:[2,75],132:[2,75],133:[2,75],134:[2,75],135:[2,75],136:[2,75]},{1:[2,76],6:[2,76],25:[2,76],26:[2,76],49:[2,76],54:[2,76],57:[2,76],66:[2,76],67:[2,76],68:[2,76],70:[2,76],72:[2,76],73:[2,76],77:[2,76],83:[2,76],84:[2,76],85:[2,76],90:[2,76],92:[2,76],101:[2,76],103:[2,76],104:[2,76],105:[2,76],109:[2,76],117:[2,76],125:[2,76],127:[2,76],128:[2,76],131:[2,76],132:[2,76],133:[2,76],134:[2,76],135:[2,76],136:[2,76]},{1:[2,77],6:[2,77],25:[2,77],26:[2,77],49:[2,77],54:[2,77],57:[2,77],66:[2,77],67:[2,77],68:[2,77],70:[2,77],72:[2,77],73:[2,77],77:[2,77],83:[2,77],84:[2,77],85:[2,77],90:[2,77],92:[2,77],101:[2,77],103:[2,77],104:[2,77],105:[2,77],109:[2,77],117:[2,77],125:[2,77],127:[2,77],128:[2,77],131:[2,77],132:[2,77],133:[2,77],134:[2,77],135:[2,77],136:[2,77]},{1:[2,78],6:[2,78],25:[2,78],26:[2,78],49:[2,78],54:[2,78],57:[2,78],66:[2,78],67:[2,78],68:[2,78],70:[2,78],72:[2,78],73:[2,78],77:[2,78],83:[2,78],84:[2,78],85:[2,78],90:[2,78],92:[2,78],101:[2,78],103:[2,78],104:[2,78],105:[2,78],109:[2,78],117:[2,78],125:[2,78],127:[2,78],128:[2,78],131:[2,78],132:[2,78],133:[2,78],134:[2,78],135:[2,78],136:[2,78]},{1:[2,79],6:[2,79],25:[2,79],26:[2,79],49:[2,79],54:[2,79],57:[2,79],66:[2,79],67:[2,79],68:[2,79],70:[2,79],72:[2,79],73:[2,79],77:[2,79],83:[2,79],84:[2,79],85:[2,79],90:[2,79],92:[2,79],101:[2,79],103:[2,79],104:[2,79],105:[2,79],109:[2,79],117:[2,79],125:[2,79],127:[2,79],128:[2,79],131:[2,79],132:[2,79],133:[2,79],134:[2,79],135:[2,79],136:[2,79]},{1:[2,105],6:[2,105],25:[2,105],26:[2,105],49:[2,105],54:[2,105],57:[2,105],66:[2,105],67:[2,105],68:[2,105],70:[2,105],72:[2,105],73:[2,105],77:[2,105],81:104,83:[2,105],84:[1,105],85:[2,105],90:[2,105],92:[2,105],101:[2,105],103:[2,105],104:[2,105],105:[2,105],109:[2,105],117:[2,105],125:[2,105],127:[2,105],128:[2,105],131:[2,105],132:[2,105],133:[2,105],134:[2,105],135:[2,105],136:[2,105]},{6:[2,55],25:[2,55],27:109,28:[1,73],44:110,48:106,49:[2,55],54:[2,55],55:107,56:108,58:111,59:112,75:[1,70],88:[1,113],89:[1,114]},{5:115,25:[1,5]},{8:116,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:118,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:119,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{13:121,14:122,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:123,44:63,58:47,59:48,61:120,63:25,64:26,65:27,75:[1,70],82:[1,28],87:[1,58],88:[1,59],89:[1,57],100:[1,56]},{13:121,14:122,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:123,44:63,58:47,59:48,61:124,63:25,64:26,65:27,75:[1,70],82:[1,28],87:[1,58],88:[1,59],89:[1,57],100:[1,56]},{1:[2,72],6:[2,72],25:[2,72],26:[2,72],40:[2,72],49:[2,72],54:[2,72],57:[2,72],66:[2,72],67:[2,72],68:[2,72],70:[2,72],72:[2,72],73:[2,72],77:[2,72],79:[1,128],83:[2,72],84:[2,72],85:[2,72],90:[2,72],92:[2,72],101:[2,72],103:[2,72],104:[2,72],105:[2,72],109:[2,72],117:[2,72],125:[2,72],127:[2,72],128:[2,72],129:[1,125],130:[1,126],131:[2,72],132:[2,72],133:[2,72],134:[2,72],135:[2,72],136:[2,72],137:[1,127]},{1:[2,180],6:[2,180],25:[2,180],26:[2,180],49:[2,180],54:[2,180],57:[2,180],72:[2,180],77:[2,180],85:[2,180],90:[2,180],92:[2,180],101:[2,180],103:[2,180],104:[2,180],105:[2,180],109:[2,180],117:[2,180],120:[1,129],125:[2,180],127:[2,180],128:[2,180],131:[2,180],132:[2,180],133:[2,180],134:[2,180],135:[2,180],136:[2,180]},{5:130,25:[1,5]},{5:131,25:[1,5]},{1:[2,147],6:[2,147],25:[2,147],26:[2,147],49:[2,147],54:[2,147],57:[2,147],72:[2,147],77:[2,147],85:[2,147],90:[2,147],92:[2,147],101:[2,147],103:[2,147],104:[2,147],105:[2,147],109:[2,147],117:[2,147],125:[2,147],127:[2,147],128:[2,147],131:[2,147],132:[2,147],133:[2,147],134:[2,147],135:[2,147],136:[2,147]},{5:132,25:[1,5]},{8:133,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,134],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,95],5:135,6:[2,95],13:121,14:122,25:[1,5],26:[2,95],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:123,44:63,49:[2,95],54:[2,95],57:[2,95],58:47,59:48,61:137,63:25,64:26,65:27,72:[2,95],75:[1,70],77:[2,95],79:[1,136],82:[1,28],85:[2,95],87:[1,58],88:[1,59],89:[1,57],90:[2,95],92:[2,95],100:[1,56],101:[2,95],103:[2,95],104:[2,95],105:[2,95],109:[2,95],117:[2,95],125:[2,95],127:[2,95],128:[2,95],131:[2,95],132:[2,95],133:[2,95],134:[2,95],135:[2,95],136:[2,95]},{8:138,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,47],6:[2,47],8:139,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,26:[2,47],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],101:[2,47],102:39,103:[2,47],105:[2,47],106:40,107:[1,67],108:41,109:[2,47],110:69,118:[1,42],123:37,124:[1,64],125:[2,47],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,48],6:[2,48],25:[2,48],26:[2,48],54:[2,48],77:[2,48],101:[2,48],103:[2,48],105:[2,48],109:[2,48],125:[2,48]},{1:[2,73],6:[2,73],25:[2,73],26:[2,73],40:[2,73],49:[2,73],54:[2,73],57:[2,73],66:[2,73],67:[2,73],68:[2,73],70:[2,73],72:[2,73],73:[2,73],77:[2,73],83:[2,73],84:[2,73],85:[2,73],90:[2,73],92:[2,73],101:[2,73],103:[2,73],104:[2,73],105:[2,73],109:[2,73],117:[2,73],125:[2,73],127:[2,73],128:[2,73],131:[2,73],132:[2,73],133:[2,73],134:[2,73],135:[2,73],136:[2,73]},{1:[2,74],6:[2,74],25:[2,74],26:[2,74],40:[2,74],49:[2,74],54:[2,74],57:[2,74],66:[2,74],67:[2,74],68:[2,74],70:[2,74],72:[2,74],73:[2,74],77:[2,74],83:[2,74],84:[2,74],85:[2,74],90:[2,74],92:[2,74],101:[2,74],103:[2,74],104:[2,74],105:[2,74],109:[2,74],117:[2,74],125:[2,74],127:[2,74],128:[2,74],131:[2,74],132:[2,74],133:[2,74],134:[2,74],135:[2,74],136:[2,74]},{1:[2,29],6:[2,29],25:[2,29],26:[2,29],49:[2,29],54:[2,29],57:[2,29],66:[2,29],67:[2,29],68:[2,29],70:[2,29],72:[2,29],73:[2,29],77:[2,29],83:[2,29],84:[2,29],85:[2,29],90:[2,29],92:[2,29],101:[2,29],103:[2,29],104:[2,29],105:[2,29],109:[2,29],117:[2,29],125:[2,29],127:[2,29],128:[2,29],131:[2,29],132:[2,29],133:[2,29],134:[2,29],135:[2,29],136:[2,29]},{1:[2,30],6:[2,30],25:[2,30],26:[2,30],49:[2,30],54:[2,30],57:[2,30],66:[2,30],67:[2,30],68:[2,30],70:[2,30],72:[2,30],73:[2,30],77:[2,30],83:[2,30],84:[2,30],85:[2,30],90:[2,30],92:[2,30],101:[2,30],103:[2,30],104:[2,30],105:[2,30],109:[2,30],117:[2,30],125:[2,30],127:[2,30],128:[2,30],131:[2,30],132:[2,30],133:[2,30],134:[2,30],135:[2,30],136:[2,30]},{1:[2,31],6:[2,31],25:[2,31],26:[2,31],49:[2,31],54:[2,31],57:[2,31],66:[2,31],67:[2,31],68:[2,31],70:[2,31],72:[2,31],73:[2,31],77:[2,31],83:[2,31],84:[2,31],85:[2,31],90:[2,31],92:[2,31],101:[2,31],103:[2,31],104:[2,31],105:[2,31],109:[2,31],117:[2,31],125:[2,31],127:[2,31],128:[2,31],131:[2,31],132:[2,31],133:[2,31],134:[2,31],135:[2,31],136:[2,31]},{1:[2,32],6:[2,32],25:[2,32],26:[2,32],49:[2,32],54:[2,32],57:[2,32],66:[2,32],67:[2,32],68:[2,32],70:[2,32],72:[2,32],73:[2,32],77:[2,32],83:[2,32],84:[2,32],85:[2,32],90:[2,32],92:[2,32],101:[2,32],103:[2,32],104:[2,32],105:[2,32],109:[2,32],117:[2,32],125:[2,32],127:[2,32],128:[2,32],131:[2,32],132:[2,32],133:[2,32],134:[2,32],135:[2,32],136:[2,32]},{1:[2,33],6:[2,33],25:[2,33],26:[2,33],49:[2,33],54:[2,33],57:[2,33],66:[2,33],67:[2,33],68:[2,33],70:[2,33],72:[2,33],73:[2,33],77:[2,33],83:[2,33],84:[2,33],85:[2,33],90:[2,33],92:[2,33],101:[2,33],103:[2,33],104:[2,33],105:[2,33],109:[2,33],117:[2,33],125:[2,33],127:[2,33],128:[2,33],131:[2,33],132:[2,33],133:[2,33],134:[2,33],135:[2,33],136:[2,33]},{1:[2,34],6:[2,34],25:[2,34],26:[2,34],49:[2,34],54:[2,34],57:[2,34],66:[2,34],67:[2,34],68:[2,34],70:[2,34],72:[2,34],73:[2,34],77:[2,34],83:[2,34],84:[2,34],85:[2,34],90:[2,34],92:[2,34],101:[2,34],103:[2,34],104:[2,34],105:[2,34],109:[2,34],117:[2,34],125:[2,34],127:[2,34],128:[2,34],131:[2,34],132:[2,34],133:[2,34],134:[2,34],135:[2,34],136:[2,34]},{1:[2,35],6:[2,35],25:[2,35],26:[2,35],49:[2,35],54:[2,35],57:[2,35],66:[2,35],67:[2,35],68:[2,35],70:[2,35],72:[2,35],73:[2,35],77:[2,35],83:[2,35],84:[2,35],85:[2,35],90:[2,35],92:[2,35],101:[2,35],103:[2,35],104:[2,35],105:[2,35],109:[2,35],117:[2,35],125:[2,35],127:[2,35],128:[2,35],131:[2,35],132:[2,35],133:[2,35],134:[2,35],135:[2,35],136:[2,35]},{4:140,7:4,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,141],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:142,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],86:144,87:[1,58],88:[1,59],89:[1,57],90:[1,143],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,111],6:[2,111],25:[2,111],26:[2,111],49:[2,111],54:[2,111],57:[2,111],66:[2,111],67:[2,111],68:[2,111],70:[2,111],72:[2,111],73:[2,111],77:[2,111],83:[2,111],84:[2,111],85:[2,111],90:[2,111],92:[2,111],101:[2,111],103:[2,111],104:[2,111],105:[2,111],109:[2,111],117:[2,111],125:[2,111],127:[2,111],128:[2,111],131:[2,111],132:[2,111],133:[2,111],134:[2,111],135:[2,111],136:[2,111]},{1:[2,112],6:[2,112],25:[2,112],26:[2,112],27:148,28:[1,73],49:[2,112],54:[2,112],57:[2,112],66:[2,112],67:[2,112],68:[2,112],70:[2,112],72:[2,112],73:[2,112],77:[2,112],83:[2,112],84:[2,112],85:[2,112],90:[2,112],92:[2,112],101:[2,112],103:[2,112],104:[2,112],105:[2,112],109:[2,112],117:[2,112],125:[2,112],127:[2,112],128:[2,112],131:[2,112],132:[2,112],133:[2,112],134:[2,112],135:[2,112],136:[2,112]},{25:[2,51]},{25:[2,52]},{1:[2,68],6:[2,68],25:[2,68],26:[2,68],40:[2,68],49:[2,68],54:[2,68],57:[2,68],66:[2,68],67:[2,68],68:[2,68],70:[2,68],72:[2,68],73:[2,68],77:[2,68],79:[2,68],83:[2,68],84:[2,68],85:[2,68],90:[2,68],92:[2,68],101:[2,68],103:[2,68],104:[2,68],105:[2,68],109:[2,68],117:[2,68],125:[2,68],127:[2,68],128:[2,68],129:[2,68],130:[2,68],131:[2,68],132:[2,68],133:[2,68],134:[2,68],135:[2,68],136:[2,68],137:[2,68]},{1:[2,71],6:[2,71],25:[2,71],26:[2,71],40:[2,71],49:[2,71],54:[2,71],57:[2,71],66:[2,71],67:[2,71],68:[2,71],70:[2,71],72:[2,71],73:[2,71],77:[2,71],79:[2,71],83:[2,71],84:[2,71],85:[2,71],90:[2,71],92:[2,71],101:[2,71],103:[2,71],104:[2,71],105:[2,71],109:[2,71],117:[2,71],125:[2,71],127:[2,71],128:[2,71],129:[2,71],130:[2,71],131:[2,71],132:[2,71],133:[2,71],134:[2,71],135:[2,71],136:[2,71],137:[2,71]},{8:149,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:150,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:151,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{5:152,8:153,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,5],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{27:158,28:[1,73],44:159,58:160,59:161,64:154,75:[1,70],88:[1,113],89:[1,57],112:155,113:[1,156],114:157},{111:162,115:[1,163],116:[1,164]},{6:[2,90],11:168,25:[2,90],27:169,28:[1,73],29:170,30:[1,71],31:[1,72],41:166,42:167,44:171,46:[1,46],54:[2,90],76:165,77:[2,90],88:[1,113]},{1:[2,27],6:[2,27],25:[2,27],26:[2,27],43:[2,27],49:[2,27],54:[2,27],57:[2,27],66:[2,27],67:[2,27],68:[2,27],70:[2,27],72:[2,27],73:[2,27],77:[2,27],83:[2,27],84:[2,27],85:[2,27],90:[2,27],92:[2,27],101:[2,27],103:[2,27],104:[2,27],105:[2,27],109:[2,27],117:[2,27],125:[2,27],127:[2,27],128:[2,27],131:[2,27],132:[2,27],133:[2,27],134:[2,27],135:[2,27],136:[2,27]},{1:[2,28],6:[2,28],25:[2,28],26:[2,28],43:[2,28],49:[2,28],54:[2,28],57:[2,28],66:[2,28],67:[2,28],68:[2,28],70:[2,28],72:[2,28],73:[2,28],77:[2,28],83:[2,28],84:[2,28],85:[2,28],90:[2,28],92:[2,28],101:[2,28],103:[2,28],104:[2,28],105:[2,28],109:[2,28],117:[2,28],125:[2,28],127:[2,28],128:[2,28],131:[2,28],132:[2,28],133:[2,28],134:[2,28],135:[2,28],136:[2,28]},{1:[2,26],6:[2,26],25:[2,26],26:[2,26],40:[2,26],43:[2,26],49:[2,26],54:[2,26],57:[2,26],66:[2,26],67:[2,26],68:[2,26],70:[2,26],72:[2,26],73:[2,26],77:[2,26],79:[2,26],83:[2,26],84:[2,26],85:[2,26],90:[2,26],92:[2,26],101:[2,26],103:[2,26],104:[2,26],105:[2,26],109:[2,26],115:[2,26],116:[2,26],117:[2,26],125:[2,26],127:[2,26],128:[2,26],129:[2,26],130:[2,26],131:[2,26],132:[2,26],133:[2,26],134:[2,26],135:[2,26],136:[2,26],137:[2,26]},{1:[2,6],6:[2,6],7:172,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,26:[2,6],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],101:[2,6],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,3]},{1:[2,24],6:[2,24],25:[2,24],26:[2,24],49:[2,24],54:[2,24],57:[2,24],72:[2,24],77:[2,24],85:[2,24],90:[2,24],92:[2,24],97:[2,24],98:[2,24],101:[2,24],103:[2,24],104:[2,24],105:[2,24],109:[2,24],117:[2,24],120:[2,24],122:[2,24],125:[2,24],127:[2,24],128:[2,24],131:[2,24],132:[2,24],133:[2,24],134:[2,24],135:[2,24],136:[2,24]},{6:[1,74],26:[1,173]},{1:[2,191],6:[2,191],25:[2,191],26:[2,191],49:[2,191],54:[2,191],57:[2,191],72:[2,191],77:[2,191],85:[2,191],90:[2,191],92:[2,191],101:[2,191],103:[2,191],104:[2,191],105:[2,191],109:[2,191],117:[2,191],125:[2,191],127:[2,191],128:[2,191],131:[2,191],132:[2,191],133:[2,191],134:[2,191],135:[2,191],136:[2,191]},{8:174,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:175,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:176,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:177,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:178,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:179,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:180,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:181,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,146],6:[2,146],25:[2,146],26:[2,146],49:[2,146],54:[2,146],57:[2,146],72:[2,146],77:[2,146],85:[2,146],90:[2,146],92:[2,146],101:[2,146],103:[2,146],104:[2,146],105:[2,146],109:[2,146],117:[2,146],125:[2,146],127:[2,146],128:[2,146],131:[2,146],132:[2,146],133:[2,146],134:[2,146],135:[2,146],136:[2,146]},{1:[2,151],6:[2,151],25:[2,151],26:[2,151],49:[2,151],54:[2,151],57:[2,151],72:[2,151],77:[2,151],85:[2,151],90:[2,151],92:[2,151],101:[2,151],103:[2,151],104:[2,151],105:[2,151],109:[2,151],117:[2,151],125:[2,151],127:[2,151],128:[2,151],131:[2,151],132:[2,151],133:[2,151],134:[2,151],135:[2,151],136:[2,151]},{8:182,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,145],6:[2,145],25:[2,145],26:[2,145],49:[2,145],54:[2,145],57:[2,145],72:[2,145],77:[2,145],85:[2,145],90:[2,145],92:[2,145],101:[2,145],103:[2,145],104:[2,145],105:[2,145],109:[2,145],117:[2,145],125:[2,145],127:[2,145],128:[2,145],131:[2,145],132:[2,145],133:[2,145],134:[2,145],135:[2,145],136:[2,145]},{1:[2,150],6:[2,150],25:[2,150],26:[2,150],49:[2,150],54:[2,150],57:[2,150],72:[2,150],77:[2,150],85:[2,150],90:[2,150],92:[2,150],101:[2,150],103:[2,150],104:[2,150],105:[2,150],109:[2,150],117:[2,150],125:[2,150],127:[2,150],128:[2,150],131:[2,150],132:[2,150],133:[2,150],134:[2,150],135:[2,150],136:[2,150]},{81:183,84:[1,105]},{1:[2,69],6:[2,69],25:[2,69],26:[2,69],40:[2,69],49:[2,69],54:[2,69],57:[2,69],66:[2,69],67:[2,69],68:[2,69],70:[2,69],72:[2,69],73:[2,69],77:[2,69],79:[2,69],83:[2,69],84:[2,69],85:[2,69],90:[2,69],92:[2,69],101:[2,69],103:[2,69],104:[2,69],105:[2,69],109:[2,69],117:[2,69],125:[2,69],127:[2,69],128:[2,69],129:[2,69],130:[2,69],131:[2,69],132:[2,69],133:[2,69],134:[2,69],135:[2,69],136:[2,69],137:[2,69]},{84:[2,108]},{27:184,28:[1,73]},{27:185,28:[1,73]},{1:[2,83],6:[2,83],25:[2,83],26:[2,83],27:186,28:[1,73],40:[2,83],49:[2,83],54:[2,83],57:[2,83],66:[2,83],67:[2,83],68:[2,83],70:[2,83],72:[2,83],73:[2,83],77:[2,83],79:[2,83],83:[2,83],84:[2,83],85:[2,83],90:[2,83],92:[2,83],101:[2,83],103:[2,83],104:[2,83],105:[2,83],109:[2,83],117:[2,83],125:[2,83],127:[2,83],128:[2,83],129:[2,83],130:[2,83],131:[2,83],132:[2,83],133:[2,83],134:[2,83],135:[2,83],136:[2,83],137:[2,83]},{1:[2,84],6:[2,84],25:[2,84],26:[2,84],40:[2,84],49:[2,84],54:[2,84],57:[2,84],66:[2,84],67:[2,84],68:[2,84],70:[2,84],72:[2,84],73:[2,84],77:[2,84],79:[2,84],83:[2,84],84:[2,84],85:[2,84],90:[2,84],92:[2,84],101:[2,84],103:[2,84],104:[2,84],105:[2,84],109:[2,84],117:[2,84],125:[2,84],127:[2,84],128:[2,84],129:[2,84],130:[2,84],131:[2,84],132:[2,84],133:[2,84],134:[2,84],135:[2,84],136:[2,84],137:[2,84]},{8:188,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],57:[1,192],58:47,59:48,61:36,63:25,64:26,65:27,71:187,74:189,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],91:190,92:[1,191],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{69:193,70:[1,99],73:[1,100]},{81:194,84:[1,105]},{1:[2,70],6:[2,70],25:[2,70],26:[2,70],40:[2,70],49:[2,70],54:[2,70],57:[2,70],66:[2,70],67:[2,70],68:[2,70],70:[2,70],72:[2,70],73:[2,70],77:[2,70],79:[2,70],83:[2,70],84:[2,70],85:[2,70],90:[2,70],92:[2,70],101:[2,70],103:[2,70],104:[2,70],105:[2,70],109:[2,70],117:[2,70],125:[2,70],127:[2,70],128:[2,70],129:[2,70],130:[2,70],131:[2,70],132:[2,70],133:[2,70],134:[2,70],135:[2,70],136:[2,70],137:[2,70]},{6:[1,196],8:195,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,197],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,106],6:[2,106],25:[2,106],26:[2,106],49:[2,106],54:[2,106],57:[2,106],66:[2,106],67:[2,106],68:[2,106],70:[2,106],72:[2,106],73:[2,106],77:[2,106],83:[2,106],84:[2,106],85:[2,106],90:[2,106],92:[2,106],101:[2,106],103:[2,106],104:[2,106],105:[2,106],109:[2,106],117:[2,106],125:[2,106],127:[2,106],128:[2,106],131:[2,106],132:[2,106],133:[2,106],134:[2,106],135:[2,106],136:[2,106]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],85:[1,198],86:199,87:[1,58],88:[1,59],89:[1,57],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,53],25:[2,53],49:[1,201],53:203,54:[1,202]},{6:[2,56],25:[2,56],26:[2,56],49:[2,56],54:[2,56]},{6:[2,60],25:[2,60],26:[2,60],40:[1,205],49:[2,60],54:[2,60],57:[1,204]},{6:[2,63],25:[2,63],26:[2,63],40:[2,63],49:[2,63],54:[2,63],57:[2,63]},{6:[2,64],25:[2,64],26:[2,64],40:[2,64],49:[2,64],54:[2,64],57:[2,64]},{6:[2,65],25:[2,65],26:[2,65],40:[2,65],49:[2,65],54:[2,65],57:[2,65]},{6:[2,66],25:[2,66],26:[2,66],40:[2,66],49:[2,66],54:[2,66],57:[2,66]},{27:148,28:[1,73]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],86:144,87:[1,58],88:[1,59],89:[1,57],90:[1,143],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,50],6:[2,50],25:[2,50],26:[2,50],49:[2,50],54:[2,50],57:[2,50],72:[2,50],77:[2,50],85:[2,50],90:[2,50],92:[2,50],101:[2,50],103:[2,50],104:[2,50],105:[2,50],109:[2,50],117:[2,50],125:[2,50],127:[2,50],128:[2,50],131:[2,50],132:[2,50],133:[2,50],134:[2,50],135:[2,50],136:[2,50]},{1:[2,184],6:[2,184],25:[2,184],26:[2,184],49:[2,184],54:[2,184],57:[2,184],72:[2,184],77:[2,184],85:[2,184],90:[2,184],92:[2,184],101:[2,184],102:87,103:[2,184],104:[2,184],105:[2,184],108:88,109:[2,184],110:69,117:[2,184],125:[2,184],127:[2,184],128:[2,184],131:[1,78],132:[2,184],133:[2,184],134:[2,184],135:[2,184],136:[2,184]},{102:90,103:[1,65],105:[1,66],108:91,109:[1,68],110:69,125:[1,89]},{1:[2,185],6:[2,185],25:[2,185],26:[2,185],49:[2,185],54:[2,185],57:[2,185],72:[2,185],77:[2,185],85:[2,185],90:[2,185],92:[2,185],101:[2,185],102:87,103:[2,185],104:[2,185],105:[2,185],108:88,109:[2,185],110:69,117:[2,185],125:[2,185],127:[2,185],128:[2,185],131:[1,78],132:[2,185],133:[2,185],134:[2,185],135:[2,185],136:[2,185]},{1:[2,186],6:[2,186],25:[2,186],26:[2,186],49:[2,186],54:[2,186],57:[2,186],72:[2,186],77:[2,186],85:[2,186],90:[2,186],92:[2,186],101:[2,186],102:87,103:[2,186],104:[2,186],105:[2,186],108:88,109:[2,186],110:69,117:[2,186],125:[2,186],127:[2,186],128:[2,186],131:[1,78],132:[2,186],133:[2,186],134:[2,186],135:[2,186],136:[2,186]},{1:[2,187],6:[2,187],25:[2,187],26:[2,187],49:[2,187],54:[2,187],57:[2,187],66:[2,72],67:[2,72],68:[2,72],70:[2,72],72:[2,187],73:[2,72],77:[2,187],83:[2,72],84:[2,72],85:[2,187],90:[2,187],92:[2,187],101:[2,187],103:[2,187],104:[2,187],105:[2,187],109:[2,187],117:[2,187],125:[2,187],127:[2,187],128:[2,187],131:[2,187],132:[2,187],133:[2,187],134:[2,187],135:[2,187],136:[2,187]},{62:93,66:[1,95],67:[1,96],68:[1,97],69:98,70:[1,99],73:[1,100],80:92,83:[1,94],84:[2,107]},{62:102,66:[1,95],67:[1,96],68:[1,97],69:98,70:[1,99],73:[1,100],80:101,83:[1,94],84:[2,107]},{66:[2,75],67:[2,75],68:[2,75],70:[2,75],73:[2,75],83:[2,75],84:[2,75]},{1:[2,188],6:[2,188],25:[2,188],26:[2,188],49:[2,188],54:[2,188],57:[2,188],66:[2,72],67:[2,72],68:[2,72],70:[2,72],72:[2,188],73:[2,72],77:[2,188],83:[2,72],84:[2,72],85:[2,188],90:[2,188],92:[2,188],101:[2,188],103:[2,188],104:[2,188],105:[2,188],109:[2,188],117:[2,188],125:[2,188],127:[2,188],128:[2,188],131:[2,188],132:[2,188],133:[2,188],134:[2,188],135:[2,188],136:[2,188]},{1:[2,189],6:[2,189],25:[2,189],26:[2,189],49:[2,189],54:[2,189],57:[2,189],72:[2,189],77:[2,189],85:[2,189],90:[2,189],92:[2,189],101:[2,189],103:[2,189],104:[2,189],105:[2,189],109:[2,189],117:[2,189],125:[2,189],127:[2,189],128:[2,189],131:[2,189],132:[2,189],133:[2,189],134:[2,189],135:[2,189],136:[2,189]},{1:[2,190],6:[2,190],25:[2,190],26:[2,190],49:[2,190],54:[2,190],57:[2,190],72:[2,190],77:[2,190],85:[2,190],90:[2,190],92:[2,190],101:[2,190],103:[2,190],104:[2,190],105:[2,190],109:[2,190],117:[2,190],125:[2,190],127:[2,190],128:[2,190],131:[2,190],132:[2,190],133:[2,190],134:[2,190],135:[2,190],136:[2,190]},{8:206,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,207],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:208,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{5:209,25:[1,5],124:[1,210]},{1:[2,132],6:[2,132],25:[2,132],26:[2,132],49:[2,132],54:[2,132],57:[2,132],72:[2,132],77:[2,132],85:[2,132],90:[2,132],92:[2,132],96:211,97:[1,212],98:[1,213],101:[2,132],103:[2,132],104:[2,132],105:[2,132],109:[2,132],117:[2,132],125:[2,132],127:[2,132],128:[2,132],131:[2,132],132:[2,132],133:[2,132],134:[2,132],135:[2,132],136:[2,132]},{1:[2,144],6:[2,144],25:[2,144],26:[2,144],49:[2,144],54:[2,144],57:[2,144],72:[2,144],77:[2,144],85:[2,144],90:[2,144],92:[2,144],101:[2,144],103:[2,144],104:[2,144],105:[2,144],109:[2,144],117:[2,144],125:[2,144],127:[2,144],128:[2,144],131:[2,144],132:[2,144],133:[2,144],134:[2,144],135:[2,144],136:[2,144]},{1:[2,152],6:[2,152],25:[2,152],26:[2,152],49:[2,152],54:[2,152],57:[2,152],72:[2,152],77:[2,152],85:[2,152],90:[2,152],92:[2,152],101:[2,152],103:[2,152],104:[2,152],105:[2,152],109:[2,152],117:[2,152],125:[2,152],127:[2,152],128:[2,152],131:[2,152],132:[2,152],133:[2,152],134:[2,152],135:[2,152],136:[2,152]},{25:[1,214],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{119:215,121:216,122:[1,217]},{1:[2,96],6:[2,96],25:[2,96],26:[2,96],49:[2,96],54:[2,96],57:[2,96],72:[2,96],77:[2,96],85:[2,96],90:[2,96],92:[2,96],101:[2,96],103:[2,96],104:[2,96],105:[2,96],109:[2,96],117:[2,96],125:[2,96],127:[2,96],128:[2,96],131:[2,96],132:[2,96],133:[2,96],134:[2,96],135:[2,96],136:[2,96]},{8:218,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,99],5:219,6:[2,99],25:[1,5],26:[2,99],49:[2,99],54:[2,99],57:[2,99],66:[2,72],67:[2,72],68:[2,72],70:[2,72],72:[2,99],73:[2,72],77:[2,99],79:[1,220],83:[2,72],84:[2,72],85:[2,99],90:[2,99],92:[2,99],101:[2,99],103:[2,99],104:[2,99],105:[2,99],109:[2,99],117:[2,99],125:[2,99],127:[2,99],128:[2,99],131:[2,99],132:[2,99],133:[2,99],134:[2,99],135:[2,99],136:[2,99]},{1:[2,137],6:[2,137],25:[2,137],26:[2,137],49:[2,137],54:[2,137],57:[2,137],72:[2,137],77:[2,137],85:[2,137],90:[2,137],92:[2,137],101:[2,137],102:87,103:[2,137],104:[2,137],105:[2,137],108:88,109:[2,137],110:69,117:[2,137],125:[2,137],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,46],6:[2,46],26:[2,46],101:[2,46],102:87,103:[2,46],105:[2,46],108:88,109:[2,46],110:69,125:[2,46],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,74],101:[1,221]},{4:222,7:4,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,128],25:[2,128],54:[2,128],57:[1,224],90:[2,128],91:223,92:[1,191],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,114],6:[2,114],25:[2,114],26:[2,114],40:[2,114],49:[2,114],54:[2,114],57:[2,114],66:[2,114],67:[2,114],68:[2,114],70:[2,114],72:[2,114],73:[2,114],77:[2,114],83:[2,114],84:[2,114],85:[2,114],90:[2,114],92:[2,114],101:[2,114],103:[2,114],104:[2,114],105:[2,114],109:[2,114],115:[2,114],116:[2,114],117:[2,114],125:[2,114],127:[2,114],128:[2,114],131:[2,114],132:[2,114],133:[2,114],134:[2,114],135:[2,114],136:[2,114]},{6:[2,53],25:[2,53],53:225,54:[1,226],90:[2,53]},{6:[2,123],25:[2,123],26:[2,123],54:[2,123],85:[2,123],90:[2,123]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],86:227,87:[1,58],88:[1,59],89:[1,57],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,129],25:[2,129],26:[2,129],54:[2,129],85:[2,129],90:[2,129]},{1:[2,113],6:[2,113],25:[2,113],26:[2,113],40:[2,113],43:[2,113],49:[2,113],54:[2,113],57:[2,113],66:[2,113],67:[2,113],68:[2,113],70:[2,113],72:[2,113],73:[2,113],77:[2,113],79:[2,113],83:[2,113],84:[2,113],85:[2,113],90:[2,113],92:[2,113],101:[2,113],103:[2,113],104:[2,113],105:[2,113],109:[2,113],115:[2,113],116:[2,113],117:[2,113],125:[2,113],127:[2,113],128:[2,113],129:[2,113],130:[2,113],131:[2,113],132:[2,113],133:[2,113],134:[2,113],135:[2,113],136:[2,113],137:[2,113]},{5:228,25:[1,5],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,140],6:[2,140],25:[2,140],26:[2,140],49:[2,140],54:[2,140],57:[2,140],72:[2,140],77:[2,140],85:[2,140],90:[2,140],92:[2,140],101:[2,140],102:87,103:[1,65],104:[1,229],105:[1,66],108:88,109:[1,68],110:69,117:[2,140],125:[2,140],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,142],6:[2,142],25:[2,142],26:[2,142],49:[2,142],54:[2,142],57:[2,142],72:[2,142],77:[2,142],85:[2,142],90:[2,142],92:[2,142],101:[2,142],102:87,103:[1,65],104:[1,230],105:[1,66],108:88,109:[1,68],110:69,117:[2,142],125:[2,142],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,148],6:[2,148],25:[2,148],26:[2,148],49:[2,148],54:[2,148],57:[2,148],72:[2,148],77:[2,148],85:[2,148],90:[2,148],92:[2,148],101:[2,148],103:[2,148],104:[2,148],105:[2,148],109:[2,148],117:[2,148],125:[2,148],127:[2,148],128:[2,148],131:[2,148],132:[2,148],133:[2,148],134:[2,148],135:[2,148],136:[2,148]},{1:[2,149],6:[2,149],25:[2,149],26:[2,149],49:[2,149],54:[2,149],57:[2,149],72:[2,149],77:[2,149],85:[2,149],90:[2,149],92:[2,149],101:[2,149],102:87,103:[1,65],104:[2,149],105:[1,66],108:88,109:[1,68],110:69,117:[2,149],125:[2,149],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,153],6:[2,153],25:[2,153],26:[2,153],49:[2,153],54:[2,153],57:[2,153],72:[2,153],77:[2,153],85:[2,153],90:[2,153],92:[2,153],101:[2,153],103:[2,153],104:[2,153],105:[2,153],109:[2,153],117:[2,153],125:[2,153],127:[2,153],128:[2,153],131:[2,153],132:[2,153],133:[2,153],134:[2,153],135:[2,153],136:[2,153]},{115:[2,155],116:[2,155]},{27:158,28:[1,73],44:159,58:160,59:161,75:[1,70],88:[1,113],89:[1,114],112:231,114:157},{54:[1,232],115:[2,161],116:[2,161]},{54:[2,157],115:[2,157],116:[2,157]},{54:[2,158],115:[2,158],116:[2,158]},{54:[2,159],115:[2,159],116:[2,159]},{54:[2,160],115:[2,160],116:[2,160]},{1:[2,154],6:[2,154],25:[2,154],26:[2,154],49:[2,154],54:[2,154],57:[2,154],72:[2,154],77:[2,154],85:[2,154],90:[2,154],92:[2,154],101:[2,154],103:[2,154],104:[2,154],105:[2,154],109:[2,154],117:[2,154],125:[2,154],127:[2,154],128:[2,154],131:[2,154],132:[2,154],133:[2,154],134:[2,154],135:[2,154],136:[2,154]},{8:233,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:234,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,53],25:[2,53],53:235,54:[1,236],77:[2,53]},{6:[2,91],25:[2,91],26:[2,91],54:[2,91],77:[2,91]},{6:[2,39],25:[2,39],26:[2,39],43:[1,237],54:[2,39],77:[2,39]},{6:[2,42],25:[2,42],26:[2,42],54:[2,42],77:[2,42]},{6:[2,43],25:[2,43],26:[2,43],43:[2,43],54:[2,43],77:[2,43]},{6:[2,44],25:[2,44],26:[2,44],43:[2,44],54:[2,44],77:[2,44]},{6:[2,45],25:[2,45],26:[2,45],43:[2,45],54:[2,45],77:[2,45]},{1:[2,5],6:[2,5],26:[2,5],101:[2,5]},{1:[2,25],6:[2,25],25:[2,25],26:[2,25],49:[2,25],54:[2,25],57:[2,25],72:[2,25],77:[2,25],85:[2,25],90:[2,25],92:[2,25],97:[2,25],98:[2,25],101:[2,25],103:[2,25],104:[2,25],105:[2,25],109:[2,25],117:[2,25],120:[2,25],122:[2,25],125:[2,25],127:[2,25],128:[2,25],131:[2,25],132:[2,25],133:[2,25],134:[2,25],135:[2,25],136:[2,25]},{1:[2,192],6:[2,192],25:[2,192],26:[2,192],49:[2,192],54:[2,192],57:[2,192],72:[2,192],77:[2,192],85:[2,192],90:[2,192],92:[2,192],101:[2,192],102:87,103:[2,192],104:[2,192],105:[2,192],108:88,109:[2,192],110:69,117:[2,192],125:[2,192],127:[2,192],128:[2,192],131:[1,78],132:[1,81],133:[2,192],134:[2,192],135:[2,192],136:[2,192]},{1:[2,193],6:[2,193],25:[2,193],26:[2,193],49:[2,193],54:[2,193],57:[2,193],72:[2,193],77:[2,193],85:[2,193],90:[2,193],92:[2,193],101:[2,193],102:87,103:[2,193],104:[2,193],105:[2,193],108:88,109:[2,193],110:69,117:[2,193],125:[2,193],127:[2,193],128:[2,193],131:[1,78],132:[1,81],133:[2,193],134:[2,193],135:[2,193],136:[2,193]},{1:[2,194],6:[2,194],25:[2,194],26:[2,194],49:[2,194],54:[2,194],57:[2,194],72:[2,194],77:[2,194],85:[2,194],90:[2,194],92:[2,194],101:[2,194],102:87,103:[2,194],104:[2,194],105:[2,194],108:88,109:[2,194],110:69,117:[2,194],125:[2,194],127:[2,194],128:[2,194],131:[1,78],132:[2,194],133:[2,194],134:[2,194],135:[2,194],136:[2,194]},{1:[2,195],6:[2,195],25:[2,195],26:[2,195],49:[2,195],54:[2,195],57:[2,195],72:[2,195],77:[2,195],85:[2,195],90:[2,195],92:[2,195],101:[2,195],102:87,103:[2,195],104:[2,195],105:[2,195],108:88,109:[2,195],110:69,117:[2,195],125:[2,195],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[2,195],134:[2,195],135:[2,195],136:[2,195]},{1:[2,196],6:[2,196],25:[2,196],26:[2,196],49:[2,196],54:[2,196],57:[2,196],72:[2,196],77:[2,196],85:[2,196],90:[2,196],92:[2,196],101:[2,196],102:87,103:[2,196],104:[2,196],105:[2,196],108:88,109:[2,196],110:69,117:[2,196],125:[2,196],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[2,196],135:[2,196],136:[1,85]},{1:[2,197],6:[2,197],25:[2,197],26:[2,197],49:[2,197],54:[2,197],57:[2,197],72:[2,197],77:[2,197],85:[2,197],90:[2,197],92:[2,197],101:[2,197],102:87,103:[2,197],104:[2,197],105:[2,197],108:88,109:[2,197],110:69,117:[2,197],125:[2,197],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[2,197],136:[1,85]},{1:[2,198],6:[2,198],25:[2,198],26:[2,198],49:[2,198],54:[2,198],57:[2,198],72:[2,198],77:[2,198],85:[2,198],90:[2,198],92:[2,198],101:[2,198],102:87,103:[2,198],104:[2,198],105:[2,198],108:88,109:[2,198],110:69,117:[2,198],125:[2,198],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[2,198],135:[2,198],136:[2,198]},{1:[2,183],6:[2,183],25:[2,183],26:[2,183],49:[2,183],54:[2,183],57:[2,183],72:[2,183],77:[2,183],85:[2,183],90:[2,183],92:[2,183],101:[2,183],102:87,103:[1,65],104:[2,183],105:[1,66],108:88,109:[1,68],110:69,117:[2,183],125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,182],6:[2,182],25:[2,182],26:[2,182],49:[2,182],54:[2,182],57:[2,182],72:[2,182],77:[2,182],85:[2,182],90:[2,182],92:[2,182],101:[2,182],102:87,103:[1,65],104:[2,182],105:[1,66],108:88,109:[1,68],110:69,117:[2,182],125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,103],6:[2,103],25:[2,103],26:[2,103],49:[2,103],54:[2,103],57:[2,103],66:[2,103],67:[2,103],68:[2,103],70:[2,103],72:[2,103],73:[2,103],77:[2,103],83:[2,103],84:[2,103],85:[2,103],90:[2,103],92:[2,103],101:[2,103],103:[2,103],104:[2,103],105:[2,103],109:[2,103],117:[2,103],125:[2,103],127:[2,103],128:[2,103],131:[2,103],132:[2,103],133:[2,103],134:[2,103],135:[2,103],136:[2,103]},{1:[2,80],6:[2,80],25:[2,80],26:[2,80],40:[2,80],49:[2,80],54:[2,80],57:[2,80],66:[2,80],67:[2,80],68:[2,80],70:[2,80],72:[2,80],73:[2,80],77:[2,80],79:[2,80],83:[2,80],84:[2,80],85:[2,80],90:[2,80],92:[2,80],101:[2,80],103:[2,80],104:[2,80],105:[2,80],109:[2,80],117:[2,80],125:[2,80],127:[2,80],128:[2,80],129:[2,80],130:[2,80],131:[2,80],132:[2,80],133:[2,80],134:[2,80],135:[2,80],136:[2,80],137:[2,80]},{1:[2,81],6:[2,81],25:[2,81],26:[2,81],40:[2,81],49:[2,81],54:[2,81],57:[2,81],66:[2,81],67:[2,81],68:[2,81],70:[2,81],72:[2,81],73:[2,81],77:[2,81],79:[2,81],83:[2,81],84:[2,81],85:[2,81],90:[2,81],92:[2,81],101:[2,81],103:[2,81],104:[2,81],105:[2,81],109:[2,81],117:[2,81],125:[2,81],127:[2,81],128:[2,81],129:[2,81],130:[2,81],131:[2,81],132:[2,81],133:[2,81],134:[2,81],135:[2,81],136:[2,81],137:[2,81]},{1:[2,82],6:[2,82],25:[2,82],26:[2,82],40:[2,82],49:[2,82],54:[2,82],57:[2,82],66:[2,82],67:[2,82],68:[2,82],70:[2,82],72:[2,82],73:[2,82],77:[2,82],79:[2,82],83:[2,82],84:[2,82],85:[2,82],90:[2,82],92:[2,82],101:[2,82],103:[2,82],104:[2,82],105:[2,82],109:[2,82],117:[2,82],125:[2,82],127:[2,82],128:[2,82],129:[2,82],130:[2,82],131:[2,82],132:[2,82],133:[2,82],134:[2,82],135:[2,82],136:[2,82],137:[2,82]},{72:[1,238]},{57:[1,192],72:[2,87],91:239,92:[1,191],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{72:[2,88]},{8:240,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,72:[2,122],75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{12:[2,116],28:[2,116],30:[2,116],31:[2,116],33:[2,116],34:[2,116],35:[2,116],36:[2,116],37:[2,116],38:[2,116],45:[2,116],46:[2,116],47:[2,116],51:[2,116],52:[2,116],72:[2,116],75:[2,116],78:[2,116],82:[2,116],87:[2,116],88:[2,116],89:[2,116],95:[2,116],99:[2,116],100:[2,116],103:[2,116],105:[2,116],107:[2,116],109:[2,116],118:[2,116],124:[2,116],126:[2,116],127:[2,116],128:[2,116],129:[2,116],130:[2,116]},{12:[2,117],28:[2,117],30:[2,117],31:[2,117],33:[2,117],34:[2,117],35:[2,117],36:[2,117],37:[2,117],38:[2,117],45:[2,117],46:[2,117],47:[2,117],51:[2,117],52:[2,117],72:[2,117],75:[2,117],78:[2,117],82:[2,117],87:[2,117],88:[2,117],89:[2,117],95:[2,117],99:[2,117],100:[2,117],103:[2,117],105:[2,117],107:[2,117],109:[2,117],118:[2,117],124:[2,117],126:[2,117],127:[2,117],128:[2,117],129:[2,117],130:[2,117]},{1:[2,86],6:[2,86],25:[2,86],26:[2,86],40:[2,86],49:[2,86],54:[2,86],57:[2,86],66:[2,86],67:[2,86],68:[2,86],70:[2,86],72:[2,86],73:[2,86],77:[2,86],79:[2,86],83:[2,86],84:[2,86],85:[2,86],90:[2,86],92:[2,86],101:[2,86],103:[2,86],104:[2,86],105:[2,86],109:[2,86],117:[2,86],125:[2,86],127:[2,86],128:[2,86],129:[2,86],130:[2,86],131:[2,86],132:[2,86],133:[2,86],134:[2,86],135:[2,86],136:[2,86],137:[2,86]},{1:[2,104],6:[2,104],25:[2,104],26:[2,104],49:[2,104],54:[2,104],57:[2,104],66:[2,104],67:[2,104],68:[2,104],70:[2,104],72:[2,104],73:[2,104],77:[2,104],83:[2,104],84:[2,104],85:[2,104],90:[2,104],92:[2,104],101:[2,104],103:[2,104],104:[2,104],105:[2,104],109:[2,104],117:[2,104],125:[2,104],127:[2,104],128:[2,104],131:[2,104],132:[2,104],133:[2,104],134:[2,104],135:[2,104],136:[2,104]},{1:[2,36],6:[2,36],25:[2,36],26:[2,36],49:[2,36],54:[2,36],57:[2,36],72:[2,36],77:[2,36],85:[2,36],90:[2,36],92:[2,36],101:[2,36],102:87,103:[2,36],104:[2,36],105:[2,36],108:88,109:[2,36],110:69,117:[2,36],125:[2,36],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{8:241,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:242,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,109],6:[2,109],25:[2,109],26:[2,109],49:[2,109],54:[2,109],57:[2,109],66:[2,109],67:[2,109],68:[2,109],70:[2,109],72:[2,109],73:[2,109],77:[2,109],83:[2,109],84:[2,109],85:[2,109],90:[2,109],92:[2,109],101:[2,109],103:[2,109],104:[2,109],105:[2,109],109:[2,109],117:[2,109],125:[2,109],127:[2,109],128:[2,109],131:[2,109],132:[2,109],133:[2,109],134:[2,109],135:[2,109],136:[2,109]},{6:[2,53],25:[2,53],53:243,54:[1,226],85:[2,53]},{6:[2,128],25:[2,128],26:[2,128],54:[2,128],57:[1,244],85:[2,128],90:[2,128],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{50:245,51:[1,60],52:[1,61]},{6:[2,54],25:[2,54],26:[2,54],27:109,28:[1,73],44:110,55:246,56:108,58:111,59:112,75:[1,70],88:[1,113],89:[1,114]},{6:[1,247],25:[1,248]},{6:[2,61],25:[2,61],26:[2,61],49:[2,61],54:[2,61]},{8:249,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,199],6:[2,199],25:[2,199],26:[2,199],49:[2,199],54:[2,199],57:[2,199],72:[2,199],77:[2,199],85:[2,199],90:[2,199],92:[2,199],101:[2,199],102:87,103:[2,199],104:[2,199],105:[2,199],108:88,109:[2,199],110:69,117:[2,199],125:[2,199],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{8:250,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,201],6:[2,201],25:[2,201],26:[2,201],49:[2,201],54:[2,201],57:[2,201],72:[2,201],77:[2,201],85:[2,201],90:[2,201],92:[2,201],101:[2,201],102:87,103:[2,201],104:[2,201],105:[2,201],108:88,109:[2,201],110:69,117:[2,201],125:[2,201],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,181],6:[2,181],25:[2,181],26:[2,181],49:[2,181],54:[2,181],57:[2,181],72:[2,181],77:[2,181],85:[2,181],90:[2,181],92:[2,181],101:[2,181],103:[2,181],104:[2,181],105:[2,181],109:[2,181],117:[2,181],125:[2,181],127:[2,181],128:[2,181],131:[2,181],132:[2,181],133:[2,181],134:[2,181],135:[2,181],136:[2,181]},{8:251,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,133],6:[2,133],25:[2,133],26:[2,133],49:[2,133],54:[2,133],57:[2,133],72:[2,133],77:[2,133],85:[2,133],90:[2,133],92:[2,133],97:[1,252],101:[2,133],103:[2,133],104:[2,133],105:[2,133],109:[2,133],117:[2,133],125:[2,133],127:[2,133],128:[2,133],131:[2,133],132:[2,133],133:[2,133],134:[2,133],135:[2,133],136:[2,133]},{5:253,25:[1,5]},{27:254,28:[1,73]},{119:255,121:216,122:[1,217]},{26:[1,256],120:[1,257],121:258,122:[1,217]},{26:[2,174],120:[2,174],122:[2,174]},{8:260,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],94:259,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,97],5:261,6:[2,97],25:[1,5],26:[2,97],49:[2,97],54:[2,97],57:[2,97],72:[2,97],77:[2,97],85:[2,97],90:[2,97],92:[2,97],101:[2,97],102:87,103:[1,65],104:[2,97],105:[1,66],108:88,109:[1,68],110:69,117:[2,97],125:[2,97],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,100],6:[2,100],25:[2,100],26:[2,100],49:[2,100],54:[2,100],57:[2,100],72:[2,100],77:[2,100],85:[2,100],90:[2,100],92:[2,100],101:[2,100],103:[2,100],104:[2,100],105:[2,100],109:[2,100],117:[2,100],125:[2,100],127:[2,100],128:[2,100],131:[2,100],132:[2,100],133:[2,100],134:[2,100],135:[2,100],136:[2,100]},{8:262,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,138],6:[2,138],25:[2,138],26:[2,138],49:[2,138],54:[2,138],57:[2,138],66:[2,138],67:[2,138],68:[2,138],70:[2,138],72:[2,138],73:[2,138],77:[2,138],83:[2,138],84:[2,138],85:[2,138],90:[2,138],92:[2,138],101:[2,138],103:[2,138],104:[2,138],105:[2,138],109:[2,138],117:[2,138],125:[2,138],127:[2,138],128:[2,138],131:[2,138],132:[2,138],133:[2,138],134:[2,138],135:[2,138],136:[2,138]},{6:[1,74],26:[1,263]},{8:264,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,67],12:[2,117],25:[2,67],28:[2,117],30:[2,117],31:[2,117],33:[2,117],34:[2,117],35:[2,117],36:[2,117],37:[2,117],38:[2,117],45:[2,117],46:[2,117],47:[2,117],51:[2,117],52:[2,117],54:[2,67],75:[2,117],78:[2,117],82:[2,117],87:[2,117],88:[2,117],89:[2,117],90:[2,67],95:[2,117],99:[2,117],100:[2,117],103:[2,117],105:[2,117],107:[2,117],109:[2,117],118:[2,117],124:[2,117],126:[2,117],127:[2,117],128:[2,117],129:[2,117],130:[2,117]},{6:[1,266],25:[1,267],90:[1,265]},{6:[2,54],8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[2,54],26:[2,54],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],85:[2,54],87:[1,58],88:[1,59],89:[1,57],90:[2,54],93:268,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,53],25:[2,53],26:[2,53],53:269,54:[1,226]},{1:[2,178],6:[2,178],25:[2,178],26:[2,178],49:[2,178],54:[2,178],57:[2,178],72:[2,178],77:[2,178],85:[2,178],90:[2,178],92:[2,178],101:[2,178],103:[2,178],104:[2,178],105:[2,178],109:[2,178],117:[2,178],120:[2,178],125:[2,178],127:[2,178],128:[2,178],131:[2,178],132:[2,178],133:[2,178],134:[2,178],135:[2,178],136:[2,178]},{8:270,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:271,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{115:[2,156],116:[2,156]},{27:158,28:[1,73],44:159,58:160,59:161,75:[1,70],88:[1,113],89:[1,114],114:272},{1:[2,163],6:[2,163],25:[2,163],26:[2,163],49:[2,163],54:[2,163],57:[2,163],72:[2,163],77:[2,163],85:[2,163],90:[2,163],92:[2,163],101:[2,163],102:87,103:[2,163],104:[1,273],105:[2,163],108:88,109:[2,163],110:69,117:[1,274],125:[2,163],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,164],6:[2,164],25:[2,164],26:[2,164],49:[2,164],54:[2,164],57:[2,164],72:[2,164],77:[2,164],85:[2,164],90:[2,164],92:[2,164],101:[2,164],102:87,103:[2,164],104:[1,275],105:[2,164],108:88,109:[2,164],110:69,117:[2,164],125:[2,164],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,277],25:[1,278],77:[1,276]},{6:[2,54],11:168,25:[2,54],26:[2,54],27:169,28:[1,73],29:170,30:[1,71],31:[1,72],41:279,42:167,44:171,46:[1,46],77:[2,54],88:[1,113]},{8:280,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,281],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,85],6:[2,85],25:[2,85],26:[2,85],40:[2,85],49:[2,85],54:[2,85],57:[2,85],66:[2,85],67:[2,85],68:[2,85],70:[2,85],72:[2,85],73:[2,85],77:[2,85],79:[2,85],83:[2,85],84:[2,85],85:[2,85],90:[2,85],92:[2,85],101:[2,85],103:[2,85],104:[2,85],105:[2,85],109:[2,85],117:[2,85],125:[2,85],127:[2,85],128:[2,85],129:[2,85],130:[2,85],131:[2,85],132:[2,85],133:[2,85],134:[2,85],135:[2,85],136:[2,85],137:[2,85]},{8:282,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,72:[2,120],75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{72:[2,121],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,37],6:[2,37],25:[2,37],26:[2,37],49:[2,37],54:[2,37],57:[2,37],72:[2,37],77:[2,37],85:[2,37],90:[2,37],92:[2,37],101:[2,37],102:87,103:[2,37],104:[2,37],105:[2,37],108:88,109:[2,37],110:69,117:[2,37],125:[2,37],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{26:[1,283],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,266],25:[1,267],85:[1,284]},{6:[2,67],25:[2,67],26:[2,67],54:[2,67],85:[2,67],90:[2,67]},{5:285,25:[1,5]},{6:[2,57],25:[2,57],26:[2,57],49:[2,57],54:[2,57]},{27:109,28:[1,73],44:110,55:286,56:108,58:111,59:112,75:[1,70],88:[1,113],89:[1,114]},{6:[2,55],25:[2,55],26:[2,55],27:109,28:[1,73],44:110,48:287,54:[2,55],55:107,56:108,58:111,59:112,75:[1,70],88:[1,113],89:[1,114]},{6:[2,62],25:[2,62],26:[2,62],49:[2,62],54:[2,62],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{26:[1,288],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{5:289,25:[1,5],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{5:290,25:[1,5]},{1:[2,134],6:[2,134],25:[2,134],26:[2,134],49:[2,134],54:[2,134],57:[2,134],72:[2,134],77:[2,134],85:[2,134],90:[2,134],92:[2,134],101:[2,134],103:[2,134],104:[2,134],105:[2,134],109:[2,134],117:[2,134],125:[2,134],127:[2,134],128:[2,134],131:[2,134],132:[2,134],133:[2,134],134:[2,134],135:[2,134],136:[2,134]},{5:291,25:[1,5]},{26:[1,292],120:[1,293],121:258,122:[1,217]},{1:[2,172],6:[2,172],25:[2,172],26:[2,172],49:[2,172],54:[2,172],57:[2,172],72:[2,172],77:[2,172],85:[2,172],90:[2,172],92:[2,172],101:[2,172],103:[2,172],104:[2,172],105:[2,172],109:[2,172],117:[2,172],125:[2,172],127:[2,172],128:[2,172],131:[2,172],132:[2,172],133:[2,172],134:[2,172],135:[2,172],136:[2,172]},{5:294,25:[1,5]},{26:[2,175],120:[2,175],122:[2,175]},{5:295,25:[1,5],54:[1,296]},{25:[2,130],54:[2,130],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,98],6:[2,98],25:[2,98],26:[2,98],49:[2,98],54:[2,98],57:[2,98],72:[2,98],77:[2,98],85:[2,98],90:[2,98],92:[2,98],101:[2,98],103:[2,98],104:[2,98],105:[2,98],109:[2,98],117:[2,98],125:[2,98],127:[2,98],128:[2,98],131:[2,98],132:[2,98],133:[2,98],134:[2,98],135:[2,98],136:[2,98]},{1:[2,101],5:297,6:[2,101],25:[1,5],26:[2,101],49:[2,101],54:[2,101],57:[2,101],72:[2,101],77:[2,101],85:[2,101],90:[2,101],92:[2,101],101:[2,101],102:87,103:[1,65],104:[2,101],105:[1,66],108:88,109:[1,68],110:69,117:[2,101],125:[2,101],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{101:[1,298]},{90:[1,299],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,115],6:[2,115],25:[2,115],26:[2,115],40:[2,115],49:[2,115],54:[2,115],57:[2,115],66:[2,115],67:[2,115],68:[2,115],70:[2,115],72:[2,115],73:[2,115],77:[2,115],83:[2,115],84:[2,115],85:[2,115],90:[2,115],92:[2,115],101:[2,115],103:[2,115],104:[2,115],105:[2,115],109:[2,115],115:[2,115],116:[2,115],117:[2,115],125:[2,115],127:[2,115],128:[2,115],131:[2,115],132:[2,115],133:[2,115],134:[2,115],135:[2,115],136:[2,115]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],93:300,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],86:301,87:[1,58],88:[1,59],89:[1,57],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,124],25:[2,124],26:[2,124],54:[2,124],85:[2,124],90:[2,124]},{6:[1,266],25:[1,267],26:[1,302]},{1:[2,141],6:[2,141],25:[2,141],26:[2,141],49:[2,141],54:[2,141],57:[2,141],72:[2,141],77:[2,141],85:[2,141],90:[2,141],92:[2,141],101:[2,141],102:87,103:[1,65],104:[2,141],105:[1,66],108:88,109:[1,68],110:69,117:[2,141],125:[2,141],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,143],6:[2,143],25:[2,143],26:[2,143],49:[2,143],54:[2,143],57:[2,143],72:[2,143],77:[2,143],85:[2,143],90:[2,143],92:[2,143],101:[2,143],102:87,103:[1,65],104:[2,143],105:[1,66],108:88,109:[1,68],110:69,117:[2,143],125:[2,143],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{115:[2,162],116:[2,162]},{8:303,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:304,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:305,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,89],6:[2,89],25:[2,89],26:[2,89],40:[2,89],49:[2,89],54:[2,89],57:[2,89],66:[2,89],67:[2,89],68:[2,89],70:[2,89],72:[2,89],73:[2,89],77:[2,89],83:[2,89],84:[2,89],85:[2,89],90:[2,89],92:[2,89],101:[2,89],103:[2,89],104:[2,89],105:[2,89],109:[2,89],115:[2,89],116:[2,89],117:[2,89],125:[2,89],127:[2,89],128:[2,89],131:[2,89],132:[2,89],133:[2,89],134:[2,89],135:[2,89],136:[2,89]},{11:168,27:169,28:[1,73],29:170,30:[1,71],31:[1,72],41:306,42:167,44:171,46:[1,46],88:[1,113]},{6:[2,90],11:168,25:[2,90],26:[2,90],27:169,28:[1,73],29:170,30:[1,71],31:[1,72],41:166,42:167,44:171,46:[1,46],54:[2,90],76:307,88:[1,113]},{6:[2,92],25:[2,92],26:[2,92],54:[2,92],77:[2,92]},{6:[2,40],25:[2,40],26:[2,40],54:[2,40],77:[2,40],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{8:308,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{72:[2,119],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,38],6:[2,38],25:[2,38],26:[2,38],49:[2,38],54:[2,38],57:[2,38],72:[2,38],77:[2,38],85:[2,38],90:[2,38],92:[2,38],101:[2,38],103:[2,38],104:[2,38],105:[2,38],109:[2,38],117:[2,38],125:[2,38],127:[2,38],128:[2,38],131:[2,38],132:[2,38],133:[2,38],134:[2,38],135:[2,38],136:[2,38]},{1:[2,110],6:[2,110],25:[2,110],26:[2,110],49:[2,110],54:[2,110],57:[2,110],66:[2,110],67:[2,110],68:[2,110],70:[2,110],72:[2,110],73:[2,110],77:[2,110],83:[2,110],84:[2,110],85:[2,110],90:[2,110],92:[2,110],101:[2,110],103:[2,110],104:[2,110],105:[2,110],109:[2,110],117:[2,110],125:[2,110],127:[2,110],128:[2,110],131:[2,110],132:[2,110],133:[2,110],134:[2,110],135:[2,110],136:[2,110]},{1:[2,49],6:[2,49],25:[2,49],26:[2,49],49:[2,49],54:[2,49],57:[2,49],72:[2,49],77:[2,49],85:[2,49],90:[2,49],92:[2,49],101:[2,49],103:[2,49],104:[2,49],105:[2,49],109:[2,49],117:[2,49],125:[2,49],127:[2,49],128:[2,49],131:[2,49],132:[2,49],133:[2,49],134:[2,49],135:[2,49],136:[2,49]},{6:[2,58],25:[2,58],26:[2,58],49:[2,58],54:[2,58]},{6:[2,53],25:[2,53],26:[2,53],53:309,54:[1,202]},{1:[2,200],6:[2,200],25:[2,200],26:[2,200],49:[2,200],54:[2,200],57:[2,200],72:[2,200],77:[2,200],85:[2,200],90:[2,200],92:[2,200],101:[2,200],103:[2,200],104:[2,200],105:[2,200],109:[2,200],117:[2,200],125:[2,200],127:[2,200],128:[2,200],131:[2,200],132:[2,200],133:[2,200],134:[2,200],135:[2,200],136:[2,200]},{1:[2,179],6:[2,179],25:[2,179],26:[2,179],49:[2,179],54:[2,179],57:[2,179],72:[2,179],77:[2,179],85:[2,179],90:[2,179],92:[2,179],101:[2,179],103:[2,179],104:[2,179],105:[2,179],109:[2,179],117:[2,179],120:[2,179],125:[2,179],127:[2,179],128:[2,179],131:[2,179],132:[2,179],133:[2,179],134:[2,179],135:[2,179],136:[2,179]},{1:[2,135],6:[2,135],25:[2,135],26:[2,135],49:[2,135],54:[2,135],57:[2,135],72:[2,135],77:[2,135],85:[2,135],90:[2,135],92:[2,135],101:[2,135],103:[2,135],104:[2,135],105:[2,135],109:[2,135],117:[2,135],125:[2,135],127:[2,135],128:[2,135],131:[2,135],132:[2,135],133:[2,135],134:[2,135],135:[2,135],136:[2,135]},{1:[2,136],6:[2,136],25:[2,136],26:[2,136],49:[2,136],54:[2,136],57:[2,136],72:[2,136],77:[2,136],85:[2,136],90:[2,136],92:[2,136],97:[2,136],101:[2,136],103:[2,136],104:[2,136],105:[2,136],109:[2,136],117:[2,136],125:[2,136],127:[2,136],128:[2,136],131:[2,136],132:[2,136],133:[2,136],134:[2,136],135:[2,136],136:[2,136]},{1:[2,170],6:[2,170],25:[2,170],26:[2,170],49:[2,170],54:[2,170],57:[2,170],72:[2,170],77:[2,170],85:[2,170],90:[2,170],92:[2,170],101:[2,170],103:[2,170],104:[2,170],105:[2,170],109:[2,170],117:[2,170],125:[2,170],127:[2,170],128:[2,170],131:[2,170],132:[2,170],133:[2,170],134:[2,170],135:[2,170],136:[2,170]},{5:310,25:[1,5]},{26:[1,311]},{6:[1,312],26:[2,176],120:[2,176],122:[2,176]},{8:313,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,102],6:[2,102],25:[2,102],26:[2,102],49:[2,102],54:[2,102],57:[2,102],72:[2,102],77:[2,102],85:[2,102],90:[2,102],92:[2,102],101:[2,102],103:[2,102],104:[2,102],105:[2,102],109:[2,102],117:[2,102],125:[2,102],127:[2,102],128:[2,102],131:[2,102],132:[2,102],133:[2,102],134:[2,102],135:[2,102],136:[2,102]},{1:[2,139],6:[2,139],25:[2,139],26:[2,139],49:[2,139],54:[2,139],57:[2,139],66:[2,139],67:[2,139],68:[2,139],70:[2,139],72:[2,139],73:[2,139],77:[2,139],83:[2,139],84:[2,139],85:[2,139],90:[2,139],92:[2,139],101:[2,139],103:[2,139],104:[2,139],105:[2,139],109:[2,139],117:[2,139],125:[2,139],127:[2,139],128:[2,139],131:[2,139],132:[2,139],133:[2,139],134:[2,139],135:[2,139],136:[2,139]},{1:[2,118],6:[2,118],25:[2,118],26:[2,118],49:[2,118],54:[2,118],57:[2,118],66:[2,118],67:[2,118],68:[2,118],70:[2,118],72:[2,118],73:[2,118],77:[2,118],83:[2,118],84:[2,118],85:[2,118],90:[2,118],92:[2,118],101:[2,118],103:[2,118],104:[2,118],105:[2,118],109:[2,118],117:[2,118],125:[2,118],127:[2,118],128:[2,118],131:[2,118],132:[2,118],133:[2,118],134:[2,118],135:[2,118],136:[2,118]},{6:[2,125],25:[2,125],26:[2,125],54:[2,125],85:[2,125],90:[2,125]},{6:[2,53],25:[2,53],26:[2,53],53:314,54:[1,226]},{6:[2,126],25:[2,126],26:[2,126],54:[2,126],85:[2,126],90:[2,126]},{1:[2,165],6:[2,165],25:[2,165],26:[2,165],49:[2,165],54:[2,165],57:[2,165],72:[2,165],77:[2,165],85:[2,165],90:[2,165],92:[2,165],101:[2,165],102:87,103:[2,165],104:[2,165],105:[2,165],108:88,109:[2,165],110:69,117:[1,315],125:[2,165],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,167],6:[2,167],25:[2,167],26:[2,167],49:[2,167],54:[2,167],57:[2,167],72:[2,167],77:[2,167],85:[2,167],90:[2,167],92:[2,167],101:[2,167],102:87,103:[2,167],104:[1,316],105:[2,167],108:88,109:[2,167],110:69,117:[2,167],125:[2,167],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,166],6:[2,166],25:[2,166],26:[2,166],49:[2,166],54:[2,166],57:[2,166],72:[2,166],77:[2,166],85:[2,166],90:[2,166],92:[2,166],101:[2,166],102:87,103:[2,166],104:[2,166],105:[2,166],108:88,109:[2,166],110:69,117:[2,166],125:[2,166],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[2,93],25:[2,93],26:[2,93],54:[2,93],77:[2,93]},{6:[2,53],25:[2,53],26:[2,53],53:317,54:[1,236]},{26:[1,318],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,247],25:[1,248],26:[1,319]},{26:[1,320]},{1:[2,173],6:[2,173],25:[2,173],26:[2,173],49:[2,173],54:[2,173],57:[2,173],72:[2,173],77:[2,173],85:[2,173],90:[2,173],92:[2,173],101:[2,173],103:[2,173],104:[2,173],105:[2,173],109:[2,173],117:[2,173],125:[2,173],127:[2,173],128:[2,173],131:[2,173],132:[2,173],133:[2,173],134:[2,173],135:[2,173],136:[2,173]},{26:[2,177],120:[2,177],122:[2,177]},{25:[2,131],54:[2,131],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,266],25:[1,267],26:[1,321]},{8:322,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:323,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[1,277],25:[1,278],26:[1,324]},{6:[2,41],25:[2,41],26:[2,41],54:[2,41],77:[2,41]},{6:[2,59],25:[2,59],26:[2,59],49:[2,59],54:[2,59]},{1:[2,171],6:[2,171],25:[2,171],26:[2,171],49:[2,171],54:[2,171],57:[2,171],72:[2,171],77:[2,171],85:[2,171],90:[2,171],92:[2,171],101:[2,171],103:[2,171],104:[2,171],105:[2,171],109:[2,171],117:[2,171],125:[2,171],127:[2,171],128:[2,171],131:[2,171],132:[2,171],133:[2,171],134:[2,171],135:[2,171],136:[2,171]},{6:[2,127],25:[2,127],26:[2,127],54:[2,127],85:[2,127],90:[2,127]},{1:[2,168],6:[2,168],25:[2,168],26:[2,168],49:[2,168],54:[2,168],57:[2,168],72:[2,168],77:[2,168],85:[2,168],90:[2,168],92:[2,168],101:[2,168],102:87,103:[2,168],104:[2,168],105:[2,168],108:88,109:[2,168],110:69,117:[2,168],125:[2,168],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,169],6:[2,169],25:[2,169],26:[2,169],49:[2,169],54:[2,169],57:[2,169],72:[2,169],77:[2,169],85:[2,169],90:[2,169],92:[2,169],101:[2,169],102:87,103:[2,169],104:[2,169],105:[2,169],108:88,109:[2,169],110:69,117:[2,169],125:[2,169],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[2,94],25:[2,94],26:[2,94],54:[2,94],77:[2,94]}], +defaultActions: {60:[2,51],61:[2,52],75:[2,3],94:[2,108],189:[2,88]}, +parseError: function parseError(str, hash) { + throw new Error(str); +}, +parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], // semantic value stack + lstack = [], // location stack + table = this.table, + yytext = '', + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + + //this.reductionCount = this.shiftCount = 0; + + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + if (typeof this.lexer.yylloc == 'undefined') + this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + + if (typeof this.yy.parseError === 'function') + this.parseError = this.yy.parseError; + + function popStack (n) { + stack.length = stack.length - 2*n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + + function lex() { + var token; + token = self.lexer.lex() || 1; // $end = 1 + // if token isn't its numeric value, convert + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + return token; + } + + var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; + while (true) { + // retreive state number from top of stack + state = stack[stack.length-1]; + + // use default actions if available + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol == null) + symbol = lex(); + // read action for current state and first input + action = table[state] && table[state][symbol]; + } + + // handle parse error + _handle_error: + if (typeof action === 'undefined' || !action.length || !action[0]) { + + if (!recovering) { + // Report error + expected = []; + for (p in table[state]) if (this.terminals_[p] && p > 2) { + expected.push("'"+this.terminals_[p]+"'"); + } + var errStr = ''; + if (this.lexer.showPosition) { + errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'"; + } else { + errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + + (symbol == 1 /*EOF*/ ? "end of input" : + ("'"+(this.terminals_[symbol] || symbol)+"'")); + } + this.parseError(errStr, + {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + + // just recovered from another error + if (recovering == 3) { + if (symbol == EOF) { + throw new Error(errStr || 'Parsing halted.'); + } + + // discard current lookahead and grab another + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + symbol = lex(); + } + + // try to recover from error + while (1) { + // check for error recovery rule in this state + if ((TERROR.toString()) in table[state]) { + break; + } + if (state == 0) { + throw new Error(errStr || 'Parsing halted.'); + } + popStack(1); + state = stack[stack.length-1]; + } + + preErrorSymbol = symbol; // save the lookahead token + symbol = TERROR; // insert generic error symbol as new lookahead + state = stack[stack.length-1]; + action = table[state] && table[state][TERROR]; + recovering = 3; // allow 3 real symbols to be shifted before reporting a new error + } + + // this shouldn't happen, unless resolve defaults are off + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); + } + + switch (action[0]) { + + case 1: // shift + //this.shiftCount++; + + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); // push state + symbol = null; + if (!preErrorSymbol) { // normal execution/no error + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { // error just occurred, resume old lookahead f/ before error + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + + case 2: // reduce + //this.reductionCount++; + + len = this.productions_[action[1]][1]; + + // perform semantic action + yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 + // default location, uses first token for firsts, last for lasts + yyval._$ = { + first_line: lstack[lstack.length-(len||1)].first_line, + last_line: lstack[lstack.length-1].last_line, + first_column: lstack[lstack.length-(len||1)].first_column, + last_column: lstack[lstack.length-1].last_column + }; + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + + if (typeof r !== 'undefined') { + return r; + } + + // pop off stack + if (len) { + stack = stack.slice(0,-1*len*2); + vstack = vstack.slice(0, -1*len); + lstack = lstack.slice(0, -1*len); + } + + stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) + vstack.push(yyval.$); + lstack.push(yyval._$); + // goto new state = table[STATE][NONTERMINAL] + newState = table[stack[stack.length-2]][stack[stack.length-1]]; + stack.push(newState); + break; + + case 3: // accept + return true; + } + + } + + return true; +}}; +undefined +return parser; +})(); +if (typeof require !== 'undefined' && typeof exports !== 'undefined') { +exports.parser = parser; +exports.parse = function () { return parser.parse.apply(parser, arguments); } +exports.main = function commonjsMain(args) { + if (!args[1]) + throw new Error('Usage: '+args[0]+' FILE'); + if (typeof process !== 'undefined') { + var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); + } else { + var cwd = require("file").path(require("file").cwd()); + var source = cwd.join(args[1]).read({charset: "utf-8"}); + } + return exports.parser.parse(source); +} +if (typeof module !== 'undefined' && require.main === module) { + exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); +} +} \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/repl.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/repl.js new file mode 100644 index 0000000..578dc59 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/repl.js @@ -0,0 +1,276 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var ACCESSOR, CoffeeScript, Module, REPL_PROMPT, REPL_PROMPT_CONTINUATION, REPL_PROMPT_MULTILINE, SIMPLEVAR, Script, autocomplete, backlog, completeAttribute, completeVariable, enableColours, error, getCompletions, inspect, multilineMode, pipedInput, readline, repl, run, stdin, stdout, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + stdin = process.openStdin(); + + stdout = process.stdout; + + CoffeeScript = require('./coffee-script'); + + readline = require('readline'); + + inspect = require('util').inspect; + + Script = require('vm').Script; + + Module = require('module'); + + REPL_PROMPT = 'coffee> '; + + REPL_PROMPT_MULTILINE = '------> '; + + REPL_PROMPT_CONTINUATION = '......> '; + + enableColours = false; + + if (process.platform !== 'win32') { + enableColours = !process.env.NODE_DISABLE_COLORS; + } + + error = function(err) { + return stdout.write((err.stack || err.toString()) + '\n'); + }; + + ACCESSOR = /\s*([\w\.]+)(?:\.(\w*))$/; + + SIMPLEVAR = /(\w+)$/i; + + autocomplete = function(text) { + return completeAttribute(text) || completeVariable(text) || [[], text]; + }; + + completeAttribute = function(text) { + var all, candidates, completions, key, match, obj, prefix, _i, _len, _ref; + if (match = text.match(ACCESSOR)) { + all = match[0], obj = match[1], prefix = match[2]; + try { + obj = Script.runInThisContext(obj); + } catch (e) { + return; + } + if (obj == null) { + return; + } + obj = Object(obj); + candidates = Object.getOwnPropertyNames(obj); + while (obj = Object.getPrototypeOf(obj)) { + _ref = Object.getOwnPropertyNames(obj); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + if (__indexOf.call(candidates, key) < 0) { + candidates.push(key); + } + } + } + completions = getCompletions(prefix, candidates); + return [completions, prefix]; + } + }; + + completeVariable = function(text) { + var candidates, completions, free, key, keywords, r, vars, _i, _len, _ref; + free = (_ref = text.match(SIMPLEVAR)) != null ? _ref[1] : void 0; + if (text === "") { + free = ""; + } + if (free != null) { + vars = Script.runInThisContext('Object.getOwnPropertyNames(Object(this))'); + keywords = (function() { + var _i, _len, _ref1, _results; + _ref1 = CoffeeScript.RESERVED; + _results = []; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + r = _ref1[_i]; + if (r.slice(0, 2) !== '__') { + _results.push(r); + } + } + return _results; + })(); + candidates = vars; + for (_i = 0, _len = keywords.length; _i < _len; _i++) { + key = keywords[_i]; + if (__indexOf.call(candidates, key) < 0) { + candidates.push(key); + } + } + completions = getCompletions(free, candidates); + return [completions, free]; + } + }; + + getCompletions = function(prefix, candidates) { + var el, _i, _len, _results; + _results = []; + for (_i = 0, _len = candidates.length; _i < _len; _i++) { + el = candidates[_i]; + if (0 === el.indexOf(prefix)) { + _results.push(el); + } + } + return _results; + }; + + process.on('uncaughtException', error); + + backlog = ''; + + run = function(buffer) { + var code, returnValue, _; + buffer = buffer.replace(/(^|[\r\n]+)(\s*)##?(?:[^#\r\n][^\r\n]*|)($|[\r\n])/, "$1$2$3"); + buffer = buffer.replace(/[\r\n]+$/, ""); + if (multilineMode) { + backlog += "" + buffer + "\n"; + repl.setPrompt(REPL_PROMPT_CONTINUATION); + repl.prompt(); + return; + } + if (!buffer.toString().trim() && !backlog) { + repl.prompt(); + return; + } + code = backlog += buffer; + if (code[code.length - 1] === '\\') { + backlog = "" + backlog.slice(0, -1) + "\n"; + repl.setPrompt(REPL_PROMPT_CONTINUATION); + repl.prompt(); + return; + } + repl.setPrompt(REPL_PROMPT); + backlog = ''; + try { + _ = global._; + returnValue = CoffeeScript["eval"]("_=(" + code + "\n)", { + filename: 'repl', + modulename: 'repl' + }); + if (returnValue === void 0) { + global._ = _; + } + repl.output.write("" + (inspect(returnValue, false, 2, enableColours)) + "\n"); + } catch (err) { + error(err); + } + return repl.prompt(); + }; + + if (stdin.readable && stdin.isRaw) { + pipedInput = ''; + repl = { + prompt: function() { + return stdout.write(this._prompt); + }, + setPrompt: function(p) { + return this._prompt = p; + }, + input: stdin, + output: stdout, + on: function() {} + }; + stdin.on('data', function(chunk) { + var line, lines, _i, _len, _ref; + pipedInput += chunk; + if (!/\n/.test(pipedInput)) { + return; + } + lines = pipedInput.split("\n"); + pipedInput = lines[lines.length - 1]; + _ref = lines.slice(0, -1); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + line = _ref[_i]; + if (!(line)) { + continue; + } + stdout.write("" + line + "\n"); + run(line); + } + }); + stdin.on('end', function() { + var line, _i, _len, _ref; + _ref = pipedInput.trim().split("\n"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + line = _ref[_i]; + if (!(line)) { + continue; + } + stdout.write("" + line + "\n"); + run(line); + } + stdout.write('\n'); + return process.exit(0); + }); + } else { + if (readline.createInterface.length < 3) { + repl = readline.createInterface(stdin, autocomplete); + stdin.on('data', function(buffer) { + return repl.write(buffer); + }); + } else { + repl = readline.createInterface(stdin, stdout, autocomplete); + } + } + + multilineMode = false; + + repl.input.on('keypress', function(char, key) { + var cursorPos, newPrompt; + if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'v')) { + return; + } + cursorPos = repl.cursor; + repl.output.cursorTo(0); + repl.output.clearLine(1); + multilineMode = !multilineMode; + if (!multilineMode && backlog) { + repl._line(); + } + backlog = ''; + repl.setPrompt((newPrompt = multilineMode ? REPL_PROMPT_MULTILINE : REPL_PROMPT)); + repl.prompt(); + return repl.output.cursorTo(newPrompt.length + (repl.cursor = cursorPos)); + }); + + repl.input.on('keypress', function(char, key) { + if (!(multilineMode && repl.line)) { + return; + } + if (!(key && key.ctrl && !key.meta && !key.shift && key.name === 'd')) { + return; + } + multilineMode = false; + return repl._line(); + }); + + repl.on('attemptClose', function() { + if (multilineMode) { + multilineMode = false; + repl.output.cursorTo(0); + repl.output.clearLine(1); + repl._onLine(repl.line); + return; + } + if (backlog || repl.line) { + backlog = ''; + repl.historyIndex = -1; + repl.setPrompt(REPL_PROMPT); + repl.output.write('\n(^C again to quit)'); + return repl._line((repl.line = '')); + } else { + return repl.close(); + } + }); + + repl.on('close', function() { + repl.output.write('\n'); + return repl.input.destroy(); + }); + + repl.on('line', run); + + repl.setPrompt(REPL_PROMPT); + + repl.prompt(); + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/rewriter.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/rewriter.js new file mode 100644 index 0000000..fdfeffc --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/rewriter.js @@ -0,0 +1,349 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_BLOCK, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, left, rite, _i, _len, _ref, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, + __slice = [].slice; + + exports.Rewriter = (function() { + + function Rewriter() {} + + Rewriter.prototype.rewrite = function(tokens) { + this.tokens = tokens; + this.removeLeadingNewlines(); + this.removeMidExpressionNewlines(); + this.closeOpenCalls(); + this.closeOpenIndexes(); + this.addImplicitIndentation(); + this.tagPostfixConditionals(); + this.addImplicitBraces(); + this.addImplicitParentheses(); + return this.tokens; + }; + + Rewriter.prototype.scanTokens = function(block) { + var i, token, tokens; + tokens = this.tokens; + i = 0; + while (token = tokens[i]) { + i += block.call(this, token, i, tokens); + } + return true; + }; + + Rewriter.prototype.detectEnd = function(i, condition, action) { + var levels, token, tokens, _ref, _ref1; + tokens = this.tokens; + levels = 0; + while (token = tokens[i]) { + if (levels === 0 && condition.call(this, token, i)) { + return action.call(this, token, i); + } + if (!token || levels < 0) { + return action.call(this, token, i - 1); + } + if (_ref = token[0], __indexOf.call(EXPRESSION_START, _ref) >= 0) { + levels += 1; + } else if (_ref1 = token[0], __indexOf.call(EXPRESSION_END, _ref1) >= 0) { + levels -= 1; + } + i += 1; + } + return i - 1; + }; + + Rewriter.prototype.removeLeadingNewlines = function() { + var i, tag, _i, _len, _ref; + _ref = this.tokens; + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + tag = _ref[i][0]; + if (tag !== 'TERMINATOR') { + break; + } + } + if (i) { + return this.tokens.splice(0, i); + } + }; + + Rewriter.prototype.removeMidExpressionNewlines = function() { + return this.scanTokens(function(token, i, tokens) { + var _ref; + if (!(token[0] === 'TERMINATOR' && (_ref = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref) >= 0))) { + return 1; + } + tokens.splice(i, 1); + return 0; + }); + }; + + Rewriter.prototype.closeOpenCalls = function() { + var action, condition; + condition = function(token, i) { + var _ref; + return ((_ref = token[0]) === ')' || _ref === 'CALL_END') || token[0] === 'OUTDENT' && this.tag(i - 1) === ')'; + }; + action = function(token, i) { + return this.tokens[token[0] === 'OUTDENT' ? i - 1 : i][0] = 'CALL_END'; + }; + return this.scanTokens(function(token, i) { + if (token[0] === 'CALL_START') { + this.detectEnd(i + 1, condition, action); + } + return 1; + }); + }; + + Rewriter.prototype.closeOpenIndexes = function() { + var action, condition; + condition = function(token, i) { + var _ref; + return (_ref = token[0]) === ']' || _ref === 'INDEX_END'; + }; + action = function(token, i) { + return token[0] = 'INDEX_END'; + }; + return this.scanTokens(function(token, i) { + if (token[0] === 'INDEX_START') { + this.detectEnd(i + 1, condition, action); + } + return 1; + }); + }; + + Rewriter.prototype.addImplicitBraces = function() { + var action, condition, sameLine, stack, start, startIndent, startIndex, startsLine; + stack = []; + start = null; + startsLine = null; + sameLine = true; + startIndent = 0; + startIndex = 0; + condition = function(token, i) { + var one, tag, three, two, _ref, _ref1; + _ref = this.tokens.slice(i + 1, +(i + 3) + 1 || 9e9), one = _ref[0], two = _ref[1], three = _ref[2]; + if ('HERECOMMENT' === (one != null ? one[0] : void 0)) { + return false; + } + tag = token[0]; + if (__indexOf.call(LINEBREAKS, tag) >= 0) { + sameLine = false; + } + return (((tag === 'TERMINATOR' || tag === 'OUTDENT') || (__indexOf.call(IMPLICIT_END, tag) >= 0 && sameLine && !(i - startIndex === 1))) && ((!startsLine && this.tag(i - 1) !== ',') || !((two != null ? two[0] : void 0) === ':' || (one != null ? one[0] : void 0) === '@' && (three != null ? three[0] : void 0) === ':'))) || (tag === ',' && one && ((_ref1 = one[0]) !== 'IDENTIFIER' && _ref1 !== 'NUMBER' && _ref1 !== 'STRING' && _ref1 !== '@' && _ref1 !== 'TERMINATOR' && _ref1 !== 'OUTDENT')); + }; + action = function(token, i) { + var tok; + tok = this.generate('}', '}', token[2]); + return this.tokens.splice(i, 0, tok); + }; + return this.scanTokens(function(token, i, tokens) { + var ago, idx, prevTag, tag, tok, value, _ref, _ref1; + if (_ref = (tag = token[0]), __indexOf.call(EXPRESSION_START, _ref) >= 0) { + stack.push([(tag === 'INDENT' && this.tag(i - 1) === '{' ? '{' : tag), i]); + return 1; + } + if (__indexOf.call(EXPRESSION_END, tag) >= 0) { + start = stack.pop(); + return 1; + } + if (!(tag === ':' && ((ago = this.tag(i - 2)) === ':' || ((_ref1 = stack[stack.length - 1]) != null ? _ref1[0] : void 0) !== '{'))) { + return 1; + } + sameLine = true; + startIndex = i + 1; + stack.push(['{']); + idx = ago === '@' ? i - 2 : i - 1; + while (this.tag(idx - 2) === 'HERECOMMENT') { + idx -= 2; + } + prevTag = this.tag(idx - 1); + startsLine = !prevTag || (__indexOf.call(LINEBREAKS, prevTag) >= 0); + value = new String('{'); + value.generated = true; + tok = this.generate('{', value, token[2]); + tokens.splice(idx, 0, tok); + this.detectEnd(i + 2, condition, action); + return 2; + }); + }; + + Rewriter.prototype.addImplicitParentheses = function() { + var action, condition, noCall, seenControl, seenSingle; + noCall = seenSingle = seenControl = false; + condition = function(token, i) { + var post, tag, _ref, _ref1; + tag = token[0]; + if (!seenSingle && token.fromThen) { + return true; + } + if (tag === 'IF' || tag === 'ELSE' || tag === 'CATCH' || tag === '->' || tag === '=>' || tag === 'CLASS') { + seenSingle = true; + } + if (tag === 'IF' || tag === 'ELSE' || tag === 'SWITCH' || tag === 'TRY' || tag === '=') { + seenControl = true; + } + if ((tag === '.' || tag === '?.' || tag === '::') && this.tag(i - 1) === 'OUTDENT') { + return true; + } + return !token.generated && this.tag(i - 1) !== ',' && (__indexOf.call(IMPLICIT_END, tag) >= 0 || (tag === 'INDENT' && !seenControl)) && (tag !== 'INDENT' || (((_ref = this.tag(i - 2)) !== 'CLASS' && _ref !== 'EXTENDS') && (_ref1 = this.tag(i - 1), __indexOf.call(IMPLICIT_BLOCK, _ref1) < 0) && !((post = this.tokens[i + 1]) && post.generated && post[0] === '{'))); + }; + action = function(token, i) { + return this.tokens.splice(i, 0, this.generate('CALL_END', ')', token[2])); + }; + return this.scanTokens(function(token, i, tokens) { + var callObject, current, next, prev, tag, _ref, _ref1, _ref2; + tag = token[0]; + if (tag === 'CLASS' || tag === 'IF' || tag === 'FOR' || tag === 'WHILE') { + noCall = true; + } + _ref = tokens.slice(i - 1, +(i + 1) + 1 || 9e9), prev = _ref[0], current = _ref[1], next = _ref[2]; + callObject = !noCall && tag === 'INDENT' && next && next.generated && next[0] === '{' && prev && (_ref1 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref1) >= 0); + seenSingle = false; + seenControl = false; + if (__indexOf.call(LINEBREAKS, tag) >= 0) { + noCall = false; + } + if (prev && !prev.spaced && tag === '?') { + token.call = true; + } + if (token.fromThen) { + return 1; + } + if (!(callObject || (prev != null ? prev.spaced : void 0) && (prev.call || (_ref2 = prev[0], __indexOf.call(IMPLICIT_FUNC, _ref2) >= 0)) && (__indexOf.call(IMPLICIT_CALL, tag) >= 0 || !(token.spaced || token.newLine) && __indexOf.call(IMPLICIT_UNSPACED_CALL, tag) >= 0))) { + return 1; + } + tokens.splice(i, 0, this.generate('CALL_START', '(', token[2])); + this.detectEnd(i + 1, condition, action); + if (prev[0] === '?') { + prev[0] = 'FUNC_EXIST'; + } + return 2; + }); + }; + + Rewriter.prototype.addImplicitIndentation = function() { + var action, condition, indent, outdent, starter; + starter = indent = outdent = null; + condition = function(token, i) { + var _ref; + return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'ELSE' && (starter !== 'IF' && starter !== 'THEN')); + }; + action = function(token, i) { + return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent); + }; + return this.scanTokens(function(token, i, tokens) { + var tag, _ref, _ref1; + tag = token[0]; + if (tag === 'TERMINATOR' && this.tag(i + 1) === 'THEN') { + tokens.splice(i, 1); + return 0; + } + if (tag === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') { + tokens.splice.apply(tokens, [i, 0].concat(__slice.call(this.indentation(token)))); + return 2; + } + if (tag === 'CATCH' && ((_ref = this.tag(i + 2)) === 'OUTDENT' || _ref === 'TERMINATOR' || _ref === 'FINALLY')) { + tokens.splice.apply(tokens, [i + 2, 0].concat(__slice.call(this.indentation(token)))); + return 4; + } + if (__indexOf.call(SINGLE_LINERS, tag) >= 0 && this.tag(i + 1) !== 'INDENT' && !(tag === 'ELSE' && this.tag(i + 1) === 'IF')) { + starter = tag; + _ref1 = this.indentation(token, true), indent = _ref1[0], outdent = _ref1[1]; + if (starter === 'THEN') { + indent.fromThen = true; + } + tokens.splice(i + 1, 0, indent); + this.detectEnd(i + 2, condition, action); + if (tag === 'THEN') { + tokens.splice(i, 1); + } + return 1; + } + return 1; + }); + }; + + Rewriter.prototype.tagPostfixConditionals = function() { + var action, condition, original; + original = null; + condition = function(token, i) { + var _ref; + return (_ref = token[0]) === 'TERMINATOR' || _ref === 'INDENT'; + }; + action = function(token, i) { + if (token[0] !== 'INDENT' || (token.generated && !token.fromThen)) { + return original[0] = 'POST_' + original[0]; + } + }; + return this.scanTokens(function(token, i) { + if (token[0] !== 'IF') { + return 1; + } + original = token; + this.detectEnd(i + 1, condition, action); + return 1; + }); + }; + + Rewriter.prototype.indentation = function(token, implicit) { + var indent, outdent; + if (implicit == null) { + implicit = false; + } + indent = ['INDENT', 2, token[2]]; + outdent = ['OUTDENT', 2, token[2]]; + if (implicit) { + indent.generated = outdent.generated = true; + } + return [indent, outdent]; + }; + + Rewriter.prototype.generate = function(tag, value, line) { + var tok; + tok = [tag, value, line]; + tok.generated = true; + return tok; + }; + + Rewriter.prototype.tag = function(i) { + var _ref; + return (_ref = this.tokens[i]) != null ? _ref[0] : void 0; + }; + + return Rewriter; + + })(); + + BALANCED_PAIRS = [['(', ')'], ['[', ']'], ['{', '}'], ['INDENT', 'OUTDENT'], ['CALL_START', 'CALL_END'], ['PARAM_START', 'PARAM_END'], ['INDEX_START', 'INDEX_END']]; + + exports.INVERSES = INVERSES = {}; + + EXPRESSION_START = []; + + EXPRESSION_END = []; + + for (_i = 0, _len = BALANCED_PAIRS.length; _i < _len; _i++) { + _ref = BALANCED_PAIRS[_i], left = _ref[0], rite = _ref[1]; + EXPRESSION_START.push(INVERSES[rite] = left); + EXPRESSION_END.push(INVERSES[left] = rite); + } + + EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END); + + IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS']; + + IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'NULL', 'UNDEFINED', 'UNARY', 'SUPER', '@', '->', '=>', '[', '(', '{', '--', '++']; + + IMPLICIT_UNSPACED_CALL = ['+', '-']; + + IMPLICIT_BLOCK = ['->', '=>', '{', '[', ',']; + + IMPLICIT_END = ['POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR']; + + SINGLE_LINERS = ['ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN']; + + SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN']; + + LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT']; + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/scope.js b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/scope.js new file mode 100644 index 0000000..3f35dbf --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/lib/coffee-script/scope.js @@ -0,0 +1,146 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Scope, extend, last, _ref; + + _ref = require('./helpers'), extend = _ref.extend, last = _ref.last; + + exports.Scope = Scope = (function() { + + Scope.root = null; + + function Scope(parent, expressions, method) { + this.parent = parent; + this.expressions = expressions; + this.method = method; + this.variables = [ + { + name: 'arguments', + type: 'arguments' + } + ]; + this.positions = {}; + if (!this.parent) { + Scope.root = this; + } + } + + Scope.prototype.add = function(name, type, immediate) { + if (this.shared && !immediate) { + return this.parent.add(name, type, immediate); + } + if (Object.prototype.hasOwnProperty.call(this.positions, name)) { + return this.variables[this.positions[name]].type = type; + } else { + return this.positions[name] = this.variables.push({ + name: name, + type: type + }) - 1; + } + }; + + Scope.prototype.namedMethod = function() { + if (this.method.name || !this.parent) { + return this.method; + } + return this.parent.namedMethod(); + }; + + Scope.prototype.find = function(name) { + if (this.check(name)) { + return true; + } + this.add(name, 'var'); + return false; + }; + + Scope.prototype.parameter = function(name) { + if (this.shared && this.parent.check(name, true)) { + return; + } + return this.add(name, 'param'); + }; + + Scope.prototype.check = function(name) { + var _ref1; + return !!(this.type(name) || ((_ref1 = this.parent) != null ? _ref1.check(name) : void 0)); + }; + + Scope.prototype.temporary = function(name, index) { + if (name.length > 1) { + return '_' + name + (index > 1 ? index - 1 : ''); + } else { + return '_' + (index + parseInt(name, 36)).toString(36).replace(/\d/g, 'a'); + } + }; + + Scope.prototype.type = function(name) { + var v, _i, _len, _ref1; + _ref1 = this.variables; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + v = _ref1[_i]; + if (v.name === name) { + return v.type; + } + } + return null; + }; + + Scope.prototype.freeVariable = function(name, reserve) { + var index, temp; + if (reserve == null) { + reserve = true; + } + index = 0; + while (this.check((temp = this.temporary(name, index)))) { + index++; + } + if (reserve) { + this.add(temp, 'var', true); + } + return temp; + }; + + Scope.prototype.assign = function(name, value) { + this.add(name, { + value: value, + assigned: true + }, true); + return this.hasAssignments = true; + }; + + Scope.prototype.hasDeclarations = function() { + return !!this.declaredVariables().length; + }; + + Scope.prototype.declaredVariables = function() { + var realVars, tempVars, v, _i, _len, _ref1; + realVars = []; + tempVars = []; + _ref1 = this.variables; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + v = _ref1[_i]; + if (v.type === 'var') { + (v.name.charAt(0) === '_' ? tempVars : realVars).push(v.name); + } + } + return realVars.sort().concat(tempVars.sort()); + }; + + Scope.prototype.assignedVariables = function() { + var v, _i, _len, _ref1, _results; + _ref1 = this.variables; + _results = []; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + v = _ref1[_i]; + if (v.type.assigned) { + _results.push("" + v.name + " = " + v.type.value); + } + } + return _results; + }; + + return Scope; + + })(); + +}).call(this); diff --git a/test/node_modules/jade/node_modules/coffee-script/package.json b/test/node_modules/jade/node_modules/coffee-script/package.json new file mode 100644 index 0000000..cc8a004 --- /dev/null +++ b/test/node_modules/jade/node_modules/coffee-script/package.json @@ -0,0 +1,48 @@ +{ + "name": "coffee-script", + "description": "Unfancy JavaScript", + "keywords": [ + "javascript", + "language", + "coffeescript", + "compiler" + ], + "author": { + "name": "Jeremy Ashkenas" + }, + "version": "1.4.0", + "licenses": [ + { + "type": "MIT", + "url": "https://raw.github.com/jashkenas/coffee-script/master/LICENSE" + } + ], + "engines": { + "node": ">=0.4.0" + }, + "directories": { + "lib": "./lib/coffee-script" + }, + "main": "./lib/coffee-script/coffee-script", + "bin": { + "coffee": "./bin/coffee", + "cake": "./bin/cake" + }, + "scripts": { + "test": "node ./bin/cake test" + }, + "homepage": "http://coffeescript.org", + "bugs": "https://github.com/jashkenas/coffee-script/issues", + "repository": { + "type": "git", + "url": "git://github.com/jashkenas/coffee-script.git" + }, + "devDependencies": { + "uglify-js": ">=1.0.0", + "jison": ">=0.2.0" + }, + "readme": "\n {\n } } {\n { { } }\n } }{ {\n { }{ } } _____ __ __\n ( }{ }{ { ) / ____| / _|/ _|\n .- { { } { }} -. | | ___ | |_| |_ ___ ___\n ( ( } { } { } } ) | | / _ \\| _| _/ _ \\/ _ \\\n |`-..________ ..-'| | |___| (_) | | | || __/ __/\n | | \\_____\\___/|_| |_| \\___|\\___|\n | ;--.\n | (__ \\ _____ _ _\n | | ) ) / ____| (_) | |\n | |/ / | (___ ___ _ __ _ _ __ | |_\n | ( / \\___ \\ / __| '__| | '_ \\| __|\n | |/ ____) | (__| | | | |_) | |_\n | | |_____/ \\___|_| |_| .__/ \\__|\n `-.._________..-' | |\n |_|\n\n\n CoffeeScript is a little language that compiles into JavaScript.\n\n Install Node.js, and then the CoffeeScript compiler:\n sudo bin/cake install\n\n Or, if you have the Node Package Manager installed:\n npm install -g coffee-script\n (Leave off the -g if you don't wish to install globally.)\n\n Execute a script:\n coffee /path/to/script.coffee\n\n Compile a script:\n coffee -c /path/to/script.coffee\n\n For documentation, usage, and examples, see:\n http://coffeescript.org/\n\n To suggest a feature, report a bug, or general discussion:\n http://github.com/jashkenas/coffee-script/issues/\n\n If you'd like to chat, drop by #coffeescript on Freenode IRC,\n or on webchat.freenode.net.\n\n The source repository:\n git://github.com/jashkenas/coffee-script.git\n\n All contributors are listed here:\n http://github.com/jashkenas/coffee-script/contributors\n", + "readmeFilename": "README", + "_id": "coffee-script@1.4.0", + "_from": "coffee-script@~1.4.0" +} diff --git a/test/node_modules/jade/node_modules/commander/.npmignore b/test/node_modules/jade/node_modules/commander/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/test/node_modules/jade/node_modules/commander/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/test/node_modules/jade/node_modules/commander/.travis.yml b/test/node_modules/jade/node_modules/commander/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/test/node_modules/jade/node_modules/commander/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/test/node_modules/jade/node_modules/commander/History.md b/test/node_modules/jade/node_modules/commander/History.md new file mode 100644 index 0000000..4961d2e --- /dev/null +++ b/test/node_modules/jade/node_modules/commander/History.md @@ -0,0 +1,107 @@ + +0.6.1 / 2012-06-01 +================== + + * Added: append (yes or no) on confirmation + * Added: allow node.js v0.7.x + +0.6.0 / 2012-04-10 +================== + + * Added `.prompt(obj, callback)` support. Closes #49 + * Added default support to .choose(). Closes #41 + * Fixed the choice example + +0.5.1 / 2011-12-20 +================== + + * Fixed `password()` for recent nodes. Closes #36 + +0.5.0 / 2011-12-04 +================== + + * Added sub-command option support [itay] + +0.4.3 / 2011-12-04 +================== + + * Fixed custom help ordering. Closes #32 + +0.4.2 / 2011-11-24 +================== + + * Added travis support + * Fixed: line-buffered input automatically trimmed. Closes #31 + +0.4.1 / 2011-11-18 +================== + + * Removed listening for "close" on --help + +0.4.0 / 2011-11-15 +================== + + * Added support for `--`. Closes #24 + +0.3.3 / 2011-11-14 +================== + + * Fixed: wait for close event when writing help info [Jerry Hamlet] + +0.3.2 / 2011-11-01 +================== + + * Fixed long flag definitions with values [felixge] + +0.3.1 / 2011-10-31 +================== + + * Changed `--version` short flag to `-V` from `-v` + * Changed `.version()` so it's configurable [felixge] + +0.3.0 / 2011-10-31 +================== + + * Added support for long flags only. Closes #18 + +0.2.1 / 2011-10-24 +================== + + * "node": ">= 0.4.x < 0.7.0". Closes #20 + +0.2.0 / 2011-09-26 +================== + + * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs] + +0.1.0 / 2011-08-24 +================== + + * Added support for custom `--help` output + +0.0.5 / 2011-08-18 +================== + + * Changed: when the user enters nothing prompt for password again + * Fixed issue with passwords beginning with numbers [NuckChorris] + +0.0.4 / 2011-08-15 +================== + + * Fixed `Commander#args` + +0.0.3 / 2011-08-15 +================== + + * Added default option value support + +0.0.2 / 2011-08-15 +================== + + * Added mask support to `Command#password(str[, mask], fn)` + * Added `Command#password(str, fn)` + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/test/node_modules/jade/node_modules/commander/Makefile b/test/node_modules/jade/node_modules/commander/Makefile new file mode 100644 index 0000000..0074625 --- /dev/null +++ b/test/node_modules/jade/node_modules/commander/Makefile @@ -0,0 +1,7 @@ + +TESTS = $(shell find test/test.*.js) + +test: + @./test/run $(TESTS) + +.PHONY: test \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/commander/Readme.md b/test/node_modules/jade/node_modules/commander/Readme.md new file mode 100644 index 0000000..b8328c3 --- /dev/null +++ b/test/node_modules/jade/node_modules/commander/Readme.md @@ -0,0 +1,262 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineappe'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineappe + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .prompt(msg, fn) + + Single-line prompt: + +```js +program.prompt('name: ', function(name){ + console.log('hi %s', name); +}); +``` + + Multi-line prompt: + +```js +program.prompt('description:', function(name){ + console.log('hi %s', name); +}); +``` + + Coercion: + +```js +program.prompt('Age: ', Number, function(age){ + console.log('age: %j', age); +}); +``` + +```js +program.prompt('Birthdate: ', Date, function(date){ + console.log('date: %s', date); +}); +``` + +## .password(msg[, mask], fn) + +Prompt for password without echoing: + +```js +program.password('Password: ', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +Prompt for password with mask char "*": + +```js +program.password('Password: ', '*', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +## .confirm(msg, fn) + + Confirm with the given `msg`: + +```js +program.confirm('continue? ', function(ok){ + console.log(' got %j', ok); +}); +``` + +## .choose(list, fn) + + Let the user choose from a `list`: + +```js +var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + +console.log('Choose the coolest pet:'); +program.choose(list, function(i){ + console.log('you chose %d "%s"', i, list[i]); +}); +``` + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +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. \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/commander/index.js b/test/node_modules/jade/node_modules/commander/index.js new file mode 100644 index 0000000..06ec1e4 --- /dev/null +++ b/test/node_modules/jade/node_modules/commander/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/commander'); \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/commander/lib/commander.js b/test/node_modules/jade/node_modules/commander/lib/commander.js new file mode 100644 index 0000000..5ba87eb --- /dev/null +++ b/test/node_modules/jade/node_modules/commander/lib/commander.js @@ -0,0 +1,1026 @@ + +/*! + * commander + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , path = require('path') + , tty = require('tty') + , basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this.args = []; + this.name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name){ + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + return cmd; +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self.args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self.args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + this.parent.on(this.name, function(args, unknown){ + // Parse any so-far unknown options + unknown = unknown || []; + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + self.args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self.args.length) { + args[self.args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to false + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => true + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null != val && fn) val = fn(val); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // store raw args + this.rawArgs = argv; + + // guess name + if (!this.name) this.name = basename(argv[1]); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + this.args = parsed.args; + return this.parseArgs(this.args, parsed.unknown); +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + if ('-' == arg[0]) return this.optionMissingArgument(option, arg); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || '-' == arg[0]) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this.args.length ? ' ' + args : ''); + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return cmd.name + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args + + (cmd.description() + ? '\n' + cmd.description() + : ''); + }).join('\n\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this.name + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Prompt for a `Number`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForNumber = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseNumber(val){ + val = Number(val); + if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber); + fn(val); + }); +}; + +/** + * Prompt for a `Date`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForDate = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseDate(val){ + val = new Date(val); + if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate); + fn(val); + }); +}; + +/** + * Single-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptSingleLine = function(str, fn){ + if ('function' == typeof arguments[2]) { + return this['promptFor' + (fn.name || fn)](str, arguments[2]); + } + + process.stdout.write(str); + process.stdin.setEncoding('utf8'); + process.stdin.once('data', function(val){ + fn(val.trim()); + }).resume(); +}; + +/** + * Multi-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptMultiLine = function(str, fn){ + var buf = []; + console.log(str); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(val){ + if ('\n' == val || '\r\n' == val) { + process.stdin.removeAllListeners('data'); + fn(buf.join('\n')); + } else { + buf.push(val.trimRight()); + } + }).resume(); +}; + +/** + * Prompt `str` and callback `fn(val)` + * + * Commander supports single-line and multi-line prompts. + * To issue a single-line prompt simply add white-space + * to the end of `str`, something like "name: ", whereas + * for a multi-line prompt omit this "description:". + * + * + * Examples: + * + * program.prompt('Username: ', function(name){ + * console.log('hi %s', name); + * }); + * + * program.prompt('Description:', function(desc){ + * console.log('description was "%s"', desc.trim()); + * }); + * + * @param {String|Object} str + * @param {Function} fn + * @api public + */ + +Command.prototype.prompt = function(str, fn){ + var self = this; + + if ('string' == typeof str) { + if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments); + this.promptMultiLine(str, fn); + } else { + var keys = Object.keys(str) + , obj = {}; + + function next() { + var key = keys.shift() + , label = str[key]; + + if (!key) return fn(obj); + self.prompt(label, function(val){ + obj[key] = val; + next(); + }); + } + + next(); + } +}; + +/** + * Prompt for password with `str`, `mask` char and callback `fn(val)`. + * + * The mask string defaults to '', aka no output is + * written while typing, you may want to use "*" etc. + * + * Examples: + * + * program.password('Password: ', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * program.password('Password: ', '*', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {String} mask + * @param {Function} fn + * @api public + */ + +Command.prototype.password = function(str, mask, fn){ + var self = this + , buf = ''; + + // default mask + if ('function' == typeof mask) { + fn = mask; + mask = ''; + } + + process.stdin.resume(); + tty.setRawMode(true); + process.stdout.write(str); + + // keypress + process.stdin.on('keypress', function(c, key){ + if (key && 'enter' == key.name) { + console.log(); + process.stdin.removeAllListeners('keypress'); + tty.setRawMode(false); + if (!buf.trim().length) return self.password(str, mask, fn); + fn(buf); + return; + } + + if (key && key.ctrl && 'c' == key.name) { + console.log('%s', buf); + process.exit(); + } + + process.stdout.write(mask); + buf += c; + }).resume(); +}; + +/** + * Confirmation prompt with `str` and callback `fn(bool)` + * + * Examples: + * + * program.confirm('continue? ', function(ok){ + * console.log(' got %j', ok); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {Function} fn + * @api public + */ + + +Command.prototype.confirm = function(str, fn, verbose){ + var self = this; + this.prompt(str, function(ok){ + if (!ok.trim()) { + if (!verbose) str += '(yes or no) '; + return self.confirm(str, fn, true); + } + fn(parseBool(ok)); + }); +}; + +/** + * Choice prompt with `list` of items and callback `fn(index, item)` + * + * Examples: + * + * var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + * + * console.log('Choose the coolest pet:'); + * program.choose(list, function(i){ + * console.log('you chose %d "%s"', i, list[i]); + * process.stdin.destroy(); + * }); + * + * @param {Array} list + * @param {Number|Function} index or fn + * @param {Function} fn + * @api public + */ + +Command.prototype.choose = function(list, index, fn){ + var self = this + , hasDefault = 'number' == typeof index; + + if (!hasDefault) { + fn = index; + index = null; + } + + list.forEach(function(item, i){ + if (hasDefault && i == index) { + console.log('* %d) %s', i + 1, item); + } else { + console.log(' %d) %s', i + 1, item); + } + }); + + function again() { + self.prompt(' : ', function(val){ + val = parseInt(val, 10) - 1; + if (hasDefault && isNaN(val)) val = index; + + if (null == list[val]) { + again(); + } else { + fn(val, list[val]); + } + }); + } + + again(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Parse a boolean `str`. + * + * @param {String} str + * @return {Boolean} + * @api private + */ + +function parseBool(str) { + return /^y|yes|ok|true$/i.test(str); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + process.stdout.write(cmd.helpInformation()); + cmd.emit('--help'); + process.exit(0); + } + } +} diff --git a/test/node_modules/jade/node_modules/commander/package.json b/test/node_modules/jade/node_modules/commander/package.json new file mode 100644 index 0000000..942b794 --- /dev/null +++ b/test/node_modules/jade/node_modules/commander/package.json @@ -0,0 +1,35 @@ +{ + "name": "commander", + "version": "0.6.1", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt", + "stdin" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "https://github.com/visionmedia/commander.js.git" + }, + "dependencies": {}, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.4.x" + }, + "readme": "# Commander.js\n\n The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).\n\n [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js)\n\n## Installation\n\n $ npm install commander\n\n## Option parsing\n\n Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('commander');\n\nprogram\n .version('0.0.1')\n .option('-p, --peppers', 'Add peppers')\n .option('-P, --pineapple', 'Add pineapple')\n .option('-b, --bbq', 'Add bbq sauce')\n .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')\n .parse(process.argv);\n\nconsole.log('you ordered a pizza with:');\nif (program.peppers) console.log(' - peppers');\nif (program.pineapple) console.log(' - pineappe');\nif (program.bbq) console.log(' - bbq');\nconsole.log(' - %s cheese', program.cheese);\n```\n\n Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as \"--template-engine\" are camel-cased, becoming `program.templateEngine` etc.\n\n## Automated --help\n\n The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:\n\n``` \n $ ./examples/pizza --help\n\n Usage: pizza [options]\n\n Options:\n\n -V, --version output the version number\n -p, --peppers Add peppers\n -P, --pineapple Add pineappe\n -b, --bbq Add bbq sauce\n -c, --cheese Add the specified type of cheese [marble]\n -h, --help output usage information\n\n```\n\n## Coercion\n\n```js\nfunction range(val) {\n return val.split('..').map(Number);\n}\n\nfunction list(val) {\n return val.split(',');\n}\n\nprogram\n .version('0.0.1')\n .usage('[options] ')\n .option('-i, --integer ', 'An integer argument', parseInt)\n .option('-f, --float ', 'A float argument', parseFloat)\n .option('-r, --range ..', 'A range', range)\n .option('-l, --list ', 'A list', list)\n .option('-o, --optional [value]', 'An optional value')\n .parse(process.argv);\n\nconsole.log(' int: %j', program.integer);\nconsole.log(' float: %j', program.float);\nconsole.log(' optional: %j', program.optional);\nprogram.range = program.range || [];\nconsole.log(' range: %j..%j', program.range[0], program.range[1]);\nconsole.log(' list: %j', program.list);\nconsole.log(' args: %j', program.args);\n```\n\n## Custom help\n\n You can display arbitrary `-h, --help` information\n by listening for \"--help\". Commander will automatically\n exit once you are done so that the remainder of your program\n does not execute causing undesired behaviours, for example\n in the following executable \"stuff\" will not output when\n `--help` is used.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('../');\n\nfunction list(val) {\n return val.split(',').map(Number);\n}\n\nprogram\n .version('0.0.1')\n .option('-f, --foo', 'enable some foo')\n .option('-b, --bar', 'enable some bar')\n .option('-B, --baz', 'enable some baz');\n\n// must be before .parse() since\n// node's emit() is immediate\n\nprogram.on('--help', function(){\n console.log(' Examples:');\n console.log('');\n console.log(' $ custom-help --help');\n console.log(' $ custom-help -h');\n console.log('');\n});\n\nprogram.parse(process.argv);\n\nconsole.log('stuff');\n```\n\nyielding the following help output:\n\n```\n\nUsage: custom-help [options]\n\nOptions:\n\n -h, --help output usage information\n -V, --version output the version number\n -f, --foo enable some foo\n -b, --bar enable some bar\n -B, --baz enable some baz\n\nExamples:\n\n $ custom-help --help\n $ custom-help -h\n\n```\n\n## .prompt(msg, fn)\n\n Single-line prompt:\n\n```js\nprogram.prompt('name: ', function(name){\n console.log('hi %s', name);\n});\n```\n\n Multi-line prompt:\n\n```js\nprogram.prompt('description:', function(name){\n console.log('hi %s', name);\n});\n```\n\n Coercion:\n\n```js\nprogram.prompt('Age: ', Number, function(age){\n console.log('age: %j', age);\n});\n```\n\n```js\nprogram.prompt('Birthdate: ', Date, function(date){\n console.log('date: %s', date);\n});\n```\n\n## .password(msg[, mask], fn)\n\nPrompt for password without echoing:\n\n```js\nprogram.password('Password: ', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\nPrompt for password with mask char \"*\":\n\n```js\nprogram.password('Password: ', '*', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\n## .confirm(msg, fn)\n\n Confirm with the given `msg`:\n\n```js\nprogram.confirm('continue? ', function(ok){\n console.log(' got %j', ok);\n});\n```\n\n## .choose(list, fn)\n\n Let the user choose from a `list`:\n\n```js\nvar list = ['tobi', 'loki', 'jane', 'manny', 'luna'];\n\nconsole.log('Choose the coolest pet:');\nprogram.choose(list, function(i){\n console.log('you chose %d \"%s\"', i, list[i]);\n});\n```\n\n## Links\n\n - [API documentation](http://visionmedia.github.com/commander.js/)\n - [ascii tables](https://github.com/LearnBoost/cli-table)\n - [progress bars](https://github.com/visionmedia/node-progress)\n - [more progress bars](https://github.com/substack/node-multimeter)\n - [examples](https://github.com/visionmedia/commander.js/tree/master/examples)\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "readmeFilename": "Readme.md", + "_id": "commander@0.6.1", + "_from": "commander@0.6.1" +} diff --git a/test/node_modules/jade/node_modules/mkdirp/.npmignore b/test/node_modules/jade/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/test/node_modules/jade/node_modules/mkdirp/.travis.yml b/test/node_modules/jade/node_modules/mkdirp/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/test/node_modules/jade/node_modules/mkdirp/LICENSE b/test/node_modules/jade/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +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/test/node_modules/jade/node_modules/mkdirp/README.markdown b/test/node_modules/jade/node_modules/mkdirp/README.markdown new file mode 100644 index 0000000..40de04f --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/README.markdown @@ -0,0 +1,61 @@ +mkdirp +====== + +Like `mkdir -p`, but in node.js! + +[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp) + +example +======= + +pow.js +------ + var mkdirp = require('mkdirp'); + + mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') + }); + +Output + pow! + +And now /tmp/foo/bar/baz exists, huzzah! + +methods +======= + +var mkdirp = require('mkdirp'); + +mkdirp(dir, mode, cb) +--------------------- + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +`cb(err, made)` fires with the error or the first directory `made` +that had to be created, if any. + +mkdirp.sync(dir, mode) +---------------------- + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +Returns the first directory that had to be created, if any. + +install +======= + +With [npm](http://npmjs.org) do: + + npm install mkdirp + +license +======= + +MIT/X11 diff --git a/test/node_modules/jade/node_modules/mkdirp/examples/pow.js b/test/node_modules/jade/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/index.js b/test/node_modules/jade/node_modules/mkdirp/index.js new file mode 100644 index 0000000..fda6de8 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/index.js @@ -0,0 +1,82 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, mode, f, made) { + if (typeof mode === 'function' || mode === undefined) { + f = mode; + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + fs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), mode, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, mode, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + fs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, mode, made) { + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + try { + fs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), mode, made); + sync(p, mode, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = fs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; diff --git a/test/node_modules/jade/node_modules/mkdirp/package.json b/test/node_modules/jade/node_modules/mkdirp/package.json new file mode 100644 index 0000000..fe04106 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/package.json @@ -0,0 +1,33 @@ +{ + "name": "mkdirp", + "description": "Recursively mkdir, like `mkdir -p`", + "version": "0.3.4", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "main": "./index", + "keywords": [ + "mkdir", + "directory" + ], + "repository": { + "type": "git", + "url": "http://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "devDependencies": { + "tap": "~0.2.4" + }, + "license": "MIT/X11", + "engines": { + "node": "*" + }, + "readme": "mkdirp\n======\n\nLike `mkdir -p`, but in node.js!\n\n[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp)\n\nexample\n=======\n\npow.js\n------\n var mkdirp = require('mkdirp');\n \n mkdirp('/tmp/foo/bar/baz', function (err) {\n if (err) console.error(err)\n else console.log('pow!')\n });\n\nOutput\n pow!\n\nAnd now /tmp/foo/bar/baz exists, huzzah!\n\nmethods\n=======\n\nvar mkdirp = require('mkdirp');\n\nmkdirp(dir, mode, cb)\n---------------------\n\nCreate a new directory and any necessary subdirectories at `dir` with octal\npermission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\n`cb(err, made)` fires with the error or the first directory `made`\nthat had to be created, if any.\n\nmkdirp.sync(dir, mode)\n----------------------\n\nSynchronously create a new directory and any necessary subdirectories at `dir`\nwith octal permission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\nReturns the first directory that had to be created, if any.\n\ninstall\n=======\n\nWith [npm](http://npmjs.org) do:\n\n npm install mkdirp\n\nlicense\n=======\n\nMIT/X11\n", + "readmeFilename": "README.markdown", + "_id": "mkdirp@0.3.4", + "_from": "mkdirp@0.3.x" +} diff --git a/test/node_modules/jade/node_modules/mkdirp/test/chmod.js b/test/node_modules/jade/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/clobber.js b/test/node_modules/jade/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/mkdirp.js b/test/node_modules/jade/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..b07cd70 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('woo', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/perm.js b/test/node_modules/jade/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..23a7abb --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/perm.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/perm_sync.js b/test/node_modules/jade/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..f685f60 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,39 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); + +test('sync root perm', function (t) { + t.plan(1); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/race.js b/test/node_modules/jade/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..96a0447 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/race.js @@ -0,0 +1,41 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('race', function (t) { + t.plan(4); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + } + }) + }) + }); + } +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/rel.js b/test/node_modules/jade/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..7985824 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/rel.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('rel', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/return.js b/test/node_modules/jade/node_modules/mkdirp/test/return.js new file mode 100644 index 0000000..bce68e5 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/return.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, '/tmp/' + x); + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, null); + }); + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/return_sync.js b/test/node_modules/jade/node_modules/mkdirp/test/return_sync.js new file mode 100644 index 0000000..7c222d3 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/return_sync.js @@ -0,0 +1,24 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + // Note that this will throw on failure, which will fail the test. + var made = mkdirp.sync(file); + t.equal(made, '/tmp/' + x); + + // making the same file again should have no effect. + made = mkdirp.sync(file); + t.equal(made, null); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/root.js b/test/node_modules/jade/node_modules/mkdirp/test/root.js new file mode 100644 index 0000000..97ad7a2 --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/root.js @@ -0,0 +1,18 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('root', function (t) { + // '/' on unix, 'c:/' on windows. + var file = path.resolve('/'); + + mkdirp(file, 0755, function (err) { + if (err) throw err + fs.stat(file, function (er, stat) { + if (er) throw er + t.ok(stat.isDirectory(), 'target is a directory'); + t.end(); + }) + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/sync.js b/test/node_modules/jade/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..7530cad --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file, 0755); + } catch (err) { + t.fail(err); + return t.end(); + } + + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }); + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/umask.js b/test/node_modules/jade/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..64ccafe --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/umask.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/test/node_modules/jade/node_modules/mkdirp/test/umask_sync.js b/test/node_modules/jade/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..35bd5cb --- /dev/null +++ b/test/node_modules/jade/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file); + } catch (err) { + t.fail(err); + return t.end(); + } + + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }); + }); +}); diff --git a/test/node_modules/jade/package.json b/test/node_modules/jade/package.json new file mode 100644 index 0000000..190c74e --- /dev/null +++ b/test/node_modules/jade/package.json @@ -0,0 +1,46 @@ +{ + "name": "jade", + "description": "Jade template engine", + "version": "0.28.1", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/jade" + }, + "main": "./index.js", + "bin": { + "jade": "./bin/jade" + }, + "man": [ + "./jade.1" + ], + "dependencies": { + "commander": "0.6.1", + "mkdirp": "0.3.x", + "coffee-script": "~1.4.0" + }, + "devDependencies": { + "mocha": "*", + "markdown": "*", + "stylus": "*", + "uubench": "*", + "should": "*", + "less": "*", + "uglify-js": "*" + }, + "component": { + "scripts": { + "jade": "runtime.js" + } + }, + "scripts": { + "prepublish": "npm prune" + }, + "readme": " [![Build Status](https://secure.travis-ci.org/visionmedia/jade.png)](http://travis-ci.org/visionmedia/jade)\n\n# Jade - template engine\n\n Jade is a high performance template engine heavily influenced by [Haml](http://haml-lang.com)\n and implemented with JavaScript for [node](http://nodejs.org). For discussion join the [Google Group](http://groups.google.com/group/jadejs).\n\n## Test drive\n\n You can test drive Jade online [here](http://naltatis.github.com/jade-syntax-docs).\n\n## README Contents\n\n- [Features](#a1)\n- [Implementations](#a2)\n- [Installation](#a3)\n- [Browser Support](#a4)\n- [Public API](#a5)\n- [Syntax](#a6)\n - [Line Endings](#a6-1)\n - [Tags](#a6-2)\n - [Tag Text](#a6-3)\n - [Comments](#a6-4)\n - [Block Comments](#a6-5)\n - [Nesting](#a6-6)\n - [Block Expansion](#a6-7)\n - [Case](#a6-8)\n - [Attributes](#a6-9)\n - [HTML](#a6-10)\n - [Doctypes](#a6-11)\n- [Filters](#a7)\n- [Code](#a8)\n- [Iteration](#a9)\n- [Conditionals](#a10)\n- [Template inheritance](#a11)\n- [Block append / prepend](#a12)\n- [Includes](#a13)\n- [Mixins](#a14)\n- [Generated Output](#a15)\n- [Example Makefile](#a16)\n- [jade(1)](#a17)\n- [Tutorials](#a18)\n- [License](#a19)\n\n\n## Features\n\n - client-side support\n - great readability\n - flexible indentation\n - block-expansion\n - mixins\n - static includes\n - attribute interpolation\n - code is escaped by default for security\n - contextual error reporting at compile & run time\n - executable for compiling jade templates via the command line\n - html 5 mode (the default doctype)\n - optional memory caching\n - combine dynamic and static tag classes\n - parse tree manipulation via _filters_\n - template inheritance\n - block append / prepend\n - supports [Express JS](http://expressjs.com) out of the box\n - transparent iteration over objects, arrays, and even non-enumerables via `each`\n - block comments\n - no tag prefix\n - filters\n - :stylus must have [stylus](http://github.com/LearnBoost/stylus) installed\n - :less must have [less.js](http://github.com/cloudhead/less.js) installed\n - :markdown must have [markdown-js](http://github.com/evilstreak/markdown-js), [node-discount](http://github.com/visionmedia/node-discount), or [marked](http://github.com/chjj/marked) installed\n - :cdata\n - :coffeescript must have [coffee-script](http://jashkenas.github.com/coffee-script/) installed\n - [Emacs Mode](https://github.com/brianc/jade-mode)\n - [Vim Syntax](https://github.com/digitaltoad/vim-jade)\n - [TextMate Bundle](http://github.com/miksago/jade-tmbundle)\n - [Coda/SubEtha syntax Mode](https://github.com/aaronmccall/jade.mode)\n - [Screencasts](http://tjholowaychuk.com/post/1004255394/jade-screencast-template-engine-for-nodejs)\n - [html2jade](https://github.com/donpark/html2jade) converter\n\n\n## Implementations\n\n - [php](http://github.com/everzet/jade.php)\n - [scala](http://scalate.fusesource.org/versions/snapshot/documentation/scaml-reference.html)\n - [ruby](http://github.com/stonean/slim)\n - [python](https://github.com/SyrusAkbary/pyjade)\n - [java](https://github.com/neuland/jade4j)\n\n\n## Installation\n\nvia npm:\n\n```bash\n$ npm install jade\n```\n\n\n## Browser Support\n\n To compile jade to a single file compatible for client-side use simply execute:\n\n```bash\n$ make jade.js\n```\n\n Alternatively, if uglifyjs is installed via npm (`npm install uglify-js`) you may execute the following which will create both files. However each release builds these for you.\n\n```bash\n$ make jade.min.js\n```\n\n By default Jade instruments templates with line number statements such as `__.lineno = 3` for debugging purposes. When used in a browser it's useful to minimize this boiler plate, you can do so by passing the option `{ compileDebug: false }`. The following template\n\n```jade\np Hello #{name}\n```\n\n Can then be as small as the following generated function:\n\n```js\nfunction anonymous(locals, attrs, escape, rethrow) {\n var buf = [];\n with (locals || {}) {\n var interp;\n buf.push('\\n

    Hello ' + escape((interp = name) == null ? '' : interp) + '\\n

    ');\n }\n return buf.join(\"\");\n}\n```\n\n Through the use of Jade's `./runtime.js` you may utilize these pre-compiled templates on the client-side _without_ Jade itself, all you need is the associated utility functions (in runtime.js), which are then available as `jade.attrs`, `jade.escape` etc. To enable this you should pass `{ client: true }` to `jade.compile()` to tell Jade to reference the helper functions\n via `jade.attrs`, `jade.escape` etc.\n\n```js\nfunction anonymous(locals, attrs, escape, rethrow) {\n var attrs = jade.attrs, escape = jade.escape, rethrow = jade.rethrow;\n var buf = [];\n with (locals || {}) {\n var interp;\n buf.push('\\n

    Hello ' + escape((interp = name) == null ? '' : interp) + '\\n

    ');\n }\n return buf.join(\"\");\n}\n```\n\n
    \n## Public API\n\n```js\nvar jade = require('jade');\n\n// Compile a function\nvar fn = jade.compile('string of jade', options);\nfn(locals);\n```\n\n### Options\n\n - `self` Use a `self` namespace to hold the locals _(false by default)_\n - `locals` Local variable object\n - `filename` Used in exceptions, and required when using includes\n - `debug` Outputs tokens and function body generated\n - `compiler` Compiler to replace jade's default\n - `compileDebug` When `false` no debug instrumentation is compiled\n - `pretty` Add pretty-indentation whitespace to output _(false by default)_\n\n\n## Syntax\n\n\n### Line Endings\n\n**CRLF** and **CR** are converted to **LF** before parsing.\n\n\n### Tags\n\nA tag is simply a leading word:\n\n```jade\nhtml\n```\n\nfor example is converted to ``\n\ntags can also have ids:\n\n```jade\ndiv#container\n```\n\nwhich would render `
    `\n\nhow about some classes?\n\n```jade\ndiv.user-details\n```\n\nrenders `
    `\n\nmultiple classes? _and_ an id? sure:\n\n```jade\ndiv#foo.bar.baz\n```\n\nrenders `
    `\n\ndiv div div sure is annoying, how about:\n\n```jade\n#foo\n.bar\n```\n\nwhich is syntactic sugar for what we have already been doing, and outputs:\n\n```html\n
    \n```\n\n
    \n### Tag Text\n\nSimply place some content after the tag:\n\n```jade\np wahoo!\n```\n\nrenders `

    wahoo!

    `.\n\nwell cool, but how about large bodies of text:\n\n```jade\np\n | foo bar baz\n | rawr rawr\n | super cool\n | go jade go\n```\n\nrenders `

    foo bar baz rawr.....

    `\n\ninterpolation? yup! both types of text can utilize interpolation,\nif we passed `{ name: 'tj', email: 'tj@vision-media.ca' }` to the compiled function we can do the following:\n\n```jade\n#user #{name} <#{email}>\n```\n\noutputs `
    tj <tj@vision-media.ca>
    `\n\nActually want `#{}` for some reason? escape it!\n\n```jade\np \\#{something}\n```\n\nnow we have `

    #{something}

    `\n\nWe can also utilize the unescaped variant `!{html}`, so the following\nwill result in a literal script tag:\n\n```jade\n- var html = \"\"\n| !{html}\n```\n\nNested tags that also contain text can optionally use a text block:\n\n```jade\nlabel\n | Username:\n input(name='user[name]')\n```\n\nor immediate tag text:\n\n```jade\nlabel Username:\n input(name='user[name]')\n```\n\nTags that accept _only_ text such as `script` and `style` do not\nneed the leading `|` character, for example:\n\n```jade\nhtml\n head\n title Example\n script\n if (foo) {\n bar();\n } else {\n baz();\n }\n```\n\nOnce again as an alternative, we may use a trailing `.` to indicate a text block, for example:\n\n```jade\np.\n foo asdf\n asdf\n asdfasdfaf\n asdf\n asd.\n```\n\noutputs:\n\n```html\n

    foo asdf\nasdf\n asdfasdfaf\n asdf\nasd.\n

    \n```\n\nThis however differs from a trailing `.` followed by a space, which although is ignored by the Jade parser, tells Jade that this period is a literal:\n\n```jade\np .\n```\n\noutputs:\n\n```html\n

    .

    \n```\n\nIt should be noted that text blocks should be doubled escaped. For example if you desire the following output.\n\n```html\n

    foo\\bar

    \n```\n\nuse:\n\n```jade\np.\n foo\\\\bar\n```\n\n
    \n### Comments\n\nSingle line comments currently look the same as JavaScript comments,\naka `//` and must be placed on their own line:\n\n```jade\n// just some paragraphs\np foo\np bar\n```\n\nwould output\n\n```html\n\n

    foo

    \n

    bar

    \n```\n\nJade also supports unbuffered comments, by simply adding a hyphen:\n\n```jade\n//- will not output within markup\np foo\np bar\n```\n\noutputting\n\n```html\n

    foo

    \n

    bar

    \n```\n\n
    \n### Block Comments\n\n A block comment is legal as well:\n\n```jade\nbody\n //\n #content\n h1 Example\n```\n\noutputting\n\n```html\n\n \n\n```\n\nJade supports conditional-comments as well, for example:\n\n```jade\nhead\n //if lt IE 8\n script(src='/ie-sucks.js')\n```\n\noutputs:\n\n```html\n\n \n\n```\n\n\n### Nesting\n\n Jade supports nesting to define the tags in a natural way:\n\n```jade\nul\n li.first\n a(href='#') foo\n li\n a(href='#') bar\n li.last\n a(href='#') baz\n```\n\n\n### Block Expansion\n\n Block expansion allows you to create terse single-line nested tags,\n the following example is equivalent to the nesting example above.\n\n```jade\nul\n li.first: a(href='#') foo\n li: a(href='#') bar\n li.last: a(href='#') baz\n```\n\n\n### Case\n\n The case statement takes the following form:\n\n```jade\nhtml\n body\n friends = 10\n case friends\n when 0\n p you have no friends\n when 1\n p you have a friend\n default\n p you have #{friends} friends\n```\n\n Block expansion may also be used:\n\n```jade\nfriends = 5\n\nhtml\n body\n case friends\n when 0: p you have no friends\n when 1: p you have a friend\n default: p you have #{friends} friends\n```\n\n\n### Attributes\n\nJade currently supports `(` and `)` as attribute delimiters.\n\n```jade\na(href='/login', title='View login page') Login\n```\n\nWhen a value is `undefined` or `null` the attribute is _not_ added,\nso this is fine, it will not compile `something=\"null\"`.\n\n```jade\ndiv(something=null)\n```\n\nBoolean attributes are also supported:\n\n```jade\ninput(type=\"checkbox\", checked)\n```\n\nBoolean attributes with code will only output the attribute when `true`:\n\n```jade\ninput(type=\"checkbox\", checked=someValue)\n```\n\nMultiple lines work too:\n\n```jade\ninput(type='checkbox',\n name='agreement',\n checked)\n```\n\nMultiple lines without the comma work fine:\n\n```jade\ninput(type='checkbox'\n name='agreement'\n checked)\n```\n\nFunky whitespace? fine:\n\n```jade\ninput(\n type='checkbox'\n name='agreement'\n checked)\n```\n\nColons work:\n\n```jade\nrss(xmlns:atom=\"atom\")\n```\n\nSuppose we have the `user` local `{ id: 12, name: 'tobi' }`\nand we wish to create an anchor tag with `href` pointing to \"/user/12\"\nwe could use regular javascript concatenation:\n\n```jade\na(href='/user/' + user.id)= user.name\n```\n\nor we could use jade's interpolation, which I added because everyone\nusing Ruby or CoffeeScript seems to think this is legal js..:\n\n```jade\na(href='/user/#{user.id}')= user.name\n```\n\nThe `class` attribute is special-cased when an array is given,\nallowing you to pass an array such as `bodyClasses = ['user', 'authenticated']` directly:\n\n```jade\nbody(class=bodyClasses)\n```\n\n\n### HTML\n\n Inline html is fine, we can use the pipe syntax to\n write arbitrary text, in this case some html:\n\n```jade\nhtml\n body\n |

    Title

    \n |

    foo bar baz

    \n```\n\n Or we can use the trailing `.` to indicate to Jade that we\n only want text in this block, allowing us to omit the pipes:\n\n```jade\nhtml\n body.\n

    Title

    \n

    foo bar baz

    \n```\n\n Both of these examples yield the same result:\n\n```html\n

    Title

    \n

    foo bar baz

    \n\n```\n\n The same rule applies for anywhere you can have text\n in jade, raw html is fine:\n\n```jade\nhtml\n body\n h1 User #{name}\n```\n\n
    \n### Doctypes\n\nTo add a doctype simply use `!!!`, or `doctype` followed by an optional value:\n\n```jade\n!!!\n```\n\nor\n\n```jade\ndoctype\n```\n\nWill output the _html 5_ doctype, however:\n\n```jade\n!!! transitional\n```\n\nWill output the _transitional_ doctype.\n\nDoctypes are case-insensitive, so the following are equivalent:\n\n```jade\ndoctype Basic\ndoctype basic\n```\n\nit's also possible to simply pass a doctype literal:\n\n```jade\ndoctype html PUBLIC \"-//W3C//DTD XHTML Basic 1.1//EN\n```\n\nyielding:\n\n```html\n\n```\n\nBelow are the doctypes defined by default, which can easily be extended:\n\n```js\nvar doctypes = exports.doctypes = {\n '5': '',\n 'default': '',\n 'xml': '',\n 'transitional': '',\n 'strict': '',\n 'frameset': '',\n '1.1': '',\n 'basic': '',\n 'mobile': ''\n};\n```\n\nTo alter the default simply change:\n\n```js\njade.doctypes.default = 'whatever you want';\n```\n\n\n## Filters\n\nFilters are prefixed with `:`, for example `:markdown` and\npass the following block of text to an arbitrary function for processing. View the _features_\nat the top of this document for available filters.\n\n```jade\nbody\n :markdown\n Woah! jade _and_ markdown, very **cool**\n we can even link to [stuff](http://google.com)\n```\n\nRenders:\n\n```html\n

    Woah! jade and markdown, very cool we can even link to stuff

    \n```\n\n\n## Code\n\nJade currently supports three classifications of executable code. The first\nis prefixed by `-`, and is not buffered:\n\n```jade\n- var foo = 'bar';\n```\n\nThis can be used for conditionals, or iteration:\n\n```jade\n- for (var key in obj)\n p= obj[key]\n```\n\nDue to Jade's buffering techniques the following is valid as well:\n\n```jade\n- if (foo)\n ul\n li yay\n li foo\n li worked\n- else\n p oh no! didnt work\n```\n\nHell, even verbose iteration:\n\n```jade\n- if (items.length)\n ul\n - items.forEach(function(item){\n li= item\n - })\n```\n\nAnything you want!\n\nNext up we have _escaped_ buffered code, which is used to\nbuffer a return value, which is prefixed by `=`:\n\n```jade\n- var foo = 'bar'\n= foo\nh1= foo\n```\n\nWhich outputs `bar

    bar

    `. Code buffered by `=` is escaped\nby default for security, however to output unescaped return values\nyou may use `!=`:\n\n```jade\np!= aVarContainingMoreHTML\n```\n\n Jade also has designer-friendly variants, making the literal JavaScript\n more expressive and declarative. For example the following assignments\n are equivalent, and the expression is still regular javascript:\n\n```jade\n- var foo = 'foo ' + 'bar'\nfoo = 'foo ' + 'bar'\n```\n\n Likewise Jade has first-class `if`, `else if`, `else`, `until`, `while`, `unless` among others, however you must remember that the expressions are still regular javascript:\n\n```jade\nif foo == 'bar'\n ul\n li yay\n li foo\n li worked\nelse\n p oh no! didnt work\n```\n\n
    \n## Iteration\n\n Along with vanilla JavaScript Jade also supports a subset of\n constructs that allow you to create more designer-friendly templates,\n one of these constructs is `each`, taking the form:\n\n```jade\neach VAL[, KEY] in OBJ\n```\n\nAn example iterating over an array:\n\n```jade\n- var items = [\"one\", \"two\", \"three\"]\neach item in items\n li= item\n```\n\noutputs:\n\n```html\n
  • one
  • \n
  • two
  • \n
  • three
  • \n```\n\niterating an array with index:\n\n```jade\nitems = [\"one\", \"two\", \"three\"]\neach item, i in items\n li #{item}: #{i}\n```\n\noutputs:\n\n```html\n
  • one: 0
  • \n
  • two: 1
  • \n
  • three: 2
  • \n```\n\niterating an object's keys and values:\n\n```jade\nobj = { foo: 'bar' }\neach val, key in obj\n li #{key}: #{val}\n```\n\nwould output `
  • foo: bar
  • `\n\nInternally Jade converts these statements to regular\nJavaScript loops such as `users.forEach(function(user){`,\nso lexical scope and nesting applies as it would with regular\nJavaScript:\n\n```jade\neach user in users\n each role in user.roles\n li= role\n```\n\n You may also use `for` if you prefer:\n\n```jade\nfor user in users\n for role in user.roles\n li= role\n```\n\n
    \n## Conditionals\n\n Jade conditionals are equivalent to those using the code (`-`) prefix,\n however allow you to ditch parenthesis to become more designer friendly,\n however keep in mind the expression given is _regular_ JavaScript:\n\n```jade\nfor user in users\n if user.role == 'admin'\n p #{user.name} is an admin\n else\n p= user.name\n```\n\n is equivalent to the following using vanilla JavaScript literals:\n\n```jade\nfor user in users\n - if (user.role == 'admin')\n p #{user.name} is an admin\n - else\n p= user.name\n```\n\n Jade also provides have `unless` which is equivalent to `if (!(expr))`:\n\n```jade\nfor user in users\n unless user.isAnonymous\n p\n | Click to view\n a(href='/users/' + user.id)= user.name\n```\n\n\n## Template inheritance\n\n Jade supports template inheritance via the `block` and `extends` keywords. A block is simply a \"block\" of Jade that may be replaced within a child template, this process is recursive. To activate template inheritance in Express 2.x you must add: `app.set('view options', { layout: false });`.\n\n Jade blocks can provide default content if desired, however optional as shown below by `block scripts`, `block content`, and `block foot`.\n\n```jade\nhtml\n head\n h1 My Site - #{title}\n block scripts\n script(src='/jquery.js')\n body\n block content\n block foot\n #footer\n p some footer content\n```\n\n Now to extend the layout, simply create a new file and use the `extends` directive as shown below, giving the path (with or without the .jade extension). You may now define one or more blocks that will override the parent block content, note that here the `foot` block is _not_ redefined and will output \"some footer content\".\n\n```jade\nextends layout\n\nblock scripts\n script(src='/jquery.js')\n script(src='/pets.js')\n\nblock content\n h1= title\n each pet in pets\n include pet\n```\n\n It's also possible to override a block to provide additional blocks, as shown in the following example where `content` now exposes a `sidebar` and `primary` block for overriding, or the child template could override `content` all together.\n\n```jade\nextends regular-layout\n\nblock content\n .sidebar\n block sidebar\n p nothing\n .primary\n block primary\n p nothing\n```\n\n\n## Block append / prepend\n\n Jade allows you to _replace_ (default), _prepend_, or _append_ blocks. Suppose for example you have default scripts in a \"head\" block that you wish to utilize on _every_ page, you might do this:\n\n```jade\nhtml\n head\n block head\n script(src='/vendor/jquery.js')\n script(src='/vendor/caustic.js')\n body\n block content\n```\n\n Now suppose you have a page of your application for a JavaScript game, you want some game related scripts as well as these defaults, you can simply `append` the block:\n\n```jade\nextends layout\n\nblock append head\n script(src='/vendor/three.js')\n script(src='/game.js')\n```\n\n When using `block append` or `block prepend` the `block` is optional:\n\n```jade\nextends layout\n\nappend head\n script(src='/vendor/three.js')\n script(src='/game.js')\n```\n\n\n## Includes\n\n Includes allow you to statically include chunks of Jade,\n or other content like css, or html which lives in separate files. The classical example is including a header and footer. Suppose we have the following directory structure:\n\n ./layout.jade\n ./includes/\n ./head.jade\n ./foot.jade\n\nand the following _layout.jade_:\n\n```jade\nhtml\n include includes/head\n body\n h1 My Site\n p Welcome to my super amazing site.\n include includes/foot\n```\n\nboth includes _includes/head_ and _includes/foot_ are\nread relative to the `filename` option given to _layout.jade_,\nwhich should be an absolute path to this file, however Express does this for you. Include then parses these files, and injects the AST produced to render what you would expect:\n\n```html\n\n \n My Site\n \n \n \n

    My Site

    \n

    Welcome to my super lame site.

    \n
    \n

    Copyright>(c) foobar

    \n
    \n \n\n```\n\n As mentioned `include` can be used to include other content\n such as html or css. By providing an extension Jade will not\n assume that the file is Jade source and will include it as\n a literal:\n\n```jade\nhtml\n body\n include content.html\n```\n\n Include directives may also accept a block, in which case the\n the given block will be appended to the _last_ block defined\n in the file. For example if `head.jade` contains:\n\n```jade\nhead\n script(src='/jquery.js')\n```\n\n We may append values by providing a block to `include head`\n as shown below, adding the two scripts.\n\n```jade\nhtml\n include head\n script(src='/foo.js')\n script(src='/bar.js')\n body\n h1 test\n```\n\n You may also `yield` within an included template, allowing you to explicitly mark where the block given to `include` will be placed. Suppose for example you wish to prepend scripts rather than append, you might do the following:\n\n```jade\nhead\n yield\n script(src='/jquery.js')\n script(src='/jquery.ui.js')\n```\n\n Since included Jade is parsed and literally merges the AST, lexically scoped variables function as if the included Jade was written right in the same file. This means `include` may be used as sort of partial, for example suppose we have `user.jade` which utilizes a `user` variable.\n\n```jade\nh1= user.name\np= user.occupation\n```\n\nWe could then simply `include user` while iterating users, and since the `user` variable is already defined within the loop the included template will have access to it.\n\n```jade\nusers = [{ name: 'Tobi', occupation: 'Ferret' }]\n\neach user in users\n .user\n include user\n```\n\nyielding:\n\n```html\n
    \n

    Tobi

    \n

    Ferret

    \n
    \n```\n\nIf we wanted to expose a different variable name as `user` since `user.jade` references that name, we could simply define a new variable as shown here with `user = person`:\n\n```jade\neach person in users\n .user\n user = person\n include user\n```\n\n
    \n## Mixins\n\n Mixins are converted to regular JavaScript functions in\n the compiled template that Jade constructs. Mixins may\n take arguments, though not required:\n\n```jade\nmixin list\n ul\n li foo\n li bar\n li baz\n```\n\n Utilizing a mixin without args looks similar, just without a block:\n\n```jade\nh2 Groceries\nmixin list\n```\n\n Mixins may take one or more arguments as well, the arguments\n are regular javascripts expressions, so for example the following:\n\n```jade\nmixin pets(pets)\n ul.pets\n - each pet in pets\n li= pet\n\nmixin profile(user)\n .user\n h2= user.name\n mixin pets(user.pets)\n```\n\n Would yield something similar to the following html:\n\n```html\n
    \n

    tj

    \n
      \n
    • tobi
    • \n
    • loki
    • \n
    • jane
    • \n
    • manny
    • \n
    \n
    \n```\n\n
    \n## Generated Output\n\n Suppose we have the following Jade:\n\n```jade\n- var title = 'yay'\nh1.title #{title}\np Just an example\n```\n\n When the `compileDebug` option is not explicitly `false`, Jade\n will compile the function instrumented with `__.lineno = n;`, which\n in the event of an exception is passed to `rethrow()` which constructs\n a useful message relative to the initial Jade input.\n\n```js\nfunction anonymous(locals) {\n var __ = { lineno: 1, input: \"- var title = 'yay'\\nh1.title #{title}\\np Just an example\", filename: \"testing/test.js\" };\n var rethrow = jade.rethrow;\n try {\n var attrs = jade.attrs, escape = jade.escape;\n var buf = [];\n with (locals || {}) {\n var interp;\n __.lineno = 1;\n var title = 'yay'\n __.lineno = 2;\n buf.push('');\n buf.push('' + escape((interp = title) == null ? '' : interp) + '');\n buf.push('');\n __.lineno = 3;\n buf.push('

    ');\n buf.push('Just an example');\n buf.push('

    ');\n }\n return buf.join(\"\");\n } catch (err) {\n rethrow(err, __.input, __.filename, __.lineno);\n }\n}\n```\n\nWhen the `compileDebug` option _is_ explicitly `false`, this instrumentation\nis stripped, which is very helpful for light-weight client-side templates. Combining Jade's options with the `./runtime.js` file in this repo allows you\nto toString() compiled templates and avoid running the entire Jade library on\nthe client, increasing performance, and decreasing the amount of JavaScript\nrequired.\n\n```js\nfunction anonymous(locals) {\n var attrs = jade.attrs, escape = jade.escape;\n var buf = [];\n with (locals || {}) {\n var interp;\n var title = 'yay'\n buf.push('');\n buf.push('' + escape((interp = title) == null ? '' : interp) + '');\n buf.push('');\n buf.push('

    ');\n buf.push('Just an example');\n buf.push('

    ');\n }\n return buf.join(\"\");\n}\n```\n\n
    \n## Example Makefile\n\n Below is an example Makefile used to compile _pages/*.jade_\n into _pages/*.html_ files by simply executing `make`.\n\n```make\nJADE = $(shell find pages/*.jade)\nHTML = $(JADE:.jade=.html)\n\nall: $(HTML)\n\n%.html: %.jade\n jade < $< --path $< > $@\n\nclean:\n rm -f $(HTML)\n\n.PHONY: clean\n```\n\nthis can be combined with the `watch(1)` command to produce\na watcher-like behaviour:\n\n```bash\n$ watch make\n```\n\n\n## jade(1)\n\n```\n\nUsage: jade [options] [dir|file ...]\n\nOptions:\n\n -h, --help output usage information\n -V, --version output the version number\n -o, --obj javascript options object\n -O, --out output the compiled html to \n -p, --path filename used to resolve includes\n -P, --pretty compile pretty html output\n -c, --client compile for client-side runtime.js\n -D, --no-debug compile without debugging (smaller functions)\n\nExamples:\n\n # translate jade the templates dir\n $ jade templates\n\n # create {foo,bar}.html\n $ jade {foo,bar}.jade\n\n # jade over stdio\n $ jade < my.jade > my.html\n\n # jade over stdio\n $ echo \"h1 Jade!\" | jade\n\n # foo, bar dirs rendering to /tmp\n $ jade foo bar --out /tmp\n\n```\n\n\n## Tutorials\n\n - cssdeck interactive [Jade syntax tutorial](http://cssdeck.com/labs/learning-the-jade-templating-engine-syntax)\n - cssdeck interactive [Jade logic tutorial](http://cssdeck.com/labs/jade-templating-tutorial-codecast-part-2)\n - in [Japanese](http://blog.craftgear.net/4f501e97c1347ec934000001/title/10%E5%88%86%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8Bjade%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3)\n\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "Readme.md", + "_id": "jade@0.28.1", + "_from": "jade@>= 0.0.1" +} diff --git a/test/node_modules/jade/runtime.js b/test/node_modules/jade/runtime.js new file mode 100644 index 0000000..0f54907 --- /dev/null +++ b/test/node_modules/jade/runtime.js @@ -0,0 +1,179 @@ + +jade = (function(exports){ +/*! + * Jade - runtime + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Lame Array.isArray() polyfill for now. + */ + +if (!Array.isArray) { + Array.isArray = function(arr){ + return '[object Array]' == Object.prototype.toString.call(arr); + }; +} + +/** + * Lame Object.keys() polyfill for now. + */ + +if (!Object.keys) { + Object.keys = function(obj){ + var arr = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + arr.push(key); + } + } + return arr; + } +} + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + ac = ac.filter(nulls); + bc = bc.filter(nulls); + a['class'] = ac.concat(bc).join(' '); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function nulls(val) { + return val != null; +} + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + * @api private + */ + +exports.attrs = function attrs(obj, escaped){ + var buf = [] + , terse = obj.terse; + + delete obj.terse; + var keys = Object.keys(obj) + , len = keys.length; + + if (len) { + buf.push(''); + for (var i = 0; i < len; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('boolean' == typeof val || null == val) { + if (val) { + terse + ? buf.push(key) + : buf.push(key + '="' + key + '"'); + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + buf.push(key + "='" + JSON.stringify(val) + "'"); + } else if ('class' == key && Array.isArray(val)) { + buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); + } else if (escaped && escaped[key]) { + buf.push(key + '="' + exports.escape(val) + '"'); + } else { + buf.push(key + '="' + val + '"'); + } + } + } + + return buf.join(' '); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + return String(html) + .replace(/&(?!(\w+|\#\d+);)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno){ + if (!filename) throw err; + + var context = 3 + , str = require('fs').readFileSync(filename, 'utf8') + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' > ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; + + return exports; + +})({}); diff --git a/test/node_modules/jade/runtime.min.js b/test/node_modules/jade/runtime.min.js new file mode 100644 index 0000000..1714efb --- /dev/null +++ b/test/node_modules/jade/runtime.min.js @@ -0,0 +1 @@ +jade=function(exports){Array.isArray||(Array.isArray=function(arr){return"[object Array]"==Object.prototype.toString.call(arr)}),Object.keys||(Object.keys=function(obj){var arr=[];for(var key in obj)obj.hasOwnProperty(key)&&arr.push(key);return arr}),exports.merge=function merge(a,b){var ac=a["class"],bc=b["class"];if(ac||bc)ac=ac||[],bc=bc||[],Array.isArray(ac)||(ac=[ac]),Array.isArray(bc)||(bc=[bc]),ac=ac.filter(nulls),bc=bc.filter(nulls),a["class"]=ac.concat(bc).join(" ");for(var key in b)key!="class"&&(a[key]=b[key]);return a};function nulls(val){return val!=null}return exports.attrs=function attrs(obj,escaped){var buf=[],terse=obj.terse;delete obj.terse;var keys=Object.keys(obj),len=keys.length;if(len){buf.push("");for(var i=0;i/g,">").replace(/"/g,""")},exports.rethrow=function rethrow(err,filename,lineno){if(!filename)throw err;var context=3,str=require("fs").readFileSync(filename,"utf8"),lines=str.split("\n"),start=Math.max(lineno-context,0),end=Math.min(lines.length,lineno+context),context=lines.slice(start,end).map(function(line,i){var curr=i+start+1;return(curr==lineno?" > ":" ")+curr+"| "+line}).join("\n");throw err.path=filename,err.message=(filename||"Jade")+":"+lineno+"\n"+context+"\n\n"+err.message,err},exports}({}); \ No newline at end of file diff --git a/test/node_modules/jade/testing/index.html b/test/node_modules/jade/testing/index.html new file mode 100644 index 0000000..105c9f4 --- /dev/null +++ b/test/node_modules/jade/testing/index.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/test/node_modules/jade/testing/index.jade b/test/node_modules/jade/testing/index.jade new file mode 100644 index 0000000..a22e82a --- /dev/null +++ b/test/node_modules/jade/testing/index.jade @@ -0,0 +1,4 @@ + +html + body + include some.js diff --git a/test/node_modules/jade/testing/layout.html b/test/node_modules/jade/testing/layout.html new file mode 100644 index 0000000..cbbfc7a --- /dev/null +++ b/test/node_modules/jade/testing/layout.html @@ -0,0 +1 @@ +Application \ No newline at end of file diff --git a/test/node_modules/jade/testing/layout.jade b/test/node_modules/jade/testing/layout.jade new file mode 100644 index 0000000..a48765c --- /dev/null +++ b/test/node_modules/jade/testing/layout.jade @@ -0,0 +1,6 @@ +!!! 5 +html + head + title Application + body + block content diff --git a/test/node_modules/jade/testing/mobile.html b/test/node_modules/jade/testing/mobile.html new file mode 100644 index 0000000..e69de29 diff --git a/test/node_modules/jade/testing/mobile.jade b/test/node_modules/jade/testing/mobile.jade new file mode 100644 index 0000000..e69de29 diff --git a/test/node_modules/jade/testing/nested/something.html b/test/node_modules/jade/testing/nested/something.html new file mode 100644 index 0000000..d6b0493 --- /dev/null +++ b/test/node_modules/jade/testing/nested/something.html @@ -0,0 +1 @@ +

    out

    \ No newline at end of file diff --git a/test/node_modules/jade/testing/nested/something.jade b/test/node_modules/jade/testing/nested/something.jade new file mode 100644 index 0000000..552c797 --- /dev/null +++ b/test/node_modules/jade/testing/nested/something.jade @@ -0,0 +1 @@ +p out \ No newline at end of file diff --git a/test/node_modules/jade/testing/some.js b/test/node_modules/jade/testing/some.js new file mode 100644 index 0000000..3104e71 --- /dev/null +++ b/test/node_modules/jade/testing/some.js @@ -0,0 +1,4 @@ + +if (something) { + something('hey'); +} diff --git a/test/node_modules/jade/testing/test.md b/test/node_modules/jade/testing/test.md new file mode 100644 index 0000000..9af1bb2 --- /dev/null +++ b/test/node_modules/jade/testing/test.md @@ -0,0 +1,5 @@ +Just a _test_ of some **markdown**: + + - foo + - bar + - baz diff --git a/test/node_modules/mongoose/.npmignore b/test/node_modules/mongoose/.npmignore new file mode 100644 index 0000000..df5ea94 --- /dev/null +++ b/test/node_modules/mongoose/.npmignore @@ -0,0 +1,13 @@ +lib-cov +**.swp +*.sw* +*.orig +.DS_Store +node_modules/ +benchmarks/ +docs/ +test/ +Makefile +CNAME +index.html +index.jade diff --git a/test/node_modules/mongoose/.travis.yml b/test/node_modules/mongoose/.travis.yml new file mode 100644 index 0000000..c663337 --- /dev/null +++ b/test/node_modules/mongoose/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - 0.4 + - 0.6 + - 0.8 +services: + - mongodb diff --git a/test/node_modules/mongoose/CONTRIBUTING.md b/test/node_modules/mongoose/CONTRIBUTING.md new file mode 100644 index 0000000..05349a4 --- /dev/null +++ b/test/node_modules/mongoose/CONTRIBUTING.md @@ -0,0 +1,35 @@ +## Contributing to Mongoose + +### Reporting bugs + +- Before opening a new issue, look for existing [issues](https://github.com/learnboost/mongoose/issues) to avoid duplication. If the issue does not yet exist, [create one](https://github.com/learnboost/mongoose/issues/new). + - _The source of this project is written in javascript, not coffeescript, therefore your bug reports should be written in javascript_. + +### Fixing bugs / Adding features + +- Before starting to write code, look for existing [issues](https://github.com/learnboost/mongoose/issues). That way you avoid working on something that might not be of interest or that has been addressed already in a different branch. You can create a new issue [here](https://github.com/learnboost/mongoose/issues/new). + - _The source of this project is written in javascript, not coffeescript, therefore your bug reports should be written in javascript_. +- Fork the [repo](https://github.com/learnboost/mongoose) _or_ for small documentation changes, navigate to the source on github and click the [Edit](https://github.com/blog/844-forking-with-the-edit-button) button. +- Follow the general coding style of the rest of the project: + - 2 space tabs + - no trailing whitespace + - comma first + - inline documentation for new methods, class members, etc + - 1 space between conditionals/functions, and their parenthesis and curly braces + - `if (..) {` + - `for (..) {` + - `while (..) {` + - `function (err) {` +- Write tests and make sure they pass (execute `make test` from the command line to run the test suite). + +### Documentation + +To contribute to the [API documentation](http://mongoosejs.com/docs/api.html) just make your changes to the inline documentation of the appropriate [source code](https://github.com/LearnBoost/mongoose/tree/master/lib) in the master branch and submit a [pull request](https://help.github.com/articles/using-pull-requests/). You might also use the github [Edit](https://github.com/blog/844-forking-with-the-edit-button) button. + +To contribute to the [guide](http://mongoosejs.com/docs/guide.html) or [quick start](http://mongoosejs.com/docs/index.html) docs, make your changes to the appropriate `.jade` files in the [docs](https://github.com/LearnBoost/mongoose/tree/master/docs) directory of the master branch and submit a pull request. Again, the [Edit](https://github.com/blog/844-forking-with-the-edit-button) button might work for you here. + +If you'd like to preview your documentation changes, first commit your changes to your local master branch, then execute `make docs` from the project root, which switches to the gh-pages branch, merges from master, and builds all the static pages for you. Now execute `node server.js` from the project root which will launch a local webserver where you can browse the documentation site locally. If all looks good, submit a [pull request](https://help.github.com/articles/using-pull-requests/) to the master branch with your changes. + +### Plugins website + +The [plugins](http://plugins.mongoosejs.com/) site is also an [open source project](https://github.com/aheckmann/mongooseplugins) that you can get involved with. Feel free to fork and improve it as well! diff --git a/test/node_modules/mongoose/History.md b/test/node_modules/mongoose/History.md new file mode 100644 index 0000000..a9919b0 --- /dev/null +++ b/test/node_modules/mongoose/History.md @@ -0,0 +1,1264 @@ + +3.5.4 / 2013-01-07 +================== + + * changed; "_pres" & "_posts" are now reserved pathnames #1261 + * updated; driver to 1.2.8 + * fixed; exception when reopening a replica set. #1263 [ethankan](https://github.com/ethankan) + * website; updated + +3.5.3 / 2012-12-26 +================== + + * added; support for geo object notation #1257 + * fixed; $within query casting with arrays + * fixed; unix domain socket support #1254 + * updated; driver to 1.2.7 + * updated; muri to 0.0.5 + +3.5.2 / 2012-12-17 +================== + + * fixed; using auth with replica sets #1253 + +3.5.1 / 2012-12-12 +================== + + * fixed; regression when using subdoc with `path` as pathname #1245 [daeq](https://github.com/daeq) + * fixed; safer db option checks + * updated; driver to 1.2.5 + * website; add more examples + * website; clean up old docs + * website; fix prev release urls + * docs; clarify streaming with HTTP responses + +3.5.0 / 2012-12-10 +================== + + * added; paths to CastErrors #1239 + * added; support for mongodb connection string spec #1187 + * added; post validate event + * added; Schema#get (to retrieve schema options) + * added; VersionError #1071 + * added; npmignore [hidekiy](https://github.com/hidekiy) + * update; driver to 1.2.3 + * fixed; stackoverflow in setter #1234 + * fixed; utils.isObject() + * fixed; do not clobber user specified driver writeConcern #1227 + * fixed; always pass current document to post hooks + * fixed; throw error when user attempts to overwrite a model + * fixed; connection.model only caches on connection #1209 + * fixed; respect conn.model() creation when matching global model exists #1209 + * fixed; passing model name + collection name now always honors collection name + * fixed; setting virtual field to an empty object #1154 + * fixed; subclassed MongooseErrors exposure, now available in mongoose.Error.xxxx + * fixed; model.remove() ignoring callback when executed twice [daeq](https://github.com/daeq) #1210 + * docs; add collection option to schema api docs #1222 + * docs; NOTE about db safe options + * docs; add post hooks docs + * docs; connection string options + * docs; middleware is not executed with Model.remove #1241 + * docs; {g,s}etter introspection #777 + * docs; update validation docs + * docs; add link to plugins page + * docs; clarify error returned by unique indexes #1225 + * docs; more detail about disabling autoIndex behavior + * docs; add homepage section to package (npm docs mongoose) + * docs; more detail around collection name pluralization #1193 + * website; add .important css + * website; update models page + * website; update getting started + * website; update quick start + +3.4.0 / 2012-11-10 +================== + + * added; support for generic toJSON/toObject transforms #1160 #1020 #1197 + * added; doc.set() merge support #1148 [NuORDER](https://github.com/NuORDER) + * added; query#add support #1188 [aleclofabbro](https://github.com/aleclofabbro) + * changed; adding invalid nested paths to non-objects throws 4216f14 + * changed; fixed; stop invalid function cloning (internal fix) + * fixed; add query $and casting support #1180 [anotheri](https://github.com/anotheri) + * fixed; overwriting of query arguments #1176 + * docs; fix expires examples + * docs; transforms + * docs; schema `collection` option docs [hermanjunge](https://github.com/hermanjunge) + * website; updated + * tests; added + +3.3.1 / 2012-10-11 +================== + + * fixed; allow goose.connect(uris, dbname, opts) #1144 + * docs; persist API private checked state across page loads + +3.3.0 / 2012-10-10 +================== + + * fixed; passing options as 2nd arg to connect() #1144 + * fixed; race condition after no-op save #1139 + * fixed; schema field selection application in findAndModify #1150 + * fixed; directly setting arrays #1126 + * updated; driver to 1.1.11 + * updated; collection pluralization rules [mrickard](https://github.com/mrickard) + * tests; added + * docs; updated + +3.2.2 / 2012-10-08 +================== + + * updated; driver to 1.1.10 #1143 + * updated; use sliced 0.0.3 + * fixed; do not recast embedded docs unnecessarily + * fixed; expires schema option helper #1132 + * fixed; built in string setters #1131 + * fixed; debug output for Dates/ObjectId properties #1129 + * docs; fixed Javascript syntax error in example [olalonde](https://github.com/olalonde) + * docs; fix toJSON example #1137 + * docs; add ensureIndex production notes + * docs; fix spelling + * docs; add blogposts about v3 + * website; updated + * removed; undocumented inGroupsOf util + * tests; added + +3.2.1 / 2012-09-28 +================== + + * fixed; remove query batchSize option default of 1000 https://github.com/learnboost/mongoose/commit/3edaa8651 + * docs; updated + * website; updated + +3.2.0 / 2012-09-27 +================== + + * added; direct array index assignment with casting support `doc.array.set(index, value)` + * fixed; QueryStream#resume within same tick as pause() #1116 + * fixed; default value validatation #1109 + * fixed; array splice() not casting #1123 + * fixed; default array construction edge case #1108 + * fixed; query casting for inequalities in arrays #1101 [dpatti](https://github.com/dpatti) + * tests; added + * website; more documentation + * website; fixed layout issue #1111 [SlashmanX](https://github.com/SlashmanX) + * website; refactored [guille](https://github.com/guille) + +3.1.2 / 2012-09-10 +================== + + * added; ReadPreferrence schema option #1097 + * updated; driver to 1.1.7 + * updated; default query batchSize to 1000 + * fixed; we now cast the mapReduce query option #1095 + * fixed; $elemMatch+$in with field selection #1091 + * fixed; properly cast $elemMatch+$in conditions #1100 + * fixed; default field application of subdocs #1027 + * fixed; querystream prematurely dying #1092 + * fixed; querystream never resumes when paused at getMore boundries #1092 + * fixed; querystream occasionally emits data events after destroy #1092 + * fixed; remove unnecessary ObjectId creation in querystream + * fixed; allow ne(boolean) again #1093 + * docs; add populate/field selection syntax notes + * docs; add toObject/toJSON options detail + * docs; `read` schema option + +3.1.1 / 2012-08-31 +================== + + * updated; driver to 1.1.6 + +3.1.0 / 2012-08-29 +================== + + * changed; fixed; directly setting nested objects now overwrites entire object (previously incorrectly merged them) + * added; read pref support (mongodb 2.2) 205a709c + * added; aggregate support (mongodb 2.2) f3a5bd3d + * added; virtual {g,s}etter introspection (#1070) + * updated; docs [brettz9](https://github.com/brettz9) + * updated; driver to 1.1.5 + * fixed; retain virtual setter return values (#1069) + +3.0.3 / 2012-08-23 +================== + + * fixed; use of nested paths beginning w/ numbers #1062 + * fixed; query population edge case #1053 #1055 [jfremy](https://github.com/jfremy) + * fixed; simultaneous top and sub level array modifications #1073 + * added; id and _id schema option aliases + tests + * improve debug formatting to allow copy/paste logged queries into mongo shell [eknkc](https://github.com/eknkc) + * docs + +3.0.2 / 2012-08-17 +================== + + * added; missing support for v3 sort/select syntax to findAndModify helpers (#1058) + * fixed; replset fullsetup event emission + * fixed; reconnected event for replsets + * fixed; server reconnection setting discovery + * fixed; compat with non-schema path props using positional notation (#1048) + * fixed; setter/casting order (#665) + * docs; updated + +3.0.1 / 2012-08-11 +================== + + * fixed; throw Error on bad validators (1044) + * fixed; typo in EmbeddedDocument#parentArray [lackac] + * fixed; repair mongoose.SchemaTypes alias + * updated; docs + +3.0.0 / 2012-08-07 +================== + + * removed; old subdocument#commit method + * fixed; setting arrays of matching docs [6924cbc2] + * fixed; doc!remove event now emits in save order as save for consistency + * fixed; pre-save hooks no longer fire on subdocuments when validation fails + * added; subdoc#parent() and subdoc#parentArray() to access subdocument parent objects + * added; query#lean() helper + +3.0.0rc0 / 2012-08-01 +===================== + + * fixed; allow subdoc literal declarations containing "type" pathname (#993) + * fixed; unsetting a default array (#758) + * fixed; boolean $in queries (#998) + * fixed; allow use of `options` as a pathname (#529) + * fixed; `model` is again a permitted schema path name + * fixed; field selection option on subdocs (#1022) + * fixed; handle another edge case with subdoc saving (#975) + * added; emit save err on model if listening + * added; MongoDB TTL collection support (#1006) + * added; $center options support + * added; $nearSphere and $polygon support + * updated; driver version to 1.1.2 + +3.0.0alpha2 / 2012-07-18 +========================= + + * changed; index errors are now emitted on their model and passed to an optional callback (#984) + * fixed; specifying index along with sparse/unique option no longer overwrites (#1004) + * fixed; never swallow connection errors (#618) + * fixed; creating object from model with emded object no longer overwrites defaults [achurkin] (#859) + * fixed; stop needless validation of unchanged/unselected fields (#891) + * fixed; document#equals behavior of objectids (#974) + * fixed; honor the minimize schema option (#978) + * fixed; provide helpful error msgs when reserved schema path is used (#928) + * fixed; callback to conn#disconnect is optional (#875) + * fixed; handle missing protocols in connection urls (#987) + * fixed; validate args to query#where (#969) + * fixed; saving modified/removed subdocs (#975) + * fixed; update with $pull from Mixed array (#735) + * fixed; error with null shard key value + * fixed; allow unsetting enums (#967) + * added; support for manual index creation (#984) + * added; support for disabled auto-indexing (#984) + * added; support for preserving MongooseArray#sort changes (#752) + * added; emit state change events on connection + * added; support for specifying BSON subtype in MongooseBuffer#toObject [jcrugzz] + * added; support for disabled versioning (#977) + * added; implicit "new" support for models and Schemas + +3.0.0alpha1 / 2012-06-15 +========================= + + * removed; doc#commit (use doc#markModified) + * removed; doc.modified getter (#950) + * removed; mongoose{connectSet,createSetConnection}. use connect,createConnection instead + * removed; query alias methods 1149804c + * removed; MongooseNumber + * changed; now creating indexes in background by default + * changed; strict mode now enabled by default (#952) + * changed; doc#modifiedPaths is now a method (#950) + * changed; getters no longer cast (#820); casting happens during set + * fixed; no need to pass updateArg to findOneAndUpdate (#931) + * fixed: utils.merge bug when merging nested non-objects. [treygriffith] + * fixed; strict:throw should produce errors in findAndModify (#963) + * fixed; findAndUpdate no longer overwrites document (#962) + * fixed; setting default DocumentArrays (#953) + * fixed; selection of _id with schema deselection (#954) + * fixed; ensure promise#error emits instanceof Error + * fixed; CursorStream: No stack overflow on any size result (#929) + * fixed; doc#remove now passes safe options + * fixed; invalid use of $set during $pop + * fixed; array#{$pop,$shift} mirror MongoDB behavior + * fixed; no longer test non-required vals in string match (#934) + * fixed; edge case with doc#inspect + * fixed; setter order (#665) + * fixed; setting invalid paths in strict mode (#916) + * fixed; handle docs without id in DocumentArray#id method (#897) + * fixed; do not save virtuals during model.update (#894) + * fixed; sub doc toObject virtuals application (#889) + * fixed; MongooseArray#pull of ObjectId (#881) + * fixed; handle passing db name with any repl set string + * fixed; default application of selected fields (#870) + * fixed; subdoc paths reported in validation errors (#725) + * fixed; incorrect reported num of affected docs in update ops (#862) + * fixed; connection assignment in Model#model (#853) + * fixed; stringifying arrays of docs (#852) + * fixed; modifying subdoc and parent array works (#842) + * fixed; passing undefined to next hook (#785) + * fixed; Query#{update,remove}() works without callbacks (#788) + * fixed; set/updating nested objects by parent pathname (#843) + * fixed; allow null in number arrays (#840) + * fixed; isNew on sub doc after insertion error (#837) + * fixed; if an insert fails, set isNew back to false [boutell] + * fixed; isSelected when only _id is selected (#730) + * fixed; setting an unset default value (#742) + * fixed; query#sort error messaging (#671) + * fixed; support for passing $options with $regex + * added; array of object literal notation in schema creates DocumentArrays + * added; gt,gte,lt,lte query support for arrays (#902) + * added; capped collection support (#938) + * added; document versioning support + * added; inclusion of deselected schema path (#786) + * added; non-atomic array#pop + * added; EmbeddedDocument constructor is now exposed in DocArray#create 7cf8beec + * added; mapReduce support (#678) + * added; support for a configurable minimize option #to{Object,JSON}(option) (#848) + * added; support for strict: `throws` [regality] + * added; support for named schema types (#795) + * added; to{Object,JSON} schema options (#805) + * added; findByIdAnd{Update,Remove}() + * added; findOneAnd{Update,Remove}() + * added; query.setOptions() + * added; instance.update() (#794) + * added; support specifying model in populate() [DanielBaulig] + * added; `lean` query option [gitfy] + * added; multi-atomic support to MongooseArray#nonAtomicPush + * added; support for $set + other $atomic ops on single array + * added; tests + * updated; driver to 1.0.2 + * updated; query.sort() syntax to mirror query.select() + * updated; clearer cast error msg for array numbers + * updated; docs + * updated; doc.clone 3x faster (#950) + * updated; only create _id if necessary (#950) + +2.7.3 / 2012-08-01 +================== + + * fixed; boolean $in queries (#998) + * fixed field selection option on subdocs (#1022) + +2.7.2 / 2012-07-18 +================== + + * fixed; callback to conn#disconnect is optional (#875) + * fixed; handle missing protocols in connection urls (#987) + * fixed; saving modified/removed subdocs (#975) + * updated; tests + +2.7.1 / 2012-06-26 +=================== + + * fixed; sharding: when a document holds a null as a value of the shard key + * fixed; update() using $pull on an array of Mixed (gh-735) + * deprecated; MongooseNumber#{inc, increment, decrement} methods + * tests; now using mocha + +2.7.0 / 2012-06-14 +=================== + + * added; deprecation warnings to methods being removed in 3.x + +2.6.8 / 2012-06-14 +=================== + + * fixed; edge case when using 'options' as a path name (#961) + +2.6.7 / 2012-06-08 +=================== + + * fixed; ensure promise#error always emits instanceof Error + * fixed; selection of _id w/ another excluded path (#954) + * fixed; setting default DocumentArrays (#953) + +2.6.6 / 2012-06-06 +=================== + + * fixed; stack overflow in query stream with large result sets (#929) + * added; $gt, $gte, $lt, $lte support to arrays (#902) + * fixed; pass option `safe` along to doc#remove() calls + +2.6.5 / 2012-05-24 +=================== + + * fixed; do not save virtuals in Model.update (#894) + * added; missing $ prefixed query aliases (going away in 3.x) (#884) [timoxley] + * fixed; setting invalid paths in strict mode (#916) + * fixed; resetting isNew after insert failure (#837) [boutell] + +2.6.4 / 2012-05-15 +=================== + + * updated; backport string regex $options to 2.x + * updated; use driver 1.0.2 (performance improvements) (#914) + * fixed; calling MongooseDocumentArray#id when the doc has no _id (#897) + +2.6.3 / 2012-05-03 +=================== + + * fixed; repl-set connectivity issues during failover on MongoDB 2.0.1 + * updated; driver to 1.0.0 + * fixed; virtuals application of subdocs when using toObject({ virtuals: true }) (#889) + * fixed; MongooseArray#pull of ObjectId correctly updates the array itself (#881) + +2.6.2 / 2012-04-30 +=================== + + * fixed; default field application of selected fields (#870) + +2.6.1 / 2012-04-30 +=================== + + * fixed; connection assignment in mongoose#model (#853, #877) + * fixed; incorrect reported num of affected docs in update ops (#862) + +2.6.0 / 2012-04-19 +=================== + + * updated; hooks.js to 0.2.1 + * fixed; issue with passing undefined to a hook callback. thanks to [chrisleishman] for reporting. + * fixed; updating/setting nested objects in strict schemas (#843) as reported by [kof] + * fixed; Query#{update,remove}() work without callbacks again (#788) + * fixed; modifying subdoc along with parent array $atomic op (#842) + +2.5.14 / 2012-04-13 +=================== + + * fixed; setting an unset default value (#742) + * fixed; doc.isSelected(otherpath) when only _id is selected (#730) + * updated; docs + +2.5.13 / 2012-03-22 +=================== + + * fixed; failing validation of unselected required paths (#730,#713) + * fixed; emitting connection error when only one listener (#759) + * fixed; MongooseArray#splice was not returning values (#784) [chrisleishman] + +2.5.12 / 2012-03-21 +=================== + + * fixed; honor the `safe` option in all ensureIndex calls + * updated; node-mongodb-native driver to 0.9.9-7 + +2.5.11 / 2012-03-15 +=================== + + * added; introspection for getters/setters (#745) + * updated; node-mongodb-driver to 0.9.9-5 + * added; tailable method to Query (#769) [holic] + * fixed; Number min/max validation of null (#764) [btamas] + * added; more flexible user/password connection options (#738) [KarneAsada] + +2.5.10 / 2012-03-06 +=================== + + * updated; node-mongodb-native driver to 0.9.9-4 + * added; Query#comment() + * fixed; allow unsetting arrays + * fixed; hooking the set method of subdocuments (#746) + * fixed; edge case in hooks + * fixed; allow $id and $ref in queries (fixes compatibility with mongoose-dbref) (#749) [richtera] + * added; default path selection to SchemaTypes + +2.5.9 / 2012-02-22 +=================== + + * fixed; properly cast nested atomic update operators for sub-documents + +2.5.8 / 2012-02-21 +=================== + + * added; post 'remove' middleware includes model that was removed (#729) [timoxley] + +2.5.7 / 2012-02-09 +=================== + + * fixed; RegExp validators on node >= v0.6.x + +2.5.6 / 2012-02-09 +=================== + + * fixed; emit errors returned from db.collection() on the connection (were being swallowed) + * added; can add multiple validators in your schema at once (#718) [diogogmt] + * fixed; strict embedded documents (#717) + * updated; docs [niemyjski] + * added; pass number of affected docs back in model.update/save + +2.5.5 / 2012-02-03 +=================== + + * fixed; RangeError: maximum call stack exceed error when removing docs with Number _id (#714) + +2.5.4 / 2012-02-03 +=================== + + * fixed; RangeError: maximum call stack exceed error (#714) + +2.5.3 / 2012-02-02 +=================== + + * added; doc#isSelected(path) + * added; query#equals() + * added; beta sharding support + * added; more descript error msgs (#700) [obeleh] + * added; document.modifiedPaths (#709) [ljharb] + * fixed; only functions can be added as getters/setters (#707,704) [ljharb] + +2.5.2 / 2012-01-30 +=================== + + * fixed; rollback -native driver to 0.9.7-3-5 (was causing timeouts and other replica set weirdness) + * deprecated; MongooseNumber (will be moved to a separate repo for 3.x) + * added; init event is emitted on schemas + +2.5.1 / 2012-01-27 +=================== + + * fixed; honor strict schemas in Model.update (#699) + +2.5.0 / 2012-01-26 +=================== + + * added; doc.toJSON calls toJSON on embedded docs when exists [jerem] + * added; populate support for refs of type Buffer (#686) [jerem] + * added; $all support for ObjectIds and Dates (#690) + * fixed; virtual setter calling on instantiation when strict: true (#682) [hunterloftis] + * fixed; doc construction triggering getters (#685) + * fixed; MongooseBuffer check in deepEquals (#688) + * fixed; range error when using Number _ids with `instance.save()` (#691) + * fixed; isNew on embedded docs edge case (#680) + * updated; driver to 0.9.8-3 + * updated; expose `model()` method within static methods + +2.4.10 / 2012-01-10 +=================== + + * added; optional getter application in .toObject()/.toJSON() (#412) + * fixed; nested $operators in $all queries (#670) + * added; $nor support (#674) + * fixed; bug when adding nested schema (#662) [paulwe] + +2.4.9 / 2012-01-04 +=================== + + * updated; driver to 0.9.7-3-5 to fix Linux performance degradation on some boxes + +2.4.8 / 2011-12-22 +=================== + + * updated; bump -native to 0.9.7.2-5 + * fixed; compatibility with date.js (#646) [chrisleishman] + * changed; undocumented schema "lax" option to "strict" + * fixed; default value population for strict schemas + * updated; the nextTick helper for small performance gain. 1bee2a2 + +2.4.7 / 2011-12-16 +=================== + + * fixed; bug in 2.4.6 with path setting + * updated; bump -native to 0.9.7.2-1 + * added; strict schema option [nw] + +2.4.6 / 2011-12-16 +=================== + + * fixed; conflicting mods on update bug [sirlantis] + * improved; doc.id getter performance + +2.4.5 / 2011-12-14 +=================== + + * fixed; bad MongooseArray behavior in 2.4.2 - 2.4.4 + +2.4.4 / 2011-12-14 +=================== + + * fixed; MongooseArray#doAtomics throwing after sliced + +2.4.3 / 2011-12-14 +=================== + + * updated; system.profile schema for MongoDB 2x + +2.4.2 / 2011-12-12 +=================== + + * fixed; partially populating multiple children of subdocs (#639) [kenpratt] + * fixed; allow Update of numbers to null (#640) [jerem] + +2.4.1 / 2011-12-02 +=================== + + * added; options support for populate() queries + * updated; -native driver to 0.9.7-1.4 + +2.4.0 / 2011-11-29 +=================== + + * added; QueryStreams (#614) + * added; debug print mode for development + * added; $within support to Array queries (#586) [ggoodale] + * added; $centerSphere query support + * fixed; $within support + * added; $unset is now used when setting a path to undefined (#519) + * added; query#batchSize support + * updated; docs + * updated; -native driver to 0.9.7-1.3 (provides Windows support) + +2.3.13 / 2011-11-15 +=================== + + * fixed; required validation for Refs (#612) [ded] + * added; $nearSphere support for Arrays (#610) + +2.3.12 / 2011-11-09 +=================== + + * fixed; regression, objects passed to Model.update should not be changed (#605) + * fixed; regression, empty Model.update should not be executed + +2.3.11 / 2011-11-08 +=================== + + * fixed; using $elemMatch on arrays of Mixed types (#591) + * fixed; allow using $regex when querying Arrays (#599) + * fixed; calling Model.update with no atomic keys (#602) + +2.3.10 / 2011-11-05 +=================== + + * fixed; model.update casting for nested paths works (#542) + +2.3.9 / 2011-11-04 +================== + + * fixed; deepEquals check for MongooseArray returned false + * fixed; reset modified flags of embedded docs after save [gitfy] + * fixed; setting embedded doc with identical values no longer marks modified [gitfy] + * updated; -native driver to 0.9.6.23 [mlazarov] + * fixed; Model.update casting (#542, #545, #479) + * fixed; populated refs no longer fail required validators (#577) + * fixed; populating refs of objects with custom ids works + * fixed; $pop & $unset work with Model.update (#574) + * added; more helpful debugging message for Schema#add (#578) + * fixed; accessing .id when no _id exists now returns null (#590) + +2.3.8 / 2011-10-26 +================== + + * added; callback to query#findOne is now optional (#581) + +2.3.7 / 2011-10-24 +================== + + * fixed; wrapped save/remove callbacks in nextTick to mitigate -native swallowing thrown errors + +2.3.6 / 2011-10-21 +================== + + * fixed; exclusion of embedded doc _id from query results (#541) + +2.3.5 / 2011-10-19 +================== + + * fixed; calling queries without passing a callback works (#569) + * fixed; populate() works with String and Number _ids too (#568) + +2.3.4 / 2011-10-18 +================== + + * added; Model.create now accepts an array as a first arg + * fixed; calling toObject on a DocumentArray with nulls no longer throws + * fixed; calling inspect on a DocumentArray with nulls no longer throws + * added; MongooseArray#unshift support + * fixed; save hooks now fire on embedded documents [gitfy] (#456) + * updated; -native driver to 0.9.6-22 + * fixed; correctly pass $addToSet op instead of $push + * fixed; $addToSet properly detects dates + * fixed; $addToSet with multiple items works + * updated; better node 0.6 Buffer support + +2.3.3 / 2011-10-12 +================== + + * fixed; population conditions in multi-query settings [vedmalex] (#563) + * fixed; now compatible with Node v0.5.x + +2.3.2 / 2011-10-11 +================== + + * fixed; population of null subdoc properties no longer hangs (#561) + +2.3.1 / 2011-10-10 +================== + + * added; support for Query filters to populate() [eneko] + * fixed; querying with number no longer crashes mongodb (#555) [jlbyrey] + * updated; version of -native driver to 0.9.6-21 + * fixed; prevent query callbacks that throw errors from corrupting -native connection state + +2.3.0 / 2011-10-04 +================== + + * fixed; nulls as default values for Boolean now works as expected + * updated; version of -native driver to 0.9.6-20 + +2.2.4 / 2011-10-03 +================== + + * fixed; populate() works when returned array contains undefined/nulls + +2.2.3 / 2011-09-29 +================== + + * updated; version of -native driver to 0.9.6-19 + +2.2.2 / 2011-09-28 +================== + + * added; $regex support to String [davidandrewcope] + * added; support for other contexts like repl etc (#535) + * fixed; clear modified state properly after saving + * added; $addToSet support to Array + +2.2.1 / 2011-09-22 +================== + + * more descript error when casting undefined to string + * updated; version of -native driver to 0.9.6-18 + +2.2.0 / 2011-09-22 +================== + + * fixed; maxListeners warning on schemas with many arrays (#530) + * changed; return / apply defaults based on fields selected in query (#423) + * fixed; correctly detect Mixed types within schema arrays (#532) + +2.1.4 / 2011-09-20 +================== + + * fixed; new private methods that stomped on users code + * changed; finished removing old "compat" support which did nothing + +2.1.3 / 2011-09-16 +================== + + * updated; version of -native driver to 0.9.6-15 + * added; emit `error` on connection when open fails [edwardhotchkiss] + * added; index support to Buffers (thanks justmoon for helping track this down) + * fixed; passing collection name via schema in conn.model() now works (thanks vedmalex for reporting) + +2.1.2 / 2011-09-07 +================== + + * fixed; Query#find with no args no longer throws + +2.1.1 / 2011-09-07 +================== + + * added; support Model.count(fn) + * fixed; compatibility with node >=0.4.0 < 0.4.3 + * added; pass model.options.safe through with .save() so w:2, wtimeout:5000 options work [andrewjstone] + * added; support for $type queries + * added; support for Query#or + * added; more tests + * optimized populate queries + +2.1.0 / 2011-09-01 +================== + + * changed; document#validate is a public method + * fixed; setting number to same value no longer marks modified (#476) [gitfy] + * fixed; Buffers shouldn't have default vals + * added; allow specifying collection name in schema (#470) [ixti] + * fixed; reset modified paths and atomics after saved (#459) + * fixed; set isNew on embedded docs to false after save + * fixed; use self to ensure proper scope of options in doOpenSet (#483) [andrewjstone] + +2.0.4 / 2011-08-29 +================== + + * Fixed; Only send the depopulated ObjectId instead of the entire doc on save (DBRefs) + * Fixed; Properly cast nested array values in Model.update (the data was stored in Mongo incorrectly but recast on document fetch was "fixing" it) + +2.0.3 / 2011-08-28 +================== + + * Fixed; manipulating a populated array no longer causes infinite loop in BSON serializer during save (#477) + * Fixed; populating an empty array no longer hangs foreeeeeeeever (#481) + +2.0.2 / 2011-08-25 +================== + + * Fixed; Maintain query option key order (fixes 'bad hint' error from compound query hints) + +2.0.1 / 2011-08-25 +================== + + * Fixed; do not over-write the doc when no valide props exist in Model.update (#473) + +2.0.0 / 2011-08-24 +=================== + + * Added; support for Buffers [justmoon] + * Changed; improved error handling [maelstrom] + * Removed: unused utils.erase + * Fixed; support for passing other context object into Schemas (#234) [Sija] + * Fixed; getters are no longer circular refs to themselves (#366) + * Removed; unused compat.js + * Fixed; getter/setter scopes are set properly + * Changed; made several private properties more obvious by prefixing _ + * Added; DBRef support [guille] + * Changed; removed support for multiple collection names per model + * Fixed; no longer applying setters when document returned from db + * Changed; default auto_reconnect to true + * Changed; Query#bind no longer clones the query + * Fixed; Model.update now accepts $pull, $inc and friends (#404) + * Added; virtual type option support [nw] + +1.8.4 / 2011-08-21 +=================== + + * Fixed; validation bug when instantiated with non-schema properties (#464) [jmreidy] + +1.8.3 / 2011-08-19 +=================== + + * Fixed; regression in connection#open [jshaw86] + +1.8.2 / 2011-08-17 +=================== + + * fixed; reset connection.readyState after failure [tomseago] + * fixed; can now query positionally for non-embedded docs (arrays of numbers/strings etc) + * fixed; embedded document query casting + * added; support for passing options to node-mongo-native db, server, and replsetserver [tomseago] + +1.8.1 / 2011-08-10 +=================== + + * fixed; ObjectIds were always marked modified + * fixed; can now query using document instances + * fixed; can now query/update using documents with subdocs + +1.8.0 / 2011-08-04 +=================== + + * fixed; can now use $all with String and Number + * fixed; can query subdoc array with $ne: null + * fixed; instance.subdocs#id now works with custom _ids + * fixed; do not apply setters when doc returned from db (change in bad behavior) + +1.7.4 / 2011-07-25 +=================== + + * fixed; sparse now a valid seperate schema option + * fixed; now catching cast errors in queries + * fixed; calling new Schema with object created in vm.runInNewContext now works (#384) [Sija] + * fixed; String enum was disallowing null + * fixed; Find by nested document _id now works (#389) + +1.7.3 / 2011-07-16 +=================== + + * fixed; MongooseArray#indexOf now works with ObjectIds + * fixed; validation scope now set properly (#418) + * fixed; added missing colors dependency (#398) + +1.7.2 / 2011-07-13 +=================== + + * changed; node-mongodb-native driver to v0.9.6.7 + +1.7.1 / 2011-07-12 +=================== + + * changed; roll back node-mongodb-native driver to v0.9.6.4 + +1.7.0 / 2011-07-12 +=================== + + * fixed; collection name misspelling [mathrawka] + * fixed; 2nd param is required for ReplSetServers [kevinmarvin] + * fixed; MongooseArray behaves properly with Object.keys + * changed; node-mongodb-native driver to v0.9.6.6 + * fixed/changed; Mongodb segfault when passed invalid ObjectId (#407) + - This means invalid data passed to the ObjectId constructor will now error + +1.6.0 / 2011-07-07 +=================== + + * changed; .save() errors are now emitted on the instances db instead of the instance 9782463fc + * fixed; errors occurring when creating indexes now properly emit on db + * added; $maxDistance support to MongooseArrays + * fixed; RegExps now work with $all + * changed; node-mongodb-native driver to v0.9.6.4 + * fixed; model names are now accessible via .modelName + * added; Query#slaveOk support + +1.5.0 / 2011-06-27 +=================== + + * changed; saving without a callback no longer ignores the error (@bnoguchi) + * changed; hook-js version bump to 0.1.9 + * changed; node-mongodb-native version bumped to 0.9.6.1 - When .remove() doesn't + return an error, null is no longer passed. + * fixed; two memory leaks (@justmoon) + * added; sparse index support + * added; more ObjectId conditionals (gt, lt, gte, lte) (@phillyqueso) + * added; options are now passed in model#remote (@JerryLuke) + +1.4.0 / 2011-06-10 +=================== + + * bumped hooks-js dependency (fixes issue passing null as first arg to next()) + * fixed; document#inspect now works properly with nested docs + * fixed; 'set' now works as a schema attribute (GH-365) + * fixed; _id is now set properly within pre-init hooks (GH-289) + * added; Query#distinct / Model#distinct support (GH-155) + * fixed; embedded docs now can use instance methods (GH-249) + * fixed; can now overwrite strings conflicting with schema type + +1.3.7 / 2011-06-03 +=================== + + * added MongooseArray#splice support + * fixed; 'path' is now a valid Schema pathname + * improved hooks (utilizing https://github.com/bnoguchi/hooks-js) + * fixed; MongooseArray#$shift now works (never did) + * fixed; Document.modified no longer throws + * fixed; modifying subdoc property sets modified paths for subdoc and parent doc + * fixed; marking subdoc path as modified properly persists the value to the db + * fixed; RexExps can again be saved ( #357 ) + +1.3.6 / 2011-05-18 +=================== + + * fixed; corrected casting for queries against array types + * added; Document#set now accepts Document instances + +1.3.5 / 2011-05-17 +=================== + + * fixed; $ne queries work properly with single vals + * added; #inspect() methods to improve console.log output + +1.3.4 / 2011-05-17 +=================== + + * fixed; find by Date works as expected (#336) + * added; geospatial 2d index support + * added; support for $near (#309) + * updated; node-mongodb-native driver + * fixed; updating numbers work (#342) + * added; better error msg when try to remove an embedded doc without an _id (#307) + * added; support for 'on-the-fly' schemas (#227) + * changed; virtual id getters can now be skipped + * fixed; .index() called on subdoc schema now works as expected + * fixed; db.setProfile() now buffers until the db is open (#340) + +1.3.3 / 2011-04-27 +=================== + + * fixed; corrected query casting on nested mixed types + +1.3.2 / 2011-04-27 +=================== + + * fixed; query hints now retain key order + +1.3.1 / 2011-04-27 +=================== + + * fixed; setting a property on an embedded array no longer overwrites entire array (GH-310) + * fixed; setting nested properties works when sibling prop is named "type" + * fixed; isModified is now much finer grained when .set() is used (GH-323) + * fixed; mongoose.model() and connection.model() now return the Model (GH-308, GH-305) + * fixed; can now use $gt, $lt, $gte, $lte with String schema types (GH-317) + * fixed; .lowercase() -> .toLowerCase() in pluralize() + * fixed; updating an embedded document by index works (GH-334) + * changed; .save() now passes the instance to the callback (GH-294, GH-264) + * added; can now query system.profile and system.indexes collections + * added; db.model('system.profile') is now included as a default Schema + * added; db.setProfiling(level, ms, callback) + * added; Query#hint() support + * added; more tests + * updated node-mongodb-native to 0.9.3 + +1.3.0 / 2011-04-19 +=================== + + * changed; save() callbacks now fire only once on failed validation + * changed; Errors returned from save() callbacks now instances of ValidationError + * fixed; MongooseArray#indexOf now works properly + +1.2.0 / 2011-04-11 +=================== + + * changed; MongooseNumber now casts empty string to null + +1.1.25 / 2011-04-08 +=================== + + * fixed; post init now fires at proper time + +1.1.24 / 2011-04-03 +=================== + + * fixed; pushing an array onto an Array works on existing docs + +1.1.23 / 2011-04-01 +=================== + + * Added Model#model + +1.1.22 / 2011-03-31 +=================== + + * Fixed; $in queries on mixed types now work + +1.1.21 / 2011-03-31 +=================== + + * Fixed; setting object root to null/undefined works + +1.1.20 / 2011-03-31 +=================== + + * Fixed; setting multiple props on null field works + +1.1.19 / 2011-03-31 +=================== + + * Fixed; no longer using $set on paths to an unexisting fields + +1.1.18 / 2011-03-30 +=================== + + * Fixed; non-mixed type object setters work after initd from null + +1.1.17 / 2011-03-30 +=================== + + * Fixed; nested object property access works when root initd with null value + +1.1.16 / 2011-03-28 +=================== + + * Fixed; empty arrays are now saved + +1.1.15 / 2011-03-28 +=================== + + * Fixed; `null` and `undefined` are set atomically. + +1.1.14 / 2011-03-28 +=================== + + * Changed; more forgiving date casting, accepting '' as null. + +1.1.13 / 2011-03-26 +=================== + + * Fixed setting values as `undefined`. + +1.1.12 / 2011-03-26 +=================== + + * Fixed; nested objects now convert to JSON properly + * Fixed; setting nested objects directly now works + * Update node-mongodb-native + +1.1.11 / 2011-03-25 +=================== + + * Fixed for use of `type` as a key. + +1.1.10 / 2011-03-23 +=================== + + * Changed; Make sure to only ensure indexes while connected + +1.1.9 / 2011-03-2 +================== + + * Fixed; Mixed can now default to empty arrays + * Fixed; keys by the name 'type' are now valid + * Fixed; null values retrieved from the database are hydrated as null values. + * Fixed repeated atomic operations when saving a same document twice. + +1.1.8 / 2011-03-23 +================== + + * Fixed 'id' overriding. [bnoguchi] + +1.1.7 / 2011-03-22 +================== + + * Fixed RegExp query casting when querying against an Array of Strings [bnoguchi] + * Fixed getters/setters for nested virtualsl. [bnoguchi] + +1.1.6 / 2011-03-22 +================== + + * Only doValidate when path exists in Schema [aheckmann] + * Allow function defaults for Array types [aheckmann] + * Fix validation hang [aheckmann] + * Fix setting of isRequired of SchemaType [aheckmann] + * Fix SchemaType#required(false) filter [aheckmann] + * More backwards compatibility [aheckmann] + * More tests [aheckmann] + +1.1.5 / 2011-03-14 +================== + + * Added support for `uri, db, fn` and `uri, fn` signatures for replica sets. + * Improved/extended replica set tests. + +1.1.4 / 2011-03-09 +================== + + * Fixed; running an empty Query doesn't throw. [aheckmann] + * Changed; Promise#addBack returns promise. [aheckmann] + * Added streaming cursor support. [aheckmann] + * Changed; Query#update defaults to use$SetOnSave now. [brian] + * Added more docs. + +1.1.3 / 2011-03-04 +================== + + * Added Promise#resolve [aheckmann] + * Fixed backward compatibility with nulls [aheckmann] + * Changed; Query#{run,exec} return promises [aheckmann] + +1.1.2 / 2011-03-03 +================== + + * Restored Query#exec and added notion of default operation [brian] + * Fixed ValidatorError messages [brian] + +1.1.1 / 2011-03-01 +================== + + * Added SchemaType String `lowercase`, `uppercase`, `trim`. + * Public exports (`Model`, `Document`) and tests. + * Added ObjectId casting support for `Document`s. + +1.1.0 / 2011-02-25 +================== + + * Added support for replica sets. + +1.0.16 / 2011-02-18 +=================== + + * Added $nin as another whitelisted $conditional for SchemaArray [brian] + * Changed #with to #where [brian] + * Added ability to use $in conditional with Array types [brian] + +1.0.15 / 2011-02-18 +=================== + + * Added `id` virtual getter for documents to easily access the hexString of + the `_id`. + +1.0.14 / 2011-02-17 +=================== + + * Fix for arrays within subdocuments [brian] + +1.0.13 / 2011-02-16 +=================== + + * Fixed embedded documents saving. + +1.0.12 / 2011-02-14 +=================== + + * Minor refactorings [brian] + +1.0.11 / 2011-02-14 +=================== + + * Query refactor and $ne, $slice, $or, $size, $elemMatch, $nin, $exists support [brian] + * Named scopes sugar [brian] + +1.0.10 / 2011-02-11 +=================== + + * Updated node-mongodb-native driver [thanks John Allen] + +1.0.9 / 2011-02-09 +================== + + * Fixed single member arrays as defaults [brian] + +1.0.8 / 2011-02-09 +================== + + * Fixed for collection-level buffering of commands [gitfy] + * Fixed `Document#toJSON` [dalejefferson] + * Fixed `Connection` authentication [robrighter] + * Fixed clash of accessors in getters/setters [eirikurn] + * Improved `Model#save` promise handling + +1.0.7 / 2011-02-05 +================== + + * Fixed memory leak warnings for test suite on 0.3 + * Fixed querying documents that have an array that contain at least one + specified member. [brian] + * Fixed default value for Array types (fixes GH-210). [brian] + * Fixed example code. + +1.0.6 / 2011-02-03 +================== + + * Fixed `post` middleware + * Fixed; it's now possible to instantiate a model even when one of the paths maps + to an undefined value [brian] + +1.0.5 / 2011-02-02 +================== + + * Fixed; combo $push and $pushAll auto-converts into a $pushAll [brian] + * Fixed; combo $pull and $pullAll auto-converts to a single $pullAll [brian] + * Fixed; $pullAll now removes said members from array before save (so it acts just + like pushAll) [brian] + * Fixed; multiple $pulls and $pushes become a single $pullAll and $pushAll. + Moreover, $pull now modifies the array before save to reflect the immediate + change [brian] + * Added tests for nested shortcut getters [brian] + * Added tests that show that Schemas with nested Arrays don't apply defaults + [brian] + +1.0.4 / 2011-02-02 +================== + + * Added MongooseNumber#toString + * Added MongooseNumber unit tests + +1.0.3 / 2011-02-02 +================== + + * Make sure safe mode works with Model#save + * Changed Schema options: safe mode is now the default + * Updated node-mongodb-native to HEAD + +1.0.2 / 2011-02-02 +================== + + * Added a Model.create shortcut for creating documents. [brian] + * Fixed; we can now instantiate models with hashes that map to at least one + null value. [brian] + * Fixed Schema with more than 2 nested levels. [brian] + +1.0.1 / 2011-02-02 +================== + + * Improved `MongooseNumber`, works almost like the native except for `typeof` + not being `'number'`. diff --git a/test/node_modules/mongoose/README.md b/test/node_modules/mongoose/README.md new file mode 100644 index 0000000..06db9dc --- /dev/null +++ b/test/node_modules/mongoose/README.md @@ -0,0 +1,346 @@ +## What's Mongoose? + +Mongoose is a [MongoDB](http://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment. + +Defining a model is as easy as: + + var Comment = new Schema({ + title : String + , body : String + , date : Date + }); + + var BlogPost = new Schema({ + author : ObjectId + , title : String + , body : String + , buf : Buffer + , date : Date + , comments : [Comment] + , meta : { + votes : Number + , favs : Number + } + }); + + var Post = mongoose.model('BlogPost', BlogPost); + +## Documentation + +[mongoosejs.com](http://mongoosejs.com/) + +## Try it live +
    +## Installation + +The recommended way is through the excellent [NPM](http://www.npmjs.org/): + + $ npm install mongoose + +Otherwise, you can check it in your repository and then expose it: + + $ git clone git://github.com/LearnBoost/mongoose.git node_modules/mongoose/ + +And install dependency modules written on `package.json`. + +Then you can `require` it: + + require('mongoose') + +## Connecting to MongoDB + +First, we need to define a connection. If your app uses only one database, you should use `mongose.connect`. If you need to create additional connections, use `mongoose.createConnection`. + +Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters `host, database, port, options`. + + var mongoose = require('mongoose'); + + mongoose.connect('mongodb://localhost/my_database'); + +Once connected, the `open` event is fired on the `Connection` instance. If you're using `mongoose.connect`, the `Connection` is `mongoose.connection`. Otherwise, `mongoose.createConnection` return value is a `Connection`. + +**Important!** Mongoose buffers all the commands until it's connected to the database. This means that you don't have to wait until it connects to MongoDB in order to define models, run queries, etc. + +## Defining a Model + +Models are defined through the `Schema` interface. + + var Schema = mongoose.Schema + , ObjectId = Schema.ObjectId; + + var BlogPost = new Schema({ + author : ObjectId + , title : String + , body : String + , date : Date + }); + +Aside from defining the structure of your documents and the types of data you're storing, a Schema handles the definition of: + +* [Validators](http://mongoosejs.com/docs/validation.html) (async and sync) +* [Defaults](http://mongoosejs.com/docs/api.html#schematype_SchemaType-default) +* [Getters](http://mongoosejs.com/docs/api.html#schematype_SchemaType-get) +* [Setters](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set) +* [Indexes](http://mongoosejs.com/docs/guide.html#indexes) +* [Middleware](http://mongoosejs.com/docs/middleware.html) +* [Methods](http://mongoosejs.com/docs/guide.html#methods) definition +* [Statics](http://mongoosejs.com/docs/guide.html#statics) definition +* [Plugins](http://mongoosejs.com/docs/plugins.html) +* [psuedo-JOINs](http://mongoosejs.com/docs/populate.html) + +The following example shows some of these features: + + var Comment = new Schema({ + name : { type: String, default: 'hahaha' } + , age : { type: Number, min: 18, index: true } + , bio : { type: String, match: /[a-z]/ } + , date : { type: Date, default: Date.now } + , buff : Buffer + }); + + // a setter + Comment.path('name').set(function (v) { + return capitalize(v); + }); + + // middleware + Comment.pre('save', function (next) { + notify(this.get('email')); + next(); + }); + +Take a look at the example in `examples/schema.js` for an end-to-end example of a typical setup. + +## Accessing a Model + +Once we define a model through `mongoose.model('ModelName', mySchema)`, we can access it through the same function + + var myModel = mongoose.model('ModelName'); + +Or just do it all at once + + var MyModel = mongoose.model('ModelName', mySchema); + +We can then instantiate it, and save it: + + var instance = new MyModel(); + instance.my.key = 'hello'; + instance.save(function (err) { + // + }); + +Or we can find documents from the same collection + + MyModel.find({}, function (err, docs) { + // docs.forEach + }); + +You can also `findOne`, `findById`, `update`, etc. For more details check out [this link](http://mongoosejs.com/docs/queries.html). + +**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this case access your model through the connection you created: + + var conn = mongoose.createConnection('your connection string'); + var MyModel = conn.model('ModelName', schema); + var m = new MyModel; + m.save() // works + + vs + + var conn = mongoose.createConnection('your connection string'); + var MyModel = mongoose.model('ModelName', schema); + var m = new MyModel; + m.save() // does not work b/c the default connection object was never connected + +## Embedded Documents + +In the first example snippet, we defined a key in the Schema that looks like: + + comments: [Comments] + +Where `Comments` is a `Schema` we created. This means that creating embedded documents is as simple as: + + // retrieve my model + var BlogPost = mongoose.model('BlogPost'); + + // create a blog post + var post = new BlogPost(); + + // create a comment + post.comments.push({ title: 'My comment' }); + + post.save(function (err) { + if (!err) console.log('Success!'); + }); + +The same goes for removing them: + + BlogPost.findById(myId, function (err, post) { + if (!err) { + post.comments[0].remove(); + post.save(function (err) { + // do something + }); + } + }); + +Embedded documents enjoy all the same features as your models. Defaults, validators, middleware. Whenever an error occurs, it's bubbled to the `save()` error callback, so error handling is a snap! + +Mongoose interacts with your embedded documents in arrays _atomically_, out of the box. + +## Middleware + +Middleware is one of the most exciting features about Mongoose. Middleware takes away all the pain of nested callbacks. + +Middleware are defined at the Schema level and are applied for the methods `init` (when a document is initialized with data from MongoDB), `save` (when a document or embedded document is saved). + +There's two types of middleware: + +- Serial + Serial middleware are defined like: + + .pre(method, function (next, methodArg1, methodArg2, ...) { + // ... + }) + + They're executed one after the other, when each middleware calls `next`. + + You can also intercept the `method`'s incoming arguments via your middleware -- notice `methodArg1`, `methodArg2`, etc in the `pre` definition above. See section "Intercepting and mutating method arguments" below. + + +- Parallel + Parallel middleware offer more fine-grained flow control, and are defined like: + + .pre(method, true, function (next, done, methodArg1, methodArg2) { + // ... + }) + + Parallel middleware can `next()` immediately, but the final argument will be called when all the parallel middleware have called `done()`. + +### Error handling + +If any middleware calls `next` or `done` with an `Error` instance, the flow is interrupted, and the error is passed to the function passed as an argument. + +For example: + + schema.pre('save', function (next) { + // something goes wrong + next(new Error('something went wrong')); + }); + + // later... + + myModel.save(function (err) { + // err can come from a middleware + }); + +### Intercepting and mutating method arguments + +You can intercept method arguments via middleware. + +For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to a new value: + + schema.pre('set', function (next, path, val, typel) { + // `this` is the current Document + this.emit('set', path, val); + + // Pass control to the next pre + next(); + }); + +Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments. To do so, just pass the new values to `next`: + + .pre(method, function firstPre (next, methodArg1, methodArg2) { + // Mutate methodArg1 + next("altered-" + methodArg1.toString(), methodArg2); + }) + + // pre declaration is chainable + .pre(method, function secondPre (next, methodArg1, methodArg2) { + console.log(methodArg1); + // => 'altered-originalValOfMethodArg1' + + console.log(methodArg2); + // => 'originalValOfMethodArg2' + + // Passing no arguments to `next` automatically passes along the current argument values + // i.e., the following `next()` is equivalent to `next(methodArg1, methodArg2)` + // and also equivalent to, with the example method arg + // values, `next('altered-originalValOfMethodArg1', 'originalValOfMethodArg2')` + next(); + }) + +### Schema gotcha + +`type`, when used in a schema has special meaning within Mongoose. If your schema requires using `type` as a nested property you must use object notation: + + new Schema({ + broken: { type: Boolean } + , asset : { + name: String + , type: String // uh oh, it broke. asset will be interpreted as String + } + }); + + new Schema({ + works: { type: Boolean } + , asset : { + name: String + , type: { type: String } // works. asset is an object with a type property + } + }); + +## API docs + +You can find the [Dox](http://github.com/visionmedia/dox) generated API docs [here](http://mongoosejs.com/docs/api.html). + +## Getting support + +- Google Groups [mailing list](http://groups.google.com/group/mongoose-orm) +- (irc) #mongoosejs on freenode +- reporting [issues](https://github.com/learnboost/mongoose/issues/) +- [10gen](http://www.mongodb.org/display/DOCS/Technical+Support) + +## Driver access + +The driver being used defaults to [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) and is directly accessible through `YourModel.collection`. **Note**: using the driver directly bypasses all Mongoose power-tools like validation, getters, setters, hooks, etc. + +## Mongoose Plugins + +Take a peek at the [plugins search site](http://plugins.mongoosejs.com/) to see related modules from the community. + +## Contributing to Mongoose + +### Cloning the repository + + git clone git://github.com/LearnBoost/mongoose.git + +### Guidelines + +See [contributing](http://mongoosejs.com/docs/contributing.html). + +## Credits + +[contributors](https://github.com/learnboost/mongoose/graphs/contributors) + +## License + +Copyright (c) 2010-2012 LearnBoost <dev@learnboost.com> + +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/test/node_modules/mongoose/examples/schema.js b/test/node_modules/mongoose/examples/schema.js new file mode 100644 index 0000000..d108d05 --- /dev/null +++ b/test/node_modules/mongoose/examples/schema.js @@ -0,0 +1,102 @@ + +/** + * Module dependencies. + */ + +var mongoose = require('mongoose') + , Schema = mongoose.Schema; + +/** + * Schema definition + */ + +// recursive embedded-document schema + +var Comment = new Schema(); + +Comment.add({ + title : { type: String, index: true } + , date : Date + , body : String + , comments : [Comment] +}); + +var BlogPost = new Schema({ + title : { type: String, index: true } + , slug : { type: String, lowercase: true, trim: true } + , date : Date + , buf : Buffer + , comments : [Comment] + , creator : Schema.ObjectId +}); + +var Person = new Schema({ + name: { + first: String + , last : String + } + , email: { type: String, required: true, index: { unique: true, sparse: true } } + , alive: Boolean +}); + +/** + * Accessing a specific schema type by key + */ + +BlogPost.path('date') +.default(function(){ + return new Date() + }) +.set(function(v){ + return v == 'now' ? new Date() : v; + }); + +/** + * Pre hook. + */ + +BlogPost.pre('save', function(next, done){ + emailAuthor(done); // some async function + next(); +}); + +/** + * Methods + */ + +BlogPost.methods.findCreator = function (callback) { + return this.db.model('Person').findById(this.creator, callback); +} + +BlogPost.statics.findByTitle = function (title, callback) { + return this.find({ title: title }, callback); +} + +BlogPost.methods.expressiveQuery = function (creator, date, callback) { + return this.find('creator', creator).where('date').gte(date).run(callback); +} + +/** + * Plugins + */ + +function slugGenerator (options){ + options = options || {}; + var key = options.key || 'title'; + + return function slugGenerator(schema){ + schema.path(key).set(function(v){ + this.slug = v.toLowerCase().replace(/[^a-z0-9]/g, '').replace(/-+/g, ''); + return v; + }); + }; +}; + +BlogPost.plugin(slugGenerator()); + +/** + * Define model. + */ + +mongoose.model('BlogPost', BlogPost); +mongoose.model('Person', Person); diff --git a/test/node_modules/mongoose/index.js b/test/node_modules/mongoose/index.js new file mode 100644 index 0000000..e7e6278 --- /dev/null +++ b/test/node_modules/mongoose/index.js @@ -0,0 +1,7 @@ + +/** + * Export lib/mongoose + * + */ + +module.exports = require('./lib/'); diff --git a/test/node_modules/mongoose/lib/collection.js b/test/node_modules/mongoose/lib/collection.js new file mode 100644 index 0000000..1c2ab7d --- /dev/null +++ b/test/node_modules/mongoose/lib/collection.js @@ -0,0 +1,177 @@ + +/*! + * Module dependencies. + */ + +var STATES = require('./connectionstate') + +/** + * Abstract Collection constructor + * + * This is the base class that drivers inherit from and implement. + * + * @param {String} name name of the collection + * @param {Connection} conn A MongooseConnection instance + * @param {Object} opts optional collection options + * @api public + */ + +function Collection (name, conn, opts) { + this.name = name; + this.conn = conn; + this.buffer = true; + this.queue = []; + + if ('number' == typeof opts) opts = { size: opts }; + this.opts = opts || {}; + + if (STATES.connected == this.conn.readyState) { + this.onOpen(); + } +}; + +/** + * The collection name + * + * @api public + * @property name + */ + +Collection.prototype.name; + +/** + * The Connection instance + * + * @api public + * @property conn + */ + +Collection.prototype.conn; + +/** + * Called when the database connects + * + * @api private + */ + +Collection.prototype.onOpen = function () { + var self = this; + this.buffer = false; + self.doQueue(); +}; + +/** + * Called when the database disconnects + * + * @api private + */ + +Collection.prototype.onClose = function () { + this.buffer = true; +}; + +/** + * Queues a method for later execution when its + * database connection opens. + * + * @param {String} name name of the method to queue + * @param {Array} args arguments to pass to the method when executed + * @api private + */ + +Collection.prototype.addQueue = function (name, args) { + this.queue.push([name, args]); + return this; +}; + +/** + * Executes all queued methods and clears the queue. + * + * @api private + */ + +Collection.prototype.doQueue = function () { + for (var i = 0, l = this.queue.length; i < l; i++){ + this[this.queue[i][0]].apply(this, this.queue[i][1]); + } + this.queue = []; + return this; +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.ensureIndex = function(){ + throw new Error('Collection#ensureIndex unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.findAndModify = function(){ + throw new Error('Collection#findAndModify unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.findOne = function(){ + throw new Error('Collection#findOne unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.find = function(){ + throw new Error('Collection#find unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.insert = function(){ + throw new Error('Collection#insert unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.save = function(){ + throw new Error('Collection#save unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.update = function(){ + throw new Error('Collection#update unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.getIndexes = function(){ + throw new Error('Collection#getIndexes unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.mapReduce = function(){ + throw new Error('Collection#mapReduce unimplemented by driver'); +}; + +/*! + * Module exports. + */ + +module.exports = Collection; diff --git a/test/node_modules/mongoose/lib/connection.js b/test/node_modules/mongoose/lib/connection.js new file mode 100644 index 0000000..dc48644 --- /dev/null +++ b/test/node_modules/mongoose/lib/connection.js @@ -0,0 +1,680 @@ +/*! + * Module dependencies. + */ + +var url = require('url') + , utils = require('./utils') + , EventEmitter = utils.EventEmitter + , driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native' + , Model = require('./model') + , Schema = require('./schema') + , Collection = require(driver + '/collection') + , STATES = require('./connectionstate') + , MongooseError = require('./error') + , assert =require('assert') + , muri = require('muri') + +/*! + * Protocol prefix regexp. + * + * @api private + */ + +var rgxProtocol = /^(?:.)+:\/\//; + +/** + * Connection constructor + * + * For practical reasons, a Connection equals a Db. + * + * @param {Mongoose} base a mongoose instance + * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter + * @event `connecting`: Emitted when `connection.{open,openSet}()` is executed on this connection. + * @event `connected`: Emitted when this connection successfully connects to the db. May be emitted _multiple_ times in `reconnected` scenarios. + * @event `open`: Emitted after we `connected` and `onOpen` is executed on all of this connections models. + * @event `disconnecting`: Emitted when `connection.close()` was executed. + * @event `disconnected`: Emitted after getting disconnected from the db. + * @event `close`: Emitted after we `disconnected` and `onClose` executed on all of this connections models. + * @event `reconnected`: Emitted after we `connected` and subsequently `disconnected`, followed by successfully another successfull connection. + * @event `error`: Emitted when an error occurs on this connection. + * @event `fullsetup`: Emitted in a replica-set scenario, when all nodes specified in the connection string are connected. + * @api public + */ + +function Connection (base) { + this.base = base; + this.collections = {}; + this.models = {}; + this.replica = false; + this.hosts = null; + this.host = null; + this.port = null; + this.user = null; + this.pass = null; + this.name = null; + this.options = null; + this._readyState = STATES.disconnected; + this._closeCalled = false; + this._hasOpened = false; +}; + +/*! + * Inherit from EventEmitter + */ + +Connection.prototype.__proto__ = EventEmitter.prototype; + +/** + * Connection ready state + * + * - 0 = disconnected + * - 1 = connected + * - 2 = connecting + * - 3 = disconnecting + * + * Each state change emits its associated event name. + * + * ####Example + * + * conn.on('connected', callback); + * conn.on('disconnected', callback); + * + * @property readyState + * @api public + */ + +Object.defineProperty(Connection.prototype, 'readyState', { + get: function(){ return this._readyState; } + , set: function (val) { + if (!(val in STATES)) { + throw new Error('Invalid connection state: ' + val); + } + + if (this._readyState !== val) { + this._readyState = val; + + if (STATES.connected === val) + this._hasOpened = true; + + this.emit(STATES[val]); + } + } +}); + +/** + * A hash of the collections associated with this connection + * + * @property collections + */ + +Connection.prototype.collections; + +/** + * The mongodb.Db instance, set when the connection is opened + * + * @property db + */ + +Connection.prototype.db; + +/** + * Opens the connection to MongoDB. + * + * `options` is a hash with the following possible properties: + * + * db - passed to the connection db instance + * server - passed to the connection server instance(s) + * replset - passed to the connection ReplSet instance + * user - username for authentication + * pass - password for authentication + * + * ####Notes: + * + * Mongoose forces the db option `forceServerObjectId` false and cannot be overridden. + * Mongoose defaults the server `auto_reconnect` options to true which can be overridden. + * See the node-mongodb-native driver instance for options that it understands. + * + * _Options passed take precedence over options included in connection strings._ + * + * @param {String} connection_string mongodb://uri or the host to which you are connecting + * @param {String} [database] database name + * @param {Number} [port] database port + * @param {Object} [options] options + * @param {Function} [callback] + * @see node-mongodb-native https://github.com/mongodb/node-mongodb-native + * @api public + */ + +Connection.prototype.open = function (host, database, port, options, callback) { + var self = this + , parsed + , uri; + + if ('string' === typeof database) { + switch (arguments.length) { + case 2: + port = 27017; + case 3: + switch (typeof port) { + case 'function': + callback = port, port = 27017; + break; + case 'object': + options = port, port = 27017; + break; + } + break; + case 4: + if ('function' === typeof options) + callback = options, options = {}; + } + } else { + switch (typeof database) { + case 'function': + callback = database, database = undefined; + break; + case 'object': + options = database; + database = undefined; + callback = port; + break; + } + + if (!rgxProtocol.test(host)) { + host = 'mongodb://' + host; + } + + try { + parsed = muri(host); + } catch (err) { + this.error(err, callback); + return this; + } + + database = parsed.db; + host = parsed.hosts[0].host || parsed.hosts[0].ipc; + port = parsed.hosts[0].port || 27017; + } + + this.options = this.parseOptions(options, parsed && parsed.options); + + // make sure we can open + if (STATES.disconnected !== this.readyState) { + var err = new Error('Trying to open unclosed connection.'); + err.state = this.readyState; + this.error(err, callback); + return this; + } + + if (!host) { + this.error(new Error('Missing hostname.'), callback); + return this; + } + + if (!database) { + this.error(new Error('Missing database name.'), callback); + return this; + } + + // authentication + if (options && options.user && options.pass) { + this.user = options.user; + this.pass = options.pass; + + } else if (parsed && parsed.auth) { + this.user = parsed.auth.user; + this.pass = parsed.auth.pass; + + // Check hostname for user/pass + } else if (/@/.test(host) && /:/.test(host.split('@')[0])) { + host = host.split('@'); + var auth = host.shift().split(':'); + host = host.pop(); + this.user = auth[0]; + this.pass = auth[1]; + + } else { + this.user = this.pass = undefined; + } + + this.name = database; + this.host = host; + this.port = port; + + this._open(callback); + return this; +}; + +/** + * Connects to a replica set. + * + * ####Example: + * + * var db = mongoose.createConnection(); + * db.openSet("mongodb://user:pwd@localhost:27020/testing,mongodb://example.com:27020,mongodb://localhost:27019"); + * + * The database name and/or auth need only be included in one URI. + * The `options` is a hash which is passed to the internal driver connection object. + * + * Valid `options` + * + * db - passed to the connection db instance + * server - passed to the connection server instance(s) + * replset - passed to the connection ReplSetServer instance + * user - username for authentication + * pass - password for authentication + * + * _Options passed take precedence over options included in connection strings._ + * + * @param {String} uris comma-separated mongodb:// `URI`s + * @param {String} [database] database name if not included in `uris` + * @param {Object} [options] passed to the internal driver + * @param {Function} [callback] + * @see node-mongodb-native https://github.com/mongodb/node-mongodb-native + * @api public + */ + +Connection.prototype.openSet = function (uris, database, options, callback) { + if (!rgxProtocol.test(uris)) { + uris = 'mongodb://' + uris; + } + + var self = this; + + switch (arguments.length) { + case 3: + switch (typeof database) { + case 'string': + this.name = database; + break; + case 'object': + callback = options; + options = database; + database = null; + break; + } + + if ('function' === typeof options) { + callback = options; + options = {}; + } + break; + case 2: + switch (typeof database) { + case 'string': + this.name = database; + break; + case 'function': + callback = database, database = null; + break; + case 'object': + options = database, database = null; + break; + } + } + + var parsed; + try { + parsed = muri(uris); + } catch (err) { + this.error(err, callback); + return this; + } + + if (!this.name) { + this.name = parsed.db; + } + + this.hosts = parsed.hosts; + this.options = this.parseOptions(options, parsed && parsed.options); + this.replica = true; + + if (!this.name) { + this.error(new Error('No database name provided for replica set'), callback); + return this; + } + + // authentication + if (options && options.user && options.pass) { + this.user = options.user; + this.pass = options.pass; + + } else if (parsed && parsed.auth) { + this.user = parsed.auth.user; + this.pass = parsed.auth.pass; + + } else { + this.user = this.pass = undefined; + } + + this._open(callback); + return this; +}; + +/** + * error + * + * Graceful error handling, passes error to callback + * if available, else emits error on the connection. + * + * @param {Error} err + * @param {Function} callback optional + * @api private + */ + +Connection.prototype.error = function (err, callback) { + if (callback) return callback(err); + this.emit('error', err); +} + +/** + * Handles opening the connection with the appropriate method based on connection type. + * + * @param {Function} callback + * @api private + */ + +Connection.prototype._open = function (callback) { + this.readyState = STATES.connecting; + this._closeCalled = false; + + var self = this; + + var method = this.replica + ? 'doOpenSet' + : 'doOpen'; + + // open connection + this[method](function (err) { + if (err) { + self.readyState = STATES.disconnected; + if (self._hasOpened) { + if (callback) callback(err); + } else { + self.error(err, callback); + } + return; + } + + self.onOpen(callback); + }); +} + +/** + * Called when the connection is opened + * + * @api private + */ + +Connection.prototype.onOpen = function (callback) { + var self = this; + + function open (err) { + if (err) { + self.readyState = STATES.disconnected; + if (self._hasOpened) { + if (callback) callback(err); + } else { + self.error(err, callback); + } + return; + } + + self.readyState = STATES.connected; + + // avoid having the collection subscribe to our event emitter + // to prevent 0.3 warning + for (var i in self.collections) + self.collections[i].onOpen(); + + callback && callback(); + self.emit('open'); + }; + + // re-authenticate + if (self.user && self.pass) { + self.db.authenticate(self.user, self.pass, open); + } + else + open(); +}; + +/** + * Closes the connection + * + * @param {Function} [callback] optional + * @return {Connection} self + * @api public + */ + +Connection.prototype.close = function (callback) { + var self = this; + this._closeCalled = true; + + switch (this.readyState){ + case 0: // disconnected + callback && callback(); + break; + + case 1: // connected + this.readyState = STATES.disconnecting; + this.doClose(function(err){ + if (err){ + self.error(err, callback); + } else { + self.onClose(); + callback && callback(); + } + }); + break; + + case 2: // connecting + this.once('open', function(){ + self.close(callback); + }); + break; + + case 3: // disconnecting + if (!callback) break; + this.once('close', function () { + callback(); + }); + break; + } + + return this; +}; + +/** + * Called when the connection closes + * + * @api private + */ + +Connection.prototype.onClose = function () { + this.readyState = STATES.disconnected; + + // avoid having the collection subscribe to our event emitter + // to prevent 0.3 warning + for (var i in this.collections) + this.collections[i].onClose(); + + this.emit('close'); +}; + +/** + * Retrieves a collection, creating it if not cached. + * + * @param {String} name of the collection + * @param {Object} [options] optional collection options + * @return {Collection} collection instance + * @api public + */ + +Connection.prototype.collection = function (name, options) { + if (!(name in this.collections)) + this.collections[name] = new Collection(name, this, options); + return this.collections[name]; +}; + +/** + * Defines or retrieves a model. + * + * var mongoose = require('mongoose'); + * var db = mongoose.createConnection(..); + * db.model('Venue', new Schema(..)); + * var Ticket = db.model('Ticket', new Schema(..)); + * var Venue = db.model('Venue'); + * + * _When no `collection` argument is passed, Mongoose produces a collection name by passing the model `name` to the [utils.toCollectionName](#utils.toCollectionName) method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option._ + * + * ####Example: + * + * var schema = new Schema({ name: String }, { collection: 'actor' }); + * + * // or + * + * schema.set('collection', 'actor'); + * + * // or + * + * var collectionName = 'actor' + * var M = conn.model('Actor', schema, collectionName) + * + * @param {String} name the model name + * @param {Schema} [schema] a schema. necessary when defining a model + * @param {String} [collection] name of mongodb collection (optional) if not given it will be induced from model name + * @see Mongoose#model #index_Mongoose-model + * @return {Model} The compiled model + * @api public + */ + +Connection.prototype.model = function (name, schema, collection) { + // collection name discovery + if ('string' == typeof schema) { + collection = schema; + schema = false; + } + + if (this.models[name] && !collection) { + // model exists but we are not subclassing with custom collection + if (schema instanceof Schema && schema != this.models[name].schema) { + throw new MongooseError.OverwriteModelError(name); + } + return this.models[name]; + } + + var opts = { cache: false, connection: this } + var model; + + if (schema instanceof Schema) { + // compile a model + model = this.base.model(name, schema, collection, opts) + + // only the first model with this name is cached to allow + // for one-offs with custom collection names etc. + if (!this.models[name]) { + this.models[name] = model; + } + + model.init(); + return model; + } + + if (this.models[name] && collection) { + // subclassing current model with alternate collection + model = this.models[name]; + schema = model.prototype.schema; + var sub = model.__subclass(this, schema, collection); + // do not cache the sub model + return sub; + } + + // lookup model in mongoose module + model = this.base.models[name]; + + if (!model) { + throw new MongooseError.MissingSchemaError(name); + } + + if (this == model.prototype.db + && (!collection || collection == model.collection.name)) { + // model already uses this connection. + + // only the first model with this name is cached to allow + // for one-offs with custom collection names etc. + if (!this.models[name]) { + this.models[name] = model; + } + + return model; + } + + return this.models[name] = model.__subclass(this, schema, collection); +} + +/** + * Set profiling level. + * + * @param {Number|String} level either off (0), slow (1), or all (2) + * @param {Number} [ms] the threshold in milliseconds above which queries will be logged when in `slow` mode. defaults to 100. + * @param {Function} callback + * @api public + */ + +Connection.prototype.setProfiling = function (level, ms, callback) { + if (STATES.connected !== this.readyState) { + return this.on('open', this.setProfiling.bind(this, level, ms, callback)); + } + + if (!callback) callback = ms, ms = 100; + + var cmd = {}; + + switch (level) { + case 0: + case 'off': + cmd.profile = 0; + break; + case 1: + case 'slow': + cmd.profile = 1; + if ('number' !== typeof ms) { + ms = parseInt(ms, 10); + if (isNaN(ms)) ms = 100; + } + cmd.slowms = ms; + break; + case 2: + case 'all': + cmd.profile = 2; + break; + default: + return callback(new Error('Invalid profiling level: '+ level)); + } + + this.db.executeDbCommand(cmd, function (err, resp) { + if (err) return callback(err); + + var doc = resp.documents[0]; + + err = 1 === doc.ok + ? null + : new Error('Could not set profiling level to: '+ level) + + callback(err, doc); + }); +}; + +/*! + * Noop. + */ + +function noop () {} + +/*! + * Module exports. + */ + +Connection.STATES = STATES; +module.exports = Connection; diff --git a/test/node_modules/mongoose/lib/connectionstate.js b/test/node_modules/mongoose/lib/connectionstate.js new file mode 100644 index 0000000..2d05ab0 --- /dev/null +++ b/test/node_modules/mongoose/lib/connectionstate.js @@ -0,0 +1,24 @@ + +/*! + * Connection states + */ + +var STATES = module.exports = exports = Object.create(null); + +var disconnected = 'disconnected'; +var connected = 'connected'; +var connecting = 'connecting'; +var disconnecting = 'disconnecting'; +var uninitialized = 'uninitialized'; + +STATES[0] = disconnected; +STATES[1] = connected; +STATES[2] = connecting; +STATES[3] = disconnecting; +STATES[99] = uninitialized; + +STATES[disconnected] = 0; +STATES[connected] = 1; +STATES[connecting] = 2; +STATES[disconnecting] = 3; +STATES[uninitialized] = 99; diff --git a/test/node_modules/mongoose/lib/document.js b/test/node_modules/mongoose/lib/document.js new file mode 100644 index 0000000..3e4e9d6 --- /dev/null +++ b/test/node_modules/mongoose/lib/document.js @@ -0,0 +1,1520 @@ +/*! + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , MongooseError = require('./error') + , MixedSchema = require('./schema/mixed') + , Schema = require('./schema') + , ValidatorError = require('./schematype').ValidatorError + , utils = require('./utils') + , clone = utils.clone + , isMongooseObject = utils.isMongooseObject + , inspect = require('util').inspect + , StateMachine = require('./statemachine') + , ActiveRoster = StateMachine.ctor('require', 'modify', 'init', 'default') + , ValidationError = require('./errors/validation') + , DocumentError = require('./errors/document') + , deepEqual = utils.deepEqual + , hooks = require('hooks') + , DocumentArray + , MongooseArray + +/** + * Document constructor. + * + * @param {Object} obj the values to set + * @param {Object} [fields] the fields which were selected in the query returning this document + * @param {Boolean} [skipId] bool, should we auto create an ObjectId _id + * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter + * @event `init`: Emitted on a document after it has was retreived from the db and fully hydrated by Mongoose. + * @event `save`: Emitted when the document is successfully saved + * @api private + */ + +function Document (obj, fields, skipId) { + // node <0.4.3 bug + if (!this._events) this._events = {}; + this.setMaxListeners(0); + + if ('boolean' === typeof fields) { + this._strictMode = fields; + this._selected = fields = undefined; + } else { + this._strictMode = this.schema.options && this.schema.options.strict; + this._selected = fields; + } + + this.isNew = true; + this.errors = undefined; + this._shardval = undefined; + this._saveError = undefined; + this._validationError = undefined; + this._adhocPaths = undefined; + this._removing = undefined; + this._inserting = undefined; + this.__version = undefined; + this.__getters = {}; + this.__id = undefined; + + this._activePaths = new ActiveRoster; + + var required = this.schema.requiredPaths(); + for (var i = 0; i < required.length; ++i) { + this._activePaths.require(required[i]); + } + + this._doc = this._buildDoc(obj, fields, skipId); + if (obj) this.set(obj, undefined, true); + this._registerHooks(); +}; + +/*! + * Inherit from EventEmitter. + */ + +Document.prototype.__proto__ = EventEmitter.prototype; + +/** + * The documents schema. + * + * @api public + * @property schema + */ + +Document.prototype.schema; + +/** + * Boolean flag specifying if the document is new. + * + * @api public + * @property isNew + */ + +Document.prototype.isNew; + +/** + * The string version of this documents _id. + * + * ####Note: + * + * This getter exists on all documents by default. The getter can be disabled by setting the `id` [option](/docs/guide.html#id) of its `Schema` to false at construction time. + * + * new Schema({ name: String }, { id: false }); + * + * @api public + * @see Schema options /docs/guide.html#options + * @property id + */ + +Document.prototype.id; + +/** + * Hash containing current validation errors. + * + * @api public + * @property errors + */ + +Document.prototype.errors; + +/** + * Builds the default doc structure + * + * @param {Object} obj + * @param {Object} [fields] + * @param {Boolean} [skipId] + * @return {Object} + * @api private + */ + +Document.prototype._buildDoc = function (obj, fields, skipId) { + var doc = {} + , self = this + , exclude + , keys + , key + , ki + + // determine if this doc is a result of a query with + // excluded fields + if (fields && 'Object' === fields.constructor.name) { + keys = Object.keys(fields); + ki = keys.length; + + while (ki--) { + if ('_id' !== keys[ki]) { + exclude = 0 === fields[keys[ki]]; + break; + } + } + } + + var paths = Object.keys(this.schema.paths) + , plen = paths.length + , ii = 0 + + for (; ii < plen; ++ii) { + var p = paths[ii]; + + if ('_id' == p) { + if (skipId) continue; + if (obj && '_id' in obj) continue; + } + + var type = this.schema.paths[p] + , path = p.split('.') + , len = path.length + , last = len-1 + , doc_ = doc + , i = 0 + + for (; i < len; ++i) { + var piece = path[i] + , def + + if (i === last) { + if (fields) { + if (exclude) { + // apply defaults to all non-excluded fields + if (p in fields) continue; + + def = type.getDefault(self, true); + if ('undefined' !== typeof def) { + doc_[piece] = def; + self._activePaths.default(p); + } + + } else if (p in fields) { + // selected field + def = type.getDefault(self, true); + if ('undefined' !== typeof def) { + doc_[piece] = def; + self._activePaths.default(p); + } + } + } else { + def = type.getDefault(self, true); + if ('undefined' !== typeof def) { + doc_[piece] = def; + self._activePaths.default(p); + } + } + } else { + doc_ = doc_[piece] || (doc_[piece] = {}); + } + } + }; + + return doc; +}; + +/** + * Initializes the document without setters or marking anything modified. + * + * Called internally after a document is returned from mongodb. + * + * @param {Object} doc document returned by mongo + * @param {Function} fn callback + * @api private + */ + +Document.prototype.init = function (doc, fn) { + this.isNew = false; + + init(this, doc, this._doc); + this._storeShard(); + + this.emit('init', this); + if (fn) fn(null); + return this; +}; + +/*! + * Init helper. + * + * @param {Object} self document instance + * @param {Object} obj raw mongodb doc + * @param {Object} doc object we are initializing + * @api private + */ + +function init (self, obj, doc, prefix) { + prefix = prefix || ''; + + var keys = Object.keys(obj) + , len = keys.length + , schema + , path + , i; + + while (len--) { + i = keys[len]; + path = prefix + i; + schema = self.schema.path(path); + + if (!schema && obj[i] && 'Object' === obj[i].constructor.name) { + // assume nested object + if (!doc[i]) { + doc[i] = {}; + } + init(self, obj[i], doc[i], path + '.'); + } else { + if (obj[i] === null) { + doc[i] = null; + } else if (obj[i] !== undefined) { + if (schema) { + self.try(function(){ + doc[i] = schema.cast(obj[i], self, true); + }); + } else { + doc[i] = obj[i]; + } + } + // mark as hydrated + self._activePaths.init(path); + } + } +}; + +/** + * Stores the current values of the shard keys. + * + * ####Note: + * + * _Shard key values do not / are not allowed to change._ + * + * @api private + */ + +Document.prototype._storeShard = function _storeShard () { + // backwards compat + var key = this.schema.options.shardKey || this.schema.options.shardkey; + if (!(key && 'Object' == key.constructor.name)) return; + + var orig = this._shardval = {} + , paths = Object.keys(key) + , len = paths.length + , val + + for (var i = 0; i < len; ++i) { + val = this.getValue(paths[i]); + if (isMongooseObject(val)) { + orig[paths[i]] = val.toObject({ depopulate: true }) + } else if (null != val && val.valueOf) { + orig[paths[i]] = val.valueOf(); + } else { + orig[paths[i]] = val; + } + } +} + +/*! + * Set up middleware support + */ + +for (var k in hooks) { + Document.prototype[k] = Document[k] = hooks[k]; +} + +/** + * Sends an update command with this document `_id` as the query selector. + * + * ####Example: + * + * weirdCar.update({$inc: {wheels:1}}, { safe: true }, callback); + * + * ####Valid options: + * + * - `safe` safe mode (defaults to value set in schema (true)) + * - `upsert` (boolean) whether to create the doc if it doesn't match (false) + * + * @param {Object} doc + * @param {Object} options + * @param {Function} callback + * @return {Query} + * @api public + */ + +Document.prototype.update = function update () { + var args = utils.args(arguments); + args.unshift({_id: this._id}); + this.constructor.update.apply(this.constructor, args); +} + +/** + * Sets the value of a path, or many paths. + * + * ####Example: + * + * // path, value + * doc.set(path, value) + * + * // object + * doc.set({ + * path : value + * , path2 : { + * path : value + * } + * }) + * + * // only-the-fly cast to number + * doc.set(path, value, Number) + * + * // only-the-fly cast to string + * doc.set(path, value, String) + * + * @param {String|Object} path path or object of key/vals to set + * @param {Any} val the value to set + * @param {Schema|String|Number|Buffer|etc..} [type] optionally specify a type for "on-the-fly" attributes + * @param {Object} [options] optionally specify options that modify the behavior of the set + * @api public + */ + +Document.prototype.set = function (path, val, type, options) { + if (type && 'Object' == type.constructor.name) { + options = type; + type = undefined; + } + + var merge = options && options.merge + , adhoc = type && true !== type + , constructing = true === type + , adhocs + + if (adhoc) { + adhocs = this._adhocPaths || (this._adhocPaths = {}); + adhocs[path] = Schema.interpretAsType(path, type); + } + + if ('string' !== typeof path) { + // new Document({ key: val }) + + if (null === path || undefined === path) { + var _ = path; + path = val; + val = _; + + } else { + var prefix = val + ? val + '.' + : ''; + + if (path instanceof Document) path = path._doc; + + var keys = Object.keys(path) + , i = keys.length + , pathtype + , key + + while (i--) { + key = keys[i]; + pathtype = this.schema.pathType(prefix + key); + if (null != path[key] + && 'Object' == path[key].constructor.name + && 'virtual' != pathtype + && !(this._path(prefix + key) instanceof MixedSchema)) { + this.set(path[key], prefix + key, constructing); + } else if (this._strictMode) { + if ('real' === pathtype || 'virtual' === pathtype) { + this.set(prefix + key, path[key], constructing); + } else if ('throw' == this._strictMode) { + throw new Error("Field `" + key + "` is not in schema."); + } + } else if (undefined !== path[key]) { + this.set(prefix + key, path[key], constructing); + } + } + + return this; + } + } + + // ensure _strict is honored for obj props + // docschema = new Schema({ path: { nest: 'string' }}) + // doc.set('path', obj); + var pathType = this.schema.pathType(path); + if ('nested' == pathType && val && 'Object' == val.constructor.name) { + if (!merge) this.setValue(path, null); + this.set(val, path, constructing); + return this; + } + + var schema; + if ('adhocOrUndefined' == pathType && this._strictMode) { + return this; + } else if ('virtual' == pathType) { + schema = this.schema.virtualpath(path); + schema.applySetters(val, this); + return this; + } else { + schema = this._path(path); + } + + var parts = path.split('.') + , pathToMark + + // When using the $set operator the path to the field must already exist. + // Else mongodb throws: "LEFT_SUBFIELD only supports Object" + + if (parts.length <= 1) { + pathToMark = path; + } else { + for (var i = 0; i < parts.length; ++i) { + var part = parts[i]; + var subpath = parts.slice(0, i).concat(part).join('.'); + if (this.isDirectModified(subpath) // earlier prefixes that are already + // marked as dirty have precedence + || this.get(subpath) === null) { + pathToMark = subpath; + break; + } + } + + if (!pathToMark) pathToMark = path; + } + + if (!schema || null === val || undefined === val) { + this._set(pathToMark, path, constructing, parts, schema, val); + return this; + } + + var self = this; + + // if this doc is being constructed we should not + // trigger getters. + var priorVal = constructing + ? undefined + : this.get(path); + + var shouldSet = this.try(function(){ + val = schema.applySetters(val, self, false, priorVal); + }); + + if (shouldSet) { + this._set(pathToMark, path, constructing, parts, schema, val, priorVal); + } + + return this; +} + +/** + * Determine if we should mark this change as modified. + * + * @return {Boolean} + * @api private + */ + +Document.prototype._shouldModify = function ( + pathToMark, path, constructing, parts, schema, val, priorVal) { + + if (this.isNew) return true; + if (this.isDirectModified(pathToMark)) return false; + + if (undefined === val && !this.isSelected(path)) { + // when a path is not selected in a query, its initial + // value will be undefined. + return true; + } + + if (undefined === val && path in this._activePaths.states.default) { + // we're just unsetting the default value which was never saved + return false; + } + + if (!deepEqual(val, priorVal || this.get(path))) { + return true; + } + + if (!constructing && + null != val && + path in this._activePaths.states.default && + deepEqual(val, schema.getDefault(this, constructing))) { + // a path with a default was $unset on the server + // and the user is setting it to the same value again + return true; + } + + return false; +} + +/** + * Handles the actual setting of the value and marking the path modified if appropriate. + * + * @api private + */ + +Document.prototype._set = function ( + pathToMark, path, constructing, parts, schema, val, priorVal) { + + var shouldModify = this._shouldModify.apply(this, arguments); + + if (shouldModify) { + this.markModified(pathToMark, val); + + // handle directly setting arrays (gh-1126) + MongooseArray || (MongooseArray = require('./types/array')); + if (val instanceof MongooseArray) { + val._registerAtomic('$set', val); + } + } + + var obj = this._doc + , i = 0 + , l = parts.length + + for (; i < l; i++) { + var next = i + 1 + , last = next === l; + + if (last) { + obj[parts[i]] = val; + } else { + if (obj[parts[i]] && 'Object' === obj[parts[i]].constructor.name) { + obj = obj[parts[i]]; + } else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) { + obj = obj[parts[i]]; + } else { + obj = obj[parts[i]] = {}; + } + } + } +} + +/** + * Gets a raw value from a path (no getters) + * + * @param {String} path + * @api private + */ + +Document.prototype.getValue = function (path) { + var parts = path.split('.') + , obj = this._doc + , part; + + for (var i = 0, l = parts.length; i < l; i++) { + part = parts[i]; + obj = obj.getValue + ? obj.getValue(part) // If we have an embedded array document member + : obj[part]; + if (!obj) return obj; + } + + return obj; +} + +/** + * Sets a raw value for a path (no casting, setters, transformations) + * + * @param {String} path + * @param {Object} value + * @api private + */ + +Document.prototype.setValue = function (path, val) { + var parts = path.split('.') + , obj = this._doc; + + for (var i = 0, len = parts.length-1; i < len; i++) { + obj = obj[parts[i]]; + } + + obj[parts[len]] = val; + return this; +}; + +/** + * Returns the value of a path. + * + * ####Example + * + * // path + * doc.get('age') // 47 + * + * // dynamic casting to a string + * doc.get('age', String) // "47" + * + * @param {String} path + * @param {Schema|String|Number|Buffer|etc..} [type] optionally specify a type for on-the-fly attributes + * @api public + */ + +Document.prototype.get = function (path, type) { + var adhocs; + if (type) { + adhocs = this._adhocPaths || (this._adhocPaths = {}); + adhocs[path] = Schema.interpretAsType(path, type); + } + + var schema = this._path(path) || this.schema.virtualpath(path) + , pieces = path.split('.') + , obj = this._doc; + + for (var i = 0, l = pieces.length; i < l; i++) { + obj = null == obj ? null : obj[pieces[i]]; + } + + if (schema) { + obj = schema.applyGetters(obj, this); + } + + return obj; +}; + +/** + * Returns the schematype for the given `path`. + * + * @param {String} path + * @api private + */ + +Document.prototype._path = function (path) { + var adhocs = this._adhocPaths + , adhocType = adhocs && adhocs[path]; + + if (adhocType) { + return adhocType; + } else { + return this.schema.path(path); + } +}; + +/** + * Marks the path as having pending changes to write to the db. + * + * _Very helpful when using [Mixed](./schematypes.html#mixed) types._ + * + * ####Example: + * + * doc.mixed.type = 'changed'; + * doc.markModified('mixed.type'); + * doc.save() // changes to mixed.type are now persisted + * + * @param {String} path the path to mark modified + * @api public + */ + +Document.prototype.markModified = function (path) { + this._activePaths.modify(path); +} + +/** + * Catches errors that occur during execution of `fn` and stores them to later be passed when `save()` is executed. + * + * @param {Function} fn function to execute + * @param {Object} scope the scope with which to call fn + * @api private + */ + +Document.prototype.try = function (fn, scope) { + var res; + try { + fn.call(scope); + res = true; + } catch (e) { + this._error(e); + res = false; + } + return res; +}; + +/** + * Returns the list of paths that have been modified. + * + * @return {Array} + * @api public + */ + +Document.prototype.modifiedPaths = function () { + var directModifiedPaths = Object.keys(this._activePaths.states.modify); + + return directModifiedPaths.reduce(function (list, path) { + var parts = path.split('.'); + return list.concat(parts.reduce(function (chains, part, i) { + return chains.concat(parts.slice(0, i).concat(part).join('.')); + }, [])); + }, []); +}; + +/** + * Returns true if this document was modified, else false. + * + * If `path` is given, checks if a path or any full path containing `path` as part of its path chain has been modified. + * + * ####Example + * + * doc.set('documents.0.title', 'changed'); + * doc.isModified() // true + * doc.isModified('documents') // true + * doc.isModified('documents.0.title') // true + * doc.isDirectModified('documents') // false + * + * @param {String} [path] optional + * @return {Boolean} + * @api public + */ + +Document.prototype.isModified = function (path) { + return path + ? !!~this.modifiedPaths().indexOf(path) + : this._activePaths.some('modify'); +}; + +/** + * Returns true if `path` was directly set and modified, else false. + * + * ####Example + * + * doc.set('documents.0.title', 'changed'); + * doc.isDirectModified('documents.0.title') // true + * doc.isDirectModified('documents') // false + * + * @param {String} path + * @return {Boolean} + * @api public + */ + +Document.prototype.isDirectModified = function (path) { + return (path in this._activePaths.states.modify); +}; + +/** + * Checks if `path` was initialized. + * + * @param {String} path + * @return {Boolean} + * @api public + */ + +Document.prototype.isInit = function (path) { + return (path in this._activePaths.states.init); +}; + +/** + * Checks if `path` was selected in the source query which initialized this document. + * + * ####Example + * + * Thing.findOne().select('name').exec(function (err, doc) { + * doc.isSelected('name') // true + * doc.isSelected('age') // false + * }) + * + * @param {String} path + * @return {Boolean} + * @api public + */ + +Document.prototype.isSelected = function isSelected (path) { + if (this._selected) { + + if ('_id' === path) { + return 0 !== this._selected._id; + } + + var paths = Object.keys(this._selected) + , i = paths.length + , inclusive = false + , cur + + if (1 === i && '_id' === paths[0]) { + // only _id was selected. + return 0 === this._selected._id; + } + + while (i--) { + cur = paths[i]; + if ('_id' == cur) continue; + inclusive = !! this._selected[cur]; + break; + } + + if (path in this._selected) { + return inclusive; + } + + i = paths.length; + var pathDot = path + '.'; + + while (i--) { + cur = paths[i]; + if ('_id' == cur) continue; + + if (0 === cur.indexOf(pathDot)) { + return inclusive; + } + + if (0 === pathDot.indexOf(cur)) { + return inclusive; + } + } + + return ! inclusive; + } + + return true; +} + +/** + * Executes registered validation rules for this document. + * + * ####Note: + * + * This method is called `pre` save and if a validation rule is violated, [save](#model_Model-save) is aborted and the error is returned to your `callback`. + * + * ####Example: + * + * doc.validate(function (err) { + * if (err) handleError(err); + * else // validation passed + * }); + * + * @param {Function} cb called after validation completes, passing an error if one occurred + * @api public + */ + +Document.prototype.validate = function (cb) { + var self = this + + // only validate required fields when necessary + var paths = Object.keys(this._activePaths.states.require).filter(function (path) { + if (!self.isSelected(path) && !self.isModified(path)) return false; + return true; + }); + + paths = paths.concat(Object.keys(this._activePaths.states.init)); + paths = paths.concat(Object.keys(this._activePaths.states.modify)); + paths = paths.concat(Object.keys(this._activePaths.states.default)); + + if (0 === paths.length) { + complete(); + return this; + } + + var validating = {} + , total = 0; + + paths.forEach(validatePath); + return this; + + function validatePath (path) { + if (validating[path]) return; + + validating[path] = true; + total++; + + process.nextTick(function(){ + var p = self.schema.path(path); + if (!p) return --total || complete(); + + p.doValidate(self.getValue(path), function (err) { + if (err) self.invalidate(path, err, true); + --total || complete(); + }, self); + }); + } + + function complete () { + var err = self._validationError; + self._validationError = undefined; + self.emit('validate', self); + cb(err); + } +}; + +/** + * Marks a path as invalid, causing validation to fail. + * + * @param {String} path the field to invalidate + * @param {String|Error} err the error which states the reason `path` was invalid + * @api public + */ + +Document.prototype.invalidate = function (path, err) { + if (!this._validationError) { + this._validationError = new ValidationError(this); + } + + if (!err || 'string' === typeof err) { + err = new ValidatorError(path, err); + } + + this._validationError.errors[path] = err; +} + +/** + * Resets the internal modified state of this document. + * + * @api private + * @return {Document} + */ + +Document.prototype._reset = function reset () { + var self = this; + DocumentArray || (DocumentArray = require('./types/documentarray')); + + this._activePaths + .map('init', 'modify', function (i) { + return self.getValue(i); + }) + .filter(function (val) { + return val && val instanceof DocumentArray && val.length; + }) + .forEach(function (array) { + var i = array.length; + while (i--) { + var doc = array[i]; + if (!doc) continue; + doc._reset(); + } + }); + + // clear atomics + this._dirty().forEach(function (dirt) { + var type = dirt.value; + if (type && type._atomics) { + type._atomics = {}; + } + }); + + // Clear 'modify'('dirty') cache + this._activePaths.clear('modify'); + this._validationError = undefined; + this.errors = undefined; + var self = this; + this.schema.requiredPaths().forEach(function (path) { + self._activePaths.require(path); + }); + + return this; +} + +/** + * Returns this documents dirty paths / vals. + * + * @api private + */ + +Document.prototype._dirty = function _dirty () { + var self = this; + + var all = this._activePaths.map('modify', function (path) { + return { path: path + , value: self.getValue(path) + , schema: self._path(path) }; + }); + + // Sort dirty paths in a flat hierarchy. + all.sort(function (a, b) { + return (a.path < b.path ? -1 : (a.path > b.path ? 1 : 0)); + }); + + // Ignore "foo.a" if "foo" is dirty already. + var minimal = [] + , lastPath + , top; + + all.forEach(function (item, i) { + if (item.path.indexOf(lastPath) !== 0) { + lastPath = item.path + '.'; + minimal.push(item); + top = item; + } else { + if (!(item.value && top.value)) return; + + // special case for top level MongooseArrays + if (top.value._atomics && top.value.hasAtomics()) { + // the `top` array itself and a sub path of `top` are being modified. + // the only way to honor all of both modifications is through a $set + // of entire array. + top.value._atomics = {}; + top.value._atomics.$set = top.value; + } + } + }); + + top = lastPath = null; + return minimal; +} + +/*! + * Compiles schemas. + */ + +function compile (tree, proto, prefix) { + var keys = Object.keys(tree) + , i = keys.length + , limb + , key; + + while (i--) { + key = keys[i]; + limb = tree[key]; + + define(key + , (('Object' === limb.constructor.name + && Object.keys(limb).length) + && (!limb.type || limb.type.type) + ? limb + : null) + , proto + , prefix + , keys); + } +}; + +/*! + * Defines the accessor named prop on the incoming prototype. + */ + +function define (prop, subprops, prototype, prefix, keys) { + var prefix = prefix || '' + , path = (prefix ? prefix + '.' : '') + prop; + + if (subprops) { + + Object.defineProperty(prototype, prop, { + enumerable: true + , get: function () { + if (!this.__getters) + this.__getters = {}; + + if (!this.__getters[path]) { + var nested = Object.create(this); + + // save scope for nested getters/setters + if (!prefix) nested._scope = this; + + // shadow inherited getters from sub-objects so + // thing.nested.nested.nested... doesn't occur (gh-366) + var i = 0 + , len = keys.length; + + for (; i < len; ++i) { + // over-write the parents getter without triggering it + Object.defineProperty(nested, keys[i], { + enumerable: false // It doesn't show up. + , writable: true // We can set it later. + , configurable: true // We can Object.defineProperty again. + , value: undefined // It shadows its parent. + }); + } + + nested.toObject = function () { + return this.get(path); + }; + + compile(subprops, nested, path); + this.__getters[path] = nested; + } + + return this.__getters[path]; + } + , set: function (v) { + if (v instanceof Document) v = v.toObject(); + return this.set(path, v); + } + }); + + } else { + + Object.defineProperty(prototype, prop, { + enumerable: true + , get: function ( ) { return this.get.call(this._scope || this, path); } + , set: function (v) { return this.set.call(this._scope || this, path, v); } + }); + } +}; + +/** + * Assigns/compiles `schema` into this documents prototype. + * + * @param {Schema} schema + * @api private + */ + +Document.prototype._setSchema = function (schema) { + compile(schema.tree, this); + this.schema = schema; +} + +/** + * Register default hooks + * + * @api private + */ + +Document.prototype._registerHooks = function _registerHooks () { + if (!this.save) return; + + DocumentArray || (DocumentArray = require('./types/documentarray')); + + this.pre('save', function (next) { + // we keep the error semaphore to make sure we don't + // call `save` unnecessarily (we only need 1 error) + var subdocs = 0 + , error = false + , self = this; + + // check for DocumentArrays + var arrays = this._activePaths + .map('init', 'modify', function (i) { + return self.getValue(i); + }) + .filter(function (val) { + return val && val instanceof DocumentArray && val.length; + }); + + if (!arrays.length) + return next(); + + arrays.forEach(function (array) { + if (error) return; + + // handle sparse arrays by using for loop vs array.forEach + // which skips the sparse elements + + var len = array.length + subdocs += len; + + for (var i = 0; i < len; ++i) { + if (error) break; + + var doc = array[i]; + if (!doc) { + --subdocs || next(); + continue; + } + + doc.save(handleSave); + } + }); + + function handleSave (err) { + if (error) return; + + if (err) { + self._validationError = undefined; + return next(error = err); + } + + --subdocs || next(); + } + + }, function (err) { + // emit on the Model if listening + if (this.constructor.listeners('error').length) { + this.constructor.emit('error', err); + } else { + // emit on the connection + if (!this.db.listeners('error').length) { + err.stack = 'No listeners detected, throwing. ' + + 'Consider adding an error listener to your connection.\n' + + err.stack + } + this.db.emit('error', err); + } + }).pre('save', function checkForExistingErrors (next) { + // if any doc.set() calls failed + if (this._saveError) { + next(this._saveError); + this._saveError = null; + } else { + next(); + } + }).pre('save', function validation (next) { + return this.validate(next); + }); + + // add user defined queues + this._doQueue(); +}; + +/** + * Registers an error + * + * @param {Error} err + * @api private + */ + +Document.prototype._error = function (err) { + this._saveError = err; + return this; +}; + +/** + * Executes methods queued from the Schema definition + * + * @api private + */ + +Document.prototype._doQueue = function () { + var q = this.schema && this.schema.callQueue; + if (q) { + for (var i = 0, l = q.length; i < l; i++) { + this[q[i][0]].apply(this, q[i][1]); + } + } + return this; +}; + +/** + * Converts this document into a plain javascript object + * + * ####Options: + * + * - `getters` apply all getters (path and virtual getters) + * - `virtuals` apply virtual getters (can override `getters` option) + * - `minimize` remove empty objects (defaults to true) + * - `transform` a transform function to apply to the resulting document before returning + * + * ####Getters/Virtuals + * + * Example of only applying path getters + * + * doc.toObject({ getters: true, virtuals: false }) + * + * Example of only applying virtual getters + * + * doc.toObject({ virtuals: true }) + * + * Example of applying both path and virtual getters + * + * doc.toObject({ getters: true }) + * + * To apply these options to every document of your schema by default, set your [schemas](#schema_Schema) `toObject` option to the same argument. + * + * schema.set('toObject', { virtuals: true }) + * + * ####Transform + * + * We may need to perform a transformation of the resulting object based on some criteria, say to remove some sensitive information or return a custom object. In this case we set the optional `transform` function. + * + * Transform functions receive three arguments + * + * function (doc, ret, options) {} + * + * - `doc` The mongoose document which is being converted + * - `ret` The plain object representation which has been converted + * - `options` The options in use (either schema options or the options passed inline) + * + * ####Example + * + * // specify the transform schema option + * schema.options.toObject.transform = function (doc, ret, options) { + * // remove the _id of every document before returning the result + * delete ret._id; + * } + * + * // without the transformation in the schema + * doc.toObject(); // { _id: 'anId', name: 'Wreck-it Ralph' } + * + * // with the transformation + * doc.toObject(); // { name: 'Wreck-it Ralph' } + * + * With transformations we can do a lot more than remove properties. We can even return completely new customized objects: + * + * schema.options.toObject.transform = function (doc, ret, options) { + * return { movie: ret.name } + * } + * + * // without the transformation in the schema + * doc.toObject(); // { _id: 'anId', name: 'Wreck-it Ralph' } + * + * // with the transformation + * doc.toObject(); // { movie: 'Wreck-it Ralph' } + * + * _Note: if a transform function returns `undefined`, the return value will be ignored._ + * + * Transformations may also be applied inline, overridding any transform set in the options: + * + * function xform (doc, ret, options) { + * return { inline: ret.name, custom: true } + * } + * + * // pass the transform as an inline option + * doc.toObject({ transform: xform }); // { inline: 'Wreck-it Ralph', custom: true } + * + * _Note: if you call `toObject` and pass any options, the transform declared in your schema options will __not__ be applied. To force its application pass `transform: true`_ + * + * schema.options.toObject.hide = '_id'; + * schema.options.toObject.transform = function (doc, ret, options) { + * if (options.hide) { + * options.hide.split(' ').forEach(function (prop) { + * delete ret[prop]; + * }); + * } + * } + * + * var doc = new Doc({ _id: 'anId', secret: 47, name: 'Wreck-it Ralph' }); + * doc.toObject(); // { secret: 47, name: 'Wreck-it Ralph' } + * doc.toObject({ hide: 'secret _id' }); // { _id: 'anId', secret: 47, name: 'Wreck-it Ralph' } + * doc.toObject({ hide: 'secret _id', transform: true }); // { name: 'Wreck-it Ralph' } + * + * Transforms are applied to the document _and each of its sub-documents_. To determine whether or not you are currently operating on a sub-document you might use the following guard: + * + * if ('function' == typeof doc.ownerDocument) { + * // working with a sub doc + * } + * + * Transforms, like all of these options, are also available for `toJSON`. + * + * See [schema options](/docs/guide.html#toObject) for some more details. + * + * @param {Object} [options] + * @return {Object} js object + * @api public + */ + +Document.prototype.toObject = function (options) { + // When internally saving this document we always pass options, + // bypassing the custom schema options. + if (!(options && 'Object' == options.constructor.name)) { + options = this.schema.options.toObject + ? clone(this.schema.options.toObject) + : {}; + } + + ;('minimize' in options) || (options.minimize = this.schema.options.minimize); + + var ret = clone(this._doc, options); + + if (options.virtuals || options.getters && false !== options.virtuals) { + applyGetters(this, ret, 'virtuals', options); + } + + if (options.getters) { + applyGetters(this, ret, 'paths', options); + } + + if (true === options.transform) { + var opts = options.json + ? this.schema.options.toJSON + : this.schema.options.toObject; + if (opts) { + options.transform = opts.transform; + } + } + + if ('function' == typeof options.transform) { + var xformed = options.transform(this, ret, options); + if ('undefined' != typeof xformed) ret = xformed; + } + + return ret; +}; + +/*! + * Applies virtuals properties to `json`. + * + * @param {Document} self + * @param {Object} json + * @param {String} type either `virtuals` or `paths` + * @return {Object} `json` + */ + +function applyGetters (self, json, type, options) { + var schema = self.schema + , paths = Object.keys(schema[type]) + , i = paths.length + , path + + while (i--) { + path = paths[i]; + + var parts = path.split('.') + , plen = parts.length + , last = plen - 1 + , branch = json + , part + + for (var ii = 0; ii < plen; ++ii) { + part = parts[ii]; + if (ii === last) { + branch[part] = clone(self.get(path), options); + } else { + branch = branch[part] || (branch[part] = {}); + } + } + } + + return json; +} + +/** + * The return value of this method is used in calls to JSON.stringify(doc). + * + * This method accepts the same options as [Document#toObject](#document_Document-toObject). To apply the options to every document of your schema by default, set your [schemas](#schema_Schema) `toJSON` option to the same argument. + * + * schema.set('toJSON', { virtuals: true }) + * + * See [schema options](/docs/guide.html#toJSON) for details. + * + * @param {Object} options same options as [Document#toObject](#document_Document-toObject) + * @return {Object} + * @see Document#toObject #document_Document-toObject + + * @api public + */ + +Document.prototype.toJSON = function (options) { + // check for object type since an array of documents + // being stringified passes array indexes instead + // of options objects. JSON.stringify([doc, doc]) + if (!(options && 'Object' == options.constructor.name)) { + options = this.schema.options.toJSON + ? clone(this.schema.options.toJSON) + : {}; + } + options.json = true; + return this.toObject(options); +}; + +/** + * Helper for console.log + * + * @api public + */ + +Document.prototype.inspect = function (options) { + var opts = options && 'Object' == options.constructor.name + ? options + : undefined + return inspect(this.toObject(opts)); +}; + +/** + * Helper for console.log + * + * @api public + * @method toString + */ + +Document.prototype.toString = Document.prototype.inspect; + +/** + * Returns true if the Document stores the same data as doc. + * + * Documents are considered equal when they have matching `_id`s. + * + * @param {Document} doc a document to compare + * @return {Boolean} + * @api public + */ + +Document.prototype.equals = function (doc) { + var tid = this.get('_id'); + var docid = doc.get('_id'); + return tid.equals + ? tid.equals(docid) + : tid === docid; +}; + +/*! + * Module exports. + */ + +Document.ValidationError = ValidationError; +module.exports = exports = Document; +exports.Error = DocumentError; diff --git a/test/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js b/test/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js new file mode 100644 index 0000000..0480d31 --- /dev/null +++ b/test/node_modules/mongoose/lib/drivers/node-mongodb-native/binary.js @@ -0,0 +1,8 @@ + +/*! + * Module dependencies. + */ + +var Binary = require('mongodb').BSONPure.Binary; + +module.exports = exports = Binary; diff --git a/test/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js b/test/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js new file mode 100644 index 0000000..a53857f --- /dev/null +++ b/test/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js @@ -0,0 +1,212 @@ + +/*! + * Module dependencies. + */ + +var MongooseCollection = require('../../collection') + , Collection = require('mongodb').Collection + , STATES = require('../../connectionstate') + , utils = require('../../utils') + +/** + * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation. + * + * All methods methods from the [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver are copied and wrapped in queue management. + * + * @inherits Collection + * @api private + */ + +function NativeCollection () { + this.collection = null; + MongooseCollection.apply(this, arguments); +} + +/*! + * Inherit from abstract Collection. + */ + +NativeCollection.prototype.__proto__ = MongooseCollection.prototype; + +/** + * Called when the connection opens. + * + * @api private + */ + +NativeCollection.prototype.onOpen = function () { + var self = this; + + if (this.collection) { + return MongooseCollection.prototype.onOpen.call(self); + } + + if (!self.opts.size) { + // non-capped + return self.conn.db.collection(self.name, callback); + } + + // capped + return self.conn.db.collection(self.name, function (err, c) { + if (err) return callback(err); + + // discover if this collection exists and if it is capped + c.options(function (err, exists) { + if (err) return callback(err); + + if (exists) { + if (exists.capped) { + callback(null, c); + } else { + var msg = 'A non-capped collection exists with this name.\n\n' + + ' To use this collection as a capped collection, please ' + + 'first convert it.\n' + + ' http://www.mongodb.org/display/DOCS/Capped+Collections#CappedCollections-Convertingacollectiontocapped' + err = new Error(msg); + callback(err); + } + } else { + // create + var opts = utils.clone(self.opts); + opts.capped = true; + self.conn.db.createCollection(self.name, opts, callback); + } + }); + }); + + function callback (err, collection) { + if (err) { + // likely a strict mode error + self.conn.emit('error', err); + } else { + self.collection = collection; + MongooseCollection.prototype.onOpen.call(self); + } + }; +}; + +/** + * Called when the connection closes + * + * @api private + */ + +NativeCollection.prototype.onClose = function () { + MongooseCollection.prototype.onClose.call(this); +}; + +/*! + * Copy the collection methods and make them subject to queues + */ + +for (var i in Collection.prototype) { + (function(i){ + NativeCollection.prototype[i] = function () { + if (this.buffer) { + this.addQueue(i, arguments); + return; + } + + var collection = this.collection + , args = arguments + , self = this + , debug = self.conn.base.options.debug; + + if (debug) { + if ('function' === typeof debug) { + debug.apply(debug + , [self.name, i].concat(utils.args(args, 0, args.length-1))); + } else { + console.error('\x1B[0;36mMongoose:\x1B[0m %s.%s(%s) %s %s %s' + , self.name + , i + , print(args[0]) + , print(args[1]) + , print(args[2]) + , print(args[3])) + } + } + + collection[i].apply(collection, args); + }; + })(i); +} + +/*! + * Debug print helper + */ + +function print (arg) { + var type = typeof arg; + if ('function' === type || 'undefined' === type) return ''; + return format(arg); +} + +/*! + * Debug print helper + */ + +function format (obj, sub) { + var x = utils.clone(obj); + if (x) { + if ('Binary' === x.constructor.name) { + x = '[object Buffer]'; + } else if ('ObjectID' === x.constructor.name) { + var representation = 'ObjectId("' + x.toHexString() + '")'; + x = { inspect: function() { return representation; } }; + } else if ('Date' === x.constructor.name) { + var representation = 'new Date("' + x.toUTCString() + '")'; + x = { inspect: function() { return representation; } }; + } else if ('Object' === x.constructor.name) { + var keys = Object.keys(x) + , i = keys.length + , key + while (i--) { + key = keys[i]; + if (x[key]) { + if ('Binary' === x[key].constructor.name) { + x[key] = '[object Buffer]'; + } else if ('Object' === x[key].constructor.name) { + x[key] = format(x[key], true); + } else if ('ObjectID' === x[key].constructor.name) { + ;(function(x){ + var representation = 'ObjectId("' + x[key].toHexString() + '")'; + x[key] = { inspect: function() { return representation; } }; + })(x) + } else if ('Date' === x[key].constructor.name) { + ;(function(x){ + var representation = 'new Date("' + x[key].toUTCString() + '")'; + x[key] = { inspect: function() { return representation; } }; + })(x) + } else if (Array.isArray(x[key])) { + x[key] = x[key].map(function (o) { + return format(o, true) + }); + } + } + } + } + if (sub) return x; + } + + return require('util') + .inspect(x, false, 10, true) + .replace(/\n/g, '') + .replace(/\s{2,}/g, ' ') +} + +/** + * Retreives information about this collections indexes. + * + * @param {Function} callback + * @method getIndexes + * @api public + */ + +NativeCollection.prototype.getIndexes = NativeCollection.prototype.indexInformation; + +/*! + * Module exports. + */ + +module.exports = NativeCollection; diff --git a/test/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js b/test/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js new file mode 100644 index 0000000..551e68f --- /dev/null +++ b/test/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js @@ -0,0 +1,267 @@ +/*! + * Module dependencies. + */ + +var MongooseConnection = require('../../connection') + , mongo = require('mongodb') + , Server = mongo.Server + , STATES = require('../../connectionstate') + , ReplSetServers = mongo.ReplSetServers; + +/** + * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation. + * + * @inherits Connection + * @api private + */ + +function NativeConnection() { + MongooseConnection.apply(this, arguments); +}; + +/*! + * Inherits from Connection. + */ + +NativeConnection.prototype.__proto__ = MongooseConnection.prototype; + +/** + * Opens the connection to MongoDB. + * + * @param {Function} fn + * @return {Connection} this + * @api private + */ + +NativeConnection.prototype.doOpen = function (fn) { + var server + , self = this; + + if (!this.db) { + server = new mongo.Server(this.host, this.port, this.options.server); + this.db = new mongo.Db(this.name, server, this.options.db); + } + + this.db.open(function (err) { + if (err) return fn(err); + fn(); + listen(self); + }); + + return this; +}; + +function listen (conn) { + if (conn._listening) return; + conn._listening = true; + + conn.db.on('close', function(){ + if (conn._closeCalled) return; + + // the driver never emits an `open` event. auto_reconnect still + // emits a `close` event but since we never get another + // `open` we can't emit close + if (conn.db.serverConfig.autoReconnect) { + conn.readyState = STATES.disconnected; + conn.emit('close'); + return; + } + conn.onClose(); + }); + conn.db.on('error', function(err){ + conn.emit('error', err); + }); + conn.db.on('timeout', function(err){ + var error = new Error(err && err.err || 'connection timeout'); + conn.emit('error', error); + }); + conn.db.on('open', function (err, db) { + if (STATES.disconnected === conn.readyState && db && db.databaseName) { + conn.readyState = STATES.connected; + conn.emit('reconnected') + } + }) +} + +/** + * Opens a connection to a MongoDB ReplicaSet. + * + * See description of [doOpen](#NativeConnection-doOpen) for server options. In this case `options.replset` is also passed to ReplSetServers. + * + * @param {Function} fn + * @api private + * @return {Connection} this + */ + +NativeConnection.prototype.doOpenSet = function (fn) { + var servers = [] + , self = this; + + if (!this.db) { + this.hosts.forEach(function (server) { + var host = server.host || server.ipc; + var port = server.port || 27017; + servers.push(new mongo.Server(host, port, self.options.server)); + }) + + var server = new ReplSetServers(servers, this.options.replset); + this.db = new mongo.Db(this.name, server, this.options.db); + + this.db.on('fullsetup', function () { + self.emit('fullsetup') + }); + } + + this.db.open(function (err) { + if (err) return fn(err); + fn(); + listen(self); + }); + + return this; +}; + +/** + * Closes the connection + * + * @param {Function} fn + * @return {Connection} this + * @api private + */ + +NativeConnection.prototype.doClose = function (fn) { + this.db.close(); + if (fn) fn(); + return this; +} + +/** + * Prepares default connection options for the node-mongodb-native driver. + * + * _NOTE: `passed` options take precedence over connection string options._ + * + * @param {Object} passed options that were passed directly during connection + * @param {Object} [connStrOptions] options that were passed in the connection string + * @api private + */ + +NativeConnection.prototype.parseOptions = function (passed, connStrOpts) { + var o = passed || {}; + o.db || (o.db = {}); + o.server || (o.server = {}); + o.replset || (o.replset = {}); + o.server.socketOptions || (o.server.socketOptions = {}); + o.replset.socketOptions || (o.replset.socketOptions = {}); + + var opts = connStrOpts || {}; + Object.keys(opts).forEach(function (name) { + switch (name) { + case 'poolSize': + if ('undefined' == typeof o.server.poolSize) { + o.server.poolSize = o.replset.poolSize = opts[name]; + } + break; + case 'slaveOk': + if ('undefined' == typeof o.server.slave_ok) { + o.server.slave_ok = opts[name]; + } + break; + case 'autoReconnect': + if ('undefined' == typeof o.server.auto_reconnect) { + o.server.auto_reconnect = opts[name]; + } + break; + case 'ssl': + case 'socketTimeoutMS': + case 'connectTimeoutMS': + if ('undefined' == typeof o.server.socketOptions[name]) { + o.server.socketOptions[name] = o.replset.socketOptions[name] = opts[name]; + } + break; + case 'retries': + case 'reconnectWait': + case 'rs_name': + if ('undefined' == typeof o.replset[name]) { + o.replset[name] = opts[name]; + } + break; + case 'replicaSet': + if ('undefined' == typeof o.replset.rs_name) { + o.replset.rs_name = opts[name]; + } + break; + case 'readSecondary': + if ('undefined' == typeof o.replset.read_secondary) { + o.replset.read_secondary = opts[name]; + } + break; + case 'nativeParser': + if ('undefined' == typeof o.db.native_parser) { + o.db.native_parser = opts[name]; + } + break; + case 'w': + case 'safe': + case 'fsync': + case 'journal': + case 'wtimeoutMS': + if ('undefined' == typeof o.db[name]) { + o.db[name] = opts[name]; + } + break; + case 'readPreference': + if ('undefined' == typeof o.db.read_preference) { + o.db.read_preference = opts[name]; + } + break; + case 'readPreferenceTags': + if ('undefined' == typeof o.db.read_preference_tags) { + o.db.read_preference_tags = opts[name]; + } + break; + } + }) + + if (!('auto_reconnect' in o.server)) { + o.server.auto_reconnect = true; + } + + if (!o.db.read_preference) { + // read from primaries by default + o.db.read_preference = 'primary'; + } + + // mongoose creates its own ObjectIds + o.db.forceServerObjectId = false; + + // default safe using new nomenclature + if (!('journal' in o.db || 'j' in o.db || + 'fsync' in o.db || 'safe' in o.db || 'w' in o.db)) { + o.db.w = 1; + } + + validate(o); + return o; +} + +/*! + * Validates the driver db options. + * + * @param {Object} o + */ + +function validate (o) { + if (-1 === o.db.w || 0 === o.db.w) { + if (o.db.journal || o.db.fsync || o.db.safe) { + throw new Error( + 'Invalid writeConcern: ' + + 'w set to -1 or 0 cannot be combined with safe|fsync|journal'); + } + } +} + +/*! + * Module exports. + */ + +module.exports = NativeConnection; diff --git a/test/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js b/test/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js new file mode 100644 index 0000000..3c46c93 --- /dev/null +++ b/test/node_modules/mongoose/lib/drivers/node-mongodb-native/objectid.js @@ -0,0 +1,29 @@ + +/*! + * [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) ObjectId + * @constructor NodeMongoDbObjectId + * @see ObjectId + */ + +var ObjectId = require('mongodb').BSONPure.ObjectID; + +/*! + * ignore + */ + +var ObjectIdToString = ObjectId.toString.bind(ObjectId); +module.exports = exports = ObjectId; + +ObjectId.fromString = function(str){ + // patch native driver bug in V0.9.6.4 + if (!('string' === typeof str && 24 === str.length)) { + throw new Error("Invalid ObjectId"); + } + + return ObjectId.createFromHexString(str); +}; + +ObjectId.toString = function(oid){ + if (!arguments.length) return ObjectIdToString(); + return oid.toHexString(); +}; diff --git a/test/node_modules/mongoose/lib/error.js b/test/node_modules/mongoose/lib/error.js new file mode 100644 index 0000000..9216cd9 --- /dev/null +++ b/test/node_modules/mongoose/lib/error.js @@ -0,0 +1,38 @@ + +/** + * Mongoose error + * + * @api private + * @inherits Error https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error + */ + +function MongooseError (msg) { + Error.call(this); + Error.captureStackTrace(this, arguments.callee); + this.message = msg; + this.name = 'MongooseError'; +}; + +/*! + * Inherits from Error. + */ + +MongooseError.prototype.__proto__ = Error.prototype; + +/*! + * Module exports. + */ + +module.exports = exports = MongooseError; + +/*! + * Expose subclasses + */ + +MongooseError.CastError = require('./errors/cast'); +MongooseError.DocumentError = require('./errors/document'); +MongooseError.ValidationError = require('./errors/validation') +MongooseError.ValidatorError = require('./errors/validator') +MongooseError.VersionError =require('./errors/version') +MongooseError.OverwriteModelError = require('./errors/overwriteModel') +MongooseError.MissingSchemaError = require('./errors/missingSchema') diff --git a/test/node_modules/mongoose/lib/errors/cast.js b/test/node_modules/mongoose/lib/errors/cast.js new file mode 100644 index 0000000..055063a --- /dev/null +++ b/test/node_modules/mongoose/lib/errors/cast.js @@ -0,0 +1,35 @@ +/*! + * Module dependencies. + */ + +var MongooseError = require('../error'); + +/** + * Casting Error constructor. + * + * @param {String} type + * @param {String} value + * @inherits MongooseError + * @api private + */ + +function CastError (type, value, path) { + MongooseError.call(this, 'Cast to ' + type + ' failed for value "' + value + '" at path "' + path + '"'); + Error.captureStackTrace(this, arguments.callee); + this.name = 'CastError'; + this.type = type; + this.value = value; + this.path = path; +}; + +/*! + * Inherits from MongooseError. + */ + +CastError.prototype.__proto__ = MongooseError.prototype; + +/*! + * exports + */ + +module.exports = CastError; diff --git a/test/node_modules/mongoose/lib/errors/document.js b/test/node_modules/mongoose/lib/errors/document.js new file mode 100644 index 0000000..6955256 --- /dev/null +++ b/test/node_modules/mongoose/lib/errors/document.js @@ -0,0 +1,32 @@ + +/*! + * Module requirements + */ + +var MongooseError = require('../error') + +/** + * Document Error + * + * @param {String} msg + * @inherits MongooseError + * @api private + */ + +function DocumentError (msg) { + MongooseError.call(this, msg); + Error.captureStackTrace(this, arguments.callee); + this.name = 'DocumentError'; +}; + +/*! + * Inherits from MongooseError. + */ + +DocumentError.prototype.__proto__ = MongooseError.prototype; + +/*! + * Module exports. + */ + +module.exports = exports = DocumentError; diff --git a/test/node_modules/mongoose/lib/errors/missingSchema.js b/test/node_modules/mongoose/lib/errors/missingSchema.js new file mode 100644 index 0000000..02a02ee --- /dev/null +++ b/test/node_modules/mongoose/lib/errors/missingSchema.js @@ -0,0 +1,32 @@ + +/*! + * Module dependencies. + */ + +var MongooseError = require('../error'); + +/*! + * MissingSchema Error constructor. + * + * @inherits MongooseError + */ + +function MissingSchemaError (name) { + var msg = 'Schema hasn\'t been registered for model "' + name + '".\n' + + 'Use mongoose.model(name, schema)'; + MongooseError.call(this, msg); + Error.captureStackTrace(this, arguments.callee); + this.name = 'MissingSchemaError'; +}; + +/*! + * Inherits from MongooseError. + */ + +MissingSchemaError.prototype.__proto__ = MongooseError.prototype; + +/*! + * exports + */ + +module.exports = MissingSchemaError; diff --git a/test/node_modules/mongoose/lib/errors/overwriteModel.js b/test/node_modules/mongoose/lib/errors/overwriteModel.js new file mode 100644 index 0000000..2591581 --- /dev/null +++ b/test/node_modules/mongoose/lib/errors/overwriteModel.js @@ -0,0 +1,30 @@ + +/*! + * Module dependencies. + */ + +var MongooseError = require('../error'); + +/*! + * OverwriteModel Error constructor. + * + * @inherits MongooseError + */ + +function OverwriteModelError (name) { + MongooseError.call(this, 'Cannot overwrite `' + name + '` model once compiled.'); + Error.captureStackTrace(this, arguments.callee); + this.name = 'OverwriteModelError'; +}; + +/*! + * Inherits from MongooseError. + */ + +OverwriteModelError.prototype.__proto__ = MongooseError.prototype; + +/*! + * exports + */ + +module.exports = OverwriteModelError; diff --git a/test/node_modules/mongoose/lib/errors/validation.js b/test/node_modules/mongoose/lib/errors/validation.js new file mode 100644 index 0000000..08f2aaa --- /dev/null +++ b/test/node_modules/mongoose/lib/errors/validation.js @@ -0,0 +1,43 @@ + +/*! + * Module requirements + */ + +var MongooseError = require('../error') + +/** + * Document Validation Error + * + * @api private + * @param {Document} instance + * @inherits MongooseError + */ + +function ValidationError (instance) { + MongooseError.call(this, "Validation failed"); + Error.captureStackTrace(this, arguments.callee); + this.name = 'ValidationError'; + this.errors = instance.errors = {}; +}; + +/** + * Console.log helper + */ + +ValidationError.prototype.toString = function () { + return this.name + ': ' + Object.keys(this.errors).map(function (key) { + return String(this.errors[key]); + }, this).join(', '); +}; + +/*! + * Inherits from MongooseError. + */ + +ValidationError.prototype.__proto__ = MongooseError.prototype; + +/*! + * Module exports + */ + +module.exports = exports = ValidationError; diff --git a/test/node_modules/mongoose/lib/errors/validator.js b/test/node_modules/mongoose/lib/errors/validator.js new file mode 100644 index 0000000..5499bee --- /dev/null +++ b/test/node_modules/mongoose/lib/errors/validator.js @@ -0,0 +1,45 @@ +/*! + * Module dependencies. + */ + +var MongooseError = require('../error'); + +/** + * Schema validator error + * + * @param {String} path + * @param {String} msg + * @inherits MongooseError + * @api private + */ + +function ValidatorError (path, type) { + var msg = type + ? '"' + type + '" ' + : ''; + MongooseError.call(this, 'Validator ' + msg + 'failed for path ' + path); + Error.captureStackTrace(this, arguments.callee); + this.name = 'ValidatorError'; + this.path = path; + this.type = type; +}; + +/*! + * toString helper + */ + +ValidatorError.prototype.toString = function () { + return this.message; +} + +/*! + * Inherits from MongooseError + */ + +ValidatorError.prototype.__proto__ = MongooseError.prototype; + +/*! + * exports + */ + +module.exports = ValidatorError; diff --git a/test/node_modules/mongoose/lib/errors/version.js b/test/node_modules/mongoose/lib/errors/version.js new file mode 100644 index 0000000..b2388aa --- /dev/null +++ b/test/node_modules/mongoose/lib/errors/version.js @@ -0,0 +1,31 @@ + +/*! + * Module dependencies. + */ + +var MongooseError = require('../error'); + +/** + * Version Error constructor. + * + * @inherits MongooseError + * @api private + */ + +function VersionError () { + MongooseError.call(this, 'No matching document found.'); + Error.captureStackTrace(this, arguments.callee); + this.name = 'VersionError'; +}; + +/*! + * Inherits from MongooseError. + */ + +VersionError.prototype.__proto__ = MongooseError.prototype; + +/*! + * exports + */ + +module.exports = VersionError; diff --git a/test/node_modules/mongoose/lib/index.js b/test/node_modules/mongoose/lib/index.js new file mode 100644 index 0000000..e4d4a9f --- /dev/null +++ b/test/node_modules/mongoose/lib/index.js @@ -0,0 +1,547 @@ + +/*! + * Module dependencies. + */ + +var Schema = require('./schema') + , SchemaType = require('./schematype') + , VirtualType = require('./virtualtype') + , SchemaTypes = Schema.Types + , SchemaDefaults = require('./schemadefault') + , Types = require('./types') + , Query = require('./query') + , Promise = require('./promise') + , Model = require('./model') + , Document = require('./document') + , utils = require('./utils') + , format = utils.toCollectionName + , mongodb = require('mongodb') + +/** + * Mongoose constructor. + * + * The exports object of the `mongoose` module is an instance of this class. + * Most apps will only use this one instance. + * + * @api public + */ + +function Mongoose () { + this.connections = []; + this.plugins = []; + this.models = {}; + this.modelSchemas = {}; + this.options = {}; + this.createConnection(); // default connection +}; + +/** + * Sets mongoose options + * + * ####Example: + * + * mongoose.set('test', value) // sets the 'test' option to `value` + * + * @param {String} key + * @param {String} value + * @api public + */ + +Mongoose.prototype.set = function (key, value) { + if (arguments.length == 1) + return this.options[key]; + this.options[key] = value; + return this; +}; + +/** + * Gets mongoose options + * + * ####Example: + * + * mongoose.get('test') // returns the 'test' value + * + * @param {String} key + * @method get + * @api public + */ + +Mongoose.prototype.get = Mongoose.prototype.set; + +/*! + * ReplSet connection string check. + */ + +var rgxReplSet = /^.+,.+$/; + +/** + * Creates a Connection instance. + * + * Each `connection` instance maps to a single database. This method is helpful when mangaging multiple db connections. + * + * If arguments are passed, they are proxied to either [Connection#open](#connection_Connection-open) or [Connection#openSet](#connection_Connection-openSet) appropriately. This means we can pass `db`, `server`, and `replset` options to the driver. + * + * _Options passed take precedence over options included in connection strings._ + * + * ####Example: + * + * // with mongodb:// URI + * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database'); + * + * // and options + * var opts = { db: { native_parser: true }} + * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database', opts); + * + * // replica sets + * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database,mongodb://anotherhost:port,mongodb://yetanother:port'); + * + * // and options + * var opts = { replset: { strategy: 'ping', rs_name: 'testSet' }} + * db = mongoose.createConnection('mongodb://user:pass@localhost:port/database,mongodb://anotherhost:port,mongodb://yetanother:port', opts); + * + * // with [host, database_name[, port] signature + * db = mongoose.createConnection('localhost', 'database', port) + * + * // and options + * var opts = { server: { auto_reconnect: false }, user: 'username', pass: 'mypassword' } + * db = mongoose.createConnection('localhost', 'database', port, opts) + * + * // initialize now, connect later + * db = mongoose.createConnection(); + * db.open('localhost', 'database', port, [opts]); + * + * @param {String} [uri] a mongodb:// URI + * @param {Object} [options] options to pass to the driver + * @see Connection#open #connection_Connection-open + * @see Connection#openSet #connection_Connection-openSet + * @return {Connection} the created Connection object + * @api public + */ + +Mongoose.prototype.createConnection = function () { + var conn = new Connection(this); + this.connections.push(conn); + + if (arguments.length) { + if (rgxReplSet.test(arguments[0])) { + conn.openSet.apply(conn, arguments); + } else { + conn.open.apply(conn, arguments); + } + } + + return conn; +}; + +/** + * Opens the default mongoose connection. + * + * If arguments are passed, they are proxied to either [Connection#open](#connection_Connection-open) or [Connection#openSet](#connection_Connection-openSet) appropriately. + * + * _Options passed take precedence over options included in connection strings._ + * + * @see Mongoose#createConnection #index_Mongoose-createConnection + * @api public + * @return {Mongoose} this + */ + +Mongoose.prototype.connect = function () { + var conn = this.connection; + + if (rgxReplSet.test(arguments[0])) { + conn.openSet.apply(conn, arguments); + } else { + conn.open.apply(conn, arguments); + } + + return this; +}; + +/** + * Disconnects all connections. + * + * @param {Function} [fn] called after all connection close. + * @return {Mongoose} this + * @api public + */ + +Mongoose.prototype.disconnect = function (fn) { + var count = this.connections.length + , error + + this.connections.forEach(function(conn){ + conn.close(function(err){ + if (error) return; + + if (err) { + error = err; + if (fn) return fn(err); + throw err; + } + + if (fn) + --count || fn(); + }); + }); + return this; +}; + +/** + * Defines a model or retrieves it. + * + * Models defined on the `mongoose` instance are available to all connection created by the same `mongoose` instance. + * + * ####Example: + * + * var mongoose = require('mongoose'); + * + * // define an Actor model with this mongoose instance + * mongoose.model('Actor', new Schema({ name: String })); + * + * // create a new connection + * var conn = mongoose.createConnection(..); + * + * // retrieve the Actor model + * var Actor = conn.model('Actor'); + * + * _When no `collection` argument is passed, Mongoose produces a collection name by passing the model `name` to the [utils.toCollectionName](#utils.toCollectionName) method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option._ + * + * ####Example: + * + * var schema = new Schema({ name: String }, { collection: 'actor' }); + * + * // or + * + * schema.set('collection', 'actor'); + * + * // or + * + * var collectionName = 'actor' + * var M = mongoose.model('Actor', schema, collectionName) + * + * @param {String} name model name + * @param {Schema} [schema] + * @param {String} [collection] name (optional, induced from model name) + * @param {Boolean} [skipInit] whether to skip initialization (defaults to false) + * @api public + */ + +Mongoose.prototype.model = function (name, schema, collection, skipInit) { + if (!(schema instanceof Schema)) { + collection = schema; + schema = false; + } + + if ('boolean' === typeof collection) { + skipInit = collection; + collection = null; + } + + // handle internal options from connection.model() + var options; + if (skipInit && utils.isObject(skipInit)) { + options = skipInit; + skipInit = true; + } else { + options = {}; + } + + // look up schema for the collection. this might be a + // default schema like system.indexes stored in SchemaDefaults. + if (!this.modelSchemas[name]) { + if (!schema && name in SchemaDefaults) { + schema = SchemaDefaults[name]; + } + + if (schema) { + // cache it so we only apply plugins once + this.modelSchemas[name] = schema; + this._applyPlugins(schema); + } else { + throw new mongoose.Error.MissingSchemaError(name); + } + } + + var model; + var sub; + + // connection.model() may be passing a different schema for + // an existing model name. in this case don't read from cache. + if (this.models[name] && false !== options.cache) { + if (schema instanceof Schema && schema != this.models[name].schema) { + throw new mongoose.Error.OverwriteModelError(name); + } + + if (collection) { + // subclass current model with alternate collection + model = this.models[name]; + schema = model.prototype.schema; + sub = model.__subclass(this.connection, schema, collection); + // do not cache the sub model + return sub; + } + + return this.models[name]; + } + + // ensure a schema exists + if (!schema) { + schema = this.modelSchemas[name]; + if (!schema) { + throw new mongoose.Error.MissingSchemaError(name); + } + } + + if (!collection) { + collection = schema.get('collection') || format(name); + } + + var connection = options.connection || this.connection; + model = Model.compile(name, schema, collection, connection, this); + + if (!skipInit) { + model.init(); + } + + if (false === options.cache) { + return model; + } + + return this.models[name] = model; +} + +/** + * Applies global plugins to `schema`. + * + * @param {Schema} schema + * @api private + */ + +Mongoose.prototype._applyPlugins = function (schema) { + for (var i = 0, l = this.plugins.length; i < l; i++) { + schema.plugin(this.plugins[i][0], this.plugins[i][1]); + } +} + +/** + * Declares a global plugin executed on all Schemas. + * + * Equivalent to calling `.plugin(fn)` on each Schema you create. + * + * @param {Function} fn plugin callback + * @param {Object} [opts] optional options + * @return {Mongoose} this + * @see plugins ./plugins.html + * @api public + */ + +Mongoose.prototype.plugin = function (fn, opts) { + this.plugins.push([fn, opts]); + return this; +}; + +/** + * The default connection of the mongoose module. + * + * ####Example: + * + * var mongoose = require('mongoose'); + * mongoose.connect(...); + * mongoose.connection.on('error', cb); + * + * This is the connection used by default for every model created using [mongoose.model](#index_Mongoose-model). + * + * @property connection + * @return {Connection} + * @api public + */ + +Mongoose.prototype.__defineGetter__('connection', function(){ + return this.connections[0]; +}); + +/*! + * Driver depentend APIs + */ + +var driver = global.MONGOOSE_DRIVER_PATH || './drivers/node-mongodb-native'; + +/*! + * Connection + */ + +var Connection = require(driver + '/connection'); + +/*! + * Collection + */ + +var Collection = require(driver + '/collection'); + +/** + * The exports object is an instance of Mongoose. + * + * @api public + */ + +module.exports = exports = new Mongoose; +var mongoose = module.exports; + +/** + * The Mongoose Collection constructor + * + * @api public + */ + +mongoose.Collection = Collection; + +/** + * The Mongoose Connection constructor + * + * @api public + */ + +mongoose.Connection = Connection; + +/** + * Mongoose version + * + * @api public + */ + +mongoose.version = JSON.parse( + require('fs').readFileSync(__dirname + '/../package.json', 'utf8') +).version; + +/** + * The Mongoose constructor + * + * The exports of the mongoose module is an instance of this class. + * + * ####Example: + * + * var mongoose = require('mongoose'); + * var mongoose2 = new mongoose.Mongoose(); + * + * @api public + */ + +mongoose.Mongoose = Mongoose; + +/** + * The Mongoose Schema constructor + * + * ####Example: + * + * var mongoose = require('mongoose'); + * var Schema = mongoose.Schema; + * var CatSchema = new Schema(..); + * + * @api public + */ + +mongoose.Schema = Schema; + +/** + * The Mongoose SchemaType constructor. + * + * @api public + */ + +mongoose.SchemaType = SchemaType; + +/** + * The various Mongoose SchemaTypes. + * + * ####Note: + * + * _Alias of mongoose.Schema.Types for backwards compatibility._ + * + * @see Schema.SchemaTypes #schema_Schema-Types + * @api public + */ + +mongoose.SchemaTypes = Schema.Types; + +/** + * The Mongoose VirtualType constructor. + * + * @api public + */ + +mongoose.VirtualType = VirtualType; + +/** + * The various Mongoose Types. + * + * ####Example: + * + * var mongoose = require('mongoose'); + * var array = mongoose.Types.Array; + * + * ####Types: + * + * - Array + * - Buffer + * - Document + * - Embedded + * - DocumentArray + * - ObjectId + * + * Using this exposed access to the `ObjectId` type, we can construct ids on demand. + * + * var ObjectId = mongoose.Types.ObjectId; + * var id1 = new ObjectId; + * + * @api public + */ + +mongoose.Types = Types; + +/** + * The Mongoose Query constructor. + * + * @api public + */ + +mongoose.Query = Query; + +/** + * The Mongoose Promise constructor. + * + * @api public + */ + +mongoose.Promise = Promise; + +/** + * The Mongoose Model constructor. + * + * @api public + */ + +mongoose.Model = Model; + +/** + * The Mongoose Document constructor. + * + * @api public + */ + +mongoose.Document = Document; + +/** + * The MongooseError constructor. + * + * @api public + */ + +mongoose.Error = require('./error'); + +/** + * The node-mongodb-native driver Mongoose uses. + * + * @api public + */ + +mongoose.mongo = require('mongodb'); diff --git a/test/node_modules/mongoose/lib/model.js b/test/node_modules/mongoose/lib/model.js new file mode 100644 index 0000000..ec4aa1b --- /dev/null +++ b/test/node_modules/mongoose/lib/model.js @@ -0,0 +1,1745 @@ +/*! + * Module dependencies. + */ + +var Document = require('./document') + , MongooseArray = require('./types/array') + , MongooseBuffer = require('./types/buffer') + , MongooseError = require('./error') + , VersionError = require('./errors/version') + , Query = require('./query') + , Schema = require('./schema') + , utils = require('./utils') + , isMongooseObject = utils.isMongooseObject + , EventEmitter = utils.EventEmitter + , merge = utils.merge + , Promise = require('./promise') + , tick = utils.tick + +var VERSION_WHERE = 1 + , VERSION_INC = 2 + , VERSION_ALL = VERSION_WHERE | VERSION_INC; + +/** + * Model constructor + * + * @param {Object} doc values to with which to create the document + * @inherits Document + * @event `error`: If listening to this Model event, it is emitted when a document was saved without passing a callback and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model. + * @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event. + * @api public + */ + +function Model (doc, fields, skipId) { + Document.call(this, doc, fields, skipId); +}; + +/*! + * Inherits from Document. + * + * All Model.prototype features are available on + * top level (non-sub) documents. + */ + +Model.prototype.__proto__ = Document.prototype; + +/** + * Connection the model uses. + * + * @api public + * @property db + */ + +Model.prototype.db; + +/** + * Collection the model uses. + * + * @api public + * @property collection + */ + +Model.prototype.collection; + +/** + * The name of the model + * + * @api public + * @property modelName + */ + +Model.prototype.modelName; + +/** + * Returns what paths can be populated + * + * @param {Query} query object + * @return {Object|undefined} population paths + * @api private + */ + +Model.prototype._getPopulationKeys = function getPopulationKeys (query) { + if (!(query && query.options.populate)) return; + + var names = Object.keys(query.options.populate) + , n = names.length + , name + , paths = {} + , hasKeys + , schema + + while (n--) { + name = names[n]; + schema = this.schema.path(name); + hasKeys = true; + + if (!schema) { + // if the path is not recognized, it's potentially embedded docs + // walk path atoms from right to left to find a matching path + var pieces = name.split('.') + , i = pieces.length; + + while (i--) { + var path = pieces.slice(0, i).join('.') + , pathSchema = this.schema.path(path); + + // loop until we find an array schema + if (pathSchema && pathSchema.caster) { + if (!paths[path]) { + paths[path] = { sub: {} }; + } + + paths[path].sub[pieces.slice(i).join('.')] = query.options.populate[name]; + hasKeys || (hasKeys = true); + break; + } + } + } else { + paths[name] = query.options.populate[name]; + hasKeys || (hasKeys = true); + } + } + + return hasKeys && paths; +}; + +/** + * Populates an object + * + * @param {SchemaType} schema type for the oid + * @param {Object} oid object id or array of object ids + * @param {Object} query object specifying query conditions, fields, and options + * @param {Function} fn + * @api private + */ + +Model.prototype._populate = function populate (schema, oid, query, fn) { + if (!Array.isArray(oid)) { + var conditions = query.conditions || {}; + conditions._id = oid; + + return this + .db.model(query.model || schema.options.ref) + .findOne(conditions, query.fields, query.options, fn); + } + + if (!oid.length) { + return fn(null, oid); + } + + var model = this.db.model(query.model || schema.caster.options.ref) + , conditions = query && query.conditions || {}; + + conditions._id || (conditions._id = { $in: oid }); + + model.find(conditions, query.fields, query.options, function (err, docs) { + if (err) return fn(err); + + // user specified sort order? + if (query.options && query.options.sort) { + return fn(null, docs); + } + + // put back in original id order (using a hash reduces complexity from n*n to 2n) + var docHash = {}; + docs.forEach(function (doc) { + docHash[doc._id] = doc; + }); + + var arr = []; + oid.forEach(function (id) { + if (id in docHash) arr.push(docHash[id]); + }); + + fn(null, arr); + }); +}; + +/** + * Performs auto-population of relations. + * + * @param {Object} doc document returned by mongo + * @param {Query} query query that originated the initialization + * @param {Function} fn + * @api private + */ + +Model.prototype.init = function init (doc, query, fn) { + if ('function' == typeof query) { + fn = query; + query = null; + } + + var populate = this._getPopulationKeys(query); + + if (!populate) { + return Document.prototype.init.call(this, doc, fn); + } + + // population from other models is necessary + var self = this; + + init(doc, '', function (err) { + if (err) return fn(err); + Document.prototype.init.call(self, doc, fn); + }); + + return this; + + function init (obj, prefix, fn) { + prefix = prefix || ''; + + var keys = Object.keys(obj) + , len = keys.length; + + return next(); + + function next () { + if (--len < 0) return fn(); + + var i = keys[len] + , path = prefix + i + , schema = self.schema.path(path) + , total = 0 + , inline = false + , poppath + + if (!schema && obj[i] && 'Object' === obj[i].constructor.name) { + // assume nested object + return init(obj[i], path + '.', next); + } + + if (!(obj[i] && schema && populate[path])) return next(); + + // this query object is re-used and passed around. we clone + // it to prevent query condition contamination between + // one populate call to the next. + poppath = utils.clone(populate[path]); + + if (poppath.sub) { + obj[i].forEach(function (subobj) { + inline = true; + + var pkeys = Object.keys(poppath.sub) + , pi = pkeys.length + , key + + while (pi--) { + key = pkeys[pi]; + + if (subobj[key]) (function (key) { + total++; + self._populate(schema.schema.path(key), subobj[key], poppath.sub[key], done); + function done (err, doc) { + if (err) return error(err); + subobj[key] = doc; + if (--total < 1 && !inline) { + next(); + } + } + })(key); + } + }); + + inline = false; + + if (0 === total) return next(); + + } else { + self._populate(schema, obj[i], poppath, function (err, doc) { + if (err) return error(err); + obj[i] = doc; + next(); + }); + } + }; + }; + + function error (err) { + if (error.err) return; + fn(error.err = err); + } +}; + +/*! + * Handles doc.save() callbacks + */ + +function handleSave (promise, self) { + return tick(function handleSave (err, result) { + if (err) { + // If the initial insert fails provide a second chance. + // (If we did this all the time we would break updates) + if (self._inserting) { + self.isNew = true; + self.emit('isNew', true); + } + promise.error(err); + promise = self = null; + return; + } + + self._storeShard(); + + var numAffected; + if (result) { + // when inserting, the array of created docs is returned + numAffected = result.length + ? result.length + : result; + } else { + numAffected = 0; + } + + // was this an update that required a version bump? + if (self.__version && !self._inserting) { + var doIncrement = VERSION_INC === (VERSION_INC & self.__version); + self.__version = undefined; + + // increment version if was successful + if (numAffected > 0) { + if (doIncrement) { + var key = self.schema.options.versionKey; + var version = self.getValue(key) | 0; + self.setValue(key, version + 1); + } + } else { + // the update failed. pass an error back + promise.error(new VersionError); + promise = self = null; + return; + } + } + + self.emit('save', self, numAffected); + promise.complete(self, numAffected); + promise = self = null; + }); +} + +/** + * Saves this document. + * + * ####Example: + * + * product.sold = Date.now(); + * product.save(function (err, product) { + * if (err) .. + * }) + * + * The `fn` callback is optional. If no `fn` is passed and validation fails, the validation error will be emitted on the connection used to create this model. + * + * var db = mongoose.createConnection(..); + * var schema = new Schema(..); + * var Product = db.model('Product', schema); + * + * db.on('error', handleError); + * + * However, if you desire more local error handling you can add an `error` listener to the model and handle errors there instead. + * + * Product.on('error', handleError); + * + * @param {Function} [fn] optional callback + * @api public + * @see middleware http://mongoosejs.com/docs/middleware.html + */ + +Model.prototype.save = function save (fn) { + var promise = new Promise(fn) + , complete = handleSave(promise, this) + , options = {} + + if (this.schema.options.safe) { + options.safe = this.schema.options.safe; + } + + if (this.isNew) { + // send entire doc + var obj = this.toObject({ depopulate: 1 }); + this._version(true, obj); + this.collection.insert(obj, options, complete); + this._reset(); + this.isNew = false; + this.emit('isNew', false); + // Make it possible to retry the insert + this._inserting = true; + + } else { + // Make sure we don't treat it as a new object on error, + // since it already exists + this._inserting = false; + + var delta = this._delta(); + if (delta) { + var where = this._where(delta[0]); + this.collection.update(where, delta[1], options, complete); + } else { + process.nextTick(function () { + complete(null); + }) + } + + this._reset(); + this.emit('isNew', false); + } +}; + +/*! + * Apply the operation to the delta (update) clause as + * well as track versioning for our where clause. + * + * @param {Document} self + * @param {Object} where + * @param {Object} delta + * @param {Object} data + * @param {Mixed} val + * @param {String} [operation] + */ + +function operand (self, where, delta, data, val, op) { + // delta + op || (op = '$set'); + if (!delta[op]) delta[op] = {}; + delta[op][data.path] = val; + + // disabled versioning? + if (false === self.schema.options.versionKey) return; + + // already marked for versioning? + if (VERSION_ALL === (VERSION_ALL & self.__version)) return; + + switch (op) { + case '$set': + case '$unset': + case '$pop': + case '$pull': + case '$pullAll': + case '$push': + case '$pushAll': + case '$addToSet': + break; + default: + // nothing to do + return; + } + + // ensure updates sent with positional notation are + // editing the correct array element. + // only increment the version if an array position changes. + // modifying elements of an array is ok if position does not change. + + if ('$push' == op || '$pushAll' == op || '$addToSet' == op) { + self.__version = VERSION_INC; + } + else if (/^\$p/.test(op)) { + // potentially changing array positions + self.increment(); + } + else if (Array.isArray(val)) { + // $set an array + self.increment(); + } + // now handling $set, $unset + else if (/\.\d+/.test(data.path)) { + // subpath of array + self.__version = VERSION_WHERE; + } +} + +/*! + * Compiles an update and where clause for a `val` with _atomics. + * + * @param {Document} self + * @param {Object} where + * @param {Object} delta + * @param {Object} data + * @param {Array} val + */ + +function handleAtomics (self, where, delta, data, val) { + if (delta.$set && delta.$set[data.path]) { + // $set has precedence over other atomics + return; + } + + var atomics = val._atomics + , ops = Object.keys(atomics) + , schema = data.schema + , path = data.path + , i = ops.length + , val + , op; + + if (0 === i) { + // $set + + if (isMongooseObject(val)) { + val = val.toObject({ depopulate: 1 }); + } else if (val.valueOf) { + val = val.valueOf(); + } + + return operand(self, where, delta, data, val); + } + + while (i--) { + op = ops[i]; + val = atomics[op]; + if (isMongooseObject(val)) { + val = val.toObject({ depopulate: 1 }) + } else if (Array.isArray(val)) { + val = val.map(function (mem) { + return isMongooseObject(mem) + ? mem.toObject({ depopulate: 1 }) + : mem; + }) + } else if (val.valueOf) { + val = val.valueOf() + } + + if ('$addToSet' === op) + val = { $each: val }; + + operand(self, where, delta, data, val, op); + } +} + +/** + * Produces a special query document of the modified properties used in updates. + * + * @api private + */ + +Model.prototype._delta = function _delta () { + var dirty = this._dirty(); + if (!dirty.length) return; + + var self = this + , where = {} + , delta = {} + , len = dirty.length + , d = 0 + , val + , obj + + for (; d < len; ++d) { + var data = dirty[d] + var value = data.value + var schema = data.schema + + if (undefined === value) { + operand(self, where, delta, data, 1, '$unset'); + + } else if (null === value) { + operand(self, where, delta, data, null); + + } else if (value._path && value._atomics) { + // arrays and other custom types (support plugins etc) + handleAtomics(self, where, delta, data, value); + + } else if (value._path && Buffer.isBuffer(value)) { + // MongooseBuffer + value = value.toObject(); + operand(self, where, delta, data, value); + + } else { + value = utils.clone(value); + operand(self, where, delta, data, value); + } + } + + if (this.__version) { + this._version(where, delta); + } + + return [where, delta]; +} + +/** + * Appends versioning to the where and update clauses. + * + * @api private + */ + +Model.prototype._version = function _version (where, delta) { + var key = this.schema.options.versionKey; + + if (true === where) { + // this is an insert + if (key) this.setValue(key, delta[key] = 0); + return; + } + + // updates + + // only apply versioning if our versionKey was selected. else + // there is no way to select the correct version. we could fail + // fast here and force them to include the versionKey but + // thats a bit intrusive. can we do this automatically? + // TODO fail fast option? + if (!this.isSelected(key)) { + return; + } + + // $push $addToSet don't need the where clause set + if (VERSION_WHERE === (VERSION_WHERE & this.__version)) { + where[key] = this.getValue(key); + } + + if (VERSION_INC === (VERSION_INC & this.__version)) { + delta.$inc || (delta.$inc = {}); + delta.$inc[key] = 1; + } +} + +/** + * Signal that we desire an increment of this documents version. + * + * @see versionKeys http://mongoosejs.com/docs/guide.html#versionKey + * @api public + */ + +Model.prototype.increment = function increment () { + this.__version = VERSION_ALL; + return this; +} + +/** + * Returns a query object which applies shardkeys if they exist. + * + * @api private + */ + +Model.prototype._where = function _where (where) { + where || (where = {}); + + var paths + , len + + if (this._shardval) { + paths = Object.keys(this._shardval) + len = paths.length + + for (var i = 0; i < len; ++i) { + where[paths[i]] = this._shardval[paths[i]]; + } + } + + where._id = this._doc._id; + return where; +} + +/** + * Removes this document from the db. + * + * ####Example: + * + * product.remove(function (err, product) { + * if (err) return handleError(err); + * Product.findById(product._id, function (err, product) { + * console.log(product) // null + * }) + * }) + * + * @param {Function} [fn] optional callback + * @api public + */ + +Model.prototype.remove = function remove (fn) { + if (this._removing) { + this._removing.addBack(fn); + return this; + } + + var promise = this._removing = new Promise(fn) + , where = this._where() + , self = this + , options = {} + + if (this.schema.options.safe) { + options.safe = this.schema.options.safe; + } + + this.collection.remove(where, options, tick(function (err) { + if (err) { + promise.error(err); + promise = self = self._removing = where = options = null; + return; + } + self.emit('remove', self); + promise.complete(); + promise = self = where = options = null; + })); + + return this; +}; + +/** + * Register hooks override + * + * @api private + */ + +Model.prototype._registerHooks = function registerHooks () { + Document.prototype._registerHooks.call(this); +}; + +/** + * Returns another Model instance. + * + * ####Example: + * + * var doc = new Tank; + * doc.model('User').findById(id, callback); + * + * @param {String} name model name + * @api public + */ + +Model.prototype.model = function model (name) { + return this.db.model(name); +}; + +// Model (class) features + +/*! + * Give the constructor the ability to emit events. + */ + +for (var i in EventEmitter.prototype) + Model[i] = EventEmitter.prototype[i]; + +/** + * Called when the model compiles. + * + * @api private + */ + +Model.init = function init () { + if (this.schema.options.autoIndex) { + this.ensureIndexes(); + } + + this.schema.emit('init', this); +}; + +/** + * Sends `ensureIndex` commands to mongo for each index declared in the schema. + * + * After completion, an `index` event is emitted on this `Model` passing an error if one occurred. + * + * _NOTE: It is not recommended that you run this in production. Index creation may impact database performance depending on your load. Use with caution._ + * + * ####Example: + * + * Event.ensureIndexes(function (err) { + * if (err) return handleError(err); + * }); + * + * @param {Function} [cb] optional callback + * @api public + */ + +Model.ensureIndexes = function ensureIndexes (cb) { + var indexes = this.schema.indexes(); + if (!indexes.length) { + return cb && cb(); + } + + var self = this + , safe = self.schema.options.safe + , count = indexes.length + , error + + indexes.forEach(function (index) { + var options = index[1]; + options.safe = safe; + self.collection.ensureIndex(index[0], options, tick(function (err) { + if (err) error = err; + if (--count) return; + + self.emit('index', error); + cb && cb(error); + })); + }); +} + +/** + * Schema the model uses. + * + * @property schema + * @receiver Model + * @api public + */ + +Model.schema; + +/** + * Database instance the model uses. + * + * @property db + * @receiver Model + * @api public + */ + +Model.db; + +/** + * Collection the model uses. + * + * @property collection + * @receiver Model + * @api public + */ + +Model.collection; + +/** + * Base Mongoose instance the model uses. + * + * @property base + * @receiver Model + * @api public + */ + +Model.base; + +/** + * Removes documents from the collection. + * + * ####Note: + * + * To remove documents without waiting for a response from MongoDB, do not pass a `callback`, then call `exec` on the returned [Query](#query-js): + * + * Comment.remove({ _id: id }).exec(); + * + * ####Note: + * + * This method sends a remove command directly to MongoDB, no Mongoose documents are involved. Because no Mongoose documents are involved, _no middleware (hooks) are executed_. + * + * @param {Object} conditions + * @param {Function} [callback] + * @return {Query} + * @api public + */ + +Model.remove = function remove (conditions, callback) { + if ('function' === typeof conditions) { + callback = conditions; + conditions = {}; + } + + var query = new Query(conditions).bind(this, 'remove'); + + if ('undefined' === typeof callback) + return query; + + this._applyNamedScope(query); + return query.remove(callback); +}; + +/** + * Finds documents + * + * ####Examples: + * + * // retrieve only certain keys + * MyModel.find({ name: /john/i }, 'name friends', function () { }) + * + * // pass options + * MyModel.find({ name: /john/i }, null, { skip: 10 } ) + * + * @param {Object} conditions + * @param {Object} [fields] optional fields to select + * @param {Object} [options] optional + * @param {Function} [callback] + * @return {Query} + * @see field selection #query_Query-select + * @api public + */ + +Model.find = function find (conditions, fields, options, callback) { + if ('function' == typeof conditions) { + callback = conditions; + conditions = {}; + fields = null; + options = null; + } else if ('function' == typeof fields) { + callback = fields; + fields = null; + options = null; + } else if ('function' == typeof options) { + callback = options; + options = null; + } + + var query = new Query(conditions, options); + query.bind(this, 'find'); + query.select(fields); + + if ('undefined' === typeof callback) + return query; + + this._applyNamedScope(query); + return query.find(callback); +}; + +/** + * Merges the current named scope query into `query`. + * + * @param {Query} query + * @return {Query} + * @api private + */ + +Model._applyNamedScope = function _applyNamedScope (query) { + var cQuery = this._cumulativeQuery; + + if (cQuery) { + merge(query._conditions, cQuery._conditions); + if (query._fields && cQuery._fields) + merge(query._fields, cQuery._fields); + if (query.options && cQuery.options) + merge(query.options, cQuery.options); + delete this._cumulativeQuery; + } + + return query; +} + +/** + * Finds a single document by id. + * + * The `id` is cast to an ObjectId before sending the command. + * + * ####Example: + * + * Adventure.findById(id, callback); + * + * @param {ObjectId|HexId} id objectid, or a value that can be casted to one + * @param {Object} [fields] optional fields to select + * @param {Object} [options] optional + * @param {Function} [callback] + * @return {Query} + * @see field selection #query_Query-select + * @api public + */ + +Model.findById = function findById (id, fields, options, callback) { + return this.findOne({ _id: id }, fields, options, callback); +}; + +/** + * Finds one document. + * + * The `conditions` are cast to their respective SchemaTypes before the command is sent. + * + * ####Example: + * + * Adventure.findOne({ type: 'iphone' }, 'name', { safe: true }, callback); + * + * @param {Object} conditions + * @param {Object} [fields] optional fields to select + * @param {Object} [options] optional + * @param {Function} [callback] + * @return {Query} + * @see field selection #query_Query-select + * @api public + */ + +Model.findOne = function findOne (conditions, fields, options, callback) { + if ('function' == typeof options) { + callback = options; + options = null; + } else if ('function' == typeof fields) { + callback = fields; + fields = null; + options = null; + } else if ('function' == typeof conditions) { + callback = conditions; + conditions = {}; + fields = null; + options = null; + } + + var query = new Query(conditions, options).select(fields).bind(this, 'findOne'); + + if ('undefined' == typeof callback) + return query; + + this._applyNamedScope(query); + return query.findOne(callback); +}; + +/** + * Counts number of matching documents in a database collection. + * + * ####Example: + * + * Adventure.count({ type: 'jungle' }, function (err, count) { + * if (err) .. + * console.log('there are %d jungle adventures', count); + * }); + * + * @param {Object} conditions + * @param {Function} [callback] + * @return {Query} + * @api public + */ + +Model.count = function count (conditions, callback) { + if ('function' === typeof conditions) + callback = conditions, conditions = {}; + + var query = new Query(conditions).bind(this, 'count'); + if ('undefined' == typeof callback) + return query; + + this._applyNamedScope(query); + return query.count(callback); +}; + +/** + * Executes a DISTINCT command + * + * @param {String} field + * @param {Object} [conditions] optional + * @param {Function} [callback] + * @return {Query} + * @api public + */ + +Model.distinct = function distinct (field, conditions, callback) { + var query = new Query(conditions).bind(this, 'distinct'); + if ('undefined' == typeof callback) { + query._distinctArg = field; + return query; + } + + this._applyNamedScope(query); + return query.distinct(field, callback); +}; + +/** + * Creates a Query, applies the passed conditions, and returns the Query. + * + * For example, instead of writing: + * + * User.find({age: {$gte: 21, $lte: 65}}, callback); + * + * we can instead write: + * + * User.where('age').gte(21).lte(65).exec(callback); + * + * Since the Query class also supports `where` you can continue chaining + * + * User + * .where('age').gte(21).lte(65) + * .where('name', /^b/i) + * ... etc + * + * @param {String} path + * @param {Object} [val] optional value + * @return {Query} + * @api public + */ + +Model.where = function where (path, val) { + var q = new Query().bind(this, 'find'); + return q.where.apply(q, arguments); +}; + +/** + * Creates a `Query` and specifies a `$where` condition. + * + * Sometimes you need to query for things in mongodb using a JavaScript expression. You can do so via `find({ $where: javascript })`, or you can use the mongoose shortcut method $where via a Query chain or from your mongoose Model. + * + * Blog.$where('this.comments.length > 5'); + * + * @param {String|Function} argument is a javascript string or anonymous function + * @method $where + * @memberOf Model + * @return {Query} + * @see Query.$where #query_Query-%24where + * @api public + */ + +Model.$where = function $where () { + var q = new Query().bind(this, 'find'); + return q.$where.apply(q, arguments); +}; + +/** + * Issues a mongodb findAndModify update command. + * + * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned. + * + * ####Options: + * + * - `new`: bool - true to return the modified document rather than the original. defaults to true + * - `upsert`: bool - creates the object if it doesn't exist. defaults to false. + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * - `select`: sets the document fields to return + * + * ####Examples: + * + * A.findOneAndUpdate(conditions, update, options, callback) // executes + * A.findOneAndUpdate(conditions, update, options) // returns Query + * A.findOneAndUpdate(conditions, update, callback) // executes + * A.findOneAndUpdate(conditions, update) // returns Query + * A.findOneAndUpdate() // returns Query + * + * ####Note: + * + * All top level update keys which are not `atomic` operation names are treated as set operations: + * + * ####Example: + * + * var query = { name: 'borne' }; + * Model.findOneAndUpdate(query, { name: 'jason borne' }, options, callback) + * + * // is sent as + * Model.findOneAndUpdate(query, { $set: { name: 'jason borne' }}, options, callback) + * + * This helps prevent accidentally overwriting your document with `{ name: 'jason borne' }`. + * + * ####Note: + * + * Although values are cast to their appropriate types when using the findAndModify helpers, the following are *not* applied: + * + * - defaults + * - setters + * - validators + * - middleware + * + * If you need those features, use the traditional approach of first retrieving the document. + * + * Model.findOne({ name: 'borne' }, function (err, doc) { + * if (err) .. + * doc.name = 'jason borne'; + * doc.save(callback); + * }) + * + * @param {Object} [conditions] + * @param {Object} [update] + * @param {Object} [options] + * @param {Function} [callback] + * @return {Query} + * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command + * @api public + */ + +Model.findOneAndUpdate = function (conditions, update, options, callback) { + if ('function' == typeof options) { + callback = options; + options = null; + } + else if (1 === arguments.length) { + if ('function' == typeof conditions) { + var msg = 'Model.findOneAndUpdate(): First argument must not be a function.\n\n' + + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)\n' + + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options)\n' + + ' ' + this.modelName + '.findOneAndUpdate(conditions, update)\n' + + ' ' + this.modelName + '.findOneAndUpdate(update)\n' + + ' ' + this.modelName + '.findOneAndUpdate()\n'; + throw new TypeError(msg) + } + update = conditions; + conditions = undefined; + } + + var fields; + if (options && options.fields) { + fields = options.fields; + options.fields = undefined; + } + + var query = new Query(conditions); + query.setOptions(options); + query.select(fields); + query.bind(this, 'findOneAndUpdate', update); + + if ('undefined' == typeof callback) + return query; + + this._applyNamedScope(query); + return query.findOneAndUpdate(callback); +} + +/** + * Issues a mongodb findAndModify update command by a documents id. + * + * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned. + * + * ####Options: + * + * - `new`: bool - true to return the modified document rather than the original. defaults to true + * - `upsert`: bool - creates the object if it doesn't exist. defaults to false. + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * - `select`: sets the document fields to return + * + * ####Examples: + * + * A.findByIdAndUpdate(id, update, options, callback) // executes + * A.findByIdAndUpdate(id, update, options) // returns Query + * A.findByIdAndUpdate(id, update, callback) // executes + * A.findByIdAndUpdate(id, update) // returns Query + * A.findByIdAndUpdate() // returns Query + * + * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned. + * + * ####Options: + * + * - `new`: bool - true to return the modified document rather than the original. defaults to true + * - `upsert`: bool - creates the object if it doesn't exist. defaults to false. + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * + * ####Note: + * + * All top level update keys which are not `atomic` operation names are treated as set operations: + * + * ####Example: + * + * Model.findByIdAndUpdate(id, { name: 'jason borne' }, options, callback) + * + * // is sent as + * Model.findByIdAndUpdate(id, { $set: { name: 'jason borne' }}, options, callback) + * + * This helps prevent accidentally overwriting your document with `{ name: 'jason borne' }`. + * + * ####Note: + * + * Although values are cast to their appropriate types when using the findAndModify helpers, the following are *not* applied: + * + * - defaults + * - setters + * - validators + * - middleware + * + * If you need those features, use the traditional approach of first retrieving the document. + * + * Model.findById(id, function (err, doc) { + * if (err) .. + * doc.name = 'jason borne'; + * doc.save(callback); + * }) + * + * @param {ObjectId|HexId} id an ObjectId or string that can be cast to one. + * @param {Object} [update] + * @param {Object} [options] + * @param {Function} [callback] + * @return {Query} + * @see Model.findOneAndUpdate #model_Model-findOneAndUpdate + * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command + * @api public + */ + +Model.findByIdAndUpdate = function (id, update, options, callback) { + var args; + + if (1 === arguments.length) { + if ('function' == typeof id) { + var msg = 'Model.findByIdAndUpdate(): First argument must not be a function.\n\n' + + ' ' + this.modelName + '.findByIdAndUpdate(id, callback)\n' + + ' ' + this.modelName + '.findByIdAndUpdate(id)\n' + + ' ' + this.modelName + '.findByIdAndUpdate()\n'; + throw new TypeError(msg) + } + return this.findOneAndUpdate({_id: id }, undefined); + } + + args = utils.args(arguments, 1); + args.unshift({ _id: id }); + return this.findOneAndUpdate.apply(this, args); +} + +/** + * Issue a mongodb findAndModify remove command. + * + * Finds a matching document, removes it, passing the found document (if any) to the callback. + * + * Executes immediately if `callback` is passed else a Query object is returned. + * + * ####Options: + * + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * - `select`: sets the document fields to return + * + * ####Examples: + * + * A.findOneAndRemove(conditions, options, callback) // executes + * A.findOneAndRemove(conditions, options) // return Query + * A.findOneAndRemove(conditions, callback) // executes + * A.findOneAndRemove(conditions) // returns Query + * A.findOneAndRemove() // returns Query + * + * @param {Object} conditions + * @param {Object} [options] + * @param {Function} [callback] + * @return {Query} + * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command + * @api public + */ + +Model.findOneAndRemove = function (conditions, options, callback) { + if (1 === arguments.length && 'function' == typeof conditions) { + var msg = 'Model.findOneAndRemove(): First argument must not be a function.\n\n' + + ' ' + this.modelName + '.findOneAndRemove(conditions, callback)\n' + + ' ' + this.modelName + '.findOneAndRemove(conditions)\n' + + ' ' + this.modelName + '.findOneAndRemove()\n'; + throw new TypeError(msg) + } + + if ('function' == typeof options) { + callback = options; + options = undefined; + } + + var fields; + if (options) { + fields = options.select; + options.select = undefined; + } + + var query = new Query(conditions); + query.setOptions(options); + query.select(fields); + query.bind(this, 'findOneAndRemove'); + + if ('undefined' == typeof callback) + return query; + + this._applyNamedScope(query); + return query.findOneAndRemove(callback); +} + +/** + * Issue a mongodb findAndModify remove command by a documents id. + * + * Finds a matching document, removes it, passing the found document (if any) to the callback. + * + * Executes immediately if `callback` is passed, else a `Query` object is returned. + * + * ####Options: + * + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * - `select`: sets the document fields to return + * + * ####Examples: + * + * A.findByIdAndRemove(id, options, callback) // executes + * A.findByIdAndRemove(id, options) // return Query + * A.findByIdAndRemove(id, callback) // executes + * A.findByIdAndRemove(id) // returns Query + * A.findByIdAndRemove() // returns Query + * + * @param {ObjectId|HexString} id ObjectId or string that can be cast to one + * @param {Object} [options] + * @param {Function} [callback] + * @return {Query} + * @see Model.findOneAndRemove #model_Model-findOneAndRemove + * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command + */ + +Model.findByIdAndRemove = function (id, options, callback) { + if (1 === arguments.length && 'function' == typeof id) { + var msg = 'Model.findByIdAndRemove(): First argument must not be a function.\n\n' + + ' ' + this.modelName + '.findByIdAndRemove(id, callback)\n' + + ' ' + this.modelName + '.findByIdAndRemove(id)\n' + + ' ' + this.modelName + '.findByIdAndRemove()\n'; + throw new TypeError(msg) + } + + return this.findOneAndRemove({ _id: id }, options, callback); +} + +/** + * Shortcut for creating a new Document that is automatically saved to the db if valid. + * + * ####Example: + * + * Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) { + * if (err) // ... + * }); + * + * var array = [{ type: 'jelly bean' }, { type: 'snickers' }]; + * Candy.create(array, function (err, jellybean, snickers) { + * if (err) // ... + * }); + * + * @param {Array|Object...} doc + * @param {Function} fn callback + * @api public + */ + +Model.create = function create (doc, fn) { + if (1 === arguments.length) { + return 'function' === typeof doc && doc(null); + } + + var self = this + , docs = [null] + , promise + , count + , args + + if (Array.isArray(doc)) { + args = doc; + } else { + args = utils.args(arguments, 0, arguments.length - 1); + fn = arguments[arguments.length - 1]; + } + + if (0 === args.length) return fn(null); + + promise = new Promise(fn); + count = args.length; + + args.forEach(function (arg, i) { + var doc = new self(arg); + docs[i+1] = doc; + doc.save(function (err) { + if (err) return promise.error(err); + --count || fn.apply(null, docs); + }); + }); + + // TODO + // utilize collection.insertAll for batch processing? +}; + +/** + * Updates documents in the database without returning them. + * + * ####Examples: + * + * MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn); + * MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, function (err, numberAffected, raw) { + * if (err) return handleError(err); + * console.log('The number of updated documents was %d', numberAffected); + * console.log('The raw response from Mongo was ', raw); + * }); + * + * ####Valid options: + * + * - `safe` (boolean) safe mode (defaults to value set in schema (true)) + * - `upsert` (boolean) whether to create the doc if it doesn't match (false) + * - `multi` (boolean) whether multiple documents should be updated (false) + * + * All `update` values are cast to their appropriate SchemaTypes before being sent. + * + * The `callback` function receives `(err, numberAffected, rawResponse)`. + * + * - `err` is the error if any occurred + * - `numberAffected` is the count of updated documents Mongo reported + * - `rawResponse` is the full response from Mongo + * + * ####Note: + * + * All top level keys which are not `atomic` operation names are treated as set operations: + * + * ####Example: + * + * var query = { name: 'borne' }; + * Model.update(query, { name: 'jason borne' }, options, callback) + * + * // is sent as + * Model.update(query, { $set: { name: 'jason borne' }}, options, callback) + * + * This helps prevent accidentally overwriting all documents in your collection with `{ name: 'jason borne' }`. + * + * ####Note: + * + * To update documents without waiting for a response from MongoDB, do not pass a `callback`, then call `exec` on the returned [Query](#query-js): + * + * Comment.update({ _id: id }, { $set: { text: 'changed' }}).exec(); + * + * ####Note: + * + * Although values are casted to their appropriate types when using update, the following are *not* applied: + * + * - defaults + * - setters + * - validators + * - middleware + * + * If you need those features, use the traditional approach of first retrieving the document. + * + * Model.findOne({ name: 'borne' }, function (err, doc) { + * if (err) .. + * doc.name = 'jason borne'; + * doc.save(callback); + * }) + * + * @param {Object} conditions + * @param {Object} update + * @param {Object} [options] + * @param {Function} [callback] + * @return {Query} + * @api public + */ + +Model.update = function update (conditions, doc, options, callback) { + if (arguments.length < 4) { + if ('function' === typeof options) { + // Scenario: update(conditions, doc, callback) + callback = options; + options = null; + } else if ('function' === typeof doc) { + // Scenario: update(doc, callback); + callback = doc; + doc = conditions; + conditions = {}; + options = null; + } + } + + var query = new Query(conditions, options).bind(this, 'update', doc); + + if ('undefined' == typeof callback) + return query; + + this._applyNamedScope(query); + return query.update(doc, callback); +}; + +/** + * Executes a mapReduce command. + * + * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation. + * + * ####Example: + * + * var o = {}; + * o.map = function () { emit(this.name, 1) } + * o.reduce = function (k, vals) { return vals.length } + * User.mapReduce(o, function (err, results) { + * console.log(results) + * }) + * + * ####Other options: + * + * - `query` {Object} query filter object. + * - `limit` {Number} max number of documents + * - `keeptemp` {Boolean, default:false} keep temporary data + * - `finalize` {Function} finalize function + * - `scope` {Object} scope variables exposed to map/reduce/finalize during execution + * - `jsMode` {Boolean, default:false} it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X + * - `verbose` {Boolean, default:false} provide statistics on job execution time. + * - `out*` {Object, default: {inline:1}} sets the output target for the map reduce job. + * + * ####* out options: + * + * - `{inline:1}` the results are returned in an array + * - `{replace: 'collectionName'}` add the results to collectionName: the results replace the collection + * - `{reduce: 'collectionName'}` add the results to collectionName: if dups are detected, uses the reducer / finalize functions + * - `{merge: 'collectionName'}` add the results to collectionName: if dups exist the new docs overwrite the old + * + * If `options.out` is set to `replace`, `merge`, or `reduce`, a Model instance is returned that can be used for further querying. Queries run against this model are all executed with the `lean` option; meaning only the js object is returned and no Mongoose magic is applied (getters, setters, etc). + * + * ####Example: + * + * var o = {}; + * o.map = function () { emit(this.name, 1) } + * o.reduce = function (k, vals) { return vals.length } + * o.out = { replace: 'createdCollectionNameForResults' } + * o.verbose = true; + * User.mapReduce(o, function (err, model, stats) { + * console.log('map reduce took %d ms', stats.processtime) + * model.find().where('value').gt(10).exec(function (err, docs) { + * console.log(docs); + * }); + * }) + * + * @param {Object} o an object specifying map-reduce options + * @param {Function} callback + * @see http://www.mongodb.org/display/DOCS/MapReduce + * @api public + */ + +Model.mapReduce = function mapReduce (o, callback) { + if ('function' != typeof callback) throw new Error('missing callback'); + + var self = this; + + if (!Model.mapReduce.schema) { + var opts = { noId: true, noVirtualId: true, strict: false } + Model.mapReduce.schema = new Schema({}, opts); + } + + if (!o.out) o.out = { inline: 1 }; + + o.map = String(o.map); + o.reduce = String(o.reduce); + + if (o.query) { + var q = new Query(o.query); + q.cast(this); + o.query = q._conditions; + q = undefined; + } + + this.collection.mapReduce(null, null, o, function (err, ret, stats) { + if (err) return callback(err); + + if (ret.findOne && ret.mapReduce) { + // returned a collection, convert to Model + var model = Model.compile( + '_mapreduce_' + ret.collectionName + , Model.mapReduce.schema + , ret.collectionName + , self.db + , self.base); + + model._mapreduce = true; + + return callback(err, model, stats); + } + + callback(err, ret, stats); + }); +} + +/** + * Executes an aggregate command on this models collection. + * + * ####Example: + * + * // find the max age of all users + * Users.aggregate( + * { $group: { _id: null, maxAge: { $max: '$age' }}} + * , { $project: { _id: 0, maxAge: 1 }} + * , function (err, res) { + * if (err) return handleError(err); + * console.log(res); // [ { maxAge: 98 } ] + * }); + * + * _NOTE: the documents returned are plain javascript objects, not mongoose documents cast to this models schema definition (since any shape of document can be returned)._ + * + * _NOTE: this requires running MongoDB >= 2.1_ + * + * @param {Array} array an array of pipeline commands + * @param {Object} [options] + * @param {Function} callback + * @see aggregation http://docs.mongodb.org/manual/applications/aggregation/ + * @see driver http://mongodb.github.com/node-mongodb-native/api-generated/collection.html#aggregate + * @api public + */ + +Model.aggregate = function aggregate () { + return this.collection.aggregate.apply(this.collection, arguments); +} + +/*! + * Compiler utility. + * + * @param {String} name model name + * @param {Schema} schema + * @param {String} collectionName + * @param {Connection} connection + * @param {Mongoose} base mongoose instance + */ + +Model.compile = function compile (name, schema, collectionName, connection, base) { + // generate new class + function model (doc, fields, skipId) { + if (!(this instanceof model)) + return new model(doc, fields, skipId); + Model.call(this, doc, fields, skipId); + }; + + model.modelName = name; + model.__proto__ = Model; + model.prototype.__proto__ = Model.prototype; + model.prototype.db = connection; + model.prototype._setSchema(schema); + model.prototype.collection = connection.collection( + collectionName + , schema.options.capped + ); + + // apply methods + for (var i in schema.methods) + model.prototype[i] = schema.methods[i]; + + // apply statics + for (var i in schema.statics) + model[i] = schema.statics[i]; + + // apply named scopes + if (schema.namedScopes) schema.namedScopes.compile(model); + + model.model = model.prototype.model; + model.options = model.prototype.options; + model.db = model.prototype.db; + model.schema = model.prototype.schema; + model.collection = model.prototype.collection; + model.base = base; + + return model; +}; + +/*! + * Subclass this model with `conn`, `schema`, and `collection` settings. + * + * @param {Connection} conn + * @param {Schema} [schema] + * @param {String} [collection] + * @return {Model} + */ + +Model.__subclass = function subclass (conn, schema, collection) { + // subclass model using this connection and collection name + var model = this; + + var Model = function Model (doc, fields, skipId) { + if (!(this instanceof Model)) { + return new Model(doc, fields, skipId); + } + model.call(this, doc, fields, skipId); + } + + Model.__proto__ = model; + Model.prototype.__proto__ = model.prototype; + Model.db = Model.prototype.db = conn; + + var s = 'string' != typeof schema + ? schema + : model.prototype.schema; + + if (!collection) { + collection = model.prototype.schema.get('collection') + || utils.toCollectionName(model.modelName); + } + + Model.prototype.collection = conn.collection(collection, s && s.options.capped); + Model.collection = Model.prototype.collection; + Model.init(); + return Model; +} + +/*! + * Module exports. + */ + +module.exports = exports = Model; diff --git a/test/node_modules/mongoose/lib/namedscope.js b/test/node_modules/mongoose/lib/namedscope.js new file mode 100644 index 0000000..1b3f5d4 --- /dev/null +++ b/test/node_modules/mongoose/lib/namedscope.js @@ -0,0 +1,70 @@ +var Query = require('./query'); +function NamedScope () {} + +NamedScope.prototype.query; + +NamedScope.prototype.where = function () { + var q = this.query || (this.query = new Query()); + q.where.apply(q, arguments); + return q; +}; + +/** + * Decorate + * + * @param {NamedScope} target + * @param {Object} getters + * @api private + */ + +NamedScope.prototype.decorate = function (target, getters) { + var name = this.name + , block = this.block + , query = this.query; + if (block) { + if (block.length === 0) { + Object.defineProperty(target, name, { + get: getters.block0(block) + }); + } else { + target[name] = getters.blockN(block); + } + } else { + Object.defineProperty(target, name, { + get: getters.basic(query) + }); + } +}; + +NamedScope.prototype.compile = function (model) { + var allScopes = this.scopesByName + , scope; + for (var k in allScopes) { + scope = allScopes[k]; + scope.decorate(model, { + block0: function (block) { + return function () { + var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this)); + block.call(cquery); + return this; + }; + }, + blockN: function (block) { + return function () { + var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this)); + block.apply(cquery, arguments); + return this; + }; + }, + basic: function (query) { + return function () { + var cquery = this._cumulativeQuery || (this._cumulativeQuery = new Query().bind(this)); + cquery.find(query); + return this; + }; + } + }); + } +}; + +module.exports = NamedScope; diff --git a/test/node_modules/mongoose/lib/promise.js b/test/node_modules/mongoose/lib/promise.js new file mode 100644 index 0000000..26639c1 --- /dev/null +++ b/test/node_modules/mongoose/lib/promise.js @@ -0,0 +1,151 @@ + +/*! + * Module dependencies. + */ + +var util = require('./utils'); +var EventEmitter = util.EventEmitter; + +/** + * Promise constructor. + * + * @param {Function} back a callback+errback that accepts `fn(err, ...){}` as signature + * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter + * @event `err`: Emits when the promise resolves to an error. + * @event `complete`: Emits when the promise resolves sucessfully. + * @api public + */ + +function Promise (back) { + this.emitted = {}; + if ('function' == typeof back) + this.addBack(back); +}; + +/*! + * Inherits from EventEmitter. + */ + +Promise.prototype.__proto__ = EventEmitter.prototype; + +/** + * Adds `listener` to the `event`. + * + * If `event` is either `error` or `complete` and the event has already been emitted, the`listener` is called immediately and passed the results of the original emitted event. + * + * @param {Event} event + * @param {Function} callback + * @return {Promise} this + * @api public + */ + +Promise.prototype.on = function (event, callback) { + if (this.emitted[event]) + callback.apply(this, this.emitted[event]); + else + EventEmitter.prototype.on.call(this, event, callback); + + return this; +}; + +/** + * Keeps track of emitted events to run them on `on`. + * + * @api private + */ + +Promise.prototype.emit = function (event) { + // ensures a promise can't be complete() or error() twice + if (event == 'err' || event == 'complete'){ + if (this.emitted.err || this.emitted.complete) { + return this; + } + this.emitted[event] = util.args(arguments, 1); + } + + return EventEmitter.prototype.emit.apply(this, arguments); +}; + +/** + * Shortcut for emitting the `complete` event. + * + * @api public + */ + +Promise.prototype.complete = function () { + var args = util.args(arguments); + return this.emit.apply(this, ['complete'].concat(args)); +}; + +/** + * Shortcut for emitting the `err` event. + * + * @api public + * @return {Promise} + */ + +Promise.prototype.error = function (err) { + if (!(err instanceof Error)) err = new Error(err); + return this.emit('err', err); +}; + +/** + * Shortcut for `.on('complete', fn)`. + * + * @return {Promise} + * @api public + */ + +Promise.prototype.addCallback = function (fn) { + return this.on('complete', fn); +}; + +/** + * Shortcut for `.on('err', fn)`. + * + * @return {Promise} + * @api public + */ + +Promise.prototype.addErrback = function (fn) { + return this.on('err', fn); +}; + +/** + * Adds a single function that's both a callback and errback. + * + * @param {Function} fn + * @return {Promise} + */ + +Promise.prototype.addBack = function (fn) { + this.on('err', function(err){ + fn.call(this, err); + }); + + this.on('complete', function(){ + var args = util.args(arguments); + fn.apply(this, [null].concat(args)); + }); + + return this; +}; + +/** + * Sugar for handling cases where you may be resolving to either an error condition or a success condition. + * + * @param {Error} err optional error or null + * @param {Object} val value to complete the promise with + * @api public + */ + +Promise.prototype.resolve = function (err, val) { + if (err) return this.error(err); + return this.complete(val); +}; + +/*! + * Module exports. + */ + +module.exports = Promise; diff --git a/test/node_modules/mongoose/lib/query.js b/test/node_modules/mongoose/lib/query.js new file mode 100644 index 0000000..eebf7a5 --- /dev/null +++ b/test/node_modules/mongoose/lib/query.js @@ -0,0 +1,2395 @@ +/*! + * Module dependencies. + */ + +var utils = require('./utils') + , merge = utils.merge + , Promise = require('./promise') + , Document = require('./document') + , Types = require('./schema/index') + , inGroupsOf = utils.inGroupsOf + , tick = utils.tick + , QueryStream = require('./querystream') + , ReadPref = require('mongodb').ReadPreference + +/** + * Query constructor used for building queries. + * + * ####Example: + * + * var query = Model.find(); + * query.where('age').gte(21).exec(callback); + * + * @param {Object} criteria + * @param {Object} options + * @api public + */ + +function Query (criteria, options) { + this.setOptions(options, true); + this._conditions = {}; + this._updateArg = {}; + this._fields = undefined; + if (criteria) this.find(criteria); +} + +/** + * Sets query options. + * + * ####Options: + * + * - [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors) * + * - [sort](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsort(\)%7D%7D) * + * - [limit](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D) * + * - [skip](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D) * + * - [maxscan](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24maxScan) * + * - [batchSize](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D) * + * - [comment](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment) * + * - [snapshot](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D) * + * - [hint](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint) * + * - [slaveOk](http://docs.mongodb.org/manual/applications/replication/#read-preference) * + * - [lean](./api.html#query_Query-lean) * + * - [safe](http://www.mongodb.org/display/DOCS/getLastError+Command) + * + * _* denotes a query helper method is also available_ + * + * @param {Object} options + * @api public + */ + +Query.prototype.setOptions = function (options, overwrite) { + // overwrite is internal use only + if (overwrite) { + options = this.options = options || {}; + this.safe = options.safe + + // normalize population options + var pop = this.options.populate; + this.options.populate = {}; + + if (pop && Array.isArray(pop)) { + for (var i = 0, l = pop.length; i < l; i++) { + this.options.populate[pop[i]] = {}; + } + } + + return this; + } + + if (!(options && 'Object' == options.constructor.name)) + return this; + + if ('safe' in options) + this.safe = options.safe; + + // set arbitrary options + var methods = Object.keys(options) + , i = methods.length + , method + + while (i--) { + method = methods[i]; + + // use methods if exist (safer option manipulation) + if ('function' == typeof this[method]) { + var args = Array.isArray(options[method]) + ? options[method] + : [options[method]]; + this[method].apply(this, args) + } else { + this.options[method] = options[method]; + } + } + return this; +} + +/** + * Binds this query to a model. + * + * @param {Model} model the model to which the query is bound + * @param {String} op the operation to execute + * @param {Object} updateArg used in update methods + * @return {Query} + * @api private + */ + +Query.prototype.bind = function bind (model, op, updateArg) { + this.model = model; + this.op = op; + + if (model._mapreduce) this.options.lean = true; + + if (op == 'update' || op == 'findOneAndUpdate') { + merge(this._updateArg, updateArg || {}); + } + + return this; +}; + +/** + * Executes the query + * + * ####Examples + * + * query.exec(); + * query.exec(callback); + * query.exec('update'); + * query.exec('find', callback); + * + * @param {String|Function} [operation] + * @param {Function} [callback] + * @return {Promise} + * @api public + */ + +Query.prototype.exec = function exec (op, callback) { + var promise = new Promise(); + + switch (typeof op) { + case 'function': + callback = op; + op = null; + break; + case 'string': + this.op = op; + break; + } + + if (callback) promise.addBack(callback); + + if (!this.op) { + promise.complete(); + return promise; + } + + if ('update' == this.op) { + this[this.op](this._updateArg, promise.resolve.bind(promise)); + return promise; + } + + if ('distinct' == this.op) { + this.distinct(this._distinctArg, promise.resolve.bind(promise)); + return promise; + } + + this[this.op](promise.resolve.bind(promise)); + return promise; +}; + +/** + * Finds documents. + * + * When no `callback` is passed, the query is not executed. + * + * ####Example + * + * query.find({ name: 'Los Pollos Hermanos' }).find(callback) + * + * @param {Object} [criteria] mongodb selector + * @param {Function} [callback] + * @return {Query} this + * @api public + */ + +Query.prototype.find = function (criteria, callback) { + this.op = 'find'; + if ('function' === typeof criteria) { + callback = criteria; + criteria = {}; + } else if (criteria instanceof Query) { + // TODO Merge options, too + merge(this._conditions, criteria._conditions); + } else if (criteria instanceof Document) { + merge(this._conditions, criteria.toObject()); + } else if (criteria && 'Object' === criteria.constructor.name) { + merge(this._conditions, criteria); + } + if (!callback) return this; + return this.execFind(callback); +}; + +/** + * Casts this query to the schema of `model` + * + * ####Note + * + * If `obj` is present, it is cast instead of this query. + * + * @param {Model} model + * @param {Object} [obj] + * @return {Object} + * @api public + */ + +Query.prototype.cast = function (model, obj) { + obj || (obj= this._conditions); + + var schema = model.schema + , paths = Object.keys(obj) + , i = paths.length + , any$conditionals + , schematype + , nested + , path + , type + , val; + + while (i--) { + path = paths[i]; + val = obj[path]; + + if ('$or' === path || '$nor' === path || '$and' === path) { + var k = val.length + , orComponentQuery; + + while (k--) { + orComponentQuery = new Query(val[k]); + orComponentQuery.cast(model); + val[k] = orComponentQuery._conditions; + } + + } else if (path === '$where') { + type = typeof val; + + if ('string' !== type && 'function' !== type) { + throw new Error("Must have a string or function for $where"); + } + + if ('function' === type) { + obj[path] = val.toString(); + } + + continue; + + } else { + + if (!schema) { + // no casting for Mixed types + continue; + } + + schematype = schema.path(path); + + if (!schematype) { + // Handle potential embedded array queries + var split = path.split('.') + , j = split.length + , pathFirstHalf + , pathLastHalf + , remainingConds + , castingQuery; + + // Find the part of the var path that is a path of the Schema + while (j--) { + pathFirstHalf = split.slice(0, j).join('.'); + schematype = schema.path(pathFirstHalf); + if (schematype) break; + } + + // If a substring of the input path resolves to an actual real path... + if (schematype) { + // Apply the casting; similar code for $elemMatch in schema/array.js + if (schematype.caster && schematype.caster.schema) { + remainingConds = {}; + pathLastHalf = split.slice(j).join('.'); + remainingConds[pathLastHalf] = val; + castingQuery = new Query(remainingConds); + castingQuery.cast(schematype.caster); + obj[path] = castingQuery._conditions[pathLastHalf]; + } else { + obj[path] = val; + } + continue; + } + + if (utils.isObject(val)) { + // handle geo schemas that use object notation + // { loc: { long: Number, lat: Number } + + var geo = val.$near ? '$near' : + val.$nearSphere ? '$nearSphere' : + val.$within ? '$within' : ''; + + if (!geo) { + continue; + } + + var numbertype = new Types.Number('__QueryCasting__') + var value = val[geo]; + + if (val.$maxDistance) { + val.$maxDistance = numbertype.castForQuery(val.$maxDistance); + } + + if ('$within' == geo) { + // find $center, $centerSphere, $box, $polygon + var withinType = value.$center || value.$centerSphere || value.$box || value.$polygon; + if (!withinType) { + throw new Error('Bad $within paramater: ' + JSON.stringify(val)); + } + + value = withinType; + } + + ;(function _cast (val) { + if (Array.isArray(val)) { + val.forEach(function (item, i) { + if (Array.isArray(item) || utils.isObject(item)) { + return _cast(item); + } + val[i] = numbertype.castForQuery(item); + }); + } else { + var nearKeys= Object.keys(val); + var nearLen = nearKeys.length; + while (nearLen--) { + var nkey = nearKeys[nearLen]; + var item = val[nkey]; + if (Array.isArray(item) || utils.isObject(item)) { + _cast(item); + val[nkey] = item; + } else { + val[nkey] = numbertype.castForQuery(item); + } + } + } + })(value); + } + + } else if (val === null || val === undefined) { + continue; + } else if ('Object' === val.constructor.name) { + + any$conditionals = Object.keys(val).some(function (k) { + return k.charAt(0) === '$' && k !== '$id' && k !== '$ref'; + }); + + if (!any$conditionals) { + obj[path] = schematype.castForQuery(val); + } else { + + var ks = Object.keys(val) + , k = ks.length + , $cond; + + while (k--) { + $cond = ks[k]; + nested = val[$cond]; + + if ('$exists' === $cond) { + if ('boolean' !== typeof nested) { + throw new Error("$exists parameter must be Boolean"); + } + continue; + } + + if ('$type' === $cond) { + if ('number' !== typeof nested) { + throw new Error("$type parameter must be Number"); + } + continue; + } + + if ('$not' === $cond) { + this.cast(model, nested); + } else { + val[$cond] = schematype.castForQuery($cond, nested); + } + } + } + } else { + obj[path] = schematype.castForQuery(val); + } + } + } + + return obj; +}; + +/** + * Returns default options. + * @param {Model} model + * @api private + */ + +Query.prototype._optionsForExec = function (model) { + var options = utils.clone(this.options, { retainKeyOrder: true }); + delete options.populate; + + if (!('safe' in options)) + options.safe = model.schema.options.safe; + + if (!('readPreference' in options) && model.schema.options.read) + options.readPreference = model.schema.options.read; + + return options; +}; + +/** + * Applies schematype selected options to this query. + * @api private + */ + +Query.prototype._applyPaths = function applyPaths () { + // determine if query is selecting or excluding fields + + var fields = this._fields + , exclude + , keys + , ki + + if (fields) { + keys = Object.keys(fields); + ki = keys.length; + + while (ki--) { + if ('+' == keys[ki][0]) continue; + exclude = 0 === fields[keys[ki]]; + break; + } + } + + // if selecting, apply default schematype select:true fields + // if excluding, apply schematype select:false fields + + var selected = [] + , excluded = [] + , seen = []; + + analyzeSchema(this.model.schema); + + switch (exclude) { + case true: + excluded.length && this.select('-' + excluded.join(' -')); + break; + case false: + selected.length && this.select(selected.join(' ')); + break; + case undefined: + // user didn't specify fields, implies returning all fields. + // only need to apply excluded fields + excluded.length && this.select('-' + excluded.join(' -')); + break; + } + + return seen = excluded = selected = keys = fields = null; + + function analyzeSchema (schema, prefix) { + prefix || (prefix = ''); + + // avoid recursion + if (~seen.indexOf(schema)) return; + seen.push(schema); + + schema.eachPath(function (path, type) { + if (prefix) path = prefix + '.' + path; + + // array of subdocs? + if (type.schema) { + analyzeSchema(type.schema, path); + } + + analyzePath(path, type); + }); + } + + function analyzePath (path, type) { + if ('boolean' != typeof type.selected) return; + + if (fields && ('+' + path) in fields) { + // forced inclusion + delete fields['+' + path]; + + // if there are other fields being included, add this one + // if no other included fields, leave this out (implied inclusion) + if (false === exclude && keys.length > 1) { + fields[path] = 1; + } + + return + }; + + ;(type.selected ? selected : excluded).push(path); + } +} + +/** + * Specifies a `$where` condition + * + * Use `$where` when you need to select documents using a JavaScript expression. + * + * ####Example + * + * query.$where('this.comments.length > 10 || this.name.length > 5') + * + * query.$where(function () { + * return this.comments.length > 10 || this.name.length > 5; + * }) + * + * @param {String|Function} js javascript string or function + * @return {Query} this + * @memberOf Query + * @method $where + * @api public + */ + +Query.prototype.$where = function (js) { + this._conditions['$where'] = js; + return this; +}; + +/** + * Specifies a `path` for use with chaining. + * + * ####Example + * + * // instead of writing: + * User.find({age: {$gte: 21, $lte: 65}}, callback); + * + * // we can instead write: + * User.where('age').gte(21).lte(65); + * + * // Moreover, you can also chain a bunch of these together: + * + * User + * .where('age').gte(21).lte(65) + * .where('name', /^b/i) + * .where('friends').slice(10) + * .exec(callback) + * + * @param {String} [path] + * @param {Object} [val] + * @return {Query} this + * @api public + */ + +Query.prototype.where = function (path, val) { + if (!arguments.length) return this; + + if ('string' != typeof path) { + throw new TypeError('path must be a string'); + } + + this._currPath = path; + + if (2 === arguments.length) { + this._conditions[path] = val; + } + + return this; +}; + +/** + * Specifies the complementary comparison value for paths specified with `where()` + * + * ####Example + * + * User.where('age').equals(49); + * + * // is the same as + * + * User.where('age', 49); + * + * @param {Object} val + * @return {Query} this + * @api public + */ + +Query.prototype.equals = function equals (val) { + var path = this._currPath; + if (!path) throw new Error('equals() must be used after where()'); + this._conditions[path] = val; + return this; +} + +/** + * Specifies arguments for an `$or` condition. + * + * ####Example + * + * query.or([{ color: 'red' }, { status: 'emergency' }]) + * + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +Query.prototype.or = function or (array) { + var or = this._conditions.$or || (this._conditions.$or = []); + if (!Array.isArray(array)) array = [array]; + or.push.apply(or, array); + return this; +} + +/** + * Specifies arguments for a `$nor` condition. + * + * ####Example + * + * query.nor([{ color: 'green' }, { status: 'ok' }]) + * + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +Query.prototype.nor = function nor (array) { + var nor = this._conditions.$nor || (this._conditions.$nor = []); + if (!Array.isArray(array)) array = [array]; + nor.push.apply(nor, array); + return this; +} + +/** + * Specifies arguments for a `$and` condition. + * + * ####Example + * + * query.and([{ color: 'green' }, { status: 'ok' }]) + * + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +Query.prototype.and = function and (array) { + var and = this._conditions.$and || (this._conditions.$and = []); + if (!Array.isArray(array)) array = [array]; + and.push.apply(and, array); + return this; +} + +/** + * Specifies a $gt query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * ####Example + * + * Thing.find().where('age').gt(21) + * + * // or + * Thing.find().gt('age', 21) + * + * @method gt + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies a $gte query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method gte + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies a $lt query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method lt + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies a $lte query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method lte + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies a $ne query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method ne + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies an $in query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method in + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies an $nin query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method nin + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies an $all query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method all + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies an $size query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method size + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies a $regex query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method regex + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/** + * Specifies a $maxDistance query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method maxDistance + * @memberOf Query + * @param {String} path + * @param {Number} val + * @api public + */ + +/*! + * gt, gte, lt, lte, ne, in, nin, all, regex, size, maxDistance + * + * Thing.where('type').nin(array) + */ + +'gt gte lt lte ne in nin all regex size maxDistance'.split(' ').forEach(function ($conditional) { + Query.prototype[$conditional] = function (path, val) { + if (arguments.length === 1) { + val = path; + path = this._currPath + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds['$' + $conditional] = val; + return this; + }; +}); + +/** + * Specifies a `$near` condition + * + * @param {String} path + * @param {Number} val + * @return {Query} this + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @api public + */ + +Query.prototype.near = function (path, val) { + if (arguments.length === 1) { + val = path; + path = this._currPath + } else if (arguments.length === 2 && !Array.isArray(val)) { + val = utils.args(arguments); + path = this._currPath; + } else if (arguments.length === 3) { + val = utils.args(arguments, 1); + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds.$near = val; + return this; +} + +/** + * Specifies a `$nearSphere` condition. + * + * @param {String} path + * @param {Object} val + * @return {Query} this + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @api public + */ + +Query.prototype.nearSphere = function (path, val) { + if (arguments.length === 1) { + val = path; + path = this._currPath + } else if (arguments.length === 2 && !Array.isArray(val)) { + val = utils.args(arguments); + path = this._currPath; + } else if (arguments.length === 3) { + val = utils.args(arguments, 1); + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds.$nearSphere = val; + return this; +} + +/** + * Specifies a `$mod` condition + * + * @param {String} path + * @param {Number} val + * @return {Query} this + * @api public + */ + +Query.prototype.mod = function (path, val) { + if (arguments.length === 1) { + val = path; + path = this._currPath + } else if (arguments.length === 2 && !Array.isArray(val)) { + val = utils.args(arguments); + path = this._currPath; + } else if (arguments.length === 3) { + val = utils.args(arguments, 1); + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds.$mod = val; + return this; +} + +/** + * Specifies an `$exists` condition + * + * @param {String} path + * @param {Number} val + * @return {Query} this + * @api public + */ + +Query.prototype.exists = function (path, val) { + if (arguments.length === 0) { + path = this._currPath + val = true; + } else if (arguments.length === 1) { + if ('boolean' === typeof path) { + val = path; + path = this._currPath; + } else { + val = true; + } + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds['$exists'] = val; + return this; +}; + +/** + * Specifies an `$elemMatch` condition + * + * ####Example + * + * query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}}) + * + * query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}}) + * + * query.elemMatch('comment', function (elem) { + * elem.where('author').equals('autobot'); + * elem.where('votes').gte(5); + * }) + * + * query.where('comment').elemMatch(function (elem) { + * elem.where('author').equals('autobot'); + * elem.where('votes').gte(5); + * }) + * + * @param {String|Object|Function} path + * @param {Object|Function} criteria + * @return {Query} this + * @api public + */ + +Query.prototype.elemMatch = function (path, criteria) { + var block; + if ('Object' === path.constructor.name) { + criteria = path; + path = this._currPath; + } else if ('function' === typeof path) { + block = path; + path = this._currPath; + } else if ('Object' === criteria.constructor.name) { + } else if ('function' === typeof criteria) { + block = criteria; + } else { + throw new Error("Argument error"); + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + if (block) { + criteria = new Query(); + block(criteria); + conds['$elemMatch'] = criteria._conditions; + } else { + conds['$elemMatch'] = criteria; + } + return this; +}; + +// Spatial queries + +/** + * Syntax sugar for expressive queries. + * + * ####Example + * + * query.within.box() + * query.within.center() + * + * @property within + * @memberOf Query + * @return {Query} this + * @api public + */ + +Object.defineProperty(Query.prototype, 'within', { + get: function () { return this } +}); + +/** + * Specifies a $box condition + * + * ####Example + * + * var lowerLeft = [40.73083, -73.99756] + * var upperRight= [40.741404, -73.988135] + * query.where('loc').within.box({ ll: lowerLeft , ur: upperRight }) + * + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @see Query#within #query_Query-within + * @param {String} path + * @param {Object} val + * @return {Query} this + * @api public + */ + +Query.prototype.box = function (path, val) { + if (arguments.length === 1) { + val = path; + path = this._currPath; + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds['$within'] = { '$box': [val.ll, val.ur] }; + return this; +}; + +/** + * Specifies a $center condition + * + * ####Example + * + * var area = { center: [50, 50], radius: 10 } + * query.where('loc').within.center(area) + * + * @param {String} path + * @param {Object} val + * @param {Object} [opts] options e.g. { $uniqueDocs: true } + * @return {Query} this + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @api public + */ + +Query.prototype.center = function (path, val, opts) { + if (arguments.length === 1) { + val = path; + path = this._currPath; + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds['$within'] = { '$center': [val.center, val.radius] }; + + // copy any options + if (opts && 'Object' == opts.constructor.name) { + utils.options(opts, conds.$within); + } + + return this; +}; + +/** + * Specifies a $centerSphere condition + * + * ####Example + * + * var area = { center: [50, 50], radius: 10 } + * query.where('loc').within.centerSphere(area) + * + * @param {String} path + * @param {Object} val + * @return {Query} this + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @api public + */ + +Query.prototype.centerSphere = function (path, val) { + if (arguments.length === 1) { + val = path; + path = this._currPath; + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds['$within'] = { '$centerSphere': [val.center, val.radius] }; + return this; +}; + +/** + * Specifies a $polygon condition + * + * ####Example + * + * var polyA = [ [ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ] ] + * query.where('loc').within.polygon(polyA) + * + * // or + * var polyB = { a : { x : 10, y : 20 }, b : { x : 15, y : 25 }, c : { x : 20, y : 20 } } + * query.where('loc').within.polygon(polyB) + * + * @param {String} path + * @param {Array|Object} val + * @return {Query} this + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @api public + */ + +Query.prototype.polygon = function (path, val) { + if (arguments.length === 1) { + val = path; + path = this._currPath; + } + var conds = this._conditions[path] || (this._conditions[path] = {}); + conds['$within'] = { '$polygon': val }; + return this; +}; + +/** + * Specifies which document fields to include or exclude + * + * When using string syntax, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. Lastly, if a path is prefixed with `+`, it forces inclusion of the path, which is useful for paths excluded at the [schema level](/docs/api.html#schematype_SchemaType-select). + * + * ####Example + * + * // include a and b, exclude c + * query.select('a b -c'); + * + * // or you may use object notation, useful when + * // you have keys already prefixed with a "-" + * query.select({a: 1, b: 1, c: 0}); + * + * // force inclusion of field excluded at schema level + * query.select('+path') + * + * ####NOTE: + * + * _v2 had slightly different syntax such as allowing arrays of field names. This support was removed in v3._ + * + * @param {Object|String} arg + * @return {Query} this + * @see SchemaType + * @api public + */ + +Query.prototype.select = function select (arg) { + if (!arg) return this; + + var fields = this._fields || (this._fields = {}); + + if ('Object' === arg.constructor.name) { + Object.keys(arg).forEach(function (field) { + fields[field] = arg[field]; + }); + } else if (1 === arguments.length && 'string' == typeof arg) { + arg.split(/\s+/).forEach(function (field) { + if (!field) return; + var include = '-' == field[0] ? 0 : 1; + if (include === 0) field = field.substring(1); + fields[field] = include; + }); + } else { + throw new TypeError('Invalid select() argument. Must be a string or object.'); + } + + return this; +}; + +/** + * Specifies a $slice condition + * + * ####Example + * + * query.slice('comments', 5) + * query.slice('comments', -5) + * query.slice('comments', [10, 5]) + * query.where('comments').slice(5) + * query.where('comments').slice([-10, 5]) + * + * @param {String} path + * @param {Number} val number of elements to slice + * @return {Query} this + * @see mongodb http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements + * @api public + */ + +Query.prototype.slice = function (path, val) { + if (arguments.length === 1) { + val = path; + path = this._currPath + } else if (arguments.length === 2) { + if ('number' === typeof path) { + val = [path, val]; + path = this._currPath; + } + } else if (arguments.length === 3) { + val = utils.args(arguments, 1); + } + var myFields = this._fields || (this._fields = {}); + myFields[path] = { '$slice': val }; + return this; +}; + +/** + * Sets the sort order + * + * If an object is passed, values allowed are 'asc', 'desc', 'ascending', 'descending', 1, and -1. + * + * If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending. + * + * ####Example + * + * // these are equivalent + * query.sort({ field: 'asc', test: -1 }); + * query.sort('field -test'); + * + * @param {Object|String} arg + * @return {Query} this + * @api public + */ + +Query.prototype.sort = function (arg) { + if (!arg) return this; + + var sort = this.options.sort || (this.options.sort = []); + + if ('Object' === arg.constructor.name) { + Object.keys(arg).forEach(function (field) { + push(sort, field, arg[field]); + }); + } else if (1 === arguments.length && 'string' == typeof arg) { + arg.split(/\s+/).forEach(function (field) { + if (!field) return; + var ascend = '-' == field[0] ? -1 : 1; + if (ascend === -1) field = field.substring(1); + push(sort, field, ascend); + }); + } else { + throw new TypeError('Invalid sort() argument. Must be a string or object.'); + } + + return this; +}; + +/*! + * @ignore + */ + +function push (arr, field, value) { + var val = String(value || 1).toLowerCase(); + if (!/^(?:ascending|asc|descending|desc|1|-1)$/.test(val)) { + if (Array.isArray(value)) value = '['+value+']'; + throw new TypeError('Invalid sort value: {' + field + ': ' + value + ' }'); + } + arr.push([field, value]); +} + +/** + * Specifies the limit option. + * + * ####Example + * + * Kitten.find().limit(20) + * + * @method limit + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D + * @api public + */ +/** + * Specifies the skip option. + * + * ####Example + * + * Kitten.find().skip(100).limit(20) + * + * @method skip + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D + * @api public + */ +/** + * Specifies the maxscan option. + * + * ####Example + * + * Kitten.find().maxscan(100) + * + * @method maxscan + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24maxScan + * @api public + */ +/** + * Specifies the batchSize option. + * + * ####Example + * + * Kitten.find().batchSize(100) + * + * @method batchSize + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D + * @api public + */ +/** + * Specifies the `comment` option. + * + * ####Example + * + * Kitten.findOne(condition).comment('login query') + * + * @method comment + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment + * @api public + */ + +/*! + * limit, skip, maxscan, batchSize, comment + * + * Sets these associated options. + * + * query.comment('feed query'); + */ + +;['limit', 'skip', 'maxscan', 'batchSize', 'comment'].forEach(function (method) { + Query.prototype[method] = function (v) { + this.options[method] = v; + return this; + }; +}); + +/** + * Specifies this query as a `snapshot` query. + * + * ####Example + * + * Kitten.find().snapshot() + * + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D + * @return {Query} this + * @api public + */ + +Query.prototype.snapshot = function () { + this.options.snapshot = true; + return this; +}; + +/** + * Sets query hints. + * + * ####Example + * + * Model.find().hint({ indexA: 1, indexB: -1}) + * + * @param {Object} val a hint object + * @return {Query} this + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint + * @api public + */ + +Query.prototype.hint = function (val) { + if (!val) return this; + + var hint = this.options.hint || (this.options.hint = {}); + + if ('Object' === val.constructor.name) { + // must keep object keys in order so don't use Object.keys() + for (var k in val) { + hint[k] = val[k]; + } + } else { + throw new TypeError('Invalid hint. ' + val); + } + + return this; +}; + +/** + * Sets the slaveOk option. + * + * ####Example: + * + * new Query().slaveOk() // true + * new Query().slaveOk(true) + * new Query().slaveOk(false) + * + * @param {Boolean} v defaults to true + * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference + * @return {Query} this + * @api public + */ + +Query.prototype.slaveOk = function (v) { + this.options.slaveOk = arguments.length ? !!v : true; + return this; +} + +/** + * Sets the readPreference option for the query. + * + * ####Example: + * + * new Query().read('primary') + * new Query().read('p') // same as primary + * + * new Query().read('primaryPreferred') + * new Query().read('pp') // same as primaryPreferred + * + * new Query().read('secondary') + * new Query().read('s') // same as secondary + * + * new Query().read('secondaryPreferred') + * new Query().read('sp') // same as secondaryPreferred + * + * new Query().read('nearest') + * new Query().read('n') // same as nearest + * + * // with tags + * new Query().read('s', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }]) + * + * ####Preferences: + * + * primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags. + * secondary Read from secondary if available, otherwise error. + * primaryPreferred Read from primary if available, otherwise a secondary. + * secondaryPreferred Read from a secondary if available, otherwise read from the primary. + * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection. + * + * Aliases + * + * p primary + * pp primaryPreferred + * s secondary + * sp secondaryPreferred + * n nearest + * + * Read more about how to use read preferrences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences). + * + * @param {String} pref one of the listed preference options or their aliases + * @param {Array} [tags] optional tags for this query + * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference + * @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences + * @return {Query} this + * @api public + */ + +Query.prototype.read = function (pref, tags) { + this.options.readPreference = utils.readPref(pref, tags); + return this; +} + +/** + * Sets the lean option. + * + * Documents returned from queries with the `lean` option enabled are plain javascript objects, not [MongooseDocuments](#document-js). They have no `save` method, getters/setters or any other Mongoose magic applied. + * + * This is a [great](https://groups.google.com/forum/#!topic/mongoose-orm/u2_DzDydcnA/discussion) option in high-performance read-only scenarios, especially when combined with the [stream](#query_Query-stream) option. + * + * ####Example: + * + * new Query().lean() // true + * new Query().lean(true) + * new Query().lean(false) + * + * Model.find().lean().exec(); + * + * var leanStream = Model.find().lean().stream(); + * + * @param {Boolean} v defaults to true + * @return {Query} this + * @api public + */ + +Query.prototype.lean = function (v) { + this.options.lean = arguments.length ? !!v : true; + return this; +} + +/** + * Sets tailable option. + * + * ####Example + * + * Kitten.find().tailable() <== true + * Kitten.find().tailable(true) + * Kitten.find().tailable(false) + * + * @param {Boolean} v defaults to true + * @see mongodb http://www.mongodb.org/display/DOCS/Tailable+Cursors + * @api public + */ + +Query.prototype.tailable = function (v) { + this.options.tailable = arguments.length ? !!v : true; + return this; +}; + +/** + * Executes the query as a find() operation. + * + * @param {Function} callback + * @return {Query} this + * @api private + */ + +Query.prototype.execFind = function (callback) { + var model = this.model + , promise = new Promise(callback); + + try { + this.cast(model); + } catch (err) { + promise.error(err); + return this; + } + + // apply default schematype path selections + this._applyPaths(); + + var self = this + , castQuery = this._conditions + , options = this._optionsForExec(model) + , fields = utils.clone(this._fields) + + options.fields = this._castFields(fields); + if (options.fields instanceof Error) { + promise.error(options.fields); + return this; + } + + model.collection.find(castQuery, options, function (err, cursor) { + if (err) return promise.error(err); + cursor.toArray(tick(cb)); + }); + + function cb (err, docs) { + if (err) return promise.error(err); + + if (true === options.lean) + return promise.complete(docs); + + var arr = [] + , count = docs.length; + + if (!count) return promise.complete([]); + + for (var i = 0, l = docs.length; i < l; i++) { + arr[i] = new model(undefined, fields, true); + arr[i].init(docs[i], self, function (err) { + if (err) return promise.error(err); + --count || promise.complete(arr); + }); + } + } + + return this; +}; + +/** + * Executes the query as a findOne() operation. + * + * ####Example + * + * Kitten.where('color', 'white').findOne(function (err, kitten) { + * if (err) return handleError(err); + * + * // kitten may be null if no document matched + * if (kitten) { + * ... + * } + * }) + * + * @param {Function} callback + * @return {Query} this + * @api public + */ + +Query.prototype.findOne = function (callback) { + this.op = 'findOne'; + + if (!callback) return this; + + var model = this.model; + var promise = new Promise(callback); + + try { + this.cast(model); + } catch (err) { + promise.error(err); + return this; + } + + // apply default schematype path selections + this._applyPaths(); + + var self = this + , castQuery = this._conditions + , options = this._optionsForExec(model) + , fields = utils.clone(this._fields) + + options.fields = this._castFields(fields); + if (options.fields instanceof Error) { + promise.error(options.fields); + return this; + } + + model.collection.findOne(castQuery, options, tick(function (err, doc) { + if (err) return promise.error(err); + if (!doc) return promise.complete(null); + + if (true === options.lean) return promise.complete(doc); + + var casted = new model(undefined, fields, true); + casted.init(doc, self, function (err) { + if (err) return promise.error(err); + promise.complete(casted); + }); + })); + + return this; +}; + +/** + * Exectues the query as a count() operation. + * + * ####Example + * + * Kitten.where('color', 'black').count(function (err, count) { + * if (err) return handleError(err); + * console.log('there are %d black kittens', count); + * }) + * + * @param {Function} callback + * @return {Query} this + * @see mongodb http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Count + * @api public + */ + +Query.prototype.count = function (callback) { + this.op = 'count'; + var model = this.model; + + try { + this.cast(model); + } catch (err) { + return callback(err); + } + + var castQuery = this._conditions; + model.collection.count(castQuery, tick(callback)); + + return this; +}; + +/** + * Executes this query as a distict() operation. + * + * @param {String} field + * @param {Function} callback + * @return {Query} this + * @see mongodb http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Distinct + * @api public + */ + +Query.prototype.distinct = function (field, callback) { + this.op = 'distinct'; + var model = this.model; + + try { + this.cast(model); + } catch (err) { + return callback(err); + } + + var castQuery = this._conditions; + model.collection.distinct(field, castQuery, tick(callback)); + + return this; +}; + +/*! + * These operators require casting docs + * to real Documents for Update operations. + */ + +var castOps = { + $push: 1 + , $pushAll: 1 + , $addToSet: 1 + , $set: 1 +}; + +/*! + * These operators should be cast to numbers instead + * of their path schema type. + */ + +var numberOps = { + $pop: 1 + , $unset: 1 + , $inc: 1 +} + +/** + * Executes this query as an update() operation. + * + * _All paths passed that are not $atomic operations will become $set ops so we retain backwards compatibility._ + * + * ####Example + * + * Model.update({..}, { title: 'remove words' }, ...) + * + * becomes + * + * Model.update({..}, { $set: { title: 'remove words' }}, ...) + * + * ####Note + * + * Passing an empty object `{}` as the doc will result in a no-op. The update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting the collection. + * + * @param {Object} doc the update conditions + * @param {Function} callback + * @return {Query} this + * @api public + */ + +Query.prototype.update = function update (doc, callback) { + this.op = 'update'; + this._updateArg = doc; + + var model = this.model + , options = this._optionsForExec(model) + , fn = 'function' == typeof callback + , castedQuery + , castedDoc + + castedQuery = castQuery(this); + if (castedQuery instanceof Error) { + if (fn) { + process.nextTick(callback.bind(null, castedQuery)); + return this; + } + throw castedQuery; + } + + castedDoc = castDoc(this); + if (!castedDoc) { + fn && process.nextTick(callback.bind(null, null, 0)); + return this; + } + + if (castedDoc instanceof Error) { + if (fn) { + process.nextTick(callback.bind(null, castedDoc)); + return this; + } + throw castedDoc; + } + + if (!fn) { + options.safe = { w: 0 }; + } + + model.collection.update(castedQuery, castedDoc, options, tick(callback)); + return this; +}; + +/** + * Casts obj for an update command. + * + * @param {Object} obj + * @return {Object} obj after casting its values + * @api private + */ + +Query.prototype._castUpdate = function _castUpdate (obj) { + var ops = Object.keys(obj) + , i = ops.length + , ret = {} + , hasKeys + , val + + while (i--) { + var op = ops[i]; + if ('$' !== op[0]) { + // fix up $set sugar + if (!ret.$set) { + if (obj.$set) { + ret.$set = obj.$set; + } else { + ret.$set = {}; + } + } + ret.$set[op] = obj[op]; + ops.splice(i, 1); + if (!~ops.indexOf('$set')) ops.push('$set'); + } else if ('$set' === op) { + if (!ret.$set) { + ret[op] = obj[op]; + } + } else { + ret[op] = obj[op]; + } + } + + // cast each value + i = ops.length; + + while (i--) { + op = ops[i]; + val = ret[op]; + if ('Object' === val.constructor.name) { + hasKeys |= this._walkUpdatePath(val, op); + } else { + var msg = 'Invalid atomic update value for ' + op + '. ' + + 'Expected an object, received ' + typeof val; + throw new Error(msg); + } + } + + return hasKeys && ret; +} + +/** + * Walk each path of obj and cast its values + * according to its schema. + * + * @param {Object} obj - part of a query + * @param {String} op - the atomic operator ($pull, $set, etc) + * @param {String} pref - path prefix (internal only) + * @return {Bool} true if this path has keys to update + * @api private + */ + +Query.prototype._walkUpdatePath = function _walkUpdatePath (obj, op, pref) { + var strict = this.model.schema.options.strict + , prefix = pref ? pref + '.' : '' + , keys = Object.keys(obj) + , i = keys.length + , hasKeys = false + , schema + , key + , val + + while (i--) { + key = keys[i]; + val = obj[key]; + + if (val && 'Object' === val.constructor.name) { + // watch for embedded doc schemas + schema = this._getSchema(prefix + key); + if (schema && schema.caster && op in castOps) { + // embedded doc schema + + if (strict && !schema) { + // path is not in our strict schema + if ('throw' == strict) { + throw new Error('Field `' + key + '` is not in schema.'); + } else { + // ignore paths not specified in schema + delete obj[key]; + } + } else { + hasKeys = true; + if ('$each' in val) { + obj[key] = { + $each: this._castUpdateVal(schema, val.$each, op) + } + } else { + obj[key] = this._castUpdateVal(schema, val, op); + } + } + } else { + hasKeys |= this._walkUpdatePath(val, op, prefix + key); + } + } else { + schema = '$each' === key + ? this._getSchema(pref) + : this._getSchema(prefix + key); + + var skip = strict && + !schema && + !/real|nested/.test(this.model.schema.pathType(prefix + key)); + + if (skip) { + if ('throw' == strict) { + throw new Error('Field `' + prefix + key + '` is not in schema.'); + } else { + delete obj[key]; + } + } else { + hasKeys = true; + obj[key] = this._castUpdateVal(schema, val, op, key); + } + } + } + return hasKeys; +} + +/** + * Casts `val` according to `schema` and atomic `op`. + * + * @param {Schema} schema + * @param {Object} val + * @param {String} op - the atomic operator ($pull, $set, etc) + * @param {String} [$conditional] + * @api private + */ + +Query.prototype._castUpdateVal = function _castUpdateVal (schema, val, op, $conditional) { + if (!schema) { + // non-existing schema path + return op in numberOps + ? Number(val) + : val + } + + if (schema.caster && op in castOps && + ('Object' === val.constructor.name || Array.isArray(val))) { + // Cast values for ops that add data to MongoDB. + // Ensures embedded documents get ObjectIds etc. + var tmp = schema.cast(val); + + if (Array.isArray(val)) { + val = tmp; + } else { + val = tmp[0]; + } + } + + if (op in numberOps) return Number(val); + if (/^\$/.test($conditional)) return schema.castForQuery($conditional, val); + return schema.castForQuery(val) +} + +/** + * Finds the schema for `path`. This is different than + * calling `schema.path` as it also resolves paths with + * positional selectors (something.$.another.$.path). + * + * @param {String} path + * @api private + */ + +Query.prototype._getSchema = function _getSchema (path) { + var schema = this.model.schema + , pathschema = schema.path(path); + + if (pathschema) + return pathschema; + + // look for arrays + return (function search (parts, schema) { + var p = parts.length + 1 + , foundschema + , trypath + + while (p--) { + trypath = parts.slice(0, p).join('.'); + foundschema = schema.path(trypath); + if (foundschema) { + if (foundschema.caster) { + + // array of Mixed? + if (foundschema.caster instanceof Types.Mixed) { + return foundschema.caster; + } + + // Now that we found the array, we need to check if there + // are remaining document paths to look up for casting. + // Also we need to handle array.$.path since schema.path + // doesn't work for that. + if (p !== parts.length) { + if ('$' === parts[p]) { + // comments.$.comments.$.title + return search(parts.slice(p+1), foundschema.schema); + } else { + // this is the last path of the selector + return search(parts.slice(p), foundschema.schema); + } + } + } + return foundschema; + } + } + })(path.split('.'), schema) +} + +/** + * Casts selected field arguments for field selection with mongo 2.2 + * + * query.select({ ids: { $elemMatch: { $in: [hexString] }}) + * + * @param {Object} fields + * @see https://github.com/LearnBoost/mongoose/issues/1091 + * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/ + * @api private + */ + +Query.prototype._castFields = function _castFields (fields) { + var selected + , elemMatchKeys + , keys + , key + , out + , i + + if (fields) { + keys = Object.keys(fields); + elemMatchKeys = []; + i = keys.length; + + // collect $elemMatch args + while (i--) { + key = keys[i]; + if (fields[key].$elemMatch) { + selected || (selected = {}); + selected[key] = fields[key]; + elemMatchKeys.push(key); + } + } + } + + if (selected) { + // they passed $elemMatch, cast em + try { + out = this.cast(this.model, selected); + } catch (err) { + return err; + } + + // apply the casted field args + i = elemMatchKeys.length; + while (i--) { + key = elemMatchKeys[i]; + fields[key] = out[key]; + } + } + + return fields; +} + +/** + * Executes this query as a remove() operation. + * + * ####Example + * + * Cassette.where('artist').equals('Anne Murray').remove(callback) + * + * @param {Function} callback + * @api public + */ + +Query.prototype.remove = function (callback) { + this.op = 'remove'; + + var model = this.model + , options = this._optionsForExec(model) + , cb = 'function' == typeof callback + + try { + this.cast(model); + } catch (err) { + if (cb) return callback(err); + throw err; + } + + if (!cb) { + options.safe = { w: 0 }; + } + + var castQuery = this._conditions; + model.collection.remove(castQuery, options, tick(callback)); + return this; +}; + +/** + * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) update command. + * + * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned. + * + * ####Available options + * + * - `new`: bool - true to return the modified document rather than the original. defaults to true + * - `upsert`: bool - creates the object if it doesn't exist. defaults to false. + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * + * ####Examples + * + * query.findOneAndUpdate(conditions, update, options, callback) // executes + * query.findOneAndUpdate(conditions, update, options) // returns Query + * query.findOneAndUpdate(conditions, update, callback) // executes + * query.findOneAndUpdate(conditions, update) // returns Query + * query.findOneAndUpdate(callback) // executes + * query.findOneAndUpdate() // returns Query + * + * @param {Object} [query] + * @param {Object} [doc] + * @param {Object} [options] + * @param {Function} [callback] + * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command + * @return {Query} this + * @api public + */ + +Query.prototype.findOneAndUpdate = function (query, doc, options, callback) { + this.op = 'findOneAndUpdate'; + + switch (arguments.length) { + case 3: + if ('function' == typeof options) + callback = options, options = {}; + break; + case 2: + if ('function' == typeof doc) { + callback = doc; + doc = query; + query = undefined; + } + options = undefined; + break; + case 1: + if ('function' == typeof query) { + callback = query; + query = options = doc = undefined; + } else { + doc = query; + query = options = undefined; + } + } + + // apply query + if (query) { + if ('Object' === query.constructor.name) { + merge(this._conditions, query); + } else if (query instanceof Query) { + merge(this._conditions, query._conditions); + } else if (query instanceof Document) { + merge(this._conditions, query.toObject()); + } + } + + // apply doc + if (doc) { + merge(this._updateArg, doc); + } + + // apply options + options && this.setOptions(options); + + if (!callback) return this; + + return this._findAndModify('update', callback); +} + +/** + * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) remove command. + * + * Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if `callback` is passed else a Query object is returned. + * + * ####Available options + * + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * + * ####Examples + * + * A.where().findOneAndRemove(conditions, options, callback) // executes + * A.where().findOneAndRemove(conditions, options) // return Query + * A.where().findOneAndRemove(conditions, callback) // executes + * A.where().findOneAndRemove(conditions) // returns Query + * A.where().findOneAndRemove(callback) // executes + * A.where().findOneAndRemove() // returns Query + * + * @param {Object} [conditions] + * @param {Object} [options] + * @param {Function} [callback] + * @return {Query} this + * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command + * @api public + */ + +Query.prototype.findOneAndRemove = function (conditions, options, callback) { + this.op = 'findOneAndRemove'; + + if ('function' == typeof options) { + callback = options; + options = undefined; + } else if ('function' == typeof conditions) { + callback = conditions; + conditions = undefined; + } + + // apply conditions + if (conditions) { + if ('Object' === conditions.constructor.name) { + merge(this._conditions, conditions); + } else if (conditions instanceof Query) { + merge(this._conditions, conditions._conditions); + } else if (conditions instanceof Document) { + merge(this._conditions, conditions.toObject()); + } + } + + // apply options + options && this.setOptions(options); + + if (!callback) return this; + + return this._findAndModify('remove', callback); +} + +/** + * _findAndModify + * + * @param {String} type - either "remove" or "update" + * @param {Function} callback + * @api private + */ + +Query.prototype._findAndModify = function (type, callback) { + var model = this.model + , promise = new Promise(callback) + , self = this + , castedQuery + , castedDoc + , fields + , sort + , opts + + castedQuery = castQuery(this); + if (castedQuery instanceof Error) { + process.nextTick(promise.error.bind(promise, castedQuery)); + return promise; + } + + opts = this._optionsForExec(model); + + if ('remove' == type) { + opts.remove = true; + } else { + if (!('new' in opts)) opts.new = true; + if (!('upsert' in opts)) opts.upsert = false; + + castedDoc = castDoc(this); + if (!castedDoc) { + if (opts.upsert) { + // still need to do the upsert to empty doc + castedDoc = { $set: {} }; + } else { + return this.findOne(callback); + } + } else if (castedDoc instanceof Error) { + process.nextTick(promise.error.bind(promise, castedDoc)); + return promise; + } + } + + this._applyPaths(); + + if (this._fields) { + fields = utils.clone(this._fields) + opts.fields = this._castFields(fields); + if (opts.fields instanceof Error) { + process.nextTick(promise.error.bind(promise, opts.fields)); + return promise; + } + } + + // the driver needs a default + sort = opts.sort || []; + + model + .collection + .findAndModify(castedQuery, sort, castedDoc, opts, tick(function (err, doc) { + if (err) return promise.error(err); + if (!doc) return promise.complete(null); + + if (true === opts.lean) { + return promise.complete(doc); + } + + var casted = new model(undefined, fields, true); + casted.init(doc, self, function (err) { + if (err) return promise.error(err); + promise.complete(casted); + }); + })); + + return promise; +} + +/** + * Specifies paths which should be populated with other documents. + * + * Paths are populated after the query executes and a response is received. A separate query is then executed for each path specified for population. After a response for each query has also been returned, the results are passed to the callback. + * + * ####Example: + * + * Kitten.findOne().populate('owner').exec(function (err, kitten) { + * console.log(kitten.owner.name) // Max + * }) + * + * @param {String} path + * @param {Object|String} [fields] + * @param {Model} [model] + * @param {Object} [conditions] + * @param {Object} [options] + * @see population ./populate.html + * @see Query#select #query_Query-select + * @return {Query} this + * @api public + */ + +Query.prototype.populate = function (path, fields, model, conditions, options) { + if ('string' !== typeof model) { + options = conditions; + conditions = model; + model = undefined; + } + // The order of fields/conditions args is opposite Model.find but + // necessary to keep backward compatibility (fields could be + // an array, string, or object literal). + this.options.populate[path] = + new PopulateOptions(fields, conditions, options, model); + + return this; +}; + +/*! + * Populate options constructor + */ + +function PopulateOptions (fields, conditions, options, model) { + this.conditions = conditions; + this.fields = fields; + this.options = options; + this.model = model; +} + +// make it compatible with utils.clone +PopulateOptions.prototype.constructor = Object; + +/** + * Returns a stream interface + * + * ####Example + * + * // follows the nodejs stream api + * Thing.find({ name: /^hello/ }).stream().pipe(res) + * + * // manual streaming + * var stream = Thing.find({ name: /^hello/ }).stream(); + * + * stream.on('data', function (doc) { + * // do something with the mongoose document + * }).on('error', function (err) { + * // handle the error + * }).on('close', function () { + * // the stream is closed + * }); + * + * @return {QueryStream} + * @see QueryStream + * @api public + */ + +Query.prototype.stream = function stream () { + return new QueryStream(this); +} + +// helpers + +/*! + * castDoc + * @api private + */ + +function castDoc (query) { + try { + return query._castUpdate(query._updateArg); + } catch (err) { + return err; + } +} + +/*! + * castQuery + * @api private + */ + +function castQuery (query) { + try { + return query.cast(query.model); + } catch (err) { + return err; + } +} + +/*! + * Exports. + */ + +module.exports = Query; +module.exports.QueryStream = QueryStream; diff --git a/test/node_modules/mongoose/lib/querystream.js b/test/node_modules/mongoose/lib/querystream.js new file mode 100644 index 0000000..69726c4 --- /dev/null +++ b/test/node_modules/mongoose/lib/querystream.js @@ -0,0 +1,313 @@ + +/*! + * Module dependencies. + */ + +var Stream = require('stream').Stream +var utils = require('./utils') + +/** + * Provides a [ReadStream](http://nodejs.org/api/stream.html#stream_readable_stream) interface for Queries. + * + * var stream = Model.find().stream(); + * + * stream.on('data', function (doc) { + * // do something with the mongoose document + * }).on('error', function (err) { + * // handle the error + * }).on('close', function () { + * // the stream is closed + * }); + * + * + * The stream interface allows us to simply "plug-in" to other Node.js write streams (fs.createWriteStream) so everything "just works" out of the box. + * + * Model.where('created').gte(twoWeeksAgo).stream().pipe(writeStream); + * + * _NOTE: plugging into an HTTP response will *not* work out of the box. Those streams expect only strings or buffers to be emitted, so first formatting our documents as strings/buffers is necessary._ + * + * @param {Query} query + * @inherits NodeJS Stream http://nodejs.org/api/stream.html + * @event `data`: emits a single Mongoose document + * @event `error`: emits when an error occurs during streaming. This will emit _before_ the `close` event. + * @event `close`: emits when the stream reaches the end of the cursor or an error occurs, or the stream is manually `destroy`ed. After this event, no more events are emitted. + * @api public + */ + +function QueryStream (query) { + Stream.call(this); + + this.query = query; + this.readable = true; + this.paused = false; + this._cursor = null; + this._destroyed = null; + this._fields = null; + this._buffer = null; + this._inline = T_INIT; + this._running = false; + + // give time to hook up events + var self = this; + process.nextTick(function () { + self._init(); + }); +} + +/*! + * Inherit from Stream + */ + +QueryStream.prototype.__proto__ = Stream.prototype; + +/** + * Flag stating whether or not this stream is readable. + * + * @property readable + * @api public + */ + +QueryStream.prototype.readable; + +/** + * Flag stating whether or not this stream is paused. + * + * @property paused + * @api public + */ + +QueryStream.prototype.paused; + +// trampoline flags +var T_INIT = 0; +var T_IDLE = 1; +var T_CONT = 2; + +/** + * Initializes the query. + * + * @api private + */ + +QueryStream.prototype._init = function () { + if (this._destroyed) return; + + var query = this.query + , model = query.model + , options = query._optionsForExec(model) + , self = this + + try { + query.cast(model); + } catch (err) { + return self.destroy(err); + } + + self._fields = utils.clone(query._fields); + options.fields = query._castFields(self._fields); + + model.collection.find(query._conditions, options, function (err, cursor) { + if (err) return self.destroy(err); + self._cursor = cursor; + self._next(); + }); +} + +/** + * Trampoline for pulling the next doc from cursor. + * + * @see QueryStream#__next #querystream_QueryStream-__next + * @api private + */ + +QueryStream.prototype._next = function _next () { + if (this.paused || this._destroyed) { + return this._running = false; + } + + this._running = true; + + if (this._buffer && this._buffer.length) { + var arg; + while (!this.paused && !this._destroyed && (arg = this._buffer.shift())) { + this._onNextObject.apply(this, arg); + } + } + + // avoid stack overflows with large result sets. + // trampoline instead of recursion. + while (this.__next()) {} +} + +/** + * Pulls the next doc from the cursor. + * + * @see QueryStream#_next #querystream_QueryStream-_next + * @api private + */ + +QueryStream.prototype.__next = function () { + if (this.paused || this._destroyed) + return this._running = false; + + var self = this; + self._inline = T_INIT; + + self._cursor.nextObject(function cursorcb (err, doc) { + self._onNextObject(err, doc); + }); + + // if onNextObject() was already called in this tick + // return ourselves to the trampoline. + if (T_CONT === this._inline) { + return true; + } else { + // onNextObject() hasn't fired yet. tell onNextObject + // that its ok to call _next b/c we are not within + // the trampoline anymore. + this._inline = T_IDLE; + } +} + +/** + * Transforms raw `doc`s returned from the cursor into a model instance. + * + * @param {Error|null} err + * @param {Object} doc + * @api private + */ + +QueryStream.prototype._onNextObject = function _onNextObject (err, doc) { + if (this._destroyed) return; + + if (this.paused) { + this._buffer || (this._buffer = []); + this._buffer.push([err, doc]); + return this._running = false; + } + + if (err) return this.destroy(err); + + // when doc is null we hit the end of the cursor + if (!doc) { + this.emit('end'); + return this.destroy(); + } + + if (this.query.options && true === this.query.options.lean) { + this.emit('data', doc); + + // trampoline management + if (T_IDLE === this._inline) { + // no longer in trampoline. restart it. + this._next(); + } else { + // in a trampoline. tell __next that its + // ok to continue jumping. + this._inline = T_CONT; + } + return; + } + + var instance = new this.query.model(undefined, this._fields, true); + + var self = this; + instance.init(doc, this.query, function (err) { + if (err) return self.destroy(err); + self.emit('data', instance); + + // trampoline management + if (T_IDLE === self._inline) { + // no longer in trampoline. restart it. + self._next(); + } else + // in a trampoline. tell __next that its + // ok to continue jumping. + self._inline = T_CONT; + }); +} + +/** + * Pauses this stream. + * + * @api public + */ + +QueryStream.prototype.pause = function () { + this.paused = true; +} + +/** + * Resumes this stream. + * + * @api public + */ + +QueryStream.prototype.resume = function () { + this.paused = false; + + if (!this._cursor) { + // cannot start if not initialized + return; + } + + // are we within the trampoline? + if (T_INIT === this._inline) { + return; + } + + if (!this._running) { + // outside QueryStream control, need manual restart + return this._next(); + } +} + +/** + * Destroys the stream, closing the underlying cursor. No more events will be emitted. + * + * @param {Error} [err] + * @api public + */ + +QueryStream.prototype.destroy = function (err) { + if (this._destroyed) return; + this._destroyed = true; + this._running = false; + this.readable = false; + + if (this._cursor) { + this._cursor.close(); + } + + if (err) { + this.emit('error', err); + } + + this.emit('close'); +} + +/** + * Pipes this query stream into another stream. This method is inherited from NodeJS Streams. + * + * ####Example: + * + * query.stream().pipe(writeStream [, options]) + * + * This could be particularily useful if you are, for example, setting up an API for a service and want to stream out the docs based on some criteria. We could first pipe the QueryStream into a sort of filter that formats the stream as an array before passing on the document to an http response. + * + * var format = new ArrayFormatter; + * Events.find().stream().pipe(format).pipe(res); + * + * As long as ArrayFormat implements the WriteStream API we can stream large formatted result sets out to the client. See this [gist](https://gist.github.com/1403797) for a hacked example. + * + * @method pipe + * @memberOf QueryStream + * @see NodeJS http://nodejs.org/api/stream.html + * @api public + */ + +/*! + * Module exports + */ + +module.exports = exports = QueryStream; diff --git a/test/node_modules/mongoose/lib/schema.js b/test/node_modules/mongoose/lib/schema.js new file mode 100644 index 0000000..ef6b017 --- /dev/null +++ b/test/node_modules/mongoose/lib/schema.js @@ -0,0 +1,869 @@ +/*! + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , VirtualType = require('./virtualtype') + , utils = require('./utils') + , NamedScope + , Query + , Types + +/** + * Schema constructor. + * + * ####Example: + * + * var child = new Schema({ name: String }); + * var schema = new Schema({ name: String, age: Number, children: [child] }); + * var Tree = mongoose.model('Tree', schema); + * + * // setting schema options + * new Schema({ name: String }, { _id: false, autoIndex: false }) + * + * ####Options: + * + * - [autoIndex](/docs/guide.html#autoIndex): bool - defaults to true + * - [capped](/docs/guide.html#capped): bool - defaults to false + * - [collection](/docs/guide.html#collection): string - no default + * - [id](/docs/guide.html#id): bool - defaults to true + * - [_id](/docs/guide.html#_id): bool - defaults to true + * - [read](/docs/guide.html#read): string + * - [safe](/docs/guide.html#safe): bool - defaults to true. + * - [shardKey](/docs/guide.html#shardKey): bool - defaults to `null` + * - [strict](/docs/guide.html#strict): bool - defaults to true + * - [toJSON](/docs/guide.html#toJSON) - object - no default + * - [toObject](/docs/guide.html#toObject) - object - no default + * - [versionKey](/docs/guide.html#versionKey): bool - defaults to "__v" + * - `minimize`: bool - controls [document#toObject](#document_Document-toObject) behavior when called manually - defaults to true + * + * ####Note: + * + * _When nesting schemas, (`children` in the example above), always declare the child schema first before passing it into is parent._ + * + * @param {Object} definition + * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter + * @event `init`: Emitted after the schema is compiled into a `Model`. + * @api public + */ + +function Schema (obj, options) { + if (!(this instanceof Schema)) + return new Schema(obj, options); + + this.paths = {}; + this.subpaths = {}; + this.virtuals = {}; + this.nested = {}; + this.inherits = {}; + this.callQueue = []; + this._indexes = []; + this.methods = {}; + this.statics = {}; + this.tree = {}; + this._requiredpaths = undefined; + + this.options = this.defaultOptions(options); + + // build paths + if (obj) { + this.add(obj); + } + + // ensure the documents get an auto _id unless disabled + var auto_id = !this.paths['_id'] && (!this.options.noId && this.options._id); + if (auto_id) { + this.add({ _id: {type: Schema.ObjectId, auto: true} }); + } + + // ensure the documents receive an id getter unless disabled + var autoid = !this.paths['id'] && (!this.options.noVirtualId && this.options.id); + if (autoid) { + this.virtual('id').get(idGetter); + } + + // versioning not directly added to schema b/c we only want + // it in the top level document, not embedded ones. +}; + +/*! + * Returns this documents _id cast to a string. + */ + +function idGetter () { + if (this.__id) { + return this.__id; + } + + return this.__id = null == this._id + ? null + : String(this._id); +} + +/*! + * Inherit from EventEmitter. + */ + +Schema.prototype.__proto__ = EventEmitter.prototype; + +/** + * Schema as flat paths + * + * ####Example: + * { + * '_id' : SchemaType, + * , 'nested.key' : SchemaType, + * } + * + * @api private + * @property paths + */ + +Schema.prototype.paths; + +/** + * Schema as a tree + * + * ####Example: + * { + * '_id' : ObjectId + * , 'nested' : { + * 'key' : String + * } + * } + * + * @api private + * @property tree + */ + +Schema.prototype.tree; + +/** + * Returns default options for this schema, merged with `options`. + * + * @param {Object} options + * @return {Object} + * @api private + */ + +Schema.prototype.defaultOptions = function (options) { + if (options && false === options.safe) { + options.safe = { w: 0 }; + } + + options = utils.options({ + strict: true + , capped: false // { size, max, autoIndexId } + , versionKey: '__v' + , minimize: true + , autoIndex: true + , shardKey: null + , read: null + // the following are only applied at construction time + , noId: false // deprecated, use { _id: false } + , _id: true + , noVirtualId: false // deprecated, use { id: false } + , id: true + }, options); + + if (options.read) + options.read = utils.readPref(options.read); + + return options; +} + +/** + * Adds key path / schema type pairs to this schema. + * + * ####Example: + * + * var ToySchema = new Schema; + * ToySchema.add({ name: 'string', color: 'string', price: 'number' }); + * + * @param {Object} obj + * @param {String} prefix + * @api public + */ + +Schema.prototype.add = function add (obj, prefix) { + prefix = prefix || ''; + for (var i in obj) { + if (null == obj[i]) { + throw new TypeError('Invalid value for schema path `'+ prefix + i +'`'); + } + + if (obj[i].constructor.name == 'Object' && (!obj[i].type || obj[i].type.type)) { + if (Object.keys(obj[i]).length) { + // nested object { last: { name: String }} + this.nested[prefix + i] = true; + this.add(obj[i], prefix + i + '.'); + } else { + this.path(prefix + i, obj[i]); // mixed type + } + } else { + this.path(prefix + i, obj[i]); + } + } +}; + +/** + * Reserved document keys. + * + * Keys in this object are names that are rejected in schema declarations b/c they conflict with mongoose functionality. Using these key name will throw an error. + * + * on, db, init, isNew, errors, schema, options, modelName, collection + * + * _NOTE:_ Use of these terms as method names is permitted, but play at your own risk, as they may be existing mongoose document methods you are stomping on. + * + * var schema = new Schema(..); + * schema.methods.init = function () {} // potentially breaking + */ + +Schema.reserved = Object.create(null); +var reserved = Schema.reserved; +reserved.on = +reserved.db = +reserved.init = +reserved.isNew = +reserved.errors = +reserved.schema = +reserved.options = +reserved.modelName = +reserved._pres = reserved._posts = // hooks.js +reserved.collection = 1; + +/** + * Gets/sets schema paths. + * + * Sets a path (if arity 2) + * Gets a path (if arity 1) + * + * ####Example + * + * schema.path('name') // returns a SchemaType + * schema.path('name', Number) // changes the schemaType of `name` to Number + * + * @param {String} path + * @param {Object} constructor + * @api public + */ + +Schema.prototype.path = function (path, obj) { + if (obj == undefined) { + if (this.paths[path]) return this.paths[path]; + if (this.subpaths[path]) return this.subpaths[path]; + + // subpaths? + return /\.\d+\.?$/.test(path) + ? getPositionalPath(this, path) + : undefined; + } + + // some path names conflict with document methods + if (reserved[path]) { + throw new Error("`" + path + "` may not be used as a schema pathname"); + } + + // update the tree + var subpaths = path.split(/\./) + , last = subpaths.pop() + , branch = this.tree; + + subpaths.forEach(function(sub, i) { + if (!branch[sub]) branch[sub] = {}; + if ('object' != typeof branch[sub]) { + var msg = 'Cannot set nested path `' + path + '`. ' + + 'Parent path `' + + subpaths.slice(0, i).concat([sub]).join('.') + + '` already set to type ' + branch[sub].name + + '.'; + throw new Error(msg); + } + branch = branch[sub]; + }); + + branch[last] = utils.clone(obj); + + this.paths[path] = Schema.interpretAsType(path, obj); + return this; +}; + +/** + * Converts type arguments into Mongoose Types. + * + * @param {String} path + * @param {Object} obj constructor + * @api private + */ + +Schema.interpretAsType = function (path, obj) { + if (obj.constructor.name != 'Object') + obj = { type: obj }; + + // Get the type making sure to allow keys named "type" + // and default to mixed if not specified. + // { type: { type: String, default: 'freshcut' } } + var type = obj.type && !obj.type.type + ? obj.type + : {}; + + if ('Object' == type.constructor.name || 'mixed' == type) { + return new Types.Mixed(path, obj); + } + + if (Array.isArray(type) || Array == type || 'array' == type) { + // if it was specified through { type } look for `cast` + var cast = (Array == type || 'array' == type) + ? obj.cast + : type[0]; + + if (cast instanceof Schema) { + return new Types.DocumentArray(path, cast, obj); + } + + if ('string' == typeof cast) { + cast = Types[cast.charAt(0).toUpperCase() + cast.substring(1)]; + } else if (cast && (!cast.type || cast.type.type) + && 'Object' == cast.constructor.name + && Object.keys(cast).length) { + return new Types.DocumentArray(path, new Schema(cast), obj); + } + + return new Types.Array(path, cast || Types.Mixed, obj); + } + + var name = 'string' == typeof type + ? type + : type.name; + + if (name) { + name = name.charAt(0).toUpperCase() + name.substring(1); + } + + if (undefined == Types[name]) { + throw new TypeError('Undefined type at `' + path + + '`\n Did you try nesting Schemas? ' + + 'You can only nest using refs or arrays.'); + } + + return new Types[name](path, obj); +}; + +/** + * Iterates the schemas paths similar to Array#forEach. + * + * The callback is passed the pathname and schemaType as arguments on each iteration. + * + * @param {Function} fn callback function + * @return {Schema} this + * @api public + */ + +Schema.prototype.eachPath = function (fn) { + var keys = Object.keys(this.paths) + , len = keys.length; + + for (var i = 0; i < len; ++i) { + fn(keys[i], this.paths[keys[i]]); + } + + return this; +}; + +/** + * Returns an Array of path strings that are required by this schema. + * + * @api public + * @return {Array} + */ + +Schema.prototype.requiredPaths = function requiredPaths () { + if (this._requiredpaths) return this._requiredpaths; + + var paths = Object.keys(this.paths) + , i = paths.length + , ret = []; + + while (i--) { + var path = paths[i]; + if (this.paths[path].isRequired) ret.push(path); + } + + return this._requiredpaths = ret; +} + +/** + * Returns the pathType of `path` for this schema. + * + * Given a path, returns whether it is a real, virtual, nested, or ad-hoc/undefined path. + * + * @param {String} path + * @return {String} + * @api public + */ + +Schema.prototype.pathType = function (path) { + if (path in this.paths) return 'real'; + if (path in this.virtuals) return 'virtual'; + if (path in this.nested) return 'nested'; + if (path in this.subpaths) return 'real'; + + if (/\.\d+\.?/.test(path) && getPositionalPath(this, path)) { + return 'real'; + } else { + return 'adhocOrUndefined' + } +}; + +/*! + * ignore + */ + +function getPositionalPath (self, path) { + var subpaths = path.split(/\.(\d+)\.?/).filter(Boolean); + if (subpaths.length < 2) { + return self.paths[subpaths[0]]; + } + + var val = self.path(subpaths[0]); + if (!val) return val; + + var last = subpaths.length - 1 + , subpath + , i = 1; + + for (; i < subpaths.length; ++i) { + subpath = subpaths[i]; + + if (i === last && + val && + !val.schema && + !/\D/.test(subpath) && + val instanceof Types.Array) { + // StringSchema, NumberSchema, etc + val = val.caster; + continue; + } + + // 'path.0.subpath' + if (!/\D/.test(subpath)) continue; + val = val.schema.path(subpath); + } + + return self.subpaths[path] = val; +} + +/** + * Adds a method call to the queue. + * + * @param {String} name name of the document method to call later + * @param {Array} args arguments to pass to the method + * @api private + */ + +Schema.prototype.queue = function(name, args){ + this.callQueue.push([name, args]); + return this; +}; + +/** + * Defines a pre hook for the document. + * + * ####Example + * + * var toySchema = new Schema(..); + * + * toySchema.pre('save', function (next) { + * if (!this.created) this.created = new Date; + * next(); + * }) + * + * toySchema.pre('validate', function (next) { + * if (this.name != 'Woody') this.name = 'Woody'; + * next(); + * }) + * + * @param {String} method + * @param {Function} callback + * @see hooks.js https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3 + * @api public + */ + +Schema.prototype.pre = function(){ + return this.queue('pre', arguments); +}; + +/** + * Defines a post for the document + * + * Post hooks fire `on` the event emitted from document instances of Models compiled from this schema. + * + * var schema = new Schema(..); + * schema.post('save', function (doc) { + * console.log('this fired after a document was saved'); + * }); + * + * var Model = mongoose.model('Model', schema); + * + * var m = new Model(..); + * m.save(function (err) { + * console.log('this fires after the `post` hook'); + * }); + * + * @param {String} method name of the method to hook + * @param {Function} fn callback + * @see hooks.js https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3 + * @api public + */ + +Schema.prototype.post = function(method, fn){ + return this.queue('on', arguments); +}; + +/** + * Registers a plugin for this schema. + * + * @param {Function} plugin callback + * @param {Object} opts + * @see plugins + * @api public + */ + +Schema.prototype.plugin = function (fn, opts) { + fn(this, opts); + return this; +}; + +/** + * Adds an instance method to documents constructed from Models compiled from this schema. + * + * ####Example + * + * var schema = kittySchema = new Schema(..); + * + * schema.methods.meow = function () { + * console.log('meeeeeoooooooooooow'); + * }) + * + * var Kitty = mongoose.model('Kitty', schema); + * + * var fizz = new Kitty; + * fizz.meow(); // meeeeeooooooooooooow + * + * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as methods. + * + * schema.method({ + * purr: function () {} + * , scratch: function () {} + * }); + * + * // later + * fizz.purr(); + * fizz.scratch(); + * + * @param {String|Object} method name + * @param {Function} [fn] + * @api public + */ + +Schema.prototype.method = function (name, fn) { + if ('string' != typeof name) + for (var i in name) + this.methods[i] = name[i]; + else + this.methods[name] = fn; + return this; +}; + +/** + * Adds static "class" methods to Models compiled from this schema. + * + * ####Example + * + * var schema = new Schema(..); + * schema.static('findByName', function (name, callback) { + * return this.find({ name: name }, callback); + * }); + * + * var Drink = mongoose.model('Drink', schema); + * Drink.findByName('sanpellegrino', function (err, drinks) { + * // + * }); + * + * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as statics. + * + * @param {String} name + * @param {Function} fn + * @api public + */ + +Schema.prototype.static = function(name, fn) { + if ('string' != typeof name) + for (var i in name) + this.statics[i] = name[i]; + else + this.statics[name] = fn; + return this; +}; + +/** + * Defines an index (most likely compound) for this schema. + * + * ####Example + * + * schema.index({ first: 1, last: -1 }) + * + * @param {Object} fields + * @param {Object} [options] + * @api public + */ + +Schema.prototype.index = function (fields, options) { + options || (options = {}); + + if (options.expires) + utils.expires(options); + + this._indexes.push([fields, options]); + return this; +}; + +/** + * Sets/gets a schema option. + * + * @param {String} key option name + * @param {Object} [value] if not passed, the current option value is returned + * @api public + */ + +Schema.prototype.set = function (key, value, _tags) { + if (1 === arguments.length) { + return this.options[key]; + } + + switch (key) { + case 'read': + this.options[key] = utils.readPref(value, _tags) + break; + case 'safe': + this.options[key] = false === value + ? { w: 0 } + : value + break; + default: + this.options[key] = value; + } + + return this; +} + +/** + * Gets a schema option. + * + * @param {String} key option name + * @api public + */ + +Schema.prototype.get = function (key) { + return this.options[key]; +} + +/** + * Compiles indexes from fields and schema-level indexes + * + * @api public + */ + +Schema.prototype.indexes = function () { + var indexes = [] + , seenSchemas = []; + + collectIndexes(this); + + return indexes; + + function collectIndexes (schema, prefix) { + if (~seenSchemas.indexOf(schema)) return; + seenSchemas.push(schema); + + var index; + var paths = schema.paths; + prefix = prefix || ''; + + for (var i in paths) { + if (paths[i]) { + if (paths[i] instanceof Types.DocumentArray) { + collectIndexes(paths[i].schema, i + '.'); + } else { + index = paths[i]._index; + + if (index !== false && index !== null){ + var field = {}; + field[prefix + i] = '2d' === index ? index : 1; + var options = 'Object' === index.constructor.name ? index : {}; + if (!('background' in options)) options.background = true; + indexes.push([field, options]); + } + } + } + } + + if (prefix) { + fixSubIndexPaths(schema, prefix); + } else { + schema._indexes.forEach(function (index) { + if (!('background' in index[1])) index[1].background = true; + }); + indexes = indexes.concat(schema._indexes); + } + } + + /*! + * Checks for indexes added to subdocs using Schema.index(). + * These indexes need their paths prefixed properly. + * + * schema._indexes = [ [indexObj, options], [indexObj, options] ..] + */ + + function fixSubIndexPaths (schema, prefix) { + var subindexes = schema._indexes + , len = subindexes.length + , indexObj + , newindex + , klen + , keys + , key + , i = 0 + , j + + for (i = 0; i < len; ++i) { + indexObj = subindexes[i][0]; + keys = Object.keys(indexObj); + klen = keys.length; + newindex = {}; + + // use forward iteration, order matters + for (j = 0; j < klen; ++j) { + key = keys[j]; + newindex[prefix + key] = indexObj[key]; + } + + indexes.push([newindex, subindexes[i][1]]); + } + } +} + +/** + * Creates a virtual type with the given name. + * + * @param {String} name + * @param {Object} [options] + * @return {VirtualType} + */ + +Schema.prototype.virtual = function (name, options) { + var virtuals = this.virtuals; + var parts = name.split('.'); + return virtuals[name] = parts.reduce(function (mem, part, i) { + mem[part] || (mem[part] = (i === parts.length-1) + ? new VirtualType(options, name) + : {}); + return mem[part]; + }, this.tree); +}; + +/** + * Returns the virtual type with the given `name`. + * + * @param {String} name + * @return {VirtualType} + */ + +Schema.prototype.virtualpath = function (name) { + return this.virtuals[name]; +}; + +/** + * These still haven't been fixed. Once they're working we'll make them public again. + * @api private + */ + +Schema.prototype.namedScope = function (name, fn) { + var namedScopes = this.namedScopes || (this.namedScopes = new NamedScope) + , newScope = Object.create(namedScopes) + , allScopes = namedScopes.scopesByName || (namedScopes.scopesByName = {}); + allScopes[name] = newScope; + newScope.name = name; + newScope.block = fn; + newScope.query = new Query(); + newScope.decorate(namedScopes, { + block0: function (block) { + return function () { + block.call(this.query); + return this; + }; + }, + blockN: function (block) { + return function () { + block.apply(this.query, arguments); + return this; + }; + }, + basic: function (query) { + return function () { + this.query.find(query); + return this; + }; + } + }); + return newScope; +}; + +/*! + * Module exports. + */ + +module.exports = exports = Schema; + +// require down here because of reference issues + +/** + * The various Mongoose Schema Types. + * + * ####Example: + * + * ####Example: + * + * var mongoose = require('mongoose'); + * var ObjectId = mongoose.Schema.Types.ObjectId; + * + * ####Types: + * + * - String + * - Number + * - Boolean | Bool + * - Array + * - Buffer + * - Date + * - ObjectId | Oid + * - Mixed + * + * Using this exposed access to the `Mixed` SchemaType, we can use them in our schema. + * + * var Mixed = mongoose.Schema.Types.Mixed; + * new mongoose.Schema({ _user: Mixed }) + * + * @api public + */ + +Schema.Types = require('./schema/index'); + +/*! + * ignore + */ + +Types = Schema.Types; +NamedScope = require('./namedscope') +Query = require('./query'); +var ObjectId = exports.ObjectId = Types.ObjectId; + diff --git a/test/node_modules/mongoose/lib/schema/array.js b/test/node_modules/mongoose/lib/schema/array.js new file mode 100644 index 0000000..89ab4d4 --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/array.js @@ -0,0 +1,261 @@ +/*! + * Module dependencies. + */ + +var SchemaType = require('../schematype') + , CastError = SchemaType.CastError + , NumberSchema = require('./number') + , Types = { + Boolean: require('./boolean') + , Date: require('./date') + , Number: require('./number') + , String: require('./string') + , ObjectId: require('./objectid') + , Buffer: require('./buffer') + } + , MongooseArray = require('../types').Array + , EmbeddedDoc = require('../types').Embedded + , Mixed = require('./mixed') + , Query = require('../query') + , isMongooseObject = require('../utils').isMongooseObject + +/** + * Array SchemaType constructor + * + * @param {String} key + * @param {SchemaType} cast + * @param {Object} options + * @inherits SchemaType + * @api private + */ + +function SchemaArray (key, cast, options) { + if (cast) { + var castOptions = {}; + + if ('Object' === cast.constructor.name) { + if (cast.type) { + // support { type: Woot } + castOptions = cast; + cast = cast.type; + delete castOptions.type; + } else { + cast = Mixed; + } + } + + var caster = cast.name in Types ? Types[cast.name] : cast; + this.casterConstructor = caster; + this.caster = new caster(null, castOptions); + if (!(this.caster instanceof EmbeddedDoc)) { + this.caster.path = key; + } + } + + SchemaType.call(this, key, options); + + var self = this + , defaultArr + , fn; + + if (this.defaultValue) { + defaultArr = this.defaultValue; + fn = 'function' == typeof defaultArr; + } + + this.default(function(){ + var arr = fn ? defaultArr() : defaultArr || []; + return new MongooseArray(arr, self.path, this); + }); +}; + +/*! + * Inherits from SchemaType. + */ + +SchemaArray.prototype.__proto__ = SchemaType.prototype; + +/** + * Check required + * + * @param {Array} value + * @api private + */ + +SchemaArray.prototype.checkRequired = function (value) { + return !!(value && value.length); +}; + +/** + * Overrides the getters application for the population special-case + * + * @param {Object} value + * @param {Object} scope + * @api private + */ + +SchemaArray.prototype.applyGetters = function (value, scope) { + if (this.caster.options && this.caster.options.ref) { + // means the object id was populated + return value; + } + + return SchemaType.prototype.applyGetters.call(this, value, scope); +}; + +/** + * Casts contents + * + * @param {Object} value + * @param {Document} doc document that triggers the casting + * @param {Boolean} init whether this is an initialization cast + * @api private + */ + +SchemaArray.prototype.cast = function (value, doc, init) { + if (Array.isArray(value)) { + if (!(value instanceof MongooseArray)) { + value = new MongooseArray(value, this.path, doc); + } + + if (this.caster) { + try { + for (var i = 0, l = value.length; i < l; i++) { + value[i] = this.caster.cast(value[i], doc, init); + } + } catch (e) { + // rethrow + throw new CastError(e.type, value, this.path); + } + } + + return value; + } else { + return this.cast([value], doc, init); + } +}; + +/** + * Casts contents for queries. + * + * @param {String} $conditional + * @param {any} [value] + * @api private + */ + +SchemaArray.prototype.castForQuery = function ($conditional, value) { + var handler + , val; + if (arguments.length === 2) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) + throw new Error("Can't use " + $conditional + " with Array."); + val = handler.call(this, value); + } else { + val = $conditional; + var proto = this.casterConstructor.prototype; + var method = proto.castForQuery || proto.cast; + + var caster = this.caster; + if (Array.isArray(val)) { + val = val.map(function (v) { + if (method) v = method.call(caster, v); + + return isMongooseObject(v) + ? v.toObject() + : v; + }); + } else if (method) { + val = method.call(caster, val); + } + } + return val && isMongooseObject(val) + ? val.toObject() + : val; +}; + +/*! + * @ignore + */ + +function castToNumber (val) { + return Types.Number.prototype.cast.call(this, val); +} + +SchemaArray.prototype.$conditionalHandlers = { + '$all': function handle$all (val) { + if (!Array.isArray(val)) { + val = [val]; + } + + val = val.map(function (v) { + if (v && 'Object' === v.constructor.name) { + var o = {}; + o[this.path] = v; + var query = new Query(o); + query.cast(this.casterConstructor); + return query._conditions[this.path]; + } + return v; + }, this); + + return this.castForQuery(val); + } + , '$elemMatch': function (val) { + if (val.$in) { + val.$in = this.castForQuery('$in', val.$in); + return val; + } + + var query = new Query(val); + query.cast(this.casterConstructor); + return query._conditions; + } + , '$size': castToNumber + , '$ne': SchemaArray.prototype.castForQuery + , '$in': SchemaArray.prototype.castForQuery + , '$nin': SchemaArray.prototype.castForQuery + , '$regex': SchemaArray.prototype.castForQuery + , '$near': SchemaArray.prototype.castForQuery + , '$nearSphere': SchemaArray.prototype.castForQuery + , '$gt': SchemaArray.prototype.castForQuery + , '$gte': SchemaArray.prototype.castForQuery + , '$lt': SchemaArray.prototype.castForQuery + , '$lte': SchemaArray.prototype.castForQuery + , '$within': function (val) { + var self = this; + + if (val.$maxDistance) { + val.$maxDistance = castToNumber.call(this, val.$maxDistance); + } + + if (val.$box || val.$polygon) { + var type = val.$box ? '$box' : '$polygon'; + val[type].forEach(function (arr) { + arr.forEach(function (v, i) { + arr[i] = castToNumber.call(this, v); + }); + }) + } else if (val.$center || val.$centerSphere) { + var type = val.$center ? '$center' : '$centerSphere'; + val[type].forEach(function (item, i) { + if (Array.isArray(item)) { + item.forEach(function (v, j) { + item[j] = castToNumber.call(this, v); + }); + } else { + val[type][i] = castToNumber.call(this, item); + } + }) + } + + return val; + } + , '$maxDistance': castToNumber +}; + +/*! + * Module exports. + */ + +module.exports = SchemaArray; diff --git a/test/node_modules/mongoose/lib/schema/boolean.js b/test/node_modules/mongoose/lib/schema/boolean.js new file mode 100644 index 0000000..05635bc --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/boolean.js @@ -0,0 +1,91 @@ + +/*! + * Module dependencies. + */ + +var SchemaType = require('../schematype'); + +/** + * Boolean SchemaType constructor. + * + * @param {String} path + * @param {Object} options + * @inherits SchemaType + * @api private + */ + +function SchemaBoolean (path, options) { + SchemaType.call(this, path, options); +}; + +/*! + * Inherits from SchemaType. + */ +SchemaBoolean.prototype.__proto__ = SchemaType.prototype; + +/** + * Required validator + * + * @api private + */ + +SchemaBoolean.prototype.checkRequired = function (value) { + return value === true || value === false; +}; + +/** + * Casts to boolean + * + * @param {Object} value + * @api private + */ + +SchemaBoolean.prototype.cast = function (value) { + if (value === null) return value; + if (value === '0') return false; + return !!value; +}; + +/*! + * ignore + */ + +function handleArray (val) { + var self = this; + return val.map(function (m) { + return self.cast(m); + }); +} + +SchemaBoolean.$conditionalHandlers = { + '$in': handleArray +} + +/** + * Casts contents for queries. + * + * @param {String} $conditional + * @param {any} val + * @api private + */ + +SchemaBoolean.prototype.castForQuery = function ($conditional, val) { + var handler; + if (2 === arguments.length) { + handler = SchemaBoolean.$conditionalHandlers[$conditional]; + + if (handler) { + return handler.call(this, val); + } + + return this.cast(val); + } + + return this.cast($conditional); +}; + +/*! + * Module exports. + */ + +module.exports = SchemaBoolean; diff --git a/test/node_modules/mongoose/lib/schema/buffer.js b/test/node_modules/mongoose/lib/schema/buffer.js new file mode 100644 index 0000000..22c4c0e --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/buffer.js @@ -0,0 +1,118 @@ +/*! + * Module dependencies. + */ + +var SchemaType = require('../schematype') + , CastError = SchemaType.CastError + , MongooseBuffer = require('../types').Buffer + , Binary = MongooseBuffer.Binary + , Query = require('../query'); + +/** + * Buffer SchemaType constructor + * + * @param {String} key + * @param {SchemaType} cast + * @inherits SchemaType + * @api private + */ + +function SchemaBuffer (key, options) { + SchemaType.call(this, key, options, 'Buffer'); +}; + +/*! + * Inherits from SchemaType. + */ + +SchemaBuffer.prototype.__proto__ = SchemaType.prototype; + +/** + * Check required + * + * @api private + */ + +SchemaBuffer.prototype.checkRequired = function (value) { + return !!(value && value.length); +}; + +/** + * Casts contents + * + * @param {Object} value + * @param {Document} doc document that triggers the casting + * @param {Boolean} init + * @api private + */ + +SchemaBuffer.prototype.cast = function (value, doc, init) { + if (SchemaType._isRef(this, value, init)) return value; + + if (Buffer.isBuffer(value)) { + if (!(value instanceof MongooseBuffer)) { + value = new MongooseBuffer(value, [this.path, doc]); + } + + return value; + } else if (value instanceof Binary) { + return new MongooseBuffer(value.value(true), [this.path, doc]); + } + + if ('string' === typeof value || Array.isArray(value)) { + return new MongooseBuffer(value, [this.path, doc]); + } + + throw new CastError('buffer', value, this.path); +}; + +/*! + * ignore + */ +function handleSingle (val) { + return this.castForQuery(val); +} + +function handleArray (val) { + var self = this; + return val.map( function (m) { + return self.castForQuery(m); + }); +} + +SchemaBuffer.prototype.$conditionalHandlers = { + '$ne' : handleSingle + , '$in' : handleArray + , '$nin': handleArray + , '$gt' : handleSingle + , '$lt' : handleSingle + , '$gte': handleSingle + , '$lte': handleSingle +}; + +/** + * Casts contents for queries. + * + * @param {String} $conditional + * @param {any} [value] + * @api private + */ + +SchemaBuffer.prototype.castForQuery = function ($conditional, val) { + var handler; + if (arguments.length === 2) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) + throw new Error("Can't use " + $conditional + " with Buffer."); + return handler.call(this, val); + } else { + val = $conditional; + return this.cast(val).toObject(); + } +}; + +/*! + * Module exports. + */ + +module.exports = SchemaBuffer; diff --git a/test/node_modules/mongoose/lib/schema/date.js b/test/node_modules/mongoose/lib/schema/date.js new file mode 100644 index 0000000..fecd7d9 --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/date.js @@ -0,0 +1,126 @@ + +/*! + * Module requirements. + */ + +var SchemaType = require('../schematype') + , CastError = SchemaType.CastError; + +/** + * Date SchemaType constructor. + * + * @param {String} key + * @param {Object} options + * @inherits SchemaType + * @api private + */ + +function SchemaDate (key, options) { + SchemaType.call(this, key, options); +}; + +/*! + * Inherits from SchemaType. + */ + +SchemaDate.prototype.__proto__ = SchemaType.prototype; + +/** + * Required validator for date + * + * @api private + */ + +SchemaDate.prototype.checkRequired = function (value) { + return value instanceof Date; +}; + +/** + * Casts to date + * + * @param {Object} value to cast + * @api private + */ + +SchemaDate.prototype.cast = function (value) { + if (value === null || value === '') + return null; + + if (value instanceof Date) + return value; + + var date; + + // support for timestamps + if (value instanceof Number || 'number' == typeof value + || String(value) == Number(value)) + date = new Date(Number(value)); + + // support for date strings + else if (value.toString) + date = new Date(value.toString()); + + if (date.toString() != 'Invalid Date') + return date; + + throw new CastError('date', value, this.path); +}; + +/*! + * Date Query casting. + * + * @api private + */ + +function handleSingle (val) { + return this.cast(val); +} + +function handleArray (val) { + var self = this; + return val.map( function (m) { + return self.cast(m); + }); +} + +SchemaDate.prototype.$conditionalHandlers = { + '$lt': handleSingle + , '$lte': handleSingle + , '$gt': handleSingle + , '$gte': handleSingle + , '$ne': handleSingle + , '$in': handleArray + , '$nin': handleArray + , '$all': handleArray +}; + + +/** + * Casts contents for queries. + * + * @param {String} $conditional + * @param {any} [value] + * @api private + */ + +SchemaDate.prototype.castForQuery = function ($conditional, val) { + var handler; + + if (2 !== arguments.length) { + return this.cast($conditional); + } + + handler = this.$conditionalHandlers[$conditional]; + + if (!handler) { + throw new Error("Can't use " + $conditional + " with Date."); + } + + return handler.call(this, val); +}; + +/*! + * Module exports. + */ + +module.exports = SchemaDate; diff --git a/test/node_modules/mongoose/lib/schema/documentarray.js b/test/node_modules/mongoose/lib/schema/documentarray.js new file mode 100644 index 0000000..93f2455 --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/documentarray.js @@ -0,0 +1,189 @@ + +/*! + * Module dependencies. + */ + +var SchemaType = require('../schematype') + , ArrayType = require('./array') + , MongooseDocumentArray = require('../types/documentarray') + , Subdocument = require('../types/embedded') + , Document = require('../document'); + +/** + * SubdocsArray SchemaType constructor + * + * @param {String} key + * @param {Schema} schema + * @param {Object} options + * @inherits SchemaArray + * @api private + */ + +function DocumentArray (key, schema, options) { + + // compile an embedded document for this schema + function EmbeddedDocument () { + Subdocument.apply(this, arguments); + } + + EmbeddedDocument.prototype.__proto__ = Subdocument.prototype; + EmbeddedDocument.prototype._setSchema(schema); + EmbeddedDocument.schema = schema; + + // apply methods + for (var i in schema.methods) { + EmbeddedDocument.prototype[i] = schema.methods[i]; + } + + // apply statics + for (var i in schema.statics) + EmbeddedDocument[i] = schema.statics[i]; + + EmbeddedDocument.options = options; + this.schema = schema; + + ArrayType.call(this, key, EmbeddedDocument, options); + + this.schema = schema; + var path = this.path; + var fn = this.defaultValue; + + this.default(function(){ + var arr = fn.call(this); + if (!Array.isArray(arr)) arr = [arr]; + return new MongooseDocumentArray(arr, path, this); + }); +}; + +/*! + * Inherits from ArrayType. + */ + +DocumentArray.prototype.__proto__ = ArrayType.prototype; + +/** + * Performs local validations first, then validations on each embedded doc + * + * @api private + */ + +DocumentArray.prototype.doValidate = function (array, fn, scope) { + var self = this; + + SchemaType.prototype.doValidate.call(this, array, function (err) { + if (err) return fn(err); + + var count = array && array.length + , error; + + if (!count) return fn(); + + // handle sparse arrays, do not use array.forEach which does not + // iterate over sparse elements yet reports array.length including + // them :( + + for (var i = 0, len = count; i < len; ++i) { + // sidestep sparse entries + var doc = array[i]; + if (!doc) { + --count || fn(); + continue; + } + + ;(function (i) { + doc.validate(function (err) { + if (err && !error) { + // rewrite the key + err.key = self.key + '.' + i + '.' + err.key; + return fn(error = err); + } + --count || fn(); + }); + })(i); + } + }, scope); +}; + +/** + * Casts contents + * + * @param {Object} value + * @param {Document} document that triggers the casting + * @api private + */ + +DocumentArray.prototype.cast = function (value, doc, init, prev) { + var selected + , subdoc + , i + + if (!Array.isArray(value)) { + return this.cast([value], doc, init, prev); + } + + if (!(value instanceof MongooseDocumentArray)) { + value = new MongooseDocumentArray(value, this.path, doc); + } + + i = value.length; + + while (i--) { + if (!(value[i] instanceof Subdocument) && value[i]) { + if (init) { + selected || (selected = scopePaths(this, doc._selected, init)); + subdoc = new this.casterConstructor(null, value, true, selected); + value[i] = subdoc.init(value[i]); + } else { + if (prev && (subdoc = prev.id(value[i]._id))) { + // handle resetting doc with existing id but differing data + // doc.array = [{ doc: 'val' }] + subdoc.set(value[i]); + } else { + subdoc = new this.casterConstructor(value[i], value); + } + + // if set() is hooked it will have no return value + // see gh-746 + value[i] = subdoc; + } + } + } + + return value; +} + +/*! + * Scopes paths selected in a query to this array. + * Necessary for proper default application of subdocument values. + * + * @param {DocumentArray} array - the array to scope `fields` paths + * @param {Object|undefined} fields - the root fields selected in the query + * @param {Boolean|undefined} init - if we are being created part of a query result + */ + +function scopePaths (array, fields, init) { + if (!(init && fields)) return undefined; + + var path = array.path + '.' + , keys = Object.keys(fields) + , i = keys.length + , selected = {} + , hasKeys + , key + + while (i--) { + key = keys[i]; + if (0 === key.indexOf(path)) { + hasKeys || (hasKeys = true); + selected[key.substring(path.length)] = fields[key]; + } + } + + return hasKeys && selected || undefined; +} + +/*! + * Module exports. + */ + +module.exports = DocumentArray; diff --git a/test/node_modules/mongoose/lib/schema/index.js b/test/node_modules/mongoose/lib/schema/index.js new file mode 100644 index 0000000..d1347ed --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/index.js @@ -0,0 +1,28 @@ + +/*! + * Module exports. + */ + +exports.String = require('./string'); + +exports.Number = require('./number'); + +exports.Boolean = require('./boolean'); + +exports.DocumentArray = require('./documentarray'); + +exports.Array = require('./array'); + +exports.Buffer = require('./buffer'); + +exports.Date = require('./date'); + +exports.ObjectId = require('./objectid'); + +exports.Mixed = require('./mixed'); + +// alias + +exports.Oid = exports.ObjectId; +exports.Object = exports.Mixed; +exports.Bool = exports.Boolean; diff --git a/test/node_modules/mongoose/lib/schema/mixed.js b/test/node_modules/mongoose/lib/schema/mixed.js new file mode 100644 index 0000000..f22ebb9 --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/mixed.js @@ -0,0 +1,75 @@ + +/*! + * Module dependencies. + */ + +var SchemaType = require('../schematype'); + +/** + * Mixed SchemaType constructor. + * + * @param {String} path + * @param {Object} options + * @inherits SchemaType + * @api private + */ + +function Mixed (path, options) { + // make sure empty array defaults are handled + if (options && + options.default && + Array.isArray(options.default) && + 0 === options.default.length) { + options.default = Array; + } + + SchemaType.call(this, path, options); +}; + +/*! + * Inherits from SchemaType. + */ + +Mixed.prototype.__proto__ = SchemaType.prototype; + +/** + * Required validator + * + * @api private + */ + +Mixed.prototype.checkRequired = function (val) { + return true; +}; + +/** + * Casts `val` for Mixed. + * + * _this is a no-op_ + * + * @param {Object} value to cast + * @api private + */ + +Mixed.prototype.cast = function (val) { + return val; +}; + +/** + * Casts contents for queries. + * + * @param {String} $cond + * @param {any} [val] + * @api private + */ + +Mixed.prototype.castForQuery = function ($cond, val) { + if (arguments.length === 2) return val; + return $cond; +}; + +/*! + * Module exports. + */ + +module.exports = Mixed; diff --git a/test/node_modules/mongoose/lib/schema/number.js b/test/node_modules/mongoose/lib/schema/number.js new file mode 100644 index 0000000..f0282df --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/number.js @@ -0,0 +1,182 @@ +/*! + * Module requirements. + */ + +var SchemaType = require('../schematype') + , CastError = SchemaType.CastError + +/** + * Number SchemaType constructor. + * + * @param {String} key + * @param {Object} options + * @inherits SchemaType + * @api private + */ + +function SchemaNumber (key, options) { + SchemaType.call(this, key, options, 'Number'); +}; + +/*! + * Inherits from SchemaType. + */ + +SchemaNumber.prototype.__proto__ = SchemaType.prototype; + +/** + * Required validator for number + * + * @api private + */ + +SchemaNumber.prototype.checkRequired = function checkRequired (value) { + if (SchemaType._isRef(this, value, true)) { + return null != value; + } else { + return typeof value == 'number' || value instanceof Number; + } +}; + +/** + * Sets a maximum number validator. + * + * ####Example: + * + * var s = new Schema({ n: { type: Number, min: 10 }) + * var M = db.model('M', s) + * var m = new M({ n: 9 }) + * m.save(function (err) { + * console.error(err) // validator error + * m.n = 10; + * m.save() // success + * }) + * + * @param {Number} value minimum number + * @param {String} message + * @api public + */ + +SchemaNumber.prototype.min = function (value, message) { + if (this.minValidator) + this.validators = this.validators.filter(function(v){ + return v[1] != 'min'; + }); + if (value != null) + this.validators.push([function(v){ + return v === null || v >= value; + }, 'min']); + return this; +}; + +/** + * Sets a maximum number validator. + * + * ####Example: + * + * var s = new Schema({ n: { type: Number, max: 10 }) + * var M = db.model('M', s) + * var m = new M({ n: 11 }) + * m.save(function (err) { + * console.error(err) // validator error + * m.n = 10; + * m.save() // success + * }) + * + * @param {Number} maximum number + * @param {String} message + * @api public + */ + +SchemaNumber.prototype.max = function (value, message) { + if (this.maxValidator) + this.validators = this.validators.filter(function(v){ + return v[1] != 'max'; + }); + if (value != null) + this.validators.push([this.maxValidator = function(v){ + return v === null || v <= value; + }, 'max']); + return this; +}; + +/** + * Casts to number + * + * @param {Object} value value to cast + * @param {Document} doc document that triggers the casting + * @param {Boolean} init + * @api private + */ + +SchemaNumber.prototype.cast = function (value, doc, init) { + if (SchemaType._isRef(this, value, init)) return value; + + if (!isNaN(value)){ + if (null === value) return value; + if ('' === value) return null; + if ('string' == typeof value) value = Number(value); + if (value instanceof Number) return value + if ('number' == typeof value) return value; + if (value.toString && !Array.isArray(value) && + value.toString() == Number(value)) { + return new Number(value) + } + } + + throw new CastError('number', value, this.path); +}; + +/*! + * ignore + */ + +function handleSingle (val) { + return this.cast(val) +} + +function handleArray (val) { + var self = this; + return val.map( function (m) { + return self.cast(m) + }); +} + +SchemaNumber.prototype.$conditionalHandlers = { + '$lt' : handleSingle + , '$lte': handleSingle + , '$gt' : handleSingle + , '$gte': handleSingle + , '$ne' : handleSingle + , '$in' : handleArray + , '$nin': handleArray + , '$mod': handleArray + , '$all': handleArray +}; + +/** + * Casts contents for queries. + * + * @param {String} $conditional + * @param {any} [value] + * @api private + */ + +SchemaNumber.prototype.castForQuery = function ($conditional, val) { + var handler; + if (arguments.length === 2) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) + throw new Error("Can't use " + $conditional + " with Number."); + return handler.call(this, val); + } else { + val = this.cast($conditional); + return val == null ? val : val + } +}; + +/*! + * Module exports. + */ + +module.exports = SchemaNumber; diff --git a/test/node_modules/mongoose/lib/schema/objectid.js b/test/node_modules/mongoose/lib/schema/objectid.js new file mode 100644 index 0000000..05762fe --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/objectid.js @@ -0,0 +1,151 @@ +/*! + * Module dependencies. + */ + +var SchemaType = require('../schematype') + , CastError = SchemaType.CastError + , driver = global.MONGOOSE_DRIVER_PATH || './../drivers/node-mongodb-native' + , oid = require('../types/objectid'); + + +/** + * ObjectId SchemaType constructor. + * + * @param {String} key + * @param {Object} options + * @inherits SchemaType + * @api private + */ + +function ObjectId (key, options) { + SchemaType.call(this, key, options, 'ObjectID'); +}; + +/*! + * Inherits from SchemaType. + */ + +ObjectId.prototype.__proto__ = SchemaType.prototype; + +/** + * Check required + * + * @api private + */ + +ObjectId.prototype.checkRequired = function checkRequired (value) { + if (SchemaType._isRef(this, value, true)) { + return null != value; + } else { + return value instanceof oid; + } +}; + +/** + * Casts to ObjectId + * + * @param {Object} value + * @param {Object} scope + * @param {Boolean} init whether this is an initialization cast + * @api private + */ + +ObjectId.prototype.cast = function (value, scope, init) { + if (SchemaType._isRef(this, value, init)) return value; + + if (value === null) return value; + + if (value instanceof oid) + return value; + + if (value._id && value._id instanceof oid) + return value._id; + + if (value.toString) { + try { + return oid.fromString(value.toString()); + } catch (err) { + throw new CastError('ObjectId', value, this.path); + } + } + + throw new CastError('ObjectId', value, this.path); +}; + +/*! + * ignore + */ + +function handleSingle (val) { + return this.cast(val); +} + +function handleArray (val) { + var self = this; + return val.map(function (m) { + return self.cast(m); + }); +} + +ObjectId.prototype.$conditionalHandlers = { + '$ne': handleSingle + , '$in': handleArray + , '$nin': handleArray + , '$gt': handleSingle + , '$lt': handleSingle + , '$gte': handleSingle + , '$lte': handleSingle + , '$all': handleArray +}; + +/** + * Casts contents for queries. + * + * @param {String} $conditional + * @param {any} [val] + * @api private + */ + +ObjectId.prototype.castForQuery = function ($conditional, val) { + var handler; + if (arguments.length === 2) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) + throw new Error("Can't use " + $conditional + " with ObjectId."); + return handler.call(this, val); + } else { + return this.cast($conditional); + } +}; + +/** + * Adds an auto-generated ObjectId default if turnOn is true. + * @param {Boolean} turnOn auto generated ObjectId defaults + * @api private + */ + +ObjectId.prototype.auto = function (turnOn) { + if (turnOn) { + this.default(defaultId); + this.set(resetId) + } +}; + +/*! + * ignore + */ + +function defaultId () { + return new oid(); +}; + +function resetId (v) { + this.__id = null; + return v; +} + +/*! + * Module exports. + */ + +module.exports = ObjectId; diff --git a/test/node_modules/mongoose/lib/schema/string.js b/test/node_modules/mongoose/lib/schema/string.js new file mode 100644 index 0000000..d1bef26 --- /dev/null +++ b/test/node_modules/mongoose/lib/schema/string.js @@ -0,0 +1,255 @@ + +/*! + * Module dependencies. + */ + +var SchemaType = require('../schematype') + , CastError = SchemaType.CastError; + +/** + * String SchemaType constructor. + * + * @param {String} key + * @param {Object} options + * @inherits SchemaType + * @api private + */ + +function SchemaString (key, options) { + this.enumValues = []; + this.regExp = null; + SchemaType.call(this, key, options, 'String'); +}; + +/*! + * Inherits from SchemaType. + */ + +SchemaString.prototype.__proto__ = SchemaType.prototype; + +/** + * Adds enumeration values and a coinciding validator. + * + * ####Example: + * + * var states = 'opening open closing closed'.split(' ') + * var s = new Schema({ state: { type: String, enum: states }) + * var M = db.model('M', s) + * var m = new M({ state: 'invalid' }) + * m.save(function (err) { + * console.error(err) // validator error + * m.state = 'open' + * m.save() // success + * }) + * + * @param {String} [args...] enumeration values + * @api public + */ + +SchemaString.prototype.enum = function () { + var len = arguments.length; + if (!len || undefined === arguments[0] || false === arguments[0]) { + if (this.enumValidator){ + this.enumValidator = false; + this.validators = this.validators.filter(function(v){ + return v[1] != 'enum'; + }); + } + return; + } + + for (var i = 0; i < len; i++) { + if (undefined !== arguments[i]) { + this.enumValues.push(this.cast(arguments[i])); + } + } + + if (!this.enumValidator) { + var values = this.enumValues; + this.enumValidator = function(v){ + return undefined === v || ~values.indexOf(v); + }; + this.validators.push([this.enumValidator, 'enum']); + } +}; + +/** + * Adds a lowercase setter. + * + * ####Example: + * + * var s = new Schema({ email: { type: String, lowercase: true }}) + * var M = db.model('M', s); + * var m = new M({ email: 'SomeEmail@example.COM' }); + * console.log(m.email) // someemail@example.com + * + * @api public + */ + +SchemaString.prototype.lowercase = function () { + return this.set(function (v, self) { + if ('string' != typeof v) v = self.cast(v) + if (v) return v.toLowerCase(); + return v; + }); +}; + +/** + * Adds an uppercase setter. + * + * ####Example: + * + * var s = new Schema({ caps: { type: String, uppercase: true }}) + * var M = db.model('M', s); + * var m = new M({ caps: 'an example' }); + * console.log(m.caps) // AN EXAMPLE + * + * @api public + */ + +SchemaString.prototype.uppercase = function () { + return this.set(function (v, self) { + if ('string' != typeof v) v = self.cast(v) + if (v) return v.toUpperCase(); + return v; + }); +}; + +/** + * Adds a trim setter. + * + * The string value will be trimmed when set. + * + * ####Example: + * + * var s = new Schema({ name: { type: String, trim: true }}) + * var M = db.model('M', s) + * var string = ' some name ' + * console.log(string.length) // 11 + * var m = new M({ name: string }) + * console.log(m.name.length) // 9 + * + * @api public + */ + +SchemaString.prototype.trim = function () { + return this.set(function (v, self) { + if ('string' != typeof v) v = self.cast(v) + if (v) return v.trim(); + return v; + }); +}; + +/** + * Sets a regexp validator. + * + * Any value that does not pass `regExp`.test(val) will fail validation. + * + * ####Example: + * + * var s = new Schema({ name: { type: String, match: /^a/ }}) + * var M = db.model('M', s) + * var m = new M({ name: 'invalid' }) + * m.validate(function (err) { + * console.error(err) // validation error + * m.name = 'apples' + * m.validate(function (err) { + * assert.ok(err) // success + * }) + * }) + * + * @param {RegExp} regExp regular expression to test against + * @api public + */ + +SchemaString.prototype.match = function match (regExp) { + this.validators.push([function(v){ + return null != v && '' !== v + ? regExp.test(v) + : true + }, 'regexp']); +}; + +/** + * Check required + * + * @param {String|null|undefined} value + * @api private + */ + +SchemaString.prototype.checkRequired = function checkRequired (value) { + if (SchemaType._isRef(this, value, true)) { + return null != value; + } else { + return (value instanceof String || typeof value == 'string') && value.length; + } +}; + +/** + * Casts to String + * + * @api private + */ + +SchemaString.prototype.cast = function (value, scope, init) { + if (SchemaType._isRef(this, value, init)) return value; + if (value === null) return value; + if ('undefined' !== typeof value && value.toString) return value.toString(); + throw new CastError('string', value, this.path); +}; + +/*! + * ignore + */ + +function handleSingle (val) { + return this.castForQuery(val); +} + +function handleArray (val) { + var self = this; + return val.map(function (m) { + return self.castForQuery(m); + }); +} + +SchemaString.prototype.$conditionalHandlers = { + '$ne' : handleSingle + , '$in' : handleArray + , '$nin': handleArray + , '$gt' : handleSingle + , '$lt' : handleSingle + , '$gte': handleSingle + , '$lte': handleSingle + , '$all': handleArray + , '$regex': handleSingle + , '$options': handleSingle +}; + +/** + * Casts contents for queries. + * + * @param {String} $conditional + * @param {any} [val] + * @api private + */ + +SchemaString.prototype.castForQuery = function ($conditional, val) { + var handler; + if (arguments.length === 2) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) + throw new Error("Can't use " + $conditional + " with String."); + return handler.call(this, val); + } else { + val = $conditional; + if (val instanceof RegExp) return val; + return this.cast(val); + } +}; + +/*! + * Module exports. + */ + +module.exports = SchemaString; diff --git a/test/node_modules/mongoose/lib/schemadefault.js b/test/node_modules/mongoose/lib/schemadefault.js new file mode 100644 index 0000000..aebcff5 --- /dev/null +++ b/test/node_modules/mongoose/lib/schemadefault.js @@ -0,0 +1,34 @@ + +/*! + * Module dependencies. + */ + +var Schema = require('./schema') + +/** + * Default model for querying the system.profiles collection. + * + * @property system.profile + * @receiver exports + * @api private + */ + +exports['system.profile'] = new Schema({ + ts: Date + , info: String // deprecated + , millis: Number + , op: String + , ns: String + , query: Schema.Types.Mixed + , updateobj: Schema.Types.Mixed + , ntoreturn: Number + , nreturned: Number + , nscanned: Number + , responseLength: Number + , client: String + , user: String + , idhack: Boolean + , scanAndOrder: Boolean + , keyUpdates: Number + , cursorid: Number +}, { noVirtualId: true, noId: true }); diff --git a/test/node_modules/mongoose/lib/schematype.js b/test/node_modules/mongoose/lib/schematype.js new file mode 100644 index 0000000..9a1edb9 --- /dev/null +++ b/test/node_modules/mongoose/lib/schematype.js @@ -0,0 +1,657 @@ +/*! + * Module dependencies. + */ + +var utils = require('./utils'); +var CastError = require('./error').CastError; +var ValidatorError = require('./error').ValidatorError; + +/** + * SchemaType constructor + * + * @param {String} path + * @param {Object} [options] + * @param {String} [instance] + * @api public + */ + +function SchemaType (path, options, instance) { + this.path = path; + this.instance = instance; + this.validators = []; + this.setters = []; + this.getters = []; + this.options = options; + this._index = null; + this.selected; + + for (var i in options) if (this[i] && 'function' == typeof this[i]) { + // { unique: true, index: true } + if ('index' == i && this._index) continue; + + var opts = Array.isArray(options[i]) + ? options[i] + : [options[i]]; + + this[i].apply(this, opts); + } +}; + +/** + * Sets a default value for this SchemaType. + * + * ####Example: + * + * var schema = new Schema({ n: { type: Number, default: 10 }) + * var M = db.model('M', schema) + * var m = new M; + * console.log(m.n) // 10 + * + * Defaults can be either `functions` which return the value to use as the default or the literal value itself. Either way, the value will be cast based on its schema type before being set during document creation. + * + * ####Example: + * + * // values are cast: + * var schema = new Schema({ aNumber: Number, default: "4.815162342" }) + * var M = db.model('M', schema) + * var m = new M; + * console.log(m.aNumber) // 4.815162342 + * + * // default unique objects for Mixed types: + * var schema = new Schema({ mixed: Schema.Types.Mixed }); + * schema.path('mixed').default(function () { + * return {}; + * }); + * + * // if we don't use a function to return object literals for Mixed defaults, + * // each document will receive a reference to the same object literal creating + * // a "shared" object instance: + * var schema = new Schema({ mixed: Schema.Types.Mixed }); + * schema.path('mixed').default({}); + * var M = db.model('M', schema); + * var m1 = new M; + * m1.mixed.added = 1; + * console.log(m1.mixed); // { added: 1 } + * var m2 = new M; + * console.log(m2.mixed); // { added: 1 } + * + * @param {Function|any} val the default value + * @return {defaultValue} + * @api public + */ + +SchemaType.prototype.default = function (val) { + if (1 === arguments.length) { + this.defaultValue = typeof val === 'function' + ? val + : this.cast(val); + return this; + } else if (arguments.length > 1) { + this.defaultValue = utils.args(arguments); + } + return this.defaultValue; +}; + +/** + * Declares the index options for this schematype. + * + * ####Example: + * + * var s = new Schema({ name: { type: String, index: true }) + * var s = new Schema({ date: { type: Date, index: { unique: true, expires: '1d' }}) + * Schema.path('my.path').index(true); + * Schema.path('my.date').index({ expires: 60 }); + * Schema.path('my.path').index({ unique: true, sparse: true }); + * + * ####NOTE: + * + * _Indexes are created in the background by default. Specify `background: false` to override._ + * + * [Direction doesn't matter for single key indexes](http://www.mongodb.org/display/DOCS/Indexes#Indexes-CompoundKeysIndexes) + * + * @param {Object|Boolean} options + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.index = function (options) { + this._index = options; + utils.expires(this._index); + return this; +}; + +/** + * Declares an unique index. + * + * ####Example: + * + * var s = new Schema({ name: { type: String, unique: true }) + * Schema.path('name').index({ unique: true }); + * + * _NOTE: violating the constraint returns an `E11000` error from MongoDB when saving, not a Mongoose validation error._ + * + * @param {Boolean} bool + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.unique = function (bool) { + if (!this._index || 'Object' !== this._index.constructor.name) { + this._index = {}; + } + + this._index.unique = bool; + return this; +}; + +/** + * Declares a sparse index. + * + * ####Example: + * + * var s = new Schema({ name: { type: String, sparse: true }) + * Schema.path('name').index({ sparse: true }); + * + * @param {Boolean} bool + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.sparse = function (bool) { + if (!this._index || 'Object' !== this._index.constructor.name) { + this._index = {}; + } + + this._index.sparse = bool; + return this; +}; + +/** + * Declares a TTL index (rounded to the nearest second) for _Date_ types only. + * + * This sets the `expiresAfterSeconds` index option available in MongoDB >= 2.1.2. + * This index type is only compatible with Date types. + * + * ####Example: + * + * // expire in 24 hours + * new Schema({ createdAt: { type: Date, expires: 60*60*24 }}); + * + * `expires` utilizes the `ms` module from [guille](https://github.com/guille/) allowing us to use a friendlier syntax: + * + * ####Example: + * + * // expire in 24 hours + * new Schema({ createdAt: { type: Date, expires: '24h' }}); + * + * // expire in 1.5 hours + * new Schema({ createdAt: { type: Date, expires: '1.5h' }}); + * + * // expire in 7 days + * var schema = new Schema({ createdAt: Date }); + * schema.path('createdAt').expires('7d'); + * + * @param {Number|String} when + * @added 3.0.0 + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.expires = function (when) { + if (!this._index || 'Object' !== this._index.constructor.name) { + this._index = {}; + } + + this._index.expires = when; + utils.expires(this._index); + return this; +}; + +/** + * Adds a setter to this schematype. + * + * ####Example: + * + * function capitalize (val) { + * if ('string' != typeof val) val = ''; + * return val.charAt(0).toUpperCase() + val.substring(1); + * } + * + * // defining within the schema + * var s = new Schema({ name: { type: String, set: capitalize }}) + * + * // or by retreiving its SchemaType + * var s = new Schema({ name: String }) + * s.path('name').set(capitalize) + * + * Setters allow you to transform the data before it gets to the raw mongodb document and is set as a value on an actual key. + * + * Suppose you are implementing user registration for a website. Users provide an email and password, which gets saved to mongodb. The email is a string that you will want to normalize to lower case, in order to avoid one email having more than one account -- e.g., otherwise, avenue@q.com can be registered for 2 accounts via avenue@q.com and AvEnUe@Q.CoM. + * + * You can set up email lower case normalization easily via a Mongoose setter. + * + * function toLower (v) { + * return v.toLowerCase(); + * } + * + * var UserSchema = new Schema({ + * email: { type: String, set: toLower } + * }) + * + * var User = db.model('User', UserSchema) + * + * var user = new User({email: 'AVENUE@Q.COM'}) + * console.log(user.email); // 'avenue@q.com' + * + * // or + * var user = new User + * user.email = 'Avenue@Q.com' + * console.log(user.email) // 'avenue@q.com' + * + * As you can see above, setters allow you to transform the data before it gets to the raw mongodb document and is set as a value on an actual key. + * + * _NOTE: we could have also just used the built-in `lowercase: true` SchemaType option instead of defining our own function._ + * + * new Schema({ email: { type: String, lowercase: true }}) + * + * Setters are also passed a second argument, the schematype on which the setter was defined. This allows for tailored behavior based on options passed in the schema. + * + * function inspector (val, schematype) { + * if (schematype.options.required) { + * return schematype.path + ' is required'; + * } else { + * return val; + * } + * } + * + * var VirusSchema = new Schema({ + * name: { type: String, required: true, set: inspector }, + * taxonomy: { type: String, set: inspector } + * }) + * + * var Virus = db.model('Virus', VirusSchema); + * var v = new Virus({ name: 'Parvoviridae', taxonomy: 'Parvovirinae' }); + * + * console.log(v.name); // name is required + * console.log(v.taxonomy); // Parvovirinae + * + * @param {Function} fn + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.set = function (fn) { + if ('function' != typeof fn) + throw new Error('A setter must be a function.'); + this.setters.push(fn); + return this; +}; + +/** + * Adds a getter to this schematype. + * + * ####Example: + * + * function dob (val) { + * if (!val) return val; + * return (val.getMonth() + 1) + "/" + val.getDate() + "/" + val.getFullYear(); + * } + * + * // defining within the schema + * var s = new Schema({ born: { type: Date, get: dob }) + * + * // or by retreiving its SchemaType + * var s = new Schema({ born: Date }) + * s.path('born').get(dob) + * + * Getters allow you to transform the representation of the data as it travels from the raw mongodb document to the value that you see. + * + * Suppose you are storing credit card numbers and you want to hide everything except the last 4 digits to the mongoose user. You can do so by defining a getter in the following way: + * + * function obfuscate (cc) { + * return '****-****-****-' + cc.slice(cc.length-4, cc.length); + * } + * + * var AccountSchema = new Schema({ + * creditCardNumber: { type: String, get: obfuscate } + * }); + * + * var Account = db.model('Account', AccountSchema); + * + * Account.findById(id, function (err, found) { + * console.log(found.creditCardNumber); // '****-****-****-1234' + * }); + * + * Getters are also passed a second argument, the schematype on which the getter was defined. This allows for tailored behavior based on options passed in the schema. + * + * function inspector (val, schematype) { + * if (schematype.options.required) { + * return schematype.path + ' is required'; + * } else { + * return schematype.path + ' is not'; + * } + * } + * + * var VirusSchema = new Schema({ + * name: { type: String, required: true, get: inspector }, + * taxonomy: { type: String, get: inspector } + * }) + * + * var Virus = db.model('Virus', VirusSchema); + * + * Virus.findById(id, function (err, virus) { + * console.log(virus.name); // name is required + * console.log(virus.taxonomy); // taxonomy is not + * }) + * + * @param {Function} fn + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.get = function (fn) { + if ('function' != typeof fn) + throw new Error('A getter must be a function.'); + this.getters.push(fn); + return this; +}; + +/** + * Adds validator(s) for this document path. + * + * Validators always receive the value to validate as their first argument and must return `Boolean`. Returning false is interpreted as validation failure. + * + * ####Examples: + * + * function validator (val) { + * return val == 'something'; + * } + * + * new Schema({ name: { type: String, validate: validator }}); + * + * // with a custom error message + * + * var custom = [validator, 'validation failed'] + * new Schema({ name: { type: String, validate: custom }}); + * + * var many = [ + * { validator: validator, msg: 'uh oh' } + * , { validator: fn, msg: 'failed' } + * ] + * new Schema({ name: { type: String, validate: many }}); + * + * // or utilizing SchemaType methods directly: + * + * var schema = new Schema({ name: 'string' }); + * schema.path('name').validate(validator, 'validation failed'); + * + * ####Asynchronous validation: + * + * Passing a validator function that receives two arguments tells mongoose that the validator is an asynchronous validator. The second argument is an callback function that must be passed either `true` or `false` when validation is complete. + * + * schema.path('name').validate(function (value, respond) { + * doStuff(value, function () { + * ... + * respond(false); // validation failed + * }) +* }, 'my error type'); +* + * You might use asynchronous validators to retreive other documents from the database to validate against or to meet other I/O bound validation needs. + * + * Validation occurs `pre('save')` or whenever you manually execute [document#validate](#document_Document-validate). + * + * If validation fails during `pre('save')` and no callback was passed to receive the error, an `error` event will be emitted on your Models associated db [connection](#connection_Connection), passing the validation error object along. + * + * var conn = mongoose.createConnection(..); + * conn.on('error', handleError); + * + * var Product = conn.model('Product', yourSchema); + * var dvd = new Product(..); + * dvd.save(); // emits error on the `conn` above + * + * If you desire handling these errors at the Model level, attach an `error` listener to your Model and the event will instead be emitted there. + * + * // registering an error listener on the Model lets us handle errors more locally + * Product.on('error', handleError); + * + * @param {RegExp|Function|Object} obj validator + * @param {String} [error] optional error message + * @api public + */ + +SchemaType.prototype.validate = function (obj, error) { + if ('function' == typeof obj || obj && 'RegExp' === obj.constructor.name) { + this.validators.push([obj, error]); + return this; + } + + var i = arguments.length + , arg + + while (i--) { + arg = arguments[i]; + if (!(arg && 'Object' == arg.constructor.name)) { + var msg = 'Invalid validator. Received (' + typeof arg + ') ' + + arg + + '. See http://mongoosejs.com/docs/api.html#schematype_SchemaType-validate'; + + throw new Error(msg); + } + this.validate(arg.validator, arg.msg); + } + + return this; +}; + +/** + * Adds a required validator to this schematype. + * + * ####Example: + * + * var s = new Schema({ born: { type: Date, required: true }) + * // or + * Schema.path('name').required(true); + * + * + * @param {Boolean} required enable/disable the validator + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.required = function (required) { + var self = this; + + function __checkRequired (v) { + // in here, `this` refers to the validating document. + // no validation when this path wasn't selected in the query. + if ('isSelected' in this && + !this.isSelected(self.path) && + !this.isModified(self.path)) return true; + return self.checkRequired(v); + } + + if (false === required) { + this.isRequired = false; + this.validators = this.validators.filter(function (v) { + return v[0].name !== '__checkRequired'; + }); + } else { + this.isRequired = true; + this.validators.push([__checkRequired, 'required']); + } + + return this; +}; + +/** + * Gets the default value + * + * @param {Object} scope the scope which callback are executed + * @param {Boolean} init + * @api private + */ + +SchemaType.prototype.getDefault = function (scope, init) { + var ret = 'function' === typeof this.defaultValue + ? this.defaultValue.call(scope) + : this.defaultValue; + + if (null !== ret && undefined !== ret) { + return this.cast(ret, scope, init); + } else { + return ret; + } +}; + +/** + * Applies setters + * + * @param {Object} value + * @param {Object} scope + * @param {Boolean} init + * @api private + */ + +SchemaType.prototype.applySetters = function (value, scope, init, priorVal) { + if (SchemaType._isRef(this, value, init)) return value; + + var v = value + , setters = this.setters + , len = setters.length + + if (!len) { + if (null === v || undefined === v) return v; + return init + ? v // if we just initialized we dont recast + : this.cast(v, scope, init, priorVal) + } + + while (len--) { + v = setters[len].call(scope, v, this); + } + + if (null === v || undefined === v) return v; + + // do not cast until all setters are applied #665 + v = this.cast(v, scope, init, priorVal); + + return v; +}; + +/** + * Applies getters to a value + * + * @param {Object} value + * @param {Object} scope + * @api private + */ + +SchemaType.prototype.applyGetters = function (value, scope) { + if (SchemaType._isRef(this, value, true)) return value; + + var v = value + , getters = this.getters + , len = getters.length; + + if (!len) { + return v; + } + + while (len--) { + v = getters[len].call(scope, v, this); + } + + return v; +}; + +/** + * Sets default `select()` behavior for this path. + * + * Set to `true` if this path should always be included in the results, `false` if it should be excluded by default. This setting can be overridden at the query level. + * + * ####Example: + * + * T = db.model('T', new Schema({ x: { type: String, select: true }})); + * T.find(..); // field x will always be selected .. + * // .. unless overridden; + * T.find().select('-x').exec(callback); + * + * @param {Boolean} val + * @api public + */ + +SchemaType.prototype.select = function select (val) { + this.selected = !! val; +} + +/** + * Performs a validation of `value` using the validators declared for this SchemaType. + * + * @param {any} value + * @param {Function} callback + * @param {Object} scope + * @api private + */ + +SchemaType.prototype.doValidate = function (value, fn, scope) { + var err = false + , path = this.path + , count = this.validators.length; + + if (!count) return fn(null); + + function validate (val, msg) { + if (err) return; + if (val === undefined || val) { + --count || fn(null); + } else { + fn(err = new ValidatorError(path, msg)); + } + } + + this.validators.forEach(function (v) { + var validator = v[0] + , message = v[1]; + + if (validator instanceof RegExp) { + validate(validator.test(value), message); + } else if ('function' === typeof validator) { + if (2 === validator.length) { + validator.call(scope, value, function (val) { + validate(val, message); + }); + } else { + validate(validator.call(scope, value), message); + } + } + }); +}; + +/** + * Determines if value is a valid Reference. + * + * @param {SchemaType} self + * @param {Object} value + * @param {Boolean} init + * @return {Boolean} + * @api private + */ + +SchemaType._isRef = function (self, value, init) { + if (init && self.options && self.options.ref) { + if (null == value) return true; + if (value._id && value._id.constructor.name === self.instance) return true; + } + + return false; +} + +/*! + * Module exports. + */ + +module.exports = exports = SchemaType; + +exports.CastError = CastError; + +exports.ValidatorError = ValidatorError; diff --git a/test/node_modules/mongoose/lib/statemachine.js b/test/node_modules/mongoose/lib/statemachine.js new file mode 100644 index 0000000..76005d8 --- /dev/null +++ b/test/node_modules/mongoose/lib/statemachine.js @@ -0,0 +1,179 @@ + +/*! + * Module dependencies. + */ + +var utils = require('./utils'); + +/*! + * StateMachine represents a minimal `interface` for the + * constructors it builds via StateMachine.ctor(...). + * + * @api private + */ + +var StateMachine = module.exports = exports = function StateMachine () { + this.paths = {}; + this.states = {}; +} + +/*! + * StateMachine.ctor('state1', 'state2', ...) + * A factory method for subclassing StateMachine. + * The arguments are a list of states. For each state, + * the constructor's prototype gets state transition + * methods named after each state. These transition methods + * place their path argument into the given state. + * + * @param {String} state + * @param {String} [state] + * @return {Function} subclass constructor + * @private + */ + +StateMachine.ctor = function () { + var states = utils.args(arguments); + + var ctor = function () { + StateMachine.apply(this, arguments); + this.stateNames = states; + + var i = states.length + , state; + + while (i--) { + state = states[i]; + this.states[state] = {}; + } + }; + + ctor.prototype.__proto__ = StateMachine.prototype; + + states.forEach(function (state) { + // Changes the `path`'s state to `state`. + ctor.prototype[state] = function (path) { + this._changeState(path, state); + } + }); + + return ctor; +}; + +/*! + * This function is wrapped by the state change functions: + * + * - `require(path)` + * - `modify(path)` + * - `init(path)` + * + * @api private + */ + +StateMachine.prototype._changeState = function _changeState (path, nextState) { + var prevBucket = this.states[this.paths[path]]; + if (prevBucket) delete prevBucket[path]; + + this.paths[path] = nextState; + this.states[nextState][path] = true; +} + +/*! + * ignore + */ + +StateMachine.prototype.clear = function clear (state) { + var keys = Object.keys(this.states[state]) + , i = keys.length + , path + + while (i--) { + path = keys[i]; + delete this.states[state][path]; + delete this.paths[path]; + } +} + +/*! + * Checks to see if at least one path is in the states passed in via `arguments` + * e.g., this.some('required', 'inited') + * + * @param {String} state that we want to check for. + * @private + */ + +StateMachine.prototype.some = function some () { + var self = this; + var what = arguments.length ? arguments : this.stateNames; + return Array.prototype.some.call(what, function (state) { + return Object.keys(self.states[state]).length; + }); +} + +/*! + * This function builds the functions that get assigned to `forEach` and `map`, + * since both of those methods share a lot of the same logic. + * + * @param {String} iterMethod is either 'forEach' or 'map' + * @return {Function} + * @api private + */ + +StateMachine.prototype._iter = function _iter (iterMethod) { + return function () { + var numArgs = arguments.length + , states = utils.args(arguments, 0, numArgs-1) + , callback = arguments[numArgs-1]; + + if (!states.length) states = this.stateNames; + + var self = this; + + var paths = states.reduce(function (paths, state) { + return paths.concat(Object.keys(self.states[state])); + }, []); + + return paths[iterMethod](function (path, i, paths) { + return callback(path, i, paths); + }); + }; +} + +/*! + * Iterates over the paths that belong to one of the parameter states. + * + * The function profile can look like: + * this.forEach(state1, fn); // iterates over all paths in state1 + * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2 + * this.forEach(fn); // iterates over all paths in all states + * + * @param {String} [state] + * @param {String} [state] + * @param {Function} callback + * @private + */ + +StateMachine.prototype.forEach = function forEach () { + this.forEach = this._iter('forEach'); + return this.forEach.apply(this, arguments); +} + +/*! + * Maps over the paths that belong to one of the parameter states. + * + * The function profile can look like: + * this.forEach(state1, fn); // iterates over all paths in state1 + * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2 + * this.forEach(fn); // iterates over all paths in all states + * + * @param {String} [state] + * @param {String} [state] + * @param {Function} callback + * @return {Array} + * @private + */ + +StateMachine.prototype.map = function map () { + this.map = this._iter('map'); + return this.map.apply(this, arguments); +} + diff --git a/test/node_modules/mongoose/lib/types/array.js b/test/node_modules/mongoose/lib/types/array.js new file mode 100644 index 0000000..294c989 --- /dev/null +++ b/test/node_modules/mongoose/lib/types/array.js @@ -0,0 +1,598 @@ + +/*! + * Module dependencies. + */ + +var EmbeddedDocument = require('./embedded'); +var Document = require('../document'); +var ObjectId = require('./objectid'); + +/** + * Mongoose Array constructor. + * + * ####NOTE: + * + * _Values always have to be passed to the constructor to initialize, otherwise `MongooseArray#push` will mark the array as modified._ + * + * @param {Array} values + * @param {String} path + * @param {Document} doc parent document + * @api private + * @inherits Array + * @see http://bit.ly/f6CnZU + */ + +function MongooseArray (values, path, doc) { + var arr = []; + arr.push.apply(arr, values); + arr.__proto__ = MongooseArray.prototype; + + arr._atomics = {}; + arr.validators = []; + arr._path = path; + + if (doc) { + arr._parent = doc; + arr._schema = doc.schema.path(path); + } + + return arr; +}; + +/*! + * Inherit from Array + */ + +MongooseArray.prototype = new Array; + +/** + * Stores a queue of atomic operations to perform + * + * @property _atomics + * @api private + */ + +MongooseArray.prototype._atomics; + +/** + * Parent owner document + * + * @property _parent + * @api private + */ + +MongooseArray.prototype._parent; + +/** + * Casts a member based on this arrays schema. + * + * @param {any} value + * @return value the casted value + * @api private + */ + +MongooseArray.prototype._cast = function (value) { + var cast = this._schema.caster.cast + , doc = this._parent; + + return cast.call(null, value, doc); +}; + +/** + * Marks this array as modified. + * + * If it bubbles up from an embedded document change, then it takes the following arguments (otherwise, takes 0 arguments) + * + * @param {EmbeddedDocument} embeddedDoc the embedded doc that invoked this method on the Array + * @param {String} embeddedPath the path which changed in the embeddedDoc + * @api private + */ + +MongooseArray.prototype._markModified = function (elem, embeddedPath) { + var parent = this._parent + , dirtyPath; + + if (parent) { + dirtyPath = this._path; + + if (arguments.length) { + if (null != embeddedPath) { + // an embedded doc bubbled up the change + dirtyPath = dirtyPath + '.' + this.indexOf(elem) + '.' + embeddedPath; + } else { + // directly set an index + dirtyPath = dirtyPath + '.' + elem; + } + + } + parent.markModified(dirtyPath); + } + + return this; +}; + +/** + * Register an atomic operation with the parent. + * + * @param {Array} op operation + * @param {any} val + * @api private + */ + +MongooseArray.prototype._registerAtomic = function (op, val) { + if ('$set' == op) { + // $set takes precedence over all other ops. + // mark entire array modified. + this._atomics = { $set: val }; + return this; + } + + var atomics = this._atomics; + + // reset pop/shift after save + if ('$pop' == op && !('$pop' in atomics)) { + var self = this; + this._parent.once('save', function () { + self._popped = self._shifted = null; + }); + } + + // check for impossible $atomic combos (Mongo denies more than one + // $atomic op on a single path + if (this._atomics.$set || + Object.keys(atomics).length && !(op in atomics)) { + // a different op was previously registered. + // save the entire thing. + this._atomics = { $set: this }; + return this; + } + + if (op === '$pullAll' || op === '$pushAll' || op === '$addToSet') { + atomics[op] || (atomics[op] = []); + atomics[op] = atomics[op].concat(val); + } else if (op === '$pullDocs') { + var pullOp = atomics['$pull'] || (atomics['$pull'] = {}) + , selector = pullOp['_id'] || (pullOp['_id'] = {'$in' : [] }); + selector['$in'] = selector['$in'].concat(val); + } else { + atomics[op] = val; + } + + return this; +}; + +/** + * Returns the number of pending atomic operations to send to the db for this array. + * + * @api private + * @return {Number} + */ + +MongooseArray.prototype.hasAtomics = function hasAtomics () { + if (!(this._atomics && 'Object' === this._atomics.constructor.name)) { + return 0; + } + + return Object.keys(this._atomics).length; +} + +/** + * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking. + * + * @param {Object} [args...] + * @api public + */ + +MongooseArray.prototype.push = function () { + var values = [].map.call(arguments, this._cast, this) + , ret = [].push.apply(this, values); + + // $pushAll might be fibbed (could be $push). But it makes it easier to + // handle what could have been $push, $pushAll combos + this._registerAtomic('$pushAll', values); + this._markModified(); + return ret; +}; + +/** + * Pushes items to the array non-atomically. + * + * ####NOTE: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @param {any} [args...] + * @api public + */ + +MongooseArray.prototype.nonAtomicPush = function () { + var values = [].map.call(arguments, this._cast, this) + , ret = [].push.apply(this, values); + this._registerAtomic('$set', this); + this._markModified(); + return ret; +}; + +/** + * Pops the array atomically at most one time per document `save()`. + * + * #### NOTE: + * + * _Calling this mulitple times on an array before saving sends the same command as calling it once._ + * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._ + * + * doc.array = [1,2,3]; + * + * var popped = doc.array.$pop(); + * console.log(popped); // 3 + * console.log(doc.array); // [1,2] + * + * // no affect + * popped = doc.array.$pop(); + * console.log(doc.array); // [1,2] + * + * doc.save(function (err) { + * if (err) return handleError(err); + * + * // we saved, now $pop works again + * popped = doc.array.$pop(); + * console.log(popped); // 2 + * console.log(doc.array); // [1] + * }) + * + * @api public + * @method $pop + * @memberOf MongooseArray + * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop + */ + +MongooseArray.prototype.$pop = function () { + this._registerAtomic('$pop', 1); + this._markModified(); + + // only allow popping once + if (this._popped) return; + this._popped = true; + + return [].pop.call(this); +}; + +/** + * Wraps [`Array#pop`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/pop) with proper change tracking. + * + * ####Note: + * + * _marks the entire array as modified which will pass the entire thing to $set potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @see MongooseArray#$pop #types_array_MongooseArray-%24pop + * @api public + */ + +MongooseArray.prototype.pop = function () { + var ret = [].pop.call(this); + this._registerAtomic('$set', this); + this._markModified(); + return ret; +}; + +/** + * Atomically shifts the array at most one time per document `save()`. + * + * ####NOTE: + * + * _Calling this mulitple times on an array before saving sends the same command as calling it once._ + * _This update is implemented using the MongoDB [$pop](http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop) method which enforces this restriction._ + * + * doc.array = [1,2,3]; + * + * var shifted = doc.array.$shift(); + * console.log(shifted); // 1 + * console.log(doc.array); // [2,3] + * + * // no affect + * shifted = doc.array.$shift(); + * console.log(doc.array); // [2,3] + * + * doc.save(function (err) { + * if (err) return handleError(err); + * + * // we saved, now $shift works again + * shifted = doc.array.$shift(); + * console.log(shifted ); // 2 + * console.log(doc.array); // [3] + * }) + * + * @api public + * @memberOf MongooseArray + * @method $shift + * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pop + */ + +MongooseArray.prototype.$shift = function $shift () { + this._registerAtomic('$pop', -1); + this._markModified(); + + // only allow shifting once + if (this._shifted) return; + this._shifted = true; + + return [].shift.call(this); +}; + +/** + * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking. + * + * ####Example: + * + * doc.array = [2,3]; + * var res = doc.array.shift(); + * console.log(res) // 2 + * console.log(doc.array) // [3] + * + * ####Note: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @api public + */ + +MongooseArray.prototype.shift = function () { + var ret = [].shift.call(this); + this._registerAtomic('$set', this); + this._markModified(); + return ret; +}; + +/** + * Removes items from an array atomically + * + * ####Examples: + * + * doc.array.remove(ObjectId) + * doc.array.remove('tag 1', 'tag 2') + * + * @param {Object} [args...] values to remove + * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull + * @api public + */ + +MongooseArray.prototype.remove = function () { + var args = [].map.call(arguments, this._cast, this); + if (args.length == 1) + this.pull(args[0]); + else + this.pull.apply(this, args); + return args; +}; + +/** + * Pulls items from the array atomically. + * + * @param {any} [args...] + * @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull + * @api public + */ + +MongooseArray.prototype.pull = function () { + var values = [].map.call(arguments, this._cast, this) + , cur = this._parent.get(this._path) + , i = cur.length + , mem; + + while (i--) { + mem = cur[i]; + if (mem instanceof EmbeddedDocument) { + if (values.some(function (v) { return v.equals(mem); } )) { + [].splice.call(cur, i, 1); + } + } else if (~cur.indexOf.call(values, mem)) { + [].splice.call(cur, i, 1); + } + } + + if (values[0] instanceof EmbeddedDocument) { + this._registerAtomic('$pullDocs', values.map( function (v) { return v._id; } )); + } else { + this._registerAtomic('$pullAll', values); + } + + this._markModified(); + return this; +}; + +/** + * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting. + * + * ####Note: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @api public + */ + +MongooseArray.prototype.splice = function splice () { + var ret, vals, i; + + if (arguments.length) { + vals = []; + for (i = 0; i < arguments.length; ++i) { + vals[i] = i < 2 + ? arguments[i] + : this._cast(arguments[i]); + } + ret = [].splice.apply(this, vals); + this._registerAtomic('$set', this); + this._markModified(); + } + + return ret; +} + +/** + * Wraps [`Array#unshift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking. + * + * ####Note: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @api public + */ + +MongooseArray.prototype.unshift = function () { + var values = [].map.call(arguments, this._cast, this); + [].unshift.apply(this, values); + this._registerAtomic('$set', this); + this._markModified(); + return this.length; +}; + +/** + * Wraps [`Array#sort`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort) with proper change tracking. + * + * ####NOTE: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @api public + */ + +MongooseArray.prototype.sort = function () { + var ret = [].sort.apply(this, arguments); + this._registerAtomic('$set', this); + this._markModified(); + return ret; +} + +/** + * Adds values to the array if not already present. + * + * ####Example: + * + * console.log(doc.array) // [2,3,4] + * var added = doc.array.addToSet(4,5); + * console.log(doc.array) // [2,3,4,5] + * console.log(added) // [5] + * + * @param {any} [args...] + * @return {Array} the values that were added + * @api public + */ + +MongooseArray.prototype.addToSet = function addToSet () { + var values = [].map.call(arguments, this._cast, this) + , added = [] + , type = values[0] instanceof EmbeddedDocument ? 'doc' : + values[0] instanceof Date ? 'date' : + ''; + + values.forEach(function (v) { + var found; + switch (type) { + case 'doc': + found = this.some(function(doc){ return doc.equals(v) }); + break; + case 'date': + var val = +v; + found = this.some(function(d){ return +d === val }); + break; + default: + found = ~this.indexOf(v); + } + + if (!found) { + [].push.call(this, v); + this._registerAtomic('$addToSet', v); + this._markModified(); + [].push.call(added, v); + } + }, this); + + return added; +}; + +/** + * Sets the casted `val` at index `i` and marks the array modified. + * + * ####Example: + * + * // given documents based on the following + * var Doc = mongoose.model('Doc', new Schema({ array: [Number] })); + * + * var doc = new Doc({ array: [2,3,4] }) + * + * console.log(doc.array) // [2,3,4] + * + * doc.array.set(1,"5"); + * console.log(doc.array); // [2,5,4] // properly cast to number + * doc.save() // the change is saved + * + * // VS not using array#set + * doc.array[1] = "5"; + * console.log(doc.array); // [2,"5",4] // no casting + * doc.save() // change is not saved + * + * @return {Array} this + * @api public + */ + +MongooseArray.prototype.set = function set (i, val) { + this[i] = this._cast(val); + this._markModified(i); + return this; +} + +/** + * Returns a native js Array. + * + * @param {Object} options + * @return {Array} + * @api public + */ + +MongooseArray.prototype.toObject = function (options) { + if (options && options.depopulate && this[0] instanceof Document) { + return this.map(function (doc) { + return doc._id; + }); + } + + // return this.slice()? + return this.map(function (doc) { + return doc; + }); +}; + +/** + * Helper for console.log + * + * @api public + */ + +MongooseArray.prototype.inspect = function () { + return '[' + this.map(function (doc) { + return ' ' + doc; + }) + ' ]'; +}; + +/** + * Return the index of `obj` or `-1` if not found. + * + * @param {Object} obj the item to look for + * @return {Number} + * @api public + */ + +MongooseArray.prototype.indexOf = function indexOf (obj) { + if (obj instanceof ObjectId) obj = obj.toString(); + for (var i = 0, len = this.length; i < len; ++i) { + if (obj == this[i]) + return i; + } + return -1; +}; + +/*! + * Module exports. + */ + +module.exports = exports = MongooseArray; diff --git a/test/node_modules/mongoose/lib/types/buffer.js b/test/node_modules/mongoose/lib/types/buffer.js new file mode 100644 index 0000000..45c69a9 --- /dev/null +++ b/test/node_modules/mongoose/lib/types/buffer.js @@ -0,0 +1,185 @@ + +/*! + * Access driver. + */ + +var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native'; + +/*! + * Module dependencies. + */ + +var Binary = require(driver + '/binary'); + +/** + * Mongoose Buffer constructor. + * + * Values always have to be passed to the constructor to initialize. + * + * @param {Buffer} value + * @param {String} encode + * @param {Number} offset + * @api private + * @inherits Buffer + * @see http://bit.ly/f6CnZU + */ + +function MongooseBuffer (value, encode, offset) { + var length = arguments.length; + var val; + + if (0 === length || null === arguments[0] || undefined === arguments[0]) { + val = 0; + } else { + val = value; + } + + var encoding; + var path; + var doc; + + if (Array.isArray(encode)) { + // internal casting + path = encode[0]; + doc = encode[1]; + } else { + encoding = encode; + } + + var buf = new Buffer(val, encoding, offset); + buf.__proto__ = MongooseBuffer.prototype; + + // make sure these internal props don't show up in Object.keys() + Object.defineProperties(buf, { + validators: { value: [] } + , _path: { value: path } + , _parent: { value: doc } + }); + + if (doc && "string" === typeof path) { + Object.defineProperty(buf, '_schema', { + value: doc.schema.path(path) + }); + } + + return buf; +}; + +/*! + * Inherit from Buffer. + */ + +MongooseBuffer.prototype = new Buffer(0); + +/** + * Parent owner document + * + * @api private + * @property _parent + */ + +MongooseBuffer.prototype._parent; + +/** + * Marks this buffer as modified. + * + * @api private + */ + +MongooseBuffer.prototype._markModified = function () { + var parent = this._parent; + + if (parent) { + parent.markModified(this._path); + } + return this; +}; + +/** +* Writes the buffer. +*/ + +MongooseBuffer.prototype.write = function () { + var written = Buffer.prototype.write.apply(this, arguments); + + if (written > 0) { + this._markModified(); + } + + return written; +}; + +/** + * Copies the buffer. + * + * ####Note: + * + * `Buffer#copy` does not mark `target` as modified so you must copy from a `MongooseBuffer` for it to work as expected. This is a work around since `copy` modifies the target, not this. + * + * @return {MongooseBuffer} + * @param {Buffer} target + */ + +MongooseBuffer.prototype.copy = function (target) { + var ret = Buffer.prototype.copy.apply(this, arguments); + + if (target instanceof MongooseBuffer) { + target._markModified(); + } + + return ret; +}; + +/*! + * Compile other Buffer methods marking this buffer as modified. + */ + +;( +// node < 0.5 +'writeUInt8 writeUInt16 writeUInt32 writeInt8 writeInt16 writeInt32 ' + +'writeFloat writeDouble fill ' + +'utf8Write binaryWrite asciiWrite set ' + + +// node >= 0.5 +'writeUInt16LE writeUInt16BE writeUInt32LE writeUInt32BE ' + +'writeInt16LE writeInt16BE writeInt32LE writeInt32BE ' + +'writeFloatLE writeFloatBE writeDoubleLE writeDoubleBE' +).split(' ').forEach(function (method) { + if (!Buffer.prototype[method]) return; + MongooseBuffer.prototype[method] = new Function( + 'var ret = Buffer.prototype.'+method+'.apply(this, arguments);' + + 'this._markModified();' + + 'return ret;' + ) +}); + +/** + * Converts this buffer to its Binary type representation. + * + * ####SubTypes: + * + * - 0x00: Binary/Generic + * - 0x01: Function + * - 0x02: Binary (Deprecated, 0x00 is new default) + * - 0x03: UUID + * - 0x04: MD5 + * - 0x80: User Defined + * + * @see http://bsonspec.org/#/specification + * @param {Hex} [subtype] + * @return {Binary} + * @api public + */ + +MongooseBuffer.prototype.toObject = function (subtype) { + subtype = typeof subtype !== 'undefined' ? subtype : 0x00 + return new Binary(this, subtype); +}; + +/*! + * Module exports. + */ + +MongooseBuffer.Binary = Binary; + +module.exports = MongooseBuffer; diff --git a/test/node_modules/mongoose/lib/types/documentarray.js b/test/node_modules/mongoose/lib/types/documentarray.js new file mode 100644 index 0000000..09e6be1 --- /dev/null +++ b/test/node_modules/mongoose/lib/types/documentarray.js @@ -0,0 +1,171 @@ + +/*! + * Module dependencies. + */ + +var MongooseArray = require('./array') + , driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native' + , ObjectId = require(driver + '/objectid') + , ObjectIdSchema = require('../schema/objectid') + , util = require('util') + +/** + * DocumentArray constructor + * + * @param {Array} values + * @param {String} path the path to this array + * @param {Document} doc parent document + * @api private + * @return {MongooseDocumentArray} + * @inherits MongooseArray + * @see http://bit.ly/f6CnZU + */ + +function MongooseDocumentArray (values, path, doc) { + var arr = []; + + // Values always have to be passed to the constructor to initialize, since + // otherwise MongooseArray#push will mark the array as modified to the parent. + arr.push.apply(arr, values); + arr.__proto__ = MongooseDocumentArray.prototype; + + arr._atomics = {}; + arr.validators = []; + arr._path = path; + + if (doc) { + arr._parent = doc; + arr._schema = doc.schema.path(path); + doc.on('save', arr.notify('save')); + doc.on('isNew', arr.notify('isNew')); + } + + return arr; +}; + +/*! + * Inherits from MongooseArray + */ + +MongooseDocumentArray.prototype.__proto__ = MongooseArray.prototype; + +/** + * Overrides MongooseArray#cast + * + * @api private + */ + +MongooseDocumentArray.prototype._cast = function (value) { + if (value instanceof this._schema.casterConstructor) + return value; + + return new this._schema.casterConstructor(value, this); +}; + +/** + * Searches array items for the first document with a matching id. + * + * ####Example: + * + * var embeddedDoc = m.array.id(some_id); + * + * @return {EmbeddedDocument|null} the subdocuent or null if not found. + * @param {ObjectId|String|Number|Buffer} id + * @api public + */ + +MongooseDocumentArray.prototype.id = function (id) { + var casted + , _id; + + try { + casted = ObjectId.toString(ObjectIdSchema.prototype.cast.call({}, id)); + } catch (e) { + casted = null; + } + + for (var i = 0, l = this.length; i < l; i++) { + _id = this[i].get('_id'); + if (!(_id instanceof ObjectId)) { + if (String(id) == _id) + return this[i]; + } else { + if (casted == _id) + return this[i]; + } + } + + return null; +}; + +/** + * Returns a native js Array of plain js objects + * + * ####NOTE: + * + * _Each sub-document is converted to a plain object by calling its `#toObject` method._ + * + * @return {Array} + * @api public + */ + +MongooseDocumentArray.prototype.toObject = function () { + return this.map(function (doc) { + return doc && doc.toObject() || null; + }); +}; + +/** + * Helper for console.log + * + * @api public + */ + +MongooseDocumentArray.prototype.inspect = function () { + return '[' + this.map(function (doc) { + if (doc) { + return doc.inspect + ? doc.inspect() + : util.inspect(doc) + } + return 'null' + }).join('\n') + ']'; +}; + +/** + * Creates a subdocument casted to this schema. + * + * This is the same subdocument constructor used for casting. + * + * @param {Object} obj the value to cast to this arrays SubDocument schema + * @api public + */ + +MongooseDocumentArray.prototype.create = function (obj) { + return new this._schema.casterConstructor(obj); +} + +/** + * Creates a fn that notifies all child docs of `event`. + * + * @param {String} event + * @return {Function} + * @api private + */ + +MongooseDocumentArray.prototype.notify = function notify (event) { + var self = this; + return function notify (val) { + var i = self.length; + while (i--) { + if (!self[i]) continue; + self[i].emit(event, val); + } + } +} + +/*! + * Module exports. + */ + +module.exports = MongooseDocumentArray; diff --git a/test/node_modules/mongoose/lib/types/embedded.js b/test/node_modules/mongoose/lib/types/embedded.js new file mode 100644 index 0000000..3ebc842 --- /dev/null +++ b/test/node_modules/mongoose/lib/types/embedded.js @@ -0,0 +1,199 @@ +/*! + * Module dependencies. + */ + +var Document = require('../document') + , inspect = require('util').inspect; + +/** + * EmbeddedDocument constructor. + * + * @param {Object} obj js object returned from the db + * @param {MongooseDocumentArray} parentArr the parent array of this document + * @param {Boolean} skipId + * @inherits Document + * @api private + */ + +function EmbeddedDocument (obj, parentArr, skipId, fields) { + if (parentArr) { + this.__parentArray = parentArr; + this.__parent = parentArr._parent; + } else { + this.__parentArray = undefined; + this.__parent = undefined; + } + + Document.call(this, obj, fields, skipId); + + var self = this; + this.on('isNew', function (val) { + self.isNew = val; + }); +}; + +/*! + * Inherit from Document + */ + +EmbeddedDocument.prototype.__proto__ = Document.prototype; + +/** + * Marks the embedded doc modified. + * + * ####Example: + * + * var doc = blogpost.comments.id(hexstring); + * doc.mixed.type = 'changed'; + * doc.markModified('mixed.type'); + * + * @param {String} path the path which changed + * @api public + */ + +EmbeddedDocument.prototype.markModified = function (path) { + if (!this.__parentArray) return; + + this._activePaths.modify(path); + + if (this.isNew) { + // Mark the WHOLE parent array as modified + // if this is a new document (i.e., we are initializing + // a document), + this.__parentArray._markModified(); + } else + this.__parentArray._markModified(this, path); +}; + +/** + * Used as a stub for [hooks.js](https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3) + * + * ####NOTE: + * + * _This is a no-op. Does not actually save the doc to the db._ + * + * @param {Function} [fn] + * @return {EmbeddedDocument} this + * @api private + */ + +EmbeddedDocument.prototype.save = function(fn) { + if (fn) + fn(null); + return this; +}; + +/** + * Removes the subdocument from its parent array. + * + * @param {Function} [fn] + * @api public + */ + +EmbeddedDocument.prototype.remove = function (fn) { + if (!this.__parentArray) return this; + + var _id; + if (!this.willRemove) { + _id = this._doc._id; + if (!_id) { + throw new Error('For your own good, Mongoose does not know ' + + 'how to remove an EmbeddedDocument that has no _id'); + } + this.__parentArray.pull({ _id: _id }); + this.willRemove = true; + } + + if (fn) + fn(null); + + return this; +}; + +/** + * Override #update method of parent documents. + * @api private + */ + +EmbeddedDocument.prototype.update = function () { + throw new Error('The #update method is not available on EmbeddedDocuments'); +} + +/** + * Helper for console.log + * + * @api public + */ + +EmbeddedDocument.prototype.inspect = function () { + return inspect(this.toObject()); +}; + +/** + * Marks a path as invalid, causing validation to fail. + * + * @param {String} path the field to invalidate + * @param {String|Error} err error which states the reason `path` was invalid + * @return {Boolean} + * @api public + */ + +EmbeddedDocument.prototype.invalidate = function (path, err, first) { + if (!this.__parent) return false; + var index = this.__parentArray.indexOf(this); + var parentPath = this.__parentArray._path; + var fullPath = [parentPath, index, path].join('.'); + this.__parent.invalidate(fullPath, err); + if (first) + this._validationError = ownerDocument(this)._validationError; + return true; +} + +/** + * Returns the top level document of this sub-document. + * + * @return {Document} + */ + +EmbeddedDocument.prototype.ownerDocument = function () { + return ownerDocument(this); +} + +/*! + * Returns the top level document of this sub-document. + * + * @return {Document} + */ + +function ownerDocument (self) { + var parent = self.__parent; + while (parent.__parent) + parent = parent.__parent; + return parent; +} + +/** + * Returns this sub-documents parent document. + * + * @api public + */ + +EmbeddedDocument.prototype.parent = function () { + return this.__parent; +} + +/** + * Returns this sub-documents parent array. + * + * @api public + */ + +EmbeddedDocument.prototype.parentArray = function () { + return this.__parentArray; +} + +/*! + * Module exports. + */ + +module.exports = EmbeddedDocument; diff --git a/test/node_modules/mongoose/lib/types/index.js b/test/node_modules/mongoose/lib/types/index.js new file mode 100644 index 0000000..e6a9901 --- /dev/null +++ b/test/node_modules/mongoose/lib/types/index.js @@ -0,0 +1,13 @@ + +/*! + * Module exports. + */ + +exports.Array = require('./array'); +exports.Buffer = require('./buffer'); + +exports.Document = // @deprecate +exports.Embedded = require('./embedded'); + +exports.DocumentArray = require('./documentarray'); +exports.ObjectId = require('./objectid'); diff --git a/test/node_modules/mongoose/lib/types/objectid.js b/test/node_modules/mongoose/lib/types/objectid.js new file mode 100644 index 0000000..44ad43f --- /dev/null +++ b/test/node_modules/mongoose/lib/types/objectid.js @@ -0,0 +1,43 @@ + +/*! + * Access driver. + */ + +var driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native'; + +/** + * ObjectId type constructor + * + * ####Example + * + * var id = new mongoose.Types.ObjectId; + * + * @constructor ObjectId + */ + +var ObjectId = require(driver + '/objectid'); +module.exports = ObjectId; + +/** + * Creates an ObjectId from `str` + * + * @param {ObjectId|HexString} str + * @static fromString + * @receiver ObjectId + * @return {ObjectId} + * @api private + */ + +ObjectId.fromString; + +/** + * Converts `oid` to a string. + * + * @param {ObjectId} oid ObjectId instance + * @static toString + * @receiver ObjectId + * @return {String} + * @api private + */ + +ObjectId.toString; diff --git a/test/node_modules/mongoose/lib/utils.js b/test/node_modules/mongoose/lib/utils.js new file mode 100644 index 0000000..b9492c9 --- /dev/null +++ b/test/node_modules/mongoose/lib/utils.js @@ -0,0 +1,506 @@ +/*! + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , ReadPref = require('mongodb').ReadPreference + , ObjectId = require('./types/objectid') + , ms = require('ms') + , sliced = require('sliced') + , MongooseBuffer + , MongooseArray + , Document + +/** + * Produces a collection name from model `name`. + * + * @param {String} name a model name + * @return {String} a collection name + * @api private + */ + +exports.toCollectionName = function (name) { + if ('system.profile' === name) return name; + if ('system.indexes' === name) return name; + return pluralize(name.toLowerCase()); +}; + +/** + * Pluralization rules. + * + * These rules are applied while processing the argument to `toCollectionName`. + */ + +exports.pluralization = [ + [/(m)an$/gi, '$1en'], + [/(pe)rson$/gi, '$1ople'], + [/(child)$/gi, '$1ren'], + [/^(ox)$/gi, '$1en'], + [/(ax|test)is$/gi, '$1es'], + [/(octop|vir)us$/gi, '$1i'], + [/(alias|status)$/gi, '$1es'], + [/(bu)s$/gi, '$1ses'], + [/(buffal|tomat|potat)o$/gi, '$1oes'], + [/([ti])um$/gi, '$1a'], + [/sis$/gi, 'ses'], + [/(?:([^f])fe|([lr])f)$/gi, '$1$2ves'], + [/(hive)$/gi, '$1s'], + [/([^aeiouy]|qu)y$/gi, '$1ies'], + [/(x|ch|ss|sh)$/gi, '$1es'], + [/(matr|vert|ind)ix|ex$/gi, '$1ices'], + [/([m|l])ouse$/gi, '$1ice'], + [/(quiz)$/gi, '$1zes'], + [/s$/gi, 's'], + [/$/gi, 's'] +]; +var rules = exports.pluralization; + +/** + * Uncountable words. + * + * These words are applied while processing the argument to `toCollectionName`. + */ + +exports.uncountables = [ + 'advice', + 'energy', + 'excretion', + 'digestion', + 'cooperation', + 'health', + 'justice', + 'labour', + 'machinery', + 'equipment', + 'information', + 'pollution', + 'sewage', + 'paper', + 'money', + 'species', + 'series', + 'rain', + 'rice', + 'fish', + 'sheep', + 'moose', + 'deer', + 'news', + 'expertise', + 'status', + 'media' +]; +var uncountables = exports.uncountables; + +/*! + * Pluralize function. + * + * @author TJ Holowaychuk (extracted from _ext.js_) + * @param {String} string to pluralize + * @api private + */ + +function pluralize (str) { + var rule, found; + if (!~uncountables.indexOf(str.toLowerCase())){ + found = rules.filter(function(rule){ + return str.match(rule[0]); + }); + if (found[0]) return str.replace(found[0][0], found[0][1]); + } + return str; +}; + +/*! + * Add `once` to EventEmitter if absent + * + * @param {String} event name + * @param {Function} listener + * @api private + */ + +var Events = EventEmitter; + +if (!('once' in EventEmitter.prototype)){ + + Events = function () { + EventEmitter.apply(this, arguments); + }; + + /*! + * Inherit from EventEmitter. + */ + + Events.prototype.__proto__ = EventEmitter.prototype; + + /*! + * Add `once`. + */ + + Events.prototype.once = function (type, listener) { + var self = this; + self.on(type, function g(){ + self.removeListener(type, g); + listener.apply(this, arguments); + }); + }; +} +exports.EventEmitter = Events; + +/** + * Determines if `a` and `b` are deep equal. + * + * Modified from node/lib/assert.js + * + * @param {any} a a value to compare to `b` + * @param {any} b a value to compare to `a` + * @return {Boolean} + * @api private + */ + +exports.deepEqual = function deepEqual (a, b) { + if (a === b) return true; + + if (a instanceof Date && b instanceof Date) + return a.getTime() === b.getTime(); + + if (a instanceof ObjectId && b instanceof ObjectId) { + return a.toString() === b.toString(); + } + + if (typeof a !== 'object' && typeof b !== 'object') + return a == b; + + if (a === null || b === null || a === undefined || b === undefined) + return false + + if (a.prototype !== b.prototype) return false; + + // Handle MongooseNumbers + if (a instanceof Number && b instanceof Number) { + return a.valueOf() === b.valueOf(); + } + + if (Buffer.isBuffer(a)) { + if (!Buffer.isBuffer(b)) return false; + if (a.length !== b.length) return false; + for (var i = 0, len = a.length; i < len; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + } + + if (isMongooseObject(a)) a = a.toObject(); + if (isMongooseObject(b)) b = b.toObject(); + + try { + var ka = Object.keys(a), + kb = Object.keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!deepEqual(a[key], b[key])) return false; + } + + return true; +}; + +/** + * Object clone with Mongoose natives support. + * + * If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible. + * + * Functions are never cloned. + * + * @param {Object} obj the object to clone + * @param {Object} options + * @return {Object} the cloned object + * @api private + */ + +exports.clone = function clone (obj, options) { + if (obj === undefined || obj === null) + return obj; + + if (Array.isArray(obj)) + return cloneArray(obj, options); + + if (isMongooseObject(obj)) { + if (options && options.json && 'function' === typeof obj.toJSON) { + return obj.toJSON(options); + } else { + return obj.toObject(options); + } + } + + if ('Object' === obj.constructor.name) + return cloneObject(obj, options); + + if ('Date' === obj.constructor.name) + return new obj.constructor(+obj); + + if ('RegExp' === obj.constructor.name) + return new RegExp(obj.source); + + if (obj instanceof ObjectId) + return new ObjectId(obj.id); + + if (obj.valueOf) + return obj.valueOf(); +}; +var clone = exports.clone; + +/*! + * ignore + */ + +function cloneObject (obj, options) { + var retainKeyOrder = options && options.retainKeyOrder + , minimize = options && options.minimize + , ret = {} + , hasKeys + , keys + , val + , k + , i + + if (retainKeyOrder) { + for (k in obj) { + val = clone(obj[k], options); + + if (!minimize || ('undefined' !== typeof val)) { + hasKeys || (hasKeys = true); + ret[k] = val; + } + } + } else { + // faster + + keys = Object.keys(obj); + i = keys.length; + + while (i--) { + k = keys[i]; + val = clone(obj[k], options); + + if (!minimize || ('undefined' !== typeof val)) { + if (!hasKeys) hasKeys = true; + ret[k] = val; + } + } + } + + return minimize + ? hasKeys && ret + : ret; +}; + +function cloneArray (arr, options) { + var ret = []; + for (var i = 0, l = arr.length; i < l; i++) + ret.push(clone(arr[i], options)); + return ret; +}; + + +/** + * Copies and merges options with defaults. + * + * @param {Object} defaults + * @param {Object} options + * @return {Object} the merged object + * @api private + */ + +exports.options = function (defaults, options) { + var keys = Object.keys(defaults) + , i = keys.length + , k ; + + options = options || {}; + + while (i--) { + k = keys[i]; + if (!(k in options)) { + options[k] = defaults[k]; + } + } + + return options; +}; + +/** + * Generates a random string + * + * @api private + */ + +exports.random = function () { + return Math.random().toString().substr(3); +}; + +/** + * Merges `from` into `to` without overwriting existing properties. + * + * @param {Object} to + * @param {Object} from + * @api private + */ + +exports.merge = function merge (to, from) { + var keys = Object.keys(from) + , i = keys.length + , key + + while (i--) { + key = keys[i]; + if ('undefined' === typeof to[key]) { + to[key] = from[key]; + } else { + if (exports.isObject(from[key])) { + merge(to[key], from[key]); + } else { + to[key] = from[key]; + } + } + } +}; + +/*! + * toString helper + */ + +var toString = Object.prototype.toString; + +/** + * Determines if `arg` is an object. + * + * @param {Object|Array|String|Function|RegExp|any} arg + * @return {Boolean} + */ + +exports.isObject = function (arg) { + return '[object Object]' == toString.call(arg); +} + +/** + * A faster Array.prototype.slice.call(arguments) alternative + * @api private + */ + +exports.args = sliced; + +/** + * process.nextTick helper. + * + * Wraps `callback` in a try/catch + nextTick. + * + * node-mongodb-native has a habit of state corruption when an error is immediately thrown from within a collection callback. + * + * @param {Function} callback + * @api private + */ + +exports.tick = function tick (callback) { + if ('function' !== typeof callback) return; + return function () { + try { + callback.apply(this, arguments); + } catch (err) { + // only nextTick on err to get out of + // the event loop and avoid state corruption. + process.nextTick(function () { + throw err; + }); + } + } +} + +/** + * Returns if `v` is a mongoose object that has a `toObject()` method we can use. + * + * This is for compatibility with libs like Date.js which do foolish things to Natives. + * + * @param {any} v + * @api private + */ + +exports.isMongooseObject = function (v) { + Document || (Document = require('./document')); + MongooseArray || (MongooseArray = require('./types').Array); + MongooseBuffer || (MongooseBuffer = require('./types').Buffer); + + return v instanceof Document || + v instanceof MongooseArray || + v instanceof MongooseBuffer +} +var isMongooseObject = exports.isMongooseObject; + +/** + * Converts `expires` options of index objects to `expiresAfterSeconds` options for MongoDB. + * + * @param {Object} object + * @api private + */ + +exports.expires = function expires (object) { + if (!(object && 'Object' == object.constructor.name)) return; + if (!('expires' in object)) return; + + var when; + if ('string' != typeof object.expires) { + when = object.expires; + } else { + when = Math.round(ms(object.expires) / 1000); + } + object.expireAfterSeconds = when; + delete object.expires; +} + +exports.readPref = function readPref (pref, tags) { + if (Array.isArray(pref)) { + tags = pref[1]; + pref = pref[0]; + } + + switch (pref) { + case 'p': + pref = 'primary'; + break; + case 'pp': + pref = 'primaryPrefered'; + break; + case 's': + pref = 'secondary'; + break; + case 'sp': + pref = 'secondaryPrefered'; + break; + case 'n': + pref = 'nearest'; + break; + } + + return new ReadPref(pref, tags); +} diff --git a/test/node_modules/mongoose/lib/virtualtype.js b/test/node_modules/mongoose/lib/virtualtype.js new file mode 100644 index 0000000..819ac10 --- /dev/null +++ b/test/node_modules/mongoose/lib/virtualtype.js @@ -0,0 +1,103 @@ + +/** + * VirtualType constructor + * + * This is what mongoose uses to define virtual attributes via `Schema.prototype.virtual`. + * + * ####Example: + * + * var fullname = schema.virtual('fullname'); + * fullname instanceof mongoose.VirtualType // true + * + * @parma {Object} options + * @api public + */ + +function VirtualType (options, name) { + this.path = name; + this.getters = []; + this.setters = []; + this.options = options || {}; +} + +/** + * Defines a getter. + * + * ####Example: + * + * var virtual = schema.virtual('fullname'); + * virtual.get(function () { + * return this.name.first + ' ' + this.name.last; + * }); + * + * @param {Function} fn + * @return {VirtualType} this + * @api public + */ + +VirtualType.prototype.get = function (fn) { + this.getters.push(fn); + return this; +}; + +/** + * Defines a setter. + * + * ####Example: + * + * var virtual = schema.virtual('fullname'); + * virtual.set(function (v) { + * var parts = v.split(' '); + * this.name.first = parts[0]; + * this.name.last = parts[1]; + * }); + * + * @param {Function} fn + * @return {VirtualType} this + * @api public + */ + +VirtualType.prototype.set = function (fn) { + this.setters.push(fn); + return this; +}; + +/** + * Applies getters to `value` using optional `scope`. + * + * @param {Object} value + * @param {Object} scope + * @return {any} the value after applying all getters + * @api public + */ + +VirtualType.prototype.applyGetters = function (value, scope) { + var v = value; + for (var l = this.getters.length - 1; l >= 0; l--) { + v = this.getters[l].call(scope, v, this); + } + return v; +}; + +/** + * Applies setters to `value` using optional `scope`. + * + * @param {Object} value + * @param {Object} scope + * @return {any} the value after applying all setters + * @api public + */ + +VirtualType.prototype.applySetters = function (value, scope) { + var v = value; + for (var l = this.setters.length - 1; l >= 0; l--) { + v = this.setters[l].call(scope, v, this); + } + return v; +}; + +/*! + * exports + */ + +module.exports = VirtualType; diff --git a/test/node_modules/mongoose/node_modules/hooks/.npmignore b/test/node_modules/mongoose/node_modules/hooks/.npmignore new file mode 100644 index 0000000..96e29a8 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/hooks/.npmignore @@ -0,0 +1,2 @@ +**.swp +node_modules diff --git a/test/node_modules/mongoose/node_modules/hooks/Makefile b/test/node_modules/mongoose/node_modules/hooks/Makefile new file mode 100644 index 0000000..1db5d65 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/hooks/Makefile @@ -0,0 +1,9 @@ +test: + @NODE_ENV=test ./node_modules/expresso/bin/expresso \ + $(TESTFLAGS) \ + ./test.js + +test-cov: + @TESTFLAGS=--cov $(MAKE) test + +.PHONY: test test-cov diff --git a/test/node_modules/mongoose/node_modules/hooks/README.md b/test/node_modules/mongoose/node_modules/hooks/README.md new file mode 100644 index 0000000..af90a48 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/hooks/README.md @@ -0,0 +1,306 @@ +hooks +============ + +Add pre and post middleware hooks to your JavaScript methods. + +## Installation + npm install hooks + +## Motivation +Suppose you have a JavaScript object with a `save` method. + +It would be nice to be able to declare code that runs before `save` and after `save`. +For example, you might want to run validation code before every `save`, +and you might want to dispatch a job to a background job queue after `save`. + +One might have an urge to hard code this all into `save`, but that turns out to +couple all these pieces of functionality (validation, save, and job creation) more +tightly than is necessary. For example, what if someone does not want to do background +job creation after the logical save? + +It is nicer to tack on functionality using what we call `pre` and `post` hooks. These +are functions that you define and that you direct to execute before or after particular +methods. + +## Example +We can use `hooks` to add validation and background jobs in the following way: + + var hooks = require('hooks') + , Document = require('./path/to/some/document/constructor'); + + // Add hooks' methods: `hook`, `pre`, and `post` + for (var k in hooks) { + Document[k] = hooks[k]; + } + + // Define a new method that is able to invoke pre and post middleware + Document.hook('save', Document.prototype.save); + + // Define a middleware function to be invoked before 'save' + Document.pre('save', function validate (next) { + // The `this` context inside of `pre` and `post` functions + // is the Document instance + if (this.isValid()) next(); // next() passes control to the next middleware + // or to the target method itself + else next(new Error("Invalid")); // next(error) invokes an error callback + }); + + // Define a middleware function to be invoked after 'save' + Document.post('save', function createJob () { + this.sendToBackgroundQueue(); + }); + +If you already have defined `Document.prototype` methods for which you want pres and posts, +then you do not need to explicitly invoke `Document.hook(...)`. Invoking `Document.pre(methodName, fn)` +or `Document.post(methodName, fn)` will automatically and lazily change `Document.prototype[methodName]` +so that it plays well with `hooks`. An equivalent way to implement the previous example is: + +```javascript +var hooks = require('hooks') + , Document = require('./path/to/some/document/constructor'); + +// Add hooks' methods: `hook`, `pre`, and `post` +for (var k in hooks) { + Document[k] = hooks[k]; +} + +Document.prototype.save = function () { + // ... +}; + +// Define a middleware function to be invoked before 'save' +Document.pre('save', function validate (next) { + // The `this` context inside of `pre` and `post` functions + // is the Document instance + if (this.isValid()) next(); // next() passes control to the next middleware + // or to the target method itself + else next(new Error("Invalid")); // next(error) invokes an error callback +}); + +// Define a middleware function to be invoked after 'save' +Document.post('save', function createJob () { + this.sendToBackgroundQueue(); +}); +``` + +## Pres and Posts as Middleware +We structure pres and posts as middleware to give you maximum flexibility: + +1. You can define **multiple** pres (or posts) for a single method. +2. These pres (or posts) are then executed as a chain of methods. +3. Any functions in this middleware chain can choose to halt the chain's execution by `next`ing an Error from that middleware function. If this occurs, then none of the other middleware in the chain will execute, and the main method (e.g., `save`) will not execute. This is nice, for example, when we don't want a document to save if it is invalid. + +## Defining multiple pres (or posts) +`pre` is chainable, so you can define multiple pres via: + Document.pre('save', function (next, halt) { + console.log("hello"); + }).pre('save', function (next, halt) { + console.log("world"); + }); + +As soon as one pre finishes executing, the next one will be invoked, and so on. + +## Error Handling +You can define a default error handler by passing a 2nd function as the 3rd argument to `hook`: + Document.hook('set', function (path, val) { + this[path] = val; + }, function (err) { + // Handler the error here + console.error(err); + }); + +Then, we can pass errors to this handler from a pre or post middleware function: + Document.pre('set', function (next, path, val) { + next(new Error()); + }); + +If you do not set up a default handler, then `hooks` makes the default handler that just throws the `Error`. + +The default error handler can be over-rided on a per method invocation basis. + +If the main method that you are surrounding with pre and post middleware expects its last argument to be a function +with callback signature `function (error, ...)`, then that callback becomes the error handler, over-riding the default +error handler you may have set up. + +```javascript +Document.hook('save', function (callback) { + // Save logic goes here + ... +}); + +var doc = new Document(); +doc.save( function (err, saved) { + // We can pass err via `next` in any of our pre or post middleware functions + if (err) console.error(err); + + // Rest of callback logic follows ... +}); +``` + +## Mutating Arguments via Middleware +`pre` and `post` middleware can also accept the intended arguments for the method +they augment. This is useful if you want to mutate the arguments before passing +them along to the next middleware and eventually pass a mutated arguments list to +the main method itself. + +As a simple example, let's define a method `set` that just sets a key, value pair. +If we want to namespace the key, we can do so by adding a `pre` middleware hook +that runs before `set`, alters the arguments by namespacing the `key` argument, and passes them onto `set`: + + Document.hook('set', function (key, val) { + this[key] = val; + }); + Document.pre('set', function (next, key, val) { + next('namespace-' + key, val); + }); + var doc = new Document(); + doc.set('hello', 'world'); + console.log(doc.hello); // undefined + console.log(doc['namespace-hello']); // 'world' + +As you can see above, we pass arguments via `next`. + +If you are not mutating the arguments, then you can pass zero arguments +to `next`, and the next middleware function will still have access +to the arguments. + + Document.hook('set', function (key, val) { + this[key] = val; + }); + Document.pre('set', function (next, key, val) { + // I have access to key and val here + next(); // We don't need to pass anything to next + }); + Document.pre('set', function (next, key, val) { + // And I still have access to the original key and val here + next(); + }); + +Finally, you can add arguments that downstream middleware can also see: + + // Note that in the definition of `set`, there is no 3rd argument, options + Document.hook('set', function (key, val) { + // But... + var options = arguments[2]; // ...I have access to an options argument + // because of pre function pre2 (defined below) + console.log(options); // '{debug: true}' + this[key] = val; + }); + Document.pre('set', function pre1 (next, key, val) { + // I only have access to key and val arguments + console.log(arguments.length); // 3 + next(key, val, {debug: true}); + }); + Document.pre('set', function pre2 (next, key, val, options) { + console.log(arguments.length); // 4 + console.log(options); // '{ debug: true}' + next(); + }); + Document.pre('set', function pre3 (next, key, val, options) { + // I still have access to key, val, AND the options argument introduced via the preceding middleware + console.log(arguments.length); // 4 + console.log(options); // '{ debug: true}' + next(); + }); + + var doc = new Document() + doc.set('hey', 'there'); + +## Parallel `pre` middleware + +All middleware up to this point has been "serial" middleware -- i.e., middleware whose logic +is executed as a serial chain. + +Some scenarios call for parallel middleware -- i.e., middleware that can wait for several +asynchronous services at once to respond. + +For instance, you may only want to save a Document only after you have checked +that the Document is valid according to two different remote services. + +We accomplish asynchronous middleware by adding a second kind of flow control callback +(the only flow control callback so far has been `next`), called `done`. + +- `next` passes control to the next middleware in the chain +- `done` keeps track of how many parallel middleware have invoked `done` and passes + control to the target method when ALL parallel middleware have invoked `done`. If + you pass an `Error` to `done`, then the error is handled, and the main method that is + wrapped by pres and posts will not get invoked. + +We declare pre middleware that is parallel by passing a 3rd boolean argument to our `pre` +definition method. + +We illustrate via the parallel validation example mentioned above: + + Document.hook('save', function targetFn (callback) { + // Save logic goes here + // ... + // This only gets run once the two `done`s are both invoked via preOne and preTwo. + }); + + // true marks this as parallel middleware + Document.pre('save', true, function preOne (next, doneOne, callback) { + remoteServiceOne.validate(this.serialize(), function (err, isValid) { + // The code in here will probably be run after the `next` below this block + // and could possibly be run after the console.log("Hola") in `preTwo + if (err) return doneOne(err); + if (isValid) doneOne(); + }); + next(); // Pass control to the next middleware + }); + + // We will suppose that we need 2 different remote services to validate our document + Document.pre('save', true, function preTwo (next, doneTwo, callback) { + remoteServiceTwo.validate(this.serialize(), function (err, isValid) { + if (err) return doneTwo(err); + if (isValid) doneTwo(); + }); + next(); + }); + + // While preOne and preTwo are parallel, preThree is a serial pre middleware + Document.pre('save', function preThree (next, callback) { + next(); + }); + + var doc = new Document(); + doc.save( function (err, doc) { + // Do stuff with the saved doc here... + }); + +In the above example, flow control may happen in the following way: + +(1) doc.save -> (2) preOne --(next)--> (3) preTwo --(next)--> (4) preThree --(next)--> (wait for dones to invoke) -> (5) doneTwo -> (6) doneOne -> (7) targetFn + +So what's happening is that: + +1. You call `doc.save(...)` +2. First, your preOne middleware gets executed. It makes a remote call to the validation service and `next()`s to the preTwo middleware. +3. Now, your preTwo middleware gets executed. It makes a remote call to another validation service and `next()`s to the preThree middleware. +4. Your preThree middleware gets executed. It immediately `next()`s. But nothing else gets executing until both `doneOne` and `doneTwo` are invoked inside the callbacks handling the response from the two valiation services. +5. We will suppose that validation remoteServiceTwo returns a response to us first. In this case, we call `doneTwo` inside the callback to remoteServiceTwo. +6. Some fractions of a second later, remoteServiceOne returns a response to us. In this case, we call `doneOne` inside the callback to remoteServiceOne. +7. `hooks` implementation keeps track of how many parallel middleware has been defined per target function. It detects that both asynchronous pre middlewares (`preOne` and `preTwo`) have finally called their `done` functions (`doneOne` and `doneTwo`), so the implementation finally invokes our `targetFn` (i.e., our core `save` business logic). + +## Removing Pres + +You can remove a particular pre associated with a hook: + + Document.pre('set', someFn); + Document.removePre('set', someFn); + +And you can also remove all pres associated with a hook: + Document.removePre('set'); // Removes all declared `pre`s on the hook 'set' + +## Tests +To run the tests: + make test + +### Contributors +- [Brian Noguchi](https://github.com/bnoguchi) + +### License +MIT License + +--- +### Author +Brian Noguchi diff --git a/test/node_modules/mongoose/node_modules/hooks/hooks.alt.js b/test/node_modules/mongoose/node_modules/hooks/hooks.alt.js new file mode 100644 index 0000000..6ced357 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/hooks/hooks.alt.js @@ -0,0 +1,134 @@ +/** + * Hooks are useful if we want to add a method that automatically has `pre` and `post` hooks. + * For example, it would be convenient to have `pre` and `post` hooks for `save`. + * _.extend(Model, mixins.hooks); + * Model.hook('save', function () { + * console.log('saving'); + * }); + * Model.pre('save', function (next, done) { + * console.log('about to save'); + * next(); + * }); + * Model.post('save', function (next, done) { + * console.log('saved'); + * next(); + * }); + * + * var m = new Model(); + * m.save(); + * // about to save + * // saving + * // saved + */ + +// TODO Add in pre and post skipping options +module.exports = { + /** + * Declares a new hook to which you can add pres and posts + * @param {String} name of the function + * @param {Function} the method + * @param {Function} the error handler callback + */ + hook: function (name, fn, err) { + if (arguments.length === 1 && typeof name === 'object') { + for (var k in name) { // `name` is a hash of hookName->hookFn + this.hook(k, name[k]); + } + return; + } + + if (!err) err = fn; + + var proto = this.prototype || this + , pres = proto._pres = proto._pres || {} + , posts = proto._posts = proto._posts || {}; + pres[name] = pres[name] || []; + posts[name] = posts[name] || []; + + function noop () {} + + proto[name] = function () { + var self = this + , pres = this._pres[name] + , posts = this._posts[name] + , numAsyncPres = 0 + , hookArgs = [].slice.call(arguments) + , preChain = pres.map( function (pre, i) { + var wrapper = function () { + if (arguments[0] instanceof Error) + return err(arguments[0]); + if (numAsyncPres) { + // arguments[1] === asyncComplete + if (arguments.length) + hookArgs = [].slice.call(arguments, 2); + pre.apply(self, + [ preChain[i+1] || allPresInvoked, + asyncComplete + ].concat(hookArgs) + ); + } else { + if (arguments.length) + hookArgs = [].slice.call(arguments); + pre.apply(self, + [ preChain[i+1] || allPresDone ].concat(hookArgs)); + } + }; // end wrapper = function () {... + if (wrapper.isAsync = pre.isAsync) + numAsyncPres++; + return wrapper; + }); // end posts.map(...) + function allPresInvoked () { + if (arguments[0] instanceof Error) + err(arguments[0]); + } + + function allPresDone () { + if (arguments[0] instanceof Error) + return err(arguments[0]); + if (arguments.length) + hookArgs = [].slice.call(arguments); + fn.apply(self, hookArgs); + var postChain = posts.map( function (post, i) { + var wrapper = function () { + if (arguments[0] instanceof Error) + return err(arguments[0]); + if (arguments.length) + hookArgs = [].slice.call(arguments); + post.apply(self, + [ postChain[i+1] || noop].concat(hookArgs)); + }; // end wrapper = function () {... + return wrapper; + }); // end posts.map(...) + if (postChain.length) postChain[0](); + } + + if (numAsyncPres) { + complete = numAsyncPres; + function asyncComplete () { + if (arguments[0] instanceof Error) + return err(arguments[0]); + --complete || allPresDone.call(this); + } + } + (preChain[0] || allPresDone)(); + }; + + return this; + }, + + pre: function (name, fn, isAsync) { + var proto = this.prototype + , pres = proto._pres = proto._pres || {}; + if (fn.isAsync = isAsync) { + this.prototype[name].numAsyncPres++; + } + (pres[name] = pres[name] || []).push(fn); + return this; + }, + post: function (name, fn, isAsync) { + var proto = this.prototype + , posts = proto._posts = proto._posts || {}; + (posts[name] = posts[name] || []).push(fn); + return this; + } +}; diff --git a/test/node_modules/mongoose/node_modules/hooks/hooks.js b/test/node_modules/mongoose/node_modules/hooks/hooks.js new file mode 100644 index 0000000..9a887c7 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/hooks/hooks.js @@ -0,0 +1,161 @@ +// TODO Add in pre and post skipping options +module.exports = { + /** + * Declares a new hook to which you can add pres and posts + * @param {String} name of the function + * @param {Function} the method + * @param {Function} the error handler callback + */ + hook: function (name, fn, errorCb) { + if (arguments.length === 1 && typeof name === 'object') { + for (var k in name) { // `name` is a hash of hookName->hookFn + this.hook(k, name[k]); + } + return; + } + + var proto = this.prototype || this + , pres = proto._pres = proto._pres || {} + , posts = proto._posts = proto._posts || {}; + pres[name] = pres[name] || []; + posts[name] = posts[name] || []; + + proto[name] = function () { + var self = this + , hookArgs // arguments eventually passed to the hook - are mutable + , lastArg = arguments[arguments.length-1] + , pres = this._pres[name] + , posts = this._posts[name] + , _total = pres.length + , _current = -1 + , _asyncsLeft = proto[name].numAsyncPres + , _next = function () { + if (arguments[0] instanceof Error) { + return handleError(arguments[0]); + } + var _args = Array.prototype.slice.call(arguments) + , currPre + , preArgs; + if (_args.length && !(arguments[0] == null && typeof lastArg === 'function')) + hookArgs = _args; + if (++_current < _total) { + currPre = pres[_current] + if (currPre.isAsync && currPre.length < 2) + throw new Error("Your pre must have next and done arguments -- e.g., function (next, done, ...)"); + if (currPre.length < 1) + throw new Error("Your pre must have a next argument -- e.g., function (next, ...)"); + preArgs = (currPre.isAsync + ? [once(_next), once(_asyncsDone)] + : [once(_next)]).concat(hookArgs); + return currPre.apply(self, preArgs); + } else if (!proto[name].numAsyncPres) { + return _done.apply(self, hookArgs); + } + } + , _done = function () { + var args_ = Array.prototype.slice.call(arguments) + , ret, total_, current_, next_, done_, postArgs; + if (_current === _total) { + ret = fn.apply(self, args_); + total_ = posts.length; + current_ = -1; + next_ = function () { + if (arguments[0] instanceof Error) { + return handleError(arguments[0]); + } + var args_ = Array.prototype.slice.call(arguments, 1) + , currPost + , postArgs; + if (args_.length) hookArgs = args_; + if (++current_ < total_) { + currPost = posts[current_] + if (currPost.length < 1) + throw new Error("Your post must have a next argument -- e.g., function (next, ...)"); + postArgs = [once(next_)].concat(hookArgs); + return currPost.apply(self, postArgs); + } + }; + if (total_) return next_(); + return ret; + } + }; + if (_asyncsLeft) { + function _asyncsDone (err) { + if (err && err instanceof Error) { + return handleError(err); + } + --_asyncsLeft || _done.apply(self, hookArgs); + } + } + function handleError (err) { + if ('function' == typeof lastArg) + return lastArg(err); + if (errorCb) return errorCb.call(self, err); + throw err; + } + return _next.apply(this, arguments); + }; + + proto[name].numAsyncPres = 0; + + return this; + }, + + pre: function (name, isAsync, fn, errorCb) { + if ('boolean' !== typeof arguments[1]) { + errorCb = fn; + fn = isAsync; + isAsync = false; + } + var proto = this.prototype || this + , pres = proto._pres = proto._pres || {}; + + this._lazySetupHooks(proto, name, errorCb); + + if (fn.isAsync = isAsync) { + proto[name].numAsyncPres++; + } + + (pres[name] = pres[name] || []).push(fn); + return this; + }, + post: function (name, isAsync, fn) { + if (arguments.length === 2) { + fn = isAsync; + isAsync = false; + } + var proto = this.prototype || this + , posts = proto._posts = proto._posts || {}; + + this._lazySetupHooks(proto, name); + (posts[name] = posts[name] || []).push(fn); + return this; + }, + removePre: function (name, fnToRemove) { + var proto = this.prototype || this + , pres = proto._pres || (proto._pres || {}); + if (!pres[name]) return this; + if (arguments.length === 1) { + // Remove all pre callbacks for hook `name` + pres[name].length = 0; + } else { + pres[name] = pres[name].filter( function (currFn) { + return currFn !== fnToRemove; + }); + } + return this; + }, + _lazySetupHooks: function (proto, methodName, errorCb) { + if ('undefined' === typeof proto[methodName].numAsyncPres) { + this.hook(methodName, proto[methodName], errorCb); + } + } +}; + +function once (fn, scope) { + return function fnWrapper () { + if (fnWrapper.hookCalled) return; + fnWrapper.hookCalled = true; + fn.apply(scope, arguments); + }; +} diff --git a/test/node_modules/mongoose/node_modules/hooks/package.json b/test/node_modules/mongoose/node_modules/hooks/package.json new file mode 100644 index 0000000..acc84cc --- /dev/null +++ b/test/node_modules/mongoose/node_modules/hooks/package.json @@ -0,0 +1,49 @@ +{ + "name": "hooks", + "description": "Adds pre and post hook functionality to your JavaScript methods.", + "version": "0.2.1", + "keywords": [ + "node", + "hooks", + "middleware", + "pre", + "post" + ], + "homepage": "https://github.com/bnoguchi/hooks-js/", + "repository": { + "type": "git", + "url": "git://github.com/bnoguchi/hooks-js.git" + }, + "author": { + "name": "Brian Noguchi", + "email": "brian.noguchi@gmail.com", + "url": "https://github.com/bnoguchi/" + }, + "main": "./hooks.js", + "directories": { + "lib": "." + }, + "scripts": { + "test": "make test" + }, + "dependencies": {}, + "devDependencies": { + "expresso": ">=0.7.6", + "should": ">=0.2.1", + "underscore": ">=1.1.4" + }, + "engines": { + "node": ">=0.4.0" + }, + "licenses": [ + "MIT" + ], + "optionalDependencies": {}, + "readme": "hooks\n============\n\nAdd pre and post middleware hooks to your JavaScript methods.\n\n## Installation\n npm install hooks\n\n## Motivation\nSuppose you have a JavaScript object with a `save` method.\n\nIt would be nice to be able to declare code that runs before `save` and after `save`.\nFor example, you might want to run validation code before every `save`,\nand you might want to dispatch a job to a background job queue after `save`.\n\nOne might have an urge to hard code this all into `save`, but that turns out to\ncouple all these pieces of functionality (validation, save, and job creation) more\ntightly than is necessary. For example, what if someone does not want to do background\njob creation after the logical save? \n\nIt is nicer to tack on functionality using what we call `pre` and `post` hooks. These\nare functions that you define and that you direct to execute before or after particular\nmethods.\n\n## Example\nWe can use `hooks` to add validation and background jobs in the following way:\n\n var hooks = require('hooks')\n , Document = require('./path/to/some/document/constructor');\n\n // Add hooks' methods: `hook`, `pre`, and `post` \n for (var k in hooks) {\n Document[k] = hooks[k];\n }\n\n // Define a new method that is able to invoke pre and post middleware\n Document.hook('save', Document.prototype.save);\n\n // Define a middleware function to be invoked before 'save'\n Document.pre('save', function validate (next) {\n // The `this` context inside of `pre` and `post` functions\n // is the Document instance\n if (this.isValid()) next(); // next() passes control to the next middleware\n // or to the target method itself\n else next(new Error(\"Invalid\")); // next(error) invokes an error callback\n });\n\n // Define a middleware function to be invoked after 'save'\n Document.post('save', function createJob () {\n this.sendToBackgroundQueue();\n });\n\nIf you already have defined `Document.prototype` methods for which you want pres and posts,\nthen you do not need to explicitly invoke `Document.hook(...)`. Invoking `Document.pre(methodName, fn)`\nor `Document.post(methodName, fn)` will automatically and lazily change `Document.prototype[methodName]`\nso that it plays well with `hooks`. An equivalent way to implement the previous example is:\n\n```javascript\nvar hooks = require('hooks')\n , Document = require('./path/to/some/document/constructor');\n\n// Add hooks' methods: `hook`, `pre`, and `post` \nfor (var k in hooks) {\n Document[k] = hooks[k];\n}\n\nDocument.prototype.save = function () {\n // ...\n};\n\n// Define a middleware function to be invoked before 'save'\nDocument.pre('save', function validate (next) {\n // The `this` context inside of `pre` and `post` functions\n // is the Document instance\n if (this.isValid()) next(); // next() passes control to the next middleware\n // or to the target method itself\n else next(new Error(\"Invalid\")); // next(error) invokes an error callback\n});\n\n// Define a middleware function to be invoked after 'save'\nDocument.post('save', function createJob () {\n this.sendToBackgroundQueue();\n});\n```\n\n## Pres and Posts as Middleware\nWe structure pres and posts as middleware to give you maximum flexibility:\n\n1. You can define **multiple** pres (or posts) for a single method.\n2. These pres (or posts) are then executed as a chain of methods.\n3. Any functions in this middleware chain can choose to halt the chain's execution by `next`ing an Error from that middleware function. If this occurs, then none of the other middleware in the chain will execute, and the main method (e.g., `save`) will not execute. This is nice, for example, when we don't want a document to save if it is invalid.\n\n## Defining multiple pres (or posts)\n`pre` is chainable, so you can define multiple pres via:\n Document.pre('save', function (next, halt) {\n console.log(\"hello\");\n }).pre('save', function (next, halt) {\n console.log(\"world\");\n });\n\nAs soon as one pre finishes executing, the next one will be invoked, and so on.\n\n## Error Handling\nYou can define a default error handler by passing a 2nd function as the 3rd argument to `hook`:\n Document.hook('set', function (path, val) {\n this[path] = val;\n }, function (err) {\n // Handler the error here\n console.error(err);\n });\n\nThen, we can pass errors to this handler from a pre or post middleware function:\n Document.pre('set', function (next, path, val) {\n next(new Error());\n });\n\nIf you do not set up a default handler, then `hooks` makes the default handler that just throws the `Error`.\n\nThe default error handler can be over-rided on a per method invocation basis.\n\nIf the main method that you are surrounding with pre and post middleware expects its last argument to be a function\nwith callback signature `function (error, ...)`, then that callback becomes the error handler, over-riding the default\nerror handler you may have set up.\n \n```javascript\nDocument.hook('save', function (callback) {\n // Save logic goes here\n ...\n});\n\nvar doc = new Document();\ndoc.save( function (err, saved) {\n // We can pass err via `next` in any of our pre or post middleware functions\n if (err) console.error(err);\n \n // Rest of callback logic follows ...\n});\n```\n\n## Mutating Arguments via Middleware\n`pre` and `post` middleware can also accept the intended arguments for the method\nthey augment. This is useful if you want to mutate the arguments before passing\nthem along to the next middleware and eventually pass a mutated arguments list to\nthe main method itself.\n\nAs a simple example, let's define a method `set` that just sets a key, value pair.\nIf we want to namespace the key, we can do so by adding a `pre` middleware hook\nthat runs before `set`, alters the arguments by namespacing the `key` argument, and passes them onto `set`:\n\n Document.hook('set', function (key, val) {\n this[key] = val;\n });\n Document.pre('set', function (next, key, val) {\n next('namespace-' + key, val);\n });\n var doc = new Document();\n doc.set('hello', 'world');\n console.log(doc.hello); // undefined\n console.log(doc['namespace-hello']); // 'world'\n\nAs you can see above, we pass arguments via `next`.\n\nIf you are not mutating the arguments, then you can pass zero arguments\nto `next`, and the next middleware function will still have access\nto the arguments.\n\n Document.hook('set', function (key, val) {\n this[key] = val;\n });\n Document.pre('set', function (next, key, val) {\n // I have access to key and val here\n next(); // We don't need to pass anything to next\n });\n Document.pre('set', function (next, key, val) {\n // And I still have access to the original key and val here\n next();\n });\n\nFinally, you can add arguments that downstream middleware can also see:\n\n // Note that in the definition of `set`, there is no 3rd argument, options\n Document.hook('set', function (key, val) {\n // But...\n var options = arguments[2]; // ...I have access to an options argument\n // because of pre function pre2 (defined below)\n console.log(options); // '{debug: true}'\n this[key] = val;\n });\n Document.pre('set', function pre1 (next, key, val) {\n // I only have access to key and val arguments\n console.log(arguments.length); // 3\n next(key, val, {debug: true});\n });\n Document.pre('set', function pre2 (next, key, val, options) {\n console.log(arguments.length); // 4\n console.log(options); // '{ debug: true}'\n next();\n });\n Document.pre('set', function pre3 (next, key, val, options) {\n // I still have access to key, val, AND the options argument introduced via the preceding middleware\n console.log(arguments.length); // 4\n console.log(options); // '{ debug: true}'\n next();\n });\n \n var doc = new Document()\n doc.set('hey', 'there');\n\n## Parallel `pre` middleware\n\nAll middleware up to this point has been \"serial\" middleware -- i.e., middleware whose logic\nis executed as a serial chain.\n\nSome scenarios call for parallel middleware -- i.e., middleware that can wait for several\nasynchronous services at once to respond.\n\nFor instance, you may only want to save a Document only after you have checked\nthat the Document is valid according to two different remote services.\n\nWe accomplish asynchronous middleware by adding a second kind of flow control callback\n(the only flow control callback so far has been `next`), called `done`.\n\n- `next` passes control to the next middleware in the chain\n- `done` keeps track of how many parallel middleware have invoked `done` and passes\n control to the target method when ALL parallel middleware have invoked `done`. If\n you pass an `Error` to `done`, then the error is handled, and the main method that is\n wrapped by pres and posts will not get invoked.\n\nWe declare pre middleware that is parallel by passing a 3rd boolean argument to our `pre`\ndefinition method.\n\nWe illustrate via the parallel validation example mentioned above:\n\n Document.hook('save', function targetFn (callback) {\n // Save logic goes here\n // ...\n // This only gets run once the two `done`s are both invoked via preOne and preTwo.\n });\n\n // true marks this as parallel middleware\n Document.pre('save', true, function preOne (next, doneOne, callback) {\n remoteServiceOne.validate(this.serialize(), function (err, isValid) {\n // The code in here will probably be run after the `next` below this block\n // and could possibly be run after the console.log(\"Hola\") in `preTwo\n if (err) return doneOne(err);\n if (isValid) doneOne();\n });\n next(); // Pass control to the next middleware\n });\n \n // We will suppose that we need 2 different remote services to validate our document\n Document.pre('save', true, function preTwo (next, doneTwo, callback) {\n remoteServiceTwo.validate(this.serialize(), function (err, isValid) {\n if (err) return doneTwo(err);\n if (isValid) doneTwo();\n });\n next();\n });\n \n // While preOne and preTwo are parallel, preThree is a serial pre middleware\n Document.pre('save', function preThree (next, callback) {\n next();\n });\n \n var doc = new Document();\n doc.save( function (err, doc) {\n // Do stuff with the saved doc here...\n });\n\nIn the above example, flow control may happen in the following way:\n\n(1) doc.save -> (2) preOne --(next)--> (3) preTwo --(next)--> (4) preThree --(next)--> (wait for dones to invoke) -> (5) doneTwo -> (6) doneOne -> (7) targetFn\n\nSo what's happening is that:\n\n1. You call `doc.save(...)`\n2. First, your preOne middleware gets executed. It makes a remote call to the validation service and `next()`s to the preTwo middleware.\n3. Now, your preTwo middleware gets executed. It makes a remote call to another validation service and `next()`s to the preThree middleware.\n4. Your preThree middleware gets executed. It immediately `next()`s. But nothing else gets executing until both `doneOne` and `doneTwo` are invoked inside the callbacks handling the response from the two valiation services.\n5. We will suppose that validation remoteServiceTwo returns a response to us first. In this case, we call `doneTwo` inside the callback to remoteServiceTwo.\n6. Some fractions of a second later, remoteServiceOne returns a response to us. In this case, we call `doneOne` inside the callback to remoteServiceOne.\n7. `hooks` implementation keeps track of how many parallel middleware has been defined per target function. It detects that both asynchronous pre middlewares (`preOne` and `preTwo`) have finally called their `done` functions (`doneOne` and `doneTwo`), so the implementation finally invokes our `targetFn` (i.e., our core `save` business logic).\n\n## Removing Pres\n\nYou can remove a particular pre associated with a hook:\n\n Document.pre('set', someFn);\n Document.removePre('set', someFn);\n\nAnd you can also remove all pres associated with a hook:\n Document.removePre('set'); // Removes all declared `pre`s on the hook 'set'\n\n## Tests\nTo run the tests:\n make test\n\n### Contributors\n- [Brian Noguchi](https://github.com/bnoguchi)\n\n### License\nMIT License\n\n---\n### Author\nBrian Noguchi\n", + "readmeFilename": "README.md", + "_id": "hooks@0.2.1", + "dist": { + "shasum": "f84022ce913384a20369adb2d062761465f6b931" + }, + "_from": "hooks@0.2.1" +} diff --git a/test/node_modules/mongoose/node_modules/hooks/test.js b/test/node_modules/mongoose/node_modules/hooks/test.js new file mode 100644 index 0000000..efe2f3e --- /dev/null +++ b/test/node_modules/mongoose/node_modules/hooks/test.js @@ -0,0 +1,691 @@ +var hooks = require('./hooks') + , should = require('should') + , assert = require('assert') + , _ = require('underscore'); + +// TODO Add in test for making sure all pres get called if pre is defined directly on an instance. +// TODO Test for calling `done` twice or `next` twice in the same function counts only once +module.exports = { + 'should be able to assign multiple hooks at once': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook({ + hook1: function (a) {}, + hook2: function (b) {} + }); + var a = new A(); + assert.equal(typeof a.hook1, 'function'); + assert.equal(typeof a.hook2, 'function'); + }, + 'should run without pres and posts when not present': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + var a = new A(); + a.save(); + a.value.should.equal(1); + }, + 'should run with pres when present': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + A.pre('save', function (next) { + this.preValue = 2; + next(); + }); + var a = new A(); + a.save(); + a.value.should.equal(1); + a.preValue.should.equal(2); + }, + 'should run with posts when present': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + A.post('save', function (next) { + this.value = 2; + next(); + }); + var a = new A(); + a.save(); + a.value.should.equal(2); + }, + 'should run pres and posts when present': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + A.pre('save', function (next) { + this.preValue = 2; + next(); + }); + A.post('save', function (next) { + this.value = 3; + next(); + }); + var a = new A(); + a.save(); + a.value.should.equal(3); + a.preValue.should.equal(2); + }, + 'should run posts after pres': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + A.pre('save', function (next) { + this.override = 100; + next(); + }); + A.post('save', function (next) { + this.override = 200; + next(); + }); + var a = new A(); + a.save(); + a.value.should.equal(1); + a.override.should.equal(200); + }, + 'should not run a hook if a pre fails': function () { + var A = function () {}; + _.extend(A, hooks); + var counter = 0; + A.hook('save', function () { + this.value = 1; + }, function (err) { + counter++; + }); + A.pre('save', true, function (next, done) { + next(new Error()); + }); + var a = new A(); + a.save(); + counter.should.equal(1); + assert.equal(typeof a.value, 'undefined'); + }, + 'should be able to run multiple pres': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + A.pre('save', function (next) { + this.v1 = 1; + next(); + }).pre('save', function (next) { + this.v2 = 2; + next(); + }); + var a = new A(); + a.save(); + a.v1.should.equal(1); + a.v2.should.equal(2); + }, + 'should run multiple pres until a pre fails and not call the hook': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }, function (err) {}); + A.pre('save', function (next) { + this.v1 = 1; + next(); + }).pre('save', function (next) { + next(new Error()); + }).pre('save', function (next) { + this.v3 = 3; + next(); + }); + var a = new A(); + a.save(); + a.v1.should.equal(1); + assert.equal(typeof a.v3, 'undefined'); + assert.equal(typeof a.value, 'undefined'); + }, + 'should be able to run multiple posts': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + A.post('save', function (next) { + this.value = 2; + next(); + }).post('save', function (next) { + this.value = 3.14; + next(); + }).post('save', function (next) { + this.v3 = 3; + next(); + }); + var a = new A(); + a.save(); + assert.equal(a.value, 3.14); + assert.equal(a.v3, 3); + }, + 'should run only posts up until an error': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }, function (err) {}); + A.post('save', function (next) { + this.value = 2; + next(); + }).post('save', function (next) { + this.value = 3; + next(new Error()); + }).post('save', function (next) { + this.value = 4; + next(); + }); + var a = new A(); + a.save(); + a.value.should.equal(3); + }, + "should fall back first to the hook method's last argument as the error handler if it is a function of arity 1 or 2": function () { + var A = function () {}; + _.extend(A, hooks); + var counter = 0; + A.hook('save', function (callback) { + this.value = 1; + }); + A.pre('save', true, function (next, done) { + next(new Error()); + }); + var a = new A(); + a.save( function (err) { + if (err instanceof Error) counter++; + }); + counter.should.equal(1); + should.deepEqual(undefined, a.value); + }, + 'should fall back second to the default error handler if specified': function () { + var A = function () {}; + _.extend(A, hooks); + var counter = 0; + A.hook('save', function (callback) { + this.value = 1; + }, function (err) { + if (err instanceof Error) counter++; + }); + A.pre('save', true, function (next, done) { + next(new Error()); + }); + var a = new A(); + a.save(); + counter.should.equal(1); + should.deepEqual(undefined, a.value); + }, + 'fallback default error handler should scope to the object': function () { + var A = function () { + this.counter = 0; + }; + _.extend(A, hooks); + var counter = 0; + A.hook('save', function (callback) { + this.value = 1; + }, function (err) { + if (err instanceof Error) this.counter++; + }); + A.pre('save', true, function (next, done) { + next(new Error()); + }); + var a = new A(); + a.save(); + a.counter.should.equal(1); + should.deepEqual(undefined, a.value); + }, + 'should fall back last to throwing the error': function () { + var A = function () {}; + _.extend(A, hooks); + var counter = 0; + A.hook('save', function (err) { + if (err instanceof Error) return counter++; + this.value = 1; + }); + A.pre('save', true, function (next, done) { + next(new Error()); + }); + var a = new A(); + var didCatch = false; + try { + a.save(); + } catch (e) { + didCatch = true; + e.should.be.an.instanceof(Error); + counter.should.equal(0); + assert.equal(typeof a.value, 'undefined'); + } + didCatch.should.be.true; + }, + "should proceed without mutating arguments if `next(null|undefined)` is called in a serial pre, and the last argument of the target method is a callback with node-like signature function (err, obj) {...} or function (err) {...}": function () { + var A = function () {}; + _.extend(A, hooks); + var counter = 0; + A.prototype.save = function (callback) { + this.value = 1; + callback(); + }; + A.pre('save', function (next) { + next(null); + }); + A.pre('save', function (next) { + next(undefined); + }); + var a = new A(); + a.save( function (err) { + if (err instanceof Error) counter++; + else counter--; + }); + counter.should.equal(-1); + a.value.should.eql(1); + }, + "should proceed with mutating arguments if `next(null|undefined)` is callback in a serial pre, and the last argument of the target method is not a function": function () { + var A = function () {}; + _.extend(A, hooks); + A.prototype.set = function (v) { + this.value = v; + }; + A.pre('set', function (next) { + next(undefined); + }); + A.pre('set', function (next) { + next(null); + }); + var a = new A(); + a.set(1); + should.strictEqual(null, a.value); + }, + 'should not run any posts if a pre fails': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 2; + }, function (err) {}); + A.pre('save', function (next) { + this.value = 1; + next(new Error()); + }).post('save', function (next) { + this.value = 3; + next(); + }); + var a = new A(); + a.save(); + a.value.should.equal(1); + }, + + "can pass the hook's arguments verbatim to pres": function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('set', function (path, val) { + this[path] = val; + }); + A.pre('set', function (next, path, val) { + path.should.equal('hello'); + val.should.equal('world'); + next(); + }); + var a = new A(); + a.set('hello', 'world'); + a.hello.should.equal('world'); + }, +// "can pass the hook's arguments as an array to pres": function () { +// // Great for dynamic arity - e.g., slice(...) +// var A = function () {}; +// _.extend(A, hooks); +// A.hook('set', function (path, val) { +// this[path] = val; +// }); +// A.pre('set', function (next, hello, world) { +// hello.should.equal('hello'); +// world.should.equal('world'); +// next(); +// }); +// var a = new A(); +// a.set('hello', 'world'); +// assert.equal(a.hello, 'world'); +// }, + "can pass the hook's arguments verbatim to posts": function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('set', function (path, val) { + this[path] = val; + }); + A.post('set', function (next, path, val) { + path.should.equal('hello'); + val.should.equal('world'); + next(); + }); + var a = new A(); + a.set('hello', 'world'); + assert.equal(a.hello, 'world'); + }, +// "can pass the hook's arguments as an array to posts": function () { +// var A = function () {}; +// _.extend(A, hooks); +// A.hook('set', function (path, val) { +// this[path] = val; +// }); +// A.post('set', function (next, halt, args) { +// assert.equal(args[0], 'hello'); +// assert.equal(args[1], 'world'); +// next(); +// }); +// var a = new A(); +// a.set('hello', 'world'); +// assert.equal(a.hello, 'world'); +// }, + "pres should be able to modify and pass on a modified version of the hook's arguments": function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('set', function (path, val) { + this[path] = val; + assert.equal(arguments[2], 'optional'); + }); + A.pre('set', function (next, path, val) { + next('foo', 'bar'); + }); + A.pre('set', function (next, path, val) { + assert.equal(path, 'foo'); + assert.equal(val, 'bar'); + next('rock', 'says', 'optional'); + }); + A.pre('set', function (next, path, val, opt) { + assert.equal(path, 'rock'); + assert.equal(val, 'says'); + assert.equal(opt, 'optional'); + next(); + }); + var a = new A(); + a.set('hello', 'world'); + assert.equal(typeof a.hello, 'undefined'); + a.rock.should.equal('says'); + }, + 'posts should see the modified version of arguments if the pres modified them': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('set', function (path, val) { + this[path] = val; + }); + A.pre('set', function (next, path, val) { + next('foo', 'bar'); + }); + A.post('set', function (next, path, val) { + path.should.equal('foo'); + val.should.equal('bar'); + }); + var a = new A(); + a.set('hello', 'world'); + assert.equal(typeof a.hello, 'undefined'); + a.foo.should.equal('bar'); + }, + 'should pad missing arguments (relative to expected arguments of the hook) with null': function () { + // Otherwise, with hookFn = function (a, b, next, ), + // if we use hookFn(a), then because the pre functions are of the form + // preFn = function (a, b, next, ), then it actually gets executed with + // preFn(a, next, ), so when we call next() from within preFn, we are actually + // calling () + + var A = function () {}; + _.extend(A, hooks); + A.hook('set', function (path, val, opts) { + this[path] = val; + }); + A.pre('set', function (next, path, val, opts) { + next('foo', 'bar'); + assert.equal(typeof opts, 'undefined'); + }); + var a = new A(); + a.set('hello', 'world'); + }, + + 'should not invoke the target method until all asynchronous middleware have invoked dones': function () { + var counter = 0; + var A = function () {}; + _.extend(A, hooks); + A.hook('set', function (path, val) { + counter++; + this[path] = val; + counter.should.equal(7); + }); + A.pre('set', function (next, path, val) { + counter++; + next(); + }); + A.pre('set', true, function (next, done, path, val) { + counter++; + setTimeout(function () { + counter++; + done(); + }, 1000); + next(); + }); + A.pre('set', function (next, path, val) { + counter++; + next(); + }); + A.pre('set', true, function (next, done, path, val) { + counter++; + setTimeout(function () { + counter++; + done(); + }, 500); + next(); + }); + var a = new A(); + a.set('hello', 'world'); + }, + + 'invoking a method twice should run its async middleware twice': function () { + var counter = 0; + var A = function () {}; + _.extend(A, hooks); + A.hook('set', function (path, val) { + this[path] = val; + if (path === 'hello') counter.should.equal(1); + if (path === 'foo') counter.should.equal(2); + }); + A.pre('set', true, function (next, done, path, val) { + setTimeout(function () { + counter++; + done(); + }, 1000); + next(); + }); + var a = new A(); + a.set('hello', 'world'); + a.set('foo', 'bar'); + }, + + 'calling the same done multiple times should have the effect of only calling it once': function () { + var A = function () { + this.acked = false; + }; + _.extend(A, hooks); + A.hook('ack', function () { + console.log("UH OH, YOU SHOULD NOT BE SEEING THIS"); + this.acked = true; + }); + A.pre('ack', true, function (next, done) { + next(); + done(); + done(); + }); + A.pre('ack', true, function (next, done) { + next(); + // Notice that done() is not invoked here + }); + var a = new A(); + a.ack(); + setTimeout( function () { + a.acked.should.be.false; + }, 1000); + }, + + 'calling the same next multiple times should have the effect of only calling it once': function (beforeExit) { + var A = function () { + this.acked = false; + }; + _.extend(A, hooks); + A.hook('ack', function () { + console.log("UH OH, YOU SHOULD NOT BE SEEING THIS"); + this.acked = true; + }); + A.pre('ack', function (next) { + // force a throw to re-exec next() + try { + next(new Error('bam')); + } catch (err) { + next(); + } + }); + A.pre('ack', function (next) { + next(); + }); + var a = new A(); + a.ack(); + beforeExit( function () { + a.acked.should.be.false; + }); + }, + + 'asynchronous middleware should be able to pass an error via `done`, stopping the middleware chain': function () { + var counter = 0; + var A = function () {}; + _.extend(A, hooks); + A.hook('set', function (path, val, fn) { + counter++; + this[path] = val; + fn(null); + }); + A.pre('set', true, function (next, done, path, val, fn) { + setTimeout(function () { + counter++; + done(new Error); + }, 1000); + next(); + }); + var a = new A(); + a.set('hello', 'world', function (err) { + err.should.be.an.instanceof(Error); + should.strictEqual(undefined, a['hello']); + counter.should.eql(1); + }); + }, + + 'should be able to remove a particular pre': function () { + var A = function () {} + , preTwo; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + A.pre('save', function (next) { + this.preValueOne = 2; + next(); + }); + A.pre('save', preTwo = function (next) { + this.preValueTwo = 4; + next(); + }); + A.removePre('save', preTwo); + var a = new A(); + a.save(); + a.value.should.equal(1); + a.preValueOne.should.equal(2); + should.strictEqual(undefined, a.preValueTwo); + }, + + 'should be able to remove all pres associated with a hook': function () { + var A = function () {}; + _.extend(A, hooks); + A.hook('save', function () { + this.value = 1; + }); + A.pre('save', function (next) { + this.preValueOne = 2; + next(); + }); + A.pre('save', function (next) { + this.preValueTwo = 4; + next(); + }); + A.removePre('save'); + var a = new A(); + a.save(); + a.value.should.equal(1); + should.strictEqual(undefined, a.preValueOne); + should.strictEqual(undefined, a.preValueTwo); + }, + + '#pre should lazily make a method hookable': function () { + var A = function () {}; + _.extend(A, hooks); + A.prototype.save = function () { + this.value = 1; + }; + A.pre('save', function (next) { + this.preValue = 2; + next(); + }); + var a = new A(); + a.save(); + a.value.should.equal(1); + a.preValue.should.equal(2); + }, + + '#pre lazily making a method hookable should be able to provide a default errorHandler as the last argument': function () { + var A = function () {}; + var preValue = ""; + _.extend(A, hooks); + A.prototype.save = function () { + this.value = 1; + }; + A.pre('save', function (next) { + next(new Error); + }, function (err) { + preValue = 'ERROR'; + }); + var a = new A(); + a.save(); + should.strictEqual(undefined, a.value); + preValue.should.equal('ERROR'); + }, + + '#post should lazily make a method hookable': function () { + var A = function () {}; + _.extend(A, hooks); + A.prototype.save = function () { + this.value = 1; + }; + A.post('save', function (next) { + this.value = 2; + next(); + }); + var a = new A(); + a.save(); + a.value.should.equal(2); + }, + + "a lazy hooks setup should handle errors via a method's last argument, if it's a callback": function () { + var A = function () {}; + _.extend(A, hooks); + A.prototype.save = function (fn) {}; + A.pre('save', function (next) { + next(new Error("hi there")); + }); + var a = new A(); + a.save( function (err) { + err.should.be.an.instanceof(Error); + }); + } +}; diff --git a/test/node_modules/mongoose/node_modules/mongodb/.travis.yml b/test/node_modules/mongoose/node_modules/mongodb/.travis.yml new file mode 100644 index 0000000..94740d0 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - 0.9 # development version of 0.8, may be unstable \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/CONTRIBUTING.md b/test/node_modules/mongoose/node_modules/mongodb/CONTRIBUTING.md new file mode 100644 index 0000000..2a1c52e --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/CONTRIBUTING.md @@ -0,0 +1,23 @@ +## Contributing to the driver + +### Bugfixes + +- Before starting to write code, look for existing [tickets](https://github.com/mongodb/node-mongodb-native/issues) or [create one](https://github.com/mongodb/node-mongodb-native/issues/new) for your specific issue. That way you avoid working on something that might not be of interest or that has been addressed already in a different branch. +- Fork the [repo](https://github.com/mongodb/node-mongodb-native) _or_ for small documentation changes, navigate to the source on github and click the [Edit](https://github.com/blog/844-forking-with-the-edit-button) button. +- Follow the general coding style of the rest of the project: + - 2 space tabs + - no trailing whitespace + - comma last + - inline documentation for new methods, class members, etc + - 0 space between conditionals/functions, and their parenthesis and curly braces + - `if(..) {` + - `for(..) {` + - `while(..) {` + - `function(err) {` +- Write tests and make sure they pass (execute `make test` from the cmd line to run the test suite). + +### Documentation + +To contribute to the [API documentation](http://mongodb.github.com/node-mongodb-native/) just make your changes to the inline documentation of the appropriate [source code](https://github.com/mongodb/node-mongodb-native/tree/master/docs) in the master branch and submit a [pull request](https://help.github.com/articles/using-pull-requests/). You might also use the github [Edit](https://github.com/blog/844-forking-with-the-edit-button) button. + +If you'd like to preview your documentation changes, first commit your changes to your local master branch, then execute `make generate_docs`. Make sure you have the python documentation framework sphinx installed `easy_install sphinx`. The docs are generated under `docs/build'. If all looks good, submit a [pull request](https://help.github.com/articles/using-pull-requests/) to the master branch with your changes. \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/Makefile b/test/node_modules/mongoose/node_modules/mongodb/Makefile new file mode 100644 index 0000000..ac55626 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/Makefile @@ -0,0 +1,64 @@ +NODE = node +NPM = npm +NODEUNIT = node_modules/nodeunit/bin/nodeunit +DOX = node_modules/dox/bin/dox +name = all + +total: build_native + +test-coverage: + rm -rf lib-cov/ + jscoverage lib/ lib-cov/ + @TEST_COVERAGE=true nodeunit test/ test/gridstore test/connection + +build_native: + +test: build_native + @echo "\n == Run All tests minus replicaset tests==" + $(NODE) dev/tools/test_all.js --noreplicaset --boot + +test_pure: build_native + @echo "\n == Run All tests minus replicaset tests==" + $(NODE) dev/tools/test_all.js --noreplicaset --boot --nonative + +test_junit: build_native + @echo "\n == Run All tests minus replicaset tests==" + $(NODE) dev/tools/test_all.js --junit --noreplicaset --nokill + +jenkins: build_native + @echo "\n == Run All tests minus replicaset tests==" + $(NODE) dev/tools/test_all.js --junit --noreplicaset --nokill + +test_nodeunit_pure: + @echo "\n == Execute Test Suite using Pure JS BSON Parser == " + @$(NODEUNIT) test/ test/gridstore test/bson + +test_nodeunit_replicaset_pure: + @echo "\n == Execute Test Suite using Pure JS BSON Parser == " + @$(NODEUNIT) test/replicaset + +test_nodeunit_native: + @echo "\n == Execute Test Suite using Native BSON Parser == " + @TEST_NATIVE=TRUE $(NODEUNIT) test/ test/gridstore test/bson + +test_nodeunit_replicaset_native: + @echo "\n == Execute Test Suite using Native BSON Parser == " + @TEST_NATIVE=TRUE $(NODEUNIT) test/replicaset + +test_all: build_native + @echo "\n == Run All tests ==" + $(NODE) dev/tools/test_all.js --boot + +test_all_junit: build_native + @echo "\n == Run All tests ==" + $(NODE) dev/tools/test_all.js --junit --boot + +clean: + rm ./external-libs/bson/bson.node + rm -r ./external-libs/bson/build + +generate_docs: + $(NODE) dev/tools/build-docs.js + make --directory=./docs/sphinx-docs --file=Makefile html + +.PHONY: total diff --git a/test/node_modules/mongoose/node_modules/mongodb/Readme.md b/test/node_modules/mongoose/node_modules/mongodb/Readme.md new file mode 100644 index 0000000..b81e719 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/Readme.md @@ -0,0 +1,442 @@ +Up to date documentation +======================== + +[Documentation](http://mongodb.github.com/node-mongodb-native/) + +Install +======= + +To install the most recent release from npm, run: + + npm install mongodb + +That may give you a warning telling you that bugs['web'] should be bugs['url'], it would be safe to ignore it (this has been fixed in the development version) + +To install the latest from the repository, run:: + + npm install path/to/node-mongodb-native + +Community +========= +Check out the google group [node-mongodb-native](http://groups.google.com/group/node-mongodb-native) for questions/answers from users of the driver. + +Try it live +============ + + +Introduction +============ + +This is a node.js driver for MongoDB. It's a port (or close to a port) of the library for ruby at http://github.com/mongodb/mongo-ruby-driver/. + +A simple example of inserting a document. + +```javascript + var client = new Db('test', new Server("127.0.0.1", 27017, {}), {w: 1}), + test = function (err, collection) { + collection.insert({a:2}, function(err, docs) { + + collection.count(function(err, count) { + test.assertEquals(1, count); + }); + + // Locate all the entries using find + collection.find().toArray(function(err, results) { + test.assertEquals(1, results.length); + test.assertTrue(results[0].a === 2); + + // Let's close the db + client.close(); + }); + }); + }; + + client.open(function(err, p_client) { + client.collection('test_insert', test); + }); +``` + +Data types +========== + +To store and retrieve the non-JSON MongoDb primitives ([ObjectID](http://www.mongodb.org/display/DOCS/Object+IDs), Long, Binary, [Timestamp](http://www.mongodb.org/display/DOCS/Timestamp+data+type), [DBRef](http://www.mongodb.org/display/DOCS/Database+References#DatabaseReferences-DBRef), Code). + +In particular, every document has a unique `_id` which can be almost any type, and by default a 12-byte ObjectID is created. ObjectIDs can be represented as 24-digit hexadecimal strings, but you must convert the string back into an ObjectID before you can use it in the database. For example: + +```javascript + // Get the objectID type + var ObjectID = require('mongodb').ObjectID; + + var idString = '4e4e1638c85e808431000003'; + collection.findOne({_id: new ObjectID(idString)}, console.log) // ok + collection.findOne({_id: idString}, console.log) // wrong! callback gets undefined +``` + +Here are the constructors the non-Javascript BSON primitive types: + +```javascript + // Fetch the library + var mongo = require('mongodb'); + // Create new instances of BSON types + new mongo.Long(numberString) + new mongo.ObjectID(hexString) + new mongo.Timestamp() // the actual unique number is generated on insert. + new mongo.DBRef(collectionName, id, dbName) + new mongo.Binary(buffer) // takes a string or Buffer + new mongo.Code(code, [context]) + new mongo.Symbol(string) + new mongo.MinKey() + new mongo.MaxKey() + new mongo.Double(number) // Force double storage +``` + +The C/C++ bson parser/serializer +-------------------------------- + +If you are running a version of this library has the C/C++ parser compiled, to enable the driver to use the C/C++ bson parser pass it the option native_parser:true like below + +```javascript + // using native_parser: + var client = new Db('integration_tests_20', + new Server("127.0.0.1", 27017), + {native_parser:true}); +``` + +The C++ parser uses the js objects both for serialization and deserialization. + +GitHub information +================== + +The source code is available at http://github.com/mongodb/node-mongodb-native. +You can either clone the repository or download a tarball of the latest release. + +Once you have the source you can test the driver by running + + $ make test + +in the main directory. You will need to have a mongo instance running on localhost for the integration tests to pass. + +Examples +======== + +For examples look in the examples/ directory. You can execute the examples using node. + + $ cd examples + $ node queries.js + +GridStore +========= + +The GridStore class allows for storage of binary files in mongoDB using the mongoDB defined files and chunks collection definition. + +For more information have a look at [Gridstore](https://github.com/mongodb/node-mongodb-native/blob/master/docs/gridfs.md) + +Replicasets +=========== +For more information about how to connect to a replicaset have a look at [Replicasets](https://github.com/mongodb/node-mongodb-native/blob/master/docs/replicaset.md) + +Primary Key Factories +--------------------- + +Defining your own primary key factory allows you to generate your own series of id's +(this could f.ex be to use something like ISBN numbers). The generated the id needs to be a 12 byte long "string". + +Simple example below + +```javascript + // Custom factory (need to provide a 12 byte array); + CustomPKFactory = function() {} + CustomPKFactory.prototype = new Object(); + CustomPKFactory.createPk = function() { + return new ObjectID("aaaaaaaaaaaa"); + } + + var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory}); + p_client.open(function(err, p_client) { + p_client.dropDatabase(function(err, done) { + p_client.createCollection('test_custom_key', function(err, collection) { + collection.insert({'a':1}, function(err, docs) { + collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { + cursor.toArray(function(err, items) { + test.assertEquals(1, items.length); + + // Let's close the db + p_client.close(); + }); + }); + }); + }); + }); + }); +``` + +Strict mode +----------- + +Each database has an optional strict mode. If it is set then asking for a collection +that does not exist will return an Error object in the callback. Similarly if you +attempt to create a collection that already exists. Strict is provided for convenience. + +```javascript + var error_client = new Db('integration_tests_', new Server("127.0.0.1", 27017, {auto_reconnect: false}), {strict:true}); + test.assertEquals(true, error_client.strict); + + error_client.open(function(err, error_client) { + error_client.collection('does-not-exist', function(err, collection) { + test.assertTrue(err instanceof Error); + test.assertEquals("Collection does-not-exist does not exist. Currently in strict mode.", err.message); + }); + + error_client.createCollection('test_strict_access_collection', function(err, collection) { + error_client.collection('test_strict_access_collection', function(err, collection) { + test.assertTrue(collection instanceof Collection); + // Let's close the db + error_client.close(); + }); + }); + }); +``` + +Documentation +============= + +If this document doesn't answer your questions, see the source of +[Collection](https://github.com/mongodb/node-mongodb-native/blob/master/lib/mongodb/collection.js) +or [Cursor](https://github.com/mongodb/node-mongodb-native/blob/master/lib/mongodb/cursor.js), +or the documentation at MongoDB for query and update formats. + +Find +---- + +The find method is actually a factory method to create +Cursor objects. A Cursor lazily uses the connection the first time +you call `nextObject`, `each`, or `toArray`. + +The basic operation on a cursor is the `nextObject` method +that fetches the next matching document from the database. The convenience +methods `each` and `toArray` call `nextObject` until the cursor is exhausted. + +Signatures: + +```javascript + var cursor = collection.find(query, [fields], options); + cursor.sort(fields).limit(n).skip(m). + + cursor.nextObject(function(err, doc) {}); + cursor.each(function(err, doc) {}); + cursor.toArray(function(err, docs) {}); + + cursor.rewind() // reset the cursor to its initial state. +``` + +Useful chainable methods of cursor. These can optionally be options of `find` instead of method calls: + +* `.limit(n).skip(m)` to control paging. +* `.sort(fields)` Order by the given fields. There are several equivalent syntaxes: + * `.sort({field1: -1, field2: 1})` descending by field1, then ascending by field2. + * `.sort([['field1', 'desc'], ['field2', 'asc']])` same as above + * `.sort([['field1', 'desc'], 'field2'])` same as above + * `.sort('field1')` ascending by field1 + +Other options of `find`: + +* `fields` the fields to fetch (to avoid transferring the entire document) +* `tailable` if true, makes the cursor [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors). +* `batchSize` The number of the subset of results to request the database +to return for every request. This should initially be greater than 1 otherwise +the database will automatically close the cursor. The batch size can be set to 1 +with `batchSize(n, function(err){})` after performing the initial query to the database. +* `hint` See [Optimization: hint](http://www.mongodb.org/display/DOCS/Optimization#Optimization-Hint). +* `explain` turns this into an explain query. You can also call +`explain()` on any cursor to fetch the explanation. +* `snapshot` prevents documents that are updated while the query is active +from being returned multiple times. See more +[details about query snapshots](http://www.mongodb.org/display/DOCS/How+to+do+Snapshotted+Queries+in+the+Mongo+Database). +* `timeout` if false, asks MongoDb not to time out this cursor after an +inactivity period. + + +For information on how to create queries, see the +[MongoDB section on querying](http://www.mongodb.org/display/DOCS/Querying). + +```javascript + var mongodb = require('mongodb'); + var server = new mongodb.Server("127.0.0.1", 27017, {}); + new mongodb.Db('test', server, {}).open(function (error, client) { + if (error) throw error; + var collection = new mongodb.Collection(client, 'test_collection'); + collection.find({}, {limit:10}).toArray(function(err, docs) { + console.dir(docs); + }); + }); +``` + +Insert +------ + +Signature: + +```javascript + collection.insert(docs, options, [callback]); +``` + +where `docs` can be a single document or an array of documents. + +Useful options: + +* `safe:true` Should always set if you have a callback. + +See also: [MongoDB docs for insert](http://www.mongodb.org/display/DOCS/Inserting). + +```javascript + var mongodb = require('mongodb'); + var server = new mongodb.Server("127.0.0.1", 27017, {}); + new mongodb.Db('test', server, {w: 1}).open(function (error, client) { + if (error) throw error; + var collection = new mongodb.Collection(client, 'test_collection'); + collection.insert({hello: 'world'}, {safe:true}, + function(err, objects) { + if (err) console.warn(err.message); + if (err && err.message.indexOf('E11000 ') !== -1) { + // this _id was already inserted in the database + } + }); + }); +``` + +Note that there's no reason to pass a callback to the insert or update commands +unless you use the `safe:true` option. If you don't specify `safe:true`, then +your callback will be called immediately. + +Update; update and insert (upsert) +---------------------------------- + +The update operation will update the first document that matches your query +(or all documents that match if you use `multi:true`). +If `safe:true`, `upsert` is not set, and no documents match, your callback will return 0 documents updated. + +See the [MongoDB docs](http://www.mongodb.org/display/DOCS/Updating) for +the modifier (`$inc`, `$set`, `$push`, etc.) formats. + +Signature: + +```javascript + collection.update(criteria, objNew, options, [callback]); +``` + +Useful options: + +* `safe:true` Should always set if you have a callback. +* `multi:true` If set, all matching documents are updated, not just the first. +* `upsert:true` Atomically inserts the document if no documents matched. + +Example for `update`: + +```javascript + var mongodb = require('mongodb'); + var server = new mongodb.Server("127.0.0.1", 27017, {}); + new mongodb.Db('test', server, {w: 1}).open(function (error, client) { + if (error) throw error; + var collection = new mongodb.Collection(client, 'test_collection'); + collection.update({hi: 'here'}, {$set: {hi: 'there'}}, {safe:true}, + function(err) { + if (err) console.warn(err.message); + else console.log('successfully updated'); + }); + }); +``` + +Find and modify +--------------- + +`findAndModify` is like `update`, but it also gives the updated document to +your callback. But there are a few key differences between findAndModify and +update: + + 1. The signatures differ. + 2. You can only findAndModify a single item, not multiple items. + +Signature: + +```javascript + collection.findAndModify(query, sort, update, options, callback) +``` + +The sort parameter is used to specify which object to operate on, if more than +one document matches. It takes the same format as the cursor sort (see +Connection.find above). + +See the +[MongoDB docs for findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) +for more details. + +Useful options: + +* `remove:true` set to a true to remove the object before returning +* `new:true` set to true if you want to return the modified object rather than the original. Ignored for remove. +* `upsert:true` Atomically inserts the document if no documents matched. + +Example for `findAndModify`: + +```javascript + var mongodb = require('mongodb'); + var server = new mongodb.Server("127.0.0.1", 27017, {}); + new mongodb.Db('test', server, {w: 1}).open(function (error, client) { + if (error) throw error; + var collection = new mongodb.Collection(client, 'test_collection'); + collection.findAndModify({hello: 'world'}, [['_id','asc']], {$set: {hi: 'there'}}, {}, + function(err, object) { + if (err) console.warn(err.message); + else console.dir(object); // undefined if no matching object exists. + }); + }); +``` + +Save +---- + +The `save` method is a shorthand for upsert if the document contains an +`_id`, or an insert if there is no `_id`. + +Sponsors +======== +Just as Felix Geisendörfer I'm also working on the driver for my own startup and this driver is a big project that also benefits other companies who are using MongoDB. + +If your company could benefit from a even better-engineered node.js mongodb driver I would appreciate any type of sponsorship you may be able to provide. All the sponsors will get a lifetime display in this readme, priority support and help on problems and votes on the roadmap decisions for the driver. If you are interested contact me on [christkv AT g m a i l.com](mailto:christkv@gmail.com) for details. + +And I'm very thankful for code contributions. If you are interested in working on features please contact me so we can discuss API design and testing. + +Release Notes +============= + +See HISTORY + +Credits +======= + +1. [10gen](http://github.com/mongodb/mongo-ruby-driver/) +2. [Google Closure Library](http://code.google.com/closure/library/) +3. [Jonas Raoni Soares Silva](http://jsfromhell.com/classes/binary-parser) + +Contributors +============ + +Aaron Heckmann, Christoph Pojer, Pau Ramon Revilla, Nathan White, Emmerman, Seth LaForge, Boris Filipov, Stefan Schärmeli, Tedde Lundgren, renctan, Sergey Ukustov, Ciaran Jessup, kuno, srimonti, Erik Abele, Pratik Daga, Slobodan Utvic, Kristina Chodorow, Yonathan Randolph, Brian Noguchi, Sam Epstein, James Harrison Fisher, Vladimir Dronnikov, Ben Hockey, Henrik Johansson, Simon Weare, Alex Gorbatchev, Shimon Doodkin, Kyle Mueller, Eran Hammer-Lahav, Marcin Ciszak, François de Metz, Vinay Pulim, nstielau, Adam Wiggins, entrinzikyl, Jeremy Selier, Ian Millington, Public Keating, andrewjstone, Christopher Stott, Corey Jewett, brettkiefer, Rob Holland, Senmiao Liu, heroic, gitfy + +License +======= + + Copyright 2009 - 2012 Christian Amor Kvalheim. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/test/node_modules/mongoose/node_modules/mongodb/index.js b/test/node_modules/mongoose/node_modules/mongodb/index.js new file mode 100644 index 0000000..4f59e9d --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/mongodb'); diff --git a/test/node_modules/mongoose/node_modules/mongodb/install.js b/test/node_modules/mongoose/node_modules/mongodb/install.js new file mode 100644 index 0000000..f9f2a57 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/install.js @@ -0,0 +1,40 @@ +var spawn = require('child_process').spawn, + exec = require('child_process').exec; + +process.stdout.write("================================================================================\n"); +process.stdout.write("= =\n"); +process.stdout.write("= To install with C++ bson parser do =\n"); +process.stdout.write("= =\n"); +process.stdout.write("================================================================================\n"); + +// Check if we want to build the native code +var build_native = process.env['npm_package_config_native'] != null ? process.env['npm_package_config_native'] : 'false'; +build_native = build_native == 'true' ? true : false; +// If we are building the native bson extension ensure we use gmake if available +if(build_native) { + // Check if we need to use gmake + exec('which gmake', function(err, stdout, stderr) { + // Set up spawn command + var make = null; + // No gmake build using make + if(err != null) { + make = spawn('make', ['total']); + } else { + make = spawn('gmake', ['total']); + } + + // Execute spawn + make.stdout.on('data', function(data) { + process.stdout.write(data); + }) + + make.stderr.on('data', function(data) { + process.stdout.write(data); + }) + + make.on('exit', function(code) { + process.stdout.write('child process exited with code ' + code + "\n"); + }) + }); +} + diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/admin.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/admin.js new file mode 100644 index 0000000..2183cf9 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/admin.js @@ -0,0 +1,338 @@ +/*! + * Module dependencies. + */ +var Collection = require('./collection').Collection, + Cursor = require('./cursor').Cursor, + DbCommand = require('./commands/db_command').DbCommand; + +/** + * Allows the user to access the admin functionality of MongoDB + * + * @class Represents the Admin methods of MongoDB. + * @param {Object} db Current db instance we wish to perform Admin operations on. + * @return {Function} Constructor for Admin type. + */ +function Admin(db) { + if(!(this instanceof Admin)) return new Admin(db); + this.db = db; +}; + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from buildInfo or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.buildInfo = function(callback) { + this.serverInfo(callback); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from serverInfo or null if an error occured. + * @return {null} Returns no result + * @api private + */ +Admin.prototype.serverInfo = function(callback) { + this.db.executeDbAdminCommand({buildinfo:1}, function(err, doc) { + if(err != null) return callback(err, null); + return callback(null, doc.documents[0]); + }); +} + +/** + * Retrieve this db's server status. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from serverStatus or null if an error occured. + * @return {null} + * @api public + */ +Admin.prototype.serverStatus = function(callback) { + var self = this; + + this.db.executeDbAdminCommand({serverStatus: 1}, function(err, doc) { + if(err == null && doc.documents[0].ok === 1) { + callback(null, doc.documents[0]); + } else { + if(err) return callback(err, false); + return callback(self.db.wrap(doc.documents[0]), false); + } + }); +}; + +/** + * Retrieve the current profiling Level for MongoDB + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from profilingLevel or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.profilingLevel = function(callback) { + var self = this; + + this.db.executeDbAdminCommand({profile:-1}, function(err, doc) { + doc = doc.documents[0]; + + if(err == null && doc.ok === 1) { + var was = doc.was; + if(was == 0) return callback(null, "off"); + if(was == 1) return callback(null, "slow_only"); + if(was == 2) return callback(null, "all"); + return callback(new Error("Error: illegal profiling level value " + was), null); + } else { + err != null ? callback(err, null) : callback(new Error("Error with profile command"), null); + } + }); +}; + +/** + * Ping the MongoDB server and retrieve results + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from ping or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.ping = function(options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + + this.db.executeDbAdminCommand({ping: 1}, callback); +} + +/** + * Authenticate against MongoDB + * + * @param {String} username The user name for the authentication. + * @param {String} password The password for the authentication. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from authenticate or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.authenticate = function(username, password, callback) { + this.db.authenticate(username, password, {authdb: 'admin'}, function(err, doc) { + return callback(err, doc); + }) +} + +/** + * Logout current authenticated user + * + * @param {Object} [options] Optional parameters to the command. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from logout or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.logout = function(callback) { + this.db.logout({authdb: 'admin'}, function(err, doc) { + return callback(err, doc); + }) +} + +/** + * Add a user to the MongoDB server, if the user exists it will + * overwrite the current password + * + * Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} username The user name for the authentication. + * @param {String} password The password for the authentication. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from addUser or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.addUser = function(username, password, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + options.dbName = 'admin'; + // Add user + this.db.addUser(username, password, options, function(err, doc) { + return callback(err, doc); + }) +} + +/** + * Remove a user from the MongoDB server + * + * Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} username The user name for the authentication. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from removeUser or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.removeUser = function(username, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + options.dbName = 'admin'; + + this.db.removeUser(username, options, function(err, doc) { + return callback(err, doc); + }) +} + +/** + * Set the current profiling level of MongoDB + * + * @param {String} level The new profiling level (off, slow_only, all) + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from setProfilingLevel or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.setProfilingLevel = function(level, callback) { + var self = this; + var command = {}; + var profile = 0; + + if(level == "off") { + profile = 0; + } else if(level == "slow_only") { + profile = 1; + } else if(level == "all") { + profile = 2; + } else { + return callback(new Error("Error: illegal profiling level value " + level)); + } + + // Set up the profile number + command['profile'] = profile; + + this.db.executeDbAdminCommand(command, function(err, doc) { + doc = doc.documents[0]; + + if(err == null && doc.ok === 1) + return callback(null, level); + return err != null ? callback(err, null) : callback(new Error("Error with profile command"), null); + }); +}; + +/** + * Retrive the current profiling information for MongoDB + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from profilingInfo or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.profilingInfo = function(callback) { + try { + new Cursor(this.db, new Collection(this.db, DbCommand.SYSTEM_PROFILE_COLLECTION), {}, {}, {dbName: 'admin'}).toArray(function(err, items) { + return callback(err, items); + }); + } catch (err) { + return callback(err, null); + } +}; + +/** + * Execute a db command against the Admin database + * + * @param {Object} command A command object `{ping:1}`. + * @param {Object} [options] Optional parameters to the command. + * @param {Function} callback this will be called after executing this method. The command always return the whole result of the command as the second parameter. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.command = function(command, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Execute a command + this.db.executeDbAdminCommand(command, options, function(err, doc) { + // Ensure change before event loop executes + return callback != null ? callback(err, doc) : null; + }); +} + +/** + * Validate an existing collection + * + * @param {String} collectionName The name of the collection to validate. + * @param {Object} [options] Optional parameters to the command. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from validateCollection or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.validateCollection = function(collectionName, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + var self = this; + var command = {validate: collectionName}; + var keys = Object.keys(options); + + // Decorate command with extra options + for(var i = 0; i < keys.length; i++) { + if(options.hasOwnProperty(keys[i])) { + command[keys[i]] = options[keys[i]]; + } + } + + this.db.executeDbCommand(command, function(err, doc) { + if(err != null) return callback(err, null); + doc = doc.documents[0]; + + if(doc.ok === 0) + return callback(new Error("Error with validate command"), null); + if(doc.result != null && doc.result.constructor != String) + return callback(new Error("Error with validation data"), null); + if(doc.result != null && doc.result.match(/exception|corrupt/) != null) + return callback(new Error("Error: invalid collection " + collectionName), null); + if(doc.valid != null && !doc.valid) + return callback(new Error("Error: invalid collection " + collectionName), null); + + return callback(null, doc); + }); +}; + +/** + * List the available databases + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from listDatabases or null if an error occured. + * @return {null} Returns no result + * @api public + */ +Admin.prototype.listDatabases = function(callback) { + // Execute the listAllDatabases command + this.db.executeDbAdminCommand({listDatabases:1}, {}, function(err, doc) { + if(err != null) return callback(err, null); + return callback(null, doc.documents[0]); + }); +} + +/** + * Get ReplicaSet status + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from replSetGetStatus or null if an error occured. + * @return {null} + * @api public + */ +Admin.prototype.replSetGetStatus = function(callback) { + var self = this; + + this.db.executeDbAdminCommand({replSetGetStatus:1}, function(err, doc) { + if(err == null && doc.documents[0].ok === 1) + return callback(null, doc.documents[0]); + if(err) return callback(err, false); + return callback(self.db.wrap(doc.documents[0]), false); + }); +}; + +/** + * @ignore + */ +exports.Admin = Admin; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/collection.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/collection.js new file mode 100644 index 0000000..60c6030 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/collection.js @@ -0,0 +1,1726 @@ +/** + * Module dependencies. + * @ignore + */ +var InsertCommand = require('./commands/insert_command').InsertCommand + , QueryCommand = require('./commands/query_command').QueryCommand + , DeleteCommand = require('./commands/delete_command').DeleteCommand + , UpdateCommand = require('./commands/update_command').UpdateCommand + , DbCommand = require('./commands/db_command').DbCommand + , ObjectID = require('bson').ObjectID + , Code = require('bson').Code + , Cursor = require('./cursor').Cursor + , utils = require('./utils'); + +/** + * Precompiled regexes + * @ignore +**/ +const eErrorMessages = /No matching object found/; + +/** + * toString helper. + * @ignore + */ +var toString = Object.prototype.toString; + +/** + * Create a new Collection instance + * + * Options + * - **slaveOk** {Boolean, default:false}, Allow reads from secondaries. + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * - **raw** {Boolean, default:false}, perform all operations using raw bson objects. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * + * @class Represents a Collection + * @param {Object} db db instance. + * @param {String} collectionName collection name. + * @param {Object} [pkFactory] alternative primary key factory. + * @param {Object} [options] additional options for the collection. + * @return {Object} a collection instance. + */ +function Collection (db, collectionName, pkFactory, options) { + if(!(this instanceof Collection)) return new Collection(db, collectionName, pkFactory, options); + + checkCollectionName(collectionName); + + this.db = db; + this.collectionName = collectionName; + this.internalHint = null; + this.opts = options != null && ('object' === typeof options) ? options : {}; + this.slaveOk = options == null || options.slaveOk == null ? db.slaveOk : options.slaveOk; + this.serializeFunctions = options == null || options.serializeFunctions == null ? db.serializeFunctions : options.serializeFunctions; + this.raw = options == null || options.raw == null ? db.raw : options.raw; + + this.readPreference = options == null || options.readPreference == null ? db.serverConfig.readPreference : options.readPreference; + this.readPreference = this.readPreference == null ? 'primary' : this.readPreference; + + this.pkFactory = pkFactory == null + ? ObjectID + : pkFactory; + + var self = this; +} + +/** + * Inserts a single document or a an array of documents into MongoDB. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **continueOnError/keepGoing** {Boolean, default:false}, keep inserting documents even if one document has an error, *mongodb 1.9.1 >*. + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Array|Object} docs + * @param {Object} [options] optional options for insert command + * @param {Function} [callback] optional callback for the function, must be provided when using a writeconcern + * @return {null} + * @api public + */ +Collection.prototype.insert = function insert (docs, options, callback) { + if ('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + var self = this; + insertAll(self, Array.isArray(docs) ? docs : [docs], options, callback); + return this; +}; + +/** + * @ignore + */ +var checkCollectionName = function checkCollectionName (collectionName) { + if ('string' !== typeof collectionName) { + throw Error("collection name must be a String"); + } + + if (!collectionName || collectionName.indexOf('..') != -1) { + throw Error("collection names cannot be empty"); + } + + if (collectionName.indexOf('$') != -1 && + collectionName.match(/((^\$cmd)|(oplog\.\$main))/) == null) { + throw Error("collection names must not contain '$'"); + } + + if (collectionName.match(/^\.|\.$/) != null) { + throw Error("collection names must not start or end with '.'"); + } +}; + +/** + * Removes documents specified by `selector` from the db. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **single** {Boolean, default:false}, removes the first document found. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} [selector] optional select, no selector is equivalent to removing all documents. + * @param {Object} [options] additional options during remove. + * @param {Function} [callback] must be provided if you performing a remove with a writeconcern + * @return {null} + * @api public + */ +Collection.prototype.remove = function remove(selector, options, callback) { + if ('function' === typeof selector) { + callback = selector; + selector = options = {}; + } else if ('function' === typeof options) { + callback = options; + options = {}; + } + + // Ensure options + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + // Ensure we have at least an empty selector + selector = selector == null ? {} : selector; + // Set up flags for the command, if we have a single document remove + var flags = 0 | (options.single ? 1 : 0); + + // DbName + var dbName = options['dbName']; + // If no dbname defined use the db one + if(dbName == null) { + dbName = this.db.databaseName; + } + + // Create a delete command + var deleteCommand = new DeleteCommand( + this.db + , dbName + "." + this.collectionName + , selector + , flags); + + var self = this; + var errorOptions = _getWriteConcern(self, options, callback); + // Execute the command, do not add a callback as it's async + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + var commandOptions = {read:false}; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + // Set safe option + commandOptions['safe'] = true; + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection) + this.db._executeRemoveCommand(deleteCommand, commandOptions, function (err, error) { + error = error && error.documents; + if(!callback) return; + + if(err) { + callback(err); + } else if(error[0].err || error[0].errmsg) { + callback(self.db.wrap(error[0])); + } else { + callback(null, error[0].n); + } + }); + } else if(_hasWriteConcern(errorOptions) && callback == null) { + throw new Error("Cannot use a writeConcern without a provided callback"); + } else { + var result = this.db._executeRemoveCommand(deleteCommand); + // If no callback just return + if (!callback) return; + // If error return error + if (result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(); + } +}; + +/** + * Renames the collection. + * + * @param {String} newName the new name of the collection. + * @param {Function} callback the callback accepting the result + * @return {null} + * @api public + */ +Collection.prototype.rename = function rename (newName, callback) { + var self = this; + // Ensure the new name is valid + checkCollectionName(newName); + // Execute the command, return the new renamed collection if successful + self.db._executeQueryCommand(DbCommand.createRenameCollectionCommand(self.db, self.collectionName, newName), function(err, result) { + if(err == null && result.documents[0].ok == 1) { + if(callback != null) { + // Set current object to point to the new name + self.collectionName = newName; + // Return the current collection + callback(null, self); + } + } else if(result.documents[0].errmsg != null) { + if(callback != null) { + err != null ? callback(err, null) : callback(self.db.wrap(result.documents[0]), null); + } + } + }); +}; + +/** + * @ignore + */ +var insertAll = function insertAll (self, docs, options, callback) { + if('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + + // Insert options (flags for insert) + var insertFlags = {}; + // If we have a mongodb version >= 1.9.1 support keepGoing attribute + if(options['keepGoing'] != null) { + insertFlags['keepGoing'] = options['keepGoing']; + } + + // If we have a mongodb version >= 1.9.1 support keepGoing attribute + if(options['continueOnError'] != null) { + insertFlags['continueOnError'] = options['continueOnError']; + } + + // DbName + var dbName = options['dbName']; + // If no dbname defined use the db one + if(dbName == null) { + dbName = self.db.databaseName; + } + + // Either use override on the function, or go back to default on either the collection + // level or db + if(options['serializeFunctions'] != null) { + insertFlags['serializeFunctions'] = options['serializeFunctions']; + } else { + insertFlags['serializeFunctions'] = self.serializeFunctions; + } + + // Pass in options + var insertCommand = new InsertCommand( + self.db + , dbName + "." + self.collectionName, true, insertFlags); + + // Add the documents and decorate them with id's if they have none + for(var index = 0, len = docs.length; index < len; ++index) { + var doc = docs[index]; + + // Add id to each document if it's not already defined + if (!(Buffer.isBuffer(doc)) && doc['_id'] == null && self.db.forceServerObjectId != true) { + doc['_id'] = self.pkFactory.createPk(); + } + + insertCommand.add(doc); + } + + // Collect errorOptions + var errorOptions = _getWriteConcern(self, options, callback); + // Default command options + var commandOptions = {}; + // If safe is defined check for error message + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + commandOptions['read'] = false; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + + // Set safe option + commandOptions['safe'] = errorOptions; + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection) + self.db._executeInsertCommand(insertCommand, commandOptions, function (err, error) { + error = error && error.documents; + if(!callback) return; + + if (err) { + callback(err); + } else if(error[0].err || error[0].errmsg) { + callback(self.db.wrap(error[0])); + } else { + callback(null, docs); + } + }); + } else if(_hasWriteConcern(errorOptions) && callback == null) { + throw new Error("Cannot use a writeConcern without a provided callback"); + } else { + // Execute the call without a write concern + var result = self.db._executeInsertCommand(insertCommand, commandOptions); + // If no callback just return + if(!callback) return; + // If error return error + if(result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(null, docs); + } +}; + +/** + * Save a document. Simple full document replacement function. Not recommended for efficiency, use atomic + * operators and update instead for more efficient operations. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} [doc] the document to save + * @param {Object} [options] additional options during remove. + * @param {Function} [callback] must be provided if you performing a safe save + * @return {null} + * @api public + */ +Collection.prototype.save = function save(doc, options, callback) { + if('function' === typeof options) callback = options, options = null; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + // Extract the id, if we have one we need to do a update command + var id = doc['_id']; + var commandOptions = _getWriteConcern(this, options, callback); + + if(id) { + commandOptions.upsert = true; + this.update({ _id: id }, doc, commandOptions, callback); + } else { + this.insert(doc, commandOptions, callback && function (err, docs) { + if (err) return callback(err, null); + + if (Array.isArray(docs)) { + callback(err, docs[0]); + } else { + callback(err, docs); + } + }); + } +}; + +/** + * Updates documents. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **upsert** {Boolean, default:false}, perform an upsert operation. + * - **multi** {Boolean, default:false}, update all documents matching the selector. + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} selector the query to select the document/documents to be updated + * @param {Object} document the fields/vals to be updated, or in the case of an upsert operation, inserted. + * @param {Object} [options] additional options during update. + * @param {Function} [callback] must be provided if you performing an update with a writeconcern + * @return {null} + * @api public + */ +Collection.prototype.update = function update(selector, document, options, callback) { + if('function' === typeof options) callback = options, options = null; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + + // DbName + var dbName = options['dbName']; + // If no dbname defined use the db one + if(dbName == null) { + dbName = this.db.databaseName; + } + + // Either use override on the function, or go back to default on either the collection + // level or db + if(options['serializeFunctions'] != null) { + options['serializeFunctions'] = options['serializeFunctions']; + } else { + options['serializeFunctions'] = this.serializeFunctions; + } + + var updateCommand = new UpdateCommand( + this.db + , dbName + "." + this.collectionName + , selector + , document + , options); + + var self = this; + // Unpack the error options if any + var errorOptions = _getWriteConcern(this, options, callback); + // If safe is defined check for error message + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + var commandOptions = {read:false}; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + // Set safe option + commandOptions['safe'] = errorOptions; + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + // Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection) + this.db._executeUpdateCommand(updateCommand, commandOptions, function (err, error) { + error = error && error.documents; + if(!callback) return; + + if(err) { + callback(err); + } else if(error[0].err || error[0].errmsg) { + callback(self.db.wrap(error[0])); + } else { + // Perform the callback + callback(null, error[0].n, error[0]); + } + }); + } else if(_hasWriteConcern(errorOptions) && callback == null) { + throw new Error("Cannot use a writeConcern without a provided callback"); + } else { + // Execute update + var result = this.db._executeUpdateCommand(updateCommand); + // If no callback just return + if (!callback) return; + // If error return error + if (result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(); + } +}; + +/** + * The distinct command returns returns a list of distinct values for the given key across a collection. + * + * Options + * - **readPreference** {String}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {String} key key to run distinct against. + * @param {Object} [query] option query to narrow the returned objects. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from distinct or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.distinct = function distinct(key, query, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + query = args.length ? args.shift() : {}; + options = args.length ? args.shift() : {}; + + var mapCommandHash = { + 'distinct': this.collectionName + , 'query': query + , 'key': key + }; + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + // Create the command + var cmd = DbCommand.createDbSlaveOkCommand(this.db, mapCommandHash); + + this.db._executeQueryCommand(cmd, {read:readPreference}, function (err, result) { + if(err) + return callback(err); + if(result.documents[0].ok != 1) + return callback(new Error(result.documents[0].errmsg)); + callback(null, result.documents[0].values); + }); +}; + +/** + * Count number of matching documents in the db to a query. + * + * Options + * - **readPreference** {String}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Object} [query] query to filter by before performing count. + * @param {Object} [options] additional options during count. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the count method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.count = function count (query, options, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + query = args.length ? args.shift() : {}; + options = args.length ? args.shift() : {}; + + // Final query + var final_query = { + 'count': this.collectionName + , 'query': query + , 'fields': null + }; + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + + // Set up query options + var queryOptions = QueryCommand.OPTS_NO_CURSOR_TIMEOUT; + if (this.slaveOk || this.db.slaveOk) { + queryOptions |= QueryCommand.OPTS_SLAVE; + } + + var queryCommand = new QueryCommand( + this.db + , this.db.databaseName + ".$cmd" + , queryOptions + , 0 + , -1 + , final_query + , null + ); + + var self = this; + this.db._executeQueryCommand(queryCommand, {read:readPreference}, function (err, result) { + result = result && result.documents; + if(!callback) return; + + if(err) return callback(err); + if (result[0].ok != 1 || result[0].errmsg) return callback(self.db.wrap(result[0])); + callback(null, result[0].n); + }); +}; + + +/** + * Drop the collection + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the drop method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.drop = function drop(callback) { + this.db.dropCollection(this.collectionName, callback); +}; + +/** + * Find and update a document. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **remove** {Boolean, default:false}, set to true to remove the object before returning. + * - **upsert** {Boolean, default:false}, perform an upsert operation. + * - **new** {Boolean, default:false}, set to true if you want to return the modified object rather than the original. Ignored for remove. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} query query object to locate the object to modify + * @param {Array} sort - if multiple docs match, choose the first one in the specified sort order as the object to manipulate + * @param {Object} doc - the fields/vals to be updated + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findAndModify method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.findAndModify = function findAndModify (query, sort, doc, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + sort = args.length ? args.shift() : []; + doc = args.length ? args.shift() : null; + options = args.length ? args.shift() : {}; + var self = this; + + var queryObject = { + 'findandmodify': this.collectionName + , 'query': query + , 'sort': utils.formattedOrderClause(sort) + }; + + queryObject.new = options.new ? 1 : 0; + queryObject.remove = options.remove ? 1 : 0; + queryObject.upsert = options.upsert ? 1 : 0; + + if (options.fields) { + queryObject.fields = options.fields; + } + + if (doc && !options.remove) { + queryObject.update = doc; + } + + // Either use override on the function, or go back to default on either the collection + // level or db + if(options['serializeFunctions'] != null) { + options['serializeFunctions'] = options['serializeFunctions']; + } else { + options['serializeFunctions'] = this.serializeFunctions; + } + + // Unpack the error options if any + var errorOptions = _getWriteConcern(this, options, callback); + + // If we have j, w or something else do the getLast Error path + if(errorOptions != null && typeof errorOptions == 'object') { + // Commands to send + var commands = []; + // Add the find and modify command + commands.push(DbCommand.createDbCommand(this.db, queryObject, options)); + // If we have safe defined we need to return both call results + var chainedCommands = errorOptions != null ? true : false; + // Add error command if we have one + if(chainedCommands) { + commands.push(DbCommand.createGetLastErrorCommand(errorOptions, this.db)); + } + + // Fire commands and + this.db._executeQueryCommand(commands, {read:false}, function(err, result) { + if(err != null) return callback(err); + result = result && result.documents; + + if(result[0].err != null) return callback(self.db.wrap(result[0]), null); + // Workaround due to 1.8.X returning an error on no matching object + // while 2.0.X does not not, making 2.0.X behaviour standard + if(result[0].errmsg != null && !result[0].errmsg.match(eErrorMessages)) + return callback(self.db.wrap(result[0]), null, result[0]); + return callback(null, result[0].value, result[0]); + }); + } else { + // Only run command and rely on getLastError command + var command = DbCommand.createDbCommand(this.db, queryObject, options) + // Execute command + this.db._executeQueryCommand(command, {read:false}, function(err, result) { + if(err != null) return callback(err); + result = result && result.documents; + if(result[0].errmsg != null && !result[0].errmsg.match(eErrorMessages)) + return callback(self.db.wrap(result[0]), null, result[0]); + // If we have an error return it + if(result[0].lastErrorObject && result[0].lastErrorObject.err != null) return callback(self.db.wrap(result[0].lastErrorObject), null); + return callback(null, result[0].value, result[0]); + }); + } +} + +/** + * Find and remove a document + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} query query object to locate the object to modify + * @param {Array} sort - if multiple docs match, choose the first one in the specified sort order as the object to manipulate + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findAndRemove method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.findAndRemove = function(query, sort, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + sort = args.length ? args.shift() : []; + options = args.length ? args.shift() : {}; + // Add the remove option + options['remove'] = true; + // Execute the callback + this.findAndModify(query, sort, null, options, callback); +} + +var testForFields = { + limit: 1, sort: 1, fields:1, skip: 1, hint: 1, explain: 1, snapshot: 1, timeout: 1, tailable: 1, tailableRetryInterval: 1 + , numberOfRetries: 1, awaitdata: 1, exhaust: 1, batchSize: 1, returnKey: 1, maxScan: 1, min: 1, max: 1, showDiskLoc: 1 + , comment: 1, raw: 1, readPreference: 1, numberOfRetries: 1, partial: 1, read: 1, dbName: 1 +}; + +/** + * Creates a cursor for a query that can be used to iterate over results from MongoDB + * + * Various argument possibilities + * - callback? + * - selector, callback?, + * - selector, fields, callback? + * - selector, options, callback? + * - selector, fields, options, callback? + * - selector, fields, skip, limit, callback? + * - selector, fields, skip, limit, timeout, callback? + * + * Options + * - **limit** {Number, default:0}, sets the limit of documents returned in the query. + * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc. + * - **fields** {Object}, the fields to return in the query. Object of fields to include or exclude (not both), {'a':1} + * - **skip** {Number, default:0}, set to skip N documents ahead in your query (useful for pagination). + * - **hint** {Object}, tell the query to use specific indexes in the query. Object of indexes to use, {'_id':1} + * - **explain** {Boolean, default:false}, explain the query instead of returning the data. + * - **snapshot** {Boolean, default:false}, snapshot query. + * - **timeout** {Boolean, default:false}, specify if the cursor can timeout. + * - **tailable** {Boolean, default:false}, specify if the cursor is tailable. + * - **tailableRetryInterval** {Number, default:100}, specify the miliseconds between getMores on tailable cursor. + * - **numberOfRetries** {Number, default:5}, specify the number of times to retry the tailable cursor. + * - **awaitdata** {Boolean, default:false} allow the cursor to wait for data, only applicable for tailable cursor. + * - **exhaust** {Boolean, default:false} have the server send all the documents at once as getMore packets, not recommended. + * - **batchSize** {Number, default:0}, set the batchSize for the getMoreCommand when iterating over the query results. + * - **returnKey** {Boolean, default:false}, only return the index key. + * - **maxScan** {Number}, Limit the number of items to scan. + * - **min** {Number}, Set index bounds. + * - **max** {Number}, Set index bounds. + * - **showDiskLoc** {Boolean, default:false}, Show disk location of results. + * - **comment** {String}, You can put a $comment field on a query to make looking in the profiler logs simpler. + * - **raw** {Boolean, default:false}, Return all BSON documents as Raw Buffer documents. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * - **numberOfRetries** {Number, default:5}, if using awaidata specifies the number of times to retry on timeout. + * - **partial** {Boolean, default:false}, specify if the cursor should return partial results when querying against a sharded system + * + * @param {Object} query query object to locate the object to modify + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the find method or null if an error occured. + * @return {Cursor} returns a cursor to the query + * @api public + */ +Collection.prototype.find = function find () { + var options + , args = Array.prototype.slice.call(arguments, 0) + , has_callback = typeof args[args.length - 1] === 'function' + , has_weird_callback = typeof args[0] === 'function' + , callback = has_callback ? args.pop() : (has_weird_callback ? args.shift() : null) + , len = args.length + , selector = len >= 1 ? args[0] : {} + , fields = len >= 2 ? args[1] : undefined; + + if(len === 1 && has_weird_callback) { + // backwards compat for callback?, options case + selector = {}; + options = args[0]; + } + + if(len === 2 && !Array.isArray(fields)) { + var fieldKeys = Object.getOwnPropertyNames(fields); + var is_option = false; + + for(var i = 0; i < fieldKeys.length; i++) { + if(testForFields[fieldKeys[i]] != null) { + is_option = true; + break; + } + } + + if(is_option) { + options = fields; + fields = undefined; + } else { + options = {}; + } + } else if(len === 2 && Array.isArray(fields) && !Array.isArray(fields[0])) { + var newFields = {}; + // Rewrite the array + for(var i = 0; i < fields.length; i++) { + newFields[fields[i]] = 1; + } + // Set the fields + fields = newFields; + } + + if(3 === len) { + options = args[2]; + } + + // Ensure selector is not null + selector = selector == null ? {} : selector; + // Validate correctness off the selector + var object = selector; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("query selector raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + // Validate correctness of the field selector + var object = fields; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("query fields raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + // Check special case where we are using an objectId + if(selector instanceof ObjectID) { + selector = {_id:selector}; + } + + // If it's a serialized fields field we need to just let it through + // user be warned it better be good + if(options && options.fields && !(Buffer.isBuffer(options.fields))) { + fields = {}; + + if(Array.isArray(options.fields)) { + if(!options.fields.length) { + fields['_id'] = 1; + } else { + for (var i = 0, l = options.fields.length; i < l; i++) { + fields[options.fields[i]] = 1; + } + } + } else { + fields = options.fields; + } + } + + if (!options) options = {}; + options.skip = len > 3 ? args[2] : options.skip ? options.skip : 0; + options.limit = len > 3 ? args[3] : options.limit ? options.limit : 0; + options.raw = options.raw != null && typeof options.raw === 'boolean' ? options.raw : this.raw; + options.hint = options.hint != null ? normalizeHintField(options.hint) : this.internalHint; + options.timeout = len == 5 ? args[4] : typeof options.timeout === 'undefined' ? undefined : options.timeout; + // If we have overridden slaveOk otherwise use the default db setting + options.slaveOk = options.slaveOk != null ? options.slaveOk : this.db.slaveOk; + + // Set option + var o = options; + // Support read/readPreference + if(o["read"] != null) o["readPreference"] = o["read"]; + // Set the read preference + o.read = o["readPreference"] ? o.readPreference : this.readPreference; + // Adjust slave ok if read preference is secondary or secondary only + if(o.read == "secondary" || o.read == "secondaryOnly") options.slaveOk = true; + + // callback for backward compatibility + if(callback) { + // TODO refactor Cursor args + callback(null, new Cursor(this.db, this, selector, fields, o)); + } else { + return new Cursor(this.db, this, selector, fields, o); + } +}; + +/** + * Normalizes a `hint` argument. + * + * @param {String|Object|Array} hint + * @return {Object} + * @api private + */ +var normalizeHintField = function normalizeHintField(hint) { + var finalHint = null; + + if (null != hint) { + switch (hint.constructor) { + case String: + finalHint = {}; + finalHint[hint] = 1; + break; + case Object: + finalHint = {}; + for (var name in hint) { + finalHint[name] = hint[name]; + } + break; + case Array: + finalHint = {}; + hint.forEach(function(param) { + finalHint[param] = 1; + }); + break; + } + } + + return finalHint; +}; + +/** + * Finds a single document based on the query + * + * Various argument possibilities + * - callback? + * - selector, callback?, + * - selector, fields, callback? + * - selector, options, callback? + * - selector, fields, options, callback? + * - selector, fields, skip, limit, callback? + * - selector, fields, skip, limit, timeout, callback? + * + * Options + * - **limit** {Number, default:0}, sets the limit of documents returned in the query. + * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc. + * - **fields** {Object}, the fields to return in the query. Object of fields to include or exclude (not both), {'a':1} + * - **skip** {Number, default:0}, set to skip N documents ahead in your query (useful for pagination). + * - **hint** {Object}, tell the query to use specific indexes in the query. Object of indexes to use, {'_id':1} + * - **explain** {Boolean, default:false}, explain the query instead of returning the data. + * - **snapshot** {Boolean, default:false}, snapshot query. + * - **timeout** {Boolean, default:false}, specify if the cursor can timeout. + * - **tailable** {Boolean, default:false}, specify if the cursor is tailable. + * - **batchSize** {Number, default:0}, set the batchSize for the getMoreCommand when iterating over the query results. + * - **returnKey** {Boolean, default:false}, only return the index key. + * - **maxScan** {Number}, Limit the number of items to scan. + * - **min** {Number}, Set index bounds. + * - **max** {Number}, Set index bounds. + * - **showDiskLoc** {Boolean, default:false}, Show disk location of results. + * - **comment** {String}, You can put a $comment field on a query to make looking in the profiler logs simpler. + * - **raw** {Boolean, default:false}, Return all BSON documents as Raw Buffer documents. + * - **readPreference** {String}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * - **partial** {Boolean, default:false}, specify if the cursor should return partial results when querying against a sharded system + * + * @param {Object} query query object to locate the object to modify + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the findOne method or null if an error occured. + * @return {Cursor} returns a cursor to the query + * @api public + */ +Collection.prototype.findOne = function findOne () { + var self = this; + var args = Array.prototype.slice.call(arguments, 0); + var callback = args.pop(); + var cursor = this.find.apply(this, args).limit(-1).batchSize(1); + // Return the item + cursor.nextObject(function(err, item) { + if(err != null) return callback(err instanceof Error ? err : self.db.wrap(err), null); + callback(null, item); + }); +}; + +/** + * Creates an index on the collection. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **unique** {Boolean, default:false}, creates an unique index. + * - **sparse** {Boolean, default:false}, creates a sparse index. + * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. + * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value + * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates. + * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates. + * - **v** {Number}, specify the format version of the indexes. + * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) + * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} fieldOrSpec fieldOrSpec that defines the index. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the createIndex method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.createIndex = function createIndex (fieldOrSpec, options, callback) { + // Clean up call + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + options = typeof callback === 'function' ? options : callback; + options = options == null ? {} : options; + + // Collect errorOptions + var errorOptions = _getWriteConcern(this, options, callback); + // Execute create index + this.db.createIndex(this.collectionName, fieldOrSpec, options, callback); +}; + +/** + * Ensures that an index exists, if it does not it creates it + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **unique** {Boolean, default:false}, creates an unique index. + * - **sparse** {Boolean, default:false}, creates a sparse index. + * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. + * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value + * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates. + * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates. + * - **v** {Number}, specify the format version of the indexes. + * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) + * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {Object} fieldOrSpec fieldOrSpec that defines the index. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the ensureIndex method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.ensureIndex = function ensureIndex (fieldOrSpec, options, callback) { + // Clean up call + if (typeof callback === 'undefined' && typeof options === 'function') { + callback = options; + options = {}; + } + + if (options == null) { + options = {}; + } + + // Execute create index + this.db.ensureIndex(this.collectionName, fieldOrSpec, options, callback); +}; + +/** + * Retrieves this collections index info. + * + * Options + * - **full** {Boolean, default:false}, returns the full raw index information. + * + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexInformation method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.indexInformation = function indexInformation (options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + // Call the index information + this.db.indexInformation(this.collectionName, options, callback); +}; + +/** + * Drops an index from this collection. + * + * @param {String} name + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropIndex method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.dropIndex = function dropIndex (name, callback) { + this.db.dropIndex(this.collectionName, name, callback); +}; + +/** + * Drops all indexes from this collection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropAllIndexes method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.dropAllIndexes = function dropIndexes (callback) { + this.db.dropIndex(this.collectionName, '*', function (err, result) { + if(err != null) { + callback(err, false); + } else if(result.documents[0].errmsg == null) { + callback(null, true); + } else { + callback(new Error(result.documents[0].errmsg), false); + } + }); +}; + +/** + * Drops all indexes from this collection. + * + * @deprecated + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the dropIndexes method or null if an error occured. + * @return {null} + * @api private + */ +Collection.prototype.dropIndexes = Collection.prototype.dropAllIndexes; + +/** + * Reindex all indexes on the collection + * Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the reIndex method or null if an error occured. + * @return {null} + * @api public +**/ +Collection.prototype.reIndex = function(callback) { + this.db.reIndex(this.collectionName, callback); +} + +/** + * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection. + * + * Options + * - **out** {Object, default:*{inline:1}*}, sets the output target for the map reduce job. *{inline:1} | {replace:'collectionName'} | {merge:'collectionName'} | {reduce:'collectionName'}* + * - **query** {Object}, query filter object. + * - **sort** {Object}, sorts the input objects using this key. Useful for optimization, like sorting by the emit key for fewer reduces. + * - **limit** {Number}, number of objects to return from collection. + * - **keeptemp** {Boolean, default:false}, keep temporary data. + * - **finalize** {Function | String}, finalize function. + * - **scope** {Object}, can pass in variables that can be access from map/reduce/finalize. + * - **jsMode** {Boolean, default:false}, it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X. + * - **verbose** {Boolean, default:false}, provide statistics on job execution time. + * - **readPreference** {String, only for inline results}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Function|String} map the mapping function. + * @param {Function|String} reduce the reduce function. + * @param {Objects} [options] options for the map reduce job. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the mapReduce method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.mapReduce = function mapReduce (map, reduce, options, callback) { + if ('function' === typeof options) callback = options, options = {}; + // Out must allways be defined (make sure we don't break weirdly on pre 1.8+ servers) + if(null == options.out) { + throw new Error("the out option parameter must be defined, see mongodb docs for possible values"); + } + + if ('function' === typeof map) { + map = map.toString(); + } + + if ('function' === typeof reduce) { + reduce = reduce.toString(); + } + + if ('function' === typeof options.finalize) { + options.finalize = options.finalize.toString(); + } + + var mapCommandHash = { + mapreduce: this.collectionName + , map: map + , reduce: reduce + }; + + // Add any other options passed in + for (var name in options) { + if ('scope' == name) { + mapCommandHash[name] = processScope(options[name]); + } else { + mapCommandHash[name] = options[name]; + } + } + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + // If we have a read preference and inline is not set as output fail hard + if(readPreference != false && options['out'] != 'inline') { + throw new Error("a readPreference can only be provided when performing an inline mapReduce"); + } + + // self + var self = this; + var cmd = DbCommand.createDbCommand(this.db, mapCommandHash); + + this.db._executeQueryCommand(cmd, {read:readPreference}, function (err, result) { + if (err) { + return callback(err); + } + + // + if (1 != result.documents[0].ok || result.documents[0].err || result.documents[0].errmsg) { + return callback(self.db.wrap(result.documents[0])); + } + + // Create statistics value + var stats = {}; + if(result.documents[0].timeMillis) stats['processtime'] = result.documents[0].timeMillis; + if(result.documents[0].counts) stats['counts'] = result.documents[0].counts; + if(result.documents[0].timing) stats['timing'] = result.documents[0].timing; + + // invoked with inline? + if(result.documents[0].results) { + return callback(null, result.documents[0].results, stats); + } + + // The returned collection + var collection = null; + + // If we have an object it's a different db + if(result.documents[0].result != null && typeof result.documents[0].result == 'object') { + var doc = result.documents[0].result; + collection = self.db.db(doc.db).collection(doc.collection); + } else { + // Create a collection object that wraps the result collection + collection = self.db.collection(result.documents[0].result) + } + + // If we wish for no verbosity + if(options['verbose'] == null || !options['verbose']) { + return callback(err, collection); + } + + // Return stats as third set of values + callback(err, collection, stats); + }); +}; + +/** + * Functions that are passed as scope args must + * be converted to Code instances. + * @ignore + */ +function processScope (scope) { + if (!utils.isObject(scope)) { + return scope; + } + + var keys = Object.keys(scope); + var i = keys.length; + var key; + + while (i--) { + key = keys[i]; + if ('function' == typeof scope[key]) { + scope[key] = new Code(String(scope[key])); + } + } + + return scope; +} + +/** + * Group function helper + * @ignore + */ +var groupFunction = function () { + var c = db[ns].find(condition); + var map = new Map(); + var reduce_function = reduce; + + while (c.hasNext()) { + var obj = c.next(); + var key = {}; + + for (var i = 0, len = keys.length; i < len; ++i) { + var k = keys[i]; + key[k] = obj[k]; + } + + var aggObj = map.get(key); + + if (aggObj == null) { + var newObj = Object.extend({}, key); + aggObj = Object.extend(newObj, initial); + map.put(key, aggObj); + } + + reduce_function(obj, aggObj); + } + + return { "result": map.values() }; +}.toString(); + +/** + * Run a group command across a collection + * + * Options + * - **readPreference** {String}, the preferred read preference (Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Object|Array|Function|Code} keys an object, array or function expressing the keys to group by. + * @param {Object} condition an optional condition that must be true for a row to be considered. + * @param {Object} initial initial value of the aggregation counter object. + * @param {Function|Code} reduce the reduce function aggregates (reduces) the objects iterated + * @param {Function|Code} finalize an optional function to be run on each item in the result set just before the item is returned. + * @param {Boolean} command specify if you wish to run using the internal group command or using eval, default is true. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the group method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.group = function group(keys, condition, initial, reduce, finalize, command, options, callback) { + var args = Array.prototype.slice.call(arguments, 3); + callback = args.pop(); + // Fetch all commands + reduce = args.length ? args.shift() : null; + finalize = args.length ? args.shift() : null; + command = args.length ? args.shift() : null; + options = args.length ? args.shift() : {}; + + // Make sure we are backward compatible + if(!(typeof finalize == 'function')) { + command = finalize; + finalize = null; + } + + if (!Array.isArray(keys) && keys instanceof Object && typeof(keys) !== 'function' && !(keys instanceof Code)) { + keys = Object.keys(keys); + } + + if(typeof reduce === 'function') { + reduce = reduce.toString(); + } + + if(typeof finalize === 'function') { + finalize = finalize.toString(); + } + + // Set up the command as default + command = command == null ? true : command; + + // Execute using the command + if(command) { + var reduceFunction = reduce instanceof Code + ? reduce + : new Code(reduce); + + var selector = { + group: { + 'ns': this.collectionName + , '$reduce': reduceFunction + , 'cond': condition + , 'initial': initial + , 'out': "inline" + } + }; + + // if finalize is defined + if(finalize != null) selector.group['finalize'] = finalize; + // Set up group selector + if ('function' === typeof keys || keys instanceof Code) { + selector.group.$keyf = keys instanceof Code + ? keys + : new Code(keys); + } else { + var hash = {}; + keys.forEach(function (key) { + hash[key] = 1; + }); + selector.group.key = hash; + } + + var cmd = DbCommand.createDbSlaveOkCommand(this.db, selector); + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + + this.db._executeQueryCommand(cmd, {read:readPreference}, function (err, result) { + if(err != null) return callback(err); + + var document = result.documents[0]; + if (null == document.retval) { + return callback(new Error("group command failed: " + document.errmsg)); + } + + callback(null, document.retval); + }); + + } else { + // Create execution scope + var scope = reduce != null && reduce instanceof Code + ? reduce.scope + : {}; + + scope.ns = this.collectionName; + scope.keys = keys; + scope.condition = condition; + scope.initial = initial; + + // Pass in the function text to execute within mongodb. + var groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';'); + + this.db.eval(new Code(groupfn, scope), function (err, results) { + if (err) return callback(err, null); + callback(null, results.result || results); + }); + } +}; + +/** + * Returns the options of the collection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the options method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.options = function options(callback) { + this.db.collectionsInfo(this.collectionName, function (err, cursor) { + if (err) return callback(err); + cursor.nextObject(function (err, document) { + callback(err, document && document.options || null); + }); + }); +}; + +/** + * Returns if the collection is a capped collection + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the isCapped method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.isCapped = function isCapped(callback) { + this.options(function(err, document) { + if(err != null) { + callback(err); + } else { + callback(null, document && document.capped); + } + }); +}; + +/** + * Checks if one or more indexes exist on the collection + * + * @param {String|Array} indexNames check if one or more indexes exist on the collection. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexExists method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.indexExists = function indexExists(indexes, callback) { + this.indexInformation(function(err, indexInformation) { + // If we have an error return + if(err != null) return callback(err, null); + // Let's check for the index names + if(Array.isArray(indexes)) { + for(var i = 0; i < indexes.length; i++) { + if(indexInformation[indexes[i]] == null) { + return callback(null, false); + } + } + + // All keys found return true + return callback(null, true); + } else { + return callback(null, indexInformation[indexes] != null); + } + }); +} + +/** + * Execute the geoNear command to search for items in the collection + * + * Options + * - **num** {Number}, max number of results to return. + * - **maxDistance** {Number}, include results up to maxDistance from the point. + * - **distanceMultiplier** {Number}, include a value to multiply the distances with allowing for range conversions. + * - **query** {Object}, filter the results by a query. + * - **spherical** {Boolean, default:false}, perform query using a spherical model. + * - **uniqueDocs** {Boolean, default:false}, the closest location in a document to the center of the search region will always be returned MongoDB > 2.X. + * - **includeLocs** {Boolean, default:false}, include the location data fields in the top level of the results MongoDB > 2.X. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order. + * @param {Number} y point to search on the y axis, ensure the indexes are ordered in the same order. + * @param {Objects} [options] options for the map reduce job. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the geoNear method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.geoNear = function geoNear(x, y, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + // Fetch all commands + options = args.length ? args.shift() : {}; + + // Build command object + var commandObject = { + geoNear:this.collectionName, + near: [x, y] + } + + // Decorate object if any with known properties + if(options['num'] != null) commandObject['num'] = options['num']; + if(options['maxDistance'] != null) commandObject['maxDistance'] = options['maxDistance']; + if(options['distanceMultiplier'] != null) commandObject['distanceMultiplier'] = options['distanceMultiplier']; + if(options['query'] != null) commandObject['query'] = options['query']; + if(options['spherical'] != null) commandObject['spherical'] = options['spherical']; + if(options['uniqueDocs'] != null) commandObject['uniqueDocs'] = options['uniqueDocs']; + if(options['includeLocs'] != null) commandObject['includeLocs'] = options['includeLocs']; + + // Execute the command + this.db.command(commandObject, options, callback); +} + +/** + * Execute a geo search using a geo haystack index on a collection. + * + * Options + * - **maxDistance** {Number}, include results up to maxDistance from the point. + * - **search** {Object}, filter the results by a query. + * - **limit** {Number}, max number of results to return. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Number} x point to search on the x axis, ensure the indexes are ordered in the same order. + * @param {Number} y point to search on the y axis, ensure the indexes are ordered in the same order. + * @param {Objects} [options] options for the map reduce job. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the geoHaystackSearch method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.geoHaystackSearch = function geoHaystackSearch(x, y, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + // Fetch all commands + options = args.length ? args.shift() : {}; + + // Build command object + var commandObject = { + geoSearch:this.collectionName, + near: [x, y] + } + + // Decorate object if any with known properties + if(options['maxDistance'] != null) commandObject['maxDistance'] = options['maxDistance']; + if(options['query'] != null) commandObject['search'] = options['query']; + if(options['search'] != null) commandObject['search'] = options['search']; + if(options['limit'] != null) commandObject['limit'] = options['limit']; + + // Execute the command + this.db.command(commandObject, options, callback); +} + +/** + * Retrieve all the indexes on the collection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the indexes method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.indexes = function indexes(callback) { + // Return all the index information + this.db.indexInformation(this.collectionName, {full:true}, callback); +} + +/** + * Execute an aggregation framework pipeline against the collection, needs MongoDB >= 2.1 + * + * Options + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Array} array containing all the aggregation framework commands for the execution. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the aggregate method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.aggregate = function(pipeline, options, callback) { + // * - **explain** {Boolean}, return the query plan for the aggregation pipeline instead of the results. 2.3, 2.4 + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + var self = this; + + // If we have any of the supported options in the options object + var opts = args[args.length - 1]; + options = opts.readPreference || opts.explain ? args.pop() : {} + + // Convert operations to an array + if(!Array.isArray(args[0])) { + pipeline = []; + // Push all the operations to the pipeline + for(var i = 0; i < args.length; i++) pipeline.push(args[i]); + } + + // Build the command + var command = { aggregate : this.collectionName, pipeline : pipeline}; + // Add all options + var keys = Object.keys(options); + // Add all options + for(var i = 0; i < keys.length; i++) { + command[keys[i]] = options[keys[i]]; + } + + // Execute the command + this.db.command(command, options, function(err, result) { + if(err) { + callback(err); + } else if(result['err'] || result['errmsg']) { + callback(self.db.wrap(result)); + } else if(typeof result == 'object' && result['serverPipeline']) { + callback(null, result); + } else { + callback(null, result.result); + } + }); +} + +/** + * Get all the collection statistics. + * + * Options + * - **scale** {Number}, divide the returned sizes by scale value. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Objects} [options] options for the stats command. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the stats method or null if an error occured. + * @return {null} + * @api public + */ +Collection.prototype.stats = function stats(options, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + // Fetch all commands + options = args.length ? args.shift() : {}; + + // Build command object + var commandObject = { + collStats:this.collectionName, + } + + // Check if we have the scale value + if(options['scale'] != null) commandObject['scale'] = options['scale']; + + // Execute the command + this.db.command(commandObject, options, callback); +} + +/** + * @ignore + */ +Object.defineProperty(Collection.prototype, "hint", { + enumerable: true + , get: function () { + return this.internalHint; + } + , set: function (v) { + this.internalHint = normalizeHintField(v); + } +}); + +/** + * @ignore + */ +var _hasWriteConcern = function(errorOptions) { + return errorOptions == true + || errorOptions.w > 0 + || errorOptions.w == 'majority' + || errorOptions.j == true + || errorOptions.journal == true + || errorOptions.fsync == true +} + +/** + * @ignore + */ +var _setWriteConcernHash = function(options) { + var finalOptions = {}; + if(options.w != null) finalOptions.w = options.w; + if(options.journal == true) finalOptions.j = options.journal; + if(options.j == true) finalOptions.j = options.j; + if(options.fsync == true) finalOptions.fsync = options.fsync; + if(options.wtimeout != null) finalOptions.wtimeout = options.wtimeout; + return finalOptions; +} + +/** + * @ignore + */ +var _getWriteConcern = function(self, options, callback) { + // Final options + var finalOptions = {w:1}; + // Local options verification + if(options.w != null || typeof options.j == 'boolean' || typeof options.journal == 'boolean' || typeof options.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(options); + } else if(typeof options.safe == "boolean") { + finalOptions = {w: (options.safe ? 1 : 0)}; + } else if(options.safe != null && typeof options.safe == 'object') { + finalOptions = _setWriteConcernHash(options.safe); + } else if(self.opts.w != null || typeof self.opts.j == 'boolean' || typeof self.opts.journal == 'boolean' || typeof self.opts.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.opts); + } else if(typeof self.opts.safe == "boolean") { + finalOptions = {w: (self.opts.safe ? 1 : 0)}; + } else if(self.db.safe.w != null || typeof self.db.safe.j == 'boolean' || typeof self.db.safe.journal == 'boolean' || typeof self.db.safe.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.db.safe); + } else if(self.db.options.w != null || typeof self.db.options.j == 'boolean' || typeof self.db.options.journal == 'boolean' || typeof self.db.options.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.db.options); + } else if(typeof self.db.safe == "boolean") { + finalOptions = {w: (self.db.safe ? 1 : 0)}; + } + + // Ensure we don't have an invalid combination of write concerns + if(finalOptions.w < 1 + && (finalOptions.journal == true || finalOptions.j == true || finalOptions.fsync == true)) throw new Error("No acknowlegement using w < 1 cannot be combined with journal:true or fsync:true"); + + // Return the options + return finalOptions; +} + +/** + * Expose. + */ +exports.Collection = Collection; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/base_command.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/base_command.js new file mode 100644 index 0000000..9558582 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/base_command.js @@ -0,0 +1,29 @@ +/** + Base object used for common functionality +**/ +var BaseCommand = exports.BaseCommand = function BaseCommand() { +}; + +var id = 1; +BaseCommand.prototype.getRequestId = function getRequestId() { + if (!this.requestId) this.requestId = id++; + return this.requestId; +}; + +BaseCommand.prototype.setMongosReadPreference = function setMongosReadPreference(readPreference, tags) {} + +BaseCommand.prototype.updateRequestId = function() { + this.requestId = id++; + return this.requestId; +}; + +// OpCodes +BaseCommand.OP_REPLY = 1; +BaseCommand.OP_MSG = 1000; +BaseCommand.OP_UPDATE = 2001; +BaseCommand.OP_INSERT = 2002; +BaseCommand.OP_GET_BY_OID = 2003; +BaseCommand.OP_QUERY = 2004; +BaseCommand.OP_GET_MORE = 2005; +BaseCommand.OP_DELETE = 2006; +BaseCommand.OP_KILL_CURSORS = 2007; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/db_command.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/db_command.js new file mode 100644 index 0000000..b1a798d --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/db_command.js @@ -0,0 +1,240 @@ +var QueryCommand = require('./query_command').QueryCommand, + InsertCommand = require('./insert_command').InsertCommand, + inherits = require('util').inherits, + utils = require('../utils'), + crypto = require('crypto'); + +/** + Db Command +**/ +var DbCommand = exports.DbCommand = function(dbInstance, collectionName, queryOptions, numberToSkip, numberToReturn, query, returnFieldSelector, options) { + QueryCommand.call(this); + this.collectionName = collectionName; + this.queryOptions = queryOptions; + this.numberToSkip = numberToSkip; + this.numberToReturn = numberToReturn; + this.query = query; + this.returnFieldSelector = returnFieldSelector; + this.db = dbInstance; + + // Make sure we don't get a null exception + options = options == null ? {} : options; + // Let us defined on a command basis if we want functions to be serialized or not + if(options['serializeFunctions'] != null && options['serializeFunctions']) { + this.serializeFunctions = true; + } +}; + +inherits(DbCommand, QueryCommand); + +// Constants +DbCommand.SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"; +DbCommand.SYSTEM_INDEX_COLLECTION = "system.indexes"; +DbCommand.SYSTEM_PROFILE_COLLECTION = "system.profile"; +DbCommand.SYSTEM_USER_COLLECTION = "system.users"; +DbCommand.SYSTEM_COMMAND_COLLECTION = "$cmd"; +DbCommand.SYSTEM_JS_COLLECTION = "system.js"; + +// New commands +DbCommand.NcreateIsMasterCommand = function(db, databaseName) { + return new DbCommand(db, databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'ismaster':1}, null); +}; + +// Provide constructors for different db commands +DbCommand.createIsMasterCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'ismaster':1}, null); +}; + +DbCommand.createCollectionInfoCommand = function(db, selector) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_NAMESPACE_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, 0, selector, null); +}; + +DbCommand.createGetNonceCommand = function(db, options) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'getnonce':1}, null); +}; + +DbCommand.createAuthenticationCommand = function(db, username, password, nonce, authdb) { + // Use node md5 generator + var md5 = crypto.createHash('md5'); + // Generate keys used for authentication + md5.update(username + ":mongo:" + password); + var hash_password = md5.digest('hex'); + // Final key + md5 = crypto.createHash('md5'); + md5.update(nonce + username + hash_password); + var key = md5.digest('hex'); + // Creat selector + var selector = {'authenticate':1, 'user':username, 'nonce':nonce, 'key':key}; + // Create db command + return new DbCommand(db, authdb + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NONE, 0, -1, selector, null); +}; + +DbCommand.createLogoutCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'logout':1}, null); +}; + +DbCommand.createCreateCollectionCommand = function(db, collectionName, options) { + var selector = {'create':collectionName}; + // Modify the options to ensure correct behaviour + for(var name in options) { + if(options[name] != null && options[name].constructor != Function) selector[name] = options[name]; + } + // Execute the command + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, selector, null); +}; + +DbCommand.createDropCollectionCommand = function(db, collectionName) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'drop':collectionName}, null); +}; + +DbCommand.createRenameCollectionCommand = function(db, fromCollectionName, toCollectionName) { + var renameCollection = db.databaseName + "." + fromCollectionName; + var toCollection = db.databaseName + "." + toCollectionName; + return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'renameCollection':renameCollection, 'to':toCollection}, null); +}; + +DbCommand.createGetLastErrorCommand = function(options, db) { + + if (typeof db === 'undefined') { + db = options; + options = {}; + } + // Final command + var command = {'getlasterror':1}; + // If we have an options Object let's merge in the fields (fsync/wtimeout/w) + if('object' === typeof options) { + for(var name in options) { + command[name] = options[name] + } + } + + // Special case for w == 1, remove the w + if(1 == command.w) { + delete command.w; + } + + // Execute command + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command, null); +}; + +DbCommand.createGetLastStatusCommand = DbCommand.createGetLastErrorCommand; + +DbCommand.createGetPreviousErrorsCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'getpreverror':1}, null); +}; + +DbCommand.createResetErrorHistoryCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'reseterror':1}, null); +}; + +DbCommand.createCreateIndexCommand = function(db, collectionName, fieldOrSpec, options) { + var fieldHash = {}; + var indexes = []; + var keys; + + // Get all the fields accordingly + if('string' == typeof fieldOrSpec) { + // 'type' + indexes.push(fieldOrSpec + '_' + 1); + fieldHash[fieldOrSpec] = 1; + + } else if(utils.isArray(fieldOrSpec)) { + + fieldOrSpec.forEach(function(f) { + if('string' == typeof f) { + // [{location:'2d'}, 'type'] + indexes.push(f + '_' + 1); + fieldHash[f] = 1; + } else if(utils.isArray(f)) { + // [['location', '2d'],['type', 1]] + indexes.push(f[0] + '_' + (f[1] || 1)); + fieldHash[f[0]] = f[1] || 1; + } else if(utils.isObject(f)) { + // [{location:'2d'}, {type:1}] + keys = Object.keys(f); + keys.forEach(function(k) { + indexes.push(k + '_' + f[k]); + fieldHash[k] = f[k]; + }); + } else { + // undefined (ignore) + } + }); + + } else if(utils.isObject(fieldOrSpec)) { + // {location:'2d', type:1} + keys = Object.keys(fieldOrSpec); + keys.forEach(function(key) { + indexes.push(key + '_' + fieldOrSpec[key]); + fieldHash[key] = fieldOrSpec[key]; + }); + } + + // Generate the index name + var indexName = typeof options.name == 'string' + ? options.name + : indexes.join("_"); + + var selector = { + 'ns': db.databaseName + "." + collectionName, + 'key': fieldHash, + 'name': indexName + } + + // Ensure we have a correct finalUnique + var finalUnique = options == null || 'object' === typeof options + ? false + : options; + + // Set up options + options = options == null || typeof options == 'boolean' + ? {} + : options; + + // Add all the options + var keys = Object.keys(options); + for(var i = 0; i < keys.length; i++) { + selector[keys[i]] = options[keys[i]]; + } + + if(selector['unique'] == null) + selector['unique'] = finalUnique; + + var name = db.databaseName + "." + DbCommand.SYSTEM_INDEX_COLLECTION; + var cmd = new InsertCommand(db, name, false); + return cmd.add(selector); +}; + +DbCommand.logoutCommand = function(db, command_hash, options) { + var dbName = options != null && options['authdb'] != null ? options['authdb'] : db.databaseName; + // Create logout command + return new DbCommand(db, dbName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null); +} + +DbCommand.createDropIndexCommand = function(db, collectionName, indexName) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'deleteIndexes':collectionName, 'index':indexName}, null); +}; + +DbCommand.createReIndexCommand = function(db, collectionName) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'reIndex':collectionName}, null); +}; + +DbCommand.createDropDatabaseCommand = function(db) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'dropDatabase':1}, null); +}; + +DbCommand.createDbCommand = function(db, command_hash, options) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null, options); +}; + +DbCommand.createAdminDbCommand = function(db, command_hash) { + return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null); +}; + +DbCommand.createAdminDbCommandSlaveOk = function(db, command_hash) { + return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT | QueryCommand.OPTS_SLAVE, 0, -1, command_hash, null); +}; + +DbCommand.createDbSlaveOkCommand = function(db, command_hash, options) { + return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT | QueryCommand.OPTS_SLAVE, 0, -1, command_hash, null, options); +}; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/delete_command.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/delete_command.js new file mode 100644 index 0000000..e6ae20a --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/delete_command.js @@ -0,0 +1,114 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits; + +/** + Insert Document Command +**/ +var DeleteCommand = exports.DeleteCommand = function(db, collectionName, selector, flags) { + BaseCommand.call(this); + + // Validate correctness off the selector + var object = selector; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("delete raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + this.flags = flags; + this.collectionName = collectionName; + this.selector = selector; + this.db = db; +}; + +inherits(DeleteCommand, BaseCommand); + +DeleteCommand.OP_DELETE = 2006; + +/* +struct { + MsgHeader header; // standard message header + int32 ZERO; // 0 - reserved for future use + cstring fullCollectionName; // "dbname.collectionname" + int32 ZERO; // 0 - reserved for future use + mongo.BSON selector; // query object. See below for details. +} +*/ +DeleteCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + this.db.bson.calculateObjectSize(this.selector, false, true) + (4 * 4); + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (DeleteCommand.OP_DELETE >> 24) & 0xff; + _command[_index + 2] = (DeleteCommand.OP_DELETE >> 16) & 0xff; + _command[_index + 1] = (DeleteCommand.OP_DELETE >> 8) & 0xff; + _command[_index] = DeleteCommand.OP_DELETE & 0xff; + // Adjust index + _index = _index + 4; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Write the flags + _command[_index + 3] = (this.flags >> 24) & 0xff; + _command[_index + 2] = (this.flags >> 16) & 0xff; + _command[_index + 1] = (this.flags >> 8) & 0xff; + _command[_index] = this.flags & 0xff; + // Adjust index + _index = _index + 4; + + // Document binary length + var documentLength = 0 + + // Serialize the selector + // If we are passing a raw buffer, do minimal validation + if(Buffer.isBuffer(this.selector)) { + documentLength = this.selector.length; + // Copy the data into the current buffer + this.selector.copy(_command, _index); + } else { + documentLength = this.db.bson.serializeWithBufferAndIndex(this.selector, this.checkKeys, _command, _index) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + return _command; +}; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/get_more_command.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/get_more_command.js new file mode 100644 index 0000000..d3aac02 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/get_more_command.js @@ -0,0 +1,83 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits, + binaryutils = require('../utils'); + +/** + Get More Document Command +**/ +var GetMoreCommand = exports.GetMoreCommand = function(db, collectionName, numberToReturn, cursorId) { + BaseCommand.call(this); + + this.collectionName = collectionName; + this.numberToReturn = numberToReturn; + this.cursorId = cursorId; + this.db = db; +}; + +inherits(GetMoreCommand, BaseCommand); + +GetMoreCommand.OP_GET_MORE = 2005; + +GetMoreCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 8 + (4 * 4); + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index++] = totalLengthOfCommand & 0xff; + _command[_index++] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index++] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index++] = (totalLengthOfCommand >> 24) & 0xff; + + // Write the request ID + _command[_index++] = this.requestId & 0xff; + _command[_index++] = (this.requestId >> 8) & 0xff; + _command[_index++] = (this.requestId >> 16) & 0xff; + _command[_index++] = (this.requestId >> 24) & 0xff; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Write the op_code for the command + _command[_index++] = GetMoreCommand.OP_GET_MORE & 0xff; + _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 8) & 0xff; + _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 16) & 0xff; + _command[_index++] = (GetMoreCommand.OP_GET_MORE >> 24) & 0xff; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Number of documents to return + _command[_index++] = this.numberToReturn & 0xff; + _command[_index++] = (this.numberToReturn >> 8) & 0xff; + _command[_index++] = (this.numberToReturn >> 16) & 0xff; + _command[_index++] = (this.numberToReturn >> 24) & 0xff; + + // Encode the cursor id + var low_bits = this.cursorId.getLowBits(); + // Encode low bits + _command[_index++] = low_bits & 0xff; + _command[_index++] = (low_bits >> 8) & 0xff; + _command[_index++] = (low_bits >> 16) & 0xff; + _command[_index++] = (low_bits >> 24) & 0xff; + + var high_bits = this.cursorId.getHighBits(); + // Encode high bits + _command[_index++] = high_bits & 0xff; + _command[_index++] = (high_bits >> 8) & 0xff; + _command[_index++] = (high_bits >> 16) & 0xff; + _command[_index++] = (high_bits >> 24) & 0xff; + // Return command + return _command; +}; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/insert_command.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/insert_command.js new file mode 100644 index 0000000..d6a2100 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/insert_command.js @@ -0,0 +1,147 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits; + +/** + Insert Document Command +**/ +var InsertCommand = exports.InsertCommand = function(db, collectionName, checkKeys, options) { + BaseCommand.call(this); + + this.collectionName = collectionName; + this.documents = []; + this.checkKeys = checkKeys == null ? true : checkKeys; + this.db = db; + this.flags = 0; + this.serializeFunctions = false; + + // Ensure valid options hash + options = options == null ? {} : options; + + // Check if we have keepGoing set -> set flag if it's the case + if(options['keepGoing'] != null && options['keepGoing']) { + // This will finish inserting all non-index violating documents even if it returns an error + this.flags = 1; + } + + // Check if we have keepGoing set -> set flag if it's the case + if(options['continueOnError'] != null && options['continueOnError']) { + // This will finish inserting all non-index violating documents even if it returns an error + this.flags = 1; + } + + // Let us defined on a command basis if we want functions to be serialized or not + if(options['serializeFunctions'] != null && options['serializeFunctions']) { + this.serializeFunctions = true; + } +}; + +inherits(InsertCommand, BaseCommand); + +// OpCodes +InsertCommand.OP_INSERT = 2002; + +InsertCommand.prototype.add = function(document) { + if(Buffer.isBuffer(document)) { + var object_size = document[0] | document[1] << 8 | document[2] << 16 | document[3] << 24; + if(object_size != document.length) { + var error = new Error("insert raw message size does not match message header size [" + document.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + this.documents.push(document); + return this; +}; + +/* +struct { + MsgHeader header; // standard message header + int32 ZERO; // 0 - reserved for future use + cstring fullCollectionName; // "dbname.collectionname" + BSON[] documents; // one or more documents to insert into the collection +} +*/ +InsertCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + (4 * 4); + // var docLength = 0 + for(var i = 0; i < this.documents.length; i++) { + if(Buffer.isBuffer(this.documents[i])) { + totalLengthOfCommand += this.documents[i].length; + } else { + // Calculate size of document + totalLengthOfCommand += this.db.bson.calculateObjectSize(this.documents[i], this.serializeFunctions, true); + } + } + + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (InsertCommand.OP_INSERT >> 24) & 0xff; + _command[_index + 2] = (InsertCommand.OP_INSERT >> 16) & 0xff; + _command[_index + 1] = (InsertCommand.OP_INSERT >> 8) & 0xff; + _command[_index] = InsertCommand.OP_INSERT & 0xff; + // Adjust index + _index = _index + 4; + // Write flags if any + _command[_index + 3] = (this.flags >> 24) & 0xff; + _command[_index + 2] = (this.flags >> 16) & 0xff; + _command[_index + 1] = (this.flags >> 8) & 0xff; + _command[_index] = this.flags & 0xff; + // Adjust index + _index = _index + 4; + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Write all the bson documents to the buffer at the index offset + for(var i = 0; i < this.documents.length; i++) { + // Document binary length + var documentLength = 0 + var object = this.documents[i]; + + // Serialize the selector + // If we are passing a raw buffer, do minimal validation + if(Buffer.isBuffer(object)) { + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + } else { + // Serialize the document straight to the buffer + documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + } + + return _command; +}; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js new file mode 100644 index 0000000..d8ccb0c --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/kill_cursor_command.js @@ -0,0 +1,98 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits, + binaryutils = require('../utils'); + +/** + Insert Document Command +**/ +var KillCursorCommand = exports.KillCursorCommand = function(db, cursorIds) { + BaseCommand.call(this); + + this.cursorIds = cursorIds; + this.db = db; +}; + +inherits(KillCursorCommand, BaseCommand); + +KillCursorCommand.OP_KILL_CURSORS = 2007; + +/* +struct { + MsgHeader header; // standard message header + int32 ZERO; // 0 - reserved for future use + int32 numberOfCursorIDs; // number of cursorIDs in message + int64[] cursorIDs; // array of cursorIDs to close +} +*/ +KillCursorCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + 4 + (4 * 4) + (this.cursorIds.length * 8); + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (KillCursorCommand.OP_KILL_CURSORS >> 24) & 0xff; + _command[_index + 2] = (KillCursorCommand.OP_KILL_CURSORS >> 16) & 0xff; + _command[_index + 1] = (KillCursorCommand.OP_KILL_CURSORS >> 8) & 0xff; + _command[_index] = KillCursorCommand.OP_KILL_CURSORS & 0xff; + // Adjust index + _index = _index + 4; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Number of cursors to kill + var numberOfCursors = this.cursorIds.length; + _command[_index + 3] = (numberOfCursors >> 24) & 0xff; + _command[_index + 2] = (numberOfCursors >> 16) & 0xff; + _command[_index + 1] = (numberOfCursors >> 8) & 0xff; + _command[_index] = numberOfCursors & 0xff; + // Adjust index + _index = _index + 4; + + // Encode all the cursors + for(var i = 0; i < this.cursorIds.length; i++) { + // Encode the cursor id + var low_bits = this.cursorIds[i].getLowBits(); + // Encode low bits + _command[_index + 3] = (low_bits >> 24) & 0xff; + _command[_index + 2] = (low_bits >> 16) & 0xff; + _command[_index + 1] = (low_bits >> 8) & 0xff; + _command[_index] = low_bits & 0xff; + // Adjust index + _index = _index + 4; + + var high_bits = this.cursorIds[i].getHighBits(); + // Encode high bits + _command[_index + 3] = (high_bits >> 24) & 0xff; + _command[_index + 2] = (high_bits >> 16) & 0xff; + _command[_index + 1] = (high_bits >> 8) & 0xff; + _command[_index] = high_bits & 0xff; + // Adjust index + _index = _index + 4; + } + + return _command; +}; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/query_command.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/query_command.js new file mode 100644 index 0000000..76829d9 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/query_command.js @@ -0,0 +1,261 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits; + +/** + Insert Document Command +**/ +var QueryCommand = exports.QueryCommand = function(db, collectionName, queryOptions, numberToSkip, numberToReturn, query, returnFieldSelector, options) { + BaseCommand.call(this); + + // Validate correctness off the selector + var object = query, + object_size; + if(Buffer.isBuffer(object)) { + object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("query selector raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + object = returnFieldSelector; + if(Buffer.isBuffer(object)) { + object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("query fields raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + // Make sure we don't get a null exception + options = options == null ? {} : options; + // Set up options + this.collectionName = collectionName; + this.queryOptions = queryOptions; + this.numberToSkip = numberToSkip; + this.numberToReturn = numberToReturn; + + // Ensure we have no null query + query = query == null ? {} : query; + // Wrap query in the $query parameter so we can add read preferences for mongos + this.query = query; + this.returnFieldSelector = returnFieldSelector; + this.db = db; + + // Let us defined on a command basis if we want functions to be serialized or not + if(options['serializeFunctions'] != null && options['serializeFunctions']) { + this.serializeFunctions = true; + } +}; + +inherits(QueryCommand, BaseCommand); + +QueryCommand.OP_QUERY = 2004; + +/* + * Adds the read prefrence to the current command + */ +QueryCommand.prototype.setMongosReadPreference = function(readPreference, tags) { + // If we have readPreference set to true set to secondary prefered + if(readPreference == true) { + readPreference = 'secondaryPreferred'; + } else if(readPreference == 'false') { + readPreference = 'primary'; + } + + // Force the slave ok flag to be set if we are not using primary read preference + if(readPreference != false && readPreference != 'primary') { + this.queryOptions |= QueryCommand.OPTS_SLAVE; + } + + // Backward compatibility, ensure $query only set on read preference so 1.8.X works + if((readPreference != null || tags != null) && this.query['$query'] == null) { + this.query = {'$query': this.query}; + } + + // If we have no readPreference set and no tags, check if the slaveOk bit is set + if(readPreference == null && tags == null) { + // If we have a slaveOk bit set the read preference for MongoS + if(this.queryOptions & QueryCommand.OPTS_SLAVE) { + this.query['$readPreference'] = {mode: 'secondary'} + } else { + this.query['$readPreference'] = {mode: 'primary'} + } + } + + // Build read preference object + if(typeof readPreference == 'object' && readPreference['_type'] == 'ReadPreference') { + this.query['$readPreference'] = readPreference.toObject(); + } else if(readPreference != null) { + // Add the read preference + this.query['$readPreference'] = {mode: readPreference}; + + // If we have tags let's add them + if(tags != null) { + this.query['$readPreference']['tags'] = tags; + } + } +} + +/* +struct { + MsgHeader header; // standard message header + int32 opts; // query options. See below for details. + cstring fullCollectionName; // "dbname.collectionname" + int32 numberToSkip; // number of documents to skip when returning results + int32 numberToReturn; // number of documents to return in the first OP_REPLY + BSON query ; // query object. See below for details. + [ BSON returnFieldSelector; ] // OPTIONAL : selector indicating the fields to return. See below for details. +} +*/ +QueryCommand.prototype.toBinary = function() { + // Total length of the command + var totalLengthOfCommand = 0; + // Calculate total length of the document + if(Buffer.isBuffer(this.query)) { + totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 4 + this.query.length + (4 * 4); + } else { + totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 4 + this.db.bson.calculateObjectSize(this.query, this.serializeFunctions, true) + (4 * 4); + } + + // Calculate extra fields size + if(this.returnFieldSelector != null && !(Buffer.isBuffer(this.returnFieldSelector))) { + if(Object.keys(this.returnFieldSelector).length > 0) { + totalLengthOfCommand += this.db.bson.calculateObjectSize(this.returnFieldSelector, this.serializeFunctions, true); + } + } else if(Buffer.isBuffer(this.returnFieldSelector)) { + totalLengthOfCommand += this.returnFieldSelector.length; + } + + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (QueryCommand.OP_QUERY >> 24) & 0xff; + _command[_index + 2] = (QueryCommand.OP_QUERY >> 16) & 0xff; + _command[_index + 1] = (QueryCommand.OP_QUERY >> 8) & 0xff; + _command[_index] = QueryCommand.OP_QUERY & 0xff; + // Adjust index + _index = _index + 4; + + // Write the query options + _command[_index + 3] = (this.queryOptions >> 24) & 0xff; + _command[_index + 2] = (this.queryOptions >> 16) & 0xff; + _command[_index + 1] = (this.queryOptions >> 8) & 0xff; + _command[_index] = this.queryOptions & 0xff; + // Adjust index + _index = _index + 4; + + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Write the number of documents to skip + _command[_index + 3] = (this.numberToSkip >> 24) & 0xff; + _command[_index + 2] = (this.numberToSkip >> 16) & 0xff; + _command[_index + 1] = (this.numberToSkip >> 8) & 0xff; + _command[_index] = this.numberToSkip & 0xff; + // Adjust index + _index = _index + 4; + + // Write the number of documents to return + _command[_index + 3] = (this.numberToReturn >> 24) & 0xff; + _command[_index + 2] = (this.numberToReturn >> 16) & 0xff; + _command[_index + 1] = (this.numberToReturn >> 8) & 0xff; + _command[_index] = this.numberToReturn & 0xff; + // Adjust index + _index = _index + 4; + + // Document binary length + var documentLength = 0 + var object = this.query; + + // Serialize the selector + if(Buffer.isBuffer(object)) { + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + } else { + // Serialize the document straight to the buffer + documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + + // Push field selector if available + if(this.returnFieldSelector != null && !(Buffer.isBuffer(this.returnFieldSelector))) { + if(Object.keys(this.returnFieldSelector).length > 0) { + var documentLength = this.db.bson.serializeWithBufferAndIndex(this.returnFieldSelector, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1; + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + } + } if(this.returnFieldSelector != null && Buffer.isBuffer(this.returnFieldSelector)) { + // Document binary length + var documentLength = 0 + var object = this.returnFieldSelector; + + // Serialize the selector + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + } + + // Return finished command + return _command; +}; + +// Constants +QueryCommand.OPTS_NONE = 0; +QueryCommand.OPTS_TAILABLE_CURSOR = 2; +QueryCommand.OPTS_SLAVE = 4; +QueryCommand.OPTS_OPLOG_REPLY = 8; +QueryCommand.OPTS_NO_CURSOR_TIMEOUT = 16; +QueryCommand.OPTS_AWAIT_DATA = 32; +QueryCommand.OPTS_EXHAUST = 64; +QueryCommand.OPTS_PARTIAL = 128; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/update_command.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/update_command.js new file mode 100644 index 0000000..9829dea --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/commands/update_command.js @@ -0,0 +1,174 @@ +var BaseCommand = require('./base_command').BaseCommand, + inherits = require('util').inherits; + +/** + Update Document Command +**/ +var UpdateCommand = exports.UpdateCommand = function(db, collectionName, spec, document, options) { + BaseCommand.call(this); + + var object = spec; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("update spec raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + var object = document; + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) { + var error = new Error("update document raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + error.name = 'MongoError'; + throw error; + } + } + + this.collectionName = collectionName; + this.spec = spec; + this.document = document; + this.db = db; + this.serializeFunctions = false; + + // Generate correct flags + var db_upsert = 0; + var db_multi_update = 0; + db_upsert = options != null && options['upsert'] != null ? (options['upsert'] == true ? 1 : 0) : db_upsert; + db_multi_update = options != null && options['multi'] != null ? (options['multi'] == true ? 1 : 0) : db_multi_update; + + // Flags + this.flags = parseInt(db_multi_update.toString() + db_upsert.toString(), 2); + // Let us defined on a command basis if we want functions to be serialized or not + if(options['serializeFunctions'] != null && options['serializeFunctions']) { + this.serializeFunctions = true; + } +}; + +inherits(UpdateCommand, BaseCommand); + +UpdateCommand.OP_UPDATE = 2001; + +/* +struct { + MsgHeader header; // standard message header + int32 ZERO; // 0 - reserved for future use + cstring fullCollectionName; // "dbname.collectionname" + int32 flags; // bit vector. see below + BSON spec; // the query to select the document + BSON document; // the document data to update with or insert +} +*/ +UpdateCommand.prototype.toBinary = function() { + // Calculate total length of the document + var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + this.db.bson.calculateObjectSize(this.spec, false, true) + + this.db.bson.calculateObjectSize(this.document, this.serializeFunctions, true) + (4 * 4); + + // Let's build the single pass buffer command + var _index = 0; + var _command = new Buffer(totalLengthOfCommand); + // Write the header information to the buffer + _command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff; + _command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff; + _command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff; + _command[_index] = totalLengthOfCommand & 0xff; + // Adjust index + _index = _index + 4; + // Write the request ID + _command[_index + 3] = (this.requestId >> 24) & 0xff; + _command[_index + 2] = (this.requestId >> 16) & 0xff; + _command[_index + 1] = (this.requestId >> 8) & 0xff; + _command[_index] = this.requestId & 0xff; + // Adjust index + _index = _index + 4; + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + // Write the op_code for the command + _command[_index + 3] = (UpdateCommand.OP_UPDATE >> 24) & 0xff; + _command[_index + 2] = (UpdateCommand.OP_UPDATE >> 16) & 0xff; + _command[_index + 1] = (UpdateCommand.OP_UPDATE >> 8) & 0xff; + _command[_index] = UpdateCommand.OP_UPDATE & 0xff; + // Adjust index + _index = _index + 4; + + // Write zero + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + _command[_index++] = 0; + + // Write the collection name to the command + _index = _index + _command.write(this.collectionName, _index, 'utf8') + 1; + _command[_index - 1] = 0; + + // Write the update flags + _command[_index + 3] = (this.flags >> 24) & 0xff; + _command[_index + 2] = (this.flags >> 16) & 0xff; + _command[_index + 1] = (this.flags >> 8) & 0xff; + _command[_index] = this.flags & 0xff; + // Adjust index + _index = _index + 4; + + // Document binary length + var documentLength = 0 + var object = this.spec; + + // Serialize the selector + // If we are passing a raw buffer, do minimal validation + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) throw new Error("raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + } else { + documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, false) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + + // Document binary length + var documentLength = 0 + var object = this.document; + + // Serialize the document + // If we are passing a raw buffer, do minimal validation + if(Buffer.isBuffer(object)) { + var object_size = object[0] | object[1] << 8 | object[2] << 16 | object[3] << 24; + if(object_size != object.length) throw new Error("raw message size does not match message header size [" + object.length + "] != [" + object_size + "]"); + documentLength = object.length; + // Copy the data into the current buffer + object.copy(_command, _index); + } else { + documentLength = this.db.bson.serializeWithBufferAndIndex(object, this.checkKeys, _command, _index, this.serializeFunctions) - _index + 1; + } + + // Write the length to the document + _command[_index + 3] = (documentLength >> 24) & 0xff; + _command[_index + 2] = (documentLength >> 16) & 0xff; + _command[_index + 1] = (documentLength >> 8) & 0xff; + _command[_index] = documentLength & 0xff; + // Update index in buffer + _index = _index + documentLength; + // Add terminating 0 for the object + _command[_index - 1] = 0; + + return _command; +}; + +// Constants +UpdateCommand.DB_UPSERT = 0; +UpdateCommand.DB_MULTI_UPDATE = 1; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js new file mode 100644 index 0000000..f0ff06f --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/base.js @@ -0,0 +1,50 @@ +var EventEmitter = require('events').EventEmitter + , inherits = require('util').inherits; + +var Base = function Base() { + EventEmitter.call(this); +} + +/** + * @ignore + */ +inherits(Base, EventEmitter); + +/** + * Fire all the errors + * @ignore + */ +Base.prototype.__executeAllCallbacksWithError = function(err) { + // Locate all the possible callbacks that need to return + for(var i = 0; i < this.dbInstances.length; i++) { + // Fetch the db Instance + var dbInstance = this.dbInstances[i]; + // Check all callbacks + var keys = Object.keys(dbInstance._callBackStore._notReplied); + // For each key check if it's a callback that needs to be returned + for(var j = 0; j < keys.length; j++) { + var info = dbInstance._callBackStore._notReplied[keys[j]]; + // Check if we have a chained command (findAndModify) + if(info && info['chained'] && Array.isArray(info['chained']) && info['chained'].length > 0) { + var chained = info['chained']; + // Only callback once and the last one is the right one + var finalCallback = chained.pop(); + // Emit only the last event + dbInstance._callBackStore.emit(finalCallback, err, null); + + // Put back the final callback to ensure we don't call all commands in the chain + chained.push(finalCallback); + + // Remove all chained callbacks + for(var i = 0; i < chained.length; i++) { + delete dbInstance._callBackStore._notReplied[chained[i]]; + } + } else { + dbInstance._callBackStore.emit(keys[j], err, null); + } + } + } +} + + +exports.Base = Base; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection.js new file mode 100644 index 0000000..f1c333a --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection.js @@ -0,0 +1,440 @@ +var utils = require('./connection_utils'), + inherits = require('util').inherits, + net = require('net'), + EventEmitter = require('events').EventEmitter, + inherits = require('util').inherits, + binaryutils = require('../utils'), + tls = require('tls'); + +var Connection = exports.Connection = function(id, socketOptions) { + // Set up event emitter + EventEmitter.call(this); + // Store all socket options + this.socketOptions = socketOptions ? socketOptions : {host:'localhost', port:27017, domainSocket:false}; + // Set keep alive default if not overriden + if(this.socketOptions.keepAlive == null && (process.platform !== "sunos" || process.platform !== "win32")) this.socketOptions.keepAlive = 100; + // Id for the connection + this.id = id; + // State of the connection + this.connected = false; + // Set if this is a domain socket + this.domainSocket = this.socketOptions.domainSocket; + + // + // Connection parsing state + // + this.maxBsonSize = socketOptions.maxBsonSize ? socketOptions.maxBsonSize : Connection.DEFAULT_MAX_BSON_SIZE; + // Contains the current message bytes + this.buffer = null; + // Contains the current message size + this.sizeOfMessage = 0; + // Contains the readIndex for the messaage + this.bytesRead = 0; + // Contains spill over bytes from additional messages + this.stubBuffer = 0; + + // Just keeps list of events we allow + this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[], end:[]}; + + // Just keeps list of events we allow + resetHandlers(this, false); +} + +// Set max bson size +Connection.DEFAULT_MAX_BSON_SIZE = 1024 * 1024 * 4; + +// Inherit event emitter so we can emit stuff wohoo +inherits(Connection, EventEmitter); + +Connection.prototype.start = function() { + // If we have a normal connection + if(this.socketOptions.ssl) { + // Create a new stream + this.connection = new net.Socket(); + // Set timeout allowing backward compatibility to timeout if no connectTimeoutMS is set + this.connection.setTimeout(this.socketOptions.connectTimeoutMS != null ? this.socketOptions.connectTimeoutMS : this.socketOptions.timeout); + // Work around for 0.4.X + if(process.version.indexOf("v0.4") == -1) this.connection.setNoDelay(this.socketOptions.noDelay); + // Set keep alive if defined + if(process.version.indexOf("v0.4") == -1) { + if(this.socketOptions.keepAlive > 0) { + this.connection.setKeepAlive(true, this.socketOptions.keepAlive); + } else { + this.connection.setKeepAlive(false); + } + } + + // Set up pair for tls with server, accept self-signed certificates as well + var pair = this.pair = tls.createSecurePair(false); + // Set up encrypted streams + this.pair.encrypted.pipe(this.connection); + this.connection.pipe(this.pair.encrypted); + + // Setup clearText stream + this.writeSteam = this.pair.cleartext; + // Add all handlers to the socket to manage it + this.pair.on("secure", connectHandler(this)); + this.pair.cleartext.on("data", createDataHandler(this)); + // Add handlers + this.connection.on("error", errorHandler(this)); + // this.connection.on("connect", connectHandler(this)); + this.connection.on("end", endHandler(this)); + this.connection.on("timeout", timeoutHandler(this)); + this.connection.on("drain", drainHandler(this)); + this.writeSteam.on("close", closeHandler(this)); + // Start socket + this.connection.connect(this.socketOptions.port, this.socketOptions.host); + if(this.logger != null && this.logger.doDebug){ + this.logger.debug("opened connection", this.socketOptions); + } + } else { + // Create new connection instance + if(this.domainSocket) { + this.connection = net.createConnection(this.socketOptions.host); + } else { + this.connection = net.createConnection(this.socketOptions.port, this.socketOptions.host); + } + if(this.logger != null && this.logger.doDebug){ + this.logger.debug("opened connection", this.socketOptions); + } + // Set options on the socket + this.connection.setTimeout(this.socketOptions.connectTimeoutMS != null ? this.socketOptions.connectTimeoutMS : this.socketOptions.timeout); + // Work around for 0.4.X + if(process.version.indexOf("v0.4") == -1) this.connection.setNoDelay(this.socketOptions.noDelay); + // Set keep alive if defined + if(process.version.indexOf("v0.4") == -1) { + if(this.socketOptions.keepAlive > 0) { + this.connection.setKeepAlive(true, this.socketOptions.keepAlive); + } else { + this.connection.setKeepAlive(false); + } + } + + // Set up write stream + this.writeSteam = this.connection; + // Add handlers + this.connection.on("error", errorHandler(this)); + // Add all handlers to the socket to manage it + this.connection.on("connect", connectHandler(this)); + // this.connection.on("end", endHandler(this)); + this.connection.on("data", createDataHandler(this)); + this.connection.on("timeout", timeoutHandler(this)); + this.connection.on("drain", drainHandler(this)); + this.connection.on("close", closeHandler(this)); + } +} + +// Check if the sockets are live +Connection.prototype.isConnected = function() { + return this.connected && !this.connection.destroyed && this.connection.writable && this.connection.readable; +} + +// Write the data out to the socket +Connection.prototype.write = function(command, callback) { + try { + // If we have a list off commands to be executed on the same socket + if(Array.isArray(command)) { + for(var i = 0; i < command.length; i++) { + var binaryCommand = command[i].toBinary() + if(!this.socketOptions['disableDriverBSONSizeCheck'] && binaryCommand.length > this.maxBsonSize) + return callback(new Error("Document exceeds maximal allowed bson size of " + this.maxBsonSize + " bytes")); + if(this.logger != null && this.logger.doDebug) + this.logger.debug("writing command to mongodb", {binary: binaryCommand, json: command[i]}); + + var r = this.writeSteam.write(binaryCommand); + } + } else { + var binaryCommand = command.toBinary() + if(!this.socketOptions['disableDriverBSONSizeCheck'] && binaryCommand.length > this.maxBsonSize) + return callback(new Error("Document exceeds maximal allowed bson size of " + this.maxBsonSize + " bytes")); + + if(this.logger != null && this.logger.doDebug) + this.logger.debug("writing command to mongodb", {binary: binaryCommand, json: command}); + + var r = this.writeSteam.write(binaryCommand); + } + } catch (err) { + if(typeof callback === 'function') callback(err); + } +} + +// Force the closure of the connection +Connection.prototype.close = function() { + // clear out all the listeners + resetHandlers(this, true); + // Add a dummy error listener to catch any weird last moment errors (and ignore them) + this.connection.on("error", function() {}) + // destroy connection + this.connection.destroy(); + if(this.logger != null && this.logger.doDebug){ + this.logger.debug("closed connection", this.connection); + } +} + +// Reset all handlers +var resetHandlers = function(self, clearListeners) { + self.eventHandlers = {error:[], connect:[], close:[], end:[], timeout:[], parseError:[], message:[]}; + + // If we want to clear all the listeners + if(clearListeners && self.connection != null) { + var keys = Object.keys(self.eventHandlers); + // Remove all listeners + for(var i = 0; i < keys.length; i++) { + self.connection.removeAllListeners(keys[i]); + } + } +} + +// +// Handlers +// + +// Connect handler +var connectHandler = function(self) { + return function() { + // Set connected + self.connected = true; + // Now that we are connected set the socket timeout + self.connection.setTimeout(self.socketOptions.socketTimeoutMS != null ? self.socketOptions.socketTimeoutMS : self.socketOptions.timeout); + // Emit the connect event with no error + self.emit("connect", null, self); + } +} + +var createDataHandler = exports.Connection.createDataHandler = function(self) { + // We need to handle the parsing of the data + // and emit the messages when there is a complete one + return function(data) { + // Parse until we are done with the data + while(data.length > 0) { + // If we still have bytes to read on the current message + if(self.bytesRead > 0 && self.sizeOfMessage > 0) { + // Calculate the amount of remaining bytes + var remainingBytesToRead = self.sizeOfMessage - self.bytesRead; + // Check if the current chunk contains the rest of the message + if(remainingBytesToRead > data.length) { + // Copy the new data into the exiting buffer (should have been allocated when we know the message size) + data.copy(self.buffer, self.bytesRead); + // Adjust the number of bytes read so it point to the correct index in the buffer + self.bytesRead = self.bytesRead + data.length; + + // Reset state of buffer + data = new Buffer(0); + } else { + // Copy the missing part of the data into our current buffer + data.copy(self.buffer, self.bytesRead, 0, remainingBytesToRead); + // Slice the overflow into a new buffer that we will then re-parse + data = data.slice(remainingBytesToRead); + + // Emit current complete message + try { + var emitBuffer = self.buffer; + // Reset state of buffer + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + // Emit the buffer + self.emit("message", emitBuffer, self); + } catch(err) { + var errorObject = {err:"socketHandler", trace:err, bin:buffer, parseState:{ + sizeOfMessage:self.sizeOfMessage, + bytesRead:self.bytesRead, + stubBuffer:self.stubBuffer}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + } + } + } else { + // Stub buffer is kept in case we don't get enough bytes to determine the + // size of the message (< 4 bytes) + if(self.stubBuffer != null && self.stubBuffer.length > 0) { + + // If we have enough bytes to determine the message size let's do it + if(self.stubBuffer.length + data.length > 4) { + // Prepad the data + var newData = new Buffer(self.stubBuffer.length + data.length); + self.stubBuffer.copy(newData, 0); + data.copy(newData, self.stubBuffer.length); + // Reassign for parsing + data = newData; + + // Reset state of buffer + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + + } else { + + // Add the the bytes to the stub buffer + var newStubBuffer = new Buffer(self.stubBuffer.length + data.length); + // Copy existing stub buffer + self.stubBuffer.copy(newStubBuffer, 0); + // Copy missing part of the data + data.copy(newStubBuffer, self.stubBuffer.length); + // Exit parsing loop + data = new Buffer(0); + } + } else { + if(data.length > 4) { + // Retrieve the message size + var sizeOfMessage = binaryutils.decodeUInt32(data, 0); + // If we have a negative sizeOfMessage emit error and return + if(sizeOfMessage < 0 || sizeOfMessage > self.maxBsonSize) { + var errorObject = {err:"socketHandler", trace:'', bin:self.buffer, parseState:{ + sizeOfMessage: sizeOfMessage, + bytesRead: self.bytesRead, + stubBuffer: self.stubBuffer}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + return; + } + + // Ensure that the size of message is larger than 0 and less than the max allowed + if(sizeOfMessage > 4 && sizeOfMessage < self.maxBsonSize && sizeOfMessage > data.length) { + self.buffer = new Buffer(sizeOfMessage); + // Copy all the data into the buffer + data.copy(self.buffer, 0); + // Update bytes read + self.bytesRead = data.length; + // Update sizeOfMessage + self.sizeOfMessage = sizeOfMessage; + // Ensure stub buffer is null + self.stubBuffer = null; + // Exit parsing loop + data = new Buffer(0); + + } else if(sizeOfMessage > 4 && sizeOfMessage < self.maxBsonSize && sizeOfMessage == data.length) { + try { + var emitBuffer = data; + // Reset state of buffer + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + // Exit parsing loop + data = new Buffer(0); + // Emit the message + self.emit("message", emitBuffer, self); + } catch (err) { + var errorObject = {err:"socketHandler", trace:err, bin:self.buffer, parseState:{ + sizeOfMessage:self.sizeOfMessage, + bytesRead:self.bytesRead, + stubBuffer:self.stubBuffer}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + } + } else if(sizeOfMessage <= 4 || sizeOfMessage > self.maxBsonSize) { + var errorObject = {err:"socketHandler", trace:null, bin:data, parseState:{ + sizeOfMessage:sizeOfMessage, + bytesRead:0, + buffer:null, + stubBuffer:null}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + + // Clear out the state of the parser + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + // Exit parsing loop + data = new Buffer(0); + + } else { + try { + var emitBuffer = data.slice(0, sizeOfMessage); + // Reset state of buffer + self.buffer = null; + self.sizeOfMessage = 0; + self.bytesRead = 0; + self.stubBuffer = null; + // Copy rest of message + data = data.slice(sizeOfMessage); + // Emit the message + self.emit("message", emitBuffer, self); + } catch (err) { + var errorObject = {err:"socketHandler", trace:err, bin:self.buffer, parseState:{ + sizeOfMessage:sizeOfMessage, + bytesRead:self.bytesRead, + stubBuffer:self.stubBuffer}}; + if(self.logger != null && self.logger.doError) self.logger.error("parseError", errorObject); + // We got a parse Error fire it off then keep going + self.emit("parseError", errorObject, self); + } + + } + } else { + // Create a buffer that contains the space for the non-complete message + self.stubBuffer = new Buffer(data.length) + // Copy the data to the stub buffer + data.copy(self.stubBuffer, 0); + // Exit parsing loop + data = new Buffer(0); + } + } + } + } + } +} + +var endHandler = function(self) { + return function() { + // Set connected to false + self.connected = false; + // Emit end event + self.emit("end", {err: 'connection received Fin packet from [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self); + } +} + +var timeoutHandler = function(self) { + return function() { + self.emit("timeout", {err: 'connection to [' + self.socketOptions.host + ':' + self.socketOptions.port + '] timed out'}, self); + } +} + +var drainHandler = function(self) { + return function() { + } +} + +var errorHandler = function(self) { + return function(err) { + // Set connected to false + self.connected = false; + // Emit error + self.emit("error", {err: 'failed to connect to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self); + } +} + +var closeHandler = function(self) { + return function(hadError) { + // If we have an error during the connection phase + if(hadError && !self.connected) { + // Set disconnected + self.connected = false; + // Emit error + self.emit("error", {err: 'failed to connect to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self); + } else { + // Set disconnected + self.connected = false; + // Emit close + self.emit("close", {err: 'connection closed to [' + self.socketOptions.host + ':' + self.socketOptions.port + ']'}, self); + } + } +} + +// Some basic defaults +Connection.DEFAULT_PORT = 27017; + + + + + + + diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_pool.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_pool.js new file mode 100644 index 0000000..9bbb88e --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_pool.js @@ -0,0 +1,249 @@ +var utils = require('./connection_utils'), + inherits = require('util').inherits, + net = require('net'), + EventEmitter = require('events').EventEmitter, + inherits = require('util').inherits, + MongoReply = require("../responses/mongo_reply").MongoReply, + Connection = require("./connection").Connection; + +var ConnectionPool = exports.ConnectionPool = function(host, port, poolSize, bson, socketOptions) { + if(typeof host !== 'string') { + throw new Error("host must be specified [" + host + "]"); + } + + // Set up event emitter + EventEmitter.call(this); + + // Keep all options for the socket in a specific collection allowing the user to specify the + // Wished upon socket connection parameters + this.socketOptions = typeof socketOptions === 'object' ? socketOptions : {}; + this.socketOptions.host = host; + this.socketOptions.port = port; + this.socketOptions.domainSocket = false; + this.bson = bson; + // PoolSize is always + 1 for special reserved "measurment" socket (like ping, stats etc) + this.poolSize = poolSize; + this.minPoolSize = Math.floor(this.poolSize / 2) + 1; + + // Check if the host is a socket + if(host.match(/^\//)) { + this.socketOptions.domainSocket = true; + } else if(typeof port !== 'number') { + throw new Error("port must be specified [" + port + "]"); + } + + // Set default settings for the socket options + utils.setIntegerParameter(this.socketOptions, 'timeout', 0); + // Delay before writing out the data to the server + utils.setBooleanParameter(this.socketOptions, 'noDelay', true); + // Delay before writing out the data to the server + utils.setIntegerParameter(this.socketOptions, 'keepAlive', 0); + // Set the encoding of the data read, default is binary == null + utils.setStringParameter(this.socketOptions, 'encoding', null); + // Allows you to set a throttling bufferSize if you need to stop overflows + utils.setIntegerParameter(this.socketOptions, 'bufferSize', 0); + + // Internal structures + this.openConnections = []; + // Assign connection id's + this.connectionId = 0; + + // Current index for selection of pool connection + this.currentConnectionIndex = 0; + // The pool state + this._poolState = 'disconnected'; + // timeout control + this._timeout = false; +} + +inherits(ConnectionPool, EventEmitter); + +ConnectionPool.prototype.setMaxBsonSize = function(maxBsonSize) { + if(maxBsonSize == null){ + maxBsonSize = Connection.DEFAULT_MAX_BSON_SIZE; + } + + for(var i = 0; i < this.openConnections.length; i++) { + this.openConnections[i].maxBsonSize = maxBsonSize; + } +} + +// Start a function +var _connect = function(_self) { + return new function() { + // Create a new connection instance + var connection = new Connection(_self.connectionId++, _self.socketOptions); + // Set logger on pool + connection.logger = _self.logger; + // Connect handler + connection.on("connect", function(err, connection) { + // Add connection to list of open connections + _self.openConnections.push(connection); + // If the number of open connections is equal to the poolSize signal ready pool + if(_self.openConnections.length === _self.poolSize && _self._poolState !== 'disconnected') { + // Set connected + _self._poolState = 'connected'; + // Emit pool ready + _self.emit("poolReady"); + } else if(_self.openConnections.length < _self.poolSize) { + // We need to open another connection, make sure it's in the next + // tick so we don't get a cascade of errors + process.nextTick(function() { + _connect(_self); + }); + } + }); + + var numberOfErrors = 0 + + // Error handler + connection.on("error", function(err, connection) { + numberOfErrors++; + // If we are already disconnected ignore the event + if(_self._poolState != 'disconnected' && _self.listeners("error").length > 0) { + _self.emit("error", err); + } + + connection.close(); + _self._poolState = 'disconnected'; + _self.stop(); + }); + + // Close handler + connection.on("close", function() { + // If we are already disconnected ignore the event + if(_self._poolState !== 'disconnected' && _self.listeners("close").length > 0) { + _self.emit("close"); + } + // Set disconnected + _self._poolState = 'disconnected'; + // Stop + _self.stop(); + }); + + // Timeout handler + connection.on("timeout", function(err, connection) { + // If we are already disconnected ignore the event + if(_self._poolState !== 'disconnected' && _self.listeners("timeout").length > 0) { + _self.emit("timeout", err); + } + + connection.close(); + _self.stop(); + }); + + // Parse error, needs a complete shutdown of the pool + connection.on("parseError", function() { + // If we are already disconnected ignore the event + if(_self._poolState !== 'disconnected' && _self.listeners("parseError").length > 0) { + _self.emit("parseError", new Error("parseError occured")); + } + + _self.stop(); + }); + + connection.on("message", function(message) { + _self.emit("message", message); + }); + + // Start connection in the next tick + connection.start(); + }(); +} + + +// Start method, will throw error if no listeners are available +// Pass in an instance of the listener that contains the api for +// finding callbacks for a given message etc. +ConnectionPool.prototype.start = function() { + var markerDate = new Date().getTime(); + var self = this; + + if(this.listeners("poolReady").length == 0) { + throw "pool must have at least one listener ready that responds to the [poolReady] event"; + } + + // Set pool state to connecting + this._poolState = 'connecting'; + this._timeout = false; + + _connect(self); +} + +// Restart a connection pool (on a close the pool might be in a wrong state) +ConnectionPool.prototype.restart = function() { + // Close all connections + this.stop(false); + // Now restart the pool + this.start(); +} + +// Stop the connections in the pool +ConnectionPool.prototype.stop = function(removeListeners) { + removeListeners = removeListeners == null ? true : removeListeners; + // Set disconnected + this._poolState = 'disconnected'; + + // Clear all listeners if specified + if(removeListeners) { + this.removeAllEventListeners(); + } + + // Close all connections + for(var i = 0; i < this.openConnections.length; i++) { + this.openConnections[i].close(); + } + + // Clean up + this.openConnections = []; +} + +// Check the status of the connection +ConnectionPool.prototype.isConnected = function() { + return this._poolState === 'connected'; +} + +// Checkout a connection from the pool for usage, or grab a specific pool instance +ConnectionPool.prototype.checkoutConnection = function(id) { + var index = (this.currentConnectionIndex++ % (this.openConnections.length)); + var connection = this.openConnections[index]; + return connection; +} + +ConnectionPool.prototype.getAllConnections = function() { + return this.openConnections; +} + +// Remove all non-needed event listeners +ConnectionPool.prototype.removeAllEventListeners = function() { + this.removeAllListeners("close"); + this.removeAllListeners("error"); + this.removeAllListeners("timeout"); + this.removeAllListeners("connect"); + this.removeAllListeners("end"); + this.removeAllListeners("parseError"); + this.removeAllListeners("message"); + this.removeAllListeners("poolReady"); +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_utils.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_utils.js new file mode 100644 index 0000000..5910924 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_utils.js @@ -0,0 +1,23 @@ +exports.setIntegerParameter = function(object, field, defaultValue) { + if(object[field] == null) { + object[field] = defaultValue; + } else if(typeof object[field] !== "number" && object[field] !== parseInt(object[field], 10)) { + throw "object field [" + field + "] must be a numeric integer value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]"; + } +} + +exports.setBooleanParameter = function(object, field, defaultValue) { + if(object[field] == null) { + object[field] = defaultValue; + } else if(typeof object[field] !== "boolean") { + throw "object field [" + field + "] must be a boolean value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]"; + } +} + +exports.setStringParameter = function(object, field, defaultValue) { + if(object[field] == null) { + object[field] = defaultValue; + } else if(typeof object[field] !== "string") { + throw "object field [" + field + "] must be a string value, attempted to set to [" + object[field] + "] type of [" + typeof object[field] + "]"; + } +} \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/mongos.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/mongos.js new file mode 100644 index 0000000..3ba7c87 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/mongos.js @@ -0,0 +1,333 @@ +var ReadPreference = require('./read_preference').ReadPreference + , Base = require('./base').Base + , inherits = require('util').inherits; + +/** + * Mongos constructor provides a connection to a mongos proxy including failover to additional servers + * + * Options + * - **socketOptions** {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number)) + * - **ha** {Boolean, default:true}, turn on high availability, attempts to reconnect to down proxies + * - **haInterval** {Number, default:2000}, time between each replicaset status check. + * + * @class Represents a Mongos connection with failover to backup proxies + * @param {Array} list of mongos server objects + * @param {Object} [options] additional options for the mongos connection + */ +var Mongos = function Mongos(servers, options) { + // Set up basic + if(!(this instanceof Mongos)) + return new Mongos(servers, options); + + // Set up event emitter + Base.call(this); + + // Throw error on wrong setup + if(servers == null || !Array.isArray(servers) || servers.length == 0) + throw new Error("At least one mongos proxy must be in the array"); + + // Ensure we have at least an empty options object + this.options = options == null ? {} : options; + // Set default connection pool options + this.socketOptions = this.options.socketOptions != null ? this.options.socketOptions : {}; + // Enabled ha + this.haEnabled = this.options['ha'] == null ? true : this.options['ha']; + // How often are we checking for new servers in the replicaset + this.mongosStatusCheckInterval = this.options['haInterval'] == null ? 2000 : this.options['haInterval']; + // Save all the server connections + this.servers = servers; + // Servers we need to attempt reconnect with + this.downServers = []; + // Just contains the current lowest ping time and server + this.lowestPingTimeServer = null; + this.lowestPingTime = 0; + + // Add options to servers + for(var i = 0; i < this.servers.length; i++) { + var server = this.servers[i]; + // Default empty socket options object + var socketOptions = {host: server.host, port: server.port}; + // If a socket option object exists clone it + if(this.socketOptions != null) { + var keys = Object.keys(this.socketOptions); + for(var k = 0; k < keys.length;k++) socketOptions[keys[i]] = this.socketOptions[keys[i]]; + } + // Set socket options + server.socketOptions = socketOptions; + } +} + +/** + * @ignore + */ +inherits(Mongos, Base); + +/** + * @ignore + */ +Mongos.prototype.isMongos = function() { + return true; +} + +/** + * @ignore + */ +Mongos.prototype.connect = function(db, options, callback) { + if('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + var self = this; + + // Keep reference to parent + this.db = db; + // Set server state to connecting + this._serverState = 'connecting'; + // Number of total servers that need to initialized (known servers) + this._numberOfServersLeftToInitialize = this.servers.length; + // Default to the first proxy server as the first one to use + this._currentMongos = this.servers[0]; + + // Connect handler + var connectHandler = function(_server) { + return function(err, result) { + self._numberOfServersLeftToInitialize = self._numberOfServersLeftToInitialize - 1; + + if(self._numberOfServersLeftToInitialize == 0) { + // Start ha function if it exists + if(self.haEnabled) { + // Setup the ha process + self._replicasetTimeoutId = setTimeout(self.mongosCheckFunction, self.mongosStatusCheckInterval); + } + + // Set the mongos to connected + self._serverState = "connected"; + // Emit the open event + self.db.emit("open", null, self.db); + // Callback + callback(null, self.db); + } + } + }; + + // Error handler + var errorOrCloseHandler = function(_server) { + return function(err, result) { + // Create current mongos comparision + var currentUrl = self._currentMongos.host + ":" + self._currentMongos.port; + var serverUrl = this.host + ":" + this.port; + // We need to check if the server that closed is the actual current proxy we are using, otherwise + // just ignore + if(currentUrl == serverUrl) { + // Remove the server from the list + if(self.servers.indexOf(_server) != -1) { + self.servers.splice(self.servers.indexOf(_server), 1); + } + + // Pick the next one on the list if there is one + for(var i = 0; i < self.servers.length; i++) { + // Grab the server out of the array (making sure there is no servers in the list if none available) + var server = self.servers[i]; + // Generate url for comparision + var serverUrl = server.host + ":" + server.port; + // It's not the current one and connected set it as the current db + if(currentUrl != serverUrl && server.isConnected()) { + self._currentMongos = server; + break; + } + } + } + + // Ensure we don't store the _server twice + if(self.downServers.indexOf(_server) == -1) { + // Add the server instances + self.downServers.push(_server); + } + } + } + + // Mongo function + this.mongosCheckFunction = function() { + // If we have down servers let's attempt a reconnect + if(self.downServers.length > 0) { + var numberOfServersLeft = self.downServers.length; + // Attempt to reconnect + for(var i = 0; i < self.downServers.length; i++) { + var downServer = self.downServers.pop(); + // Attemp to reconnect + downServer.connect(self.db, {returnIsMasterResults: true}, function(_server) { + // Return a function to check for the values + return function(err, result) { + // Adjust the number of servers left + numberOfServersLeft = numberOfServersLeft - 1; + + if(err != null) { + self.downServers.push(_server); + } else { + // Add server event handlers + _server.on("close", errorOrCloseHandler(_server)); + _server.on("error", errorOrCloseHandler(_server)); + // Add to list of servers + self.servers.push(_server); + } + + if(numberOfServersLeft <= 0) { + // Perfom another ha + self._replicasetTimeoutId = setTimeout(self.mongosCheckFunction, self.mongosStatusCheckInterval); + } + } + }(downServer)); + } + } else if(self.servers.length > 0) { + var numberOfServersLeft = self.servers.length; + var _s = new Date().getTime() + + // Else let's perform a ping command + for(var i = 0; i < self.servers.length; i++) { + var executePing = function(_server) { + // Get a read connection + var _connection = _server.checkoutReader(); + // Execute ping command + self.db.command({ping:1}, {connection:_connection}, function(err, result) { + var pingTime = new Date().getTime() - _s; + // If no server set set the first one, otherwise check + // the lowest ping time and assign the server if it's got a lower ping time + if(self.lowestPingTimeServer == null) { + self.lowestPingTimeServer = _server; + self.lowestPingTime = pingTime; + self._currentMongos = _server; + } else if(self.lowestPingTime > pingTime + && (_server.host != self.lowestPingTimeServer.host || _server.port != self.lowestPingTimeServer.port)) { + self.lowestPingTimeServer = _server; + self.lowestPingTime = pingTime; + self._currentMongos = _server; + } + + // Number of servers left + numberOfServersLeft = numberOfServersLeft - 1; + // All active mongos's pinged + if(numberOfServersLeft == 0) { + // Perfom another ha + self._replicasetTimeoutId = setTimeout(self.mongosCheckFunction, self.mongosStatusCheckInterval); + } + }) + } + // Execute the function + executePing(self.servers[i]); + } + } else { + self._replicasetTimeoutId = setTimeout(self.mongosCheckFunction, self.mongosStatusCheckInterval); + } + } + + // Connect all the server instances + for(var i = 0; i < this.servers.length; i++) { + // Get the connection + var server = this.servers[i]; + server.mongosInstance = this; + // Add server event handlers + server.on("close", errorOrCloseHandler(server)); + server.on("timeout", errorOrCloseHandler(server)); + server.on("error", errorOrCloseHandler(server)); + // Connect the instance + server.connect(self.db, {returnIsMasterResults: true}, connectHandler(server)); + } +} + +/** + * @ignore + * Just return the currently picked active connection + */ +Mongos.prototype.allServerInstances = function() { + return this.servers; +} + +/** + * Always ourselves + * @ignore + */ +Mongos.prototype.setReadPreference = function() {} + +/** + * @ignore + */ +Mongos.prototype.allRawConnections = function() { + // Neeed to build a complete list of all raw connections, start with master server + var allConnections = []; + // Add all connections + for(var i = 0; i < this.servers.length; i++) { + allConnections = allConnections.concat(this.servers[i].allRawConnections()); + } + + // Return all the conections + return allConnections; +} + +/** + * @ignore + */ +Mongos.prototype.isConnected = function() { + return this._serverState == "connected"; +} + +/** + * @ignore + */ +Mongos.prototype.checkoutWriter = function() { + // No current mongo, just pick first server + if(this._currentMongos == null && this.servers.length > 0) { + return this.servers[0].checkoutWriter(); + } + return this._currentMongos.checkoutWriter(); +} + +/** + * @ignore + */ +Mongos.prototype.checkoutReader = function(read) { + // If we have a read preference object unpack it + if(typeof read == 'object' && read['_type'] == 'ReadPreference') { + // Validate if the object is using a valid mode + if(!read.isValid()) throw new Error("Illegal readPreference mode specified, " + read.mode); + } else if(!ReadPreference.isValid(read)) { + throw new Error("Illegal readPreference mode specified, " + read); + } + + // No current mongo, just pick first server + if(this._currentMongos == null && this.servers.length > 0) { + return this.servers[0].checkoutReader(); + } + return this._currentMongos.checkoutReader(); +} + +/** + * @ignore + */ +Mongos.prototype.close = function(callback) { + var self = this; + // Set server status as disconnected + this._serverState = 'disconnected'; + // Number of connections to close + var numberOfConnectionsToClose = self.servers.length; + // If we have a ha process running kill it + if(self._replicasetTimeoutId != null) clearTimeout(self._replicasetTimeoutId); + // Close all proxy connections + for(var i = 0; i < self.servers.length; i++) { + self.servers[i].close(function(err, result) { + numberOfConnectionsToClose = numberOfConnectionsToClose - 1; + // Callback if we have one defined + if(numberOfConnectionsToClose == 0 && typeof callback == 'function') { + callback(null); + } + }); + } +} + +/** + * @ignore + * Return the used state + */ +Mongos.prototype._isUsed = function() { + return this._used; +} + +exports.Mongos = Mongos; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/read_preference.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/read_preference.js new file mode 100644 index 0000000..4cba587 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/read_preference.js @@ -0,0 +1,66 @@ +/** + * A class representation of the Read Preference. + * + * Read Preferences + * - **ReadPreference.PRIMARY**, Read from primary only. All operations produce an error (throw an exception where applicable) if primary is unavailable. Cannot be combined with tags (This is the default.). + * - **ReadPreference.PRIMARY_PREFERRED**, Read from primary if available, otherwise a secondary. + * - **ReadPreference.SECONDARY**, Read from secondary if available, otherwise error. + * - **ReadPreference.SECONDARY_PREFERRED**, Read from a secondary if available, otherwise read from the primary. + * - **ReadPreference.NEAREST**, All modes read from among the nearest candidates, but unlike other modes, NEAREST will include both the primary and all secondaries in the random selection. + * + * @class Represents a Read Preference. + * @param {String} the read preference type + * @param {Object} tags + * @return {ReadPreference} + */ +var ReadPreference = function(mode, tags) { + if(!(this instanceof ReadPreference)) + return new ReadPreference(mode, tags); + this._type = 'ReadPreference'; + this.mode = mode; + this.tags = tags; +} + +/** + * @ignore + */ +ReadPreference.isValid = function(_mode) { + return (_mode == ReadPreference.PRIMARY || _mode == ReadPreference.PRIMARY_PREFERRED + || _mode == ReadPreference.SECONDARY || _mode == ReadPreference.SECONDARY_PREFERRED + || _mode == ReadPreference.NEAREST); +} + +/** + * @ignore + */ +ReadPreference.prototype.isValid = function(mode) { + var _mode = typeof mode == 'string' ? mode : this.mode; + return ReadPreference.isValid(_mode); +} + +/** + * @ignore + */ +ReadPreference.prototype.toObject = function() { + var object = {mode:this.mode}; + + if(this.tags != null) { + object['tags'] = this.tags; + } + + return object; +} + +/** + * @ignore + */ +ReadPreference.PRIMARY = 'primary'; +ReadPreference.PRIMARY_PREFERRED = 'primaryPreferred'; +ReadPreference.SECONDARY = 'secondary'; +ReadPreference.SECONDARY_PREFERRED = 'secondaryPreferred'; +ReadPreference.NEAREST = 'nearest' + +/** + * @ignore + */ +exports.ReadPreference = ReadPreference; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/repl_set.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/repl_set.js new file mode 100644 index 0000000..a471e89 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/repl_set.js @@ -0,0 +1,1311 @@ +var Connection = require('./connection').Connection, + ReadPreference = require('./read_preference').ReadPreference, + DbCommand = require('../commands/db_command').DbCommand, + MongoReply = require('../responses/mongo_reply').MongoReply, + debug = require('util').debug, + inherits = require('util').inherits, + inspect = require('util').inspect, + Server = require('./server').Server, + PingStrategy = require('./strategies/ping_strategy').PingStrategy, + StatisticsStrategy = require('./strategies/statistics_strategy').StatisticsStrategy, + Base = require('./base').Base; + +const STATE_STARTING_PHASE_1 = 0; +const STATE_PRIMARY = 1; +const STATE_SECONDARY = 2; +const STATE_RECOVERING = 3; +const STATE_FATAL_ERROR = 4; +const STATE_STARTING_PHASE_2 = 5; +const STATE_UNKNOWN = 6; +const STATE_ARBITER = 7; +const STATE_DOWN = 8; +const STATE_ROLLBACK = 9; + +/** + * ReplSet constructor provides replicaset functionality + * + * Options + * - **ha** {Boolean, default:true}, turn on high availability. + * - **haInterval** {Number, default:2000}, time between each replicaset status check. + * - **reconnectWait** {Number, default:1000}, time to wait in miliseconds before attempting reconnect. + * - **retries** {Number, default:30}, number of times to attempt a replicaset reconnect. + * - **rs_name** {String}, the name of the replicaset to connect to. + * - **socketOptions** {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number)) + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **strategy** {String, default:null}, selection strategy for reads choose between (ping and statistical, default is round-robin) + * - **secondaryAcceptableLatencyMS** {Number, default:15}, sets the range of servers to pick when using NEAREST (lowest ping ms + the latency fence, ex: range of 1 to (1 + 15) ms) + * - **connectArbiter** {Boolean, default:false}, sets if the driver should connect to arbiters or not. + * - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**. + * + * @class Represents a Replicaset Configuration + * @param {Array} list of server objects participating in the replicaset. + * @param {Object} [options] additional options for the replicaset connection. + */ +var ReplSet = exports.ReplSet = function(servers, options) { + this.count = 0; + + // Set up basic + if(!(this instanceof ReplSet)) + return new ReplSet(servers, options); + + // Set up event emitter + Base.call(this); + + // Ensure no Mongos's + for(var i = 0; i < servers.length; i++) { + if(!(servers[i] instanceof Server)) throw new Error("list of servers must be of type Server"); + } + + // Just reference for simplicity + var self = this; + // Contains the master server entry + this.options = options == null ? {} : options; + this.reconnectWait = this.options["reconnectWait"] != null ? this.options["reconnectWait"] : 1000; + this.retries = this.options["retries"] != null ? this.options["retries"] : 30; + this.replicaSet = this.options["rs_name"]; + + // Are we allowing reads from secondaries ? + this.readSecondary = this.options["read_secondary"]; + this.slaveOk = true; + this.closedConnectionCount = 0; + this._used = false; + + // Connect arbiters ? + this.connectArbiter = this.options.connectArbiter == null ? false : this.options.connectArbiter; + + // Default poolSize for new server instances + this.poolSize = this.options.poolSize == null ? 5 : this.options.poolSize; + this._currentServerChoice = 0; + + // Set up ssl connections + this.ssl = this.options.ssl == null ? false : this.options.ssl; + + // Just keeps list of events we allow + this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[]}; + // Internal state of server connection + this._serverState = 'disconnected'; + // Read preference + this._readPreference = null; + // Number of initalized severs + this._numberOfServersLeftToInitialize = 0; + // Do we record server stats or not + this.recordQueryStats = false; + // Update health try server + this.updateHealthServerTry = 0; + + // Get the readPreference + var readPreference = this.options['readPreference']; + + // Validate correctness of Read preferences + if(readPreference != null) { + if(readPreference != ReadPreference.PRIMARY && readPreference != ReadPreference.PRIMARY_PREFERRED + && readPreference != ReadPreference.SECONDARY && readPreference != ReadPreference.SECONDARY_PREFERRED + && readPreference != ReadPreference.NEAREST && typeof readPreference != 'object' && readPreference['_type'] != 'ReadPreference') { + throw new Error("Illegal readPreference mode specified, " + readPreference); + } + + this._readPreference = readPreference; + } else { + this._readPreference = null; + } + + // Ensure read_secondary is set correctly + if(!this.readSecondary) + this.readSecondary = this._readPreference == ReadPreference.PRIMARY + || this._readPreference == false + || this._readPreference == null ? false : true; + + // Strategy for picking a secondary + this.secondaryAcceptableLatencyMS = this.options['secondaryAcceptableLatencyMS'] == null ? 15 : this.options['secondaryAcceptableLatencyMS']; + this.strategy = this.options['strategy']; + // Make sure strategy is one of the two allowed + if(this.strategy != null && (this.strategy != 'ping' && this.strategy != 'statistical')) throw new Error("Only ping or statistical strategies allowed"); + // Let's set up our strategy object for picking secodaries + if(this.strategy == 'ping') { + // Create a new instance + this.strategyInstance = new PingStrategy(this, this.secondaryAcceptableLatencyMS); + } else if(this.strategy == 'statistical') { + // Set strategy as statistical + this.strategyInstance = new StatisticsStrategy(this); + // Add enable query information + this.enableRecordQueryStats(true); + } + + // Set default connection pool options + this.socketOptions = this.options.socketOptions != null ? this.options.socketOptions : {}; + + // Set up logger if any set + this.logger = this.options.logger != null + && (typeof this.options.logger.debug == 'function') + && (typeof this.options.logger.error == 'function') + && (typeof this.options.logger.debug == 'function') + ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}}; + + // Ensure all the instances are of type server and auto_reconnect is false + if(!Array.isArray(servers) || servers.length == 0) { + throw Error("The parameter must be an array of servers and contain at least one server"); + } else if(Array.isArray(servers) || servers.length > 0) { + var count = 0; + servers.forEach(function(server) { + if(server instanceof Server) count = count + 1; + // Ensure no server has reconnect on + server.options.auto_reconnect = false; + }); + + if(count < servers.length) { + throw Error("All server entries must be of type Server"); + } else { + this.servers = servers; + } + } + + // var deduplicate list + var uniqueServers = {}; + // De-duplicate any servers in the seed list + for(var i = 0; i < this.servers.length; i++) { + var server = this.servers[i]; + // If server does not exist set it + if(uniqueServers[server.host + ":" + server.port] == null) { + uniqueServers[server.host + ":" + server.port] = server; + } + } + + // Let's set the deduplicated list of servers + this.servers = []; + // Add the servers + for(var key in uniqueServers) { + this.servers.push(uniqueServers[key]); + } + + // Enabled ha + this.haEnabled = this.options['ha'] == null ? true : this.options['ha']; + // How often are we checking for new servers in the replicaset + this.replicasetStatusCheckInterval = this.options['haInterval'] == null ? 1000 : this.options['haInterval']; + this._replicasetTimeoutId = null; + + // Connection timeout + this._connectTimeoutMS = this.socketOptions.connectTimeoutMS + ? this.socketOptions.connectTimeoutMS + : 1000; + + // Current list of servers to test + this.pingCandidateServers = []; + + // Last replicaset check time + this.lastReplicaSetTime = new Date().getTime(); +}; + +/** + * @ignore + */ +inherits(ReplSet, Base); + +/** + * @ignore + */ +// Allow setting the read preference at the replicaset level +ReplSet.prototype.setReadPreference = function(preference) { + // Set read preference + this._readPreference = preference; + // Ensure slaveOk is correct for secodnaries read preference and tags + if((this._readPreference == ReadPreference.SECONDARY_PREFERRED || this._readPreference == ReadPreference.SECONDARY) + || (this._readPreference != null && typeof this._readPreference == 'object')) { + this.slaveOk = true; + } +} + +/** + * @ignore + */ +ReplSet.prototype._isUsed = function() { + return this._used; +} + +/** + * @ignore + */ +ReplSet.prototype.isMongos = function() { + return false; +} + +/** + * @ignore + */ +ReplSet.prototype.isConnected = function(read) { + if(read == null || read == ReadPreference.PRIMARY || read == false) + return this.primary != null && this._state.master != null && this._state.master.isConnected(); + + if((read == ReadPreference.PRIMARY_PREFERRED || read == ReadPreference.SECONDARY_PREFERRED || read == ReadPreference.NEAREST) + && ((this.primary != null && this._state.master != null && this._state.master.isConnected()) + || (this._state && this._state.secondaries && Object.keys(this._state.secondaries).length > 0))) { + return true; + } else if(read == ReadPreference.SECONDARY) { + return this._state && this._state.secondaries && Object.keys(this._state.secondaries).length > 0; + } + + // No valid connection return false + return false; +} + +/** + * @ignore + */ +ReplSet.prototype.isSetMember = function() { + return false; +} + +/** + * @ignore + */ +ReplSet.prototype.isPrimary = function(config) { + return this.readSecondary && Object.keys(this._state.secondaries).length > 0 ? false : true; +} + +/** + * @ignore + */ +ReplSet.prototype.isReadPrimary = ReplSet.prototype.isPrimary; + +/** + * @ignore + */ +ReplSet.prototype.allServerInstances = function() { + var self = this; + // If no state yet return empty + if(!self._state) return []; + // Close all the servers (concatenate entire list of servers first for ease) + var allServers = self._state.master != null ? [self._state.master] : []; + + // Secondary keys + var keys = Object.keys(self._state.secondaries); + // Add all secondaries + for(var i = 0; i < keys.length; i++) { + allServers.push(self._state.secondaries[keys[i]]); + } + + // Arbiter keys + var keys = Object.keys(self._state.arbiters); + // Add all arbiters + for(var i = 0; i < keys.length; i++) { + allServers.push(self._state.arbiters[keys[i]]); + } + + // Passive keys + var keys = Object.keys(self._state.passives); + // Add all arbiters + for(var i = 0; i < keys.length; i++) { + allServers.push(self._state.passives[keys[i]]); + } + + // Return complete list of all servers + return allServers; +} + +/** + * Enables high availability pings. + * + * @ignore + */ +ReplSet.prototype._enableHA = function () { + var self = this; + return check(); + + function ping () { + if("disconnected" == self._serverState) return; + + if(Object.keys(self._state.addresses).length == 0) return; + var selectedServer = self._state.addresses[Object.keys(self._state.addresses)[self.updateHealthServerTry++]]; + if(self.updateHealthServerTry >= Object.keys(self._state.addresses).length) self.updateHealthServerTry = 0; + if(selectedServer == null) return check(); + + // If we have an active db instance + if(self.dbInstances.length > 0) { + var db = self.dbInstances[0]; + + // Create a new master connection + var _server = new Server(selectedServer.host, selectedServer.port, { + auto_reconnect: false, + returnIsMasterResults: true, + slaveOk: true, + poolSize: 1, + socketOptions: { connectTimeoutMS: self._connectTimeoutMS } + }); + + // Connect using the new _server connection to not impact the driver + // behavior on any errors we could possibly run into + _server.connect(db, function(err, result, _server) { + if(err) { + if(_server.close) _server.close(); + return check(); + } + + // Create is master command + var cmd = DbCommand.createIsMasterCommand(db); + // Execute is master command + db._executeQueryCommand(cmd, {failFast:true, connection: _server.checkoutWriter()}, function(err, res) { + // Close the connection used + _server.close(); + // If error let's set perform another check + if(err) return check(); + // Validate the replicaset + self._validateReplicaset(res, db.auths, function() { + check(); + }); + }); + }); + } + } + + function check () { + self._haTimer = setTimeout(ping, self.replicasetStatusCheckInterval); + } +} + +/** + * @ignore + */ +ReplSet.prototype._validateReplicaset = function(result, auths, cb) { + var self = this; + var res = result.documents[0]; + + // manage master node changes + if(res.primary && self._state.master && self._state.master.name != res.primary) { + // Delete master record so we can rediscover it + delete self._state.addresses[self._state.master.name]; + + // TODO existing issue? this seems to only work if + // we already have a connection to the new primary. + + // Update information on new primary + // add as master, remove from secondary + var newMaster = self._state.addresses[res.primary]; + newMaster.isMasterDoc.ismaster = true; + newMaster.isMasterDoc.secondary = false; + self._state.master = newMaster; + delete self._state.secondaries[res.primary]; + } + + // discover new hosts + var hosts = []; + + for(var i = 0; i < res.hosts.length; ++i) { + var host = res.hosts[i]; + if (host == res.me) continue; + if (!(self._state.addresses[host] || ~hosts.indexOf(host))) { + // we dont already have a connection to this host and aren't + // already planning on connecting. + hosts.push(host); + } + } + + connectTo(hosts, auths, self, cb); +} + +/** + * Create connections to all `hosts` firing `cb` after + * connections are attempted for all `hosts`. + * + * @param {Array} hosts + * @param {Array} [auths] + * @param {ReplSet} replset + * @param {Function} cb + * @ignore + */ +function connectTo (hosts, auths, replset, cb) { + var pending = hosts.length; + if (!pending) return cb(); + + for(var i = 0; i < hosts.length; ++i) { + connectToHost(hosts[i], auths, replset, handle); + } + + function handle () { + --pending; + if (0 === pending) cb(); + } +} + +/** + * Attempts connection to `host` and authenticates with optional `auth` + * for the given `replset` firing `cb` when finished. + * + * @param {String} host + * @param {Array} auths + * @param {ReplSet} replset + * @param {Function} cb + * @ignore + */ +function connectToHost (host, auths, replset, cb) { + var server = createServer(host, replset); + + var options = { + returnIsMasterResults: true, + eventReceiver: server + } + + server.connect(replset.db, options, function(err, result) { + var doc = result && result.documents && result.documents[0]; + + if (err || !doc) { + server.close(); + return cb(err, result, server); + } + + if(!(doc.ismaster || doc.secondary || doc.arbiterOnly)) { + server.close(); + return cb(null, result, server); + } + + // if host is an arbiter, disconnect if not configured for it + if(doc.arbiterOnly && !replset.connectArbiter) { + server.close(); + return cb(null, result, server); + } + + // create handler for successful connections + var handleConnect = _connectHandler(replset, null, server); + function complete () { + handleConnect(err, result); + cb(); + } + + // authenticate if necessary + if(!(Array.isArray(auths) && auths.length > 0)) { + return complete(); + } + + var pending = auths.length; + + var connections = server.allRawConnections(); + var pendingAuthConn = connections.length; + for(var x = 0; x 0) { + self.db.emit(event, err); + } + + // If it's the primary close all connections + if(self._state.master + && self._state.master.host == server.host + && self._state.master.port == server.port) { + // return self.close(); + self._state.master = null; + } + } +} + +var _connectHandler = function(self, candidateServers, instanceServer) { + return function(err, result) { + // We are disconnected stop attempting reconnect or connect + if(self._serverState == 'disconnected') return instanceServer.close(); + + // If no error handle isMaster + if(err == null && result.documents[0].hosts != null) { + // Fetch the isMaster command result + var document = result.documents[0]; + // Break out the results + var setName = document.setName; + var isMaster = document.ismaster; + var secondary = document.secondary; + var passive = document.passive; + var arbiterOnly = document.arbiterOnly; + var hosts = Array.isArray(document.hosts) ? document.hosts : []; + var arbiters = Array.isArray(document.arbiters) ? document.arbiters : []; + var passives = Array.isArray(document.passives) ? document.passives : []; + var tags = document.tags ? document.tags : {}; + var primary = document.primary; + // Find the current server name and fallback if none + var userProvidedServerString = instanceServer.host + ":" + instanceServer.port; + var me = document.me || userProvidedServerString; + + // Verify if the set name is the same otherwise shut down and return an error + if(self.replicaSet == null) { + self.replicaSet = setName; + } else if(self.replicaSet != setName) { + // Stop the set + self.close(); + // Emit a connection error + return self.emit("connectionError", + new Error("configured mongodb replicaset does not match provided replicaset [" + setName + "] != [" + self.replicaSet + "]")) + } + + // Make sure we have the right reference + var oldServer = self._state.addresses[userProvidedServerString] + if (oldServer && oldServer !== instanceServer) oldServer.close(); + delete self._state.addresses[userProvidedServerString]; + + if (self._state.addresses[me] && self._state.addresses[me] !== instanceServer) { + self._state.addresses[me].close(); + } + + self._state.addresses[me] = instanceServer; + + // Let's add the server to our list of server types + if(secondary == true && (passive == false || passive == null)) { + self._state.secondaries[me] = instanceServer; + } else if(arbiterOnly == true) { + self._state.arbiters[me] = instanceServer; + } else if(secondary == true && passive == true) { + self._state.passives[me] = instanceServer; + } else if(isMaster == true) { + self._state.master = instanceServer; + } else if(isMaster == false && primary != null && self._state.addresses[primary]) { + self._state.master = self._state.addresses[primary]; + } + + // Set the name + instanceServer.name = me; + // Add tag info + instanceServer.tags = tags; + + // Add the handlers to the instance + instanceServer.on("close", _handler("close", self)); + instanceServer.on("error", _handler("error", self)); + instanceServer.on("timeout", _handler("timeout", self)); + + // Possible hosts + var possibleHosts = Array.isArray(hosts) ? hosts.slice() : []; + possibleHosts = Array.isArray(passives) ? possibleHosts.concat(passives) : possibleHosts; + + if(self.connectArbiter == true) { + possibleHosts = Array.isArray(arbiters) ? possibleHosts.concat(arbiters) : possibleHosts; + } + + if(Array.isArray(candidateServers)) { + // Add any new candidate servers for connection + for(var j = 0; j < possibleHosts.length; j++) { + if(self._state.addresses[possibleHosts[j]] == null && possibleHosts[j] != null) { + var parts = possibleHosts[j].split(/:/); + if(parts.length == 1) { + parts = [parts[0], Connection.DEFAULT_PORT]; + } + + // New candidate server + var candidateServer = new Server(parts[0], parseInt(parts[1])); + candidateServer.name = possibleHosts[j]; + self._state.addresses[possibleHosts[j]] = candidateServer; + // Add the new server to the list of candidate servers + candidateServers.push(candidateServer); + } + } + } + } else if(err != null || self._serverState == 'disconnected'){ + delete self._state.addresses[instanceServer.host + ":" + instanceServer.port]; + // Remove it from the set + instanceServer.close(); + } + + // Attempt to connect to the next server + if(Array.isArray(candidateServers) && candidateServers.length > 0) { + var server = candidateServers.pop(); + + // Get server addresses + var addresses = self._state.addresses; + + // Default empty socket options object + var socketOptions = {}; + + // Set fast connect timeout + socketOptions['connectTimeoutMS'] = self._connectTimeoutMS; + + // If a socket option object exists clone it + if(self.socketOptions != null && typeof self.socketOptions === 'object') { + var keys = Object.keys(self.socketOptions); + for(var j = 0; j < keys.length;j++) socketOptions[keys[j]] = self.socketOptions[keys[j]]; + } + + // If ssl is specified + if(self.ssl) server.ssl = true; + + // Add host information to socket options + socketOptions['host'] = server.host; + socketOptions['port'] = server.port; + server.socketOptions = socketOptions; + server.replicasetInstance = self; + server.enableRecordQueryStats(self.recordQueryStats); + + // Set the server + if (addresses[server.host + ":" + server.port] != server) { + if (addresses[server.host + ":" + server.port]) { + // Close the connection before deleting + addresses[server.host + ":" + server.port].close(); + } + delete addresses[server.host + ":" + server.port]; + } + addresses[server.host + ":" + server.port] = server; + // Connect + server.connect(self.db, {returnIsMasterResults: true, eventReceiver:server}, _connectHandler(self, candidateServers, server)); + } else if(Array.isArray(candidateServers)) { + // If we have no primary emit error + if(self._state.master == null) { + // Stop the set + self.close(); + // Emit a connection error + return self.emit("connectionError", + new Error("no primary server found in set")) + } else{ + if (self.strategyInstance) { + self.strategyInstance.start(); + } + + self.emit("fullsetup", null, self.db, self); + self.emit("open", null, self.db, self); + } + } + } +} + +/** + * Interval state object constructor + * + * @ignore + */ +ReplSet.State = function ReplSetState () { + this.errorMessages = []; + this.secondaries = {}; + this.addresses = {}; + this.arbiters = {}; + this.passives = {}; + this.members = []; + this.errors = {}; + this.setName = null; + this.master = null; +} + +/** + * @ignore + */ +ReplSet.prototype.connect = function(parent, options, callback) { + var self = this; + if('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + + // Ensure it's all closed + self.close(); + + // Set connecting status + this.db = parent; + this._serverState = 'connecting'; + this._callbackList = []; + + this._state = new ReplSet.State(); + + // Ensure parent can do a slave query if it's set + parent.slaveOk = this.slaveOk + ? this.slaveOk + : parent.slaveOk; + + // Remove any listeners + this.removeAllListeners("fullsetup"); + this.removeAllListeners("connectionError"); + + // Add primary found event handler + this.once("fullsetup", function() { + self._handleOnFullSetup(parent); + + // Callback + if(typeof callback == 'function') { + var internalCallback = callback; + callback = null; + internalCallback(null, parent, self); + } + }); + + this.once("connectionError", function(err) { + self._serverState = 'disconnected'; + // Ensure it's all closed + self.close(); + // Perform the callback + if(typeof callback == 'function') { + var internalCallback = callback; + callback = null; + internalCallback(err, parent, self); + } + }); + + // Get server addresses + var addresses = this._state.addresses; + + // De-duplicate any servers + var server, key; + for(var i = 0; i < this.servers.length; i++) { + server = this.servers[i]; + key = server.host + ":" + server.port; + if(null == addresses[key]) { + addresses[key] = server; + } + } + + // Get the list of servers that is deduplicated and start connecting + var candidateServers = []; + var keys = Object.keys(addresses); + for(var i = 0; i < keys.length; i++) { + server = addresses[keys[i]]; + server.assignReplicaSet(this); + candidateServers.push(server); + } + + // Let's connect to the first one on the list + server = candidateServers.pop(); + var opts = { + returnIsMasterResults: true, + eventReceiver: server + } + server.connect(parent, opts, _connectHandler(this, candidateServers, server)); +} + +/** + * Handles the first `fullsetup` event of this ReplSet. + * + * @param {Db} parent + * @ignore + */ +ReplSet.prototype._handleOnFullSetup = function (parent) { + this._serverState = 'connected'; + + // Emit the fullsetup and open event + parent.emit("open", null, this.db, this); + parent.emit("fullsetup", null, this.db, this); + + if(!this.haEnabled) return; + this._enableHA(); +} + +/** + * Disables high availability pings. + * + * @ignore + */ +ReplSet.prototype._disableHA = function () { + clearTimeout(this._haTimer); + this._haTimer = undefined; +} + +/** + * @ignore + */ +ReplSet.prototype.checkoutWriter = function() { + // Establish connection + var connection = this._state.master != null ? this._state.master.checkoutWriter() : null; + // Return the connection + return connection; +} + +/** + * @ignore + */ +var pickFirstConnectedSecondary = function pickFirstConnectedSecondary(self, tags) { + var keys = Object.keys(self._state.secondaries); + var connection; + + // Find first available reader if any + for(var i = 0; i < keys.length; i++) { + connection = self._state.secondaries[keys[i]].checkoutReader(); + if(connection) return connection; + } + + // If we still have a null, read from primary if it's not secondary only + if(self._readPreference == ReadPreference.SECONDARY_PREFERRED) { + connection = self._state.master.checkoutReader(); + if(connection) return connection; + } + + var preferenceName = self._readPreference == ReadPreference.SECONDARY_PREFERRED + ? 'secondary' + : self._readPreference; + + // console.log("================================================================ pickFirstConnectedSecondary :::: ") + + return new Error("No replica set member available for query with ReadPreference " + + preferenceName + " and tags " + JSON.stringify(tags)); +} + +/** + * @ignore + */ +var _pickFromTags = function(self, tags) { + // If we have an array or single tag selection + var tagObjects = Array.isArray(tags) ? tags : [tags]; + // Iterate over all tags until we find a candidate server + for(var _i = 0; _i < tagObjects.length; _i++) { + // Grab a tag object + var tagObject = tagObjects[_i]; + // Matching keys + var matchingKeys = Object.keys(tagObject); + // Match all the servers that match the provdided tags + var keys = Object.keys(self._state.secondaries); + var candidateServers = []; + + for(var i = 0; i < keys.length; i++) { + var server = self._state.secondaries[keys[i]]; + // If we have tags match + if(server.tags != null) { + var matching = true; + // Ensure we have all the values + for(var j = 0; j < matchingKeys.length; j++) { + if(server.tags[matchingKeys[j]] != tagObject[matchingKeys[j]]) { + matching = false; + break; + } + } + + // If we have a match add it to the list of matching servers + if(matching) { + candidateServers.push(server); + } + } + } + + // If we have a candidate server return + if(candidateServers.length > 0) { + if(this.strategyInstance) return this.strategyInstance.checkoutSecondary(tags, candidateServers); + // Set instance to return + return candidateServers[Math.floor(Math.random() * candidateServers.length)].checkoutReader(); + } + } + + // No connection found + return null; +} + +/** + * @ignore + */ +ReplSet.prototype.checkoutReader = function(readPreference, tags) { + var connection = null; + // console.log("============================ checkoutReader") + // console.dir(readPreference) + // console.log(arguments.callee.caller.toString()) + + // If we have a read preference object unpack it + if(typeof readPreference == 'object' && readPreference['_type'] == 'ReadPreference') { + // Validate if the object is using a valid mode + if(!readPreference.isValid()) throw new Error("Illegal readPreference mode specified, " + readPreference.mode); + // Set the tag + tags = readPreference.tags; + readPreference = readPreference.mode; + } else if(typeof readPreference == 'object' && readPreference['_type'] != 'ReadPreference') { + throw new Error("read preferences must be either a string or an instance of ReadPreference"); + } + + // Set up our read Preference, allowing us to override the readPreference + var finalReadPreference = readPreference != null ? readPreference : this._readPreference; + finalReadPreference = finalReadPreference == true ? ReadPreference.SECONDARY_PREFERRED : finalReadPreference; + finalReadPreference = finalReadPreference == null ? ReadPreference.PRIMARY : finalReadPreference; + // finalReadPreference = 'primary'; + + // console.log("============================ finalReadPreference: " + finalReadPreference) + // console.dir(finalReadPreference) + + // If we are reading from a primary + if(finalReadPreference == 'primary') { + // If we provide a tags set send an error + if(typeof tags == 'object' && tags != null) { + return new Error("PRIMARY cannot be combined with tags"); + } + + // If we provide a tags set send an error + if(this._state.master == null) { + return new Error("No replica set primary available for query with ReadPreference PRIMARY"); + } + + // Checkout a writer + return this.checkoutWriter(); + } + + // If we have specified to read from a secondary server grab a random one and read + // from it, otherwise just pass the primary connection + if((this.readSecondary || finalReadPreference == ReadPreference.SECONDARY_PREFERRED || finalReadPreference == ReadPreference.SECONDARY) && Object.keys(this._state.secondaries).length > 0) { + // If we have tags, look for servers matching the specific tag + if(tags != null && typeof tags == 'object') { + // Get connection + connection = _pickFromTags(this, tags);// = function(self, readPreference, tags) { + // No candidate servers that match the tags, error + if(connection == null) { + return new Error("No replica set members available for query"); + } + } else { + connection = _roundRobin(this, tags); + } + } else if(finalReadPreference == ReadPreference.PRIMARY_PREFERRED) { + // Check if there is a primary available and return that if possible + connection = this.checkoutWriter(); + // If no connection available checkout a secondary + if(connection == null) { + // If we have tags, look for servers matching the specific tag + if(tags != null && typeof tags == 'object') { + // Get connection + connection = _pickFromTags(this, tags);// = function(self, readPreference, tags) { + // No candidate servers that match the tags, error + if(connection == null) { + return new Error("No replica set members available for query"); + } + } else { + connection = _roundRobin(this, tags); + } + } + } else if(finalReadPreference == ReadPreference.SECONDARY_PREFERRED && tags == null && Object.keys(this._state.secondaries).length == 0) { + // console.log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SECONDARY_PREFERRED") + connection = this.checkoutWriter(); + // If no connection return an error + if(connection == null) { + var preferenceName = finalReadPreference == ReadPreference.SECONDARY ? 'secondary' : finalReadPreference; + connection = new Error("No replica set member available for query with ReadPreference " + preferenceName + " and tags " + JSON.stringify(tags)); + } + } else if(finalReadPreference == ReadPreference.SECONDARY_PREFERRED) { + // If we have tags, look for servers matching the specific tag + if(tags != null && typeof tags == 'object') { + // Get connection + connection = _pickFromTags(this, tags);// = function(self, readPreference, tags) { + // No candidate servers that match the tags, error + if(connection == null) { + // No secondary server avilable, attemp to checkout a primary server + connection = this.checkoutWriter(); + // If no connection return an error + if(connection == null) { + return new Error("No replica set members available for query"); + } + } + } else if(this.strategyInstance != null) { + connection = this.strategyInstance.checkoutReader(tags); + } + } else if(finalReadPreference == ReadPreference.NEAREST && this.strategyInstance != null) { + connection = this.strategyInstance.checkoutSecondary(tags); + } else if(finalReadPreference == ReadPreference.NEAREST && this.strategyInstance == null) { + return new Error("A strategy for calculating nearness must be enabled such as ping or statistical"); + } else if(finalReadPreference == ReadPreference.SECONDARY && Object.keys(this._state.secondaries).length == 0) { + // console.log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SECONDARY") + if(tags != null && typeof tags == 'object') { + var preferenceName = finalReadPreference == ReadPreference.SECONDARY ? 'secondary' : finalReadPreference; + connection = new Error("No replica set member available for query with ReadPreference " + preferenceName + " and tags " + JSON.stringify(tags)); + } else { + connection = new Error("No replica set secondary available for query with ReadPreference SECONDARY"); + } + } else { + connection = this.checkoutWriter(); + } + + // Return the connection + return connection; +} + +/** + * Pick a secondary using round robin + * + * @ignore + */ +function _roundRobin (replset, tags) { + var keys = Object.keys(replset._state.secondaries); + var key = keys[replset._currentServerChoice++ % keys.length]; + + var conn = null != replset._state.secondaries[key] + ? replset._state.secondaries[key].checkoutReader() + : null; + + // If connection is null fallback to first available secondary + if (null == conn) { + conn = pickFirstConnectedSecondary(replset, tags); + } + + return conn; +} + +/** + * @ignore + */ +ReplSet.prototype.allRawConnections = function() { + // Neeed to build a complete list of all raw connections, start with master server + var allConnections = []; + if(this._state.master == null) return []; + // Get connection object + var allMasterConnections = this._state.master.connectionPool.getAllConnections(); + // Add all connections to list + allConnections = allConnections.concat(allMasterConnections); + // If we have read secondary let's add all secondary servers + if(Object.keys(this._state.secondaries).length > 0) { + // Get all the keys + var keys = Object.keys(this._state.secondaries); + // For each of the secondaries grab the connections + for(var i = 0; i < keys.length; i++) { + // Get connection object + var secondaryPoolConnections = this._state.secondaries[keys[i]].connectionPool.getAllConnections(); + // Add all connections to list + allConnections = allConnections.concat(secondaryPoolConnections); + } + } + + // Return all the conections + return allConnections; +} + +/** + * @ignore + */ +ReplSet.prototype.enableRecordQueryStats = function(enable) { + // Set the global enable record query stats + this.recordQueryStats = enable; + // Ensure all existing servers already have the flag set, even if the + // connections are up already or we have not connected yet + if(this._state != null && this._state.addresses != null) { + var keys = Object.keys(this._state.addresses); + // Iterate over all server instances and set the enableRecordQueryStats flag + for(var i = 0; i < keys.length; i++) { + this._state.addresses[keys[i]].enableRecordQueryStats(enable); + } + } else if(Array.isArray(this.servers)) { + for(var i = 0; i < this.servers.length; i++) { + this.servers[i].enableRecordQueryStats(enable); + } + } +} + +/** + * @ignore + */ +ReplSet.prototype.disconnect = function(callback) { + this.close(callback); +} + +/** + * @ignore + */ +ReplSet.prototype.close = function(callback) { + var self = this; + // Disconnect + this._serverState = 'disconnected'; + // Close all servers + if(this._state && this._state.addresses) { + var keys = Object.keys(this._state.addresses); + // Iterate over all server instances + for(var i = 0; i < keys.length; i++) { + this._state.addresses[keys[i]].close(); + } + } + + // If we have a strategy stop it + if(this.strategyInstance) this.strategyInstance.stop(); + + // If it's a callback + if(typeof callback == 'function') callback(null, null); +} + +/** + * Auto Reconnect property + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "autoReconnect", { enumerable: true + , get: function () { + return true; + } +}); + +/** + * Get Read Preference method + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "readPreference", { enumerable: true + , get: function () { + if(this._readPreference == null && this.readSecondary) { + return ReadPreference.SECONDARY_PREFERRED; + } else if(this._readPreference == null && !this.readSecondary) { + return ReadPreference.PRIMARY; + } else { + return this._readPreference; + } + } +}); + +/** + * Db Instances + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "dbInstances", {enumerable:true + , get: function() { + var servers = this.allServerInstances(); + return servers.length > 0 ? servers[0].dbInstances : []; + } +}) + +/** + * Just make compatible with server.js + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "host", { enumerable: true + , get: function () { + if (this.primary != null) return this.primary.host; + } +}); + +/** + * Just make compatible with server.js + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "port", { enumerable: true + , get: function () { + if (this.primary != null) return this.primary.port; + } +}); + +/** + * Get status of read + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "read", { enumerable: true + , get: function () { + return this.secondaries.length > 0 ? this.secondaries[0] : null; + } +}); + +/** + * Get list of secondaries + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "secondaries", {enumerable: true + , get: function() { + var keys = Object.keys(this._state.secondaries); + var array = new Array(keys.length); + // Convert secondaries to array + for(var i = 0; i < keys.length; i++) { + array[i] = this._state.secondaries[keys[i]]; + } + return array; + } +}); + +/** + * Get list of all secondaries including passives + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "allSecondaries", {enumerable: true + , get: function() { + return this.secondaries.concat(this.passives); + } +}); + +/** + * Get list of arbiters + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "arbiters", {enumerable: true + , get: function() { + var keys = Object.keys(this._state.arbiters); + var array = new Array(keys.length); + // Convert arbiters to array + for(var i = 0; i < keys.length; i++) { + array[i] = this._state.arbiters[keys[i]]; + } + return array; + } +}); + +/** + * Get list of passives + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "passives", {enumerable: true + , get: function() { + var keys = Object.keys(this._state.passives); + var array = new Array(keys.length); + // Convert arbiters to array + for(var i = 0; i < keys.length; i++) { + array[i] = this._state.passives[keys[i]]; + } + return array; + } +}); + +/** + * Master connection property + * @ignore + */ +Object.defineProperty(ReplSet.prototype, "primary", { enumerable: true + , get: function () { + return this._state != null ? this._state.master : null; + } +}); + +/** + * @ignore + */ +// Backward compatibility +exports.ReplSetServers = ReplSet; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js new file mode 100644 index 0000000..50635ec --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js @@ -0,0 +1,860 @@ +var Connection = require('./connection').Connection, + ReadPreference = require('./read_preference').ReadPreference, + DbCommand = require('../commands/db_command').DbCommand, + MongoReply = require('../responses/mongo_reply').MongoReply, + ConnectionPool = require('./connection_pool').ConnectionPool, + EventEmitter = require('events').EventEmitter, + Base = require('./base').Base, + utils = require('../utils'), + inherits = require('util').inherits; + +/** + * Class representing a single MongoDB Server connection + * + * Options + * - **readPreference** {String, default:null}, set's the read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST) + * - **ssl** {Boolean, default:false}, use ssl connection (needs to have a mongod server with ssl support) + * - **slaveOk** {Boolean, default:false}, legacy option allowing reads from secondary, use **readPrefrence** instead. + * - **poolSize** {Number, default:5}, number of connections in the connection pool, set to 5 as default for legacy reasons. + * - **socketOptions** {Object, default:null}, an object containing socket options to use (noDelay:(boolean), keepAlive:(number), connectTimeoutMS:(number), socketTimeoutMS:(number)) + * - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**. + * - **auto_reconnect** {Boolean, default:false}, reconnect on error. + * - **disableDriverBSONSizeCheck** {Boolean, default:false}, force the server to error if the BSON message is to big + * + * @class Represents a Server connection. + * @param {String} host the server host + * @param {Number} port the server port + * @param {Object} [options] optional options for insert command + */ +function Server(host, port, options) { + // Set up Server instance + if(!(this instanceof Server)) return new Server(host, port, options); + + // Set up event emitter + Base.call(this); + + // Ensure correct values + if(port != null && typeof port == 'object') { + options = port; + port = Connection.DEFAULT_PORT; + } + + var self = this; + this.host = host; + this.port = port; + this.options = options == null ? {} : options; + this.internalConnection; + this.internalMaster = false; + this.connected = false; + this.poolSize = this.options.poolSize == null ? 5 : this.options.poolSize; + this.disableDriverBSONSizeCheck = this.options.disableDriverBSONSizeCheck != null ? this.options.disableDriverBSONSizeCheck : false; + this.ssl = this.options.ssl == null ? false : this.options.ssl; + this.slaveOk = this.options["slave_ok"] ? this.options["slave_ok"] : this.options["slaveOk"]; + this._used = false; + this.replicasetInstance = null; + + // Get the readPreference + var readPreference = this.options['readPreference']; + // If readPreference is an object get the mode string + var validateReadPreference = readPreference != null && typeof readPreference == 'object' ? readPreference.mode : readPreference; + // Read preference setting + if(validateReadPreference != null) { + if(validateReadPreference != ReadPreference.PRIMARY && validateReadPreference != ReadPreference.SECONDARY && validateReadPreference != ReadPreference.NEAREST + && validateReadPreference != ReadPreference.SECONDARY_PREFERRED && validateReadPreference != ReadPreference.PRIMARY_PREFERRED) { + throw new Error("Illegal readPreference mode specified, " + validateReadPreference); + } + + // Set read Preference + this._readPreference = readPreference; + } else { + this._readPreference = null; + } + + // Contains the isMaster information returned from the server + this.isMasterDoc; + + // Set default connection pool options + this.socketOptions = this.options.socketOptions != null ? this.options.socketOptions : {}; + if(this.disableDriverBSONSizeCheck) this.socketOptions.disableDriverBSONSizeCheck = this.disableDriverBSONSizeCheck; + // Set ssl up if it's defined + if(this.ssl) { + this.socketOptions.ssl = true; + } + + // Set up logger if any set + this.logger = this.options.logger != null + && (typeof this.options.logger.debug == 'function') + && (typeof this.options.logger.error == 'function') + && (typeof this.options.logger.log == 'function') + ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}}; + + // Just keeps list of events we allow + this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[], timeout:[]}; + // Internal state of server connection + this._serverState = 'disconnected'; + // this._timeout = false; + // Contains state information about server connection + this._state = {'runtimeStats': {'queryStats':new RunningStats()}}; + // Do we record server stats or not + this.recordQueryStats = false; +}; + +/** + * @ignore + */ +inherits(Server, Base); + +// +// Deprecated, USE ReadPreferences class +// +Server.READ_PRIMARY = ReadPreference.PRIMARY; +Server.READ_SECONDARY = ReadPreference.SECONDARY_PREFERRED; +Server.READ_SECONDARY_ONLY = ReadPreference.SECONDARY; + +/** + * Always ourselves + * @ignore + */ +Server.prototype.setReadPreference = function() {} + +/** + * @ignore + */ +Server.prototype.isMongos = function() { + return this.isMasterDoc != null && this.isMasterDoc['msg'] == "isdbgrid" ? true : false; +} + +/** + * @ignore + */ +Server.prototype._isUsed = function() { + return this._used; +} + +/** + * @ignore + */ +Server.prototype.close = function(callback) { + // Remove all local listeners + this.removeAllListeners(); + + if(this.connectionPool != null) { + // Remove all the listeners on the pool so it does not fire messages all over the place + this.connectionPool.removeAllEventListeners(); + // Close the connection if it's open + this.connectionPool.stop(true); + } + + // Set server status as disconnected + this._serverState = 'disconnected'; + // Peform callback if present + if(typeof callback === 'function') callback(null); +}; + +/** + * @ignore + */ +Server.prototype.isConnected = function() { + return this._serverState == 'connected'; +} + +/** + * @ignore + */ +Server.prototype.allServerInstances = function() { + return [this]; +} + +/** + * @ignore + */ +Server.prototype.isSetMember = function() { + return this.replicasetInstance != null || this.mongosInstance != null; +} + +/** + * Assigns a replica set to this `server`. + * + * @param {ReplSet} replset + * @ignore + */ +Server.prototype.assignReplicaSet = function (replset) { + this.replicasetInstance = replset; + this.inheritReplSetOptionsFrom(replset); + this.enableRecordQueryStats(replset.recordQueryStats); +} + +/** + * Takes needed options from `replset` and overwrites + * our own options. + * + * @param {ReplSet} replset + * @ignore + */ +Server.prototype.inheritReplSetOptionsFrom = function (replset) { + this.socketOptions = {}; + this.socketOptions.connectTimeoutMS = replset._connectTimeoutMS; + + if(replset.ssl) + this.socketOptions.ssl = true; + + // If a socket option object exists clone it + if(utils.isObject(replset.socketOptions)) { + var keys = Object.keys(replset.socketOptions); + for(var i = 0; i < keys.length; i++) + this.socketOptions[keys[i]] = replset.socketOptions[keys[i]]; + } +} + +/** + * Opens this server connection. + * + * @ignore + */ +Server.prototype.connect = function(dbInstance, options, callback) { + if('function' === typeof options) callback = options, options = {}; + if(options == null) options = {}; + if(!('function' === typeof callback)) callback = null; + + // Currently needed to work around problems with multiple connections in a pool with ssl + // TODO fix if possible + if(this.ssl == true) { + // Set up socket options for ssl + this.socketOptions.ssl = true; + } + + // Let's connect + var server = this; + // Let's us override the main receiver of events + var eventReceiver = options.eventReceiver != null ? options.eventReceiver : this; + // Creating dbInstance + this.dbInstance = dbInstance; + // Save reference to dbInstance + this.dbInstances = [dbInstance]; + + // Force connection pool if there is one + if(server.connectionPool) server.connectionPool.stop(); + + // Set server state to connecting + this._serverState = 'connecting'; + // Ensure dbInstance can do a slave query if it's set + dbInstance.slaveOk = this.slaveOk ? this.slaveOk : dbInstance.slaveOk; + // Create connection Pool instance with the current BSON serializer + var connectionPool = new ConnectionPool(this.host, this.port, this.poolSize, dbInstance.bson, this.socketOptions); + // Set logger on pool + connectionPool.logger = this.logger; + + // Set up a new pool using default settings + server.connectionPool = connectionPool; + + // Set basic parameters passed in + var returnIsMasterResults = options.returnIsMasterResults == null ? false : options.returnIsMasterResults; + + // Create a default connect handler, overriden when using replicasets + var connectCallback = function(err, reply) { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // If something close down the connection and removed the callback before + // proxy killed connection etc, ignore the erorr as close event was isssued + if(err != null && internalCallback == null) return; + // Internal callback + if(err != null) return internalCallback(err, null); + server.master = reply.documents[0].ismaster == 1 ? true : false; + server.connectionPool.setMaxBsonSize(reply.documents[0].maxBsonObjectSize); + // Set server as connected + server.connected = true; + // Save document returned so we can query it + server.isMasterDoc = reply.documents[0]; + + // Emit open event + _emitAcrossAllDbInstances(server, eventReceiver, "open", null, returnIsMasterResults ? reply : dbInstance, null); + + // If we have it set to returnIsMasterResults + if(returnIsMasterResults) { + internalCallback(null, reply, server); + } else { + internalCallback(null, dbInstance, server); + } + }; + + // Let's us override the main connect callback + var connectHandler = options.connectHandler == null ? connectCallback : options.connectHandler; + + // Set up on connect method + connectionPool.on("poolReady", function() { + // Create db command and Add the callback to the list of callbacks by the request id (mapping outgoing messages to correct callbacks) + var db_command = DbCommand.NcreateIsMasterCommand(dbInstance, dbInstance.databaseName); + // Check out a reader from the pool + var connection = connectionPool.checkoutConnection(); + // Set server state to connEcted + server._serverState = 'connected'; + + // Register handler for messages + dbInstance._registerHandler(db_command, false, connection, connectHandler); + + // Write the command out + connection.write(db_command); + }) + + // Set up item connection + connectionPool.on("message", function(message) { + // Attempt to parse the message + try { + // Create a new mongo reply + var mongoReply = new MongoReply() + // Parse the header + mongoReply.parseHeader(message, connectionPool.bson) + // If message size is not the same as the buffer size + // something went terribly wrong somewhere + if(mongoReply.messageLength != message.length) { + // Emit the error + if(eventReceiver.listeners("error") && eventReceiver.listeners("error").length > 0) eventReceiver.emit("error", new Error("bson length is different from message length"), server); + // Remove all listeners + server.removeAllListeners(); + } else { + var startDate = new Date().getTime(); + + // Callback instance + var callbackInfo = null; + var dbInstanceObject = null; + + // Locate a callback instance and remove any additional ones + for(var i = 0; i < server.dbInstances.length; i++) { + var dbInstanceObjectTemp = server.dbInstances[i]; + var hasHandler = dbInstanceObjectTemp._hasHandler(mongoReply.responseTo.toString()); + // Assign the first one we find and remove any duplicate ones + if(hasHandler && callbackInfo == null) { + callbackInfo = dbInstanceObjectTemp._findHandler(mongoReply.responseTo.toString()); + dbInstanceObject = dbInstanceObjectTemp; + } else if(hasHandler) { + dbInstanceObjectTemp._removeHandler(mongoReply.responseTo.toString()); + } + } + + // The command executed another request, log the handler again under that request id + if(mongoReply.requestId > 0 && mongoReply.cursorId.toString() != "0" + && callbackInfo && callbackInfo.info && callbackInfo.info.exhaust) { + dbInstance._reRegisterHandler(mongoReply.requestId, callbackInfo); + } + + // Only execute callback if we have a caller + // chained is for findAndModify as it does not respect write concerns + if(callbackInfo && callbackInfo.callback && callbackInfo.info && Array.isArray(callbackInfo.info.chained)) { + // Check if callback has already been fired (missing chain command) + var chained = callbackInfo.info.chained; + var numberOfFoundCallbacks = 0; + for(var i = 0; i < chained.length; i++) { + if(dbInstanceObject._hasHandler(chained[i])) numberOfFoundCallbacks++; + } + + // If we have already fired then clean up rest of chain and move on + if(numberOfFoundCallbacks != chained.length) { + for(var i = 0; i < chained.length; i++) { + dbInstanceObject._removeHandler(chained[i]); + } + + // Just return from function + return; + } + + // Parse the body + mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) { + // console.log("+++++++++++++++++++++++++++++++++++++++ recieved message") + // console.dir(message) + if(err != null) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // Remove all listeners and close the connection pool + server.removeAllListeners(); + connectionPool.stop(true); + + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error("connection closed due to parseError"), null, server); + } else if(server.isSetMember()) { + if(server.listeners("parseError") && server.listeners("parseError").length > 0) server.emit("parseError", new Error("connection closed due to parseError"), server); + } else { + if(eventReceiver.listeners("parseError") && eventReceiver.listeners("parseError").length > 0) eventReceiver.emit("parseError", new Error("connection closed due to parseError"), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error("connection closed due to parseError")); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); + } + // Short cut + return; + } + + // Fetch the callback + var callbackInfo = dbInstanceObject._findHandler(mongoReply.responseTo.toString()); + // If we have an error let's execute the callback and clean up all other + // chained commands + var firstResult = mongoReply && mongoReply.documents; + + // Check for an error, if we have one let's trigger the callback and clean up + // The chained callbacks + if(firstResult[0].err != null || firstResult[0].errmsg != null) { + // Trigger the callback for the error + dbInstanceObject._callHandler(mongoReply.responseTo, mongoReply, null); + } else { + var chainedIds = callbackInfo.info.chained; + + if(chainedIds.length > 0 && chainedIds[chainedIds.length - 1] == mongoReply.responseTo) { + // Cleanup all other chained calls + chainedIds.pop(); + // Remove listeners + for(var i = 0; i < chainedIds.length; i++) dbInstanceObject._removeHandler(chainedIds[i]); + // Call the handler + dbInstanceObject._callHandler(mongoReply.responseTo, callbackInfo.info.results.shift(), null); + } else{ + // Add the results to all the results + for(var i = 0; i < chainedIds.length; i++) { + var handler = dbInstanceObject._findHandler(chainedIds[i]); + // Check if we have an object, if it's the case take the current object commands and + // and add this one + if(handler.info != null) { + handler.info.results = Array.isArray(callbackInfo.info.results) ? callbackInfo.info.results : []; + handler.info.results.push(mongoReply); + } + } + } + } + }); + } else if(callbackInfo && callbackInfo.callback && callbackInfo.info) { + // Parse the body + mongoReply.parseBody(message, connectionPool.bson, callbackInfo.info.raw, function(err) { + // console.log("+++++++++++++++++++++++++++++++++++++++ recieved message") + // console.dir(message) + if(err != null) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // Remove all listeners and close the connection pool + server.removeAllListeners(); + connectionPool.stop(true); + + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error("connection closed due to parseError"), null, server); + } else if(server.isSetMember()) { + if(server.listeners("parseError") && server.listeners("parseError").length > 0) server.emit("parseError", new Error("connection closed due to parseError"), server); + } else { + if(eventReceiver.listeners("parseError") && eventReceiver.listeners("parseError").length > 0) eventReceiver.emit("parseError", new Error("connection closed due to parseError"), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error("connection closed due to parseError")); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); + } + // Short cut + return; + } + + // Let's record the stats info if it's enabled + if(server.recordQueryStats == true && server._state['runtimeStats'] != null + && server._state.runtimeStats['queryStats'] instanceof RunningStats) { + // Add data point to the running statistics object + server._state.runtimeStats.queryStats.push(new Date().getTime() - callbackInfo.info.start); + } + + dbInstanceObject._callHandler(mongoReply.responseTo, mongoReply, null); + }); + } + } + } catch (err) { + // Throw error in next tick + process.nextTick(function() { + throw err; + }) + } + }); + + // Handle timeout + connectionPool.on("timeout", function(err) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(err, null, server); + } else if(server.isSetMember()) { + if(server.listeners("timeout") && server.listeners("timeout").length > 0) server.emit("timeout", err, server); + } else { + if(eventReceiver.listeners("timeout") && eventReceiver.listeners("timeout").length > 0) eventReceiver.emit("timeout", err, server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(err); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "timeout", err, server, true); + } + }); + + // Handle errors + connectionPool.on("error", function(message) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error(message && message.err ? message.err : message), null, server); + } else if(server.isSetMember()) { + if(server.listeners("error") && server.listeners("error").length > 0) server.emit("error", new Error(message && message.err ? message.err : message), server); + } else { + if(eventReceiver.listeners("error") && eventReceiver.listeners("error").length > 0) eventReceiver.emit("error", new Error(message && message.err ? message.err : message), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error(message && message.err ? message.err : message)); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "error", new Error(message && message.err ? message.err : message), server, true); + } + }); + + // Handle close events + connectionPool.on("close", function() { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // If we have a callback return the error + if(typeof callback == 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error("connection closed"), null, server); + } else if(server.isSetMember()) { + if(server.listeners("close") && server.listeners("close").length > 0) server.emit("close", new Error("connection closed"), server); + } else { + if(eventReceiver.listeners("close") && eventReceiver.listeners("close").length > 0) eventReceiver.emit("close", new Error("connection closed"), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error("connection closed")); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "close", server, null, true); + } + }); + + // If we have a parser error we are in an unknown state, close everything and emit + // error + connectionPool.on("parseError", function(message) { + // If pool connection is already closed + if(server._serverState === 'disconnected') return; + // Set server state to disconnected + server._serverState = 'disconnected'; + // If we have a callback return the error + if(typeof callback === 'function') { + // ensure no callbacks get called twice + var internalCallback = callback; + callback = null; + // Perform callback + internalCallback(new Error("connection closed due to parseError"), null, server); + } else if(server.isSetMember()) { + if(server.listeners("parseError") && server.listeners("parseError").length > 0) server.emit("parseError", new Error("connection closed due to parseError"), server); + } else { + if(eventReceiver.listeners("parseError") && eventReceiver.listeners("parseError").length > 0) eventReceiver.emit("parseError", new Error("connection closed due to parseError"), server); + } + + // If we are a single server connection fire errors correctly + if(!server.isSetMember()) { + // Fire all callback errors + server.__executeAllCallbacksWithError(new Error("connection closed due to parseError")); + // Emit error + _emitAcrossAllDbInstances(server, eventReceiver, "parseError", server, null, true); + } + }); + + // Boot up connection poole, pass in a locator of callbacks + connectionPool.start(); +} + +/** + * @ignore + */ +var _emitAcrossAllDbInstances = function(server, filterDb, event, message, object, resetConnection) { + // Emit close event across all db instances sharing the sockets + var allServerInstances = server.allServerInstances(); + // Fetch the first server instance + var serverInstance = allServerInstances[0]; + // For all db instances signal all db instances + if(Array.isArray(serverInstance.dbInstances) && serverInstance.dbInstances.length >= 1) { + for(var i = 0; i < serverInstance.dbInstances.length; i++) { + var dbInstance = serverInstance.dbInstances[i]; + // Set the parent + if(resetConnection && typeof dbInstance.openCalled != 'undefined') + dbInstance.openCalled = false; + // Check if it's our current db instance and skip if it is + if(filterDb == null || filterDb.databaseName !== dbInstance.databaseName || filterDb.tag !== dbInstance.tag) { + // Only emit if there is a listener + if(dbInstance.listeners(event).length > 0) + dbInstance.emit(event, message, object); + } + } + } +} + +/** + * @ignore + */ +Server.prototype.allRawConnections = function() { + return this.connectionPool.getAllConnections(); +} + +/** + * Check if a writer can be provided + * @ignore + */ +var canCheckoutWriter = function(self, read) { + // We cannot write to an arbiter or secondary server + if(self.isMasterDoc['arbiterOnly'] == true) { + return new Error("Cannot write to an arbiter"); + } if(self.isMasterDoc['secondary'] == true) { + return new Error("Cannot write to a secondary"); + } else if(read == true && self._readPreference == ReadPreference.SECONDARY && self.isMasterDoc['ismaster'] == true) { + return new Error("Cannot read from primary when secondary only specified"); + } + + // Return no error + return null; +} + +/** + * @ignore + */ +Server.prototype.checkoutWriter = function(read) { + // console.log("===================== checkoutWriter :: " + read) + // console.dir(this.isMasterDoc) + if(read == true) return this.connectionPool.checkoutConnection(); + // Check if are allowed to do a checkout (if we try to use an arbiter f.ex) + var result = canCheckoutWriter(this, read); + // If the result is null check out a writer + if(result == null && this.connectionPool != null) { + return this.connectionPool.checkoutConnection(); + } else if(result == null) { + return null; + } else { + return result; + } +} + +/** + * Check if a reader can be provided + * @ignore + */ +var canCheckoutReader = function(self) { + // We cannot write to an arbiter or secondary server + if(self.isMasterDoc && self.isMasterDoc['arbiterOnly'] == true) { + return new Error("Cannot write to an arbiter"); + } else if(self._readPreference != null) { + // If the read preference is Primary and the instance is not a master return an error + if((self._readPreference == ReadPreference.PRIMARY) && self.isMasterDoc['ismaster'] != true) { + return new Error("Read preference is Server.PRIMARY and server is not master"); + } else if(self._readPreference == ReadPreference.SECONDARY && self.isMasterDoc['ismaster'] == true) { + return new Error("Cannot read from primary when secondary only specified"); + } + } + + // Return no error + return null; +} + +/** + * @ignore + */ +Server.prototype.checkoutReader = function() { + // console.log("===================== checkoutReader") + // Check if are allowed to do a checkout (if we try to use an arbiter f.ex) + var result = canCheckoutReader(this); + // If the result is null check out a writer + if(result == null && this.connectionPool != null) { + return this.connectionPool.checkoutConnection(); + } else if(result == null) { + return null; + } else { + return result; + } +} + +/** + * @ignore + */ +Server.prototype.enableRecordQueryStats = function(enable) { + this.recordQueryStats = enable; +} + +/** + * Internal statistics object used for calculating average and standard devitation on + * running queries + * @ignore + */ +var RunningStats = function() { + var self = this; + this.m_n = 0; + this.m_oldM = 0.0; + this.m_oldS = 0.0; + this.m_newM = 0.0; + this.m_newS = 0.0; + + // Define getters + Object.defineProperty(this, "numDataValues", { enumerable: true + , get: function () { return this.m_n; } + }); + + Object.defineProperty(this, "mean", { enumerable: true + , get: function () { return (this.m_n > 0) ? this.m_newM : 0.0; } + }); + + Object.defineProperty(this, "variance", { enumerable: true + , get: function () { return ((this.m_n > 1) ? this.m_newS/(this.m_n - 1) : 0.0); } + }); + + Object.defineProperty(this, "standardDeviation", { enumerable: true + , get: function () { return Math.sqrt(this.variance); } + }); + + Object.defineProperty(this, "sScore", { enumerable: true + , get: function () { + var bottom = this.mean + this.standardDeviation; + if(bottom == 0) return 0; + return ((2 * this.mean * this.standardDeviation)/(bottom)); + } + }); +} + +/** + * @ignore + */ +RunningStats.prototype.push = function(x) { + // Update the number of samples + this.m_n = this.m_n + 1; + // See Knuth TAOCP vol 2, 3rd edition, page 232 + if(this.m_n == 1) { + this.m_oldM = this.m_newM = x; + this.m_oldS = 0.0; + } else { + this.m_newM = this.m_oldM + (x - this.m_oldM) / this.m_n; + this.m_newS = this.m_oldS + (x - this.m_oldM) * (x - this.m_newM); + + // set up for next iteration + this.m_oldM = this.m_newM; + this.m_oldS = this.m_newS; + } +} + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "autoReconnect", { enumerable: true + , get: function () { + return this.options['auto_reconnect'] == null ? false : this.options['auto_reconnect']; + } +}); + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "connection", { enumerable: true + , get: function () { + return this.internalConnection; + } + , set: function(connection) { + this.internalConnection = connection; + } +}); + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "master", { enumerable: true + , get: function () { + return this.internalMaster; + } + , set: function(value) { + this.internalMaster = value; + } +}); + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "primary", { enumerable: true + , get: function () { + return this; + } +}); + +/** + * Getter for query Stats + * @ignore + */ +Object.defineProperty(Server.prototype, "queryStats", { enumerable: true + , get: function () { + return this._state.runtimeStats.queryStats; + } +}); + +/** + * @ignore + */ +Object.defineProperty(Server.prototype, "runtimeStats", { enumerable: true + , get: function () { + return this._state.runtimeStats; + } +}); + +/** + * Get Read Preference method + * @ignore + */ +Object.defineProperty(Server.prototype, "readPreference", { enumerable: true + , get: function () { + if(this._readPreference == null && this.readSecondary) { + return Server.READ_SECONDARY; + } else if(this._readPreference == null && !this.readSecondary) { + return Server.READ_PRIMARY; + } else { + return this._readPreference; + } + } +}); + +/** + * @ignore + */ +exports.Server = Server; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js new file mode 100644 index 0000000..4fefd78 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/strategies/ping_strategy.js @@ -0,0 +1,188 @@ +var Server = require("../server").Server; + +// The ping strategy uses pings each server and records the +// elapsed time for the server so it can pick a server based on lowest +// return time for the db command {ping:true} +var PingStrategy = exports.PingStrategy = function(replicaset, secondaryAcceptableLatencyMS) { + this.replicaset = replicaset; + this.secondaryAcceptableLatencyMS = secondaryAcceptableLatencyMS; + this.state = 'disconnected'; + this.pingInterval = 5000; + // Class instance + this.Db = require("../../db").Db; +} + +// Starts any needed code +PingStrategy.prototype.start = function(callback) { + // already running? + if ('connected' == this.state) return; + + this.state = 'connected'; + + // Start ping server + this._pingServer(callback); +} + +// Stops and kills any processes running +PingStrategy.prototype.stop = function(callback) { + // Stop the ping process + this.state = 'disconnected'; + + // optional callback + callback && callback(null, null); +} + +PingStrategy.prototype.checkoutSecondary = function(tags, secondaryCandidates) { + // Servers are picked based on the lowest ping time and then servers that lower than that + secondaryAcceptableLatencyMS + // Create a list of candidat servers, containing the primary if available + var candidateServers = []; + + // If we have not provided a list of candidate servers use the default setup + if(!Array.isArray(secondaryCandidates)) { + candidateServers = this.replicaset._state.master != null ? [this.replicaset._state.master] : []; + // Add all the secondaries + var keys = Object.keys(this.replicaset._state.secondaries); + for(var i = 0; i < keys.length; i++) { + candidateServers.push(this.replicaset._state.secondaries[keys[i]]) + } + } else { + candidateServers = secondaryCandidates; + } + + // Final list of eligable server + var finalCandidates = []; + + // If we have tags filter by tags + if(tags != null && typeof tags == 'object') { + // If we have an array or single tag selection + var tagObjects = Array.isArray(tags) ? tags : [tags]; + // Iterate over all tags until we find a candidate server + for(var _i = 0; _i < tagObjects.length; _i++) { + // Grab a tag object + var tagObject = tagObjects[_i]; + // Matching keys + var matchingKeys = Object.keys(tagObject); + // Remove any that are not tagged correctly + for(var i = 0; i < candidateServers.length; i++) { + var server = candidateServers[i]; + // If we have tags match + if(server.tags != null) { + var matching = true; + + // Ensure we have all the values + for(var j = 0; j < matchingKeys.length; j++) { + if(server.tags[matchingKeys[j]] != tagObject[matchingKeys[j]]) { + matching = false; + break; + } + } + + // If we have a match add it to the list of matching servers + if(matching) { + finalCandidates.push(server); + } + } + } + } + } else { + // Final array candidates + var finalCandidates = candidateServers; + } + + // Sort by ping time + finalCandidates.sort(function(a, b) { + return a.runtimeStats['pingMs'] > b.runtimeStats['pingMs']; + }); + + if(0 === finalCandidates.length) + return new Error("No replica set members available for query"); + + // handle undefined pingMs + var lowestPing = finalCandidates[0].runtimeStats['pingMs'] | 0; + + // determine acceptable latency + var acceptable = lowestPing + this.secondaryAcceptableLatencyMS; + + // remove any server responding slower than acceptable + var len = finalCandidates.length; + while(len--) { + if(finalCandidates[len].runtimeStats['pingMs'] > acceptable) { + finalCandidates.splice(len, 1); + } + } + + // If no candidates available return an error + if(finalCandidates.length == 0) + return new Error("No replica set members available for query"); + + // Pick a random acceptable server + return finalCandidates[Math.round(Math.random(1000000) * (finalCandidates.length - 1))].checkoutReader(); +} + +PingStrategy.prototype._pingServer = function(callback) { + var self = this; + + // Ping server function + var pingFunction = function() { + if(self.state == 'disconnected') return; + var addresses = self.replicaset._state.addresses; + + // Grab all servers + var serverKeys = Object.keys(addresses); + + // Number of server entries + var numberOfEntries = serverKeys.length; + + // We got keys + for(var i = 0; i < serverKeys.length; i++) { + + // We got a server instance + var server = addresses[serverKeys[i]]; + + // Create a new server object, avoid using internal connections as they might + // be in an illegal state + new function(serverInstance) { + var options = { poolSize: 1, timeout: 500, auto_reconnect: false }; + var server = new Server(serverInstance.host, serverInstance.port, options); + var db = new self.Db(self.replicaset.db.databaseName, server, { safe: true }); + + db.on("error", done); + + // Open the db instance + db.open(function(err, _db) { + if(err) return done(err, _db); + + // Startup time of the command + var startTime = Date.now(); + + // Execute ping on this connection + db.executeDbCommand({ping:1}, {failFast:true}, function() { + if(null != serverInstance.runtimeStats && serverInstance.isConnected()) { + serverInstance.runtimeStats['pingMs'] = Date.now() - startTime; + } + + done(null, _db); + }) + }) + + function done (err, _db) { + // Close connection + if (_db) _db.close(true); + + // Adjust the number of checks + numberOfEntries--; + + // If we are done with all results coming back trigger ping again + if(0 === numberOfEntries && 'connected' == self.state) { + setTimeout(pingFunction, self.pingInterval); + } + } + }(server); + } + } + + // Start pingFunction + setTimeout(pingFunction, 1000); + + callback && callback(null); +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js new file mode 100644 index 0000000..2e87dbd --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/strategies/statistics_strategy.js @@ -0,0 +1,78 @@ +// The Statistics strategy uses the measure of each end-start time for each +// query executed against the db to calculate the mean, variance and standard deviation +// and pick the server which the lowest mean and deviation +var StatisticsStrategy = exports.StatisticsStrategy = function(replicaset) { + this.replicaset = replicaset; +} + +// Starts any needed code +StatisticsStrategy.prototype.start = function(callback) { + callback && callback(null, null); +} + +StatisticsStrategy.prototype.stop = function(callback) { + callback && callback(null, null); +} + +StatisticsStrategy.prototype.checkoutSecondary = function(tags, secondaryCandidates) { + // Servers are picked based on the lowest ping time and then servers that lower than that + secondaryAcceptableLatencyMS + // Create a list of candidat servers, containing the primary if available + var candidateServers = []; + + // If we have not provided a list of candidate servers use the default setup + if(!Array.isArray(secondaryCandidates)) { + candidateServers = this.replicaset._state.master != null ? [this.replicaset._state.master] : []; + // Add all the secondaries + var keys = Object.keys(this.replicaset._state.secondaries); + for(var i = 0; i < keys.length; i++) { + candidateServers.push(this.replicaset._state.secondaries[keys[i]]) + } + } else { + candidateServers = secondaryCandidates; + } + + // Final list of eligable server + var finalCandidates = []; + + // If we have tags filter by tags + if(tags != null && typeof tags == 'object') { + // If we have an array or single tag selection + var tagObjects = Array.isArray(tags) ? tags : [tags]; + // Iterate over all tags until we find a candidate server + for(var _i = 0; _i < tagObjects.length; _i++) { + // Grab a tag object + var tagObject = tagObjects[_i]; + // Matching keys + var matchingKeys = Object.keys(tagObject); + // Remove any that are not tagged correctly + for(var i = 0; i < candidateServers.length; i++) { + var server = candidateServers[i]; + // If we have tags match + if(server.tags != null) { + var matching = true; + + // Ensure we have all the values + for(var j = 0; j < matchingKeys.length; j++) { + if(server.tags[matchingKeys[j]] != tagObject[matchingKeys[j]]) { + matching = false; + break; + } + } + + // If we have a match add it to the list of matching servers + if(matching) { + finalCandidates.push(server); + } + } + } + } + } else { + // Final array candidates + var finalCandidates = candidateServers; + } + + // If no candidates available return an error + if(finalCandidates.length == 0) return new Error("No replica set members available for query"); + // Pick a random server + return finalCandidates[Math.round(Math.random(1000000) * (finalCandidates.length - 1))].checkoutReader(); +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/url_parser.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/url_parser.js new file mode 100644 index 0000000..9c280b7 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/url_parser.js @@ -0,0 +1,223 @@ +var fs = require('fs'), + ReadPreference = require('./read_preference').ReadPreference; + +exports.parse = function(url, options) { + // Ensure we have a default options object if none set + options = options || {}; + // Variables + var connection_part = ''; + var auth_part = ''; + var query_string_part = ''; + var dbName = 'default'; + + // Must start with mongodb + if(url.indexOf("mongodb://") != 0) + throw Error("URL must be in the format mongodb://user:pass@host:port/dbname"); + // If we have a ? mark cut the query elements off + if(url.indexOf("?") != -1) { + query_string_part = url.substr(url.indexOf("?") + 1); + connection_part = url.substring("mongodb://".length, url.indexOf("?")) + } else { + connection_part = url.substring("mongodb://".length); + } + + // Check if we have auth params + if(connection_part.indexOf("@") != -1) { + auth_part = connection_part.split("@")[0]; + connection_part = connection_part.split("@")[1]; + } + + // Check if the connection string has a db + if(connection_part.indexOf(".sock") != -1) { + if(connection_part.indexOf(".sock/") != -1) { + dbName = connection_part.split(".sock/")[1]; + connection_part = connection_part.split("/", connection_part.indexOf(".sock") + ".sock".length); + } + } else if(connection_part.indexOf("/") != -1) { + dbName = connection_part.split("/")[1]; + connection_part = connection_part.split("/")[0]; + } + + // Result object + var object = {}; + + // Pick apart the authentication part of the string + var authPart = auth_part || ''; + var auth = authPart.split(':', 2); + if(options['uri_decode_auth']){ + auth[0] = decodeURIComponent(auth[0]); + if(auth[1]){ + auth[1] = decodeURIComponent(auth[1]); + } + } + + // Add auth to final object if we have 2 elements + if(auth.length == 2) object.auth = {user: auth[0], password: auth[1]}; + + // Variables used for temporary storage + var hostPart; + var urlOptions; + var servers; + var serverOptions = {socketOptions: {}}; + var dbOptions = {read_preference_tags: []}; + var replSetServersOptions = {socketOptions: {}}; + // Add server options to final object + object.server_options = serverOptions; + object.db_options = dbOptions; + object.rs_options = replSetServersOptions; + object.mongos_options = {}; + + // Let's check if we are using a domain socket + if(url.match(/\.sock/)) { + // Split out the socket part + var domainSocket = url.substring( + url.indexOf("mongodb://") + "mongodb://".length + , url.lastIndexOf(".sock") + ".sock".length); + // Clean out any auth stuff if any + if(domainSocket.indexOf("@") != -1) domainSocket = domainSocket.split("@")[1]; + servers = [{domain_socket: domainSocket}]; + } else { + // Split up the db + hostPart = connection_part; + // Parse all server results + servers = hostPart.split(',').map(function(h) { + var hostPort = h.split(':', 2); + var _host = hostPort[0] || 'localhost'; + var _port = hostPort[1] != null ? parseInt(hostPort[1], 10) : 27017; + // Check for localhost?safe=true style case + if(_host.indexOf("?") != -1) _host = _host.split(/\?/)[0]; + + // Return the mapped object + return {host: _host, port: _port}; + }); + } + + // Get the db name + object.dbName = dbName || 'default'; + // Split up all the options + urlOptions = (query_string_part || '').split(/[&;]/); + // Ugh, we have to figure out which options go to which constructor manually. + urlOptions.forEach(function(opt) { + if(!opt) return; + var splitOpt = opt.split('='), name = splitOpt[0], value = splitOpt[1]; + // Options implementations + switch(name) { + case 'slaveOk': + case 'slave_ok': + serverOptions.slave_ok = (value == 'true'); + break; + case 'maxPoolSize': + case 'poolSize': + serverOptions.poolSize = parseInt(value, 10); + replSetServersOptions.poolSize = parseInt(value, 10); + break; + case 'autoReconnect': + case 'auto_reconnect': + serverOptions.auto_reconnect = (value == 'true'); + break; + case 'minPoolSize': + throw new Error("minPoolSize not supported"); + case 'maxIdleTimeMS': + throw new Error("maxIdleTimeMS not supported"); + case 'waitQueueMultiple': + throw new Error("waitQueueMultiple not supported"); + case 'waitQueueTimeoutMS': + throw new Error("waitQueueTimeoutMS not supported"); + case 'uuidRepresentation': + throw new Error("uuidRepresentation not supported"); + case 'ssl': + if(value == 'prefer') { + serverOptions.socketOptions.ssl = value; + replSetServersOptions.socketOptions.ssl = value; + break; + } + serverOptions.socketOptions.ssl = (value == 'true'); + replSetServersOptions.socketOptions.ssl = (value == 'true'); + break; + case 'replicaSet': + case 'rs_name': + replSetServersOptions.rs_name = value; + break; + case 'reconnectWait': + replSetServersOptions.reconnectWait = parseInt(value, 10); + break; + case 'retries': + replSetServersOptions.retries = parseInt(value, 10); + break; + case 'readSecondary': + case 'read_secondary': + replSetServersOptions.read_secondary = (value == 'true'); + break; + case 'fsync': + dbOptions.fsync = (value == 'true'); + break; + case 'journal': + dbOptions.journal = (value == 'true'); + break; + case 'safe': + dbOptions.safe = (value == 'true'); + break; + case 'nativeParser': + case 'native_parser': + dbOptions.native_parser = (value == 'true'); + break; + case 'connectTimeoutMS': + serverOptions.socketOptions.connectTimeoutMS = parseInt(value, 10); + replSetServersOptions.socketOptions.connectTimeoutMS = parseInt(value, 10); + break; + case 'socketTimeoutMS': + serverOptions.socketOptions.socketTimeoutMS = parseInt(value, 10); + replSetServersOptions.socketOptions.socketTimeoutMS = parseInt(value, 10); + break; + case 'w': + dbOptions.w = parseInt(value, 10); + break; + case 'wtimeoutMS': + dbOptions.wtimeoutMS = parseInt(value, 10); + break; + case 'readPreference': + if(!ReadPreference.isValid(value)) throw new Error("readPreference must be either primary/primaryPreferred/secondary/secondaryPreferred/nearest"); + dbOptions.read_preference = value; + break; + case 'readPreferenceTags': + // Contains the tag object + var tagObject = {}; + if(value == null || value == '') { + dbOptions.read_preference_tags.push(tagObject); + break; + } + + // Split up the tags + var tags = value.split(/\,/); + for(var i = 0; i < tags.length; i++) { + var parts = tags[i].trim().split(/\:/); + tagObject[parts[0]] = parts[1]; + } + + // Set the preferences tags + dbOptions.read_preference_tags.push(tagObject); + break; + default: + break; + } + }); + + // No tags: should be null (not []) + if(dbOptions.read_preference_tags.length === 0) { + dbOptions.read_preference_tags = null; + } + + // Validate if there are an invalid write concern combinations + if((dbOptions.w == -1 || dbOptions.w == 0) && ( + dbOptions.journal == true + || dbOptions.fsync == true + || dbOptions.safe == true)) throw new Error("w set to -1 or 0 cannot be combined with safe/w/journal/fsync") + + // If no read preference set it to primary + if(!dbOptions.read_preference) dbOptions.read_preference = 'primary'; + + // Add servers to result + object.servers = servers; + // Returned parsed object + return object; +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/cursor.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/cursor.js new file mode 100644 index 0000000..f66be71 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/cursor.js @@ -0,0 +1,902 @@ +var QueryCommand = require('./commands/query_command').QueryCommand, + GetMoreCommand = require('./commands/get_more_command').GetMoreCommand, + KillCursorCommand = require('./commands/kill_cursor_command').KillCursorCommand, + Long = require('bson').Long, + ReadPreference = require('./connection/read_preference').ReadPreference, + CursorStream = require('./cursorstream'), + utils = require('./utils'); + +/** + * Constructor for a cursor object that handles all the operations on query result + * using find. This cursor object is unidirectional and cannot traverse backwards. Clients should not be creating a cursor directly, + * but use find to acquire a cursor. + * + * Options + * - **skip** {Number} skip number of documents to skip. + * - **limit** {Number}, limit the number of results to return. -1 has a special meaning and is used by Db.eval. A value of 1 will also be treated as if it were -1. + * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc. + * - **hint** {Object}, hint force the query to use a specific index. + * - **explain** {Boolean}, explain return the explaination of the query. + * - **snapshot** {Boolean}, snapshot Snapshot mode assures no duplicates are returned. + * - **timeout** {Boolean}, timeout allow the query to timeout. + * - **tailable** {Boolean}, tailable allow the cursor to be tailable. + * - **awaitdata** {Boolean}, awaitdata allow the cursor to wait for data, only applicable for tailable cursor. + * - **batchSize** {Number}, batchSize the number of the subset of results to request the database to return for every request. This should initially be greater than 1 otherwise the database will automatically close the cursor. The batch size can be set to 1 with cursorInstance.batchSize after performing the initial query to the database. + * - **raw** {Boolean}, raw return all query documents as raw buffers (default false). + * - **read** {Boolean}, read specify override of read from source (primary/secondary). + * - **slaveOk** {Boolean}, slaveOk, sets the slaveOk flag on the query wire protocol for secondaries. + * - **returnKey** {Boolean}, returnKey only return the index key. + * - **maxScan** {Number}, maxScan limit the number of items to scan. + * - **min** {Number}, min set index bounds. + * - **max** {Number}, max set index bounds. + * - **showDiskLoc** {Boolean}, showDiskLoc show disk location of results. + * - **comment** {String}, comment you can put a $comment field on a query to make looking in the profiler logs simpler. + * - **numberOfRetries** {Number}, numberOfRetries if using awaidata specifies the number of times to retry on timeout. + * - **dbName** {String}, dbName override the default dbName. + * - **tailableRetryInterval** {Number}, tailableRetryInterval specify the miliseconds between getMores on tailable cursor. + * - **exhaust** {Boolean}, exhaust have the server send all the documents at once as getMore packets. + * - **partial** {Boolean}, partial have the sharded system return a partial result from mongos. + * + * @class Represents a Cursor. + * @param {Db} db the database object to work with. + * @param {Collection} collection the collection to query. + * @param {Object} selector the query selector. + * @param {Object} fields an object containing what fields to include or exclude from objects returned. + * @param {Object} [options] additional options for the collection. +*/ +function Cursor(db, collection, selector, fields, options) { + this.db = db; + this.collection = collection; + this.selector = selector; + this.fields = fields; + options = !options ? {} : options; + + this.skipValue = options.skip == null ? 0 : options.skip; + this.limitValue = options.limit == null ? 0 : options.limit; + this.sortValue = options.sort; + this.hint = options.hint; + this.explainValue = options.explain; + this.snapshot = options.snapshot; + this.timeout = options.timeout == null ? true : options.timeout; + this.tailable = options.tailable; + this.awaitdata = options.awaitdata; + this.numberOfRetries = options.numberOfRetries == null ? 5 : options.numberOfRetries; + this.currentNumberOfRetries = this.numberOfRetries; + this.batchSizeValue = options.batchSize == null ? 0 : options.batchSize; + this.slaveOk = options.slaveOk == null ? collection.slaveOk : options.slaveOk; + this.raw = options.raw == null ? false : options.raw; + this.read = options.read == null ? ReadPreference.PRIMARY : options.read; + this.returnKey = options.returnKey; + this.maxScan = options.maxScan; + this.min = options.min; + this.max = options.max; + this.showDiskLoc = options.showDiskLoc; + this.comment = options.comment; + this.tailableRetryInterval = options.tailableRetryInterval || 100; + this.exhaust = options.exhaust || false; + this.partial = options.partial || false; + + this.totalNumberOfRecords = 0; + this.items = []; + this.cursorId = Long.fromInt(0); + + // This name + this.dbName = options.dbName; + + // State variables for the cursor + this.state = Cursor.INIT; + // Keep track of the current query run + this.queryRun = false; + this.getMoreTimer = false; + + // If we are using a specific db execute against it + if(this.dbName != null) { + this.collectionName = this.dbName + "." + this.collection.collectionName; + } else { + this.collectionName = (this.db.databaseName ? this.db.databaseName + "." : '') + this.collection.collectionName; + } +}; + +/** + * Resets this cursor to its initial state. All settings like the query string, + * tailable, batchSizeValue, skipValue and limits are preserved. + * + * @return {Cursor} returns itself with rewind applied. + * @api public + */ +Cursor.prototype.rewind = function() { + var self = this; + + if (self.state != Cursor.INIT) { + if (self.state != Cursor.CLOSED) { + self.close(function() {}); + } + + self.numberOfReturned = 0; + self.totalNumberOfRecords = 0; + self.items = []; + self.cursorId = Long.fromInt(0); + self.state = Cursor.INIT; + self.queryRun = false; + } + + return self; +}; + + +/** + * Returns an array of documents. The caller is responsible for making sure that there + * is enough memory to store the results. Note that the array only contain partial + * results when this cursor had been previouly accessed. In that case, + * cursor.rewind() can be used to reset the cursor. + * + * @param {Function} callback This will be called after executing this method successfully. The first parameter will contain the Error object if an error occured, or null otherwise. The second parameter will contain an array of BSON deserialized objects as a result of the query. + * @return {null} + * @api public + */ +Cursor.prototype.toArray = function(callback) { + var self = this; + + if(!callback) { + throw new Error('callback is mandatory'); + } + + if(this.tailable) { + callback(new Error("Tailable cursor cannot be converted to array"), null); + } else if(this.state != Cursor.CLOSED) { + var items = []; + + this.each(function(err, item) { + if(err != null) return callback(err, null); + + if(item != null && Array.isArray(items)) { + items.push(item); + } else { + var resultItems = items; + items = null; + self.items = []; + // Returns items + callback(err, resultItems); + } + }); + } else { + callback(new Error("Cursor is closed"), null); + } +}; + +/** + * Iterates over all the documents for this cursor. As with **{cursor.toArray}**, + * not all of the elements will be iterated if this cursor had been previouly accessed. + * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike + * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements + * at any given time if batch size is specified. Otherwise, the caller is responsible + * for making sure that the entire result can fit the memory. + * + * @param {Function} callback this will be called for while iterating every document of the query result. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the document. + * @return {null} + * @api public + */ +Cursor.prototype.each = function(callback) { + var self = this; + + if (!callback) { + throw new Error('callback is mandatory'); + } + + if(this.state != Cursor.CLOSED) { + //FIX: stack overflow (on deep callback) (cred: https://github.com/limp/node-mongodb-native/commit/27da7e4b2af02035847f262b29837a94bbbf6ce2) + process.nextTick(function(){ + var s = new Date() + // Fetch the next object until there is no more objects + self.nextObject(function(err, item) { + if(err != null) return callback(err, null); + if(item != null) { + callback(null, item); + self.each(callback); + } else { + // Close the cursor if done + self.state = Cursor.CLOSED; + callback(err, null); + } + }); + }); + } else { + callback(new Error("Cursor is closed"), null); + } +}; + +/** + * Determines how many result the query for this cursor will return + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the number of results or null if an error occured. + * @return {null} + * @api public + */ +Cursor.prototype.count = function(callback) { + this.collection.count(this.selector, callback); +}; + +/** + * Sets the sort parameter of this cursor to the given value. + * + * This method has the following method signatures: + * (keyOrList, callback) + * (keyOrList, direction, callback) + * + * @param {String|Array|Object} keyOrList This can be a string or an array. If passed as a string, the string will be the field to sort. If passed an array, each element will represent a field to be sorted and should be an array that contains the format [string, direction]. + * @param {String|Number} direction this determines how the results are sorted. "asc", "ascending" or 1 for asceding order while "desc", "desceding or -1 for descending order. Note that the strings are case insensitive. + * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.sort = function(keyOrList, direction, callback) { + callback = callback || function(){}; + if(typeof direction === "function") { callback = direction; direction = null; } + + if(this.tailable) { + callback(new Error("Tailable cursor doesn't support sorting"), null); + } else if(this.queryRun == true || this.state == Cursor.CLOSED) { + callback(new Error("Cursor is closed"), null); + } else { + var order = keyOrList; + + if(direction != null) { + order = [[keyOrList, direction]]; + } + + this.sortValue = order; + callback(null, this); + } + return this; +}; + +/** + * Sets the limit parameter of this cursor to the given value. + * + * @param {Number} limit the new limit. + * @param {Function} [callback] this optional callback will be called after executing this method. The first parameter will contain an error object when the limit given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.limit = function(limit, callback) { + if(this.tailable) { + if(callback) { + callback(new Error("Tailable cursor doesn't support limit"), null); + } else { + throw new Error("Tailable cursor doesn't support limit"); + } + } else if(this.queryRun == true || this.state == Cursor.CLOSED) { + if(callback) { + callback(new Error("Cursor is closed"), null); + } else { + throw new Error("Cursor is closed"); + } + } else { + if(limit != null && limit.constructor != Number) { + if(callback) { + callback(new Error("limit requires an integer"), null); + } else { + throw new Error("limit requires an integer"); + } + } else { + this.limitValue = limit; + if(callback) return callback(null, this); + } + } + + return this; +}; + +/** + * Sets the read preference for the cursor + * + * @param {String} the read preference for the cursor, one of Server.READ_PRIMARY, Server.READ_SECONDARY, Server.READ_SECONDARY_ONLY + * @param {Function} [callback] this optional callback will be called after executing this method. The first parameter will contain an error object when the read preference given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.setReadPreference = function(readPreference, tags, callback) { + if(typeof tags == 'function') callback = tags; + + var _mode = readPreference != null && typeof readPreference == 'object' ? readPreference.mode : readPreference; + + if(this.queryRun == true || this.state == Cursor.CLOSED) { + if(callback == null) throw new Error("Cannot change read preference on executed query or closed cursor"); + callback(new Error("Cannot change read preference on executed query or closed cursor")); + } else if(_mode != null && _mode != 'primary' + && _mode != 'secondaryOnly' && _mode != 'secondary' + && _mode != 'nearest' && _mode != 'primaryPreferred' && _mode != 'secondaryPreferred') { + if(callback == null) throw new Error("only readPreference of primary, secondary, secondaryPreferred, primaryPreferred or nearest supported"); + callback(new Error("only readPreference of primary, secondary, secondaryPreferred, primaryPreferred or nearest supported")); + } else { + this.read = readPreference; + if(callback != null) callback(null, this); + } + + return this; +} + +/** + * Sets the skip parameter of this cursor to the given value. + * + * @param {Number} skip the new skip value. + * @param {Function} [callback] this optional callback will be called after executing this method. The first parameter will contain an error object when the skip value given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.skip = function(skip, callback) { + callback = callback || function(){}; + + if(this.tailable) { + callback(new Error("Tailable cursor doesn't support skip"), null); + } else if(this.queryRun == true || this.state == Cursor.CLOSED) { + callback(new Error("Cursor is closed"), null); + } else { + if(skip != null && skip.constructor != Number) { + callback(new Error("skip requires an integer"), null); + } else { + this.skipValue = skip; + callback(null, this); + } + } + + return this; +}; + +/** + * Sets the batch size parameter of this cursor to the given value. + * + * @param {Number} batchSize the new batch size. + * @param {Function} [callback] this optional callback will be called after executing this method. The first parameter will contain an error object when the batchSize given is not a valid number or when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. + * @return {Cursor} an instance of this object. + * @api public + */ +Cursor.prototype.batchSize = function(batchSize, callback) { + if(this.state == Cursor.CLOSED) { + if(callback != null) { + return callback(new Error("Cursor is closed"), null); + } else { + throw new Error("Cursor is closed"); + } + } else if(batchSize != null && batchSize.constructor != Number) { + if(callback != null) { + return callback(new Error("batchSize requires an integer"), null); + } else { + throw new Error("batchSize requires an integer"); + } + } else { + this.batchSizeValue = batchSize; + if(callback != null) return callback(null, this); + } + + return this; +}; + +/** + * The limit used for the getMore command + * + * @return {Number} The number of records to request per batch. + * @ignore + * @api private + */ +var limitRequest = function(self) { + var requestedLimit = self.limitValue; + var absLimitValue = Math.abs(self.limitValue); + var absBatchValue = Math.abs(self.batchSizeValue); + + if(absLimitValue > 0) { + if (absBatchValue > 0) { + requestedLimit = Math.min(absLimitValue, absBatchValue); + } + } else { + requestedLimit = self.batchSizeValue; + } + + return requestedLimit; +}; + + +/** + * Generates a QueryCommand object using the parameters of this cursor. + * + * @return {QueryCommand} The command object + * @ignore + * @api private + */ +var generateQueryCommand = function(self) { + // Unpack the options + var queryOptions = QueryCommand.OPTS_NONE; + if(!self.timeout) { + queryOptions |= QueryCommand.OPTS_NO_CURSOR_TIMEOUT; + } + + if(self.tailable != null) { + queryOptions |= QueryCommand.OPTS_TAILABLE_CURSOR; + self.skipValue = self.limitValue = 0; + + // if awaitdata is set + if(self.awaitdata != null) { + queryOptions |= QueryCommand.OPTS_AWAIT_DATA; + } + } + + if(self.exhaust) { + queryOptions |= QueryCommand.OPTS_EXHAUST; + } + + if(self.slaveOk) { + queryOptions |= QueryCommand.OPTS_SLAVE; + } + + if(self.partial) { + queryOptions |= QueryCommand.OPTS_PARTIAL; + } + + // limitValue of -1 is a special case used by Db#eval + var numberToReturn = self.limitValue == -1 ? -1 : limitRequest(self); + + // Check if we need a special selector + if(self.sortValue != null || self.explainValue != null || self.hint != null || self.snapshot != null + || self.returnKey != null || self.maxScan != null || self.min != null || self.max != null + || self.showDiskLoc != null || self.comment != null) { + + // Build special selector + var specialSelector = {'$query':self.selector}; + if(self.sortValue != null) specialSelector['orderby'] = utils.formattedOrderClause(self.sortValue); + if(self.hint != null && self.hint.constructor == Object) specialSelector['$hint'] = self.hint; + if(self.snapshot != null) specialSelector['$snapshot'] = true; + if(self.returnKey != null) specialSelector['$returnKey'] = self.returnKey; + if(self.maxScan != null) specialSelector['$maxScan'] = self.maxScan; + if(self.min != null) specialSelector['$min'] = self.min; + if(self.max != null) specialSelector['$max'] = self.max; + if(self.showDiskLoc != null) specialSelector['$showDiskLoc'] = self.showDiskLoc; + if(self.comment != null) specialSelector['$comment'] = self.comment; + // If we have explain set only return a single document with automatic cursor close + if(self.explainValue != null) { + numberToReturn = (-1)*Math.abs(numberToReturn); + specialSelector['$explain'] = true; + } + + // Return the query + return new QueryCommand(self.db, self.collectionName, queryOptions, self.skipValue, numberToReturn, specialSelector, self.fields); + } else { + return new QueryCommand(self.db, self.collectionName, queryOptions, self.skipValue, numberToReturn, self.selector, self.fields); + } +}; + +/** + * @return {Object} Returns an object containing the sort value of this cursor with + * the proper formatting that can be used internally in this cursor. + * @ignore + * @api private + */ +Cursor.prototype.formattedOrderClause = function() { + return utils.formattedOrderClause(this.sortValue); +}; + +/** + * Converts the value of the sort direction into its equivalent numerical value. + * + * @param sortDirection {String|number} Range of acceptable values: + * 'ascending', 'descending', 'asc', 'desc', 1, -1 + * + * @return {number} The equivalent numerical value + * @throws Error if the given sortDirection is invalid + * @ignore + * @api private + */ +Cursor.prototype.formatSortValue = function(sortDirection) { + return utils.formatSortValue(sortDirection); +}; + +/** + * Gets the next document from the cursor. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object on error while the second parameter will contain a document from the returned result or null if there are no more results. + * @api public + */ +Cursor.prototype.nextObject = function(callback) { + var self = this; + + if(self.state == Cursor.INIT) { + var cmd; + try { + cmd = generateQueryCommand(self); + } catch (err) { + return callback(err, null); + } + + // Execute command + var commandHandler = function(err, result) { + self.state = Cursor.OPEN; + // console.dir(err) + if(err != null && result == null) return callback(err, null); + + if(!err && result.documents[0] && result.documents[0]['$err']) { + return self.close(function() {callback(result.documents[0]['$err'], null);}); + } + + self.queryRun = true; + self.state = Cursor.OPEN; // Adjust the state of the cursor + self.cursorId = result.cursorId; + self.totalNumberOfRecords = result.numberReturned; + + // Add the new documents to the list of items, using forloop to avoid + // new array allocations and copying + for(var i = 0; i < result.documents.length; i++) { + self.items.push(result.documents[i]); + } + + // Ignore callbacks until the cursor is dead for exhausted + if(self.exhaust && result.cursorId.toString() == "0") { + self.nextObject(callback); + } else if(self.exhaust == false || self.exhaust == null) { + self.nextObject(callback); + } + }; + + // If we have no connection set on this cursor check one out + if(self.connection == null) { + try { + self.connection = this.read == null ? self.db.serverConfig.checkoutWriter() : self.db.serverConfig.checkoutReader(this.read); + } catch(err) { + return callback(err, null); + } + } + + // Execute the command + self.db._executeQueryCommand(cmd, {exhaust: self.exhaust, raw:self.raw, read:this.read, connection:self.connection}, commandHandler); + // Set the command handler to null + commandHandler = null; + } else if(self.items.length) { + callback(null, self.items.shift()); + } else if(self.cursorId.greaterThan(Long.fromInt(0))) { + getMore(self, callback); + } else { + // Force cursor to stay open + return self.close(function() {callback(null, null);}); + } +} + +/** + * Gets more results from the database if any. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain an error object on error while the second parameter will contain a document from the returned result or null if there are no more results. + * @ignore + * @api private + */ +var getMore = function(self, callback) { + var limit = 0; + + if(self.state == Cursor.GET_MORE) return callback(null, null); + + // Set get more in progress + self.state = Cursor.GET_MORE; + + // Set options + if (!self.tailable && self.limitValue > 0) { + limit = self.limitValue - self.totalNumberOfRecords; + if (limit < 1) { + self.close(function() {callback(null, null);}); + return; + } + } + + try { + var getMoreCommand = new GetMoreCommand( + self.db + , self.collectionName + , limitRequest(self) + , self.cursorId + ); + + // Set up options + var options = {read: self.read, raw: self.raw, connection:self.connection }; + + // Execute the command + self.db._executeQueryCommand(getMoreCommand, options, function(err, result) { + // Get more done + self.state = Cursor.OPEN; + + try { + if(err != null) { + return callback(err, null); + } + + var isDead = 1 === result.responseFlag && result.cursorId.isZero(); + + self.cursorId = result.cursorId; + self.totalNumberOfRecords += result.numberReturned; + + // Determine if there's more documents to fetch + if(result.numberReturned > 0) { + if (self.limitValue > 0) { + var excessResult = self.totalNumberOfRecords - self.limitValue; + + if (excessResult > 0) { + result.documents.splice(-1 * excessResult, excessResult); + } + } + + // Reset the tries for awaitdata if we are using it + self.currentNumberOfRetries = self.numberOfRetries; + // Get the documents + for(var i = 0; i < result.documents.length; i++) { + self.items.push(result.documents[i]); + } + + // result = null; + callback(null, self.items.shift()); + } else if(self.tailable && !isDead && self.awaitdata) { + // Excute the tailable cursor once more, will timeout after ~4 sec if awaitdata used + self.currentNumberOfRetries = self.currentNumberOfRetries - 1; + if(self.currentNumberOfRetries == 0) { + self.close(function() { + callback(new Error("tailable cursor timed out"), null); + }); + } else { + process.nextTick(function() { getMore(self, callback); }); + } + } else if(self.tailable && !isDead) { + self.getMoreTimer = setTimeout(function() { getMore(self, callback); }, self.tailableRetryInterval); + } else { + self.close(function() {callback(null, null); }); + } + + result = null; + } catch(err) { + callback(err, null); + } + }); + + getMoreCommand = null; + } catch(err) { + // Get more done + self.state = Cursor.OPEN; + + var handleClose = function() { + callback(err, null); + }; + + self.close(handleClose); + handleClose = null; + } +} + +/** + * Gets a detailed information about how the query is performed on this cursor and how + * long it took the database to process it. + * + * @param {Function} callback this will be called after executing this method. The first parameter will always be null while the second parameter will be an object containing the details. + * @api public + */ +Cursor.prototype.explain = function(callback) { + var limit = (-1)*Math.abs(this.limitValue); + + // * - **skip** {Number} skip number of documents to skip. + // * - **limit** {Number}, limit the number of results to return. -1 has a special meaning and is used by Db.eval. A value of 1 will also be treated as if it were -1. + // * - **hint** {Object}, hint force the query to use a specific index. + // * - **explain** {Boolean}, explain return the explaination of the query. + // * - **slaveOk** {Boolean}, slaveOk, sets the slaveOk flag on the query wire protocol for secondaries. + // * - **snapshot** {Boolean}, snapshot Snapshot mode assures no duplicates are returned. + // * - **timeout** {Boolean}, timeout allow the query to timeout. + // * - **tailable** {Boolean}, tailable allow the cursor to be tailable. + // * - **awaitdata** {Boolean}, awaitdata allow the cursor to wait for data, only applicable for tailable cursor. + // * - **batchSize** {Number}, batchSize the number of the subset of results to request the database to return for every request. This should initially be greater than 1 otherwise the database will automatically close the cursor. The batch size can be set to 1 with cursorInstance.batchSize after performing the initial query to the database. + // * - **raw** {Boolean}, raw return all query documents as raw buffers (default false). + // * - **read** {Boolean}, read specify override of read from source (primary/secondary). + // * - **returnKey** {Boolean}, returnKey only return the index key. + // * - **maxScan** {Number}, maxScan limit the number of items to scan. + // * - **min** {Number}, min set index bounds. + // * - **max** {Number}, max set index bounds. + // * - **showDiskLoc** {Boolean}, showDiskLoc show disk location of results. + // * - **comment** {String}, comment you can put a $comment field on a query to make looking in the profiler logs simpler. + // * - **numberOfRetries** {Number}, numberOfRetries if using awaidata specifies the number of times to retry on timeout. + // * - **dbName** {String}, dbName override the default dbName. + // * - **tailableRetryInterval** {Number}, tailableRetryInterval specify the miliseconds between getMores on tailable cursor. + // * - **exhaust** {Boolean}, exhaust have the server send all the documents at once as getMore packets. + // * - **partial** {Boolean}, partial have the sharded system return a partial result from mongos. + + // * - **sort** {Array | Object}, set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc. + +// function Cursor(db, collection, selector, fields, skip, limit +// - , sort, hint, explain, snapshot, timeout, tailable, batchSize, slaveOk, raw, read +// - , returnKey, maxScan, min, max, showDiskLoc, comment, awaitdata, numberOfRetries, dbName, tailableRetry + + // Create a new cursor and fetch the plan + var cursor = new Cursor(this.db, this.collection, this.selector, this.fields, { + skip: this.skipValue + , limit:limit + , sort: this.sortValue + , hint: this.hint + , explain: true + , snapshot: this.snapshot + , timeout: this.timeout + , tailable: this.tailable + , batchSize: this.batchSizeValue + , slaveOk: this.slaveOk + , raw: this.raw + , read: this.read + , returnKey: this.returnKey + , maxScan: this.maxScan + , min: this.min + , max: this.max + , showDiskLoc: this.showDiskLoc + , comment: this.comment + , awaitdata: this.awaitdata + , numberOfRetries: this.numberOfRetries + , dbName: this.dbName + }); + + // Fetch the explaination document + cursor.nextObject(function(err, item) { + if(err != null) return callback(err, null); + // close the cursor + cursor.close(function(err, result) { + if(err != null) return callback(err, null); + callback(null, item); + }); + }); +}; + +/** + * @ignore + */ +Cursor.prototype.streamRecords = function(options) { + console.log("[WARNING] streamRecords method is deprecated, please use stream method which is much faster"); + var args = Array.prototype.slice.call(arguments, 0); + options = args.length ? args.shift() : {}; + + var + self = this, + stream = new process.EventEmitter(), + recordLimitValue = this.limitValue || 0, + emittedRecordCount = 0, + queryCommand = generateQueryCommand(self); + + // see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol + queryCommand.numberToReturn = options.fetchSize ? options.fetchSize : 500; + // Execute the query + execute(queryCommand); + + function execute(command) { + self.db._executeQueryCommand(command, {exhaust: self.exhaust, read:self.read, raw:self.raw, connection:self.connection}, function(err,result) { + if(err) { + stream.emit('error', err); + self.close(function(){}); + return; + } + + if (!self.queryRun && result) { + self.queryRun = true; + self.cursorId = result.cursorId; + self.state = Cursor.OPEN; + self.getMoreCommand = new GetMoreCommand(self.db, self.collectionName, queryCommand.numberToReturn, result.cursorId); + } + + var resflagsMap = { + CursorNotFound:1<<0, + QueryFailure:1<<1, + ShardConfigStale:1<<2, + AwaitCapable:1<<3 + }; + + if(result.documents && result.documents.length && !(result.responseFlag & resflagsMap.QueryFailure)) { + try { + result.documents.forEach(function(doc){ + if(recordLimitValue && emittedRecordCount>=recordLimitValue) { + throw("done"); + } + emittedRecordCount++; + stream.emit('data', doc); + }); + } catch(err) { + if (err != "done") { throw err; } + else { + self.close(function(){ + stream.emit('end', recordLimitValue); + }); + self.close(function(){}); + return; + } + } + // rinse & repeat + execute(self.getMoreCommand); + } else { + self.close(function(){ + stream.emit('end', recordLimitValue); + }); + } + }); + } + + return stream; +}; + +/** + * Returns a Node ReadStream interface for this cursor. + * + * @return {CursorStream} returns a stream object. + * @api public + */ +Cursor.prototype.stream = function stream () { + return new CursorStream(this); +} + +/** + * Close the cursor. + * + * @param {Function} callback this will be called after executing this method. The first parameter will always contain null while the second parameter will contain a reference to this cursor. + * @return {null} + * @api public + */ +Cursor.prototype.close = function(callback) { + var self = this + this.getMoreTimer && clearTimeout(this.getMoreTimer); + // Close the cursor if not needed + if(this.cursorId instanceof Long && this.cursorId.greaterThan(Long.fromInt(0))) { + try { + var command = new KillCursorCommand(this.db, [this.cursorId]); + // Added an empty callback to ensure we don't throw any null exceptions + this.db._executeQueryCommand(command, {read:self.read, raw:self.raw, connection:self.connection}, function() {}); + } catch(err) {} + } + + // Null out the connection + self.connection = null; + // Reset cursor id + this.cursorId = Long.fromInt(0); + // Set to closed status + this.state = Cursor.CLOSED; + + if(callback) { + callback(null, self); + self.items = []; + } + + return this; +}; + +/** + * Check if the cursor is closed or open. + * + * @return {Boolean} returns the state of the cursor. + * @api public + */ +Cursor.prototype.isClosed = function() { + return this.state == Cursor.CLOSED ? true : false; +}; + +/** + * Init state + * + * @classconstant INIT + **/ +Cursor.INIT = 0; + +/** + * Cursor open + * + * @classconstant OPEN + **/ +Cursor.OPEN = 1; + +/** + * Cursor closed + * + * @classconstant CLOSED + **/ +Cursor.CLOSED = 2; + +/** + * Cursor performing a get more + * + * @classconstant OPEN + **/ +Cursor.GET_MORE = 3; + +/** + * @ignore + * @api private + */ +exports.Cursor = Cursor; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/cursorstream.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/cursorstream.js new file mode 100644 index 0000000..ee0cd80 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/cursorstream.js @@ -0,0 +1,151 @@ +/** + * Module dependecies. + */ +var Stream = require('stream').Stream; + +/** + * CursorStream + * + * Returns a stream interface for the **cursor**. + * + * Events + * - **data** {function(item) {}} the data event triggers when a document is ready. + * - **error** {function(err) {}} the error event triggers if an error happens. + * - **close** {function() {}} the end event triggers when there is no more documents available. + * + * @class Represents a CursorStream. + * @param {Cursor} cursor a cursor object that the stream wraps. + * @return {Stream} + */ +function CursorStream(cursor) { + if(!(this instanceof CursorStream)) return new CursorStream(cursor); + + Stream.call(this); + + this.readable = true; + this.paused = false; + this._cursor = cursor; + this._destroyed = null; + + // give time to hook up events + var self = this; + process.nextTick(function () { + self._init(); + }); +} + +/** + * Inherit from Stream + * @ignore + * @api private + */ +CursorStream.prototype.__proto__ = Stream.prototype; + +/** + * Flag stating whether or not this stream is readable. + */ +CursorStream.prototype.readable; + +/** + * Flag stating whether or not this stream is paused. + */ +CursorStream.prototype.paused; + +/** + * Initialize the cursor. + * @ignore + * @api private + */ +CursorStream.prototype._init = function () { + if (this._destroyed) return; + this._next(); +} + +/** + * Pull the next document from the cursor. + * @ignore + * @api private + */ +CursorStream.prototype._next = function () { + if (this.paused || this._destroyed) return; + + var self = this; + // Get the next object + process.nextTick(function() { + self._cursor.nextObject(function (err, doc) { + self._onNextObject(err, doc); + }); + }); +} + +/** + * Handle each document as its returned from the cursor. + * @ignore + * @api private + */ +CursorStream.prototype._onNextObject = function (err, doc) { + if(err) return this.destroy(err); + + // when doc is null we hit the end of the cursor + if(!doc && (this._cursor.state == 1 || this._cursor.state == 2)) { + this.emit('end') + return this.destroy(); + } else if(doc) { + this.emit('data', doc); + this._next(); + } +} + +/** + * Pauses the stream. + * + * @api public + */ +CursorStream.prototype.pause = function () { + this.paused = true; +} + +/** + * Resumes the stream. + * + * @api public + */ +CursorStream.prototype.resume = function () { + var self = this; + + // Don't do anything if we are not paused + if(!this.paused) return; + if(!this._cursor.state == 3) return; + + process.nextTick(function() { + self.paused = false; + // Only trigger more fetching if the cursor is open + self._next(); + }) +} + +/** + * Destroys the stream, closing the underlying + * cursor. No more events will be emitted. + * + * @api public + */ +CursorStream.prototype.destroy = function (err) { + if (this._destroyed) return; + this._destroyed = true; + this.readable = false; + + this._cursor.close(); + + if (err) { + this.emit('error', err); + } + + this.emit('close'); +} + +// TODO - maybe implement the raw option to pass binary? +//CursorStream.prototype.setEncoding = function () { +//} + +module.exports = exports = CursorStream; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js new file mode 100644 index 0000000..024f65f --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/db.js @@ -0,0 +1,2212 @@ +/** + * Module dependencies. + * @ignore + */ +var QueryCommand = require('./commands/query_command').QueryCommand, + DbCommand = require('./commands/db_command').DbCommand, + MongoReply = require('./responses/mongo_reply').MongoReply, + Admin = require('./admin').Admin, + Collection = require('./collection').Collection, + Server = require('./connection/server').Server, + ReplSet = require('./connection/repl_set').ReplSet, + ReadPreference = require('./connection/read_preference').ReadPreference, + Mongos = require('./connection/mongos').Mongos, + Cursor = require('./cursor').Cursor, + EventEmitter = require('events').EventEmitter, + inherits = require('util').inherits, + crypto = require('crypto'), + parse = require('./connection/url_parser').parse; + +/** + * Internal class for callback storage + * @ignore + */ +var CallbackStore = function() { + // Make class an event emitter + EventEmitter.call(this); + // Add a info about call variable + this._notReplied = {}; +} + +/** + * @ignore + */ +inherits(CallbackStore, EventEmitter); + +/** + * Create a new Db instance. + * + * Options + * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **native_parser** {Boolean, default:false}, use c++ bson parser. + * - **forceServerObjectId** {Boolean, default:false}, force server to create _id fields instead of client. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * - **serializeFunctions** {Boolean, default:false}, serialize functions. + * - **raw** {Boolean, default:false}, peform operations using raw bson buffers. + * - **recordQueryStats** {Boolean, default:false}, record query statistics during execution. + * - **retryMiliSeconds** {Number, default:5000}, number of miliseconds between retries. + * - **numberOfRetries** {Number, default:5}, number of retries off connection. + * - **logger** {Object, default:null}, an object representing a logger that you want to use, needs to support functions debug, log, error **({error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}})**. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @class Represents a Db + * @param {String} databaseName name of the database. + * @param {Object} serverConfig server config object. + * @param {Object} [options] additional options for the collection. + */ +function Db(databaseName, serverConfig, options) { + if(!(this instanceof Db)) return new Db(databaseName, serverConfig, options); + + EventEmitter.call(this); + this.databaseName = databaseName; + this.serverConfig = serverConfig; + this.options = options == null ? {} : options; + // State to check against if the user force closed db + this._applicationClosed = false; + // Fetch the override flag if any + var overrideUsedFlag = this.options['override_used_flag'] == null ? false : this.options['override_used_flag']; + + // Verify that nobody is using this config + if(!overrideUsedFlag && this.serverConfig != null && typeof this.serverConfig == 'object' && this.serverConfig._isUsed && this.serverConfig._isUsed()) { + throw new Error("A Server or ReplSet instance cannot be shared across multiple Db instances"); + } else if(!overrideUsedFlag && typeof this.serverConfig == 'object'){ + // Set being used + this.serverConfig._used = true; + } + + // Ensure we have a valid db name + validateDatabaseName(databaseName); + + // Contains all the connections for the db + try { + this.native_parser = this.options.native_parser; + // The bson lib + var bsonLib = this.bsonLib = this.options.native_parser ? require('bson').BSONNative : require('bson').BSONPure; + // Fetch the serializer object + var BSON = bsonLib.BSON; + // Create a new instance + this.bson = new BSON([bsonLib.Long, bsonLib.ObjectID, bsonLib.Binary, bsonLib.Code, bsonLib.DBRef, bsonLib.Symbol, bsonLib.Double, bsonLib.Timestamp, bsonLib.MaxKey, bsonLib.MinKey]); + // Backward compatibility to access types + this.bson_deserializer = bsonLib; + this.bson_serializer = bsonLib; + } catch (err) { + // If we tried to instantiate the native driver + var msg = "Native bson parser not compiled, please compile " + + "or avoid using native_parser=true"; + throw Error(msg); + } + + // Internal state of the server + this._state = 'disconnected'; + + this.pkFactory = this.options.pk == null ? bsonLib.ObjectID : this.options.pk; + this.forceServerObjectId = this.options.forceServerObjectId != null ? this.options.forceServerObjectId : false; + + // Added safe + this.safe = this.options.safe == null ? false : this.options.safe; + + // If we have not specified a "safe mode" we just print a warning to the console + if(this.options.safe == null && this.options.w == null && this.options.journal == null && this.options.fsync == null) { + console.log("========================================================================================"); + console.log("= Please ensure that you set the default write concern for the database by setting ="); + console.log("= one of the options ="); + console.log("= ="); + console.log("= w: (value of > -1 or the string 'majority'), where < 1 means ="); + console.log("= no write acknowlegement ="); + console.log("= journal: true/false, wait for flush to journal before acknowlegement ="); + console.log("= fsync: true/false, wait for flush to file system before acknowlegement ="); + console.log("= ="); + console.log("= For backward compatibility safe is still supported and ="); + console.log("= allows values of [true | false | {j:true} | {w:n, wtimeout:n} | {fsync:true}] ="); + console.log("= the default value is false which means the driver receives does not ="); + console.log("= return the information of the success/error of the insert/update/remove ="); + console.log("= ="); + console.log("= ex: new Db(new Server('localhost', 27017), {safe:false}) ="); + console.log("= ="); + console.log("= http://www.mongodb.org/display/DOCS/getLastError+Command ="); + console.log("= ="); + console.log("= The default of no acknowlegement will change in the very near future ="); + console.log("= ="); + console.log("= This message will disappear when the default safe is set on the driver Db ="); + console.log("========================================================================================"); + } + + // Internal states variables + this.notReplied ={}; + this.isInitializing = true; + this.auths = []; + this.openCalled = false; + + // Command queue, keeps a list of incoming commands that need to be executed once the connection is up + this.commands = []; + + // Contains all the callbacks + this._callBackStore = new CallbackStore(); + + // Set up logger + this.logger = this.options.logger != null + && (typeof this.options.logger.debug == 'function') + && (typeof this.options.logger.error == 'function') + && (typeof this.options.logger.log == 'function') + ? this.options.logger : {error:function(message, object) {}, log:function(message, object) {}, debug:function(message, object) {}}; + // Allow slaveOk + this.slaveOk = this.options["slave_ok"] == null ? false : this.options["slave_ok"]; + + var self = this; + // Associate the logger with the server config + this.serverConfig.logger = this.logger; + this.tag = new Date().getTime(); + // Just keeps list of events we allow + this.eventHandlers = {error:[], parseError:[], poolReady:[], message:[], close:[]}; + + // Controls serialization options + this.serializeFunctions = this.options.serializeFunctions != null ? this.options.serializeFunctions : false; + + // Raw mode + this.raw = this.options.raw != null ? this.options.raw : false; + + // Record query stats + this.recordQueryStats = this.options.recordQueryStats != null ? this.options.recordQueryStats : false; + + // If we have server stats let's make sure the driver objects have it enabled + if(this.recordQueryStats == true) { + this.serverConfig.enableRecordQueryStats(true); + } + + // Retry information + this.retryMiliSeconds = this.options.retryMiliSeconds != null ? this.options.retryMiliSeconds : 1000; + this.numberOfRetries = this.options.numberOfRetries != null ? this.options.numberOfRetries : 60; + + // Set default read preference if any + this.readPreference = this.options.readPreference; +}; + +/** + * @ignore + */ +function validateDatabaseName(databaseName) { + if(typeof databaseName !== 'string') throw new Error("database name must be a string"); + if(databaseName.length === 0) throw new Error("database name cannot be the empty string"); + + var invalidChars = [" ", ".", "$", "/", "\\"]; + for(var i = 0; i < invalidChars.length; i++) { + if(databaseName.indexOf(invalidChars[i]) != -1) throw new Error("database names cannot contain the character '" + invalidChars[i] + "'"); + } +} + +/** + * @ignore + */ +inherits(Db, EventEmitter); + +/** + * Initialize the database connection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the index information or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.open = function(callback) { + var self = this; + + // Check that the user has not called this twice + if(this.openCalled) { + // Close db + this.close(); + // Throw error + throw new Error("db object already connecting, open cannot be called multiple times"); + } + + // If we have a specified read preference + if(this.readPreference != null) this.serverConfig.setReadPreference(this.readPreference); + + // Set that db has been opened + this.openCalled = true; + + // Set the status of the server + self._state = 'connecting'; + // Set up connections + if(self.serverConfig instanceof Server || self.serverConfig instanceof ReplSet || self.serverConfig instanceof Mongos) { + self.serverConfig.connect(self, {firstCall: true}, function(err, result) { + if(err != null) { + // Set that db has been closed + self.openCalled = false; + // Return error from connection + return callback(err, null); + } + // Set the status of the server + self._state = 'connected'; + // If we have queued up commands execute a command to trigger replays + if(self.commands.length > 0) _execute_queued_command(self); + // Callback + return callback(null, self); + }); + } else { + return callback(Error("Server parameter must be of type Server, ReplSet or Mongos"), null); + } +}; + +// Execute any baked up commands +var _execute_queued_command = function(self) { + // Execute any backed up commands + process.nextTick(function() { + // Execute any backed up commands + while(self.commands.length > 0) { + // Fetch the command + var command = self.commands.shift(); + // Execute based on type + if(command['type'] == 'query') { + __executeQueryCommand(self, command['db_command'], command['options'], command['callback']); + } else if(command['type'] == 'insert') { + __executeInsertCommand(self, command['db_command'], command['options'], command['callback']); + } + } + }); +} + +/** + * Create a new Db instance sharing the current socket connections. + * + * @param {String} dbName the name of the database we want to use. + * @return {Db} a db instance using the new database. + * @api public + */ +Db.prototype.db = function(dbName) { + // Copy the options and add out internal override of the not shared flag + var options = {}; + for(var key in this.options) { + options[key] = this.options[key]; + } + // Add override flag + options['override_used_flag'] = true; + // Create a new db instance + var newDbInstance = new Db(dbName, this.serverConfig, options); + //copy over any auths, we may need them for reconnecting + if (this.serverConfig.db) { + newDbInstance.auths = this.serverConfig.db.auths; + } + // Add the instance to the list of approved db instances + var allServerInstances = this.serverConfig.allServerInstances(); + // Add ourselves to all server callback instances + for(var i = 0; i < allServerInstances.length; i++) { + var server = allServerInstances[i]; + server.dbInstances.push(newDbInstance); + } + // Return new db object + return newDbInstance; +} + +/** + * Close the current db connection, including all the child db instances. Emits close event if no callback is provided. + * + * @param {Boolean} [forceClose] connection can never be reused. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.close = function(forceClose, callback) { + var self = this; + // Ensure we force close all connections + this._applicationClosed = false; + + if(typeof forceClose == 'function') { + callback = forceClose; + } else if(typeof forceClose == 'boolean') { + this._applicationClosed = forceClose; + } + + // Remove all listeners and close the connection + this.serverConfig.close(function(err, result) { + // Emit the close event + if(typeof callback !== 'function') self.emit("close"); + + // Emit close event across all db instances sharing the sockets + var allServerInstances = self.serverConfig.allServerInstances(); + // Fetch the first server instance + if(Array.isArray(allServerInstances) && allServerInstances.length > 0) { + var server = allServerInstances[0]; + // For all db instances signal all db instances + if(Array.isArray(server.dbInstances) && server.dbInstances.length > 1) { + for(var i = 0; i < server.dbInstances.length; i++) { + var dbInstance = server.dbInstances[i]; + // Check if it's our current db instance and skip if it is + if(dbInstance.databaseName !== self.databaseName && dbInstance.tag !== self.tag) { + server.dbInstances[i].emit("close"); + } + } + } + } + + // Remove all listeners + self.removeAllEventListeners(); + // You can reuse the db as everything is shut down + self.openCalled = false; + // If we have a callback call it + if(callback) callback(err, result); + }); +}; + +/** + * Access the Admin database + * + * @param {Function} [callback] returns the results. + * @return {Admin} the admin db object. + * @api public + */ +Db.prototype.admin = function(callback) { + if(callback == null) return new Admin(this); + callback(null, new Admin(this)); +}; + +/** + * Returns a cursor to all the collection information. + * + * @param {String} [collectionName] the collection name we wish to retrieve the information from. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the options or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.collectionsInfo = function(collectionName, callback) { + if(callback == null && typeof collectionName == 'function') { callback = collectionName; collectionName = null; } + // Create selector + var selector = {}; + // If we are limiting the access to a specific collection name + if(collectionName != null) selector.name = this.databaseName + "." + collectionName; + + // Return Cursor + // callback for backward compatibility + if(callback) { + callback(null, new Cursor(this, new Collection(this, DbCommand.SYSTEM_NAMESPACE_COLLECTION), selector)); + } else { + return new Cursor(this, new Collection(this, DbCommand.SYSTEM_NAMESPACE_COLLECTION), selector); + } +}; + +/** + * Get the list of all collection names for the specified db + * + * Options + * - **namesOnly** {String, default:false}, Return only the full collection namespace. + * + * @param {String} [collectionName] the collection name we wish to filter by. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the collection names or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.collectionNames = function(collectionName, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + collectionName = args.length ? args.shift() : null; + options = args.length ? args.shift() : {}; + + // Ensure no breaking behavior + if(collectionName != null && typeof collectionName == 'object') { + options = collectionName; + collectionName = null; + } + + // Let's make our own callback to reuse the existing collections info method + self.collectionsInfo(collectionName, function(err, cursor) { + if(err != null) return callback(err, null); + + cursor.toArray(function(err, documents) { + if(err != null) return callback(err, null); + + // List of result documents that have been filtered + var filtered_documents = documents.filter(function(document) { + return !(document.name.indexOf(self.databaseName) == -1 || document.name.indexOf('$') != -1); + }); + + // If we are returning only the names + if(options.namesOnly) { + filtered_documents = filtered_documents.map(function(document) { return document.name }); + } + + // Return filtered items + callback(null, filtered_documents); + }); + }); +}; + +/** + * Fetch a specific collection (containing the actual collection information) + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * - **raw** {Boolean, default:false}, perform all operations using raw bson objects. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **strict**, (Boolean, default:false) throws and error if collection already exists + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * + * @param {String} collectionName the collection name we wish to access. + * @param {Object} [options] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the collection or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.collection = function(collectionName, options, callback) { + var self = this; + if(typeof options === "function") { callback = options; options = {}; } + // Execute safe + + if(options && (options.strict)) { + self.collectionNames(collectionName, function(err, collections) { + if(err != null) return callback(err, null); + + if(collections.length == 0) { + return callback(new Error("Collection " + collectionName + " does not exist. Currently in safe mode."), null); + } else { + try { + var collection = new Collection(self, collectionName, self.pkFactory, options); + } catch(err) { + return callback(err, null); + } + return callback(null, collection); + } + }); + } else { + try { + var collection = new Collection(self, collectionName, self.pkFactory, options); + } catch(err) { + if(callback == null) { + throw err; + } else { + return callback(err, null); + } + } + + // If we have no callback return collection object + return callback == null ? collection : callback(null, collection); + } +}; + +/** + * Fetch all collections for the current db. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the collections or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.collections = function(callback) { + var self = this; + // Let's get the collection names + self.collectionNames(function(err, documents) { + if(err != null) return callback(err, null); + var collections = []; + documents.forEach(function(document) { + collections.push(new Collection(self, document.name.replace(self.databaseName + ".", ''), self.pkFactory)); + }); + // Return the collection objects + callback(null, collections); + }); +}; + +/** + * Evaluate javascript on the server + * + * Options + * - **nolock** {Boolean, default:false}, Tell MongoDB not to block on the evaulation of the javascript. + * + * @param {Code} code javascript to execute on server. + * @param {Object|Array} [parameters] the parameters for the call. + * @param {Object} [options] the options + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from eval or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.eval = function(code, parameters, options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + parameters = args.length ? args.shift() : parameters; + options = args.length ? args.shift() : {}; + + var finalCode = code; + var finalParameters = []; + // If not a code object translate to one + if(!(finalCode instanceof this.bsonLib.Code)) { + finalCode = new this.bsonLib.Code(finalCode); + } + + // Ensure the parameters are correct + if(parameters != null && parameters.constructor != Array && typeof parameters !== 'function') { + finalParameters = [parameters]; + } else if(parameters != null && parameters.constructor == Array && typeof parameters !== 'function') { + finalParameters = parameters; + } + + // Create execution selector + var selector = {'$eval':finalCode, 'args':finalParameters}; + // Check if the nolock parameter is passed in + if(options['nolock']) { + selector['nolock'] = options['nolock']; + } + + // Set primary read preference + options.readPreference = ReadPreference.PRIMARY; + + // Execute the eval + this.collection(DbCommand.SYSTEM_COMMAND_COLLECTION).findOne(selector, options, function(err, result) { + if(err) return callback(err); + + if(result && result.ok == 1) { + callback(null, result.retval); + } else if(result) { + callback(new Error("eval failed: " + result.errmsg), null); return; + } else { + callback(err, result); + } + }); +}; + +/** + * Dereference a dbref, against a db + * + * @param {DBRef} dbRef db reference object we wish to resolve. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from dereference or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.dereference = function(dbRef, callback) { + var db = this; + // If we have a db reference then let's get the db first + if(dbRef.db != null) db = this.db(dbRef.db); + // Fetch the collection and find the reference + var collection = db.collection(dbRef.namespace); + collection.findOne({'_id':dbRef.oid}, function(err, result) { + callback(err, result); + }); +}; + +/** + * Logout user from server, fire off on all connections and remove all auth info + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from logout or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.logout = function(options, callback) { + var self = this; + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Number of connections we need to logout from + var numberOfConnections = this.serverConfig.allRawConnections().length; + + // Let's generate the logout command object + var logoutCommand = DbCommand.logoutCommand(self, {logout:1}, options); + self._executeQueryCommand(logoutCommand, {onAll:true}, function(err, result) { + // Count down + numberOfConnections = numberOfConnections - 1; + // Work around the case where the number of connections are 0 + if(numberOfConnections <= 0 && typeof callback == 'function') { + var internalCallback = callback; + callback = null; + // Reset auth + self.auths = []; + // Handle any errors + if(err == null && result.documents[0].ok == 1) { + internalCallback(null, true); + } else { + err != null ? internalCallback(err, false) : internalCallback(new Error(result.documents[0].errmsg), false); + } + } + }); +} + +/** + * Authenticate a user against the server. + * + * Options + * - **authdb** {String}, The database that the credentials are for, + * different from the name of the current DB, for example admin + * @param {String} username username. + * @param {String} password password. + * @param {Object} [options] the options + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from authentication or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.authenticate = function(username, password, options, callback) { + var self = this; + + if (typeof callback === 'undefined') { + callback = options; + options = {}; + } + // the default db to authenticate against is 'this' + // if authententicate is called from a retry context, it may be another one, like admin + var authdb = options.authdb ? options.authdb : self.databaseName; + // Push the new auth if we have no previous record + + var numberOfConnections = 0; + var errorObject = null; + + if(options['connection'] != null) { + //if a connection was explicitly passed on options, then we have only one... + numberOfConnections = 1; + } else { + // Get the amount of connections in the pool to ensure we have authenticated all comments + numberOfConnections = this.serverConfig.allRawConnections().length; + options['onAll'] = true; + } + + // Execute all four + this._executeQueryCommand(DbCommand.createGetNonceCommand(self), options, function(err, result, connection) { + // Execute on all the connections + if(err == null) { + // Nonce used to make authentication request with md5 hash + var nonce = result.documents[0].nonce; + // Execute command + self._executeQueryCommand(DbCommand.createAuthenticationCommand(self, username, password, nonce, authdb), {connection:connection}, function(err, result) { + // Count down + numberOfConnections = numberOfConnections - 1; + // Ensure we save any error + if(err) { + errorObject = err; + } else if(result.documents[0].err != null || result.documents[0].errmsg != null){ + errorObject = self.wrap(result.documents[0]); + } + + // Work around the case where the number of connections are 0 + if(numberOfConnections <= 0 && typeof callback == 'function') { + var internalCallback = callback; + callback = null; + + if(errorObject == null && result.documents[0].ok == 1) { + // We authenticated correctly save the credentials + self.auths = [{'username':username, 'password':password, 'authdb': authdb}]; + // Return callback + internalCallback(errorObject, true); + } else { + internalCallback(errorObject, false); + } + } + }); + } + }); +}; + +/** + * Add a user to the database. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} username username. + * @param {String} password password. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from addUser or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.addUser = function(username, password, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Figure out the safe mode settings + var safe = self.safe != null && self.safe == false ? {w: 1} : self.safe; + // Override with options passed in if applicable + safe = options != null && options['safe'] != null ? options['safe'] : safe; + // Ensure it's at least set to safe + safe = safe == null ? {w: 1} : safe; + // Use node md5 generator + var md5 = crypto.createHash('md5'); + // Generate keys used for authentication + md5.update(username + ":mongo:" + password); + var userPassword = md5.digest('hex'); + // Fetch a user collection + var collection = this.collection(DbCommand.SYSTEM_USER_COLLECTION); + // Check if we are inserting the first user + collection.count({}, function(err, count) { + // We got an error (f.ex not authorized) + if(err != null) return callback(err, null); + // Check if the user exists and update i + collection.find({user: username}, {dbName: options['dbName']}).toArray(function(err, documents) { + // We got an error (f.ex not authorized) + if(err != null) return callback(err, null); + // Add command keys + var commandOptions = safe; + commandOptions.dbName = options['dbName']; + commandOptions.upsert = true; + // We have a user, let's update the password or upsert if not + collection.update({user: username},{$set: {user: username, pwd: userPassword}}, commandOptions, function(err, results) { + if(count == 0 && err) { + callback(null, [{user:username, pwd:userPassword}]); + } else if(err) { + callback(err, null) + } else { + callback(null, [{user:username, pwd:userPassword}]); + } + }); + }); + }); +}; + +/** + * Remove a user from a database + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} username username. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from removeUser or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.removeUser = function(username, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Figure out the safe mode settings + var safe = self.safe != null && self.safe == false ? {w: 1} : self.safe; + // Override with options passed in if applicable + safe = options != null && options['safe'] != null ? options['safe'] : safe; + // Ensure it's at least set to safe + safe = safe == null ? {w: 1} : safe; + + // Fetch a user collection + var collection = this.collection(DbCommand.SYSTEM_USER_COLLECTION); + collection.findOne({user: username}, {dbName: options['dbName']}, function(err, user) { + if(user != null) { + // Add command keys + var commandOptions = safe; + commandOptions.dbName = options['dbName']; + + collection.remove({user: username}, commandOptions, function(err, result) { + callback(err, true); + }); + } else { + callback(err, false); + } + }); +}; + +/** + * Creates a collection on a server pre-allocating space, need to create f.ex capped collections. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **serializeFunctions** {Boolean, default:false}, serialize functions on the document. + * - **raw** {Boolean, default:false}, perform all operations using raw bson objects. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * - **capped** {Boolean, default:false}, create a capped collection. + * - **size** {Number}, the size of the capped collection in bytes. + * - **max** {Number}, the maximum number of documents in the capped collection. + * - **autoIndexId** {Boolean, default:true}, create an index on the _id field of the document, True by default on MongoDB 2.2 or higher off for version < 2.2. + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **strict**, (Boolean, default:false) throws and error if collection already exists + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} collectionName the collection name we wish to access. + * @param {Object} [options] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from createCollection or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.createCollection = function(collectionName, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : null; + var self = this; + + // Figure out the safe mode settings + var safe = self.safe != null && self.safe == false ? {w: 1} : self.safe; + // Override with options passed in if applicable + safe = options != null && options['safe'] != null ? options['safe'] : safe; + // Ensure it's at least set to safe + safe = safe == null ? {w: 1} : safe; + + // Check if we have the name + this.collectionNames(collectionName, function(err, collections) { + if(err != null) return callback(err, null); + + var found = false; + collections.forEach(function(collection) { + if(collection.name == self.databaseName + "." + collectionName) found = true; + }); + + // If the collection exists either throw an exception (if db in safe mode) or return the existing collection + if(found && options && options.strict) { + return callback(new Error("Collection " + collectionName + " already exists. Currently in safe mode."), null); + } else if(found){ + try { + var collection = new Collection(self, collectionName, self.pkFactory, options); + } catch(err) { + return callback(err, null); + } + return callback(null, collection); + } + + // Create a new collection and return it + self._executeQueryCommand(DbCommand.createCreateCollectionCommand(self, collectionName, options), {read:false, safe:safe}, function(err, result) { + var document = result.documents[0]; + // If we have no error let's return the collection + if(err == null && document.ok == 1) { + try { + var collection = new Collection(self, collectionName, self.pkFactory, options); + } catch(err) { + return callback(err, null); + } + return callback(null, collection); + } else { + err != null ? callback(err, null) : callback(self.wrap(document), null); + } + }); + }); +}; + +/** + * Execute a command hash against MongoDB. This lets you acess any commands not available through the api on the server. + * + * @param {Object} selector the command hash to send to the server, ex: {ping:1}. + * @param {Function} callback this will be called after executing this method. The command always return the whole result of the command as the second parameter. + * @return {null} + * @api public + */ +Db.prototype.command = function(selector, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + // Set up the options + var cursor = new Cursor(this + , new Collection(this, DbCommand.SYSTEM_COMMAND_COLLECTION), selector, {}, { + limit: -1, timeout: QueryCommand.OPTS_NO_CURSOR_TIMEOUT, dbName: options['dbName'] + }); + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : false; + + // Ensure only commands who support read Prefrences are exeuted otherwise override and use Primary + if(readPreference != false) { + if(selector['group'] || selector['aggregate'] || selector['collStats'] || selector['dbStats'] + || selector['count'] || selector['distinct'] || selector['geoNear'] || selector['geoSearch'] || selector['geoWalk'] + || (selector['mapreduce'] && selector.out == 'inline')) { + // Set the read preference + cursor.setReadPreference(readPreference); + } else { + cursor.setReadPreference(ReadPreference.PRIMARY); + } + } + + // Return the next object + cursor.nextObject(callback); +}; + +/** + * Drop a collection from the database, removing it permanently. New accesses will create a new collection. + * + * @param {String} collectionName the name of the collection we wish to drop. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from dropCollection or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.dropCollection = function(collectionName, callback) { + var self = this; + + // Drop the collection + this._executeQueryCommand(DbCommand.createDropCollectionCommand(this, collectionName), function(err, result) { + if(err == null && result.documents[0].ok == 1) { + if(callback != null) return callback(null, true); + } else { + if(callback != null) err != null ? callback(err, null) : callback(self.wrap(result.documents[0]), null); + } + }); +}; + +/** + * Rename a collection. + * + * @param {String} fromCollection the name of the current collection we wish to rename. + * @param {String} toCollection the new name of the collection. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from renameCollection or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.renameCollection = function(fromCollection, toCollection, callback) { + var self = this; + + // Execute the command, return the new renamed collection if successful + this._executeQueryCommand(DbCommand.createRenameCollectionCommand(this, fromCollection, toCollection), function(err, result) { + if(err == null && result.documents[0].ok == 1) { + if(callback != null) return callback(null, new Collection(self, toCollection, self.pkFactory)); + } else { + if(callback != null) err != null ? callback(err, null) : callback(self.wrap(result.documents[0]), null); + } + }); +}; + +/** + * Return last error message for the given connection, note options can be combined. + * + * Options + * - **fsync** {Boolean, default:false}, option forces the database to fsync all files before returning. + * - **j** {Boolean, default:false}, awaits the journal commit before returning, > MongoDB 2.0. + * - **w** {Number}, until a write operation has been replicated to N servers. + * - **wtimeout** {Number}, number of miliseconds to wait before timing out. + * + * Connection Options + * - **connection** {Connection}, fire the getLastError down a specific connection. + * + * @param {Object} [options] returns option results. + * @param {Object} [connectionOptions] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from lastError or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.lastError = function(options, connectionOptions, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + connectionOptions = args.length ? args.shift() : {}; + + this._executeQueryCommand(DbCommand.createGetLastErrorCommand(options, this), connectionOptions, function(err, error) { + callback(err, error && error.documents); + }); +}; + +/** + * Legacy method calls. + * + * @ignore + * @api private + */ +Db.prototype.error = Db.prototype.lastError; +Db.prototype.lastStatus = Db.prototype.lastError; + +/** + * Return all errors up to the last time db reset_error_history was called. + * + * Options + * - **connection** {Connection}, fire the getLastError down a specific connection. + * + * @param {Object} [options] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from previousErrors or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.previousErrors = function(options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + this._executeQueryCommand(DbCommand.createGetPreviousErrorsCommand(this), options, function(err, error) { + callback(err, error.documents); + }); +}; + +/** + * Runs a command on the database. + * @ignore + * @api private + */ +Db.prototype.executeDbCommand = function(command_hash, options, callback) { + if(callback == null) { callback = options; options = {}; } + this._executeQueryCommand(DbCommand.createDbSlaveOkCommand(this, command_hash, options), options, callback); +}; + +/** + * Runs a command on the database as admin. + * @ignore + * @api private + */ +Db.prototype.executeDbAdminCommand = function(command_hash, options, callback) { + if(callback == null) { callback = options; options = {}; } + this._executeQueryCommand(DbCommand.createAdminDbCommand(this, command_hash), options, callback); +}; + +/** + * Resets the error history of the mongo instance. + * + * Options + * - **connection** {Connection}, fire the getLastError down a specific connection. + * + * @param {Object} [options] returns option results. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from resetErrorHistory or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.resetErrorHistory = function(options, callback) { + // Unpack calls + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + this._executeQueryCommand(DbCommand.createResetErrorHistoryCommand(this), options, function(err, error) { + callback(err, error.documents); + }); +}; + +/** + * Creates an index on the collection. + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **unique** {Boolean, default:false}, creates an unique index. + * - **sparse** {Boolean, default:false}, creates a sparse index. + * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. + * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value + * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates. + * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates. + * - **v** {Number}, specify the format version of the indexes. + * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) + * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * + * @param {String} collectionName name of the collection to create the index on. + * @param {Object} fieldOrSpec fieldOrSpec that defines the index. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from createIndex or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.createIndex = function(collectionName, fieldOrSpec, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + options = args.length ? args.shift() : {}; + options = typeof callback === 'function' ? options : callback; + options = options == null ? {} : options; + + // Get the error options + var errorOptions = _getWriteConcern(this, options, callback); + // Create command + var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, options); + // Default command options + var commandOptions = {}; + + // If we have error conditions set handle them + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + commandOptions['read'] = false; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + + // Set safe option + commandOptions['safe'] = errorOptions; + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + // Execute insert command + this._executeInsertCommand(command, commandOptions, function(err, result) { + if(err != null) return callback(err, null); + + result = result && result.documents; + if (result[0].err) { + callback(self.wrap(result[0])); + } else { + callback(null, command.documents[0].name); + } + }); + } else if(_hasWriteConcern(errorOptions) && callback == null) { + throw new Error("Cannot use a writeConcern without a provided callback"); + } else { + // Execute insert command + var result = this._executeInsertCommand(command, commandOptions); + // If no callback just return + if(!callback) return; + // If error return error + if(result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(null, null); + } +}; + +/** + * Ensures that an index exists, if it does not it creates it + * + * Options +* - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **unique** {Boolean, default:false}, creates an unique index. + * - **sparse** {Boolean, default:false}, creates a sparse index. + * - **background** {Boolean, default:false}, creates the index in the background, yielding whenever possible. + * - **dropDups** {Boolean, default:false}, a unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value + * - **min** {Number}, for geospatial indexes set the lower bound for the co-ordinates. + * - **max** {Number}, for geospatial indexes set the high bound for the co-ordinates. + * - **v** {Number}, specify the format version of the indexes. + * - **expireAfterSeconds** {Number}, allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher) + * - **name** {String}, override the autogenerated index name (useful if the resulting name is larger than 128 bytes) + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @param {String} collectionName name of the collection to create the index on. + * @param {Object} fieldOrSpec fieldOrSpec that defines the index. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from ensureIndex or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.ensureIndex = function(collectionName, fieldOrSpec, options, callback) { + var self = this; + + if (typeof callback === 'undefined' && typeof options === 'function') { + callback = options; + options = {}; + } + + if (options == null) { + options = {}; + } + + // Get the error options + var errorOptions = _getWriteConcern(this, options, callback); + // Make sure we don't try to do a write concern without a callback + if(_hasWriteConcern(errorOptions) && callback == null) + throw new Error("Cannot use a writeConcern without a provided callback"); + // Create command + var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, options); + var index_name = command.documents[0].name; + + // Default command options + var commandOptions = {}; + // Check if the index allready exists + this.indexInformation(collectionName, function(err, collectionInfo) { + if(err != null) return callback(err, null); + + if(!collectionInfo[index_name]) { + // If we have error conditions set handle them + if(_hasWriteConcern(errorOptions) && typeof callback == 'function') { + // Insert options + commandOptions['read'] = false; + // If we have safe set set async to false + if(errorOptions == null) commandOptions['async'] = true; + + // If we have an error option + if(typeof errorOptions == 'object') { + var keys = Object.keys(errorOptions); + for(var i = 0; i < keys.length; i++) { + commandOptions[keys[i]] = errorOptions[keys[i]]; + } + } + + if(typeof callback === 'function' + && commandOptions.w < 1 && !commandOptions.fsync && !commandOptions.journal) { + commandOptions.w = 1; + } + + self._executeInsertCommand(command, commandOptions, function(err, result) { + // Only callback if we have one specified + if(typeof callback === 'function') { + if(err != null) return callback(err, null); + + result = result && result.documents; + if (result[0].err) { + callback(self.wrap(result[0])); + } else { + callback(null, command.documents[0].name); + } + } + }); + } else { + // Execute insert command + var result = self._executeInsertCommand(command, commandOptions); + // If no callback just return + if(!callback) return; + // If error return error + if(result instanceof Error) { + return callback(result); + } + // Otherwise just return + return callback(null, index_name); + } + } else { + if(typeof callback === 'function') return callback(null, index_name); + } + }); +}; + +/** + * Returns the information available on allocated cursors. + * + * Options + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from cursorInfo or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.cursorInfo = function(options, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + options = args.length ? args.shift() : {}; + + this._executeQueryCommand(DbCommand.createDbSlaveOkCommand(this, {'cursorInfo':1}), options, function(err, result) { + callback(err, result.documents[0]); + }); +}; + +/** + * Drop an index on a collection. + * + * @param {String} collectionName the name of the collection where the command will drop an index. + * @param {String} indexName name of the index to drop. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from dropIndex or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.dropIndex = function(collectionName, indexName, callback) { + this._executeQueryCommand(DbCommand.createDropIndexCommand(this, collectionName, indexName), callback); +}; + +/** + * Reindex all indexes on the collection + * Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections. + * + * @param {String} collectionName the name of the collection. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from reIndex or null if an error occured. + * @api public +**/ +Db.prototype.reIndex = function(collectionName, callback) { + this._executeQueryCommand(DbCommand.createReIndexCommand(this, collectionName), function(err, result) { + if(err != null) { + callback(err, false); + } else if(result.documents[0].errmsg == null) { + callback(null, true); + } else { + callback(new Error(result.documents[0].errmsg), false); + } + }); +}; + +/** + * Retrieves this collections index info. + * + * Options + * - **full** {Boolean, default:false}, returns the full raw index information. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {String} collectionName the name of the collection. + * @param {Object} [options] additional options during update. + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from indexInformation or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.indexInformation = function(collectionName, options, callback) { + if(typeof callback === 'undefined') { + if(typeof options === 'undefined') { + callback = collectionName; + collectionName = null; + } else { + callback = options; + } + options = {}; + } + + // If we specified full information + var full = options['full'] == null ? false : options['full']; + // Build selector for the indexes + var selector = collectionName != null ? {ns: (this.databaseName + "." + collectionName)} : {}; + + // Set read preference if we set one + var readPreference = options['readPreference'] ? options['readPreference'] : ReadPreference.PRIMARY; + + // Iterate through all the fields of the index + this.collection(DbCommand.SYSTEM_INDEX_COLLECTION, function(err, collection) { + // Perform the find for the collection + collection.find(selector).setReadPreference(readPreference).toArray(function(err, indexes) { + if(err != null) return callback(err, null); + // Contains all the information + var info = {}; + + // if full defined just return all the indexes directly + if(full) return callback(null, indexes); + + // Process all the indexes + for(var i = 0; i < indexes.length; i++) { + var index = indexes[i]; + // Let's unpack the object + info[index.name] = []; + for(var name in index.key) { + info[index.name].push([name, index.key[name]]); + } + } + + // Return all the indexes + callback(null, info); + }); + }); +}; + +/** + * Drop a database. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from dropDatabase or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.dropDatabase = function(callback) { + var self = this; + + this._executeQueryCommand(DbCommand.createDropDatabaseCommand(this), function(err, result) { + if (err == null && result.documents[0].ok == 1) { + callback(null, true); + } else { + if (err) { + callback(err, false); + } else { + callback(self.wrap(result.documents[0]), false); + } + } + }); +}; + +/** + * Get all the db statistics. + * + * Options + * - **scale** {Number}, divide the returned sizes by scale value. + * - **readPreference** {String}, the preferred read preference ((Server.PRIMARY, Server.PRIMARY_PREFERRED, Server.SECONDARY, Server.SECONDARY_PREFERRED, Server.NEAREST). + * + * @param {Objects} [options] options for the stats command + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from stats or null if an error occured. + * @return {null} + * @api public + */ +Db.prototype.stats = function stats(options, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + // Fetch all commands + options = args.length ? args.shift() : {}; + + // Build command object + var commandObject = { + dbStats:this.collectionName, + } + + // Check if we have the scale value + if(options['scale'] != null) commandObject['scale'] = options['scale']; + + // Execute the command + this.command(commandObject, options, callback); +} + +/** + * Register a handler + * @ignore + * @api private + */ +Db.prototype._registerHandler = function(db_command, raw, connection, exhaust, callback) { + // If we have an array of commands, chain them + var chained = Array.isArray(db_command); + + // Check if we have exhausted + if(typeof exhaust == 'function') { + callback = exhaust; + exhaust = false; + } + + // If they are chained we need to add a special handler situation + if(chained) { + // List off chained id's + var chainedIds = []; + // Add all id's + for(var i = 0; i < db_command.length; i++) chainedIds.push(db_command[i].getRequestId().toString()); + // Register all the commands together + for(var i = 0; i < db_command.length; i++) { + var command = db_command[i]; + // Add the callback to the store + this._callBackStore.once(command.getRequestId(), callback); + // Add the information about the reply + this._callBackStore._notReplied[command.getRequestId().toString()] = {start: new Date().getTime(), 'raw': raw, chained:chainedIds, connection:connection, exhaust:false}; + } + } else { + // Add the callback to the list of handlers + this._callBackStore.once(db_command.getRequestId(), callback); + // Add the information about the reply + this._callBackStore._notReplied[db_command.getRequestId().toString()] = {start: new Date().getTime(), 'raw': raw, connection:connection, exhaust:exhaust}; + } +} + +/** + * Re-Register a handler, on the cursor id f.ex + * @ignore + * @api private + */ +Db.prototype._reRegisterHandler = function(newId, object, callback) { + // Add the callback to the list of handlers + this._callBackStore.once(newId, object.callback.listener); + // Add the information about the reply + this._callBackStore._notReplied[newId] = object.info; +} + +/** + * + * @ignore + * @api private + */ +Db.prototype._callHandler = function(id, document, err) { + // If there is a callback peform it + if(this._callBackStore.listeners(id).length >= 1) { + // Get info object + var info = this._callBackStore._notReplied[id]; + // Delete the current object + delete this._callBackStore._notReplied[id]; + // Emit to the callback of the object + this._callBackStore.emit(id, err, document, info.connection); + } +} + +/** + * + * @ignore + * @api private + */ +Db.prototype._hasHandler = function(id) { + // If there is a callback peform it + return this._callBackStore.listeners(id).length >= 1; +} + +/** + * + * @ignore + * @api private + */ +Db.prototype._removeHandler = function(id) { + // Remove the information + if(this._callBackStore._notReplied[id] != null) delete this._callBackStore._notReplied[id]; + // Remove the callback if it's registered + this._callBackStore.removeAllListeners(id); + // Force cleanup _events, node.js seems to set it as a null value + if(this._callBackStore._events != null) delete this._callBackStore._events[id]; +} + +/** + * + * @ignore + * @api private + */ +Db.prototype._findHandler = function(id) { + var info = this._callBackStore._notReplied[id]; + // Return the callback + return {info:info, callback:(this._callBackStore.listeners(id).length >= 1) ? this._callBackStore.listeners(id)[0] : null} +} + +/** + * @ignore + */ +var __executeQueryCommand = function(self, db_command, options, callback) { + // Options unpacking + var read = options['read'] != null ? options['read'] : false; + var raw = options['raw'] != null ? options['raw'] : self.raw; + var onAll = options['onAll'] != null ? options['onAll'] : false; + var specifiedConnection = options['connection'] != null ? options['connection'] : null; + + // Correct read preference to default primary if set to false, null or primary + if(!(typeof read == 'object') && read._type == 'ReadPreference') { + read = (read == null || read == 'primary' || read == false) ? ReadPreference.PRIMARY : read; + if(!ReadPreference.isValid(read)) return callback(new Error("Illegal readPreference mode specified, " + read)); + } else if(typeof read == 'object' && read._type == 'ReadPreference') { + if(!read.isValid()) return callback(new Error("Illegal readPreference mode specified, " + read.mode)); + } + + // If we have a read preference set and we are a mongos pass the read preference on to the mongos instance, + if(self.serverConfig.isMongos() && read != null && read != false) { + db_command.setMongosReadPreference(read); + } + + // If we got a callback object + if(typeof callback === 'function' && !onAll) { + // Override connection if we passed in a specific connection + var connection = specifiedConnection != null ? specifiedConnection : null; + // connection = connection != null && connection.connected != null ? connection : null; + + if(connection instanceof Error) return callback(connection, null); + + // Fetch either a reader or writer dependent on the specified read option if no connection + // was passed in + if(connection == null) { + connection = read == null || read == 'primary' || read == false ? self.serverConfig.checkoutWriter(true) : self.serverConfig.checkoutReader(read); + } + + // Ensure we have a valid connection + if(connection == null) { + return callback(new Error("no open connections")); + } else if(connection instanceof Error || connection['message'] != null) { + return callback(connection); + } + + // Exhaust Option + var exhaust = options.exhaust || false; + + // Register the handler in the data structure + self._registerHandler(db_command, raw, connection, exhaust, callback); + + // Write the message out and handle any errors if there are any + connection.write(db_command, function(err) { + if(err != null) { + // Call the handler with an error + self._callHandler(db_command.getRequestId(), null, err); + } + }); + } else if(typeof callback === 'function' && onAll) { + var connections = self.serverConfig.allRawConnections(); + var numberOfEntries = connections.length; + // Go through all the connections + for(var i = 0; i < connections.length; i++) { + // Fetch a connection + var connection = connections[i]; + + // Ensure we have a valid connection + if(connection == null) { + return callback(new Error("no open connections")); + } else if(connection instanceof Error) { + return callback(connection); + } + + // Register the handler in the data structure + self._registerHandler(db_command, raw, connection, callback); + + // Write the message out + connection.write(db_command, function(err) { + // Adjust the number of entries we need to process + numberOfEntries = numberOfEntries - 1; + // Remove listener + if(err != null) { + // Clean up listener and return error + self._removeHandler(db_command.getRequestId()); + } + + // No more entries to process callback with the error + if(numberOfEntries <= 0) { + callback(err); + } + }); + + // Update the db_command request id + db_command.updateRequestId(); + } + } else { + // Fetch either a reader or writer dependent on the specified read option + var connection = read == null || read == 'primary' || read == false ? self.serverConfig.checkoutWriter(true) : self.serverConfig.checkoutReader(read); + // Override connection if needed + connection = specifiedConnection != null ? specifiedConnection : connection; + // Ensure we have a valid connection + if(connection == null || connection instanceof Error || connection['message'] != null) return null; + // Write the message out + connection.write(db_command, function(err) { + if(err != null) { + // Emit the error + self.emit("error", err); + } + }); + } +} + +/** + * @ignore + */ +var __retryCommandOnFailure = function(self, retryInMilliseconds, numberOfTimes, command, db_command, options, callback) { + if(this._state == 'connected' || this._state == 'disconnected') this._state = 'connecting'; + // Number of retries done + var numberOfRetriesDone = numberOfTimes; + // Retry function, execute once + var retryFunction = function(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback) { + _self.serverConfig.connect(_self, {}, function(err, result, _serverConfig) { + // Adjust the number of retries left + _numberOfRetriesDone = _numberOfRetriesDone - 1; + // Definitively restart + if(err != null && _numberOfRetriesDone > 0) { + _self._state = 'connecting'; + // Close the server config + _serverConfig.close(function(err) { + // Retry the connect + setTimeout(function() { + retryFunction(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback); + }, _retryInMilliseconds); + }); + } else if(err != null && _numberOfRetriesDone <= 0) { + _self._state = 'disconnected'; + // Force close the current connections + _serverConfig.close(function(_err) { + // Force close the current connections + if(typeof _callback == 'function') _callback(err, null); + }); + } else if(err == null && _self.serverConfig.isConnected() == true && Array.isArray(_self.auths) && _self.auths.length > 0) { + _self._state = 'connected'; + // Get number of auths we need to execute + var numberOfAuths = _self.auths.length; + // Apply all auths + for(var i = 0; i < _self.auths.length; i++) { + _self.authenticate(_self.auths[i].username, _self.auths[i].password, {'authdb':_self.auths[i].authdb}, function(err, authenticated) { + numberOfAuths = numberOfAuths - 1; + + // If we have no more authentications to replay + if(numberOfAuths == 0) { + if(err != null || !authenticated) { + if(typeof _callback == 'function') _callback(err, null); + return; + } else { + // Execute command + command(_self, _db_command, _options, _callback); + // Execute all the commands + if(_self.commands.length > 0) _execute_queued_command(_self); + } + } + }); + } + } else if(err == null && _self.serverConfig.isConnected() == true) { + _self._state = 'connected'; + // Execute command + command(_self, _db_command, _options, _callback); + + process.nextTick(function() { + // Execute any backed up commands + while(_self.commands.length > 0) { + // Fetch the command + var command = _self.commands.shift(); + // Execute based on type + if(command['type'] == 'query') { + __executeQueryCommand(_self, command['db_command'], command['options'], command['callback']); + } else if(command['type'] == 'insert') { + __executeInsertCommand(_self, command['db_command'], command['options'], command['callback']); + } + } + }); + } else { + _self._state = 'connecting'; + // Force close the current connections + _serverConfig.close(function(err) { + // _self.serverConfig.close(function(err) { + // Retry the connect + setTimeout(function() { + retryFunction(_self, _numberOfRetriesDone, _retryInMilliseconds, _numberOfTimes, _command, _db_command, _options, _callback); + }, _retryInMilliseconds); + }); + } + }); + }; + + // Execute function first time + retryFunction(self, numberOfRetriesDone, retryInMilliseconds, numberOfTimes, command, db_command, options, callback); +} + +/** + * Execute db query command (not safe) + * @ignore + * @api private + */ +Db.prototype._executeQueryCommand = function(db_command, options, callback) { + var self = this; + + // Unpack the parameters + if (typeof callback === 'undefined') { + callback = options; + options = {}; + } + + // fast fail option used for HA, no retry + var failFast = options['failFast'] != null + ? options['failFast'] + : false; + + // Check if the user force closed the command + if(this._applicationClosed) { + var err = new Error("db closed by application"); + if('function' == typeof callback) { + return callback(err, null); + } else { + throw err; + } + } + + var config = this.serverConfig; + // If the pool is not connected, attemp to reconnect to send the message + if(this._state == 'connecting' && config.autoReconnect && !failFast) { + return process.nextTick(function() { + self.commands.push({ + type: 'query', + db_command: db_command, + options: options, + callback: callback + }); + }) + } + + if(!failFast && !config.isConnected(options.read) && config.autoReconnect + && (options.read == null + || options.read == false + || options.read == ReadPreference.PRIMARY + || config.checkoutReader() == null)) { + this._state = 'connecting'; + return __retryCommandOnFailure(this, + this.retryMiliSeconds, + this.numberOfRetries, + __executeQueryCommand, + db_command, + options, + callback); + } + + if(!config.isConnected(options.read) && !config.autoReconnect && callback) { + // Fire an error to the callback if we are not connected + // and don't reconnect. + return callback(new Error("no open connections"), null); + } + + __executeQueryCommand(self, db_command, options, function (err, result, conn) { + callback(err, result, conn); + }); + +}; + +/** + * @ignore + */ +var __executeInsertCommand = function(self, db_command, options, callback) { + // Always checkout a writer for this kind of operations + var connection = self.serverConfig.checkoutWriter(); + // Get safe mode + var safe = options['safe'] != null ? options['safe'] : false; + var raw = options['raw'] != null ? options['raw'] : self.raw; + var specifiedConnection = options['connection'] != null ? options['connection'] : null; + // Override connection if needed + connection = specifiedConnection != null ? specifiedConnection : connection; + + // Ensure we have a valid connection + if(typeof callback === 'function') { + // Ensure we have a valid connection + if(connection == null) { + return callback(new Error("no open connections")); + } else if(connection instanceof Error) { + return callback(connection); + } + + var errorOptions = _getWriteConcern(self, options, callback); + if(errorOptions.w > 0 || errorOptions.w == 'majority' || errorOptions.j || errorOptions.journal || errorOptions.fsync) { + // db command is now an array of commands (original command + lastError) + db_command = [db_command, DbCommand.createGetLastErrorCommand(safe, self)]; + // Register the handler in the data structure + self._registerHandler(db_command[1], raw, connection, callback); + } + } + + // If we have no callback and there is no connection + if(connection == null) return null; + if(connection instanceof Error && typeof callback == 'function') return callback(connection, null); + if(connection instanceof Error) return null; + if(connection == null && typeof callback == 'function') return callback(new Error("no primary server found"), null); + + // Write the message out + connection.write(db_command, function(err) { + // Return the callback if it's not a safe operation and the callback is defined + if(typeof callback === 'function' && (safe == null || safe == false)) { + // Perform the callback + callback(err, null); + } else if(typeof callback === 'function') { + // Call the handler with an error + self._callHandler(db_command[1].getRequestId(), null, err); + } else if(typeof callback == 'function' && safe && safe.w == -1) { + // Call the handler with no error + self._callHandler(db_command[1].getRequestId(), null, null); + } else if(!safe && safe.w == -1) { + self.emit("error", err); + } + }); +} + +/** + * Execute an insert Command + * @ignore + * @api private + */ +Db.prototype._executeInsertCommand = function(db_command, options, callback) { + var self = this; + + // Unpack the parameters + if(callback == null && typeof options === 'function') { + callback = options; + options = {}; + } + + // Ensure options are not null + options = options == null ? {} : options; + + // Check if the user force closed the command + if(this._applicationClosed) { + if(typeof callback == 'function') { + return callback(new Error("db closed by application"), null); + } else { + throw new Error("db closed by application"); + } + } + + // If the pool is not connected, attemp to reconnect to send the message + if(self._state == 'connecting' && this.serverConfig.autoReconnect) { + process.nextTick(function() { + self.commands.push({type:'insert', 'db_command':db_command, 'options':options, 'callback':callback}); + }) + } else if(!this.serverConfig.isConnected() && this.serverConfig.autoReconnect) { + this._state = 'connecting'; + // Retry command + __retryCommandOnFailure(this, this.retryMiliSeconds, this.numberOfRetries, __executeInsertCommand, db_command, options, callback); + } else if(!this.serverConfig.isConnected() && !this.serverConfig.autoReconnect && callback) { + // Fire an error to the callback if we are not connected and don't do reconnect + if(callback) callback(new Error("no open connections"), null); + } else { + __executeInsertCommand(self, db_command, options, callback); + } +} + +/** + * Update command is the same + * @ignore + * @api private + */ +Db.prototype._executeUpdateCommand = Db.prototype._executeInsertCommand; +/** + * Remove command is the same + * @ignore + * @api private + */ +Db.prototype._executeRemoveCommand = Db.prototype._executeInsertCommand; + +/** + * Wrap a Mongo error document into an Error instance + * @ignore + * @api private + */ +Db.prototype.wrap = function(error) { + var msg = error.err || error.errmsg || error; + var e = new Error(msg); + e.name = 'MongoError'; + + // Get all object keys + var keys = typeof error == 'object' ? Object.keys(error) : []; + // Populate error object with properties + for(var i = 0; i < keys.length; i++) { + e[keys[i]] = error[keys[i]]; + } + + return e; +} + +/** + * Default URL + * + * @classconstant DEFAULT_URL + **/ +Db.DEFAULT_URL = 'mongodb://localhost:27017/default'; + +/** + * Connect to MongoDB using a url as documented at + * + * www.mongodb.org/display/DOCS/Connections + * + * Options + * - **uri_decode_auth** {Boolean, default:false} uri decode the user name and password for authentication + * - **db** {Object, default: null} a hash off options to set on the db object, see **Db constructor** + * - **server** {Object, default: null} a hash off options to set on the server objects, see **Server** constructor** + * - **replSet** {Object, default: null} a hash off options to set on the replSet object, see **ReplSet** constructor** + * - **mongos** {Object, default: null} a hash off options to set on the mongos object, see **Mongos** constructor** + * + * @param {String} url connection url for MongoDB. + * @param {Object} [options] optional options for insert command + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the db instance or null if an error occured. + * @return {null} + * @api public + */ +Db.connect = function(url, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = typeof args[args.length - 1] == 'function' ? args.pop() : null; + options = args.length ? args.shift() : null; + options = options || {}; + var serverOptions = options.server || {}; + var mongosOptions = options.mongos || {}; + var replSetServersOptions = options.replSet || options.replSetServers || {}; + var dbOptions = options.db || {}; + + // If callback is null throw an exception + if(callback == null) throw new Error("no callback function provided"); + + // Parse the string + var object = parse(url); + // Merge in any options for db in options object + if(dbOptions) { + for(var name in dbOptions) object.db_options[name] = dbOptions[name]; + } + + // Merge in any options for server in options object + if(serverOptions) { + for(var name in serverOptions) object.server_options[name] = serverOptions[name]; + } + + // Merge in any replicaset server options + if(replSetServersOptions) { + for(var name in replSetServersOptions) object.rs_options[name] = replSetServersOptions[name]; + } + + // Merge in any replicaset server options + if(mongosOptions) { + for(var name in mongosOptions) object.mongos_options[name] = mongosOptions[name]; + } + + // We need to ensure that the list of servers are only either direct members or mongos + // they cannot be a mix of monogs and mongod's + var totalNumberOfServers = object.servers.length; + var totalNumberOfMongosServers = 0; + var totalNumberOfMongodServers = 0; + var serverConfig = null; + + // Failure modes + if(object.servers.length == 0) throw new Error("connection string must contain at least one seed host"); + + // If we have no db setting for the native parser try to set the c++ one first + object.db_options.native_parser = _setNativeParser(object.db_options); + // If no auto_reconnect is set, set it to true as default for single servers + if(typeof object.server_options.auto_reconnect != 'boolean') { + object.server_options.auto_reconnect = true; + } + + // If we have more than a server, it could be replicaset or mongos list + // need to verify that it's one or the other and fail if it's a mix + // Connect to all servers and run ismaster + for(var i = 0; i < object.servers.length; i++) { + // Set up the Server object + var _server = object.servers[i].domain_socket + ? new Server(object.servers[i].domain_socket, {socketOptions:{connectTimeoutMS:1000}, auto_reconnect:false}) + : new Server(object.servers[i].host, object.servers[i].port, {socketOptions:{connectTimeoutMS:1000}, auto_reconnect:false}); + + // Attempt connect + new Db(object.dbName, _server, {safe:false, native_parser:false}).open(function(err, db) { + // Update number of servers + totalNumberOfServers = totalNumberOfServers - 1; + // If no error do the correct checks + if(!err) { + // Close the connection + db.close(true); + var isMasterDoc = db.serverConfig.isMasterDoc; + // Check what type of server we have + if(isMasterDoc.setName) totalNumberOfMongodServers++; + if(isMasterDoc.msg && isMasterDoc.msg == "isdbgrid") totalNumberOfMongosServers++; + } + + if(totalNumberOfServers == 0) { + // If we have a mix of mongod and mongos, throw an error + if(totalNumberOfMongosServers > 0 && totalNumberOfMongodServers > 0) + return callback(new Error("cannot combine a list of replicaset seeds and mongos seeds")); + + if(totalNumberOfMongodServers == 0 && object.servers.length == 1) { + var obj = object.servers[0]; + serverConfig = obj.domain_socket ? + new Server(obj.domain_socket, object.server_options) + : new Server(obj.host, obj.port, object.server_options); + } else if(totalNumberOfMongodServers > 0) { + serverConfig = new ReplSet(object.servers.map(function(serverObj) { + return new Server(serverObj.host, serverObj.port, object.server_options); + }), object.rs_options); + } else if(totalNumberOfMongosServers > 0) { + serverConfig = new Mongos(object.servers.map(function(serverObj) { + return new Server(serverObj.host, serverObj.port, object.server_options); + }), object.mongos_options); + } + + if(serverConfig == null) return callback(new Error("Could not locate any valid servers in initial seed list")); + // Set up all options etc and connect to the database + _finishConnecting(serverConfig, object, options, callback) + } + }); + } +} + +var _setNativeParser = function(db_options) { + if(typeof db_options.native_parser == 'boolean') return db_options.native_parser; + + try { + require('bson').BSONNative.BSON; + return true; + } catch(err) { + return false; + } +} + +var _finishConnecting = function(serverConfig, object, options, callback) { + // Safe settings + var safe = {}; + // Build the safe parameter if needed + if(object.db_options.journal) safe.j = object.db_options.journal; + if(object.db_options.w) safe.w = object.db_options.w; + if(object.db_options.fsync) safe.fsync = object.db_options.fsync; + if(object.db_options.wtimeoutMS) safe.wtimeout = object.db_options.wtimeoutMS; + + // If we have a read Preference set + if(object.db_options.read_preference) { + var readPreference = new ReadPreference(object.db_options.read_preference); + // If we have the tags set up + if(object.db_options.read_preference_tags) + readPreference = new ReadPreference(object.db_options.read_preference, object.db_options.read_preference_tags); + // Add the read preference + object.db_options.readPreference = readPreference; + } + + // No safe mode if no keys + if(Object.keys(safe).length == 0) safe = false; + // Add the safe object + object.db_options.safe = safe; + // Set up the db options + var db = new Db(object.dbName, serverConfig, object.db_options); + // Don't open the connection + if(options.noOpen) return db; + + // Open the db + db.open(function(err, db){ + if(err == null && object.auth){ + db.authenticate(object.auth.user, object.auth.password, function(err, success){ + if(success){ + callback(null, db); + } else { + callback(err ? err : new Error('Could not authenticate user ' + auth[0]), db); + } + }); + } else { + callback(err, db); + } + }); +} + +/** + * State of the db connection + * @ignore + */ +Object.defineProperty(Db.prototype, "state", { enumerable: true + , get: function () { + return this.serverConfig._serverState; + } +}); + +/** + * @ignore + */ +var _hasWriteConcern = function(errorOptions) { + return errorOptions == true + || errorOptions.w > 0 + || errorOptions.w == 'majority' + || errorOptions.j == true + || errorOptions.journal == true + || errorOptions.fsync == true +} + +/** + * @ignore + */ +var _setWriteConcernHash = function(options) { + var finalOptions = {}; + if(options.w != null) finalOptions.w = options.w; + if(options.journal == true) finalOptions.j = options.journal; + if(options.j == true) finalOptions.j = options.j; + if(options.fsync == true) finalOptions.fsync = options.fsync; + if(options.wtimeout != null) finalOptions.wtimeout = options.wtimeout; + return finalOptions; +} + +/** + * @ignore + */ +var _getWriteConcern = function(self, options, callback) { + // Final options + var finalOptions = {w:1}; + // Local options verification + if(options.w != null || typeof options.j == 'boolean' || typeof options.journal == 'boolean' || typeof options.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(options); + } else if(options.safe != null && typeof options.safe == 'object') { + finalOptions = _setWriteConcernHash(options.safe); + } else if(typeof options.safe == "boolean") { + finalOptions = {w: (options.safe ? 1 : 0)}; + } else if(self.options.w != null || typeof self.options.j == 'boolean' || typeof self.options.journal == 'boolean' || typeof self.options.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.options); + } else if(self.safe.w != null || typeof self.safe.j == 'boolean' || typeof self.safe.journal == 'boolean' || typeof self.safe.fsync == 'boolean') { + finalOptions = _setWriteConcernHash(self.safe); + } else if(typeof self.safe == "boolean") { + finalOptions = {w: (self.safe ? 1 : 0)}; + } + + // Ensure we don't have an invalid combination of write concerns + if(finalOptions.w < 1 + && (finalOptions.journal == true || finalOptions.j == true || finalOptions.fsync == true)) throw new Error("No acknowlegement using w < 1 cannot be combined with journal:true or fsync:true"); + + // Return the options + return finalOptions; +} + +/** + * Legacy support + * + * @ignore + * @api private + */ +exports.connect = Db.connect; +exports.Db = Db; + +/** + * Remove all listeners to the db instance. + * @ignore + * @api private + */ +Db.prototype.removeAllEventListeners = function() { + this.removeAllListeners("close"); + this.removeAllListeners("error"); + this.removeAllListeners("timeout"); + this.removeAllListeners("parseError"); + this.removeAllListeners("poolReady"); + this.removeAllListeners("message"); +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/chunk.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/chunk.js new file mode 100644 index 0000000..572d144 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/chunk.js @@ -0,0 +1,213 @@ +var Binary = require('bson').Binary, + ObjectID = require('bson').ObjectID; + +/** + * Class for representing a single chunk in GridFS. + * + * @class + * + * @param file {GridStore} The {@link GridStore} object holding this chunk. + * @param mongoObject {object} The mongo object representation of this chunk. + * + * @throws Error when the type of data field for {@link mongoObject} is not + * supported. Currently supported types for data field are instances of + * {@link String}, {@link Array}, {@link Binary} and {@link Binary} + * from the bson module + * + * @see Chunk#buildMongoObject + */ +var Chunk = exports.Chunk = function(file, mongoObject) { + if(!(this instanceof Chunk)) return new Chunk(file, mongoObject); + + this.file = file; + var self = this; + var mongoObjectFinal = mongoObject == null ? {} : mongoObject; + + this.objectId = mongoObjectFinal._id == null ? new ObjectID() : mongoObjectFinal._id; + this.chunkNumber = mongoObjectFinal.n == null ? 0 : mongoObjectFinal.n; + this.data = new Binary(); + + if(mongoObjectFinal.data == null) { + } else if(typeof mongoObjectFinal.data == "string") { + var buffer = new Buffer(mongoObjectFinal.data.length); + buffer.write(mongoObjectFinal.data, 'binary', 0); + this.data = new Binary(buffer); + } else if(Array.isArray(mongoObjectFinal.data)) { + var buffer = new Buffer(mongoObjectFinal.data.length); + buffer.write(mongoObjectFinal.data.join(''), 'binary', 0); + this.data = new Binary(buffer); + } else if(mongoObjectFinal.data instanceof Binary || Object.prototype.toString.call(mongoObjectFinal.data) == "[object Binary]") { + this.data = mongoObjectFinal.data; + } else if(Buffer.isBuffer(mongoObjectFinal.data)) { + } else { + throw Error("Illegal chunk format"); + } + // Update position + this.internalPosition = 0; +}; + +/** + * Writes a data to this object and advance the read/write head. + * + * @param data {string} the data to write + * @param callback {function(*, GridStore)} This will be called after executing + * this method. The first parameter will contain null and the second one + * will contain a reference to this object. + */ +Chunk.prototype.write = function(data, callback) { + this.data.write(data, this.internalPosition); + this.internalPosition = this.data.length(); + if(callback != null) return callback(null, this); + return this; +}; + +/** + * Reads data and advances the read/write head. + * + * @param length {number} The length of data to read. + * + * @return {string} The data read if the given length will not exceed the end of + * the chunk. Returns an empty String otherwise. + */ +Chunk.prototype.read = function(length) { + // Default to full read if no index defined + length = length == null || length == 0 ? this.length() : length; + + if(this.length() - this.internalPosition + 1 >= length) { + var data = this.data.read(this.internalPosition, length); + this.internalPosition = this.internalPosition + length; + return data; + } else { + return ''; + } +}; + +Chunk.prototype.readSlice = function(length) { + if ((this.length() - this.internalPosition) >= length) { + var data = null; + if (this.data.buffer != null) { //Pure BSON + data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length); + } else { //Native BSON + data = new Buffer(length); + length = this.data.readInto(data, this.internalPosition); + } + this.internalPosition = this.internalPosition + length; + return data; + } else { + return null; + } +}; + +/** + * Checks if the read/write head is at the end. + * + * @return {boolean} Whether the read/write head has reached the end of this + * chunk. + */ +Chunk.prototype.eof = function() { + return this.internalPosition == this.length() ? true : false; +}; + +/** + * Reads one character from the data of this chunk and advances the read/write + * head. + * + * @return {string} a single character data read if the the read/write head is + * not at the end of the chunk. Returns an empty String otherwise. + */ +Chunk.prototype.getc = function() { + return this.read(1); +}; + +/** + * Clears the contents of the data in this chunk and resets the read/write head + * to the initial position. + */ +Chunk.prototype.rewind = function() { + this.internalPosition = 0; + this.data = new Binary(); +}; + +/** + * Saves this chunk to the database. Also overwrites existing entries having the + * same id as this chunk. + * + * @param callback {function(*, GridStore)} This will be called after executing + * this method. The first parameter will contain null and the second one + * will contain a reference to this object. + */ +Chunk.prototype.save = function(callback) { + var self = this; + + self.file.chunkCollection(function(err, collection) { + if(err) return callback(err); + + collection.remove({'_id':self.objectId}, {safe:true}, function(err, result) { + if(err) return callback(err); + + if(self.data.length() > 0) { + self.buildMongoObject(function(mongoObject) { + collection.insert(mongoObject, {safe:true}, function(err, collection) { + callback(err, self); + }); + }); + } else { + callback(null, self); + } + }); + }); +}; + +/** + * Creates a mongoDB object representation of this chunk. + * + * @param callback {function(Object)} This will be called after executing this + * method. The object will be passed to the first parameter and will have + * the structure: + * + *
    
    + *        {
    + *          '_id' : , // {number} id for this chunk
    + *          'files_id' : , // {number} foreign key to the file collection
    + *          'n' : , // {number} chunk number
    + *          'data' : , // {bson#Binary} the chunk data itself
    + *        }
    + *        
    + * + * @see MongoDB GridFS Chunk Object Structure + */ +Chunk.prototype.buildMongoObject = function(callback) { + var mongoObject = {'_id': this.objectId, + 'files_id': this.file.fileId, + 'n': this.chunkNumber, + 'data': this.data}; + callback(mongoObject); +}; + +/** + * @return {number} the length of the data + */ +Chunk.prototype.length = function() { + return this.data.length(); +}; + +/** + * The position of the read/write head + * @name position + * @lends Chunk# + * @field + */ +Object.defineProperty(Chunk.prototype, "position", { enumerable: true + , get: function () { + return this.internalPosition; + } + , set: function(value) { + this.internalPosition = value; + } +}); + +/** + * The default chunk size + * @constant + */ +Chunk.DEFAULT_CHUNK_SIZE = 1024 * 256; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/grid.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/grid.js new file mode 100644 index 0000000..aa695b7 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/grid.js @@ -0,0 +1,103 @@ +var GridStore = require('./gridstore').GridStore, + ObjectID = require('bson').ObjectID; + +/** + * A class representation of a simple Grid interface. + * + * @class Represents the Grid. + * @param {Db} db A database instance to interact with. + * @param {String} [fsName] optional different root collection for GridFS. + * @return {Grid} + */ +function Grid(db, fsName) { + + if(!(this instanceof Grid)) return new Grid(db, fsName); + + this.db = db; + this.fsName = fsName == null ? GridStore.DEFAULT_ROOT_COLLECTION : fsName; +} + +/** + * Puts binary data to the grid + * + * Options + * - **_id** {Any}, unique id for this file + * - **root** {String}, root collection to use. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**. + * - **content_type** {String}, mime type of the file. Defaults to **{GridStore.DEFAULT_CONTENT_TYPE}**. + * - **chunk_size** {Number}, size for the chunk. Defaults to **{Chunk.DEFAULT_CHUNK_SIZE}**. + * - **metadata** {Object}, arbitrary data the user wants to store. + * + * @param {Buffer} data buffer with Binary Data. + * @param {Object} [options] the options for the files. + * @param {Function} callback this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object. + * @return {null} + * @api public + */ +Grid.prototype.put = function(data, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + options = args.length ? args.shift() : {}; + // If root is not defined add our default one + options['root'] = options['root'] == null ? this.fsName : options['root']; + + // Return if we don't have a buffer object as data + if(!(Buffer.isBuffer(data))) return callback(new Error("Data object must be a buffer object"), null); + // Get filename if we are using it + var filename = options['filename'] || null; + // Get id if we are using it + var id = options['_id'] || null; + // Create gridstore + var gridStore = new GridStore(this.db, id, filename, "w", options); + gridStore.open(function(err, gridStore) { + if(err) return callback(err, null); + + gridStore.write(data, function(err, result) { + if(err) return callback(err, null); + + gridStore.close(function(err, result) { + if(err) return callback(err, null); + callback(null, result); + }) + }) + }) +} + +/** + * Get binary data to the grid + * + * @param {Any} id for file. + * @param {Function} callback this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object. + * @return {null} + * @api public + */ +Grid.prototype.get = function(id, callback) { + // Create gridstore + var gridStore = new GridStore(this.db, id, null, "r", {root:this.fsName}); + gridStore.open(function(err, gridStore) { + if(err) return callback(err, null); + + // Return the data + gridStore.read(function(err, data) { + return callback(err, data) + }); + }) +} + +/** + * Delete file from grid + * + * @param {Any} id for file. + * @param {Function} callback this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object. + * @return {null} + * @api public + */ +Grid.prototype.delete = function(id, callback) { + // Create gridstore + GridStore.unlink(this.db, id, {root:this.fsName}, function(err, result) { + if(err) return callback(err, false); + return callback(null, true); + }); +} + +exports.Grid = Grid; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js new file mode 100644 index 0000000..586a491 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/gridstore.js @@ -0,0 +1,1486 @@ +/** + * @fileOverview GridFS is a tool for MongoDB to store files to the database. + * Because of the restrictions of the object size the database can hold, a + * facility to split a file into several chunks is needed. The {@link GridStore} + * class offers a simplified api to interact with files while managing the + * chunks of split files behind the scenes. More information about GridFS can be + * found here. + */ +var Chunk = require('./chunk').Chunk, + DbCommand = require('../commands/db_command').DbCommand, + ObjectID = require('bson').ObjectID, + Buffer = require('buffer').Buffer, + fs = require('fs'), + util = require('util'), + inherits = util.inherits, + ReadStream = require('./readstream').ReadStream, + Stream = require('stream'); + +var REFERENCE_BY_FILENAME = 0, + REFERENCE_BY_ID = 1; + +/** + * A class representation of a file stored in GridFS. + * + * Modes + * - **"r"** - read only. This is the default mode. + * - **"w"** - write in truncate mode. Existing data will be overwriten. + * - **w+"** - write in edit mode. + * + * Options + * - **root** {String}, root collection to use. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**. + * - **content_type** {String}, mime type of the file. Defaults to **{GridStore.DEFAULT_CONTENT_TYPE}**. + * - **chunk_size** {Number}, size for the chunk. Defaults to **{Chunk.DEFAULT_CHUNK_SIZE}**. + * - **metadata** {Object}, arbitrary data the user wants to store. + * + * @class Represents the GridStore. + * @param {Db} db A database instance to interact with. + * @param {Any} [id] optional unique id for this file + * @param {String} [filename] optional filename for this file, no unique constrain on the field + * @param {String} mode set the mode for this file. + * @param {Object} options optional properties to specify. + * @return {GridStore} + */ +var GridStore = function GridStore(db, id, filename, mode, options) { + if(!(this instanceof GridStore)) return new GridStore(db, id, filename, mode, options); + + var self = this; + this.db = db; + + // Call stream constructor + if(typeof Stream == 'function') { + Stream.call(this); + } else { + // 0.4.X backward compatibility fix + Stream.Stream.call(this); + } + + // Handle options + if(typeof options === 'undefined') options = {}; + // Handle mode + if(typeof mode === 'undefined') { + mode = filename; + filename = undefined; + } else if(typeof mode == 'object') { + options = mode; + mode = filename; + filename = undefined; + } + + if(id instanceof ObjectID) { + this.referenceBy = REFERENCE_BY_ID; + this.fileId = id; + this.filename = filename; + } else if(typeof filename == 'undefined') { + this.referenceBy = REFERENCE_BY_FILENAME; + this.filename = id; + if (mode.indexOf('w') != null) { + this.fileId = new ObjectID(); + } + } else { + this.referenceBy = REFERENCE_BY_ID; + this.fileId = id; + this.filename = filename; + } + + /* + // Handle id + if(id instanceof ObjectID && (typeof filename == 'string' || filename == null)) { + this.referenceBy = REFERENCE_BY_ID; + this.fileId = id; + this.filename = filename; + } else if(!(id instanceof ObjectID) && typeof id == 'string' && mode.indexOf("w") != null) { + this.referenceBy = REFERENCE_BY_FILENAME; + this.fileId = new ObjectID(); + this.filename = id; + } else if(!(id instanceof ObjectID) && typeof id == 'string' && mode.indexOf("r") != null) { + this.referenceBy = REFERENCE_BY_FILENAME; + this.filename = id; + } else { + this.referenceBy = REFERENCE_BY_ID; + this.fileId = id; + this.filename = filename; + } + */ + + // Set up the rest + this.mode = mode == null ? "r" : mode; + this.options = options == null ? {} : options; + this.root = this.options['root'] == null ? exports.GridStore.DEFAULT_ROOT_COLLECTION : this.options['root']; + this.position = 0; + // Set default chunk size + this.internalChunkSize = this.options['chunkSize'] == null ? Chunk.DEFAULT_CHUNK_SIZE : this.options['chunkSize']; +} + +/** + * Code for the streaming capabilities of the gridstore object + * Most code from Aaron heckmanns project https://github.com/aheckmann/gridfs-stream + * Modified to work on the gridstore object itself + * @ignore + */ +if(typeof Stream == 'function') { + GridStore.prototype = { __proto__: Stream.prototype } +} else { + // Node 0.4.X compatibility code + GridStore.prototype = { __proto__: Stream.Stream.prototype } +} + +// Move pipe to _pipe +GridStore.prototype._pipe = GridStore.prototype.pipe; + +/** + * Opens the file from the database and initialize this object. Also creates a + * new one if file does not exist. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain an **{Error}** object and the second parameter will be null if an error occured. Otherwise, the first parameter will be null and the second will contain the reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.open = function(callback) { + if( this.mode != "w" && this.mode != "w+" && this.mode != "r"){ + callback(new Error("Illegal mode " + this.mode), null); + return; + } + + var self = this; + + if((self.mode == "w" || self.mode == "w+") && self.db.serverConfig.primary != null) { + // Get files collection + self.collection(function(err, collection) { + if(err) return callback(err); + + // Put index on filename + collection.ensureIndex([['filename', 1]], function(err, index) { + if(err) return callback(err); + + // Get chunk collection + self.chunkCollection(function(err, chunkCollection) { + if(err) return callback(err); + + // Ensure index on chunk collection + chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], function(err, index) { + if(err) return callback(err); + _open(self, callback); + }); + }); + }); + }); + } else { + // Open the gridstore + _open(self, callback); + } +}; + +/** + * Hidding the _open function + * @ignore + * @api private + */ +var _open = function(self, callback) { + self.collection(function(err, collection) { + if(err!==null) { + callback(new Error("at collection: "+err), null); + return; + } + + // Create the query + var query = self.referenceBy == REFERENCE_BY_ID ? {_id:self.fileId} : {filename:self.filename}; + query = null == self.fileId && this.filename == null ? null : query; + + // Fetch the chunks + if(query != null) { + collection.find(query, function(err, cursor) { + if(err) return error(err); + + // Fetch the file + cursor.nextObject(function(err, doc) { + if(err) return error(err); + + // Check if the collection for the files exists otherwise prepare the new one + if(doc != null) { + self.fileId = doc._id; + self.filename = doc.filename; + self.contentType = doc.contentType; + self.internalChunkSize = doc.chunkSize; + self.uploadDate = doc.uploadDate; + self.aliases = doc.aliases; + self.length = doc.length; + self.metadata = doc.metadata; + self.internalMd5 = doc.md5; + } else if (self.mode != 'r') { + self.fileId = self.fileId == null ? new ObjectID() : self.fileId; + self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE; + self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize; + self.length = 0; + } else { + self.length = 0; + var txtId = self.fileId instanceof ObjectID ? self.fileId.toHexString() : self.fileId; + return error(new Error((self.referenceBy == REFERENCE_BY_ID ? txtId : self.filename) + " does not exist", self)); + } + + // Process the mode of the object + if(self.mode == "r") { + nthChunk(self, 0, function(err, chunk) { + if(err) return error(err); + self.currentChunk = chunk; + self.position = 0; + callback(null, self); + }); + } else if(self.mode == "w") { + // Delete any existing chunks + deleteChunks(self, function(err, result) { + if(err) return error(err); + self.currentChunk = new Chunk(self, {'n':0}); + self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type']; + self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size']; + self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata']; + self.position = 0; + callback(null, self); + }); + } else if(self.mode == "w+") { + nthChunk(self, lastChunkNumber(self), function(err, chunk) { + if(err) return error(err); + // Set the current chunk + self.currentChunk = chunk == null ? new Chunk(self, {'n':0}) : chunk; + self.currentChunk.position = self.currentChunk.data.length(); + self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata']; + self.position = self.length; + callback(null, self); + }); + } + }); + }); + } else { + // Write only mode + self.fileId = null == self.fileId ? new ObjectID() : self.fileId; + self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE; + self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize; + self.length = 0; + + self.chunkCollection(function(err, collection2) { + if(err) return error(err); + + // No file exists set up write mode + if(self.mode == "w") { + // Delete any existing chunks + deleteChunks(self, function(err, result) { + if(err) return error(err); + self.currentChunk = new Chunk(self, {'n':0}); + self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type']; + self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size']; + self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata']; + self.position = 0; + callback(null, self); + }); + } else if(self.mode == "w+") { + nthChunk(self, lastChunkNumber(self), function(err, chunk) { + if(err) return error(err); + // Set the current chunk + self.currentChunk = chunk == null ? new Chunk(self, {'n':0}) : chunk; + self.currentChunk.position = self.currentChunk.data.length(); + self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata']; + self.position = self.length; + callback(null, self); + }); + } + }); + } + }); + + // only pass error to callback once + function error (err) { + if(error.err) return; + callback(error.err = err); + } +}; + +/** + * Stores a file from the file system to the GridFS database. + * + * @param {String|Buffer|FileHandle} file the file to store. + * @param {Function} callback this will be called after this method is executed. The first parameter will be null and the the second will contain the reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.writeFile = function (file, callback) { + var self = this; + if (typeof file === 'string') { + fs.open(file, 'r', 0666, function (err, fd) { + if(err) return callback(err); + self.writeFile(fd, callback); + }); + return; + } + + self.open(function (err, self) { + if(err) return callback(err); + + fs.fstat(file, function (err, stats) { + if(err) return callback(err); + + var offset = 0; + var index = 0; + var numberOfChunksLeft = Math.min(stats.size / self.chunkSize); + + // Write a chunk + var writeChunk = function() { + fs.read(file, self.chunkSize, offset, 'binary', function(err, data, bytesRead) { + if(err) return callback(err); + + offset = offset + bytesRead; + + // Create a new chunk for the data + var chunk = new Chunk(self, {n:index++}); + chunk.write(data, function(err, chunk) { + if(err) return callback(err); + + chunk.save(function(err, result) { + if(err) return callback(err); + + self.position = self.position + data.length; + + // Point to current chunk + self.currentChunk = chunk; + + if(offset >= stats.size) { + fs.close(file); + self.close(callback); + } else { + return process.nextTick(writeChunk); + } + }); + }); + }); + } + + // Process the first write + process.nextTick(writeChunk); + }); + }); +}; + +/** + * Writes some data. This method will work properly only if initialized with mode + * "w" or "w+". + * + * @param string {string} The data to write. + * @param close {boolean=false} opt_argument Closes this file after writing if + * true. + * @param callback {function(*, GridStore)} This will be called after executing + * this method. The first parameter will contain null and the second one + * will contain a reference to this object. + * + * @ignore + * @api private + */ +var writeBuffer = function(self, buffer, close, callback) { + if(typeof close === "function") { callback = close; close = null; } + var finalClose = (close == null) ? false : close; + + if(self.mode[0] != "w") { + callback(new Error((self.referenceBy == REFERENCE_BY_ID ? self.toHexString() : self.filename) + " not opened for writing"), null); + } else { + if(self.currentChunk.position + buffer.length >= self.chunkSize) { + // Write out the current Chunk and then keep writing until we have less data left than a chunkSize left + // to a new chunk (recursively) + var previousChunkNumber = self.currentChunk.chunkNumber; + var leftOverDataSize = self.chunkSize - self.currentChunk.position; + var firstChunkData = buffer.slice(0, leftOverDataSize); + var leftOverData = buffer.slice(leftOverDataSize); + // A list of chunks to write out + var chunksToWrite = [self.currentChunk.write(firstChunkData)]; + // If we have more data left than the chunk size let's keep writing new chunks + while(leftOverData.length >= self.chunkSize) { + // Create a new chunk and write to it + var newChunk = new Chunk(self, {'n': (previousChunkNumber + 1)}); + var firstChunkData = leftOverData.slice(0, self.chunkSize); + leftOverData = leftOverData.slice(self.chunkSize); + // Update chunk number + previousChunkNumber = previousChunkNumber + 1; + // Write data + newChunk.write(firstChunkData); + // Push chunk to save list + chunksToWrite.push(newChunk); + } + + // Set current chunk with remaining data + self.currentChunk = new Chunk(self, {'n': (previousChunkNumber + 1)}); + // If we have left over data write it + if(leftOverData.length > 0) self.currentChunk.write(leftOverData); + + // Update the position for the gridstore + self.position = self.position + buffer.length; + // Total number of chunks to write + var numberOfChunksToWrite = chunksToWrite.length; + // Write out all the chunks and then return + for(var i = 0; i < chunksToWrite.length; i++) { + var chunk = chunksToWrite[i]; + chunk.save(function(err, result) { + if(err) return callback(err); + + numberOfChunksToWrite = numberOfChunksToWrite - 1; + + if(numberOfChunksToWrite <= 0) { + return callback(null, self); + } + }) + } + } else { + // Update the position for the gridstore + self.position = self.position + buffer.length; + // We have less data than the chunk size just write it and callback + self.currentChunk.write(buffer); + callback(null, self); + } + } +}; + +/** + * Creates a mongoDB object representation of this object. + * + * @param callback {function(object)} This will be called after executing this + * method. The object will be passed to the first parameter and will have + * the structure: + * + *
    
    + *        {
    + *          '_id' : , // {number} id for this file
    + *          'filename' : , // {string} name for this file
    + *          'contentType' : , // {string} mime type for this file
    + *          'length' : , // {number} size of this file?
    + *          'chunksize' : , // {number} chunk size used by this file
    + *          'uploadDate' : , // {Date}
    + *          'aliases' : , // {array of string}
    + *          'metadata' : , // {string}
    + *        }
    + *        
    + * + * @ignore + * @api private + */ +var buildMongoObject = function(self, callback) { + // // Keeps the final chunk number + // var chunkNumber = 0; + // var previousChunkSize = 0; + // // Get the correct chunk Number, if we have an empty chunk return the previous chunk number + // if(null != self.currentChunk && self.currentChunk.chunkNumber > 0 && self.currentChunk.position == 0) { + // chunkNumber = self.currentChunk.chunkNumber - 1; + // } else { + // chunkNumber = self.currentChunk.chunkNumber; + // previousChunkSize = self.currentChunk.position; + // } + + // // Calcuate the length + // var length = self.currentChunk != null ? (chunkNumber * self.chunkSize + previousChunkSize) : 0; + var mongoObject = { + '_id': self.fileId, + 'filename': self.filename, + 'contentType': self.contentType, + 'length': self.position ? self.position : 0, + 'chunkSize': self.chunkSize, + 'uploadDate': self.uploadDate, + 'aliases': self.aliases, + 'metadata': self.metadata + }; + + var md5Command = {filemd5:self.fileId, root:self.root}; + self.db.command(md5Command, function(err, results) { + mongoObject.md5 = results.md5; + callback(mongoObject); + }); +}; + +/** + * Saves this file to the database. This will overwrite the old entry if it + * already exists. This will work properly only if mode was initialized to + * "w" or "w+". + * + * @param {Function} callback this will be called after executing this method. Passes an **{Error}** object to the first parameter and null to the second if an error occured. Otherwise, passes null to the first and a reference to this object to the second. + * @return {null} + * @api public + */ +GridStore.prototype.close = function(callback) { + var self = this; + + if(self.mode[0] == "w") { + if(self.currentChunk != null && self.currentChunk.position > 0) { + self.currentChunk.save(function(err, chunk) { + if(err) return callback(err); + + self.collection(function(err, files) { + if(err) return callback(err); + + // Build the mongo object + if(self.uploadDate != null) { + files.remove({'_id':self.fileId}, {safe:true}, function(err, collection) { + if(err) return callback(err); + + buildMongoObject(self, function(mongoObject) { + files.save(mongoObject, {safe:true}, function(err) { + callback(err, mongoObject); + }); + }); + }); + } else { + self.uploadDate = new Date(); + buildMongoObject(self, function(mongoObject) { + files.save(mongoObject, {safe:true}, function(err) { + callback(err, mongoObject); + }); + }); + } + }); + }); + } else { + self.collection(function(err, files) { + if(err) return callback(err); + + self.uploadDate = new Date(); + buildMongoObject(self, function(mongoObject) { + files.save(mongoObject, {safe:true}, function(err) { + callback(err, mongoObject); + }); + }); + }); + } + } else if(self.mode[0] == "r") { + callback(null, null); + } else { + callback(new Error("Illegal mode " + self.mode), null); + } +}; + +/** + * Gets the nth chunk of this file. + * + * @param chunkNumber {number} The nth chunk to retrieve. + * @param callback {function(*, Chunk|object)} This will be called after + * executing this method. null will be passed to the first parameter while + * a new {@link Chunk} instance will be passed to the second parameter if + * the chunk was found or an empty object {} if not. + * + * @ignore + * @api private + */ +var nthChunk = function(self, chunkNumber, callback) { + self.chunkCollection(function(err, collection) { + if(err) return callback(err); + + collection.find({'files_id':self.fileId, 'n':chunkNumber}, function(err, cursor) { + if(err) return callback(err); + + cursor.nextObject(function(err, chunk) { + if(err) return callback(err); + + var finalChunk = chunk == null ? {} : chunk; + callback(null, new Chunk(self, finalChunk)); + }); + }); + }); +}; + +/** + * + * @ignore + * @api private + */ +GridStore.prototype._nthChunk = function(chunkNumber, callback) { + nthChunk(this, chunkNumber, callback); +} + +/** + * @return {Number} The last chunk number of this file. + * + * @ignore + * @api private + */ +var lastChunkNumber = function(self) { + return Math.floor(self.length/self.chunkSize); +}; + +/** + * Retrieve this file's chunks collection. + * + * @param {Function} callback this will be called after executing this method. An exception object will be passed to the first parameter when an error occured or null otherwise. A new **{Collection}** object will be passed to the second parameter if no error occured. + * @return {null} + * @api public + */ +GridStore.prototype.chunkCollection = function(callback) { + this.db.collection((this.root + ".chunks"), callback); +}; + +/** + * Deletes all the chunks of this file in the database. + * + * @param callback {function(*, boolean)} This will be called after this method + * executes. Passes null to the first and true to the second argument. + * + * @ignore + * @api private + */ +var deleteChunks = function(self, callback) { + if(self.fileId != null) { + self.chunkCollection(function(err, collection) { + if(err) return callback(err, false); + collection.remove({'files_id':self.fileId}, {safe:true}, function(err, result) { + if(err) return callback(err, false); + callback(null, true); + }); + }); + } else { + callback(null, true); + } +}; + +/** + * Deletes all the chunks of this file in the database. + * + * @param {Function} callback this will be called after this method executes. Passes null to the first and true to the second argument. + * @return {null} + * @api public + */ +GridStore.prototype.unlink = function(callback) { + var self = this; + deleteChunks(this, function(err) { + if(err!==null) { + err.message = "at deleteChunks: " + err.message; + return callback(err); + } + + self.collection(function(err, collection) { + if(err!==null) { + err.message = "at collection: " + err.message; + return callback(err); + } + + collection.remove({'_id':self.fileId}, {safe:true}, function(err) { + callback(err, self); + }); + }); + }); +}; + +/** + * Retrieves the file collection associated with this object. + * + * @param {Function} callback this will be called after executing this method. An exception object will be passed to the first parameter when an error occured or null otherwise. A new **{Collection}** object will be passed to the second parameter if no error occured. + * @return {null} + * @api public + */ +GridStore.prototype.collection = function(callback) { + this.db.collection(this.root + ".files", callback); +}; + +/** + * Reads the data of this file. + * + * @param {String} [separator] the character to be recognized as the newline separator. + * @param {Function} callback This will be called after this method is executed. The first parameter will be null and the second parameter will contain an array of strings representing the entire data, each element representing a line including the separator character. + * @return {null} + * @api public + */ +GridStore.prototype.readlines = function(separator, callback) { + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + separator = args.length ? args.shift() : "\n"; + + this.read(function(err, data) { + if(err) return callback(err); + + var items = data.toString().split(separator); + items = items.length > 0 ? items.splice(0, items.length - 1) : []; + for(var i = 0; i < items.length; i++) { + items[i] = items[i] + separator; + } + + callback(null, items); + }); +}; + +/** + * Deletes all the chunks of this file in the database if mode was set to "w" or + * "w+" and resets the read/write head to the initial position. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.rewind = function(callback) { + var self = this; + + if(this.currentChunk.chunkNumber != 0) { + if(this.mode[0] == "w") { + deleteChunks(self, function(err, gridStore) { + if(err) return callback(err); + self.currentChunk = new Chunk(self, {'n': 0}); + self.position = 0; + callback(null, self); + }); + } else { + self.currentChunk(0, function(err, chunk) { + if(err) return callback(err); + self.currentChunk = chunk; + self.currentChunk.rewind(); + self.position = 0; + callback(null, self); + }); + } + } else { + self.currentChunk.rewind(); + self.position = 0; + callback(null, self); + } +}; + +/** + * Retrieves the contents of this file and advances the read/write head. Works with Buffers only. + * + * There are 3 signatures for this method: + * + * (callback) + * (length, callback) + * (length, buffer, callback) + * + * @param {Number} [length] the number of characters to read. Reads all the characters from the read/write head to the EOF if not specified. + * @param {String|Buffer} [buffer] a string to hold temporary data. This is used for storing the string data read so far when recursively calling this method. + * @param {Function} callback this will be called after this method is executed. null will be passed to the first parameter and a string containing the contents of the buffer concatenated with the contents read from this file will be passed to the second. + * @return {null} + * @api public + */ +GridStore.prototype.read = function(length, buffer, callback) { + var self = this; + + var args = Array.prototype.slice.call(arguments, 0); + callback = args.pop(); + length = args.length ? args.shift() : null; + buffer = args.length ? args.shift() : null; + + // The data is a c-terminated string and thus the length - 1 + var finalLength = length == null ? self.length - self.position : length; + var finalBuffer = buffer == null ? new Buffer(finalLength) : buffer; + // Add a index to buffer to keep track of writing position or apply current index + finalBuffer._index = buffer != null && buffer._index != null ? buffer._index : 0; + + if((self.currentChunk.length() - self.currentChunk.position + finalBuffer._index) >= finalLength) { + var slice = self.currentChunk.readSlice(finalLength - finalBuffer._index); + // Copy content to final buffer + slice.copy(finalBuffer, finalBuffer._index); + // Update internal position + self.position = finalBuffer.length; + // Check if we don't have a file at all + if(finalLength == 0 && finalBuffer.length == 0) return callback(new Error("File does not exist"), null); + // Else return data + callback(null, finalBuffer); + } else { + var slice = self.currentChunk.readSlice(self.currentChunk.length() - self.currentChunk.position); + // Copy content to final buffer + slice.copy(finalBuffer, finalBuffer._index); + // Update index position + finalBuffer._index += slice.length; + + // Load next chunk and read more + nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) { + if(err) return callback(err); + + if(chunk.length() > 0) { + self.currentChunk = chunk; + self.read(length, finalBuffer, callback); + } else { + if (finalBuffer._index > 0) { + callback(null, finalBuffer) + } else { + callback(new Error("no chunks found for file, possibly corrupt"), null); + } + } + }); + } +} + +/** + * Retrieves the position of the read/write head of this file. + * + * @param {Function} callback This gets called after this method terminates. null is passed to the first parameter and the position is passed to the second. + * @return {null} + * @api public + */ +GridStore.prototype.tell = function(callback) { + callback(null, this.position); +}; + +/** + * Moves the read/write head to a new location. + * + * There are 3 signatures for this method + * + * Seek Location Modes + * - **GridStore.IO_SEEK_SET**, **(default)** set the position from the start of the file. + * - **GridStore.IO_SEEK_CUR**, set the position from the current position in the file. + * - **GridStore.IO_SEEK_END**, set the position from the end of the file. + * + * @param {Number} [position] the position to seek to + * @param {Number} [seekLocation] seek mode. Use one of the Seek Location modes. + * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.seek = function(position, seekLocation, callback) { + var self = this; + + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + seekLocation = args.length ? args.shift() : null; + + var seekLocationFinal = seekLocation == null ? exports.GridStore.IO_SEEK_SET : seekLocation; + var finalPosition = position; + var targetPosition = 0; + if(seekLocationFinal == exports.GridStore.IO_SEEK_CUR) { + targetPosition = self.position + finalPosition; + } else if(seekLocationFinal == exports.GridStore.IO_SEEK_END) { + targetPosition = self.length + finalPosition; + } else { + targetPosition = finalPosition; + } + + var newChunkNumber = Math.floor(targetPosition/self.chunkSize); + if(newChunkNumber != self.currentChunk.chunkNumber) { + var seekChunk = function() { + nthChunk(self, newChunkNumber, function(err, chunk) { + self.currentChunk = chunk; + self.position = targetPosition; + self.currentChunk.position = (self.position % self.chunkSize); + callback(err, self); + }); + }; + + if(self.mode[0] == 'w') { + self.currentChunk.save(function(err) { + if(err) return callback(err); + seekChunk(); + }); + } else { + seekChunk(); + } + } else { + self.position = targetPosition; + self.currentChunk.position = (self.position % self.chunkSize); + callback(null, self); + } +}; + +/** + * Verify if the file is at EOF. + * + * @return {Boolean} true if the read/write head is at the end of this file. + * @api public + */ +GridStore.prototype.eof = function() { + return this.position == this.length ? true : false; +}; + +/** + * Retrieves a single character from this file. + * + * @param {Function} callback this gets called after this method is executed. Passes null to the first parameter and the character read to the second or null to the second if the read/write head is at the end of the file. + * @return {null} + * @api public + */ +GridStore.prototype.getc = function(callback) { + var self = this; + + if(self.eof()) { + callback(null, null); + } else if(self.currentChunk.eof()) { + nthChunk(self, self.currentChunk.chunkNumber + 1, function(err, chunk) { + self.currentChunk = chunk; + self.position = self.position + 1; + callback(err, self.currentChunk.getc()); + }); + } else { + self.position = self.position + 1; + callback(null, self.currentChunk.getc()); + } +}; + +/** + * Writes a string to the file with a newline character appended at the end if + * the given string does not have one. + * + * @param {String} string the string to write. + * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.puts = function(string, callback) { + var finalString = string.match(/\n$/) == null ? string + "\n" : string; + this.write(finalString, callback); +}; + +/** + * Returns read stream based on this GridStore file + * + * Events + * - **data** {function(item) {}} the data event triggers when a document is ready. + * - **end** {function() {}} the end event triggers when there is no more documents available. + * - **close** {function() {}} the close event triggers when the stream is closed. + * - **error** {function(err) {}} the error event triggers if an error happens. + * + * @param {Boolean} autoclose if true current GridStore will be closed when EOF and 'close' event will be fired + * @return {null} + * @api public + */ +GridStore.prototype.stream = function(autoclose) { + return new ReadStream(autoclose, this); +}; + +/** +* The collection to be used for holding the files and chunks collection. +* +* @classconstant DEFAULT_ROOT_COLLECTION +**/ +GridStore.DEFAULT_ROOT_COLLECTION = 'fs'; + +/** +* Default file mime type +* +* @classconstant DEFAULT_CONTENT_TYPE +**/ +GridStore.DEFAULT_CONTENT_TYPE = 'binary/octet-stream'; + +/** +* Seek mode where the given length is absolute. +* +* @classconstant IO_SEEK_SET +**/ +GridStore.IO_SEEK_SET = 0; + +/** +* Seek mode where the given length is an offset to the current read/write head. +* +* @classconstant IO_SEEK_CUR +**/ +GridStore.IO_SEEK_CUR = 1; + +/** +* Seek mode where the given length is an offset to the end of the file. +* +* @classconstant IO_SEEK_END +**/ +GridStore.IO_SEEK_END = 2; + +/** + * Checks if a file exists in the database. + * + * @param {Db} db the database to query. + * @param {String} name the name of the file to look for. + * @param {String} [rootCollection] the root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**. + * @param {Function} callback this will be called after this method executes. Passes null to the first and passes true to the second if the file exists and false otherwise. + * @return {null} + * @api public + */ +GridStore.exist = function(db, fileIdObject, rootCollection, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + rootCollection = args.length ? args.shift() : null; + + // Fetch collection + var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION; + db.collection(rootCollectionFinal + ".files", function(err, collection) { + if(err) return callback(err); + + // Build query + var query = (typeof fileIdObject == 'string' || Object.prototype.toString.call(fileIdObject) == '[object RegExp]' ) + ? {'filename':fileIdObject} + : {'_id':fileIdObject}; // Attempt to locate file + + collection.find(query, function(err, cursor) { + if(err) return callback(err); + + cursor.nextObject(function(err, item) { + if(err) return callback(err); + callback(null, item == null ? false : true); + }); + }); + }); +}; + +/** + * Gets the list of files stored in the GridFS. + * + * @param {Db} db the database to query. + * @param {String} [rootCollection] the root collection that holds the files and chunks collection. Defaults to **{GridStore.DEFAULT_ROOT_COLLECTION}**. + * @param {Function} callback this will be called after this method executes. Passes null to the first and passes an array of strings containing the names of the files. + * @return {null} + * @api public + */ +GridStore.list = function(db, rootCollection, options, callback) { + var args = Array.prototype.slice.call(arguments, 1); + callback = args.pop(); + rootCollection = args.length ? args.shift() : null; + options = args.length ? args.shift() : {}; + + // Ensure we have correct values + if(rootCollection != null && typeof rootCollection == 'object') { + options = rootCollection; + rootCollection = null; + } + + // Check if we are returning by id not filename + var byId = options['id'] != null ? options['id'] : false; + // Fetch item + var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION; + var items = []; + db.collection((rootCollectionFinal + ".files"), function(err, collection) { + if(err) return callback(err); + + collection.find(function(err, cursor) { + if(err) return callback(err); + + cursor.each(function(err, item) { + if(item != null) { + items.push(byId ? item._id : item.filename); + } else { + callback(err, items); + } + }); + }); + }); +}; + +/** + * Reads the contents of a file. + * + * This method has the following signatures + * + * (db, name, callback) + * (db, name, length, callback) + * (db, name, length, offset, callback) + * (db, name, length, offset, options, callback) + * + * @param {Db} db the database to query. + * @param {String} name the name of the file. + * @param {Number} [length] the size of data to read. + * @param {Number} [offset] the offset from the head of the file of which to start reading from. + * @param {Object} [options] the options for the file. + * @param {Function} callback this will be called after this method executes. A string with an error message will be passed to the first parameter when the length and offset combination exceeds the length of the file while an Error object will be passed if other forms of error occured, otherwise, a string is passed. The second parameter will contain the data read if successful or null if an error occured. + * @return {null} + * @api public + */ +GridStore.read = function(db, name, length, offset, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + length = args.length ? args.shift() : null; + offset = args.length ? args.shift() : null; + options = args.length ? args.shift() : null; + + new GridStore(db, name, "r", options).open(function(err, gridStore) { + if(err) return callback(err); + // Make sure we are not reading out of bounds + if(offset && offset >= gridStore.length) return callback("offset larger than size of file", null); + if(length && length > gridStore.length) return callback("length is larger than the size of the file", null); + if(offset && length && (offset + length) > gridStore.length) return callback("offset and length is larger than the size of the file", null); + + if(offset != null) { + gridStore.seek(offset, function(err, gridStore) { + if(err) return callback(err); + gridStore.read(length, callback); + }); + } else { + gridStore.read(length, callback); + } + }); +}; + +/** + * Reads the data of this file. + * + * @param {Db} db the database to query. + * @param {String} name the name of the file. + * @param {String} [separator] the character to be recognized as the newline separator. + * @param {Object} [options] file options. + * @param {Function} callback this will be called after this method is executed. The first parameter will be null and the second parameter will contain an array of strings representing the entire data, each element representing a line including the separator character. + * @return {null} + * @api public + */ +GridStore.readlines = function(db, name, separator, options, callback) { + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + separator = args.length ? args.shift() : null; + options = args.length ? args.shift() : null; + + var finalSeperator = separator == null ? "\n" : separator; + new GridStore(db, name, "r", options).open(function(err, gridStore) { + if(err) return callback(err); + gridStore.readlines(finalSeperator, callback); + }); +}; + +/** + * Deletes the chunks and metadata information of a file from GridFS. + * + * @param {Db} db the database to interact with. + * @param {String|Array} names the name/names of the files to delete. + * @param {Object} [options] the options for the files. + * @callback {Function} this will be called after this method is executed. The first parameter will contain an Error object if an error occured or null otherwise. The second parameter will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.unlink = function(db, names, options, callback) { + var self = this; + var args = Array.prototype.slice.call(arguments, 2); + callback = args.pop(); + options = args.length ? args.shift() : null; + + if(names.constructor == Array) { + var tc = 0; + for(var i = 0; i < names.length; i++) { + ++tc; + self.unlink(db, names[i], function(result) { + if(--tc == 0) { + callback(null, self); + } + }); + } + } else { + new GridStore(db, names, "w", options).open(function(err, gridStore) { + if(err) return callback(err); + deleteChunks(gridStore, function(err, result) { + if(err) return callback(err); + gridStore.collection(function(err, collection) { + if(err) return callback(err); + collection.remove({'_id':gridStore.fileId}, {safe:true}, function(err, collection) { + callback(err, self); + }); + }); + }); + }); + } +}; + +/** + * Returns the current chunksize of the file. + * + * @field chunkSize + * @type {Number} + * @getter + * @setter + * @property return number of bytes in the current chunkSize. + */ +Object.defineProperty(GridStore.prototype, "chunkSize", { enumerable: true + , get: function () { + return this.internalChunkSize; + } + , set: function(value) { + if(!(this.mode[0] == "w" && this.position == 0 && this.uploadDate == null)) { + this.internalChunkSize = this.internalChunkSize; + } else { + this.internalChunkSize = value; + } + } +}); + +/** + * The md5 checksum for this file. + * + * @field md5 + * @type {Number} + * @getter + * @setter + * @property return this files md5 checksum. + */ +Object.defineProperty(GridStore.prototype, "md5", { enumerable: true + , get: function () { + return this.internalMd5; + } +}); + +/** + * GridStore Streaming methods + * Handles the correct return of the writeable stream status + * @ignore + */ +Object.defineProperty(GridStore.prototype, "writable", { enumerable: true + , get: function () { + if(this._writeable == null) { + this._writeable = this.mode != null && this.mode.indexOf("w") != -1; + } + // Return the _writeable + return this._writeable; + } + , set: function(value) { + this._writeable = value; + } +}); + +/** + * Handles the correct return of the readable stream status + * @ignore + */ +Object.defineProperty(GridStore.prototype, "readable", { enumerable: true + , get: function () { + if(this._readable == null) { + this._readable = this.mode != null && this.mode.indexOf("r") != -1; + } + return this._readable; + } + , set: function(value) { + this._readable = value; + } +}); + +GridStore.prototype.paused; + +/** + * Handles the correct setting of encoding for the stream + * @ignore + */ +GridStore.prototype.setEncoding = fs.ReadStream.prototype.setEncoding; + +/** + * Handles the end events + * @ignore + */ +GridStore.prototype.end = function end(data) { + var self = this; + // allow queued data to write before closing + if(!this.writable) return; + this.writable = false; + + if(data) { + this._q.push(data); + } + + this.on('drain', function () { + self.close(function (err) { + if (err) return _error(self, err); + self.emit('close'); + }); + }); + + _flush(self); +} + +/** + * Handles the normal writes to gridstore + * @ignore + */ +var _writeNormal = function(self, data, close, callback) { + // If we have a buffer write it using the writeBuffer method + if(Buffer.isBuffer(data)) { + return writeBuffer(self, data, close, callback); + } else { + // Wrap the string in a buffer and write + return writeBuffer(self, new Buffer(data, 'binary'), close, callback); + } +} + +/** + * Writes some data. This method will work properly only if initialized with mode "w" or "w+". + * + * @param {String|Buffer} data the data to write. + * @param {Boolean} [close] closes this file after writing if set to true. + * @param {Function} callback this will be called after executing this method. The first parameter will contain null and the second one will contain a reference to this object. + * @return {null} + * @api public + */ +GridStore.prototype.write = function write(data, close, callback) { + // If it's a normal write delegate the call + if(typeof close == 'function' || typeof callback == 'function') { + return _writeNormal(this, data, close, callback); + } + + // Otherwise it's a stream write + var self = this; + if (!this.writable) { + throw new Error('GridWriteStream is not writable'); + } + + // queue data until we open. + if (!this._opened) { + // Set up a queue to save data until gridstore object is ready + this._q = []; + _openStream(self); + this._q.push(data); + return false; + } + + // Push data to queue + this._q.push(data); + _flush(this); + // Return write successful + return true; +} + +/** + * Handles the destroy part of a stream + * @ignore + */ +GridStore.prototype.destroy = function destroy() { + // close and do not emit any more events. queued data is not sent. + if(!this.writable) return; + this.readable = false; + if(this.writable) { + this.writable = false; + this._q.length = 0; + this.emit('close'); + } +} + +/** + * Handles the destroySoon part of a stream + * @ignore + */ +GridStore.prototype.destroySoon = function destroySoon() { + // as soon as write queue is drained, destroy. + // may call destroy immediately if no data is queued. + if(!this._q.length) { + return this.destroy(); + } + this._destroying = true; +} + +/** + * Handles the pipe part of the stream + * @ignore + */ +GridStore.prototype.pipe = function(destination, options) { + var self = this; + // Open the gridstore + this.open(function(err, result) { + if(err) _errorRead(self, err); + if(!self.readable) return; + // Set up the pipe + self._pipe(destination, options); + // Emit the stream is open + self.emit('open'); + // Read from the stream + _read(self); + }) +} + +/** + * Internal module methods + * @ignore + */ +var _read = function _read(self) { + if (!self.readable || self.paused || self.reading) { + return; + } + + self.reading = true; + var stream = self._stream = self.stream(); + stream.paused = self.paused; + + stream.on('data', function (data) { + if (self._decoder) { + var str = self._decoder.write(data); + if (str.length) self.emit('data', str); + } else { + self.emit('data', data); + } + }); + + stream.on('end', function (data) { + self.emit('end', data); + }); + + stream.on('error', function (data) { + _errorRead(self, data); + }); + + stream.on('close', function (data) { + self.emit('close', data); + }); + + self.pause = function () { + // native doesn't always pause. + // bypass its pause() method to hack it + self.paused = stream.paused = true; + } + + self.resume = function () { + if(!self.paused) return; + + self.paused = false; + stream.resume(); + self.readable = stream.readable; + } + + self.destroy = function () { + self.readable = false; + stream.destroy(); + } +} + +/** + * pause + * @ignore + */ +GridStore.prototype.pause = function pause () { + // Overridden when the GridStore opens. + this.paused = true; +} + +/** + * resume + * @ignore + */ +GridStore.prototype.resume = function resume () { + // Overridden when the GridStore opens. + this.paused = false; +} + +/** + * Internal module methods + * @ignore + */ +var _flush = function _flush(self, _force) { + if (!self._opened) return; + if (!_force && self._flushing) return; + self._flushing = true; + + // write the entire q to gridfs + if (!self._q.length) { + self._flushing = false; + self.emit('drain'); + + if(self._destroying) { + self.destroy(); + } + return; + } + + self.write(self._q.shift(), function (err, store) { + if (err) return _error(self, err); + self.emit('progress', store.position); + _flush(self, true); + }); +} + +var _openStream = function _openStream (self) { + if(self._opening == true) return; + self._opening = true; + + // Open the store + self.open(function (err, gridstore) { + if (err) return _error(self, err); + self._opened = true; + self.emit('open'); + _flush(self); + }); +} + +var _error = function _error(self, err) { + self.destroy(); + self.emit('error', err); +} + +var _errorRead = function _errorRead (self, err) { + self.readable = false; + self.emit('error', err); +} + +/** + * @ignore + * @api private + */ +exports.GridStore = GridStore; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/readstream.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/readstream.js new file mode 100644 index 0000000..ebb09bd --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/gridfs/readstream.js @@ -0,0 +1,188 @@ +var Stream = require('stream').Stream, + util = require('util'); + +/** + * ReadStream + * + * Returns a stream interface for the **file**. + * + * Events + * - **data** {function(item) {}} the data event triggers when a document is ready. + * - **end** {function() {}} the end event triggers when there is no more documents available. + * - **close** {function() {}} the close event triggers when the stream is closed. + * - **error** {function(err) {}} the error event triggers if an error happens. + * + * @class Represents a GridFS File Stream. + * @param {Boolean} autoclose automatically close file when the stream reaches the end. + * @param {GridStore} cursor a cursor object that the stream wraps. + * @return {ReadStream} + */ +function ReadStream(autoclose, gstore) { + if (!(this instanceof ReadStream)) return new ReadStream(autoclose, gstore); + Stream.call(this); + + this.autoclose = !!autoclose; + this.gstore = gstore; + + this.finalLength = gstore.length - gstore.position; + this.completedLength = 0; + this.currentChunkNumber = gstore.currentChunk.chunkNumber; + + this.paused = false; + this.readable = true; + this.pendingChunk = null; + this.executing = false; + + // Calculate the number of chunks + this.numberOfChunks = Math.ceil(gstore.length/gstore.chunkSize); + + // This seek start position inside the current chunk + this.seekStartPosition = gstore.position - (this.currentChunkNumber * gstore.chunkSize); + + var self = this; + process.nextTick(function() { + self._execute(); + }); +}; + +/** + * Inherit from Stream + * @ignore + * @api private + */ +ReadStream.prototype.__proto__ = Stream.prototype; + +/** + * Flag stating whether or not this stream is readable. + */ +ReadStream.prototype.readable; + +/** + * Flag stating whether or not this stream is paused. + */ +ReadStream.prototype.paused; + +/** + * @ignore + * @api private + */ +ReadStream.prototype._execute = function() { + if(this.paused === true || this.readable === false) { + return; + } + + var gstore = this.gstore; + var self = this; + // Set that we are executing + this.executing = true; + + var last = false; + var toRead = 0; + + if(gstore.currentChunk.chunkNumber >= (this.numberOfChunks - 1)) { + self.executing = false; + last = true; + } + + // Data setup + var data = null; + + // Read a slice (with seek set if none) + if(this.seekStartPosition > 0 && (gstore.currentChunk.length() - this.seekStartPosition) > 0) { + data = gstore.currentChunk.readSlice(gstore.currentChunk.length() - this.seekStartPosition); + this.seekStartPosition = 0; + } else { + data = gstore.currentChunk.readSlice(gstore.currentChunk.length()); + } + + // Return the data + if(data != null && gstore.currentChunk.chunkNumber == self.currentChunkNumber) { + self.currentChunkNumber = self.currentChunkNumber + 1; + self.completedLength += data.length; + self.pendingChunk = null; + self.emit("data", data); + } + + if(last === true) { + self.readable = false; + self.emit("end"); + + if(self.autoclose === true) { + if(gstore.mode[0] == "w") { + gstore.close(function(err, doc) { + if (err) { + self.emit("error", err); + return; + } + self.readable = false; + self.emit("close", doc); + }); + } else { + self.readable = false; + self.emit("close"); + } + } + } else { + gstore._nthChunk(gstore.currentChunk.chunkNumber + 1, function(err, chunk) { + if(err) { + self.readable = false; + self.emit("error", err); + self.executing = false; + return; + } + + self.pendingChunk = chunk; + if(self.paused === true) { + self.executing = false; + return; + } + + gstore.currentChunk = self.pendingChunk; + self._execute(); + }); + } +}; + +/** + * Pauses this stream, then no farther events will be fired. + * + * @ignore + * @api public + */ +ReadStream.prototype.pause = function() { + if(!this.executing) { + this.paused = true; + } +}; + +/** + * Destroys the stream, then no farther events will be fired. + * + * @ignore + * @api public + */ +ReadStream.prototype.destroy = function() { + this.readable = false; + // Emit close event + this.emit("close"); +}; + +/** + * Resumes this stream. + * + * @ignore + * @api public + */ +ReadStream.prototype.resume = function() { + if(this.paused === false || !this.readable) { + return; + } + + this.paused = false; + var self = this; + process.nextTick(function() { + self._execute(); + }); +}; + +exports.ReadStream = ReadStream; diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/index.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/index.js new file mode 100644 index 0000000..6a2b727 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/index.js @@ -0,0 +1,69 @@ +try { + exports.BSONPure = require('bson').BSONPure; + exports.BSONNative = require('bson').BSONNative; +} catch(err) { + // do nothing +} + +[ 'commands/base_command' + , 'admin' + , 'collection' + , 'connection/read_preference' + , 'connection/connection' + , 'connection/server' + , 'connection/mongos' + , 'connection/repl_set' + , 'mongo_client' + , 'cursor' + , 'db' + , 'mongo_client' + , 'gridfs/grid' + , 'gridfs/chunk' + , 'gridfs/gridstore'].forEach(function (path) { + var module = require('./' + path); + for (var i in module) { + exports[i] = module[i]; + } + + // backwards compat + exports.ReplSetServers = exports.ReplSet; + + // Add BSON Classes + exports.Binary = require('bson').Binary; + exports.Code = require('bson').Code; + exports.DBRef = require('bson').DBRef; + exports.Double = require('bson').Double; + exports.Long = require('bson').Long; + exports.MinKey = require('bson').MinKey; + exports.MaxKey = require('bson').MaxKey; + exports.ObjectID = require('bson').ObjectID; + exports.Symbol = require('bson').Symbol; + exports.Timestamp = require('bson').Timestamp; + + // Add BSON Parser + exports.BSON = require('bson').BSONPure.BSON; + +}); + +// Get the Db object +var Db = require('./db').Db; +// Set up the connect function +var connect = Db.connect; +var obj = connect; +// Map all values to the exports value +for(var name in exports) { + obj[name] = exports[name]; +} + +// Add the pure and native backward compatible functions +exports.pure = exports.native = function() { + return obj; +} + +// Map all values to the exports value +for(var name in exports) { + connect[name] = exports[name]; +} + +// Set our exports to be the connect function +module.exports = connect; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/mongo_client.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/mongo_client.js new file mode 100644 index 0000000..cfc9e6f --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/mongo_client.js @@ -0,0 +1,116 @@ +var Db = require('./db').Db; + +/** + * Create a new MongoClient instance. + * + * Options + * - **w**, {Number/String, > -1 || 'majority' || tag name} the write concern for the operation where < 1 is no acknowlegement of write and w >= 1, w = 'majority' or tag acknowledges the write + * - **wtimeout**, {Number, 0} set the timeout for waiting for write concern to finish (combines with w option) + * - **fsync**, (Boolean, default:false) write waits for fsync before returning + * - **journal**, (Boolean, default:false) write waits for journal sync before returning + * - **readPreference** {String}, the prefered read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). + * - **native_parser** {Boolean, default:false}, use c++ bson parser. + * - **forceServerObjectId** {Boolean, default:false}, force server to create _id fields instead of client. + * - **pkFactory** {Object}, object overriding the basic ObjectID primary key generation. + * - **serializeFunctions** {Boolean, default:false}, serialize functions. + * - **raw** {Boolean, default:false}, peform operations using raw bson buffers. + * - **recordQueryStats** {Boolean, default:false}, record query statistics during execution. + * - **retryMiliSeconds** {Number, default:5000}, number of miliseconds between retries. + * - **numberOfRetries** {Number, default:5}, number of retries off connection. + * + * Deprecated Options + * - **safe** {true | {w:n, wtimeout:n} | {fsync:true}, default:false}, executes with a getLastError command returning the results of the command on MongoDB. + * + * @class Represents a MongoClient + * @param {Object} serverConfig server config object. + * @param {Object} [options] additional options for the collection. + */ +function MongoClient(serverConfig, options) { + options = options == null ? {} : options; + // If no write concern is set set the default to w:1 + if(options != null && !options.journal && !options.w && !options.fsync) { + options.w = 1; + } + + // The internal db instance we are wrapping + this._db = new Db('test', serverConfig, options); +} + +/** + * Initialize the database connection. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the connected mongoclient or null if an error occured. + * @return {null} + * @api public + */ +MongoClient.prototype.open = function(callback) { + // Self reference + var self = this; + + this._db.open(function(err, db) { + if(err) return callback(err, null); + callback(null, self); + }) +} + +/** + * Close the current db connection, including all the child db instances. Emits close event if no callback is provided. + * + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the results from the close method or null if an error occured. + * @return {null} + * @api public + */ +MongoClient.prototype.close = function(callback) { + this._db.close(callback); +} + +/** + * Create a new Db instance sharing the current socket connections. + * + * @param {String} dbName the name of the database we want to use. + * @return {Db} a db instance using the new database. + * @api public + */ +MongoClient.prototype.db = function(dbName) { + return this._db.db(dbName); +} + +/** + * Connect to MongoDB using a url as documented at + * + * www.mongodb.org/display/DOCS/Connections + * + * Options + * - **uri_decode_auth** {Boolean, default:false} uri decode the user name and password for authentication + * - **db** {Object, default: null} a hash off options to set on the db object, see **Db constructor** + * - **server** {Object, default: null} a hash off options to set on the server objects, see **Server** constructor** + * - **replSet** {Object, default: null} a hash off options to set on the replSet object, see **ReplSet** constructor** + * - **mongos** {Object, default: null} a hash off options to set on the mongos object, see **Mongos** constructor** + * + * @param {String} url connection url for MongoDB. + * @param {Object} [options] optional options for insert command + * @param {Function} callback this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the initialized db object or null if an error occured. + * @return {null} + * @api public + */ +MongoClient.connect = function(url, options, callback) { + if(typeof options == 'function') { + callback = options; + options = {}; + } + + Db.connect(url, options, function(err, db) { + if(err) return callback(err, null); + + if(db.options !== null && !db.options.safe && !db.options.journal + && !db.options.w && !db.options.fsync && typeof db.options.w != 'number' + && (db.options.safe == false && url.indexOf("safe=") == -1)) { + db.options.w = 1; + } + + // Return the db + callback(null, db); + }); +} + +exports.MongoClient = MongoClient; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js new file mode 100644 index 0000000..b129dc6 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js @@ -0,0 +1,140 @@ +var Long = require('bson').Long; + +/** + Reply message from mongo db +**/ +var MongoReply = exports.MongoReply = function() { + this.documents = []; + this.index = 0; +}; + +MongoReply.prototype.parseHeader = function(binary_reply, bson) { + // Unpack the standard header first + this.messageLength = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + // Fetch the request id for this reply + this.requestId = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + // Fetch the id of the request that triggered the response + this.responseTo = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + // Skip op-code field + this.index = this.index + 4 + 4; + // Unpack the reply message + this.responseFlag = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + // Unpack the cursor id (a 64 bit long integer) + var low_bits = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + var high_bits = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + this.cursorId = new Long(low_bits, high_bits); + // Unpack the starting from + this.startingFrom = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; + // Unpack the number of objects returned + this.numberReturned = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + this.index = this.index + 4; +} + +MongoReply.prototype.parseBody = function(binary_reply, bson, raw, callback) { + raw = raw == null ? false : raw; + // Just set a doc limit for deserializing + var docLimitSize = 1024*20; + + // If our message length is very long, let's switch to process.nextTick for messages + if(this.messageLength > docLimitSize) { + var batchSize = this.numberReturned; + this.documents = new Array(this.numberReturned); + + // Just walk down until we get a positive number >= 1 + for(var i = 50; i > 0; i--) { + if((this.numberReturned/i) >= 1) { + batchSize = i; + break; + } + } + + // Actual main creator of the processFunction setting internal state to control the flow + var parseFunction = function(_self, _binary_reply, _batchSize, _numberReturned) { + var object_index = 0; + // Internal loop process that will use nextTick to ensure we yield some time + var processFunction = function() { + // Adjust batchSize if we have less results left than batchsize + if((_numberReturned - object_index) < _batchSize) { + _batchSize = _numberReturned - object_index; + } + + // If raw just process the entries + if(raw) { + // Iterate over the batch + for(var i = 0; i < _batchSize; i++) { + // Are we done ? + if(object_index <= _numberReturned) { + // Read the size of the bson object + var bsonObjectSize = _binary_reply[_self.index] | _binary_reply[_self.index + 1] << 8 | _binary_reply[_self.index + 2] << 16 | _binary_reply[_self.index + 3] << 24; + // If we are storing the raw responses to pipe straight through + _self.documents[object_index] = binary_reply.slice(_self.index, _self.index + bsonObjectSize); + // Adjust binary index to point to next block of binary bson data + _self.index = _self.index + bsonObjectSize; + // Update number of docs parsed + object_index = object_index + 1; + } + } + } else { + try { + // Parse documents + _self.index = bson.deserializeStream(binary_reply, _self.index, _batchSize, _self.documents, object_index); + // Adjust index + object_index = object_index + _batchSize; + } catch (err) { + return callback(err); + } + } + + // If we hav more documents process NextTick + if(object_index < _numberReturned) { + process.nextTick(processFunction); + } else { + callback(null); + } + } + + // Return the process function + return processFunction; + }(this, binary_reply, batchSize, this.numberReturned)(); + } else { + try { + // Let's unpack all the bson documents, deserialize them and store them + for(var object_index = 0; object_index < this.numberReturned; object_index++) { + // Read the size of the bson object + var bsonObjectSize = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24; + // If we are storing the raw responses to pipe straight through + if(raw) { + // Deserialize the object and add to the documents array + this.documents.push(binary_reply.slice(this.index, this.index + bsonObjectSize)); + } else { + // Deserialize the object and add to the documents array + this.documents.push(bson.deserialize(binary_reply.slice(this.index, this.index + bsonObjectSize))); + } + // Adjust binary index to point to next block of binary bson data + this.index = this.index + bsonObjectSize; + } + } catch(err) { + return callback(err); + } + + // No error return + callback(null); + } +} + +MongoReply.prototype.is_error = function(){ + if(this.documents.length == 1) { + return this.documents[0].ok == 1 ? false : true; + } + return false; +}; + +MongoReply.prototype.error_message = function() { + return this.documents.length == 1 && this.documents[0].ok == 1 ? '' : this.documents[0].errmsg; +}; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/utils.js b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/utils.js new file mode 100644 index 0000000..120a584 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/utils.js @@ -0,0 +1,97 @@ +/** + * Sort functions, Normalize and prepare sort parameters + */ +var formatSortValue = exports.formatSortValue = function(sortDirection) { + var value = ("" + sortDirection).toLowerCase(); + + switch (value) { + case 'ascending': + case 'asc': + case '1': + return 1; + case 'descending': + case 'desc': + case '-1': + return -1; + default: + throw new Error("Illegal sort clause, must be of the form " + + "[['field1', '(ascending|descending)'], " + + "['field2', '(ascending|descending)']]"); + } +}; + +var formattedOrderClause = exports.formattedOrderClause = function(sortValue) { + var orderBy = {}; + + if (Array.isArray(sortValue)) { + for(var i = 0; i < sortValue.length; i++) { + if(sortValue[i].constructor == String) { + orderBy[sortValue[i]] = 1; + } else { + orderBy[sortValue[i][0]] = formatSortValue(sortValue[i][1]); + } + } + } else if(Object.prototype.toString.call(sortValue) === '[object Object]') { + orderBy = sortValue; + } else if (sortValue.constructor == String) { + orderBy[sortValue] = 1; + } else { + throw new Error("Illegal sort clause, must be of the form " + + "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"); + } + + return orderBy; +}; + +exports.encodeInt = function(value) { + var buffer = new Buffer(4); + buffer[3] = (value >> 24) & 0xff; + buffer[2] = (value >> 16) & 0xff; + buffer[1] = (value >> 8) & 0xff; + buffer[0] = value & 0xff; + return buffer; +} + +exports.encodeIntInPlace = function(value, buffer, index) { + buffer[index + 3] = (value >> 24) & 0xff; + buffer[index + 2] = (value >> 16) & 0xff; + buffer[index + 1] = (value >> 8) & 0xff; + buffer[index] = value & 0xff; +} + +exports.encodeCString = function(string) { + var buf = new Buffer(string, 'utf8'); + return [buf, new Buffer([0])]; +} + +exports.decodeUInt32 = function(array, index) { + return array[index] | array[index + 1] << 8 | array[index + 2] << 16 | array[index + 3] << 24; +} + +// Decode the int +exports.decodeUInt8 = function(array, index) { + return array[index]; +} + +/** + * Context insensitive type checks + */ + +var toString = Object.prototype.toString; + +exports.isObject = function (arg) { + return '[object Object]' == toString.call(arg) +} + +exports.isArray = function (arg) { + return Array.isArray(arg) || + 'object' == typeof arg && '[object Array]' == toString.call(arg) +} + +exports.isDate = function (arg) { + return 'object' == typeof arg && '[object Date]' == toString.call(arg) +} + +exports.isRegExp = function (arg) { + return 'object' == typeof arg && '[object RegExp]' == toString.call(arg) +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/.travis.yml b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/.travis.yml new file mode 100644 index 0000000..94740d0 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - 0.9 # development version of 0.8, may be unstable \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/Makefile b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/Makefile new file mode 100644 index 0000000..2ca5592 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/Makefile @@ -0,0 +1,16 @@ +NODE = node +NPM = npm +NODEUNIT = node_modules/nodeunit/bin/nodeunit + +all: clean node_gyp + +test: clean node_gyp + npm test + +node_gyp: clean + node-gyp configure build + +clean: + node-gyp clean + +.PHONY: all diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/README.md b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/README.md new file mode 100644 index 0000000..73892e2 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/README.md @@ -0,0 +1 @@ +A JS/C++ Bson parser for node, used in the MongoDB Native driver \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/benchmarks/benchmarks.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/benchmarks/benchmarks.js new file mode 100644 index 0000000..45a1111 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/benchmarks/benchmarks.js @@ -0,0 +1,130 @@ +// var BSON = require('../../lib/mongodb').BSONNative.BSON, +// ObjectID = require('../../lib/mongodb').BSONNative.ObjectID, +// Code = require('../../lib/mongodb').BSONNative.Code, +// Long = require('../../lib/mongodb').BSONNative.Long, +// Binary = require('../../lib/mongodb').BSONNative.Binary, +// debug = require('util').debug, +// inspect = require('util').inspect, +// +// Long = require('../../lib/mongodb').Long, +// ObjectID = require('../../lib/mongodb').ObjectID, +// Binary = require('../../lib/mongodb').Binary, +// Code = require('../../lib/mongodb').Code, +// DBRef = require('../../lib/mongodb').DBRef, +// Symbol = require('../../lib/mongodb').Symbol, +// Double = require('../../lib/mongodb').Double, +// MaxKey = require('../../lib/mongodb').MaxKey, +// MinKey = require('../../lib/mongodb').MinKey, +// Timestamp = require('../../lib/mongodb').Timestamp; + + +// var BSON = require('../../lib/mongodb').BSONPure.BSON, +// ObjectID = require('../../lib/mongodb').BSONPure.ObjectID, +// Code = require('../../lib/mongodb').BSONPure.Code, +// Long = require('../../lib/mongodb').BSONPure.Long, +// Binary = require('../../lib/mongodb').BSONPure.Binary; + +var BSON = require('../lib/bson').BSONNative.BSON, + Long = require('../lib/bson').Long, + ObjectID = require('../lib/bson').ObjectID, + Binary = require('../lib/bson').Binary, + Code = require('../lib/bson').Code, + DBRef = require('../lib/bson').DBRef, + Symbol = require('../lib/bson').Symbol, + Double = require('../lib/bson').Double, + MaxKey = require('../lib/bson').MaxKey, + MinKey = require('../lib/bson').MinKey, + Timestamp = require('../lib/bson').Timestamp; + + // console.dir(require('../lib/bson')) + +var COUNT = 1000; +var COUNT = 100; + +var object = { + string: "Strings are great", + decimal: 3.14159265, + bool: true, + integer: 5, + date: new Date(), + double: new Double(1.4), + id: new ObjectID(), + min: new MinKey(), + max: new MaxKey(), + symbol: new Symbol('hello'), + long: Long.fromNumber(100), + bin: new Binary(new Buffer(100)), + + subObject: { + moreText: "Bacon ipsum dolor sit amet cow pork belly rump ribeye pastrami andouille. Tail hamburger pork belly, drumstick flank salami t-bone sirloin pork chop ribeye ham chuck pork loin shankle. Ham fatback pork swine, sirloin shankle short loin andouille shank sausage meatloaf drumstick. Pig chicken cow bresaola, pork loin jerky meatball tenderloin brisket strip steak jowl spare ribs. Biltong sirloin pork belly boudin, bacon pastrami rump chicken. Jowl rump fatback, biltong bacon t-bone turkey. Turkey pork loin boudin, tenderloin jerky beef ribs pastrami spare ribs biltong pork chop beef.", + longKeylongKeylongKeylongKeylongKeylongKey: "Pork belly boudin shoulder ribeye pork chop brisket biltong short ribs. Salami beef pork belly, t-bone sirloin meatloaf tail jowl spare ribs. Sirloin biltong bresaola cow turkey. Biltong fatback meatball, bresaola tail shankle turkey pancetta ham ribeye flank bacon jerky pork chop. Boudin sirloin shoulder, salami swine flank jerky t-bone pork chop pork beef tongue. Bresaola ribeye jerky andouille. Ribeye ground round sausage biltong beef ribs chuck, shank hamburger chicken short ribs spare ribs tenderloin meatloaf pork loin." + }, + + subArray: [1,2,3,4,5,6,7,8,9,10], + anotherString: "another string", + code: new Code("function() {}", {i:1}) +} + +// Number of objects +var numberOfObjects = 10000; +var bson = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +console.log("---------------------- 1") +var s = new Date() +// Object serialized +for(var i = 0; i < numberOfObjects; i++) { + objectBSON = bson.serialize(object, null, true) +} +console.log("====================== " + (new Date().getTime() - s.getTime()) + " :: " + ((new Date().getTime() - s.getTime()))/numberOfObjects) + +console.log("---------------------- 2") +var s = new Date() +// Object serialized +for(var i = 0; i < numberOfObjects; i++) { + bson.deserialize(objectBSON); +} +console.log("====================== " + (new Date().getTime() - s.getTime()) + " :: " + ((new Date().getTime() - s.getTime()))/numberOfObjects) + +// // Buffer With copies of the objectBSON +// var data = new Buffer(objectBSON.length * numberOfObjects); +// var index = 0; +// +// // Copy the buffer 1000 times to create a strea m of objects +// for(var i = 0; i < numberOfObjects; i++) { +// // Copy data +// objectBSON.copy(data, index); +// // Adjust index +// index = index + objectBSON.length; +// } +// +// // console.log("-----------------------------------------------------------------------------------") +// // console.dir(objectBSON) +// +// var x, start, end, j +// var objectBSON, objectJSON +// +// // Allocate the return array (avoid concatinating everything) +// var results = new Array(numberOfObjects); +// +// console.log(COUNT + "x (objectBSON = BSON.serialize(object))") +// start = new Date +// +// // var objects = BSON.deserializeStream(data, 0, numberOfObjects); +// // console.log("----------------------------------------------------------------------------------- 0") +// // var objects = BSON.deserialize(data); +// // console.log("----------------------------------------------------------------------------------- 1") +// // console.dir(objects) +// +// for (j=COUNT; --j>=0; ) { +// var nextIndex = BSON.deserializeStream(data, 0, numberOfObjects, results, 0); +// } +// +// end = new Date +// var opsprsecond = COUNT / ((end - start)/1000); +// console.log("bson size (bytes): ", objectBSON.length); +// console.log("time = ", end - start, "ms -", COUNT / ((end - start)/1000), " ops/sec"); +// console.log("MB/s = " + ((opsprsecond*objectBSON.length)/1024)); +// +// // console.dir(nextIndex) +// // console.dir(results) + + diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/binding.gyp b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/binding.gyp new file mode 100644 index 0000000..42445d3 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/binding.gyp @@ -0,0 +1,17 @@ +{ + 'targets': [ + { + 'target_name': 'bson', + 'sources': [ 'ext/bson.cc' ], + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' + } + }] + ] + } + ] +} \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Makefile b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Makefile new file mode 100644 index 0000000..5c7ea72 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Makefile @@ -0,0 +1,359 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := .. +abs_srcdir := $(abspath $(srcdir)) + +# The name of the builddir. +builddir_name ?= . + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Release + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + + + +# C++ apps need to be linked with g++. +# +# Note: flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override LINK via an envrionment variable as follows: +# +# export LINK=g++ +# +# This will allow make to invoke N linker processes as specified in -jN. +LINK ?= ./gyp-mac-tool flock $(builddir)/linker.lock $(CXX) + +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crs + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crs + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_objc = CXX($(TOOLSET)) $@ +cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< + +quiet_cmd_objcxx = CXX($(TOOLSET)) $@ +cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# Commands for precompiled header files. +quiet_cmd_pch_c = CXX($(TOOLSET)) $@ +cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ +cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< +quiet_cmd_pch_m = CXX($(TOOLSET)) $@ +cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< +quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ +cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< + +# gyp-mac-tool is written next to the root Makefile by gyp. +# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd +# already. +quiet_cmd_mac_tool = MACTOOL $(4) $< +cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" + +quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ +cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) + +quiet_cmd_infoplist = INFOPLIST $@ +cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = LIBTOOL-STATIC $@ +cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool -static -o $@ $(filter %.o,$^) + +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): Find out and document the difference between shared_library and +# loadable_module on mac. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) + +# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass +# -bundle -single_module here (for osmesa.so). +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# Helper that executes all postbuilds, and deletes the output file when done +# if any of the postbuilds failed. +define do_postbuilds + @E=0;\ + for p in $(POSTBUILDS); do\ + eval $$p;\ + F=$$?;\ + if [ $$F -ne 0 ]; then\ + E=$$F;\ + fi;\ + done;\ + if [ $$E -ne 0 ]; then\ + rm -rf "$@";\ + exit $$E;\ + fi +endef + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 2,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + $(call do_postbuilds) + ) +) +endef + +# Declare the "all" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: all +all: + +# make looks for ways to re-generate included makefiles, but in our case, we +# don't have a direct way. Explicitly telling make that it has nothing to do +# for them makes it go faster. +%.d: ; + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD + @$(call do_cmd,objc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD + @$(call do_cmd,objcxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,bson.target.mk)))),) + include bson.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = /Users/ck/.node-gyp/0.8.11/tools/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." -I/Users/ck/coding/projects/js-bson/build/config.gypi -I/usr/local/lib/node_modules/node-gyp/addon.gypi -I/Users/ck/.node-gyp/0.8.11/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/ck/.node-gyp/0.8.11" "-Dmodule_root_dir=/Users/ck/coding/projects/js-bson" binding.gyp +Makefile: $(srcdir)/../../../.node-gyp/0.8.11/common.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp $(srcdir)/../../../../../usr/local/lib/node_modules/node-gyp/addon.gypi + $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + include $(d_files) +endif diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/bson.node.d b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/bson.node.d new file mode 100644 index 0000000..20963f4 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/bson.node.d @@ -0,0 +1 @@ +cmd_Release/bson.node := ./gyp-mac-tool flock ./Release/linker.lock c++ -shared -Wl,-search_paths_first -mmacosx-version-min=10.5 -arch x86_64 -L./Release -install_name /usr/local/lib/bson.node -o Release/bson.node Release/obj.target/bson/ext/bson.o -undefined dynamic_lookup diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/obj.target/bson/ext/bson.o.d b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/obj.target/bson/ext/bson.o.d new file mode 100644 index 0000000..f224e00 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/.deps/Release/obj.target/bson/ext/bson.o.d @@ -0,0 +1,34 @@ +cmd_Release/obj.target/bson/ext/bson.o := c++ '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-D_DARWIN_USE_64_BIT_INODE=1' -I/Users/ck/.node-gyp/0.8.11/src -I/Users/ck/.node-gyp/0.8.11/deps/uv/include -I/Users/ck/.node-gyp/0.8.11/deps/v8/include -Os -gdwarf-2 -mmacosx-version-min=10.5 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -fno-rtti -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/bson/ext/bson.o.d.raw -c -o Release/obj.target/bson/ext/bson.o ../ext/bson.cc +Release/obj.target/bson/ext/bson.o: ../ext/bson.cc \ + /Users/ck/.node-gyp/0.8.11/deps/v8/include/v8.h \ + /Users/ck/.node-gyp/0.8.11/deps/v8/include/v8stdint.h \ + /Users/ck/.node-gyp/0.8.11/src/node.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/ares.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/ares_version.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/uv-unix.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/ngx-queue.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/ev.h \ + /Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/eio.h \ + /Users/ck/.node-gyp/0.8.11/src/node_object_wrap.h \ + /Users/ck/.node-gyp/0.8.11/src/ev-emul.h \ + /Users/ck/.node-gyp/0.8.11/src/eio-emul.h \ + /Users/ck/.node-gyp/0.8.11/src/node_version.h \ + /Users/ck/.node-gyp/0.8.11/src/node_buffer.h ../ext/bson.h +../ext/bson.cc: +/Users/ck/.node-gyp/0.8.11/deps/v8/include/v8.h: +/Users/ck/.node-gyp/0.8.11/deps/v8/include/v8stdint.h: +/Users/ck/.node-gyp/0.8.11/src/node.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/ares.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/ares_version.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/uv-unix.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/ngx-queue.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/ev.h: +/Users/ck/.node-gyp/0.8.11/deps/uv/include/uv-private/eio.h: +/Users/ck/.node-gyp/0.8.11/src/node_object_wrap.h: +/Users/ck/.node-gyp/0.8.11/src/ev-emul.h: +/Users/ck/.node-gyp/0.8.11/src/eio-emul.h: +/Users/ck/.node-gyp/0.8.11/src/node_version.h: +/Users/ck/.node-gyp/0.8.11/src/node_buffer.h: +../ext/bson.h: diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/bson.node b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/bson.node new file mode 100644 index 0000000..f8f0779 Binary files /dev/null and b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/bson.node differ diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/linker.lock b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/linker.lock new file mode 100644 index 0000000..e69de29 diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/obj.target/bson/ext/bson.o b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/obj.target/bson/ext/bson.o new file mode 100644 index 0000000..af87b6b Binary files /dev/null and b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/Release/obj.target/bson/ext/bson.o differ diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/binding.Makefile b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/binding.Makefile new file mode 100644 index 0000000..90bf824 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/binding.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= build/./. +.PHONY: all +all: + $(MAKE) bson diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/bson.target.mk b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/bson.target.mk new file mode 100644 index 0000000..f41c6bd --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/bson.target.mk @@ -0,0 +1,145 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := bson +DEFS_Debug := \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_DARWIN_USE_64_BIT_INODE=1' \ + '-DDEBUG' \ + '-D_DEBUG' + +# Flags passed to all source files. +CFLAGS_Debug := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Debug := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Debug := \ + -fno-rtti \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Debug := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Debug := + +INCS_Debug := \ + -I/Users/ck/.node-gyp/0.8.11/src \ + -I/Users/ck/.node-gyp/0.8.11/deps/uv/include \ + -I/Users/ck/.node-gyp/0.8.11/deps/v8/include + +DEFS_Release := \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_DARWIN_USE_64_BIT_INODE=1' + +# Flags passed to all source files. +CFLAGS_Release := \ + -Os \ + -gdwarf-2 \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -Wall \ + -Wendif-labels \ + -W \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Release := \ + -fno-strict-aliasing + +# Flags passed to only C++ files. +CFLAGS_CC_Release := \ + -fno-rtti \ + -fno-threadsafe-statics \ + -fno-strict-aliasing + +# Flags passed to only ObjC files. +CFLAGS_OBJC_Release := + +# Flags passed to only ObjC++ files. +CFLAGS_OBJCC_Release := + +INCS_Release := \ + -I/Users/ck/.node-gyp/0.8.11/src \ + -I/Users/ck/.node-gyp/0.8.11/deps/uv/include \ + -I/Users/ck/.node-gyp/0.8.11/deps/v8/include + +OBJS := \ + $(obj).target/$(TARGET)/ext/bson.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) +$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Debug := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name /usr/local/lib/bson.node + +LDFLAGS_Release := \ + -Wl,-search_paths_first \ + -mmacosx-version-min=10.5 \ + -arch x86_64 \ + -L$(builddir) \ + -install_name /usr/local/lib/bson.node + +LIBS := \ + -undefined dynamic_lookup + +$(builddir)/bson.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/bson.node: LIBS := $(LIBS) +$(builddir)/bson.node: TOOLSET := $(TOOLSET) +$(builddir)/bson.node: $(OBJS) FORCE_DO_CMD + $(call do_cmd,solink_module) + +all_deps += $(builddir)/bson.node +# Add target alias +.PHONY: bson +bson: $(builddir)/bson.node + +# Short alias for building this executable. +.PHONY: bson.node +bson.node: $(builddir)/bson.node + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/bson.node + diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/config.gypi b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/config.gypi new file mode 100644 index 0000000..431cd3c --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/config.gypi @@ -0,0 +1,28 @@ +# Do not edit. File was generated by node-gyp's "configure" step +{ + "target_defaults": { + "cflags": [], + "default_configuration": "Release", + "defines": [], + "include_dirs": [], + "libraries": [] + }, + "variables": { + "clang": 1, + "host_arch": "x64", + "node_install_npm": "true", + "node_install_waf": "true", + "node_prefix": "", + "node_shared_openssl": "false", + "node_shared_v8": "false", + "node_shared_zlib": "false", + "node_use_dtrace": "false", + "node_use_etw": "false", + "node_use_openssl": "true", + "target_arch": "x64", + "v8_no_strict_aliasing": 1, + "v8_use_snapshot": "true", + "nodedir": "/Users/ck/.node-gyp/0.8.11", + "copy_dev_lib": "true" + } +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/gyp-mac-tool b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/gyp-mac-tool new file mode 100644 index 0000000..22f8331 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/build/gyp-mac-tool @@ -0,0 +1,210 @@ +#!/usr/bin/env python +# Generated by gyp. Do not edit. +# Copyright (c) 2012 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Utility functions to perform Xcode-style build steps. + +These functions are executed via gyp-mac-tool when using the Makefile generator. +""" + +import fcntl +import os +import plistlib +import re +import shutil +import string +import subprocess +import sys + + +def main(args): + executor = MacTool() + exit_code = executor.Dispatch(args) + if exit_code is not None: + sys.exit(exit_code) + + +class MacTool(object): + """This class performs all the Mac tooling steps. The methods can either be + executed directly, or dispatched from an argument list.""" + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + return getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" + return name_string.title().replace('-', '') + + def ExecCopyBundleResource(self, source, dest): + """Copies a resource file to the bundle/Resources directory, performing any + necessary compilation on each resource.""" + extension = os.path.splitext(source)[1].lower() + if os.path.isdir(source): + # Copy tree. + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(source, dest) + elif extension == '.xib': + return self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest) + else: + shutil.copyfile(source, dest) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" + tools_dir = os.environ.get('DEVELOPER_BIN_DIR', '/usr/bin') + args = [os.path.join(tools_dir, 'ibtool'), '--errors', '--warnings', + '--notices', '--output-format', 'human-readable-text', '--compile', + dest, source] + ibtool_section_re = re.compile(r'/\*.*\*/') + ibtool_re = re.compile(r'.*note:.*is clipping its content') + ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) + current_section_header = None + for line in ibtoolout.stdout: + if ibtool_section_re.match(line): + current_section_header = line + elif not ibtool_re.match(line): + if current_section_header: + sys.stdout.write(current_section_header) + current_section_header = None + sys.stdout.write(line) + return ibtoolout.returncode + + def _CopyStringsFile(self, source, dest): + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" + input_code = self._DetectInputEncoding(source) or "UTF-8" + fp = open(dest, 'w') + args = ['/usr/bin/iconv', '--from-code', input_code, '--to-code', + 'UTF-16', source] + subprocess.call(args, stdout=fp) + fp.close() + + def _DetectInputEncoding(self, file_name): + """Reads the first few bytes from file_name and tries to guess the text + encoding. Returns None as a guess if it can't detect it.""" + fp = open(file_name, 'rb') + try: + header = fp.read(3) + except e: + fp.close() + return None + fp.close() + if header.startswith("\xFE\xFF"): + return "UTF-16BE" + elif header.startswith("\xFF\xFE"): + return "UTF-16LE" + elif header.startswith("\xEF\xBB\xBF"): + return "UTF-8" + else: + return None + + def ExecCopyInfoPlist(self, source, dest): + """Copies the |source| Info.plist to the destination directory |dest|.""" + # Read the source Info.plist into memory. + fd = open(source, 'r') + lines = fd.read() + fd.close() + + # Go through all the environment variables and replace them as variables in + # the file. + for key in os.environ: + if key.startswith('_'): + continue + evar = '${%s}' % key + lines = string.replace(lines, evar, os.environ[key]) + + # Write out the file with variables replaced. + fd = open(dest, 'w') + fd.write(lines) + fd.close() + + # Now write out PkgInfo file now that the Info.plist file has been + # "compiled". + self._WritePkgInfo(dest) + + def _WritePkgInfo(self, info_plist): + """This writes the PkgInfo file from the data stored in Info.plist.""" + plist = plistlib.readPlist(info_plist) + if not plist: + return + + # Only create PkgInfo for executable types. + package_type = plist['CFBundlePackageType'] + if package_type != 'APPL': + return + + # The format of PkgInfo is eight characters, representing the bundle type + # and bundle signature, each four characters. If that is missing, four + # '?' characters are used instead. + signature_code = plist.get('CFBundleSignature', '????') + if len(signature_code) != 4: # Wrong length resets everything, too. + signature_code = '?' * 4 + + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') + fp = open(dest, 'w') + fp.write('%s%s' % (package_type, signature_code)) + fp.close() + + def ExecFlock(self, lockfile, *cmd_list): + """Emulates the most basic behavior of Linux's flock(1).""" + # Rely on exception handling to report errors. + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) + fcntl.flock(fd, fcntl.LOCK_EX) + return subprocess.call(cmd_list) + + def ExecFilterLibtool(self, *cmd_list): + """Calls libtool and filters out 'libtool: file: foo.o has no symbols'.""" + libtool_re = re.compile(r'^libtool: file: .* has no symbols$') + libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE) + for line in libtoolout.stderr: + if not libtool_re.match(line): + sys.stderr.write(line) + return libtoolout.returncode + + def ExecPackageFramework(self, framework, version): + """Takes a path to Something.framework and the Current version of that and + sets up all the symlinks.""" + # Find the name of the binary based on the part before the ".framework". + binary = os.path.basename(framework).split('.')[0] + + CURRENT = 'Current' + RESOURCES = 'Resources' + VERSIONS = 'Versions' + + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): + # Binary-less frameworks don't seem to contain symlinks (see e.g. + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). + return + + # Move into the framework directory to set the symlinks correctly. + pwd = os.getcwd() + os.chdir(framework) + + # Set up the Current version. + self._Relink(version, os.path.join(VERSIONS, CURRENT)) + + # Set up the root symlinks. + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) + + # Back to where we were before! + os.chdir(pwd) + + def _Relink(self, dest, link): + """Creates a symlink to |dest| named |link|. If |link| already exists, + it is overwritten.""" + if os.path.lexists(link): + os.remove(link) + os.symlink(dest, link) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/Makefile b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/Makefile new file mode 100644 index 0000000..435999e --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/Makefile @@ -0,0 +1,28 @@ +NODE = node +name = all +JOBS = 1 + +all: + rm -rf build .lock-wscript bson.node + node-waf configure build + cp -R ./build/Release/bson.node . || true + +all_debug: + rm -rf build .lock-wscript bson.node + node-waf --debug configure build + cp -R ./build/Release/bson.node . || true + +clang: + rm -rf build .lock-wscript bson.node + CXX=clang node-waf configure build + cp -R ./build/Release/bson.node . || true + +clang_debug: + rm -rf build .lock-wscript bson.node + CXX=clang node-waf --debug configure build + cp -R ./build/Release/bson.node . || true + +clean: + rm -rf build .lock-wscript bson.node + +.PHONY: all \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/bson.cc b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/bson.cc new file mode 100644 index 0000000..01e4c28 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/bson.cc @@ -0,0 +1,1020 @@ +//=========================================================================== + +#include +#include +#include +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#endif + +#include + +// this and the above block must be around the v8.h header otherwise +// v8 is not happy +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __sun + #include +#endif + +#include "bson.h" + +using namespace v8; +using namespace node; + +//=========================================================================== + +void DataStream::WriteObjectId(const Handle& object, const Handle& key) +{ + uint16_t buffer[12]; + object->Get(key)->ToString()->Write(buffer, 0, 12); + for(uint32_t i = 0; i < 12; ++i) + { + *p++ = (char) buffer[i]; + } +} + +void ThrowAllocatedStringException(size_t allocationSize, const char* format, ...) +{ + va_list args; + va_start(args, format); + char* string = (char*) malloc(allocationSize); + vsprintf(string, format, args); + va_end(args); + + throw string; +} + +void DataStream::CheckKey(const Local& keyName) +{ + size_t keyLength = keyName->Utf8Length(); + if(keyLength == 0) return; + + char* keyStringBuffer = (char*) alloca(keyLength+1); + keyName->WriteUtf8(keyStringBuffer); + + if(keyStringBuffer[0] == '$') + { + ThrowAllocatedStringException(64+keyLength, "key %s must not start with '$'", keyStringBuffer); + } + + if(strchr(keyStringBuffer, '.') != NULL) + { + ThrowAllocatedStringException(64+keyLength, "key %s must not contain '.'", keyStringBuffer); + } +} + +template void BSONSerializer::SerializeDocument(const Handle& value) +{ + void* documentSize = this->BeginWriteSize(); + Local object = bson->GetSerializeObject(value); + + // Get the object property names + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + Local propertyNames = object->GetPropertyNames(); + #else + Local propertyNames = object->GetOwnPropertyNames(); + #endif + + // Length of the property + int propertyLength = propertyNames->Length(); + for(int i = 0; i < propertyLength; ++i) + { + const Local& propertyName = propertyNames->Get(i)->ToString(); + if(checkKeys) this->CheckKey(propertyName); + + const Local& propertyValue = object->Get(propertyName); + + if(serializeFunctions || !propertyValue->IsFunction()) + { + void* typeLocation = this->BeginWriteType(); + this->WriteString(propertyName); + SerializeValue(typeLocation, propertyValue); + } + } + + this->WriteByte(0); + this->CommitSize(documentSize); +} + +template void BSONSerializer::SerializeArray(const Handle& value) +{ + void* documentSize = this->BeginWriteSize(); + + Local array = Local::Cast(value->ToObject()); + uint32_t arrayLength = array->Length(); + + for(uint32_t i = 0; i < arrayLength; ++i) + { + void* typeLocation = this->BeginWriteType(); + this->WriteUInt32String(i); + SerializeValue(typeLocation, array->Get(i)); + } + + this->WriteByte(0); + this->CommitSize(documentSize); +} + +// This is templated so that we can use this function to both count the number of bytes, and to serialize those bytes. +// The template approach eliminates almost all of the inspection of values unless they're required (eg. string lengths) +// and ensures that there is always consistency between bytes counted and bytes written by design. +template void BSONSerializer::SerializeValue(void* typeLocation, const Handle& value) +{ + if(value->IsNumber()) + { + double doubleValue = value->NumberValue(); + int intValue = (int) doubleValue; + if(intValue == doubleValue) + { + this->CommitType(typeLocation, BSON_TYPE_INT); + this->WriteInt32(intValue); + } + else + { + this->CommitType(typeLocation, BSON_TYPE_NUMBER); + this->WriteDouble(doubleValue); + } + } + else if(value->IsString()) + { + this->CommitType(typeLocation, BSON_TYPE_STRING); + this->WriteLengthPrefixedString(value->ToString()); + } + else if(value->IsBoolean()) + { + this->CommitType(typeLocation, BSON_TYPE_BOOLEAN); + this->WriteBool(value); + } + else if(value->IsArray()) + { + this->CommitType(typeLocation, BSON_TYPE_ARRAY); + SerializeArray(value); + } + else if(value->IsDate()) + { + this->CommitType(typeLocation, BSON_TYPE_DATE); + this->WriteInt64(value); + } + else if(value->IsRegExp()) + { + this->CommitType(typeLocation, BSON_TYPE_REGEXP); + const Handle& regExp = Handle::Cast(value); + + this->WriteString(regExp->GetSource()); + + int flags = regExp->GetFlags(); + if(flags & RegExp::kGlobal) this->WriteByte('s'); + if(flags & RegExp::kIgnoreCase) this->WriteByte('i'); + if(flags & RegExp::kMultiline) this->WriteByte('m'); + this->WriteByte(0); + } + else if(value->IsFunction()) + { + this->CommitType(typeLocation, BSON_TYPE_CODE); + this->WriteLengthPrefixedString(value->ToString()); + } + else if(value->IsObject()) + { + const Local& object = value->ToObject(); + if(object->Has(bson->_bsontypeString)) + { + const Local& constructorString = object->GetConstructorName(); + if(bson->longString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_LONG); + this->WriteInt32(object, bson->_longLowString); + this->WriteInt32(object, bson->_longHighString); + } + else if(bson->timestampString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_TIMESTAMP); + this->WriteInt32(object, bson->_longLowString); + this->WriteInt32(object, bson->_longHighString); + } + else if(bson->objectIDString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_OID); + this->WriteObjectId(object, bson->_objectIDidString); + } + else if(bson->binaryString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_BINARY); + + uint32_t length = object->Get(bson->_binaryPositionString)->Uint32Value(); + Local bufferObj = object->Get(bson->_binaryBufferString)->ToObject(); + + this->WriteInt32(length); + this->WriteByte(object, bson->_binarySubTypeString); // write subtype + this->WriteData(Buffer::Data(bufferObj), length); + } + else if(bson->doubleString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_NUMBER); + this->WriteDouble(object, bson->_doubleValueString); + } + else if(bson->symbolString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_SYMBOL); + this->WriteLengthPrefixedString(object->Get(bson->_symbolValueString)->ToString()); + } + else if(bson->codeString->StrictEquals(constructorString)) + { + const Local& function = object->Get(bson->_codeCodeString)->ToString(); + const Local& scope = object->Get(bson->_codeScopeString)->ToObject(); + + // For Node < 0.6.X use the GetPropertyNames + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6 + uint32_t propertyNameLength = scope->GetPropertyNames()->Length(); + #else + uint32_t propertyNameLength = scope->GetOwnPropertyNames()->Length(); + #endif + + if(propertyNameLength > 0) + { + this->CommitType(typeLocation, BSON_TYPE_CODE_W_SCOPE); + void* codeWidthScopeSize = this->BeginWriteSize(); + this->WriteLengthPrefixedString(function->ToString()); + SerializeDocument(scope); + this->CommitSize(codeWidthScopeSize); + } + else + { + this->CommitType(typeLocation, BSON_TYPE_CODE); + this->WriteLengthPrefixedString(function->ToString()); + } + } + else if(bson->dbrefString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_OBJECT); + + void* dbRefSize = this->BeginWriteSize(); + + void* refType = this->BeginWriteType(); + this->WriteData("$ref", 5); + SerializeValue(refType, object->Get(bson->_dbRefNamespaceString)); + + void* idType = this->BeginWriteType(); + this->WriteData("$id", 4); + SerializeValue(idType, object->Get(bson->_dbRefOidString)); + + const Local& refDbValue = object->Get(bson->_dbRefDbString); + if(!refDbValue->IsUndefined()) + { + void* dbType = this->BeginWriteType(); + this->WriteData("$db", 4); + SerializeValue(dbType, refDbValue); + } + + this->WriteByte(0); + this->CommitSize(dbRefSize); + } + else if(bson->minKeyString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_MIN_KEY); + } + else if(bson->maxKeyString->StrictEquals(constructorString)) + { + this->CommitType(typeLocation, BSON_TYPE_MAX_KEY); + } + } + else if(Buffer::HasInstance(value)) + { + this->CommitType(typeLocation, BSON_TYPE_BINARY); + + #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(value->ToObject()); + uint32_t length = object->length(); + #else + uint32_t length = Buffer::Length(value->ToObject()); + #endif + + this->WriteInt32(length); + this->WriteByte(0); + this->WriteData(Buffer::Data(value->ToObject()), length); + } + else + { + this->CommitType(typeLocation, BSON_TYPE_OBJECT); + SerializeDocument(value); + } + } + else if(value->IsNull() || value->IsUndefined()) + { + this->CommitType(typeLocation, BSON_TYPE_NULL); + } +} + +// Data points to start of element list, length is length of entire document including '\0' but excluding initial size +BSONDeserializer::BSONDeserializer(BSON* aBson, char* data, size_t length) +: bson(aBson), + pStart(data), + p(data), + pEnd(data + length - 1) +{ + if(*pEnd != '\0') ThrowAllocatedStringException(64, "Missing end of document marker '\\0'"); +} + +BSONDeserializer::BSONDeserializer(BSONDeserializer& parentSerializer, size_t length) +: bson(parentSerializer.bson), + pStart(parentSerializer.p), + p(parentSerializer.p), + pEnd(parentSerializer.p + length - 1) +{ + parentSerializer.p += length; + if(pEnd > parentSerializer.pEnd) ThrowAllocatedStringException(64, "Child document exceeds parent's bounds"); + if(*pEnd != '\0') ThrowAllocatedStringException(64, "Missing end of document marker '\\0'"); +} + +Local BSONDeserializer::ReadCString() +{ + char* start = p; + while(*p++) { } + return String::New(start, (int32_t) (p-start-1) ); +} + +int32_t BSONDeserializer::ReadRegexOptions() +{ + int32_t options = 0; + for(;;) + { + switch(*p++) + { + case '\0': return options; + case 's': options |= RegExp::kGlobal; break; + case 'i': options |= RegExp::kIgnoreCase; break; + case 'm': options |= RegExp::kMultiline; break; + } + } +} + +uint32_t BSONDeserializer::ReadIntegerString() +{ + uint32_t value = 0; + while(*p) + { + if(*p < '0' || *p > '9') ThrowAllocatedStringException(64, "Invalid key for array"); + value = value * 10 + *p++ - '0'; + } + ++p; + return value; +} + +Local BSONDeserializer::ReadString() +{ + uint32_t length = ReadUInt32(); + char* start = p; + p += length; + return String::New(start, length-1); +} + +Local BSONDeserializer::ReadObjectId() +{ + uint16_t objectId[12]; + for(size_t i = 0; i < 12; ++i) + { + objectId[i] = *reinterpret_cast(p++); + } + return String::New(objectId, 12); +} + +Handle BSONDeserializer::DeserializeDocument() +{ + uint32_t length = ReadUInt32(); + if(length < 5) ThrowAllocatedStringException(64, "Bad BSON: Document is less than 5 bytes"); + + BSONDeserializer documentDeserializer(*this, length-4); + return documentDeserializer.DeserializeDocumentInternal(); +} + +Handle BSONDeserializer::DeserializeDocumentInternal() +{ + Local returnObject = Object::New(); + + while(HasMoreData()) + { + BsonType type = (BsonType) ReadByte(); + const Local& name = ReadCString(); + const Handle& value = DeserializeValue(type); + returnObject->ForceSet(name, value); + } + if(p != pEnd) ThrowAllocatedStringException(64, "Bad BSON Document: Serialize consumed unexpected number of bytes"); + + // From JavaScript: + // if(object['$id'] != null) object = new DBRef(object['$ref'], object['$id'], object['$db']); + if(returnObject->Has(bson->_dbRefIdRefString)) + { + Local argv[] = { returnObject->Get(bson->_dbRefRefString), returnObject->Get(bson->_dbRefIdRefString), returnObject->Get(bson->_dbRefDbRefString) }; + return bson->dbrefConstructor->NewInstance(3, argv); + } + else + { + return returnObject; + } +} + +Handle BSONDeserializer::DeserializeArray() +{ + uint32_t length = ReadUInt32(); + if(length < 5) ThrowAllocatedStringException(64, "Bad BSON: Array Document is less than 5 bytes"); + + BSONDeserializer documentDeserializer(*this, length-4); + return documentDeserializer.DeserializeArrayInternal(); +} + +Handle BSONDeserializer::DeserializeArrayInternal() +{ + Local returnArray = Array::New(); + + while(HasMoreData()) + { + BsonType type = (BsonType) ReadByte(); + uint32_t index = ReadIntegerString(); + const Handle& value = DeserializeValue(type); + returnArray->Set(index, value); + } + if(p != pEnd) ThrowAllocatedStringException(64, "Bad BSON Array: Serialize consumed unexpected number of bytes"); + + return returnArray; +} + +Handle BSONDeserializer::DeserializeValue(BsonType type) +{ + switch(type) + { + case BSON_TYPE_STRING: + return ReadString(); + + case BSON_TYPE_INT: + return Integer::New(ReadInt32()); + + case BSON_TYPE_NUMBER: + return Number::New(ReadDouble()); + + case BSON_TYPE_NULL: + return Null(); + + case BSON_TYPE_UNDEFINED: + return Undefined(); + + case BSON_TYPE_TIMESTAMP: + { + int32_t lowBits = ReadInt32(); + int32_t highBits = ReadInt32(); + Local argv[] = { Int32::New(lowBits), Int32::New(highBits) }; + return bson->timestampConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_BOOLEAN: + return (ReadByte() != 0) ? True() : False(); + + case BSON_TYPE_REGEXP: + { + const Local& regex = ReadCString(); + int32_t options = ReadRegexOptions(); + return RegExp::New(regex, (RegExp::Flags) options); + } + + case BSON_TYPE_CODE: + { + const Local& code = ReadString(); + const Local& scope = Object::New(); + Local argv[] = { code, scope }; + return bson->codeConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_CODE_W_SCOPE: + { + ReadUInt32(); + const Local& code = ReadString(); + const Handle& scope = DeserializeDocument(); + Local argv[] = { code, scope->ToObject() }; + return bson->codeConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_OID: + { + Local argv[] = { ReadObjectId() }; + return bson->objectIDConstructor->NewInstance(1, argv); + } + + case BSON_TYPE_BINARY: + { + uint32_t length = ReadUInt32(); + uint32_t subType = ReadByte(); + Buffer* buffer = Buffer::New(p, length); + p += length; + + Handle argv[] = { buffer->handle_, Uint32::New(subType) }; + return bson->binaryConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_LONG: + { + // Read 32 bit integers + int32_t lowBits = (int32_t) ReadInt32(); + int32_t highBits = (int32_t) ReadInt32(); + + // If value is < 2^53 and >-2^53 + if((highBits < 0x200000 || (highBits == 0x200000 && lowBits == 0)) && highBits >= -0x200000) { + // Adjust the pointer and read as 64 bit value + p -= 8; + // Read the 64 bit value + int64_t finalValue = (int64_t) ReadInt64(); + return Number::New(finalValue); + } + + Local argv[] = { Int32::New(lowBits), Int32::New(highBits) }; + return bson->longConstructor->NewInstance(2, argv); + } + + case BSON_TYPE_DATE: + return Date::New((double) ReadInt64()); + + case BSON_TYPE_ARRAY: + return DeserializeArray(); + + case BSON_TYPE_OBJECT: + return DeserializeDocument(); + + case BSON_TYPE_SYMBOL: + { + const Local& string = ReadString(); + Local argv[] = { string }; + return bson->symbolConstructor->NewInstance(1, argv); + } + + case BSON_TYPE_MIN_KEY: + return bson->minKeyConstructor->NewInstance(); + + case BSON_TYPE_MAX_KEY: + return bson->maxKeyConstructor->NewInstance(); + + default: + ThrowAllocatedStringException(64, "Unhandled BSON Type: %d", type); + } + + return v8::Null(); +} + + +static Handle VException(const char *msg) +{ + HandleScope scope; + return ThrowException(Exception::Error(String::New(msg))); +} + +Persistent BSON::constructor_template; + +BSON::BSON() : ObjectWrap() +{ + // Setup pre-allocated comparision objects + _bsontypeString = Persistent::New(String::New("_bsontype")); + _longLowString = Persistent::New(String::New("low_")); + _longHighString = Persistent::New(String::New("high_")); + _objectIDidString = Persistent::New(String::New("id")); + _binaryPositionString = Persistent::New(String::New("position")); + _binarySubTypeString = Persistent::New(String::New("sub_type")); + _binaryBufferString = Persistent::New(String::New("buffer")); + _doubleValueString = Persistent::New(String::New("value")); + _symbolValueString = Persistent::New(String::New("value")); + _dbRefRefString = Persistent::New(String::New("$ref")); + _dbRefIdRefString = Persistent::New(String::New("$id")); + _dbRefDbRefString = Persistent::New(String::New("$db")); + _dbRefNamespaceString = Persistent::New(String::New("namespace")); + _dbRefDbString = Persistent::New(String::New("db")); + _dbRefOidString = Persistent::New(String::New("oid")); + _codeCodeString = Persistent::New(String::New("code")); + _codeScopeString = Persistent::New(String::New("scope")); + _toBSONString = Persistent::New(String::New("toBSON")); + + longString = Persistent::New(String::New("Long")); + objectIDString = Persistent::New(String::New("ObjectID")); + binaryString = Persistent::New(String::New("Binary")); + codeString = Persistent::New(String::New("Code")); + dbrefString = Persistent::New(String::New("DBRef")); + symbolString = Persistent::New(String::New("Symbol")); + doubleString = Persistent::New(String::New("Double")); + timestampString = Persistent::New(String::New("Timestamp")); + minKeyString = Persistent::New(String::New("MinKey")); + maxKeyString = Persistent::New(String::New("MaxKey")); +} + +void BSON::Initialize(v8::Handle target) +{ + // Grab the scope of the call from Node + HandleScope scope; + // Define a new function template + Local t = FunctionTemplate::New(New); + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("BSON")); + + // Instance methods + NODE_SET_PROTOTYPE_METHOD(constructor_template, "calculateObjectSize", CalculateObjectSize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "serialize", BSONSerialize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "serializeWithBufferAndIndex", SerializeWithBufferAndIndex); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "deserialize", BSONDeserialize); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "deserializeStream", BSONDeserializeStream); + + target->ForceSet(String::NewSymbol("BSON"), constructor_template->GetFunction()); +} + +// Create a new instance of BSON and passing it the existing context +Handle BSON::New(const Arguments &args) +{ + HandleScope scope; + + // Check that we have an array + if(args.Length() == 1 && args[0]->IsArray()) + { + // Cast the array to a local reference + Local array = Local::Cast(args[0]); + + if(array->Length() > 0) + { + // Create a bson object instance and return it + BSON *bson = new BSON(); + + uint32_t foundClassesMask = 0; + + // Iterate over all entries to save the instantiate funtions + for(uint32_t i = 0; i < array->Length(); i++) + { + // Let's get a reference to the function + Local func = Local::Cast(array->Get(i)); + Local functionName = func->GetName()->ToString(); + + // Save the functions making them persistant handles (they don't get collected) + if(functionName->StrictEquals(bson->longString)) + { + bson->longConstructor = Persistent::New(func); + foundClassesMask |= 1; + } + else if(functionName->StrictEquals(bson->objectIDString)) + { + bson->objectIDConstructor = Persistent::New(func); + foundClassesMask |= 2; + } + else if(functionName->StrictEquals(bson->binaryString)) + { + bson->binaryConstructor = Persistent::New(func); + foundClassesMask |= 4; + } + else if(functionName->StrictEquals(bson->codeString)) + { + bson->codeConstructor = Persistent::New(func); + foundClassesMask |= 8; + } + else if(functionName->StrictEquals(bson->dbrefString)) + { + bson->dbrefConstructor = Persistent::New(func); + foundClassesMask |= 0x10; + } + else if(functionName->StrictEquals(bson->symbolString)) + { + bson->symbolConstructor = Persistent::New(func); + foundClassesMask |= 0x20; + } + else if(functionName->StrictEquals(bson->doubleString)) + { + bson->doubleConstructor = Persistent::New(func); + foundClassesMask |= 0x40; + } + else if(functionName->StrictEquals(bson->timestampString)) + { + bson->timestampConstructor = Persistent::New(func); + foundClassesMask |= 0x80; + } + else if(functionName->StrictEquals(bson->minKeyString)) + { + bson->minKeyConstructor = Persistent::New(func); + foundClassesMask |= 0x100; + } + else if(functionName->StrictEquals(bson->maxKeyString)) + { + bson->maxKeyConstructor = Persistent::New(func); + foundClassesMask |= 0x200; + } + } + + // Check if we have the right number of constructors otherwise throw an error + if(foundClassesMask != 0x3ff) + { + delete bson; + return VException("Missing function constructor for either [Long/ObjectID/Binary/Code/DbRef/Symbol/Double/Timestamp/MinKey/MaxKey]"); + } + else + { + bson->Wrap(args.This()); + return args.This(); + } + } + else + { + return VException("No types passed in"); + } + } + else + { + return VException("Argument passed in must be an array of types"); + } +} + +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ + +Handle BSON::BSONDeserialize(const Arguments &args) +{ + HandleScope scope; + + // Ensure that we have an parameter + if(Buffer::HasInstance(args[0]) && args.Length() > 1) return VException("One argument required - buffer1."); + if(args[0]->IsString() && args.Length() > 1) return VException("One argument required - string1."); + // Throw an exception if the argument is not of type Buffer + if(!Buffer::HasInstance(args[0]) && !args[0]->IsString()) return VException("Argument must be a Buffer or String."); + + // Define pointer to data + Local obj = args[0]->ToObject(); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // If we passed in a buffer, let's unpack it, otherwise let's unpack the string + if(Buffer::HasInstance(obj)) + { +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(obj); + char* data = buffer->data(); + size_t length = buffer->length(); +#else + char* data = Buffer::Data(obj); + size_t length = Buffer::Length(obj); +#endif + + // Validate that we have at least 5 bytes + if(length < 5) return VException("corrupt bson message < 5 bytes long"); + + try + { + BSONDeserializer deserializer(bson, data, length); + return deserializer.DeserializeDocument(); + } + catch(char* exception) + { + Handle error = VException(exception); + free(exception); + return error; + } + + } + else + { + // The length of the data for this encoding + ssize_t len = DecodeBytes(args[0], BINARY); + + // Validate that we have at least 5 bytes + if(len < 5) return VException("corrupt bson message < 5 bytes long"); + + // Let's define the buffer size + char* data = (char *)malloc(len); + DecodeWrite(data, len, args[0], BINARY); + + try + { + BSONDeserializer deserializer(bson, data, len); + Handle result = deserializer.DeserializeDocument(); + free(data); + return result; + + } + catch(char* exception) + { + Handle error = VException(exception); + free(exception); + free(data); + return error; + } + } +} + +Local BSON::GetSerializeObject(const Handle& argValue) +{ + Local object = argValue->ToObject(); + if(object->Has(_toBSONString)) + { + const Local& toBSON = object->Get(_toBSONString); + if(!toBSON->IsFunction()) ThrowAllocatedStringException(64, "toBSON is not a function"); + + Local result = Local::Cast(toBSON)->Call(object, 0, NULL); + if(!result->IsObject()) ThrowAllocatedStringException(64, "toBSON function did not return an object"); + return result->ToObject(); + } + else + { + return object; + } +} + +Handle BSON::BSONSerialize(const Arguments &args) +{ + HandleScope scope; + + if(args.Length() == 1 && !args[0]->IsObject()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 2 && !args[0]->IsObject() && !args[1]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 3 && !args[0]->IsObject() && !args[1]->IsBoolean() && !args[2]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]"); + if(args.Length() == 4 && !args[0]->IsObject() && !args[1]->IsBoolean() && !args[2]->IsBoolean() && !args[3]->IsBoolean()) return VException("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean] or [object, boolean, boolean, boolean]"); + if(args.Length() > 4) return VException("One, two, tree or four arguments required - [object] or [object, boolean] or [object, boolean, boolean] or [object, boolean, boolean, boolean]"); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // Calculate the total size of the document in binary form to ensure we only allocate memory once + // With serialize function + bool serializeFunctions = (args.Length() >= 4) && args[3]->BooleanValue(); + + char *serialized_object = NULL; + size_t object_size; + try + { + Local object = bson->GetSerializeObject(args[0]); + + BSONSerializer counter(bson, false, serializeFunctions); + counter.SerializeDocument(object); + object_size = counter.GetSerializeSize(); + + // Allocate the memory needed for the serialization + serialized_object = (char *)malloc(object_size); + + // Check if we have a boolean value + bool checkKeys = args.Length() >= 3 && args[1]->IsBoolean() && args[1]->BooleanValue(); + BSONSerializer data(bson, checkKeys, serializeFunctions, serialized_object); + data.SerializeDocument(object); + } + catch(char *err_msg) + { + free(serialized_object); + Handle error = VException(err_msg); + free(err_msg); + return error; + } + + // If we have 3 arguments + if(args.Length() == 3 || args.Length() == 4) + { + Buffer *buffer = Buffer::New(serialized_object, object_size); + free(serialized_object); + return scope.Close(buffer->handle_); + } + else + { + Local bin_value = Encode(serialized_object, object_size, BINARY)->ToString(); + free(serialized_object); + return bin_value; + } +} + +Handle BSON::CalculateObjectSize(const Arguments &args) +{ + HandleScope scope; + // Ensure we have a valid object + if(args.Length() == 1 && !args[0]->IsObject()) return VException("One argument required - [object]"); + if(args.Length() == 2 && !args[0]->IsObject() && !args[1]->IsBoolean()) return VException("Two arguments required - [object, boolean]"); + if(args.Length() > 3) return VException("One or two arguments required - [object] or [object, boolean]"); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + bool serializeFunctions = (args.Length() >= 2) && args[1]->BooleanValue(); + BSONSerializer countSerializer(bson, false, serializeFunctions); + countSerializer.SerializeDocument(args[0]); + + // Return the object size + return scope.Close(Uint32::New((uint32_t) countSerializer.GetSerializeSize())); +} + +Handle BSON::SerializeWithBufferAndIndex(const Arguments &args) +{ + HandleScope scope; + + //BSON.serializeWithBufferAndIndex = function serializeWithBufferAndIndex(object, ->, buffer, index) { + // Ensure we have the correct values + if(args.Length() > 5) return VException("Four or five parameters required [object, boolean, Buffer, int] or [object, boolean, Buffer, int, boolean]"); + if(args.Length() == 4 && !args[0]->IsObject() && !args[1]->IsBoolean() && !Buffer::HasInstance(args[2]) && !args[3]->IsUint32()) return VException("Four parameters required [object, boolean, Buffer, int]"); + if(args.Length() == 5 && !args[0]->IsObject() && !args[1]->IsBoolean() && !Buffer::HasInstance(args[2]) && !args[3]->IsUint32() && !args[4]->IsBoolean()) return VException("Four parameters required [object, boolean, Buffer, int, boolean]"); + + uint32_t index; + size_t object_size; + + try + { + BSON *bson = ObjectWrap::Unwrap(args.This()); + + Local obj = args[2]->ToObject(); + char* data = Buffer::Data(obj); + size_t length = Buffer::Length(obj); + + index = args[3]->Uint32Value(); + bool checkKeys = args.Length() >= 4 && args[1]->IsBoolean() && args[1]->BooleanValue(); + bool serializeFunctions = (args.Length() == 5) && args[4]->BooleanValue(); + + BSONSerializer dataSerializer(bson, checkKeys, serializeFunctions, data+index); + dataSerializer.SerializeDocument(bson->GetSerializeObject(args[0])); + object_size = dataSerializer.GetSerializeSize(); + + if(object_size + index > length) return VException("Serious error - overflowed buffer!!"); + } + catch(char *exception) + { + Handle error = VException(exception); + free(exception); + return error; + } + + return scope.Close(Uint32::New((uint32_t) (index + object_size - 1))); +} + +Handle BSON::BSONDeserializeStream(const Arguments &args) +{ + HandleScope scope; + + // At least 3 arguments required + if(args.Length() < 5) return VException("Arguments required (Buffer(data), Number(index in data), Number(number of documents to deserialize), Array(results), Number(index in the array), Object(optional))"); + + // If the number of argumets equals 3 + if(args.Length() >= 5) + { + if(!Buffer::HasInstance(args[0])) return VException("First argument must be Buffer instance"); + if(!args[1]->IsUint32()) return VException("Second argument must be a positive index number"); + if(!args[2]->IsUint32()) return VException("Third argument must be a positive number of documents to deserialize"); + if(!args[3]->IsArray()) return VException("Fourth argument must be an array the size of documents to deserialize"); + if(!args[4]->IsUint32()) return VException("Sixth argument must be a positive index number"); + } + + // If we have 4 arguments + if(args.Length() == 6 && !args[5]->IsObject()) return VException("Fifth argument must be an object with options"); + + // Define pointer to data + Local obj = args[0]->ToObject(); + uint32_t numberOfDocuments = args[2]->Uint32Value(); + uint32_t index = args[1]->Uint32Value(); + uint32_t resultIndex = args[4]->Uint32Value(); + + // Unpack the BSON parser instance + BSON *bson = ObjectWrap::Unwrap(args.This()); + + // Unpack the buffer variable +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3 + Buffer *buffer = ObjectWrap::Unwrap(obj); + char* data = buffer->data(); + size_t length = buffer->length(); +#else + char* data = Buffer::Data(obj); + size_t length = Buffer::Length(obj); +#endif + + // Fetch the documents + Local documents = args[3]->ToObject(); + + BSONDeserializer deserializer(bson, data+index, length-index); + for(uint32_t i = 0; i < numberOfDocuments; i++) + { + try + { + documents->Set(i + resultIndex, deserializer.DeserializeDocument()); + } + catch (char* exception) + { + Handle error = VException(exception); + free(exception); + return error; + } + } + + // Return new index of parsing + return scope.Close(Uint32::New((uint32_t) (index + deserializer.GetSerializeSize()))); +} + +// Exporting function +extern "C" void init(Handle target) +{ + HandleScope scope; + BSON::Initialize(target); +} + +NODE_MODULE(bson, BSON::Initialize); diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/bson.h b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/bson.h new file mode 100644 index 0000000..580abd4 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/bson.h @@ -0,0 +1,273 @@ +//=========================================================================== + +#ifndef BSON_H_ +#define BSON_H_ + +//=========================================================================== + +#define USE_MISALIGNED_MEMORY_ACCESS 1 + +#include +#include +#include + +using namespace v8; +using namespace node; + +//=========================================================================== + +enum BsonType +{ + BSON_TYPE_NUMBER = 1, + BSON_TYPE_STRING = 2, + BSON_TYPE_OBJECT = 3, + BSON_TYPE_ARRAY = 4, + BSON_TYPE_BINARY = 5, + BSON_TYPE_UNDEFINED = 6, + BSON_TYPE_OID = 7, + BSON_TYPE_BOOLEAN = 8, + BSON_TYPE_DATE = 9, + BSON_TYPE_NULL = 10, + BSON_TYPE_REGEXP = 11, + BSON_TYPE_CODE = 13, + BSON_TYPE_SYMBOL = 14, + BSON_TYPE_CODE_W_SCOPE = 15, + BSON_TYPE_INT = 16, + BSON_TYPE_TIMESTAMP = 17, + BSON_TYPE_LONG = 18, + BSON_TYPE_MAX_KEY = 0x7f, + BSON_TYPE_MIN_KEY = 0xff +}; + +//=========================================================================== + +template class BSONSerializer; + +class BSON : public ObjectWrap { +public: + BSON(); + ~BSON() {} + + static void Initialize(Handle target); + static Handle BSONDeserializeStream(const Arguments &args); + + // JS based objects + static Handle BSONSerialize(const Arguments &args); + static Handle BSONDeserialize(const Arguments &args); + + // Calculate size of function + static Handle CalculateObjectSize(const Arguments &args); + static Handle SerializeWithBufferAndIndex(const Arguments &args); + + // Constructor used for creating new BSON objects from C++ + static Persistent constructor_template; + +private: + static Handle New(const Arguments &args); + static Handle deserialize(BSON *bson, char *data, uint32_t dataLength, uint32_t startIndex, bool is_array_item); + + // BSON type instantiate functions + Persistent longConstructor; + Persistent objectIDConstructor; + Persistent binaryConstructor; + Persistent codeConstructor; + Persistent dbrefConstructor; + Persistent symbolConstructor; + Persistent doubleConstructor; + Persistent timestampConstructor; + Persistent minKeyConstructor; + Persistent maxKeyConstructor; + + // Equality Objects + Persistent longString; + Persistent objectIDString; + Persistent binaryString; + Persistent codeString; + Persistent dbrefString; + Persistent symbolString; + Persistent doubleString; + Persistent timestampString; + Persistent minKeyString; + Persistent maxKeyString; + + // Equality speed up comparison objects + Persistent _bsontypeString; + Persistent _longLowString; + Persistent _longHighString; + Persistent _objectIDidString; + Persistent _binaryPositionString; + Persistent _binarySubTypeString; + Persistent _binaryBufferString; + Persistent _doubleValueString; + Persistent _symbolValueString; + + Persistent _dbRefRefString; + Persistent _dbRefIdRefString; + Persistent _dbRefDbRefString; + Persistent _dbRefNamespaceString; + Persistent _dbRefDbString; + Persistent _dbRefOidString; + + Persistent _codeCodeString; + Persistent _codeScopeString; + Persistent _toBSONString; + + Local GetSerializeObject(const Handle& object); + + template friend class BSONSerializer; + friend class BSONDeserializer; +}; + +//=========================================================================== + +class CountStream +{ +public: + CountStream() : count(0) { } + + void WriteByte(int value) { ++count; } + void WriteByte(const Handle&, const Handle&) { ++count; } + void WriteBool(const Handle& value) { ++count; } + void WriteInt32(int32_t value) { count += 4; } + void WriteInt32(const Handle& value) { count += 4; } + void WriteInt32(const Handle& object, const Handle& key) { count += 4; } + void WriteInt64(int64_t value) { count += 8; } + void WriteInt64(const Handle& value) { count += 8; } + void WriteDouble(double value) { count += 8; } + void WriteDouble(const Handle& value) { count += 8; } + void WriteDouble(const Handle&, const Handle&) { count += 8; } + void WriteUInt32String(uint32_t name) { char buffer[32]; count += sprintf(buffer, "%u", name) + 1; } + void WriteLengthPrefixedString(const Local& value) { count += value->Utf8Length()+5; } + void WriteObjectId(const Handle& object, const Handle& key) { count += 12; } + void WriteString(const Local& value) { count += value->Utf8Length() + 1; } // This returns the number of bytes exclusive of the NULL terminator + void WriteData(const char* data, size_t length) { count += length; } + + void* BeginWriteType() { ++count; return NULL; } + void CommitType(void*, BsonType) { } + void* BeginWriteSize() { count += 4; return NULL; } + void CommitSize(void*) { } + + size_t GetSerializeSize() const { return count; } + + // Do nothing. CheckKey is implemented for DataStream + void CheckKey(const Local&) { } + +private: + size_t count; +}; + +class DataStream +{ +public: + DataStream(char* aDestinationBuffer) : destinationBuffer(aDestinationBuffer), p(aDestinationBuffer) { } + + void WriteByte(int value) { *p++ = value; } + void WriteByte(const Handle& object, const Handle& key) { *p++ = object->Get(key)->Int32Value(); } +#if USE_MISALIGNED_MEMORY_ACCESS + void WriteInt32(int32_t value) { *reinterpret_cast(p) = value; p += 4; } + void WriteInt64(int64_t value) { *reinterpret_cast(p) = value; p += 8; } + void WriteDouble(double value) { *reinterpret_cast(p) = value; p += 8; } +#else + void WriteInt32(int32_t value) { memcpy(p, &value, 4); p += 4; } + void WriteInt64(int64_t value) { memcpy(p, &value, 8); p += 8; } + void WriteDouble(double value) { memcpy(p, &value, 8); p += 8; } +#endif + void WriteBool(const Handle& value) { WriteByte(value->BooleanValue() ? 1 : 0); } + void WriteInt32(const Handle& value) { WriteInt32(value->Int32Value()); } + void WriteInt32(const Handle& object, const Handle& key) { WriteInt32(object->Get(key)); } + void WriteInt64(const Handle& value) { WriteInt64(value->IntegerValue()); } + void WriteDouble(const Handle& value) { WriteDouble(value->NumberValue()); } + void WriteDouble(const Handle& object, const Handle& key) { WriteDouble(object->Get(key)); } + void WriteUInt32String(uint32_t name) { p += sprintf(p, "%u", name) + 1; } + void WriteLengthPrefixedString(const Local& value) { WriteInt32(value->Utf8Length()+1); WriteString(value); } + void WriteObjectId(const Handle& object, const Handle& key); + void WriteString(const Local& value) { p += value->WriteUtf8(p); } // This returns the number of bytes inclusive of the NULL terminator. + void WriteData(const char* data, size_t length) { memcpy(p, data, length); p += length; } + + void* BeginWriteType() { void* returnValue = p; p++; return returnValue; } + void CommitType(void* beginPoint, BsonType value) { *reinterpret_cast(beginPoint) = value; } + void* BeginWriteSize() { void* returnValue = p; p += 4; return returnValue; } + +#if USE_MISALIGNED_MEMORY_ACCESS + void CommitSize(void* beginPoint) { *reinterpret_cast(beginPoint) = (int32_t) (p - (char*) beginPoint); } +#else + void CommitSize(void* beginPoint) { int32_t value = (int32_t) (p - (char*) beginPoint); memcpy(beginPoint, &value, 4); } +#endif + + size_t GetSerializeSize() const { return p - destinationBuffer; } + + void CheckKey(const Local& keyName); + +protected: + char *const destinationBuffer; // base, never changes + char* p; // cursor into buffer +}; + +template class BSONSerializer : public T +{ +private: + typedef T Inherited; + +public: + BSONSerializer(BSON* aBson, bool aCheckKeys, bool aSerializeFunctions) : Inherited(), checkKeys(aCheckKeys), serializeFunctions(aSerializeFunctions), bson(aBson) { } + BSONSerializer(BSON* aBson, bool aCheckKeys, bool aSerializeFunctions, char* parentParam) : Inherited(parentParam), checkKeys(aCheckKeys), serializeFunctions(aSerializeFunctions), bson(aBson) { } + + void SerializeDocument(const Handle& value); + void SerializeArray(const Handle& value); + void SerializeValue(void* typeLocation, const Handle& value); + +private: + bool checkKeys; + bool serializeFunctions; + BSON* bson; +}; + +//=========================================================================== + +class BSONDeserializer +{ +public: + BSONDeserializer(BSON* aBson, char* data, size_t length); + BSONDeserializer(BSONDeserializer& parentSerializer, size_t length); + + Handle DeserializeDocument(); + + bool HasMoreData() const { return p < pEnd; } + Local ReadCString(); + uint32_t ReadIntegerString(); + int32_t ReadRegexOptions(); + Local ReadString(); + Local ReadObjectId(); + + unsigned char ReadByte() { return *reinterpret_cast(p++); } +#if USE_MISALIGNED_MEMORY_ACCESS + int32_t ReadInt32() { int32_t returnValue = *reinterpret_cast(p); p += 4; return returnValue; } + uint32_t ReadUInt32() { uint32_t returnValue = *reinterpret_cast(p); p += 4; return returnValue; } + int64_t ReadInt64() { int64_t returnValue = *reinterpret_cast(p); p += 8; return returnValue; } + double ReadDouble() { double returnValue = *reinterpret_cast(p); p += 8; return returnValue; } +#else + int32_t ReadInt32() { int32_t returnValue; memcpy(&returnValue, p, 4); p += 4; return returnValue; } + uint32_t ReadUInt32() { uint32_t returnValue; memcpy(&returnValue, p, 4); p += 4; return returnValue; } + int64_t ReadInt64() { int64_t returnValue; memcpy(&returnValue, p, 8); p += 8; return returnValue; } + double ReadDouble() { double returnValue; memcpy(&returnValue, p, 8); p += 8; return returnValue; } +#endif + + size_t GetSerializeSize() const { return p - pStart; } + +private: + Handle DeserializeArray(); + Handle DeserializeValue(BsonType type); + Handle DeserializeDocumentInternal(); + Handle DeserializeArrayInternal(); + + BSON* bson; + char* const pStart; + char* p; + char* const pEnd; +}; + +//=========================================================================== + +#endif // BSON_H_ + +//=========================================================================== diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/index.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/index.js new file mode 100644 index 0000000..85e243c --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/index.js @@ -0,0 +1,30 @@ +var bson = null; + +// Load the precompiled win32 binary +if(process.platform == "win32" && process.arch == "x64") { + bson = require('./win32/x64/bson'); +} else if(process.platform == "win32" && process.arch == "ia32") { + bson = require('./win32/ia32/bson'); +} else { + bson = require('../build/Release/bson'); +} + +exports.BSON = bson.BSON; +exports.Long = require('../lib/bson/long').Long; +exports.ObjectID = require('../lib/bson/objectid').ObjectID; +exports.DBRef = require('../lib/bson/db_ref').DBRef; +exports.Code = require('../lib/bson/code').Code; +exports.Timestamp = require('../lib/bson/timestamp').Timestamp; +exports.Binary = require('../lib/bson/binary').Binary; +exports.Double = require('../lib/bson/double').Double; +exports.MaxKey = require('../lib/bson/max_key').MaxKey; +exports.MinKey = require('../lib/bson/min_key').MinKey; +exports.Symbol = require('../lib/bson/symbol').Symbol; + +// Just add constants tot he Native BSON parser +exports.BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0; +exports.BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1; +exports.BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +exports.BSON.BSON_BINARY_SUBTYPE_UUID = 3; +exports.BSON.BSON_BINARY_SUBTYPE_MD5 = 4; +exports.BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/win32/ia32/bson.node b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/win32/ia32/bson.node new file mode 100644 index 0000000..9ec6e97 Binary files /dev/null and b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/win32/ia32/bson.node differ diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/win32/x64/bson.node b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/win32/x64/bson.node new file mode 100644 index 0000000..d074266 Binary files /dev/null and b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/win32/x64/bson.node differ diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/wscript b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/wscript new file mode 100644 index 0000000..40f5317 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/ext/wscript @@ -0,0 +1,39 @@ +import Options +from os import unlink, symlink, popen +from os.path import exists + +srcdir = "." +blddir = "build" +VERSION = "0.1.0" + +def set_options(opt): + opt.tool_options("compiler_cxx") + opt.add_option( '--debug' + , action='store_true' + , default=False + , help='Build debug variant [Default: False]' + , dest='debug' + ) + +def configure(conf): + conf.check_tool("compiler_cxx") + conf.check_tool("node_addon") + conf.env.append_value('CXXFLAGS', ['-O3', '-funroll-loops']) + + # conf.env.append_value('CXXFLAGS', ['-DDEBUG', '-g', '-O0', '-Wall', '-Wextra']) + # conf.check(lib='node', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='NODE') + +def build(bld): + obj = bld.new_task_gen("cxx", "shlib", "node_addon") + obj.target = "bson" + obj.source = ["bson.cc"] + # obj.uselib = "NODE" + +def shutdown(): + # HACK to get compress.node out of build directory. + # better way to do this? + if Options.commands['clean']: + if exists('bson.node'): unlink('bson.node') + else: + if exists('build/default/bson.node') and not exists('bson.node'): + symlink('build/default/bson.node', 'bson.node') diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/install.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/install.js new file mode 100644 index 0000000..fc13b28 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/install.js @@ -0,0 +1,56 @@ +var spawn = require('child_process').spawn, + exec = require('child_process').exec; + +process.stdout.write("================================================================================\n"); +process.stdout.write("= =\n"); +process.stdout.write("= Attempting to build bson c++ extension =\n"); +process.stdout.write("= Windows: no build will be attempted as binaries are prepackaged =\n"); +process.stdout.write("= Unix: on failure the package will still install without the C++ extension =\n"); +process.stdout.write("= =\n"); +process.stdout.write("================================================================================\n"); + +// Check if we want to build the native code +var build_native = process.env['npm_package_config_mongodb_native'] != null ? process.env['npm_package_config_mongodb_native'] : 'false'; +if(process.env['npm_config_mongodb_debug']) { + console.log("== process.env['npm_package_config_mongodb_native'] :: " + process.env['npm_package_config_mongodb_native']); + console.log("== build_native :: " + build_native); +} + +build_native = process.env['npm_config_mongodb_native'] != null ? process.env['npm_config_mongodb_native'] : build_native; +if(process.env['npm_config_mongodb_debug']) { + console.log("== process.env['npm_config_mongodb_native'] :: " + process.env['npm_config_mongodb_native']); + console.log("== build_native :: " + build_native); +} + +build_native = build_native == 'true' ? true : false; +if(process.env['npm_config_mongodb_debug']) { + console.log("== build_native :: " + build_native); +} + +// If we are building the native bson extension ensure we use gmake if available +if(process.platform != "win32" && process.platform != "win64") { + // Check if we need to use gmake + exec('which gmake', function(err, stdout, stderr) { + // Set up spawn command + var make = null; + // No gmake build using make + if(err != null) { + make = spawn('make', ['node_gyp'], {cwd:process.env['PWD']}); + } else { + make = spawn('gmake', ['node_gyp'], {cwd:process.env['PWD']}); + } + + // Execute spawn + make.stdout.on('data', function(data) { + process.stdout.write(data); + }) + + make.stderr.on('data', function(data) { + process.stdout.write(data); + }) + + make.on('exit', function(code) { + process.stdout.write('child process exited with code ' + code + "\n"); + }) + }); +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/binary.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/binary.js new file mode 100644 index 0000000..cf1d69f --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/binary.js @@ -0,0 +1,332 @@ +/** + * Module dependencies. + */ +var Buffer = require('buffer').Buffer; // TODO just use global Buffer +var bson = require('./bson'); + +// Binary default subtype +var BSON_BINARY_SUBTYPE_DEFAULT = 0; + +/** + * @ignore + * @api private + */ +var writeStringToArray = function(data) { + // Create a buffer + var buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(data.length)) : new Array(data.length); + // Write the content to the buffer + for(var i = 0; i < data.length; i++) { + buffer[i] = data.charCodeAt(i); + } + // Write the string to the buffer + return buffer; +} + +/** + * Convert Array ot Uint8Array to Binary String + * + * @ignore + * @api private + */ +var convertArraytoUtf8BinaryString = function(byteArray, startIndex, endIndex) { + var result = ""; + for(var i = startIndex; i < endIndex; i++) { + result = result + String.fromCharCode(byteArray[i]); + } + return result; +}; + +/** + * A class representation of the BSON Binary type. + * + * Sub types + * - **BSON.BSON_BINARY_SUBTYPE_DEFAULT**, default BSON type. + * - **BSON.BSON_BINARY_SUBTYPE_FUNCTION**, BSON function type. + * - **BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY**, BSON byte array type. + * - **BSON.BSON_BINARY_SUBTYPE_UUID**, BSON uuid type. + * - **BSON.BSON_BINARY_SUBTYPE_MD5**, BSON md5 type. + * - **BSON.BSON_BINARY_SUBTYPE_USER_DEFINED**, BSON user defined type. + * + * @class Represents the Binary BSON type. + * @param {Buffer} buffer a buffer object containing the binary data. + * @param {Number} [subType] the option binary type. + * @return {Grid} + */ +function Binary(buffer, subType) { + if(!(this instanceof Binary)) return new Binary(buffer, subType); + + this._bsontype = 'Binary'; + + if(buffer instanceof Number) { + this.sub_type = buffer; + this.position = 0; + } else { + this.sub_type = subType == null ? BSON_BINARY_SUBTYPE_DEFAULT : subType; + this.position = 0; + } + + if(buffer != null && !(buffer instanceof Number)) { + // Only accept Buffer, Uint8Array or Arrays + if(typeof buffer == 'string') { + // Different ways of writing the length of the string for the different types + if(typeof Buffer != 'undefined') { + this.buffer = new Buffer(buffer); + } else if(typeof Uint8Array != 'undefined' || (Object.prototype.toString.call(buffer) == '[object Array]')) { + this.buffer = writeStringToArray(buffer); + } else { + throw new Error("only String, Buffer, Uint8Array or Array accepted"); + } + } else { + this.buffer = buffer; + } + this.position = buffer.length; + } else { + if(typeof Buffer != 'undefined') { + this.buffer = new Buffer(Binary.BUFFER_SIZE); + } else if(typeof Uint8Array != 'undefined'){ + this.buffer = new Uint8Array(new ArrayBuffer(Binary.BUFFER_SIZE)); + } else { + this.buffer = new Array(Binary.BUFFER_SIZE); + } + // Set position to start of buffer + this.position = 0; + } +}; + +/** + * Updates this binary with byte_value. + * + * @param {Character} byte_value a single byte we wish to write. + * @api public + */ +Binary.prototype.put = function put(byte_value) { + // If it's a string and a has more than one character throw an error + if(byte_value['length'] != null && typeof byte_value != 'number' && byte_value.length != 1) throw new Error("only accepts single character String, Uint8Array or Array"); + if(typeof byte_value != 'number' && byte_value < 0 || byte_value > 255) throw new Error("only accepts number in a valid unsigned byte range 0-255"); + + // Decode the byte value once + var decoded_byte = null; + if(typeof byte_value == 'string') { + decoded_byte = byte_value.charCodeAt(0); + } else if(byte_value['length'] != null) { + decoded_byte = byte_value[0]; + } else { + decoded_byte = byte_value; + } + + if(this.buffer.length > this.position) { + this.buffer[this.position++] = decoded_byte; + } else { + if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { + // Create additional overflow buffer + var buffer = new Buffer(Binary.BUFFER_SIZE + this.buffer.length); + // Combine the two buffers together + this.buffer.copy(buffer, 0, 0, this.buffer.length); + this.buffer = buffer; + this.buffer[this.position++] = decoded_byte; + } else { + var buffer = null; + // Create a new buffer (typed or normal array) + if(Object.prototype.toString.call(this.buffer) == '[object Uint8Array]') { + buffer = new Uint8Array(new ArrayBuffer(Binary.BUFFER_SIZE + this.buffer.length)); + } else { + buffer = new Array(Binary.BUFFER_SIZE + this.buffer.length); + } + + // We need to copy all the content to the new array + for(var i = 0; i < this.buffer.length; i++) { + buffer[i] = this.buffer[i]; + } + + // Reassign the buffer + this.buffer = buffer; + // Write the byte + this.buffer[this.position++] = decoded_byte; + } + } +}; + +/** + * Writes a buffer or string to the binary. + * + * @param {Buffer|String} string a string or buffer to be written to the Binary BSON object. + * @param {Number} offset specify the binary of where to write the content. + * @api public + */ +Binary.prototype.write = function write(string, offset) { + offset = typeof offset == 'number' ? offset : this.position; + + // If the buffer is to small let's extend the buffer + if(this.buffer.length < offset + string.length) { + var buffer = null; + // If we are in node.js + if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { + buffer = new Buffer(this.buffer.length + string.length); + this.buffer.copy(buffer, 0, 0, this.buffer.length); + } else if(Object.prototype.toString.call(this.buffer) == '[object Uint8Array]') { + // Create a new buffer + buffer = new Uint8Array(new ArrayBuffer(this.buffer.length + string.length)) + // Copy the content + for(var i = 0; i < this.position; i++) { + buffer[i] = this.buffer[i]; + } + } + + // Assign the new buffer + this.buffer = buffer; + } + + if(typeof Buffer != 'undefined' && Buffer.isBuffer(string) && Buffer.isBuffer(this.buffer)) { + string.copy(this.buffer, offset, 0, string.length); + this.position = (offset + string.length) > this.position ? (offset + string.length) : this.position; + // offset = string.length + } else if(typeof Buffer != 'undefined' && typeof string == 'string' && Buffer.isBuffer(this.buffer)) { + this.buffer.write(string, 'binary', offset); + this.position = (offset + string.length) > this.position ? (offset + string.length) : this.position; + // offset = string.length; + } else if(Object.prototype.toString.call(string) == '[object Uint8Array]' + || Object.prototype.toString.call(string) == '[object Array]' && typeof string != 'string') { + for(var i = 0; i < string.length; i++) { + this.buffer[offset++] = string[i]; + } + + this.position = offset > this.position ? offset : this.position; + } else if(typeof string == 'string') { + for(var i = 0; i < string.length; i++) { + this.buffer[offset++] = string.charCodeAt(i); + } + + this.position = offset > this.position ? offset : this.position; + } +}; + +/** + * Reads **length** bytes starting at **position**. + * + * @param {Number} position read from the given position in the Binary. + * @param {Number} length the number of bytes to read. + * @return {Buffer} + * @api public + */ +Binary.prototype.read = function read(position, length) { + length = length && length > 0 + ? length + : this.position; + + // Let's return the data based on the type we have + if(this.buffer['slice']) { + return this.buffer.slice(position, position + length); + } else { + // Create a buffer to keep the result + var buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(length)) : new Array(length); + for(var i = 0; i < length; i++) { + buffer[i] = this.buffer[position++]; + } + } + // Return the buffer + return buffer; +}; + +/** + * Returns the value of this binary as a string. + * + * @return {String} + * @api public + */ +Binary.prototype.value = function value(asRaw) { + asRaw = asRaw == null ? false : asRaw; + + // If it's a node.js buffer object + if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) { + return asRaw ? this.buffer.slice(0, this.position) : this.buffer.toString('binary', 0, this.position); + } else { + if(asRaw) { + // we support the slice command use it + if(this.buffer['slice'] != null) { + return this.buffer.slice(0, this.position); + } else { + // Create a new buffer to copy content to + var newBuffer = Object.prototype.toString.call(this.buffer) == '[object Uint8Array]' ? new Uint8Array(new ArrayBuffer(this.position)) : new Array(this.position); + // Copy content + for(var i = 0; i < this.position; i++) { + newBuffer[i] = this.buffer[i]; + } + // Return the buffer + return newBuffer; + } + } else { + return convertArraytoUtf8BinaryString(this.buffer, 0, this.position); + } + } +}; + +/** + * Length. + * + * @return {Number} the length of the binary. + * @api public + */ +Binary.prototype.length = function length() { + return this.position; +}; + +/** + * @ignore + * @api private + */ +Binary.prototype.toJSON = function() { + return this.buffer != null ? this.buffer.toString('base64') : ''; +} + +/** + * @ignore + * @api private + */ +Binary.prototype.toString = function(format) { + return this.buffer != null ? this.buffer.slice(0, this.position).toString(format) : ''; +} + +Binary.BUFFER_SIZE = 256; + +/** + * Default BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_DEFAULT = 0; +/** + * Function BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_FUNCTION = 1; +/** + * Byte Array BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_BYTE_ARRAY = 2; +/** + * UUID BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_UUID = 3; +/** + * MD5 BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_MD5 = 4; +/** + * User BSON type + * + * @classconstant SUBTYPE_DEFAULT + **/ +Binary.SUBTYPE_USER_DEFINED = 128; + +/** + * Expose. + */ +exports.Binary = Binary; + diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/binary_parser.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/binary_parser.js new file mode 100644 index 0000000..d2fc811 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/binary_parser.js @@ -0,0 +1,385 @@ +/** + * Binary Parser. + * Jonas Raoni Soares Silva + * http://jsfromhell.com/classes/binary-parser [v1.0] + */ +var chr = String.fromCharCode; + +var maxBits = []; +for (var i = 0; i < 64; i++) { + maxBits[i] = Math.pow(2, i); +} + +function BinaryParser (bigEndian, allowExceptions) { + if(!(this instanceof BinaryParser)) return new BinaryParser(bigEndian, allowExceptions); + + this.bigEndian = bigEndian; + this.allowExceptions = allowExceptions; +}; + +BinaryParser.warn = function warn (msg) { + if (this.allowExceptions) { + throw new Error(msg); + } + + return 1; +}; + +BinaryParser.decodeFloat = function decodeFloat (data, precisionBits, exponentBits) { + var b = new this.Buffer(this.bigEndian, data); + + b.checkBuffer(precisionBits + exponentBits + 1); + + var bias = maxBits[exponentBits - 1] - 1 + , signal = b.readBits(precisionBits + exponentBits, 1) + , exponent = b.readBits(precisionBits, exponentBits) + , significand = 0 + , divisor = 2 + , curByte = b.buffer.length + (-precisionBits >> 3) - 1; + + do { + for (var byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit; mask >>= 1; ( byteValue & mask ) && ( significand += 1 / divisor ), divisor *= 2 ); + } while (precisionBits -= startBit); + + return exponent == ( bias << 1 ) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity : ( 1 + signal * -2 ) * ( exponent || significand ? !exponent ? Math.pow( 2, -bias + 1 ) * significand : Math.pow( 2, exponent - bias ) * ( 1 + significand ) : 0 ); +}; + +BinaryParser.decodeInt = function decodeInt (data, bits, signed, forceBigEndian) { + var b = new this.Buffer(this.bigEndian || forceBigEndian, data) + , x = b.readBits(0, bits) + , max = maxBits[bits]; //max = Math.pow( 2, bits ); + + return signed && x >= max / 2 + ? x - max + : x; +}; + +BinaryParser.encodeFloat = function encodeFloat (data, precisionBits, exponentBits) { + var bias = maxBits[exponentBits - 1] - 1 + , minExp = -bias + 1 + , maxExp = bias + , minUnnormExp = minExp - precisionBits + , n = parseFloat(data) + , status = isNaN(n) || n == -Infinity || n == +Infinity ? n : 0 + , exp = 0 + , len = 2 * bias + 1 + precisionBits + 3 + , bin = new Array(len) + , signal = (n = status !== 0 ? 0 : n) < 0 + , intPart = Math.floor(n = Math.abs(n)) + , floatPart = n - intPart + , lastBit + , rounded + , result + , i + , j; + + for (i = len; i; bin[--i] = 0); + + for (i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor(intPart / 2)); + + for (i = bias + 1; floatPart > 0 && i; (bin[++i] = ((floatPart *= 2) >= 1) - 0 ) && --floatPart); + + for (i = -1; ++i < len && !bin[i];); + + if (bin[(lastBit = precisionBits - 1 + (i = (exp = bias + 1 - i) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - (exp = minExp - 1))) + 1]) { + if (!(rounded = bin[lastBit])) { + for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]); + } + + for (j = lastBit + 1; rounded && --j >= 0; (bin[j] = !bin[j] - 0) && (rounded = 0)); + } + + for (i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i];); + + if ((exp = bias + 1 - i) >= minExp && exp <= maxExp) { + ++i; + } else if (exp < minExp) { + exp != bias + 1 - len && exp < minUnnormExp && this.warn("encodeFloat::float underflow"); + i = bias + 1 - (exp = minExp - 1); + } + + if (intPart || status !== 0) { + this.warn(intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status); + exp = maxExp + 1; + i = bias + 2; + + if (status == -Infinity) { + signal = 1; + } else if (isNaN(status)) { + bin[i] = 1; + } + } + + for (n = Math.abs(exp + bias), j = exponentBits + 1, result = ""; --j; result = (n % 2) + result, n = n >>= 1); + + for (n = 0, j = 0, i = (result = (signal ? "1" : "0") + result + bin.slice(i, i + precisionBits).join("")).length, r = []; i; j = (j + 1) % 8) { + n += (1 << j) * result.charAt(--i); + if (j == 7) { + r[r.length] = String.fromCharCode(n); + n = 0; + } + } + + r[r.length] = n + ? String.fromCharCode(n) + : ""; + + return (this.bigEndian ? r.reverse() : r).join(""); +}; + +BinaryParser.encodeInt = function encodeInt (data, bits, signed, forceBigEndian) { + var max = maxBits[bits]; + + if (data >= max || data < -(max / 2)) { + this.warn("encodeInt::overflow"); + data = 0; + } + + if (data < 0) { + data += max; + } + + for (var r = []; data; r[r.length] = String.fromCharCode(data % 256), data = Math.floor(data / 256)); + + for (bits = -(-bits >> 3) - r.length; bits--; r[r.length] = "\0"); + + return ((this.bigEndian || forceBigEndian) ? r.reverse() : r).join(""); +}; + +BinaryParser.toSmall = function( data ){ return this.decodeInt( data, 8, true ); }; +BinaryParser.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); }; +BinaryParser.toByte = function( data ){ return this.decodeInt( data, 8, false ); }; +BinaryParser.fromByte = function( data ){ return this.encodeInt( data, 8, false ); }; +BinaryParser.toShort = function( data ){ return this.decodeInt( data, 16, true ); }; +BinaryParser.fromShort = function( data ){ return this.encodeInt( data, 16, true ); }; +BinaryParser.toWord = function( data ){ return this.decodeInt( data, 16, false ); }; +BinaryParser.fromWord = function( data ){ return this.encodeInt( data, 16, false ); }; +BinaryParser.toInt = function( data ){ return this.decodeInt( data, 32, true ); }; +BinaryParser.fromInt = function( data ){ return this.encodeInt( data, 32, true ); }; +BinaryParser.toLong = function( data ){ return this.decodeInt( data, 64, true ); }; +BinaryParser.fromLong = function( data ){ return this.encodeInt( data, 64, true ); }; +BinaryParser.toDWord = function( data ){ return this.decodeInt( data, 32, false ); }; +BinaryParser.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); }; +BinaryParser.toQWord = function( data ){ return this.decodeInt( data, 64, true ); }; +BinaryParser.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); }; +BinaryParser.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); }; +BinaryParser.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); }; +BinaryParser.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); }; +BinaryParser.fromDouble = function( data ){ return this.encodeFloat( data, 52, 11 ); }; + +// Factor out the encode so it can be shared by add_header and push_int32 +BinaryParser.encode_int32 = function encode_int32 (number, asArray) { + var a, b, c, d, unsigned; + unsigned = (number < 0) ? (number + 0x100000000) : number; + a = Math.floor(unsigned / 0xffffff); + unsigned &= 0xffffff; + b = Math.floor(unsigned / 0xffff); + unsigned &= 0xffff; + c = Math.floor(unsigned / 0xff); + unsigned &= 0xff; + d = Math.floor(unsigned); + return asArray ? [chr(a), chr(b), chr(c), chr(d)] : chr(a) + chr(b) + chr(c) + chr(d); +}; + +BinaryParser.encode_int64 = function encode_int64 (number) { + var a, b, c, d, e, f, g, h, unsigned; + unsigned = (number < 0) ? (number + 0x10000000000000000) : number; + a = Math.floor(unsigned / 0xffffffffffffff); + unsigned &= 0xffffffffffffff; + b = Math.floor(unsigned / 0xffffffffffff); + unsigned &= 0xffffffffffff; + c = Math.floor(unsigned / 0xffffffffff); + unsigned &= 0xffffffffff; + d = Math.floor(unsigned / 0xffffffff); + unsigned &= 0xffffffff; + e = Math.floor(unsigned / 0xffffff); + unsigned &= 0xffffff; + f = Math.floor(unsigned / 0xffff); + unsigned &= 0xffff; + g = Math.floor(unsigned / 0xff); + unsigned &= 0xff; + h = Math.floor(unsigned); + return chr(a) + chr(b) + chr(c) + chr(d) + chr(e) + chr(f) + chr(g) + chr(h); +}; + +/** + * UTF8 methods + */ + +// Take a raw binary string and return a utf8 string +BinaryParser.decode_utf8 = function decode_utf8 (binaryStr) { + var len = binaryStr.length + , decoded = '' + , i = 0 + , c = 0 + , c1 = 0 + , c2 = 0 + , c3; + + while (i < len) { + c = binaryStr.charCodeAt(i); + if (c < 128) { + decoded += String.fromCharCode(c); + i++; + } else if ((c > 191) && (c < 224)) { + c2 = binaryStr.charCodeAt(i+1); + decoded += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = binaryStr.charCodeAt(i+1); + c3 = binaryStr.charCodeAt(i+2); + decoded += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + + return decoded; +}; + +// Encode a cstring +BinaryParser.encode_cstring = function encode_cstring (s) { + return unescape(encodeURIComponent(s)) + BinaryParser.fromByte(0); +}; + +// Take a utf8 string and return a binary string +BinaryParser.encode_utf8 = function encode_utf8 (s) { + var a = "" + , c; + + for (var n = 0, len = s.length; n < len; n++) { + c = s.charCodeAt(n); + + if (c < 128) { + a += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + a += String.fromCharCode((c>>6) | 192) ; + a += String.fromCharCode((c&63) | 128); + } else { + a += String.fromCharCode((c>>12) | 224); + a += String.fromCharCode(((c>>6) & 63) | 128); + a += String.fromCharCode((c&63) | 128); + } + } + + return a; +}; + +BinaryParser.hprint = function hprint (s) { + var number; + + for (var i = 0, len = s.length; i < len; i++) { + if (s.charCodeAt(i) < 32) { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(16) + : s.charCodeAt(i).toString(16); + process.stdout.write(number + " ") + } else { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(16) + : s.charCodeAt(i).toString(16); + process.stdout.write(number + " ") + } + } + + process.stdout.write("\n\n"); +}; + +BinaryParser.ilprint = function hprint (s) { + var number; + + for (var i = 0, len = s.length; i < len; i++) { + if (s.charCodeAt(i) < 32) { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(10) + : s.charCodeAt(i).toString(10); + + require('util').debug(number+' : '); + } else { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(10) + : s.charCodeAt(i).toString(10); + require('util').debug(number+' : '+ s.charAt(i)); + } + } +}; + +BinaryParser.hlprint = function hprint (s) { + var number; + + for (var i = 0, len = s.length; i < len; i++) { + if (s.charCodeAt(i) < 32) { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(16) + : s.charCodeAt(i).toString(16); + require('util').debug(number+' : '); + } else { + number = s.charCodeAt(i) <= 15 + ? "0" + s.charCodeAt(i).toString(16) + : s.charCodeAt(i).toString(16); + require('util').debug(number+' : '+ s.charAt(i)); + } + } +}; + +/** + * BinaryParser buffer constructor. + */ +function BinaryParserBuffer (bigEndian, buffer) { + this.bigEndian = bigEndian || 0; + this.buffer = []; + this.setBuffer(buffer); +}; + +BinaryParserBuffer.prototype.setBuffer = function setBuffer (data) { + var l, i, b; + + if (data) { + i = l = data.length; + b = this.buffer = new Array(l); + for (; i; b[l - i] = data.charCodeAt(--i)); + this.bigEndian && b.reverse(); + } +}; + +BinaryParserBuffer.prototype.hasNeededBits = function hasNeededBits (neededBits) { + return this.buffer.length >= -(-neededBits >> 3); +}; + +BinaryParserBuffer.prototype.checkBuffer = function checkBuffer (neededBits) { + if (!this.hasNeededBits(neededBits)) { + throw new Error("checkBuffer::missing bytes"); + } +}; + +BinaryParserBuffer.prototype.readBits = function readBits (start, length) { + //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni) + + function shl (a, b) { + for (; b--; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1); + return a; + } + + if (start < 0 || length <= 0) { + return 0; + } + + this.checkBuffer(start + length); + + var offsetLeft + , offsetRight = start % 8 + , curByte = this.buffer.length - ( start >> 3 ) - 1 + , lastByte = this.buffer.length + ( -( start + length ) >> 3 ) + , diff = curByte - lastByte + , sum = ((this.buffer[ curByte ] >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1)) + (diff && (offsetLeft = (start + length) % 8) ? (this.buffer[lastByte++] & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight : 0); + + for(; diff; sum += shl(this.buffer[lastByte++], (diff-- << 3) - offsetRight)); + + return sum; +}; + +/** + * Expose. + */ +BinaryParser.Buffer = BinaryParserBuffer; + +exports.BinaryParser = BinaryParser; diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/bson.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/bson.js new file mode 100644 index 0000000..0627bf6 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/bson.js @@ -0,0 +1,1495 @@ +var Long = require('./long').Long + , Double = require('./double').Double + , Timestamp = require('./timestamp').Timestamp + , ObjectID = require('./objectid').ObjectID + , Symbol = require('./symbol').Symbol + , Code = require('./code').Code + , MinKey = require('./min_key').MinKey + , MaxKey = require('./max_key').MaxKey + , DBRef = require('./db_ref').DBRef + , Binary = require('./binary').Binary + , BinaryParser = require('./binary_parser').BinaryParser + , writeIEEE754 = require('./float_parser').writeIEEE754 + , readIEEE754 = require('./float_parser').readIEEE754 + +// To ensure that 0.4 of node works correctly +var isDate = function isDate(d) { + return typeof d === 'object' && Object.prototype.toString.call(d) === '[object Date]'; +} + +/** + * Create a new BSON instance + * + * @class Represents the BSON Parser + * @return {BSON} instance of BSON Parser. + */ +function BSON () {}; + +/** + * @ignore + * @api private + */ +// BSON MAX VALUES +BSON.BSON_INT32_MAX = 0x7FFFFFFF; +BSON.BSON_INT32_MIN = -0x80000000; + +BSON.BSON_INT64_MAX = Math.pow(2, 63) - 1; +BSON.BSON_INT64_MIN = -Math.pow(2, 63); + +// JS MAX PRECISE VALUES +BSON.JS_INT_MAX = 0x20000000000000; // Any integer up to 2^53 can be precisely represented by a double. +BSON.JS_INT_MIN = -0x20000000000000; // Any integer down to -2^53 can be precisely represented by a double. + +// Internal long versions +var JS_INT_MAX_LONG = Long.fromNumber(0x20000000000000); // Any integer up to 2^53 can be precisely represented by a double. +var JS_INT_MIN_LONG = Long.fromNumber(-0x20000000000000); // Any integer down to -2^53 can be precisely represented by a double. + +/** + * Number BSON Type + * + * @classconstant BSON_DATA_NUMBER + **/ +BSON.BSON_DATA_NUMBER = 1; +/** + * String BSON Type + * + * @classconstant BSON_DATA_STRING + **/ +BSON.BSON_DATA_STRING = 2; +/** + * Object BSON Type + * + * @classconstant BSON_DATA_OBJECT + **/ +BSON.BSON_DATA_OBJECT = 3; +/** + * Array BSON Type + * + * @classconstant BSON_DATA_ARRAY + **/ +BSON.BSON_DATA_ARRAY = 4; +/** + * Binary BSON Type + * + * @classconstant BSON_DATA_BINARY + **/ +BSON.BSON_DATA_BINARY = 5; +/** + * ObjectID BSON Type + * + * @classconstant BSON_DATA_OID + **/ +BSON.BSON_DATA_OID = 7; +/** + * Boolean BSON Type + * + * @classconstant BSON_DATA_BOOLEAN + **/ +BSON.BSON_DATA_BOOLEAN = 8; +/** + * Date BSON Type + * + * @classconstant BSON_DATA_DATE + **/ +BSON.BSON_DATA_DATE = 9; +/** + * null BSON Type + * + * @classconstant BSON_DATA_NULL + **/ +BSON.BSON_DATA_NULL = 10; +/** + * RegExp BSON Type + * + * @classconstant BSON_DATA_REGEXP + **/ +BSON.BSON_DATA_REGEXP = 11; +/** + * Code BSON Type + * + * @classconstant BSON_DATA_CODE + **/ +BSON.BSON_DATA_CODE = 13; +/** + * Symbol BSON Type + * + * @classconstant BSON_DATA_SYMBOL + **/ +BSON.BSON_DATA_SYMBOL = 14; +/** + * Code with Scope BSON Type + * + * @classconstant BSON_DATA_CODE_W_SCOPE + **/ +BSON.BSON_DATA_CODE_W_SCOPE = 15; +/** + * 32 bit Integer BSON Type + * + * @classconstant BSON_DATA_INT + **/ +BSON.BSON_DATA_INT = 16; +/** + * Timestamp BSON Type + * + * @classconstant BSON_DATA_TIMESTAMP + **/ +BSON.BSON_DATA_TIMESTAMP = 17; +/** + * Long BSON Type + * + * @classconstant BSON_DATA_LONG + **/ +BSON.BSON_DATA_LONG = 18; +/** + * MinKey BSON Type + * + * @classconstant BSON_DATA_MIN_KEY + **/ +BSON.BSON_DATA_MIN_KEY = 0xff; +/** + * MaxKey BSON Type + * + * @classconstant BSON_DATA_MAX_KEY + **/ +BSON.BSON_DATA_MAX_KEY = 0x7f; + +/** + * Binary Default Type + * + * @classconstant BSON_BINARY_SUBTYPE_DEFAULT + **/ +BSON.BSON_BINARY_SUBTYPE_DEFAULT = 0; +/** + * Binary Function Type + * + * @classconstant BSON_BINARY_SUBTYPE_FUNCTION + **/ +BSON.BSON_BINARY_SUBTYPE_FUNCTION = 1; +/** + * Binary Byte Array Type + * + * @classconstant BSON_BINARY_SUBTYPE_BYTE_ARRAY + **/ +BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +/** + * Binary UUID Type + * + * @classconstant BSON_BINARY_SUBTYPE_UUID + **/ +BSON.BSON_BINARY_SUBTYPE_UUID = 3; +/** + * Binary MD5 Type + * + * @classconstant BSON_BINARY_SUBTYPE_MD5 + **/ +BSON.BSON_BINARY_SUBTYPE_MD5 = 4; +/** + * Binary User Defined Type + * + * @classconstant BSON_BINARY_SUBTYPE_USER_DEFINED + **/ +BSON.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +/** + * Calculate the bson size for a passed in Javascript object. + * + * @param {Object} object the Javascript object to calculate the BSON byte size for. + * @param {Boolean} [serializeFunctions] serialize all functions in the object **(default:false)**. + * @return {Number} returns the number of bytes the BSON object will take up. + * @api public + */ +BSON.calculateObjectSize = function calculateObjectSize(object, serializeFunctions) { + var totalLength = (4 + 1); + + if(Array.isArray(object)) { + for(var i = 0; i < object.length; i++) { + totalLength += calculateElement(i.toString(), object[i], serializeFunctions) + } + } else { + // If we have toBSON defined, override the current object + if(object.toBSON) { + object = object.toBSON(); + } + + // Calculate size + for(var key in object) { + totalLength += calculateElement(key, object[key], serializeFunctions) + } + } + + return totalLength; +} + +/** + * @ignore + * @api private + */ +function calculateElement(name, value, serializeFunctions) { + var isBuffer = typeof Buffer !== 'undefined'; + + switch(typeof value) { + case 'string': + return 1 + (!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1 + 4 + (!isBuffer ? numberOfBytes(value) : Buffer.byteLength(value, 'utf8')) + 1; + case 'number': + if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { + if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) { // 32 bit + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (4 + 1); + } else { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (8 + 1); + } + } else { // 64 bit + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (8 + 1); + } + case 'undefined': + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (1); + case 'boolean': + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (1 + 1); + case 'object': + if(value == null || value instanceof MinKey || value instanceof MaxKey || value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (1); + } else if(value instanceof ObjectID || value['_bsontype'] == 'ObjectID') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (12 + 1); + } else if(value instanceof Date || isDate(value)) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (8 + 1); + } else if(typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (1 + 4 + 1) + value.length; + } else if(value instanceof Long || value instanceof Double || value instanceof Timestamp + || value['_bsontype'] == 'Long' || value['_bsontype'] == 'Double' || value['_bsontype'] == 'Timestamp') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (8 + 1); + } else if(value instanceof Code || value['_bsontype'] == 'Code') { + // Calculate size depending on the availability of a scope + if(value.scope != null && Object.keys(value.scope).length > 0) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + 4 + 4 + (!isBuffer ? numberOfBytes(value.code.toString()) : Buffer.byteLength(value.code.toString(), 'utf8')) + 1 + BSON.calculateObjectSize(value.scope, serializeFunctions); + } else { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + 4 + (!isBuffer ? numberOfBytes(value.code.toString()) : Buffer.byteLength(value.code.toString(), 'utf8')) + 1; + } + } else if(value instanceof Binary || value['_bsontype'] == 'Binary') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + (value.position + 1 + 4 + 1); + } else if(value instanceof Symbol || value['_bsontype'] == 'Symbol') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + ((!isBuffer ? numberOfBytes(value.value) : Buffer.byteLength(value.value, 'utf8')) + 4 + 1 + 1); + } else if(value instanceof DBRef || value['_bsontype'] == 'DBRef') { + // Set up correct object for serialization + var ordered_values = { + '$ref': value.namespace + , '$id' : value.oid + }; + + // Add db reference if it exists + if(null != value.db) { + ordered_values['$db'] = value.db; + } + + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + BSON.calculateObjectSize(ordered_values, serializeFunctions); + } else if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + (!isBuffer ? numberOfBytes(value.source) : Buffer.byteLength(value.source, 'utf8')) + 1 + + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1 + } else { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + BSON.calculateObjectSize(value, serializeFunctions) + 1; + } + case 'function': + // WTF for 0.4.X where typeof /someregexp/ === 'function' + if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]' || String.call(value) == '[object RegExp]') { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + (!isBuffer ? numberOfBytes(value.source) : Buffer.byteLength(value.source, 'utf8')) + 1 + + (value.global ? 1 : 0) + (value.ignoreCase ? 1 : 0) + (value.multiline ? 1 : 0) + 1 + } else { + if(serializeFunctions && value.scope != null && Object.keys(value.scope).length > 0) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + 4 + 4 + (!isBuffer ? numberOfBytes(value.toString()) : Buffer.byteLength(value.toString(), 'utf8')) + 1 + BSON.calculateObjectSize(value.scope, serializeFunctions); + } else if(serializeFunctions) { + return (name != null ? ((!isBuffer ? numberOfBytes(name) : Buffer.byteLength(name, 'utf8')) + 1) : 0) + 1 + 4 + (!isBuffer ? numberOfBytes(value.toString()) : Buffer.byteLength(value.toString(), 'utf8')) + 1; + } + } + } + + return 0; +} + +/** + * Serialize a Javascript object using a predefined Buffer and index into the buffer, useful when pre-allocating the space for serialization. + * + * @param {Object} object the Javascript object to serialize. + * @param {Boolean} checkKeys the serializer will check if keys are valid. + * @param {Buffer} buffer the Buffer you pre-allocated to store the serialized BSON object. + * @param {Number} index the index in the buffer where we wish to start serializing into. + * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**. + * @return {Number} returns the new write index in the Buffer. + * @api public + */ +BSON.serializeWithBufferAndIndex = function serializeWithBufferAndIndex(object, checkKeys, buffer, index, serializeFunctions) { + // Default setting false + serializeFunctions = serializeFunctions == null ? false : serializeFunctions; + // Write end information (length of the object) + var size = buffer.length; + // Write the size of the object + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + return serializeObject(object, checkKeys, buffer, index, serializeFunctions) - 1; +} + +/** + * @ignore + * @api private + */ +var serializeObject = function(object, checkKeys, buffer, index, serializeFunctions) { + // Process the object + if(Array.isArray(object)) { + for(var i = 0; i < object.length; i++) { + index = packElement(i.toString(), object[i], checkKeys, buffer, index, serializeFunctions); + } + } else { + // If we have toBSON defined, override the current object + if(object.toBSON) { + object = object.toBSON(); + } + + // Serialize the object + for(var key in object) { + // Check the key and throw error if it's illegal + if(checkKeys == true && (key != '$db' && key != '$ref' && key != '$id')) { + BSON.checkKey(key); + } + + // Pack the element + index = packElement(key, object[key], checkKeys, buffer, index, serializeFunctions); + } + } + + // Write zero + buffer[index++] = 0; + return index; +} + +var stringToBytes = function(str) { + var ch, st, re = []; + for (var i = 0; i < str.length; i++ ) { + ch = str.charCodeAt(i); // get char + st = []; // set up "stack" + do { + st.push( ch & 0xFF ); // push byte to stack + ch = ch >> 8; // shift value down by 1 byte + } + while ( ch ); + // add stack contents to result + // done because chars have "wrong" endianness + re = re.concat( st.reverse() ); + } + // return an array of bytes + return re; +} + +var numberOfBytes = function(str) { + var ch, st, re = 0; + for (var i = 0; i < str.length; i++ ) { + ch = str.charCodeAt(i); // get char + st = []; // set up "stack" + do { + st.push( ch & 0xFF ); // push byte to stack + ch = ch >> 8; // shift value down by 1 byte + } + while ( ch ); + // add stack contents to result + // done because chars have "wrong" endianness + re = re + st.length; + } + // return an array of bytes + return re; +} + +/** + * @ignore + * @api private + */ +var writeToTypedArray = function(buffer, string, index) { + var bytes = stringToBytes(string); + for(var i = 0; i < bytes.length; i++) { + buffer[index + i] = bytes[i]; + } + return bytes.length; +} + +/** + * @ignore + * @api private + */ +var supportsBuffer = typeof Buffer != 'undefined'; + +/** + * @ignore + * @api private + */ +var packElement = function(name, value, checkKeys, buffer, index, serializeFunctions) { + var startIndex = index; + + switch(typeof value) { + case 'string': + // Encode String type + buffer[index++] = BSON.BSON_DATA_STRING; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Calculate size + var size = supportsBuffer ? Buffer.byteLength(value) + 1 : numberOfBytes(value) + 1; + // Write the size of the string to buffer + buffer[index + 3] = (size >> 24) & 0xff; + buffer[index + 2] = (size >> 16) & 0xff; + buffer[index + 1] = (size >> 8) & 0xff; + buffer[index] = size & 0xff; + // Ajust the index + index = index + 4; + // Write the string + supportsBuffer ? buffer.write(value, index, 'utf8') : writeToTypedArray(buffer, value, index); + // Update index + index = index + size - 1; + // Write zero + buffer[index++] = 0; + // Return index + return index; + case 'number': + // We have an integer value + if(Math.floor(value) === value && value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { + // If the value fits in 32 bits encode as int, if it fits in a double + // encode it as a double, otherwise long + if(value >= BSON.BSON_INT32_MIN && value <= BSON.BSON_INT32_MAX) { + // Set int type 32 bits or less + buffer[index++] = BSON.BSON_DATA_INT; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write the int value + buffer[index++] = value & 0xff; + buffer[index++] = (value >> 8) & 0xff; + buffer[index++] = (value >> 16) & 0xff; + buffer[index++] = (value >> 24) & 0xff; + } else if(value >= BSON.JS_INT_MIN && value <= BSON.JS_INT_MAX) { + // Encode as double + buffer[index++] = BSON.BSON_DATA_NUMBER; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write float + writeIEEE754(buffer, value, index, 'little', 52, 8); + // Ajust index + index = index + 8; + } else { + // Set long type + buffer[index++] = BSON.BSON_DATA_LONG; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + var longVal = Long.fromNumber(value); + var lowBits = longVal.getLowBits(); + var highBits = longVal.getHighBits(); + // Encode low bits + buffer[index++] = lowBits & 0xff; + buffer[index++] = (lowBits >> 8) & 0xff; + buffer[index++] = (lowBits >> 16) & 0xff; + buffer[index++] = (lowBits >> 24) & 0xff; + // Encode high bits + buffer[index++] = highBits & 0xff; + buffer[index++] = (highBits >> 8) & 0xff; + buffer[index++] = (highBits >> 16) & 0xff; + buffer[index++] = (highBits >> 24) & 0xff; + } + } else { + // Encode as double + buffer[index++] = BSON.BSON_DATA_NUMBER; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write float + writeIEEE754(buffer, value, index, 'little', 52, 8); + // Ajust index + index = index + 8; + } + + return index; + case 'undefined': + // Set long type + buffer[index++] = BSON.BSON_DATA_NULL; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + return index; + case 'boolean': + // Write the type + buffer[index++] = BSON.BSON_DATA_BOOLEAN; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Encode the boolean value + buffer[index++] = value ? 1 : 0; + return index; + case 'object': + if(value === null || value instanceof MinKey || value instanceof MaxKey + || value['_bsontype'] == 'MinKey' || value['_bsontype'] == 'MaxKey') { + // Write the type of either min or max key + if(value === null) { + buffer[index++] = BSON.BSON_DATA_NULL; + } else if(value instanceof MinKey) { + buffer[index++] = BSON.BSON_DATA_MIN_KEY; + } else { + buffer[index++] = BSON.BSON_DATA_MAX_KEY; + } + + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + return index; + } else if(value instanceof ObjectID || value['_bsontype'] == 'ObjectID') { + // Write the type + buffer[index++] = BSON.BSON_DATA_OID; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Write objectid + supportsBuffer ? buffer.write(value.id, index, 'binary') : writeToTypedArray(buffer, value.id, index); + // Ajust index + index = index + 12; + return index; + } else if(value instanceof Date || isDate(value)) { + // Write the type + buffer[index++] = BSON.BSON_DATA_DATE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Write the date + var dateInMilis = Long.fromNumber(value.getTime()); + var lowBits = dateInMilis.getLowBits(); + var highBits = dateInMilis.getHighBits(); + // Encode low bits + buffer[index++] = lowBits & 0xff; + buffer[index++] = (lowBits >> 8) & 0xff; + buffer[index++] = (lowBits >> 16) & 0xff; + buffer[index++] = (lowBits >> 24) & 0xff; + // Encode high bits + buffer[index++] = highBits & 0xff; + buffer[index++] = (highBits >> 8) & 0xff; + buffer[index++] = (highBits >> 16) & 0xff; + buffer[index++] = (highBits >> 24) & 0xff; + return index; + } else if(typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) { + // Write the type + buffer[index++] = BSON.BSON_DATA_BINARY; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Get size of the buffer (current write point) + var size = value.length; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the default subtype + buffer[index++] = BSON.BSON_BINARY_SUBTYPE_DEFAULT; + // Copy the content form the binary field to the buffer + value.copy(buffer, index, 0, size); + // Adjust the index + index = index + size; + return index; + } else if(value instanceof Long || value instanceof Timestamp || value['_bsontype'] == 'Long' || value['_bsontype'] == 'Timestamp') { + // Write the type + buffer[index++] = value instanceof Long ? BSON.BSON_DATA_LONG : BSON.BSON_DATA_TIMESTAMP; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write the date + var lowBits = value.getLowBits(); + var highBits = value.getHighBits(); + // Encode low bits + buffer[index++] = lowBits & 0xff; + buffer[index++] = (lowBits >> 8) & 0xff; + buffer[index++] = (lowBits >> 16) & 0xff; + buffer[index++] = (lowBits >> 24) & 0xff; + // Encode high bits + buffer[index++] = highBits & 0xff; + buffer[index++] = (highBits >> 8) & 0xff; + buffer[index++] = (highBits >> 16) & 0xff; + buffer[index++] = (highBits >> 24) & 0xff; + return index; + } else if(value instanceof Double || value['_bsontype'] == 'Double') { + // Encode as double + buffer[index++] = BSON.BSON_DATA_NUMBER; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Write float + writeIEEE754(buffer, value, index, 'little', 52, 8); + // Ajust index + index = index + 8; + return index; + } else if(value instanceof Code || value['_bsontype'] == 'Code') { + if(value.scope != null && Object.keys(value.scope).length > 0) { + // Write the type + buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Calculate the scope size + var scopeSize = BSON.calculateObjectSize(value.scope, serializeFunctions); + // Function string + var functionString = value.code.toString(); + // Function Size + var codeSize = supportsBuffer ? Buffer.byteLength(functionString) + 1 : numberOfBytes(functionString) + 1; + + // Calculate full size of the object + var totalSize = 4 + codeSize + scopeSize + 4; + + // Write the total size of the object + buffer[index++] = totalSize & 0xff; + buffer[index++] = (totalSize >> 8) & 0xff; + buffer[index++] = (totalSize >> 16) & 0xff; + buffer[index++] = (totalSize >> 24) & 0xff; + + // Write the size of the string to buffer + buffer[index++] = codeSize & 0xff; + buffer[index++] = (codeSize >> 8) & 0xff; + buffer[index++] = (codeSize >> 16) & 0xff; + buffer[index++] = (codeSize >> 24) & 0xff; + + // Write the string + supportsBuffer ? buffer.write(functionString, index, 'utf8') : writeToTypedArray(buffer, functionString, index); + // Update index + index = index + codeSize - 1; + // Write zero + buffer[index++] = 0; + // Serialize the scope object + var scopeObjectBuffer = supportsBuffer ? new Buffer(scopeSize) : new Uint8Array(new ArrayBuffer(scopeSize)); + // Execute the serialization into a seperate buffer + serializeObject(value.scope, checkKeys, scopeObjectBuffer, 0, serializeFunctions); + + // Adjusted scope Size (removing the header) + var scopeDocSize = scopeSize; + // Write scope object size + buffer[index++] = scopeDocSize & 0xff; + buffer[index++] = (scopeDocSize >> 8) & 0xff; + buffer[index++] = (scopeDocSize >> 16) & 0xff; + buffer[index++] = (scopeDocSize >> 24) & 0xff; + + // Write the scopeObject into the buffer + supportsBuffer ? scopeObjectBuffer.copy(buffer, index, 0, scopeSize) : buffer.set(scopeObjectBuffer, index); + // Adjust index, removing the empty size of the doc (5 bytes 0000000005) + index = index + scopeDocSize - 5; + // Write trailing zero + buffer[index++] = 0; + return index + } else { + buffer[index++] = BSON.BSON_DATA_CODE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Function string + var functionString = value.code.toString(); + // Function Size + var size = supportsBuffer ? Buffer.byteLength(functionString) + 1 : numberOfBytes(functionString) + 1; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the string + buffer.write(functionString, index, 'utf8'); + // Update index + index = index + size - 1; + // Write zero + buffer[index++] = 0; + return index; + } + } else if(value instanceof Binary || value['_bsontype'] == 'Binary') { + // Write the type + buffer[index++] = BSON.BSON_DATA_BINARY; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Extract the buffer + var data = value.value(true); + // Calculate size + var size = value.position; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the subtype to the buffer + buffer[index++] = value.sub_type; + // Write the data to the object + supportsBuffer ? data.copy(buffer, index, 0, value.position) : buffer.set(data, index); + // Ajust index + index = index + value.position; + return index; + } else if(value instanceof Symbol || value['_bsontype'] == 'Symbol') { + // Write the type + buffer[index++] = BSON.BSON_DATA_SYMBOL; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Calculate size + var size = supportsBuffer ? Buffer.byteLength(value.value) + 1 : numberOfBytes(value.value) + 1; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the string + buffer.write(value.value, index, 'utf8'); + // Update index + index = index + size - 1; + // Write zero + buffer[index++] = 0x00; + return index; + } else if(value instanceof DBRef || value['_bsontype'] == 'DBRef') { + // Write the type + buffer[index++] = BSON.BSON_DATA_OBJECT; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Set up correct object for serialization + var ordered_values = { + '$ref': value.namespace + , '$id' : value.oid + }; + + // Add db reference if it exists + if(null != value.db) { + ordered_values['$db'] = value.db; + } + + // Message size + var size = BSON.calculateObjectSize(ordered_values, serializeFunctions); + // Serialize the object + var endIndex = BSON.serializeWithBufferAndIndex(ordered_values, checkKeys, buffer, index, serializeFunctions); + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write zero for object + buffer[endIndex++] = 0x00; + // Return the end index + return endIndex; + } else if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]') { + // Write the type + buffer[index++] = BSON.BSON_DATA_REGEXP; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Write the regular expression string + supportsBuffer ? buffer.write(value.source, index, 'utf8') : writeToTypedArray(buffer, value.source, index); + // Adjust the index + index = index + (supportsBuffer ? Buffer.byteLength(value.source) : numberOfBytes(value.source)); + // Write zero + buffer[index++] = 0x00; + // Write the parameters + if(value.global) buffer[index++] = 0x73; // s + if(value.ignoreCase) buffer[index++] = 0x69; // i + if(value.multiline) buffer[index++] = 0x6d; // m + // Add ending zero + buffer[index++] = 0x00; + return index; + } else { + // Write the type + buffer[index++] = Array.isArray(value) ? BSON.BSON_DATA_ARRAY : BSON.BSON_DATA_OBJECT; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Adjust the index + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + var endIndex = serializeObject(value, checkKeys, buffer, index + 4, serializeFunctions); + // Write size + var size = endIndex - index; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + return endIndex; + } + case 'function': + // WTF for 0.4.X where typeof /someregexp/ === 'function' + if(value instanceof RegExp || Object.prototype.toString.call(value) === '[object RegExp]' || String.call(value) == '[object RegExp]') { + // Write the type + buffer[index++] = BSON.BSON_DATA_REGEXP; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + + // Write the regular expression string + buffer.write(value.source, index, 'utf8'); + // Adjust the index + index = index + (supportsBuffer ? Buffer.byteLength(value.source) : numberOfBytes(value.source)); + // Write zero + buffer[index++] = 0x00; + // Write the parameters + if(value.global) buffer[index++] = 0x73; // s + if(value.ignoreCase) buffer[index++] = 0x69; // i + if(value.multiline) buffer[index++] = 0x6d; // m + // Add ending zero + buffer[index++] = 0x00; + return index; + } else { + if(serializeFunctions && value.scope != null && Object.keys(value.scope).length > 0) { + // Write the type + buffer[index++] = BSON.BSON_DATA_CODE_W_SCOPE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Calculate the scope size + var scopeSize = BSON.calculateObjectSize(value.scope, serializeFunctions); + // Function string + var functionString = value.toString(); + // Function Size + var codeSize = supportsBuffer ? Buffer.byteLength(functionString) + 1 : numberOfBytes(functionString) + 1; + + // Calculate full size of the object + var totalSize = 4 + codeSize + scopeSize; + + // Write the total size of the object + buffer[index++] = totalSize & 0xff; + buffer[index++] = (totalSize >> 8) & 0xff; + buffer[index++] = (totalSize >> 16) & 0xff; + buffer[index++] = (totalSize >> 24) & 0xff; + + // Write the size of the string to buffer + buffer[index++] = codeSize & 0xff; + buffer[index++] = (codeSize >> 8) & 0xff; + buffer[index++] = (codeSize >> 16) & 0xff; + buffer[index++] = (codeSize >> 24) & 0xff; + + // Write the string + buffer.write(functionString, index, 'utf8'); + // Update index + index = index + codeSize - 1; + // Write zero + buffer[index++] = 0; + // Serialize the scope object + var scopeObjectBuffer = new Buffer(scopeSize); + // Execute the serialization into a seperate buffer + serializeObject(value.scope, checkKeys, scopeObjectBuffer, 0, serializeFunctions); + + // Adjusted scope Size (removing the header) + var scopeDocSize = scopeSize - 4; + // Write scope object size + buffer[index++] = scopeDocSize & 0xff; + buffer[index++] = (scopeDocSize >> 8) & 0xff; + buffer[index++] = (scopeDocSize >> 16) & 0xff; + buffer[index++] = (scopeDocSize >> 24) & 0xff; + + // Write the scopeObject into the buffer + scopeObjectBuffer.copy(buffer, index, 0, scopeSize); + + // Adjust index, removing the empty size of the doc (5 bytes 0000000005) + index = index + scopeDocSize - 5; + // Write trailing zero + buffer[index++] = 0; + return index + } else if(serializeFunctions) { + buffer[index++] = BSON.BSON_DATA_CODE; + // Number of written bytes + var numberOfWrittenBytes = supportsBuffer ? buffer.write(name, index, 'utf8') : writeToTypedArray(buffer, name, index); + // Encode the name + index = index + numberOfWrittenBytes + 1; + buffer[index - 1] = 0; + // Function string + var functionString = value.toString(); + // Function Size + var size = supportsBuffer ? Buffer.byteLength(functionString) + 1 : numberOfBytes(functionString) + 1; + // Write the size of the string to buffer + buffer[index++] = size & 0xff; + buffer[index++] = (size >> 8) & 0xff; + buffer[index++] = (size >> 16) & 0xff; + buffer[index++] = (size >> 24) & 0xff; + // Write the string + buffer.write(functionString, index, 'utf8'); + // Update index + index = index + size - 1; + // Write zero + buffer[index++] = 0; + return index; + } + } + } + + // If no value to serialize + return index; +} + +/** + * Serialize a Javascript object. + * + * @param {Object} object the Javascript object to serialize. + * @param {Boolean} checkKeys the serializer will check if keys are valid. + * @param {Boolean} asBuffer return the serialized object as a Buffer object **(ignore)**. + * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**. + * @return {Buffer} returns the Buffer object containing the serialized object. + * @api public + */ +BSON.serialize = function(object, checkKeys, asBuffer, serializeFunctions) { + var buffer = null; + // Calculate the size of the object + var size = BSON.calculateObjectSize(object, serializeFunctions); + // Fetch the best available type for storing the binary data + if(buffer = typeof Buffer != 'undefined') { + buffer = new Buffer(size); + asBuffer = true; + } else if(typeof Uint8Array != 'undefined') { + buffer = new Uint8Array(new ArrayBuffer(size)); + } else { + buffer = new Array(size); + } + + // If asBuffer is false use typed arrays + BSON.serializeWithBufferAndIndex(object, checkKeys, buffer, 0, serializeFunctions); + return buffer; +} + +/** + * Contains the function cache if we have that enable to allow for avoiding the eval step on each deserialization, comparison is by md5 + * + * @ignore + * @api private + */ +var functionCache = BSON.functionCache = {}; + +/** + * Crc state variables shared by function + * + * @ignore + * @api private + */ +var table = [0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D]; + +/** + * CRC32 hash method, Fast and enough versitility for our usage + * + * @ignore + * @api private + */ +var crc32 = function(string, start, end) { + var crc = 0 + var x = 0; + var y = 0; + crc = crc ^ (-1); + + for(var i = start, iTop = end; i < iTop;i++) { + y = (crc ^ string[i]) & 0xFF; + x = table[y]; + crc = (crc >>> 8) ^ x; + } + + return crc ^ (-1); +} + +/** + * Deserialize stream data as BSON documents. + * + * Options + * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized. + * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse. + * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function. + * + * @param {Buffer} data the buffer containing the serialized set of BSON documents. + * @param {Number} startIndex the start index in the data Buffer where the deserialization is to start. + * @param {Number} numberOfDocuments number of documents to deserialize. + * @param {Array} documents an array where to store the deserialized documents. + * @param {Number} docStartIndex the index in the documents array from where to start inserting documents. + * @param {Object} [options] additional options used for the deserialization. + * @return {Number} returns the next index in the buffer after deserialization **x** numbers of documents. + * @api public + */ +BSON.deserializeStream = function(data, startIndex, numberOfDocuments, documents, docStartIndex, options) { + // if(numberOfDocuments !== documents.length) throw new Error("Number of expected results back is less than the number of documents"); + options = options != null ? options : {}; + var index = startIndex; + // Loop over all documents + for(var i = 0; i < numberOfDocuments; i++) { + // Find size of the document + var size = data[index] | data[index + 1] << 8 | data[index + 2] << 16 | data[index + 3] << 24; + // Update options with index + options['index'] = index; + // Parse the document at this point + documents[docStartIndex + i] = BSON.deserialize(data, options); + // Adjust index by the document size + index = index + size; + } + + // Return object containing end index of parsing and list of documents + return index; +} + +/** + * Ensure eval is isolated. + * + * @ignore + * @api private + */ +var isolateEvalWithHash = function(functionCache, hash, functionString, object) { + // Contains the value we are going to set + var value = null; + + // Check for cache hit, eval if missing and return cached function + if(functionCache[hash] == null) { + eval("value = " + functionString); + functionCache[hash] = value; + } + // Set the object + return functionCache[hash].bind(object); +} + +/** + * Ensure eval is isolated. + * + * @ignore + * @api private + */ +var isolateEval = function(functionString) { + // Contains the value we are going to set + var value = null; + // Eval the function + eval("value = " + functionString); + return value; +} + +/** + * Convert Uint8Array to String + * + * @ignore + * @api private + */ +var convertUint8ArrayToUtf8String = function(byteArray, startIndex, endIndex) { + return BinaryParser.decode_utf8(convertArraytoUtf8BinaryString(byteArray, startIndex, endIndex)); +} + +var convertArraytoUtf8BinaryString = function(byteArray, startIndex, endIndex) { + var result = ""; + for(var i = startIndex; i < endIndex; i++) { + result = result + String.fromCharCode(byteArray[i]); + } + + return result; +}; + +/** + * Deserialize data as BSON. + * + * Options + * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized. + * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse. + * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function. + * + * @param {Buffer} buffer the buffer containing the serialized set of BSON documents. + * @param {Object} [options] additional options used for the deserialization. + * @param {Boolean} [isArray] ignore used for recursive parsing. + * @return {Object} returns the deserialized Javascript Object. + * @api public + */ +BSON.deserialize = function(buffer, options, isArray) { + // Options + options = options == null ? {} : options; + var evalFunctions = options['evalFunctions'] == null ? false : options['evalFunctions']; + var cacheFunctions = options['cacheFunctions'] == null ? false : options['cacheFunctions']; + var cacheFunctionsCrc32 = options['cacheFunctionsCrc32'] == null ? false : options['cacheFunctionsCrc32']; + + // Validate that we have at least 4 bytes of buffer + if(buffer.length < 5) throw new Error("corrupt bson message < 5 bytes long"); + + // Set up index + var index = typeof options['index'] == 'number' ? options['index'] : 0; + // Reads in a C style string + var readCStyleString = function() { + // Get the start search index + var i = index; + // Locate the end of the c string + while(buffer[i] !== 0x00) { i++ } + // Grab utf8 encoded string + var string = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('utf8', index, i) : convertUint8ArrayToUtf8String(buffer, index, i); + // Update index position + index = i + 1; + // Return string + return string; + } + + // Create holding object + var object = isArray ? [] : {}; + + // Read the document size + var size = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + + // Ensure buffer is valid size + if(size < 5 || size > buffer.length) throw new Error("corrupt bson message"); + + // While we have more left data left keep parsing + while(true) { + // Read the type + var elementType = buffer[index++]; + // If we get a zero it's the last byte, exit + if(elementType == 0) break; + // Read the name of the field + var name = readCStyleString(); + // Switch on the type + switch(elementType) { + case BSON.BSON_DATA_OID: + var string = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('binary', index, index + 12) : convertArraytoUtf8BinaryString(buffer, index, index + 12); + // Decode the oid + object[name] = new ObjectID(string); + // Update index + index = index + 12; + break; + case BSON.BSON_DATA_STRING: + // Read the content of the field + var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Add string to object + object[name] = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('utf8', index, index + stringSize - 1) : convertUint8ArrayToUtf8String(buffer, index, index + stringSize - 1); + // Update parse index position + index = index + stringSize; + break; + case BSON.BSON_DATA_INT: + // Decode the 32bit value + object[name] = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + break; + case BSON.BSON_DATA_NUMBER: + // Decode the double value + object[name] = readIEEE754(buffer, index, 'little', 52, 8); + // Update the index + index = index + 8; + break; + case BSON.BSON_DATA_DATE: + // Unpack the low and high bits + var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Set date object + object[name] = new Date(new Long(lowBits, highBits).toNumber()); + break; + case BSON.BSON_DATA_BOOLEAN: + // Parse the boolean value + object[name] = buffer[index++] == 1; + break; + case BSON.BSON_DATA_NULL: + // Parse the boolean value + object[name] = null; + break; + case BSON.BSON_DATA_BINARY: + // Decode the size of the binary blob + var binarySize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Decode the subtype + var subType = buffer[index++]; + // Decode as raw Buffer object if options specifies it + if(buffer['slice'] != null) { + object[name] = new Binary(buffer.slice(index, index + binarySize), subType); + } else { + var _buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(binarySize)) : new Array(binarySize); + for(var i = 0; i < binarySize; i++) { + _buffer[i] = buffer[index + i]; + } + // Create the binary object + object[name] = new Binary(_buffer, subType); + } + // Update the index + index = index + binarySize; + break; + case BSON.BSON_DATA_ARRAY: + options['index'] = index; + // Decode the size of the array document + var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; + // Set the array to the object + object[name] = BSON.deserialize(buffer, options, true); + // Adjust the index + index = index + objectSize; + break; + case BSON.BSON_DATA_OBJECT: + options['index'] = index; + // Decode the size of the object document + var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; + // Set the array to the object + object[name] = BSON.deserialize(buffer, options, false); + // Adjust the index + index = index + objectSize; + break; + case BSON.BSON_DATA_REGEXP: + // Create the regexp + var source = readCStyleString(); + var regExpOptions = readCStyleString(); + // For each option add the corresponding one for javascript + var optionsArray = new Array(regExpOptions.length); + + // Parse options + for(var i = 0; i < regExpOptions.length; i++) { + switch(regExpOptions[i]) { + case 'm': + optionsArray[i] = 'm'; + break; + case 's': + optionsArray[i] = 'g'; + break; + case 'i': + optionsArray[i] = 'i'; + break; + } + } + + object[name] = new RegExp(source, optionsArray.join('')); + break; + case BSON.BSON_DATA_LONG: + // Unpack the low and high bits + var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Create long object + var long = new Long(lowBits, highBits); + // Set the object + object[name] = long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG) ? long.toNumber() : long; + break; + case BSON.BSON_DATA_SYMBOL: + // Read the content of the field + var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Add string to object + object[name] = new Symbol(buffer.toString('utf8', index, index + stringSize - 1)); + // Update parse index position + index = index + stringSize; + break; + case BSON.BSON_DATA_TIMESTAMP: + // Unpack the low and high bits + var lowBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + var highBits = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Set the object + object[name] = new Timestamp(lowBits, highBits); + break; + case BSON.BSON_DATA_MIN_KEY: + // Parse the object + object[name] = new MinKey(); + break; + case BSON.BSON_DATA_MAX_KEY: + // Parse the object + object[name] = new MaxKey(); + break; + case BSON.BSON_DATA_CODE: + // Read the content of the field + var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Function string + var functionString = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('utf8', index, index + stringSize - 1) : convertUint8ArrayToUtf8String(buffer, index, index + stringSize - 1); + + // If we are evaluating the functions + if(evalFunctions) { + // Contains the value we are going to set + var value = null; + // If we have cache enabled let's look for the md5 of the function in the cache + if(cacheFunctions) { + var hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString; + // Got to do this to avoid V8 deoptimizing the call due to finding eval + object[name] = isolateEvalWithHash(functionCache, hash, functionString, object); + } else { + // Set directly + object[name] = isolateEval(functionString); + } + } else { + object[name] = new Code(functionString, {}); + } + + // Update parse index position + index = index + stringSize; + break; + case BSON.BSON_DATA_CODE_W_SCOPE: + // Read the content of the field + var totalSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + var stringSize = buffer[index++] | buffer[index++] << 8 | buffer[index++] << 16 | buffer[index++] << 24; + // Javascript function + var functionString = supportsBuffer && Buffer.isBuffer(buffer) ? buffer.toString('utf8', index, index + stringSize - 1) : convertUint8ArrayToUtf8String(buffer, index, index + stringSize - 1); + // Update parse index position + index = index + stringSize; + // Parse the element + options['index'] = index; + // Decode the size of the object document + var objectSize = buffer[index] | buffer[index + 1] << 8 | buffer[index + 2] << 16 | buffer[index + 3] << 24; + // Decode the scope object + var scopeObject = BSON.deserialize(buffer, options, false); + // Adjust the index + index = index + objectSize; + + // If we are evaluating the functions + if(evalFunctions) { + // Contains the value we are going to set + var value = null; + // If we have cache enabled let's look for the md5 of the function in the cache + if(cacheFunctions) { + var hash = cacheFunctionsCrc32 ? crc32(functionString) : functionString; + // Got to do this to avoid V8 deoptimizing the call due to finding eval + object[name] = isolateEvalWithHash(functionCache, hash, functionString, object); + } else { + // Set directly + object[name] = isolateEval(functionString); + } + + // Set the scope on the object + object[name].scope = scopeObject; + } else { + object[name] = new Code(functionString, scopeObject); + } + + // Add string to object + break; + } + } + + // Check if we have a db ref object + if(object['$id'] != null) object = new DBRef(object['$ref'], object['$id'], object['$db']); + + // Return the final objects + return object; +} + +/** + * Check if key name is valid. + * + * @ignore + * @api private + */ +BSON.checkKey = function checkKey (key) { + if (!key.length) return; + // Check if we have a legal key for the object + if('$' == key[0]) { + throw Error("key " + key + " must not start with '$'"); + } else if (!!~key.indexOf('.')) { + throw Error("key " + key + " must not contain '.'"); + } +}; + +/** + * Deserialize data as BSON. + * + * Options + * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized. + * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse. + * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function. + * + * @param {Buffer} buffer the buffer containing the serialized set of BSON documents. + * @param {Object} [options] additional options used for the deserialization. + * @param {Boolean} [isArray] ignore used for recursive parsing. + * @return {Object} returns the deserialized Javascript Object. + * @api public + */ +BSON.prototype.deserialize = function(data, options) { + return BSON.deserialize(data, options); +} + +/** + * Deserialize stream data as BSON documents. + * + * Options + * - **evalFunctions** {Boolean, default:false}, evaluate functions in the BSON document scoped to the object deserialized. + * - **cacheFunctions** {Boolean, default:false}, cache evaluated functions for reuse. + * - **cacheFunctionsCrc32** {Boolean, default:false}, use a crc32 code for caching, otherwise use the string of the function. + * + * @param {Buffer} data the buffer containing the serialized set of BSON documents. + * @param {Number} startIndex the start index in the data Buffer where the deserialization is to start. + * @param {Number} numberOfDocuments number of documents to deserialize. + * @param {Array} documents an array where to store the deserialized documents. + * @param {Number} docStartIndex the index in the documents array from where to start inserting documents. + * @param {Object} [options] additional options used for the deserialization. + * @return {Number} returns the next index in the buffer after deserialization **x** numbers of documents. + * @api public + */ +BSON.prototype.deserializeStream = function(data, startIndex, numberOfDocuments, documents, docStartIndex, options) { + return BSON.deserializeStream(data, startIndex, numberOfDocuments, documents, docStartIndex, options); +} + +/** + * Serialize a Javascript object. + * + * @param {Object} object the Javascript object to serialize. + * @param {Boolean} checkKeys the serializer will check if keys are valid. + * @param {Boolean} asBuffer return the serialized object as a Buffer object **(ignore)**. + * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**. + * @return {Buffer} returns the Buffer object containing the serialized object. + * @api public + */ +BSON.prototype.serialize = function(object, checkKeys, asBuffer, serializeFunctions) { + return BSON.serialize(object, checkKeys, asBuffer, serializeFunctions); +} + +/** + * Calculate the bson size for a passed in Javascript object. + * + * @param {Object} object the Javascript object to calculate the BSON byte size for. + * @param {Boolean} [serializeFunctions] serialize all functions in the object **(default:false)**. + * @return {Number} returns the number of bytes the BSON object will take up. + * @api public + */ +BSON.prototype.calculateObjectSize = function(object, serializeFunctions) { + return BSON.calculateObjectSize(object, serializeFunctions); +} + +/** + * Serialize a Javascript object using a predefined Buffer and index into the buffer, useful when pre-allocating the space for serialization. + * + * @param {Object} object the Javascript object to serialize. + * @param {Boolean} checkKeys the serializer will check if keys are valid. + * @param {Buffer} buffer the Buffer you pre-allocated to store the serialized BSON object. + * @param {Number} index the index in the buffer where we wish to start serializing into. + * @param {Boolean} serializeFunctions serialize the javascript functions **(default:false)**. + * @return {Number} returns the new write index in the Buffer. + * @api public + */ +BSON.prototype.serializeWithBufferAndIndex = function(object, checkKeys, buffer, startIndex, serializeFunctions) { + return BSON.serializeWithBufferAndIndex(object, checkKeys, buffer, startIndex, serializeFunctions); +} + +/** + * @ignore + * @api private + */ +exports.Code = Code; +exports.Symbol = Symbol; +exports.BSON = BSON; +exports.DBRef = DBRef; +exports.Binary = Binary; +exports.ObjectID = ObjectID; +exports.Long = Long; +exports.Timestamp = Timestamp; +exports.Double = Double; +exports.MinKey = MinKey; +exports.MaxKey = MaxKey; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/code.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/code.js new file mode 100644 index 0000000..69b56a3 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/code.js @@ -0,0 +1,25 @@ +/** + * A class representation of the BSON Code type. + * + * @class Represents the BSON Code type. + * @param {String|Function} code a string or function. + * @param {Object} [scope] an optional scope for the function. + * @return {Code} + */ +function Code(code, scope) { + if(!(this instanceof Code)) return new Code(code, scope); + + this._bsontype = 'Code'; + this.code = code; + this.scope = scope == null ? {} : scope; +}; + +/** + * @ignore + * @api private + */ +Code.prototype.toJSON = function() { + return {scope:this.scope, code:this.code}; +} + +exports.Code = Code; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/db_ref.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/db_ref.js new file mode 100644 index 0000000..56b6510 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/db_ref.js @@ -0,0 +1,31 @@ +/** + * A class representation of the BSON DBRef type. + * + * @class Represents the BSON DBRef type. + * @param {String} namespace the collection name. + * @param {ObjectID} oid the reference ObjectID. + * @param {String} [db] optional db name, if omitted the reference is local to the current db. + * @return {DBRef} + */ +function DBRef(namespace, oid, db) { + if(!(this instanceof DBRef)) return new DBRef(namespace, oid, db); + + this._bsontype = 'DBRef'; + this.namespace = namespace; + this.oid = oid; + this.db = db; +}; + +/** + * @ignore + * @api private + */ +DBRef.prototype.toJSON = function() { + return { + '$ref':this.namespace, + '$id':this.oid, + '$db':this.db == null ? '' : this.db + }; +} + +exports.DBRef = DBRef; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/double.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/double.js new file mode 100644 index 0000000..ae51463 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/double.js @@ -0,0 +1,33 @@ +/** + * A class representation of the BSON Double type. + * + * @class Represents the BSON Double type. + * @param {Number} value the number we want to represent as a double. + * @return {Double} + */ +function Double(value) { + if(!(this instanceof Double)) return new Double(value); + + this._bsontype = 'Double'; + this.value = value; +} + +/** + * Access the number value. + * + * @return {Number} returns the wrapped double number. + * @api public + */ +Double.prototype.valueOf = function() { + return this.value; +}; + +/** + * @ignore + * @api private + */ +Double.prototype.toJSON = function() { + return this.value; +} + +exports.Double = Double; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/float_parser.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/float_parser.js new file mode 100644 index 0000000..6fca392 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/float_parser.js @@ -0,0 +1,121 @@ +// Copyright (c) 2008, Fair Oaks Labs, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Fair Oaks Labs, Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// +// Modifications to writeIEEE754 to support negative zeroes made by Brian White + +var readIEEE754 = function(buffer, offset, endian, mLen, nBytes) { + var e, m, + bBE = (endian === 'big'), + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = bBE ? 0 : (nBytes - 1), + d = bBE ? 1 : -1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +}; + +var writeIEEE754 = function(buffer, value, offset, endian, mLen, nBytes) { + var e, m, c, + bBE = (endian === 'big'), + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = bBE ? (nBytes-1) : 0, + d = bBE ? -1 : 1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e+eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); + + buffer[offset + i - d] |= s * 128; +}; + +exports.readIEEE754 = readIEEE754; +exports.writeIEEE754 = writeIEEE754; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/index.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/index.js new file mode 100644 index 0000000..950fcad --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/index.js @@ -0,0 +1,74 @@ +try { + exports.BSONPure = require('./bson'); + exports.BSONNative = require('../../ext'); +} catch(err) { + // do nothing +} + +[ './binary_parser' + , './binary' + , './code' + , './db_ref' + , './double' + , './max_key' + , './min_key' + , './objectid' + , './symbol' + , './timestamp' + , './long'].forEach(function (path) { + var module = require('./' + path); + for (var i in module) { + exports[i] = module[i]; + } +}); + +// Exports all the classes for the NATIVE JS BSON Parser +exports.native = function() { + var classes = {}; + // Map all the classes + [ './binary_parser' + , './binary' + , './code' + , './db_ref' + , './double' + , './max_key' + , './min_key' + , './objectid' + , './symbol' + , './timestamp' + , './long' + , '../../ext' +].forEach(function (path) { + var module = require('./' + path); + for (var i in module) { + classes[i] = module[i]; + } + }); + // Return classes list + return classes; +} + +// Exports all the classes for the PURE JS BSON Parser +exports.pure = function() { + var classes = {}; + // Map all the classes + [ './binary_parser' + , './binary' + , './code' + , './db_ref' + , './double' + , './max_key' + , './min_key' + , './objectid' + , './symbol' + , './timestamp' + , './long' + , '././bson'].forEach(function (path) { + var module = require('./' + path); + for (var i in module) { + classes[i] = module[i]; + } + }); + // Return classes list + return classes; +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/long.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/long.js new file mode 100644 index 0000000..f8f37a6 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/long.js @@ -0,0 +1,854 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2009 Google Inc. All Rights Reserved + +/** + * Defines a Long class for representing a 64-bit two's-complement + * integer value, which faithfully simulates the behavior of a Java "Long". This + * implementation is derived from LongLib in GWT. + * + * Constructs a 64-bit two's-complement integer, given its low and high 32-bit + * values as *signed* integers. See the from* functions below for more + * convenient ways of constructing Longs. + * + * The internal representation of a Long is the two given signed, 32-bit values. + * We use 32-bit pieces because these are the size of integers on which + * Javascript performs bit-operations. For operations like addition and + * multiplication, we split each number into 16-bit pieces, which can easily be + * multiplied within Javascript's floating-point representation without overflow + * or change in sign. + * + * In the algorithms below, we frequently reduce the negative case to the + * positive case by negating the input(s) and then post-processing the result. + * Note that we must ALWAYS check specially whether those values are MIN_VALUE + * (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as + * a positive number, it overflows back into a negative). Not handling this + * case would often result in infinite recursion. + * + * @class Represents the BSON Long type. + * @param {Number} low the low (signed) 32 bits of the Long. + * @param {Number} high the high (signed) 32 bits of the Long. + */ +function Long(low, high) { + if(!(this instanceof Long)) return new Long(low, high); + + this._bsontype = 'Long'; + /** + * @type {number} + * @api private + */ + this.low_ = low | 0; // force into 32 signed bits. + + /** + * @type {number} + * @api private + */ + this.high_ = high | 0; // force into 32 signed bits. +}; + +/** + * Return the int value. + * + * @return {Number} the value, assuming it is a 32-bit integer. + * @api public + */ +Long.prototype.toInt = function() { + return this.low_; +}; + +/** + * Return the Number value. + * + * @return {Number} the closest floating-point representation to this value. + * @api public + */ +Long.prototype.toNumber = function() { + return this.high_ * Long.TWO_PWR_32_DBL_ + + this.getLowBitsUnsigned(); +}; + +/** + * Return the JSON value. + * + * @return {String} the JSON representation. + * @api public + */ +Long.prototype.toJSON = function() { + return this.toString(); +} + +/** + * Return the String value. + * + * @param {Number} [opt_radix] the radix in which the text should be written. + * @return {String} the textual representation of this value. + * @api public + */ +Long.prototype.toString = function(opt_radix) { + var radix = opt_radix || 10; + if (radix < 2 || 36 < radix) { + throw Error('radix out of range: ' + radix); + } + + if (this.isZero()) { + return '0'; + } + + if (this.isNegative()) { + if (this.equals(Long.MIN_VALUE)) { + // We need to change the Long value before it can be negated, so we remove + // the bottom-most digit in this base and then recurse to do the rest. + var radixLong = Long.fromNumber(radix); + var div = this.div(radixLong); + var rem = div.multiply(radixLong).subtract(this); + return div.toString(radix) + rem.toInt().toString(radix); + } else { + return '-' + this.negate().toString(radix); + } + } + + // Do several (6) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Long.fromNumber(Math.pow(radix, 6)); + + var rem = this; + var result = ''; + while (true) { + var remDiv = rem.div(radixToPower); + var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt(); + var digits = intval.toString(radix); + + rem = remDiv; + if (rem.isZero()) { + return digits + result; + } else { + while (digits.length < 6) { + digits = '0' + digits; + } + result = '' + digits + result; + } + } +}; + +/** + * Return the high 32-bits value. + * + * @return {Number} the high 32-bits as a signed value. + * @api public + */ +Long.prototype.getHighBits = function() { + return this.high_; +}; + +/** + * Return the low 32-bits value. + * + * @return {Number} the low 32-bits as a signed value. + * @api public + */ +Long.prototype.getLowBits = function() { + return this.low_; +}; + +/** + * Return the low unsigned 32-bits value. + * + * @return {Number} the low 32-bits as an unsigned value. + * @api public + */ +Long.prototype.getLowBitsUnsigned = function() { + return (this.low_ >= 0) ? + this.low_ : Long.TWO_PWR_32_DBL_ + this.low_; +}; + +/** + * Returns the number of bits needed to represent the absolute value of this Long. + * + * @return {Number} Returns the number of bits needed to represent the absolute value of this Long. + * @api public + */ +Long.prototype.getNumBitsAbs = function() { + if (this.isNegative()) { + if (this.equals(Long.MIN_VALUE)) { + return 64; + } else { + return this.negate().getNumBitsAbs(); + } + } else { + var val = this.high_ != 0 ? this.high_ : this.low_; + for (var bit = 31; bit > 0; bit--) { + if ((val & (1 << bit)) != 0) { + break; + } + } + return this.high_ != 0 ? bit + 33 : bit + 1; + } +}; + +/** + * Return whether this value is zero. + * + * @return {Boolean} whether this value is zero. + * @api public + */ +Long.prototype.isZero = function() { + return this.high_ == 0 && this.low_ == 0; +}; + +/** + * Return whether this value is negative. + * + * @return {Boolean} whether this value is negative. + * @api public + */ +Long.prototype.isNegative = function() { + return this.high_ < 0; +}; + +/** + * Return whether this value is odd. + * + * @return {Boolean} whether this value is odd. + * @api public + */ +Long.prototype.isOdd = function() { + return (this.low_ & 1) == 1; +}; + +/** + * Return whether this Long equals the other + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long equals the other + * @api public + */ +Long.prototype.equals = function(other) { + return (this.high_ == other.high_) && (this.low_ == other.low_); +}; + +/** + * Return whether this Long does not equal the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long does not equal the other. + * @api public + */ +Long.prototype.notEquals = function(other) { + return (this.high_ != other.high_) || (this.low_ != other.low_); +}; + +/** + * Return whether this Long is less than the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long is less than the other. + * @api public + */ +Long.prototype.lessThan = function(other) { + return this.compare(other) < 0; +}; + +/** + * Return whether this Long is less than or equal to the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long is less than or equal to the other. + * @api public + */ +Long.prototype.lessThanOrEqual = function(other) { + return this.compare(other) <= 0; +}; + +/** + * Return whether this Long is greater than the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long is greater than the other. + * @api public + */ +Long.prototype.greaterThan = function(other) { + return this.compare(other) > 0; +}; + +/** + * Return whether this Long is greater than or equal to the other. + * + * @param {Long} other Long to compare against. + * @return {Boolean} whether this Long is greater than or equal to the other. + * @api public + */ +Long.prototype.greaterThanOrEqual = function(other) { + return this.compare(other) >= 0; +}; + +/** + * Compares this Long with the given one. + * + * @param {Long} other Long to compare against. + * @return {Boolean} 0 if they are the same, 1 if the this is greater, and -1 if the given one is greater. + * @api public + */ +Long.prototype.compare = function(other) { + if (this.equals(other)) { + return 0; + } + + var thisNeg = this.isNegative(); + var otherNeg = other.isNegative(); + if (thisNeg && !otherNeg) { + return -1; + } + if (!thisNeg && otherNeg) { + return 1; + } + + // at this point, the signs are the same, so subtraction will not overflow + if (this.subtract(other).isNegative()) { + return -1; + } else { + return 1; + } +}; + +/** + * The negation of this value. + * + * @return {Long} the negation of this value. + * @api public + */ +Long.prototype.negate = function() { + if (this.equals(Long.MIN_VALUE)) { + return Long.MIN_VALUE; + } else { + return this.not().add(Long.ONE); + } +}; + +/** + * Returns the sum of this and the given Long. + * + * @param {Long} other Long to add to this one. + * @return {Long} the sum of this and the given Long. + * @api public + */ +Long.prototype.add = function(other) { + // Divide each number into 4 chunks of 16 bits, and then sum the chunks. + + var a48 = this.high_ >>> 16; + var a32 = this.high_ & 0xFFFF; + var a16 = this.low_ >>> 16; + var a00 = this.low_ & 0xFFFF; + + var b48 = other.high_ >>> 16; + var b32 = other.high_ & 0xFFFF; + var b16 = other.low_ >>> 16; + var b00 = other.low_ & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 + b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 + b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 + b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 + b48; + c48 &= 0xFFFF; + return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32); +}; + +/** + * Returns the difference of this and the given Long. + * + * @param {Long} other Long to subtract from this. + * @return {Long} the difference of this and the given Long. + * @api public + */ +Long.prototype.subtract = function(other) { + return this.add(other.negate()); +}; + +/** + * Returns the product of this and the given Long. + * + * @param {Long} other Long to multiply with this. + * @return {Long} the product of this and the other. + * @api public + */ +Long.prototype.multiply = function(other) { + if (this.isZero()) { + return Long.ZERO; + } else if (other.isZero()) { + return Long.ZERO; + } + + if (this.equals(Long.MIN_VALUE)) { + return other.isOdd() ? Long.MIN_VALUE : Long.ZERO; + } else if (other.equals(Long.MIN_VALUE)) { + return this.isOdd() ? Long.MIN_VALUE : Long.ZERO; + } + + if (this.isNegative()) { + if (other.isNegative()) { + return this.negate().multiply(other.negate()); + } else { + return this.negate().multiply(other).negate(); + } + } else if (other.isNegative()) { + return this.multiply(other.negate()).negate(); + } + + // If both Longs are small, use float multiplication + if (this.lessThan(Long.TWO_PWR_24_) && + other.lessThan(Long.TWO_PWR_24_)) { + return Long.fromNumber(this.toNumber() * other.toNumber()); + } + + // Divide each Long into 4 chunks of 16 bits, and then add up 4x4 products. + // We can skip products that would overflow. + + var a48 = this.high_ >>> 16; + var a32 = this.high_ & 0xFFFF; + var a16 = this.low_ >>> 16; + var a00 = this.low_ & 0xFFFF; + + var b48 = other.high_ >>> 16; + var b32 = other.high_ & 0xFFFF; + var b16 = other.low_ >>> 16; + var b00 = other.low_ & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 * b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 * b00; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c16 += a00 * b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 * b00; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a16 * b16; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a00 * b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; + c48 &= 0xFFFF; + return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32); +}; + +/** + * Returns this Long divided by the given one. + * + * @param {Long} other Long by which to divide. + * @return {Long} this Long divided by the given one. + * @api public + */ +Long.prototype.div = function(other) { + if (other.isZero()) { + throw Error('division by zero'); + } else if (this.isZero()) { + return Long.ZERO; + } + + if (this.equals(Long.MIN_VALUE)) { + if (other.equals(Long.ONE) || + other.equals(Long.NEG_ONE)) { + return Long.MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE + } else if (other.equals(Long.MIN_VALUE)) { + return Long.ONE; + } else { + // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. + var halfThis = this.shiftRight(1); + var approx = halfThis.div(other).shiftLeft(1); + if (approx.equals(Long.ZERO)) { + return other.isNegative() ? Long.ONE : Long.NEG_ONE; + } else { + var rem = this.subtract(other.multiply(approx)); + var result = approx.add(rem.div(other)); + return result; + } + } + } else if (other.equals(Long.MIN_VALUE)) { + return Long.ZERO; + } + + if (this.isNegative()) { + if (other.isNegative()) { + return this.negate().div(other.negate()); + } else { + return this.negate().div(other).negate(); + } + } else if (other.isNegative()) { + return this.div(other.negate()).negate(); + } + + // Repeat the following until the remainder is less than other: find a + // floating-point that approximates remainder / other *from below*, add this + // into the result, and subtract it from the remainder. It is critical that + // the approximate value is less than or equal to the real value so that the + // remainder never becomes negative. + var res = Long.ZERO; + var rem = this; + while (rem.greaterThanOrEqual(other)) { + // Approximate the result of division. This may be a little greater or + // smaller than the actual value. + var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber())); + + // We will tweak the approximate result by changing it in the 48-th digit or + // the smallest non-fractional digit, whichever is larger. + var log2 = Math.ceil(Math.log(approx) / Math.LN2); + var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48); + + // Decrease the approximation until it is smaller than the remainder. Note + // that if it is too large, the product overflows and is negative. + var approxRes = Long.fromNumber(approx); + var approxRem = approxRes.multiply(other); + while (approxRem.isNegative() || approxRem.greaterThan(rem)) { + approx -= delta; + approxRes = Long.fromNumber(approx); + approxRem = approxRes.multiply(other); + } + + // We know the answer can't be zero... and actually, zero would cause + // infinite recursion since we would make no progress. + if (approxRes.isZero()) { + approxRes = Long.ONE; + } + + res = res.add(approxRes); + rem = rem.subtract(approxRem); + } + return res; +}; + +/** + * Returns this Long modulo the given one. + * + * @param {Long} other Long by which to mod. + * @return {Long} this Long modulo the given one. + * @api public + */ +Long.prototype.modulo = function(other) { + return this.subtract(this.div(other).multiply(other)); +}; + +/** + * The bitwise-NOT of this value. + * + * @return {Long} the bitwise-NOT of this value. + * @api public + */ +Long.prototype.not = function() { + return Long.fromBits(~this.low_, ~this.high_); +}; + +/** + * Returns the bitwise-AND of this Long and the given one. + * + * @param {Long} other the Long with which to AND. + * @return {Long} the bitwise-AND of this and the other. + * @api public + */ +Long.prototype.and = function(other) { + return Long.fromBits(this.low_ & other.low_, this.high_ & other.high_); +}; + +/** + * Returns the bitwise-OR of this Long and the given one. + * + * @param {Long} other the Long with which to OR. + * @return {Long} the bitwise-OR of this and the other. + * @api public + */ +Long.prototype.or = function(other) { + return Long.fromBits(this.low_ | other.low_, this.high_ | other.high_); +}; + +/** + * Returns the bitwise-XOR of this Long and the given one. + * + * @param {Long} other the Long with which to XOR. + * @return {Long} the bitwise-XOR of this and the other. + * @api public + */ +Long.prototype.xor = function(other) { + return Long.fromBits(this.low_ ^ other.low_, this.high_ ^ other.high_); +}; + +/** + * Returns this Long with bits shifted to the left by the given amount. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Long} this shifted to the left by the given amount. + * @api public + */ +Long.prototype.shiftLeft = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var low = this.low_; + if (numBits < 32) { + var high = this.high_; + return Long.fromBits( + low << numBits, + (high << numBits) | (low >>> (32 - numBits))); + } else { + return Long.fromBits(0, low << (numBits - 32)); + } + } +}; + +/** + * Returns this Long with bits shifted to the right by the given amount. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Long} this shifted to the right by the given amount. + * @api public + */ +Long.prototype.shiftRight = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var high = this.high_; + if (numBits < 32) { + var low = this.low_; + return Long.fromBits( + (low >>> numBits) | (high << (32 - numBits)), + high >> numBits); + } else { + return Long.fromBits( + high >> (numBits - 32), + high >= 0 ? 0 : -1); + } + } +}; + +/** + * Returns this Long with bits shifted to the right by the given amount, with the new top bits matching the current sign bit. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Long} this shifted to the right by the given amount, with zeros placed into the new leading bits. + * @api public + */ +Long.prototype.shiftRightUnsigned = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var high = this.high_; + if (numBits < 32) { + var low = this.low_; + return Long.fromBits( + (low >>> numBits) | (high << (32 - numBits)), + high >>> numBits); + } else if (numBits == 32) { + return Long.fromBits(high, 0); + } else { + return Long.fromBits(high >>> (numBits - 32), 0); + } + } +}; + +/** + * Returns a Long representing the given (32-bit) integer value. + * + * @param {Number} value the 32-bit integer in question. + * @return {Long} the corresponding Long value. + * @api public + */ +Long.fromInt = function(value) { + if (-128 <= value && value < 128) { + var cachedObj = Long.INT_CACHE_[value]; + if (cachedObj) { + return cachedObj; + } + } + + var obj = new Long(value | 0, value < 0 ? -1 : 0); + if (-128 <= value && value < 128) { + Long.INT_CACHE_[value] = obj; + } + return obj; +}; + +/** + * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * + * @param {Number} value the number in question. + * @return {Long} the corresponding Long value. + * @api public + */ +Long.fromNumber = function(value) { + if (isNaN(value) || !isFinite(value)) { + return Long.ZERO; + } else if (value <= -Long.TWO_PWR_63_DBL_) { + return Long.MIN_VALUE; + } else if (value + 1 >= Long.TWO_PWR_63_DBL_) { + return Long.MAX_VALUE; + } else if (value < 0) { + return Long.fromNumber(-value).negate(); + } else { + return new Long( + (value % Long.TWO_PWR_32_DBL_) | 0, + (value / Long.TWO_PWR_32_DBL_) | 0); + } +}; + +/** + * Returns a Long representing the 64-bit integer that comes by concatenating the given high and low bits. Each is assumed to use 32 bits. + * + * @param {Number} lowBits the low 32-bits. + * @param {Number} highBits the high 32-bits. + * @return {Long} the corresponding Long value. + * @api public + */ +Long.fromBits = function(lowBits, highBits) { + return new Long(lowBits, highBits); +}; + +/** + * Returns a Long representation of the given string, written using the given radix. + * + * @param {String} str the textual representation of the Long. + * @param {Number} opt_radix the radix in which the text is written. + * @return {Long} the corresponding Long value. + * @api public + */ +Long.fromString = function(str, opt_radix) { + if (str.length == 0) { + throw Error('number format error: empty string'); + } + + var radix = opt_radix || 10; + if (radix < 2 || 36 < radix) { + throw Error('radix out of range: ' + radix); + } + + if (str.charAt(0) == '-') { + return Long.fromString(str.substring(1), radix).negate(); + } else if (str.indexOf('-') >= 0) { + throw Error('number format error: interior "-" character: ' + str); + } + + // Do several (8) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Long.fromNumber(Math.pow(radix, 8)); + + var result = Long.ZERO; + for (var i = 0; i < str.length; i += 8) { + var size = Math.min(8, str.length - i); + var value = parseInt(str.substring(i, i + size), radix); + if (size < 8) { + var power = Long.fromNumber(Math.pow(radix, size)); + result = result.multiply(power).add(Long.fromNumber(value)); + } else { + result = result.multiply(radixToPower); + result = result.add(Long.fromNumber(value)); + } + } + return result; +}; + +// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the +// from* methods on which they depend. + + +/** + * A cache of the Long representations of small integer values. + * @type {Object} + * @api private + */ +Long.INT_CACHE_ = {}; + +// NOTE: the compiler should inline these constant values below and then remove +// these variables, so there should be no runtime penalty for these. + +/** + * Number used repeated below in calculations. This must appear before the + * first call to any from* function below. + * @type {number} + * @api private + */ +Long.TWO_PWR_16_DBL_ = 1 << 16; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_24_DBL_ = 1 << 24; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_32_DBL_ = Long.TWO_PWR_16_DBL_ * Long.TWO_PWR_16_DBL_; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_31_DBL_ = Long.TWO_PWR_32_DBL_ / 2; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_48_DBL_ = Long.TWO_PWR_32_DBL_ * Long.TWO_PWR_16_DBL_; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_64_DBL_ = Long.TWO_PWR_32_DBL_ * Long.TWO_PWR_32_DBL_; + +/** + * @type {number} + * @api private + */ +Long.TWO_PWR_63_DBL_ = Long.TWO_PWR_64_DBL_ / 2; + +/** @type {Long} */ +Long.ZERO = Long.fromInt(0); + +/** @type {Long} */ +Long.ONE = Long.fromInt(1); + +/** @type {Long} */ +Long.NEG_ONE = Long.fromInt(-1); + +/** @type {Long} */ +Long.MAX_VALUE = + Long.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0); + +/** @type {Long} */ +Long.MIN_VALUE = Long.fromBits(0, 0x80000000 | 0); + +/** + * @type {Long} + * @api private + */ +Long.TWO_PWR_24_ = Long.fromInt(1 << 24); + +/** + * Expose. + */ +exports.Long = Long; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/max_key.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/max_key.js new file mode 100644 index 0000000..0825408 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/max_key.js @@ -0,0 +1,13 @@ +/** + * A class representation of the BSON MaxKey type. + * + * @class Represents the BSON MaxKey type. + * @return {MaxKey} + */ +function MaxKey() { + if(!(this instanceof MaxKey)) return new MaxKey(); + + this._bsontype = 'MaxKey'; +} + +exports.MaxKey = MaxKey; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/min_key.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/min_key.js new file mode 100644 index 0000000..230c2e6 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/min_key.js @@ -0,0 +1,13 @@ +/** + * A class representation of the BSON MinKey type. + * + * @class Represents the BSON MinKey type. + * @return {MinKey} + */ +function MinKey() { + if(!(this instanceof MinKey)) return new MinKey(); + + this._bsontype = 'MinKey'; +} + +exports.MinKey = MinKey; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js new file mode 100644 index 0000000..1ff9a83 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js @@ -0,0 +1,253 @@ +/** + * Module dependencies. + */ +var BinaryParser = require('./binary_parser').BinaryParser; + +/** + * Machine id. + * + * Create a random 3-byte value (i.e. unique for this + * process). Other drivers use a md5 of the machine id here, but + * that would mean an asyc call to gethostname, so we don't bother. + */ +var MACHINE_ID = parseInt(Math.random() * 0xFFFFFF, 10); + +// Regular expression that checks for hex value +var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$"); + +/** +* Create a new ObjectID instance +* +* @class Represents the BSON ObjectID type +* @param {String|Number} id Can be a 24 byte hex string, 12 byte binary string or a Number. +* @return {Object} instance of ObjectID. +*/ +var ObjectID = function ObjectID(id, _hex) { + if(!(this instanceof ObjectID)) return new ObjectID(id, _hex); + + this._bsontype = 'ObjectID'; + var __id = null; + + // Throw an error if it's not a valid setup + if(id != null && 'number' != typeof id && (id.length != 12 && id.length != 24)) + throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"); + + // Generate id based on the input + if(id == null || typeof id == 'number') { + // convert to 12 byte binary string + this.id = this.generate(id); + } else if(id != null && id.length === 12) { + // assume 12 byte string + this.id = id; + } else if(checkForHexRegExp.test(id)) { + return ObjectID.createFromHexString(id); + } else if(!checkForHexRegExp.test(id)) { + throw new Error("Value passed in is not a valid 24 character hex string"); + } + + if(ObjectID.cacheHexString) this.__id = this.toHexString(); +}; + +// Allow usage of ObjectId aswell as ObjectID +var ObjectId = ObjectID; + +/** +* Return the ObjectID id as a 24 byte hex string representation +* +* @return {String} return the 24 byte hex string representation. +* @api public +*/ +ObjectID.prototype.toHexString = function() { + if(ObjectID.cacheHexString && this.__id) return this.__id; + + var hexString = '' + , number + , value; + + for (var index = 0, len = this.id.length; index < len; index++) { + value = BinaryParser.toByte(this.id[index]); + number = value <= 15 + ? '0' + value.toString(16) + : value.toString(16); + hexString = hexString + number; + } + + if(ObjectID.cacheHexString) this.__id = hexString; + return hexString; +}; + +/** +* Update the ObjectID index used in generating new ObjectID's on the driver +* +* @return {Number} returns next index value. +* @api private +*/ +ObjectID.prototype.get_inc = function() { + return ObjectID.index = (ObjectID.index + 1) % 0xFFFFFF; +}; + +/** +* Update the ObjectID index used in generating new ObjectID's on the driver +* +* @return {Number} returns next index value. +* @api private +*/ +ObjectID.prototype.getInc = function() { + return this.get_inc(); +}; + +/** +* Generate a 12 byte id string used in ObjectID's +* +* @param {Number} [time] optional parameter allowing to pass in a second based timestamp. +* @return {String} return the 12 byte id binary string. +* @api private +*/ +ObjectID.prototype.generate = function(time) { + if ('number' == typeof time) { + var time4Bytes = BinaryParser.encodeInt(time, 32, true, true); + /* for time-based ObjectID the bytes following the time will be zeroed */ + var machine3Bytes = BinaryParser.encodeInt(MACHINE_ID, 24, false); + var pid2Bytes = BinaryParser.fromShort(typeof process === 'undefined' ? Math.floor(Math.random() * 100000) : process.pid); + var index3Bytes = BinaryParser.encodeInt(this.get_inc(), 24, false, true); + } else { + var unixTime = parseInt(Date.now()/1000,10); + var time4Bytes = BinaryParser.encodeInt(unixTime, 32, true, true); + var machine3Bytes = BinaryParser.encodeInt(MACHINE_ID, 24, false); + var pid2Bytes = BinaryParser.fromShort(typeof process === 'undefined' ? Math.floor(Math.random() * 100000) : process.pid); + var index3Bytes = BinaryParser.encodeInt(this.get_inc(), 24, false, true); + } + + return time4Bytes + machine3Bytes + pid2Bytes + index3Bytes; +}; + +/** +* Converts the id into a 24 byte hex string for printing +* +* @return {String} return the 24 byte hex string representation. +* @api private +*/ +ObjectID.prototype.toString = function() { + return this.toHexString(); +}; + +/** +* Converts to a string representation of this Id. +* +* @return {String} return the 24 byte hex string representation. +* @api private +*/ +ObjectID.prototype.inspect = ObjectID.prototype.toString; + +/** +* Converts to its JSON representation. +* +* @return {String} return the 24 byte hex string representation. +* @api private +*/ +ObjectID.prototype.toJSON = function() { + return this.toHexString(); +}; + +/** +* Compares the equality of this ObjectID with `otherID`. +* +* @param {Object} otherID ObjectID instance to compare against. +* @return {Bool} the result of comparing two ObjectID's +* @api public +*/ +ObjectID.prototype.equals = function equals (otherID) { + var id = (otherID instanceof ObjectID || otherID.toHexString) + ? otherID.id + : ObjectID.createFromHexString(otherID).id; + + return this.id === id; +} + +/** +* Returns the generation time in seconds that this ID was generated. +* +* @return {Number} return number of seconds in the timestamp part of the 12 byte id. +* @api public +*/ +ObjectID.prototype.getTimestamp = function() { + var timestamp = new Date(); + timestamp.setTime(Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true)) * 1000); + return timestamp; +} + +/** +* @ignore +* @api private +*/ +ObjectID.index = 0; + +ObjectID.createPk = function createPk () { + return new ObjectID(); +}; + +/** +* Creates an ObjectID from a second based number, with the rest of the ObjectID zeroed out. Used for comparisons or sorting the ObjectID. +* +* @param {Number} time an integer number representing a number of seconds. +* @return {ObjectID} return the created ObjectID +* @api public +*/ +ObjectID.createFromTime = function createFromTime (time) { + var id = BinaryParser.encodeInt(time, 32, true, true) + + BinaryParser.encodeInt(0, 64, true, true); + return new ObjectID(id); +}; + +/** +* Creates an ObjectID from a hex string representation of an ObjectID. +* +* @param {String} hexString create a ObjectID from a passed in 24 byte hexstring. +* @return {ObjectID} return the created ObjectID +* @api public +*/ +ObjectID.createFromHexString = function createFromHexString (hexString) { + // Throw an error if it's not a valid setup + if(typeof hexString === 'undefined' || hexString != null && hexString.length != 24) + throw new Error("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"); + + var len = hexString.length; + + if(len > 12*2) { + throw new Error('Id cannot be longer than 12 bytes'); + } + + var result = '' + , string + , number; + + for (var index = 0; index < len; index += 2) { + string = hexString.substr(index, 2); + number = parseInt(string, 16); + result += BinaryParser.fromByte(number); + } + + return new ObjectID(result, hexString); +}; + +/** +* @ignore +*/ +Object.defineProperty(ObjectID.prototype, "generationTime", { + enumerable: true + , get: function () { + return Math.floor(BinaryParser.decodeInt(this.id.substring(0,4), 32, true, true)); + } + , set: function (value) { + var value = BinaryParser.encodeInt(value, 32, true, true); + this.id = value + this.id.substr(4); + // delete this.__id; + this.toHexString(); + } +}); + +/** + * Expose. + */ +exports.ObjectID = ObjectID; +exports.ObjectId = ObjectID; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/symbol.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/symbol.js new file mode 100644 index 0000000..8e2838d --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/symbol.js @@ -0,0 +1,48 @@ +/** + * A class representation of the BSON Symbol type. + * + * @class Represents the BSON Symbol type. + * @param {String} value the string representing the symbol. + * @return {Symbol} + */ +function Symbol(value) { + if(!(this instanceof Symbol)) return new Symbol(value); + this._bsontype = 'Symbol'; + this.value = value; +} + +/** + * Access the wrapped string value. + * + * @return {String} returns the wrapped string. + * @api public + */ +Symbol.prototype.valueOf = function() { + return this.value; +}; + +/** + * @ignore + * @api private + */ +Symbol.prototype.toString = function() { + return this.value; +} + +/** + * @ignore + * @api private + */ +Symbol.prototype.inspect = function() { + return this.value; +} + +/** + * @ignore + * @api private + */ +Symbol.prototype.toJSON = function() { + return this.value; +} + +exports.Symbol = Symbol; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/timestamp.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/timestamp.js new file mode 100644 index 0000000..c650d15 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/lib/bson/timestamp.js @@ -0,0 +1,853 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright 2009 Google Inc. All Rights Reserved + +/** + * Defines a Timestamp class for representing a 64-bit two's-complement + * integer value, which faithfully simulates the behavior of a Java "Timestamp". This + * implementation is derived from TimestampLib in GWT. + * + * Constructs a 64-bit two's-complement integer, given its low and high 32-bit + * values as *signed* integers. See the from* functions below for more + * convenient ways of constructing Timestamps. + * + * The internal representation of a Timestamp is the two given signed, 32-bit values. + * We use 32-bit pieces because these are the size of integers on which + * Javascript performs bit-operations. For operations like addition and + * multiplication, we split each number into 16-bit pieces, which can easily be + * multiplied within Javascript's floating-point representation without overflow + * or change in sign. + * + * In the algorithms below, we frequently reduce the negative case to the + * positive case by negating the input(s) and then post-processing the result. + * Note that we must ALWAYS check specially whether those values are MIN_VALUE + * (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as + * a positive number, it overflows back into a negative). Not handling this + * case would often result in infinite recursion. + * + * @class Represents the BSON Timestamp type. + * @param {Number} low the low (signed) 32 bits of the Timestamp. + * @param {Number} high the high (signed) 32 bits of the Timestamp. + */ +function Timestamp(low, high) { + if(!(this instanceof Timestamp)) return new Timestamp(low, high); + this._bsontype = 'Timestamp'; + /** + * @type {number} + * @api private + */ + this.low_ = low | 0; // force into 32 signed bits. + + /** + * @type {number} + * @api private + */ + this.high_ = high | 0; // force into 32 signed bits. +}; + +/** + * Return the int value. + * + * @return {Number} the value, assuming it is a 32-bit integer. + * @api public + */ +Timestamp.prototype.toInt = function() { + return this.low_; +}; + +/** + * Return the Number value. + * + * @return {Number} the closest floating-point representation to this value. + * @api public + */ +Timestamp.prototype.toNumber = function() { + return this.high_ * Timestamp.TWO_PWR_32_DBL_ + + this.getLowBitsUnsigned(); +}; + +/** + * Return the JSON value. + * + * @return {String} the JSON representation. + * @api public + */ +Timestamp.prototype.toJSON = function() { + return this.toString(); +} + +/** + * Return the String value. + * + * @param {Number} [opt_radix] the radix in which the text should be written. + * @return {String} the textual representation of this value. + * @api public + */ +Timestamp.prototype.toString = function(opt_radix) { + var radix = opt_radix || 10; + if (radix < 2 || 36 < radix) { + throw Error('radix out of range: ' + radix); + } + + if (this.isZero()) { + return '0'; + } + + if (this.isNegative()) { + if (this.equals(Timestamp.MIN_VALUE)) { + // We need to change the Timestamp value before it can be negated, so we remove + // the bottom-most digit in this base and then recurse to do the rest. + var radixTimestamp = Timestamp.fromNumber(radix); + var div = this.div(radixTimestamp); + var rem = div.multiply(radixTimestamp).subtract(this); + return div.toString(radix) + rem.toInt().toString(radix); + } else { + return '-' + this.negate().toString(radix); + } + } + + // Do several (6) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Timestamp.fromNumber(Math.pow(radix, 6)); + + var rem = this; + var result = ''; + while (true) { + var remDiv = rem.div(radixToPower); + var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt(); + var digits = intval.toString(radix); + + rem = remDiv; + if (rem.isZero()) { + return digits + result; + } else { + while (digits.length < 6) { + digits = '0' + digits; + } + result = '' + digits + result; + } + } +}; + +/** + * Return the high 32-bits value. + * + * @return {Number} the high 32-bits as a signed value. + * @api public + */ +Timestamp.prototype.getHighBits = function() { + return this.high_; +}; + +/** + * Return the low 32-bits value. + * + * @return {Number} the low 32-bits as a signed value. + * @api public + */ +Timestamp.prototype.getLowBits = function() { + return this.low_; +}; + +/** + * Return the low unsigned 32-bits value. + * + * @return {Number} the low 32-bits as an unsigned value. + * @api public + */ +Timestamp.prototype.getLowBitsUnsigned = function() { + return (this.low_ >= 0) ? + this.low_ : Timestamp.TWO_PWR_32_DBL_ + this.low_; +}; + +/** + * Returns the number of bits needed to represent the absolute value of this Timestamp. + * + * @return {Number} Returns the number of bits needed to represent the absolute value of this Timestamp. + * @api public + */ +Timestamp.prototype.getNumBitsAbs = function() { + if (this.isNegative()) { + if (this.equals(Timestamp.MIN_VALUE)) { + return 64; + } else { + return this.negate().getNumBitsAbs(); + } + } else { + var val = this.high_ != 0 ? this.high_ : this.low_; + for (var bit = 31; bit > 0; bit--) { + if ((val & (1 << bit)) != 0) { + break; + } + } + return this.high_ != 0 ? bit + 33 : bit + 1; + } +}; + +/** + * Return whether this value is zero. + * + * @return {Boolean} whether this value is zero. + * @api public + */ +Timestamp.prototype.isZero = function() { + return this.high_ == 0 && this.low_ == 0; +}; + +/** + * Return whether this value is negative. + * + * @return {Boolean} whether this value is negative. + * @api public + */ +Timestamp.prototype.isNegative = function() { + return this.high_ < 0; +}; + +/** + * Return whether this value is odd. + * + * @return {Boolean} whether this value is odd. + * @api public + */ +Timestamp.prototype.isOdd = function() { + return (this.low_ & 1) == 1; +}; + +/** + * Return whether this Timestamp equals the other + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp equals the other + * @api public + */ +Timestamp.prototype.equals = function(other) { + return (this.high_ == other.high_) && (this.low_ == other.low_); +}; + +/** + * Return whether this Timestamp does not equal the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp does not equal the other. + * @api public + */ +Timestamp.prototype.notEquals = function(other) { + return (this.high_ != other.high_) || (this.low_ != other.low_); +}; + +/** + * Return whether this Timestamp is less than the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp is less than the other. + * @api public + */ +Timestamp.prototype.lessThan = function(other) { + return this.compare(other) < 0; +}; + +/** + * Return whether this Timestamp is less than or equal to the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp is less than or equal to the other. + * @api public + */ +Timestamp.prototype.lessThanOrEqual = function(other) { + return this.compare(other) <= 0; +}; + +/** + * Return whether this Timestamp is greater than the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp is greater than the other. + * @api public + */ +Timestamp.prototype.greaterThan = function(other) { + return this.compare(other) > 0; +}; + +/** + * Return whether this Timestamp is greater than or equal to the other. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} whether this Timestamp is greater than or equal to the other. + * @api public + */ +Timestamp.prototype.greaterThanOrEqual = function(other) { + return this.compare(other) >= 0; +}; + +/** + * Compares this Timestamp with the given one. + * + * @param {Timestamp} other Timestamp to compare against. + * @return {Boolean} 0 if they are the same, 1 if the this is greater, and -1 if the given one is greater. + * @api public + */ +Timestamp.prototype.compare = function(other) { + if (this.equals(other)) { + return 0; + } + + var thisNeg = this.isNegative(); + var otherNeg = other.isNegative(); + if (thisNeg && !otherNeg) { + return -1; + } + if (!thisNeg && otherNeg) { + return 1; + } + + // at this point, the signs are the same, so subtraction will not overflow + if (this.subtract(other).isNegative()) { + return -1; + } else { + return 1; + } +}; + +/** + * The negation of this value. + * + * @return {Timestamp} the negation of this value. + * @api public + */ +Timestamp.prototype.negate = function() { + if (this.equals(Timestamp.MIN_VALUE)) { + return Timestamp.MIN_VALUE; + } else { + return this.not().add(Timestamp.ONE); + } +}; + +/** + * Returns the sum of this and the given Timestamp. + * + * @param {Timestamp} other Timestamp to add to this one. + * @return {Timestamp} the sum of this and the given Timestamp. + * @api public + */ +Timestamp.prototype.add = function(other) { + // Divide each number into 4 chunks of 16 bits, and then sum the chunks. + + var a48 = this.high_ >>> 16; + var a32 = this.high_ & 0xFFFF; + var a16 = this.low_ >>> 16; + var a00 = this.low_ & 0xFFFF; + + var b48 = other.high_ >>> 16; + var b32 = other.high_ & 0xFFFF; + var b16 = other.low_ >>> 16; + var b00 = other.low_ & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 + b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 + b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 + b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 + b48; + c48 &= 0xFFFF; + return Timestamp.fromBits((c16 << 16) | c00, (c48 << 16) | c32); +}; + +/** + * Returns the difference of this and the given Timestamp. + * + * @param {Timestamp} other Timestamp to subtract from this. + * @return {Timestamp} the difference of this and the given Timestamp. + * @api public + */ +Timestamp.prototype.subtract = function(other) { + return this.add(other.negate()); +}; + +/** + * Returns the product of this and the given Timestamp. + * + * @param {Timestamp} other Timestamp to multiply with this. + * @return {Timestamp} the product of this and the other. + * @api public + */ +Timestamp.prototype.multiply = function(other) { + if (this.isZero()) { + return Timestamp.ZERO; + } else if (other.isZero()) { + return Timestamp.ZERO; + } + + if (this.equals(Timestamp.MIN_VALUE)) { + return other.isOdd() ? Timestamp.MIN_VALUE : Timestamp.ZERO; + } else if (other.equals(Timestamp.MIN_VALUE)) { + return this.isOdd() ? Timestamp.MIN_VALUE : Timestamp.ZERO; + } + + if (this.isNegative()) { + if (other.isNegative()) { + return this.negate().multiply(other.negate()); + } else { + return this.negate().multiply(other).negate(); + } + } else if (other.isNegative()) { + return this.multiply(other.negate()).negate(); + } + + // If both Timestamps are small, use float multiplication + if (this.lessThan(Timestamp.TWO_PWR_24_) && + other.lessThan(Timestamp.TWO_PWR_24_)) { + return Timestamp.fromNumber(this.toNumber() * other.toNumber()); + } + + // Divide each Timestamp into 4 chunks of 16 bits, and then add up 4x4 products. + // We can skip products that would overflow. + + var a48 = this.high_ >>> 16; + var a32 = this.high_ & 0xFFFF; + var a16 = this.low_ >>> 16; + var a00 = this.low_ & 0xFFFF; + + var b48 = other.high_ >>> 16; + var b32 = other.high_ & 0xFFFF; + var b16 = other.low_ >>> 16; + var b00 = other.low_ & 0xFFFF; + + var c48 = 0, c32 = 0, c16 = 0, c00 = 0; + c00 += a00 * b00; + c16 += c00 >>> 16; + c00 &= 0xFFFF; + c16 += a16 * b00; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c16 += a00 * b16; + c32 += c16 >>> 16; + c16 &= 0xFFFF; + c32 += a32 * b00; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a16 * b16; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c32 += a00 * b32; + c48 += c32 >>> 16; + c32 &= 0xFFFF; + c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48; + c48 &= 0xFFFF; + return Timestamp.fromBits((c16 << 16) | c00, (c48 << 16) | c32); +}; + +/** + * Returns this Timestamp divided by the given one. + * + * @param {Timestamp} other Timestamp by which to divide. + * @return {Timestamp} this Timestamp divided by the given one. + * @api public + */ +Timestamp.prototype.div = function(other) { + if (other.isZero()) { + throw Error('division by zero'); + } else if (this.isZero()) { + return Timestamp.ZERO; + } + + if (this.equals(Timestamp.MIN_VALUE)) { + if (other.equals(Timestamp.ONE) || + other.equals(Timestamp.NEG_ONE)) { + return Timestamp.MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE + } else if (other.equals(Timestamp.MIN_VALUE)) { + return Timestamp.ONE; + } else { + // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|. + var halfThis = this.shiftRight(1); + var approx = halfThis.div(other).shiftLeft(1); + if (approx.equals(Timestamp.ZERO)) { + return other.isNegative() ? Timestamp.ONE : Timestamp.NEG_ONE; + } else { + var rem = this.subtract(other.multiply(approx)); + var result = approx.add(rem.div(other)); + return result; + } + } + } else if (other.equals(Timestamp.MIN_VALUE)) { + return Timestamp.ZERO; + } + + if (this.isNegative()) { + if (other.isNegative()) { + return this.negate().div(other.negate()); + } else { + return this.negate().div(other).negate(); + } + } else if (other.isNegative()) { + return this.div(other.negate()).negate(); + } + + // Repeat the following until the remainder is less than other: find a + // floating-point that approximates remainder / other *from below*, add this + // into the result, and subtract it from the remainder. It is critical that + // the approximate value is less than or equal to the real value so that the + // remainder never becomes negative. + var res = Timestamp.ZERO; + var rem = this; + while (rem.greaterThanOrEqual(other)) { + // Approximate the result of division. This may be a little greater or + // smaller than the actual value. + var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber())); + + // We will tweak the approximate result by changing it in the 48-th digit or + // the smallest non-fractional digit, whichever is larger. + var log2 = Math.ceil(Math.log(approx) / Math.LN2); + var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48); + + // Decrease the approximation until it is smaller than the remainder. Note + // that if it is too large, the product overflows and is negative. + var approxRes = Timestamp.fromNumber(approx); + var approxRem = approxRes.multiply(other); + while (approxRem.isNegative() || approxRem.greaterThan(rem)) { + approx -= delta; + approxRes = Timestamp.fromNumber(approx); + approxRem = approxRes.multiply(other); + } + + // We know the answer can't be zero... and actually, zero would cause + // infinite recursion since we would make no progress. + if (approxRes.isZero()) { + approxRes = Timestamp.ONE; + } + + res = res.add(approxRes); + rem = rem.subtract(approxRem); + } + return res; +}; + +/** + * Returns this Timestamp modulo the given one. + * + * @param {Timestamp} other Timestamp by which to mod. + * @return {Timestamp} this Timestamp modulo the given one. + * @api public + */ +Timestamp.prototype.modulo = function(other) { + return this.subtract(this.div(other).multiply(other)); +}; + +/** + * The bitwise-NOT of this value. + * + * @return {Timestamp} the bitwise-NOT of this value. + * @api public + */ +Timestamp.prototype.not = function() { + return Timestamp.fromBits(~this.low_, ~this.high_); +}; + +/** + * Returns the bitwise-AND of this Timestamp and the given one. + * + * @param {Timestamp} other the Timestamp with which to AND. + * @return {Timestamp} the bitwise-AND of this and the other. + * @api public + */ +Timestamp.prototype.and = function(other) { + return Timestamp.fromBits(this.low_ & other.low_, this.high_ & other.high_); +}; + +/** + * Returns the bitwise-OR of this Timestamp and the given one. + * + * @param {Timestamp} other the Timestamp with which to OR. + * @return {Timestamp} the bitwise-OR of this and the other. + * @api public + */ +Timestamp.prototype.or = function(other) { + return Timestamp.fromBits(this.low_ | other.low_, this.high_ | other.high_); +}; + +/** + * Returns the bitwise-XOR of this Timestamp and the given one. + * + * @param {Timestamp} other the Timestamp with which to XOR. + * @return {Timestamp} the bitwise-XOR of this and the other. + * @api public + */ +Timestamp.prototype.xor = function(other) { + return Timestamp.fromBits(this.low_ ^ other.low_, this.high_ ^ other.high_); +}; + +/** + * Returns this Timestamp with bits shifted to the left by the given amount. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Timestamp} this shifted to the left by the given amount. + * @api public + */ +Timestamp.prototype.shiftLeft = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var low = this.low_; + if (numBits < 32) { + var high = this.high_; + return Timestamp.fromBits( + low << numBits, + (high << numBits) | (low >>> (32 - numBits))); + } else { + return Timestamp.fromBits(0, low << (numBits - 32)); + } + } +}; + +/** + * Returns this Timestamp with bits shifted to the right by the given amount. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Timestamp} this shifted to the right by the given amount. + * @api public + */ +Timestamp.prototype.shiftRight = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var high = this.high_; + if (numBits < 32) { + var low = this.low_; + return Timestamp.fromBits( + (low >>> numBits) | (high << (32 - numBits)), + high >> numBits); + } else { + return Timestamp.fromBits( + high >> (numBits - 32), + high >= 0 ? 0 : -1); + } + } +}; + +/** + * Returns this Timestamp with bits shifted to the right by the given amount, with the new top bits matching the current sign bit. + * + * @param {Number} numBits the number of bits by which to shift. + * @return {Timestamp} this shifted to the right by the given amount, with zeros placed into the new leading bits. + * @api public + */ +Timestamp.prototype.shiftRightUnsigned = function(numBits) { + numBits &= 63; + if (numBits == 0) { + return this; + } else { + var high = this.high_; + if (numBits < 32) { + var low = this.low_; + return Timestamp.fromBits( + (low >>> numBits) | (high << (32 - numBits)), + high >>> numBits); + } else if (numBits == 32) { + return Timestamp.fromBits(high, 0); + } else { + return Timestamp.fromBits(high >>> (numBits - 32), 0); + } + } +}; + +/** + * Returns a Timestamp representing the given (32-bit) integer value. + * + * @param {Number} value the 32-bit integer in question. + * @return {Timestamp} the corresponding Timestamp value. + * @api public + */ +Timestamp.fromInt = function(value) { + if (-128 <= value && value < 128) { + var cachedObj = Timestamp.INT_CACHE_[value]; + if (cachedObj) { + return cachedObj; + } + } + + var obj = new Timestamp(value | 0, value < 0 ? -1 : 0); + if (-128 <= value && value < 128) { + Timestamp.INT_CACHE_[value] = obj; + } + return obj; +}; + +/** + * Returns a Timestamp representing the given value, provided that it is a finite number. Otherwise, zero is returned. + * + * @param {Number} value the number in question. + * @return {Timestamp} the corresponding Timestamp value. + * @api public + */ +Timestamp.fromNumber = function(value) { + if (isNaN(value) || !isFinite(value)) { + return Timestamp.ZERO; + } else if (value <= -Timestamp.TWO_PWR_63_DBL_) { + return Timestamp.MIN_VALUE; + } else if (value + 1 >= Timestamp.TWO_PWR_63_DBL_) { + return Timestamp.MAX_VALUE; + } else if (value < 0) { + return Timestamp.fromNumber(-value).negate(); + } else { + return new Timestamp( + (value % Timestamp.TWO_PWR_32_DBL_) | 0, + (value / Timestamp.TWO_PWR_32_DBL_) | 0); + } +}; + +/** + * Returns a Timestamp representing the 64-bit integer that comes by concatenating the given high and low bits. Each is assumed to use 32 bits. + * + * @param {Number} lowBits the low 32-bits. + * @param {Number} highBits the high 32-bits. + * @return {Timestamp} the corresponding Timestamp value. + * @api public + */ +Timestamp.fromBits = function(lowBits, highBits) { + return new Timestamp(lowBits, highBits); +}; + +/** + * Returns a Timestamp representation of the given string, written using the given radix. + * + * @param {String} str the textual representation of the Timestamp. + * @param {Number} opt_radix the radix in which the text is written. + * @return {Timestamp} the corresponding Timestamp value. + * @api public + */ +Timestamp.fromString = function(str, opt_radix) { + if (str.length == 0) { + throw Error('number format error: empty string'); + } + + var radix = opt_radix || 10; + if (radix < 2 || 36 < radix) { + throw Error('radix out of range: ' + radix); + } + + if (str.charAt(0) == '-') { + return Timestamp.fromString(str.substring(1), radix).negate(); + } else if (str.indexOf('-') >= 0) { + throw Error('number format error: interior "-" character: ' + str); + } + + // Do several (8) digits each time through the loop, so as to + // minimize the calls to the very expensive emulated div. + var radixToPower = Timestamp.fromNumber(Math.pow(radix, 8)); + + var result = Timestamp.ZERO; + for (var i = 0; i < str.length; i += 8) { + var size = Math.min(8, str.length - i); + var value = parseInt(str.substring(i, i + size), radix); + if (size < 8) { + var power = Timestamp.fromNumber(Math.pow(radix, size)); + result = result.multiply(power).add(Timestamp.fromNumber(value)); + } else { + result = result.multiply(radixToPower); + result = result.add(Timestamp.fromNumber(value)); + } + } + return result; +}; + +// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the +// from* methods on which they depend. + + +/** + * A cache of the Timestamp representations of small integer values. + * @type {Object} + * @api private + */ +Timestamp.INT_CACHE_ = {}; + +// NOTE: the compiler should inline these constant values below and then remove +// these variables, so there should be no runtime penalty for these. + +/** + * Number used repeated below in calculations. This must appear before the + * first call to any from* function below. + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_16_DBL_ = 1 << 16; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_24_DBL_ = 1 << 24; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_32_DBL_ = Timestamp.TWO_PWR_16_DBL_ * Timestamp.TWO_PWR_16_DBL_; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_31_DBL_ = Timestamp.TWO_PWR_32_DBL_ / 2; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_48_DBL_ = Timestamp.TWO_PWR_32_DBL_ * Timestamp.TWO_PWR_16_DBL_; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_64_DBL_ = Timestamp.TWO_PWR_32_DBL_ * Timestamp.TWO_PWR_32_DBL_; + +/** + * @type {number} + * @api private + */ +Timestamp.TWO_PWR_63_DBL_ = Timestamp.TWO_PWR_64_DBL_ / 2; + +/** @type {Timestamp} */ +Timestamp.ZERO = Timestamp.fromInt(0); + +/** @type {Timestamp} */ +Timestamp.ONE = Timestamp.fromInt(1); + +/** @type {Timestamp} */ +Timestamp.NEG_ONE = Timestamp.fromInt(-1); + +/** @type {Timestamp} */ +Timestamp.MAX_VALUE = + Timestamp.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0); + +/** @type {Timestamp} */ +Timestamp.MIN_VALUE = Timestamp.fromBits(0, 0x80000000 | 0); + +/** + * @type {Timestamp} + * @api private + */ +Timestamp.TWO_PWR_24_ = Timestamp.fromInt(1 << 24); + +/** + * Expose. + */ +exports.Timestamp = Timestamp; \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/package.json b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/package.json new file mode 100644 index 0000000..a6e8ad3 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/package.json @@ -0,0 +1,54 @@ +{ + "name": "bson", + "description": "A bson parser for node.js and the browser", + "keywords": [ + "mongodb", + "bson", + "parser" + ], + "version": "0.1.5", + "author": { + "name": "Christian Amor Kvalheim", + "email": "christkv@gmail.com" + }, + "contributors": [], + "repository": { + "type": "git", + "url": "git://github.com/mongodb/js-bson.git" + }, + "bugs": { + "mail": "node-mongodb-native@googlegroups.com", + "url": "https://github.com/mongodb/js-bson/issues" + }, + "devDependencies": { + "nodeunit": "0.7.3", + "gleak": "0.2.3" + }, + "config": { + "native": false + }, + "main": "./lib/bson/index", + "directories": { + "lib": "./lib/bson" + }, + "engines": { + "node": ">=0.6.0" + }, + "scripts": { + "install": "node install.js || (exit 0)", + "test": "nodeunit ./test/node && TEST_NATIVE=TRUE nodeunit ./test/node" + }, + "licenses": [ + { + "type": "Apache License, Version 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + ], + "readme": "A JS/C++ Bson parser for node, used in the MongoDB Native driver", + "readmeFilename": "README.md", + "_id": "bson@0.1.5", + "dist": { + "shasum": "3378c9788dcb3a7084d7c7768281bfc755f0ecc6" + }, + "_from": "bson@0.1.5" +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/bson_test.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/bson_test.js new file mode 100644 index 0000000..84d8ffc --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/bson_test.js @@ -0,0 +1,260 @@ +this.bson_test = { + 'Full document serialization and deserialization': function (test) { + var motherOfAllDocuments = { + 'string': "客家话", + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': new Date(), + 'oid': new ObjectID(), + 'binary': new Binary('hello world'), + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': Long.fromNumber(100), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), + 'minkey': new MinKey(), + 'maxkey': new MaxKey() + } + + // Let's serialize it + var data = BSON.serialize(motherOfAllDocuments, true, true, false); + // Deserialize the object + var object = BSON.deserialize(data); + + // Asserts + test.equal(Utf8.decode(motherOfAllDocuments.string), object.string); + test.deepEqual(motherOfAllDocuments.array, object.array); + test.deepEqual(motherOfAllDocuments.date, object.date); + test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); + test.ok(assertArrayEqual(motherOfAllDocuments.binary.value(true), object.binary.value(true))); + test.deepEqual(motherOfAllDocuments.int, object.int); + test.deepEqual(motherOfAllDocuments.float, object.float); + test.deepEqual(motherOfAllDocuments.regexp, object.regexp); + test.deepEqual(motherOfAllDocuments.boolean, object.boolean); + test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); + test.deepEqual(motherOfAllDocuments.where, object.where); + test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); + test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); + test.deepEqual(motherOfAllDocuments.minkey, object.minkey); + test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); + test.done(); + }, + + 'exercise all the binary object constructor methods': function (test) { + // Construct using array + var string = 'hello world'; + // String to array + var array = stringToArrayBuffer(string); + + // Binary from array buffer + var binary = new Binary(stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + test.ok(assertArrayEqual(array, binary.buffer)); + + // Construct using number of chars + binary = new Binary(5); + test.ok(5, binary.buffer.length); + + // Construct using an Array + var binary = new Binary(stringToArray(string)); + test.ok(string.length, binary.buffer.length); + test.ok(assertArrayEqual(array, binary.buffer)); + + // Construct using a string + var binary = new Binary(string); + test.ok(string.length, binary.buffer.length); + test.ok(assertArrayEqual(array, binary.buffer)); + test.done(); + }, + + 'exercise the put binary object method for an instance when using Uint8Array': function (test) { + // Construct using array + var string = 'hello world'; + // String to array + var array = stringToArrayBuffer(string + 'a'); + + // Binary from array buffer + var binary = new Binary(stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Write a byte to the array + binary.put('a') + + // Verify that the data was writtencorrectly + test.equal(string.length + 1, binary.position); + test.ok(assertArrayEqual(array, binary.value(true))); + test.equal('hello worlda', binary.value()); + + // Exercise a binary with lots of space in the buffer + var binary = new Binary(); + test.ok(Binary.BUFFER_SIZE, binary.buffer.length); + + // Write a byte to the array + binary.put('a') + + // Verify that the data was writtencorrectly + test.equal(1, binary.position); + test.ok(assertArrayEqual(['a'.charCodeAt(0)], binary.value(true))); + test.equal('a', binary.value()); + test.done(); + }, + + 'exercise the write binary object method for an instance when using Uint8Array': function (test) { + // Construct using array + var string = 'hello world'; + // Array + var writeArrayBuffer = new Uint8Array(new ArrayBuffer(1)); + writeArrayBuffer[0] = 'a'.charCodeAt(0); + var arrayBuffer = ['a'.charCodeAt(0)]; + + // Binary from array buffer + var binary = new Binary(stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Write a string starting at end of buffer + binary.write('a'); + test.equal('hello worlda', binary.value()); + // Write a string starting at index 0 + binary.write('a', 0); + test.equal('aello worlda', binary.value()); + // Write a arraybuffer starting at end of buffer + binary.write(writeArrayBuffer); + test.equal('aello worldaa', binary.value()); + // Write a arraybuffer starting at position 5 + binary.write(writeArrayBuffer, 5); + test.equal('aelloaworldaa', binary.value()); + // Write a array starting at end of buffer + binary.write(arrayBuffer); + test.equal('aelloaworldaaa', binary.value()); + // Write a array starting at position 6 + binary.write(arrayBuffer, 6); + test.equal('aelloaaorldaaa', binary.value()); + test.done(); + }, + + 'exercise the read binary object method for an instance when using Uint8Array': function (test) { + // Construct using array + var string = 'hello world'; + var array = stringToArrayBuffer(string); + + // Binary from array buffer + var binary = new Binary(stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Read the first 2 bytes + var data = binary.read(0, 2); + test.ok(assertArrayEqual(stringToArrayBuffer('he'), data)); + + // Read the entire field + var data = binary.read(0); + test.ok(assertArrayEqual(stringToArrayBuffer(string), data)); + + // Read 3 bytes + var data = binary.read(6, 5); + test.ok(assertArrayEqual(stringToArrayBuffer('world'), data)); + test.done(); + }, + + 'Should correctly handle toBson function for an object': function(test) { + // Test object + var doc = { + hello: new ObjectID(), + a:1 + }; + // Add a toBson method to the object + doc.toBSON = function() { + return {b:1}; + } + + // Serialize the data + var serialized_data = BSON.serialize(doc, false, true); + var deserialized_doc = BSON.deserialize(serialized_data); + test.equal(1, deserialized_doc.b); + test.done(); + } +}; + +var assertArrayEqual = function(array1, array2) { + if(array1.length != array2.length) return false; + for(var i = 0; i < array1.length; i++) { + if(array1[i] != array2[i]) return false; + } + + return true; +} + +// String to arraybuffer +var stringToArrayBuffer = function(string) { + var dataBuffer = new Uint8Array(new ArrayBuffer(string.length)); + // Return the strings + for(var i = 0; i < string.length; i++) { + dataBuffer[i] = string.charCodeAt(i); + } + // Return the data buffer + return dataBuffer; +} + +// String to arraybuffer +var stringToArray = function(string) { + var dataBuffer = new Array(string.length); + // Return the strings + for(var i = 0; i < string.length; i++) { + dataBuffer[i] = string.charCodeAt(i); + } + // Return the data buffer + return dataBuffer; +} + +var Utf8 = { + // public method for url encoding + encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // public method for url decoding + decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + c = utftext.charCodeAt(i); + if(c < 128) { + string += String.fromCharCode(c); + i++; + } else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + return string; + } +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/nodeunit.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/nodeunit.js new file mode 100644 index 0000000..af7fd0b --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/nodeunit.js @@ -0,0 +1,2034 @@ +/*! + * Nodeunit + * https://github.com/caolan/nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * json2.js + * http://www.JSON.org/json2.js + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ +nodeunit = (function(){ +/* + http://www.JSON.org/json2.js + 2010-11-17 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +var JSON = {}; + +(function () { + "use strict"; + + 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. + + 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) { + k = rep[i]; + if (typeof k === 'string') { + 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.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. + + if (typeof JSON.stringify !== 'function') { + JSON.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}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ +.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') +.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') +.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); +var assert = this.assert = {}; +var types = {}; +var core = {}; +var nodeunit = {}; +var reporter = {}; +/*global setTimeout: false, console: false */ +(function () { + + var async = {}; + + // global on the server, window in the browser + var root = this, + previous_async = root.async; + + if (typeof module !== 'undefined' && module.exports) { + module.exports = async; + } + else { + root.async = async; + } + + async.noConflict = function () { + root.async = previous_async; + return async; + }; + + //// cross-browser compatiblity functions //// + + var _forEach = function (arr, iterator) { + if (arr.forEach) { + return arr.forEach(iterator); + } + for (var i = 0; i < arr.length; i += 1) { + iterator(arr[i], i, arr); + } + }; + + var _map = function (arr, iterator) { + if (arr.map) { + return arr.map(iterator); + } + var results = []; + _forEach(arr, function (x, i, a) { + results.push(iterator(x, i, a)); + }); + return results; + }; + + var _reduce = function (arr, iterator, memo) { + if (arr.reduce) { + return arr.reduce(iterator, memo); + } + _forEach(arr, function (x, i, a) { + memo = iterator(memo, x, i, a); + }); + return memo; + }; + + var _keys = function (obj) { + if (Object.keys) { + return Object.keys(obj); + } + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + } + } + return keys; + }; + + var _indexOf = function (arr, item) { + if (arr.indexOf) { + return arr.indexOf(item); + } + for (var i = 0; i < arr.length; i += 1) { + if (arr[i] === item) { + return i; + } + } + return -1; + }; + + //// exported async module functions //// + + //// nextTick implementation with browser-compatible fallback //// + if (typeof process === 'undefined' || !(process.nextTick)) { + async.nextTick = function (fn) { + setTimeout(fn, 0); + }; + } + else { + async.nextTick = process.nextTick; + } + + async.forEach = function (arr, iterator, callback) { + if (!arr.length) { + return callback(); + } + var completed = 0; + _forEach(arr, function (x) { + iterator(x, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(); + } + } + }); + }); + }; + + async.forEachSeries = function (arr, iterator, callback) { + if (!arr.length) { + return callback(); + } + var completed = 0; + var iterate = function () { + iterator(arr[completed], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(); + } + else { + iterate(); + } + } + }); + }; + iterate(); + }; + + + var doParallel = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.forEach].concat(args)); + }; + }; + var doSeries = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.forEachSeries].concat(args)); + }; + }; + + + var _asyncMap = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (err, v) { + results[x.index] = v; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + }; + async.map = doParallel(_asyncMap); + async.mapSeries = doSeries(_asyncMap); + + + // reduce only has a series version, as doing reduce in parallel won't + // work in many situations. + async.reduce = function (arr, memo, iterator, callback) { + async.forEachSeries(arr, function (x, callback) { + iterator(memo, x, function (err, v) { + memo = v; + callback(err); + }); + }, function (err) { + callback(err, memo); + }); + }; + // inject alias + async.inject = async.reduce; + // foldl alias + async.foldl = async.reduce; + + async.reduceRight = function (arr, memo, iterator, callback) { + var reversed = _map(arr, function (x) { + return x; + }).reverse(); + async.reduce(reversed, memo, iterator, callback); + }; + // foldr alias + async.foldr = async.reduceRight; + + var _filter = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.filter = doParallel(_filter); + async.filterSeries = doSeries(_filter); + // select alias + async.select = async.filter; + async.selectSeries = async.filterSeries; + + var _reject = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (!v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.reject = doParallel(_reject); + async.rejectSeries = doSeries(_reject); + + var _detect = function (eachfn, arr, iterator, main_callback) { + eachfn(arr, function (x, callback) { + iterator(x, function (result) { + if (result) { + main_callback(x); + } + else { + callback(); + } + }); + }, function (err) { + main_callback(); + }); + }; + async.detect = doParallel(_detect); + async.detectSeries = doSeries(_detect); + + async.some = function (arr, iterator, main_callback) { + async.forEach(arr, function (x, callback) { + iterator(x, function (v) { + if (v) { + main_callback(true); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(false); + }); + }; + // any alias + async.any = async.some; + + async.every = function (arr, iterator, main_callback) { + async.forEach(arr, function (x, callback) { + iterator(x, function (v) { + if (!v) { + main_callback(false); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(true); + }); + }; + // all alias + async.all = async.every; + + 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 { + var fn = function (left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }; + callback(null, _map(results.sort(fn), function (x) { + return x.value; + })); + } + }); + }; + + async.auto = function (tasks, callback) { + callback = callback || function () {}; + var keys = _keys(tasks); + if (!keys.length) { + return callback(null); + } + + var completed = []; + + var listeners = []; + var addListener = function (fn) { + listeners.unshift(fn); + }; + var removeListener = function (fn) { + for (var i = 0; i < listeners.length; i += 1) { + if (listeners[i] === fn) { + listeners.splice(i, 1); + return; + } + } + }; + var taskComplete = function () { + _forEach(listeners, function (fn) { + fn(); + }); + }; + + addListener(function () { + if (completed.length === keys.length) { + callback(null); + } + }); + + _forEach(keys, function (k) { + var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k]; + var taskCallback = function (err) { + if (err) { + callback(err); + // stop subsequent errors hitting callback multiple times + callback = function () {}; + } + else { + completed.push(k); + taskComplete(); + } + }; + var requires = task.slice(0, Math.abs(task.length - 1)) || []; + var ready = function () { + return _reduce(requires, function (a, x) { + return (a && _indexOf(completed, x) !== -1); + }, true); + }; + if (ready()) { + task[task.length - 1](taskCallback); + } + else { + var listener = function () { + if (ready()) { + removeListener(listener); + task[task.length - 1](taskCallback); + } + }; + addListener(listener); + } + }); + }; + + async.waterfall = function (tasks, callback) { + if (!tasks.length) { + return callback(); + } + callback = callback || function () {}; + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } + else { + args.push(callback); + } + async.nextTick(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(async.iterator(tasks))(); + }; + + async.parallel = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.map(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args || null); + }); + } + }, callback); + } + else { + var results = {}; + async.forEach(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.series = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.mapSeries(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args || null); + }); + } + }, callback); + } + else { + var results = {}; + async.forEachSeries(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.iterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + 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 = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + return function () { + return fn.apply( + null, args.concat(Array.prototype.slice.call(arguments)) + ); + }; + }; + + var _concat = function (eachfn, arr, fn, callback) { + var r = []; + eachfn(arr, function (x, cb) { + fn(x, function (err, y) { + r = r.concat(y || []); + cb(err); + }); + }, function (err) { + callback(err, r); + }); + }; + async.concat = doParallel(_concat); + async.concatSeries = doSeries(_concat); + + async.whilst = function (test, iterator, callback) { + if (test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.whilst(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.until = function (test, iterator, callback) { + if (!test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.until(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.queue = function (worker, concurrency) { + var workers = 0; + var tasks = []; + var q = { + concurrency: concurrency, + push: function (data, callback) { + tasks.push({data: data, callback: callback}); + async.nextTick(q.process); + }, + process: function () { + if (workers < q.concurrency && tasks.length) { + var task = tasks.splice(0, 1)[0]; + workers += 1; + worker(task.data, function () { + workers -= 1; + if (task.callback) { + task.callback.apply(task, arguments); + } + q.process(); + }); + } + }, + length: function () { + return tasks.length; + } + }; + return q; + }; + + var _console_fn = function (name) { + return function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + fn.apply(null, args.concat([function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (typeof console !== 'undefined') { + if (err) { + if (console.error) { + console.error(err); + } + } + else if (console[name]) { + _forEach(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 = {}; + hasher = hasher || function (x) { + return x; + }; + return function () { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + var key = hasher.apply(null, args); + if (key in memo) { + callback.apply(null, memo[key]); + } + else { + fn.apply(null, args.concat([function () { + memo[key] = arguments; + callback.apply(null, arguments); + }])); + } + }; + }; + +}()); +(function(exports){ +/** + * This file is based on the node.js assert module, but with some small + * changes for browser-compatibility + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + */ + + +/** + * Added for browser compatibility + */ + +var _keys = function(obj){ + if(Object.keys) return Object.keys(obj); + if (typeof obj != 'object' && typeof obj != 'function') { + throw new TypeError('-'); + } + var keys = []; + for(var k in obj){ + if(obj.hasOwnProperty(k)) keys.push(k); + } + return keys; +}; + + + +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.com> +// +// 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 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. + + +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = exports; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({message: message, actual: actual, expected: expected}) + +assert.AssertionError = function AssertionError (options) { + this.name = "AssertionError"; + this.message = options.message; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } +}; +// code from util.inherits in node +assert.AssertionError.super_ = Error; + + +// EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call +// TODO: test what effect this may have +var ctor = function () { this.constructor = assert.AssertionError; }; +ctor.prototype = Error.prototype; +assert.AssertionError.prototype = new ctor(); + + +assert.AssertionError.prototype.toString = function() { + if (this.message) { + return [this.name+":", this.message].join(' '); + } else { + return [ this.name+":" + , JSON.stringify(this.expected ) + , this.operator + , JSON.stringify(this.actual) + ].join(" "); + } +}; + +// assert.AssertionError instanceof Error + +assert.AssertionError.__proto__ = Error.prototype; + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +assert.ok = function ok(value, message) { + if (!!!value) fail(value, true, message, "==", assert.ok); +}; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, "==", assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, "!=", assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, "deepEqual", assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == "object", + // equivalence is determined by ==. + } else if (typeof actual != 'object' && typeof expected != 'object') { + return actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical "prototype" property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isUndefinedOrNull (value) { + return value === null || value === undefined; +} + +function isArguments (object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv (a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical "prototype" property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try{ + var ka = _keys(a), + kb = _keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key] )) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, "===", assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as determined by !==. +// assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, "!==", assert.notStrictEqual); + } +}; + +function _throws (shouldThrow, block, err, message) { + var exception = null, + threw = false, + typematters = true; + + message = message || ""; + + //handle optional arguments + if (arguments.length == 3) { + if (typeof(err) == "string") { + message = err; + typematters = false; + } + } else if (arguments.length == 2) { + typematters = false; + } + + try { + block(); + } catch (e) { + threw = true; + exception = e; + } + + if (shouldThrow && !threw) { + fail( "Missing expected exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if (!shouldThrow && threw && typematters && exception instanceof err) { + fail( "Got unwanted exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if ((shouldThrow && threw && typematters && !(exception instanceof err)) || + (!shouldThrow && threw)) { + throw exception; + } +}; + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function (err) { if (err) {throw err;}}; +})(assert); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + +//var assert = require('./assert'), //@REMOVE_LINE_FOR_BROWSER +// async = require('../deps/async'); //@REMOVE_LINE_FOR_BROWSER + + +/** + * Creates assertion objects representing the result of an assert call. + * Accepts an object or AssertionError as its argument. + * + * @param {object} obj + * @api public + */ + +exports.assertion = function (obj) { + return { + method: obj.method || '', + message: obj.message || (obj.error && obj.error.message) || '', + error: obj.error, + passed: function () { + return !this.error; + }, + failed: function () { + return Boolean(this.error); + } + }; +}; + +/** + * Creates an assertion list object representing a group of assertions. + * Accepts an array of assertion objects. + * + * @param {Array} arr + * @param {Number} duration + * @api public + */ + +exports.assertionList = function (arr, duration) { + var that = arr || []; + that.failures = function () { + var failures = 0; + for (var i = 0; i < this.length; i += 1) { + if (this[i].failed()) { + failures += 1; + } + } + return failures; + }; + that.passes = function () { + return that.length - that.failures(); + }; + that.duration = duration || 0; + return that; +}; + +/** + * Create a wrapper function for assert module methods. Executes a callback + * after the it's complete with an assertion object representing the result. + * + * @param {Function} callback + * @api private + */ + +var assertWrapper = function (callback) { + return function (new_method, assert_method, arity) { + return function () { + var message = arguments[arity - 1]; + var a = exports.assertion({method: new_method, message: message}); + try { + assert[assert_method].apply(null, arguments); + } + catch (e) { + a.error = e; + } + callback(a); + }; + }; +}; + +/** + * Creates the 'test' object that gets passed to every test function. + * Accepts the name of the test function as its first argument, followed by + * the start time in ms, the options object and a callback function. + * + * @param {String} name + * @param {Number} start + * @param {Object} options + * @param {Function} callback + * @api public + */ + +exports.test = function (name, start, options, callback) { + var expecting; + var a_list = []; + + var wrapAssert = assertWrapper(function (a) { + a_list.push(a); + if (options.log) { + async.nextTick(function () { + options.log(a); + }); + } + }); + + var test = { + done: function (err) { + if (expecting !== undefined && expecting !== a_list.length) { + var e = new Error( + 'Expected ' + expecting + ' assertions, ' + + a_list.length + ' ran' + ); + var a1 = exports.assertion({method: 'expect', error: e}); + a_list.push(a1); + if (options.log) { + async.nextTick(function () { + options.log(a1); + }); + } + } + if (err) { + var a2 = exports.assertion({error: err}); + a_list.push(a2); + if (options.log) { + async.nextTick(function () { + options.log(a2); + }); + } + } + var end = new Date().getTime(); + async.nextTick(function () { + var assertion_list = exports.assertionList(a_list, end - start); + options.testDone(name, assertion_list); + callback(null, a_list); + }); + }, + ok: wrapAssert('ok', 'ok', 2), + same: wrapAssert('same', 'deepEqual', 3), + equals: wrapAssert('equals', 'equal', 3), + expect: function (num) { + expecting = num; + }, + _assertion_list: a_list + }; + // add all functions from the assert module + for (var k in assert) { + if (assert.hasOwnProperty(k)) { + test[k] = wrapAssert(k, k, assert[k].length); + } + } + return test; +}; + +/** + * Ensures an options object has all callbacks, adding empty callback functions + * if any are missing. + * + * @param {Object} opt + * @return {Object} + * @api public + */ + +exports.options = function (opt) { + var optionalCallback = function (name) { + opt[name] = opt[name] || function () {}; + }; + + optionalCallback('moduleStart'); + optionalCallback('moduleDone'); + optionalCallback('testStart'); + optionalCallback('testDone'); + //optionalCallback('log'); + + // 'done' callback is not optional. + + return opt; +}; +})(types); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + +//var async = require('../deps/async'), //@REMOVE_LINE_FOR_BROWSER +// types = require('./types'); //@REMOVE_LINE_FOR_BROWSER + + +/** + * Added for browser compatibility + */ + +var _keys = function (obj) { + if (Object.keys) { + return Object.keys(obj); + } + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + } + } + return keys; +}; + + +var _copy = function (obj) { + var nobj = {}; + var keys = _keys(obj); + for (var i = 0; i < keys.length; i += 1) { + nobj[keys[i]] = obj[keys[i]]; + } + return nobj; +}; + + +/** + * Runs a test function (fn) from a loaded module. After the test function + * calls test.done(), the callback is executed with an assertionList as its + * second argument. + * + * @param {String} name + * @param {Function} fn + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runTest = function (name, fn, opt, callback) { + var options = types.options(opt); + + options.testStart(name); + var start = new Date().getTime(); + var test = types.test(name, start, options, callback); + + try { + fn(test); + } + catch (e) { + test.done(e); + } +}; + +/** + * Takes an object containing test functions or other test suites as properties + * and runs each in series. After all tests have completed, the callback is + * called with a list of all assertions as the second argument. + * + * If a name is passed to this function it is prepended to all test and suite + * names that run within it. + * + * @param {String} name + * @param {Object} suite + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runSuite = function (name, suite, opt, callback) { + var keys = _keys(suite); + + async.concatSeries(keys, function (k, cb) { + var prop = suite[k], _name; + + _name = name ? [].concat(name, k) : [k]; + + _name.toString = function () { + // fallback for old one + return this.join(' - '); + }; + + if (typeof prop === 'function') { + var in_name = false; + for (var i = 0; i < _name.length; i += 1) { + if (_name[i] === opt.testspec) { + in_name = true; + } + } + if (!opt.testspec || in_name) { + if (opt.moduleStart) { + opt.moduleStart(); + } + exports.runTest(_name, suite[k], opt, cb); + } + else { + return cb(); + } + } + else { + exports.runSuite(_name, suite[k], opt, cb); + } + }, callback); +}; + +/** + * Run each exported test function or test suite from a loaded module. + * + * @param {String} name + * @param {Object} mod + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runModule = function (name, mod, opt, callback) { + var options = _copy(types.options(opt)); + + var _run = false; + var _moduleStart = options.moduleStart; + function run_once() { + if (!_run) { + _run = true; + _moduleStart(name); + } + } + options.moduleStart = run_once; + + var start = new Date().getTime(); + + exports.runSuite(null, mod, options, function (err, a_list) { + var end = new Date().getTime(); + var assertion_list = types.assertionList(a_list, end - start); + options.moduleDone(name, assertion_list); + callback(null, a_list); + }); +}; + +/** + * Treats an object literal as a list of modules keyed by name. Runs each + * module and finished with calling 'done'. You can think of this as a browser + * safe alternative to runFiles in the nodeunit module. + * + * @param {Object} modules + * @param {Object} opt + * @api public + */ + +// TODO: add proper unit tests for this function +exports.runModules = function (modules, opt) { + var all_assertions = []; + var options = types.options(opt); + var start = new Date().getTime(); + + async.concatSeries(_keys(modules), function (k, cb) { + exports.runModule(k, modules[k], options, cb); + }, + function (err, all_assertions) { + var end = new Date().getTime(); + options.done(types.assertionList(all_assertions, end - start)); + }); +}; + + +/** + * Wraps a test function with setUp and tearDown functions. + * Used by testCase. + * + * @param {Function} setUp + * @param {Function} tearDown + * @param {Function} fn + * @api private + */ + +var wrapTest = function (setUp, tearDown, fn) { + return function (test) { + var context = {}; + if (tearDown) { + var done = test.done; + test.done = function (err) { + try { + tearDown.call(context, function (err2) { + if (err && err2) { + test._assertion_list.push( + types.assertion({error: err}) + ); + return done(err2); + } + done(err || err2); + }); + } + catch (e) { + done(e); + } + }; + } + if (setUp) { + setUp.call(context, function (err) { + if (err) { + return test.done(err); + } + fn.call(context, test); + }); + } + else { + fn.call(context, test); + } + }; +}; + + +/** + * Wraps a group of tests with setUp and tearDown functions. + * Used by testCase. + * + * @param {Function} setUp + * @param {Function} tearDown + * @param {Object} group + * @api private + */ + +var wrapGroup = function (setUp, tearDown, group) { + var tests = {}; + var keys = _keys(group); + for (var i = 0; i < keys.length; i += 1) { + var k = keys[i]; + if (typeof group[k] === 'function') { + tests[k] = wrapTest(setUp, tearDown, group[k]); + } + else if (typeof group[k] === 'object') { + tests[k] = wrapGroup(setUp, tearDown, group[k]); + } + } + return tests; +}; + + +/** + * Utility for wrapping a suite of test functions with setUp and tearDown + * functions. + * + * @param {Object} suite + * @return {Object} + * @api public + */ + +exports.testCase = function (suite) { + var setUp = suite.setUp; + var tearDown = suite.tearDown; + delete suite.setUp; + delete suite.tearDown; + return wrapGroup(setUp, tearDown, suite); +}; +})(core); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + + +/** + * NOTE: this test runner is not listed in index.js because it cannot be + * used with the command-line tool, only inside the browser. + */ + + +/** + * Reporter info string + */ + +exports.info = "Browser-based test reporter"; + + +/** + * Run all tests within each module, reporting the results + * + * @param {Array} files + * @api public + */ + +exports.run = function (modules, options) { + var start = new Date().getTime(); + + function setText(el, txt) { + if ('innerText' in el) { + el.innerText = txt; + } + else if ('textContent' in el){ + el.textContent = txt; + } + } + + function getOrCreate(tag, id) { + var el = document.getElementById(id); + if (!el) { + el = document.createElement(tag); + el.id = id; + document.body.appendChild(el); + } + return el; + }; + + var header = getOrCreate('h1', 'nodeunit-header'); + var banner = getOrCreate('h2', 'nodeunit-banner'); + var userAgent = getOrCreate('h2', 'nodeunit-userAgent'); + var tests = getOrCreate('ol', 'nodeunit-tests'); + var result = getOrCreate('p', 'nodeunit-testresult'); + + setText(userAgent, navigator.userAgent); + + nodeunit.runModules(modules, { + moduleStart: function (name) { + /*var mheading = document.createElement('h2'); + mheading.innerText = name; + results.appendChild(mheading); + module = document.createElement('ol'); + results.appendChild(module);*/ + }, + testDone: function (name, assertions) { + var test = document.createElement('li'); + var strong = document.createElement('strong'); + strong.innerHTML = name + ' (' + + '' + assertions.failures() + ', ' + + '' + assertions.passes() + ', ' + + assertions.length + + ')'; + test.className = assertions.failures() ? 'fail': 'pass'; + test.appendChild(strong); + + var aList = document.createElement('ol'); + aList.style.display = 'none'; + test.onclick = function () { + var d = aList.style.display; + aList.style.display = (d == 'none') ? 'block': 'none'; + }; + for (var i=0; i' + (a.error.stack || a.error) + ''; + li.className = 'fail'; + } + else { + li.innerHTML = a.message || a.method || 'no message'; + li.className = 'pass'; + } + aList.appendChild(li); + } + test.appendChild(aList); + tests.appendChild(test); + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + + var failures = assertions.failures(); + banner.className = failures ? 'fail': 'pass'; + + result.innerHTML = 'Tests completed in ' + duration + + ' milliseconds.
    ' + + assertions.passes() + ' assertions of ' + + '' + assertions.length + ' passed, ' + + assertions.failures() + ' failed.'; + } + }); +}; +})(reporter); +nodeunit = core; +nodeunit.assert = assert; +nodeunit.reporter = reporter; +nodeunit.run = reporter.run; +return nodeunit; })(); diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/suite2.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/suite2.js new file mode 100644 index 0000000..c7288e8 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/suite2.js @@ -0,0 +1,13 @@ +this.suite2 = { + 'another test': function (test) { + setTimeout(function () { + // lots of assertions + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.done(); + }, 10); + } +}; diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/suite3.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/suite3.js new file mode 100644 index 0000000..8929741 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/suite3.js @@ -0,0 +1,7 @@ +this.suite3 = { + 'test for ie6,7,8': function (test) { + test.deepEqual(["test"], ["test"]); + test.notDeepEqual(["a"], ["b"]); + test.done(); + } +}; diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/test.html b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/test.html new file mode 100644 index 0000000..56d4d96 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/browser/test.html @@ -0,0 +1,30 @@ + + + Example tests + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_array_test.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_array_test.js new file mode 100644 index 0000000..5304bef --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_array_test.js @@ -0,0 +1,240 @@ +var mongodb = require('../../lib/bson').pure(); + +var testCase = require('nodeunit').testCase, + mongoO = require('../../lib/bson').pure(), + debug = require('util').debug, + inspect = require('util').inspect, + Buffer = require('buffer').Buffer, + gleak = require('../../tools/gleak'), + fs = require('fs'), + BSON = mongoO.BSON, + Code = mongoO.Code, + Binary = mongoO.Binary, + Timestamp = mongoO.Timestamp, + Long = mongoO.Long, + MongoReply = mongoO.MongoReply, + ObjectID = mongoO.ObjectID, + Symbol = mongoO.Symbol, + DBRef = mongoO.DBRef, + Double = mongoO.Double, + MinKey = mongoO.MinKey, + MaxKey = mongoO.MaxKey, + BinaryParser = mongoO.BinaryParser, + utils = require('./tools/utils'); + +var BSONSE = mongodb, + BSONDE = mongodb; + +// for tests +BSONDE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONDE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONDE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONDE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONDE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONDE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +BSONSE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONSE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONSE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONSE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONSE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONSE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +var hexStringToBinary = function(string) { + var numberofValues = string.length / 2; + var array = ""; + + for(var i = 0; i < numberofValues; i++) { + array += String.fromCharCode(parseInt(string[i*2] + string[i*2 + 1], 16)); + } + return array; +} + +var assertBuffersEqual = function(test, buffer1, buffer2) { + if(buffer1.length != buffer2.length) test.fail("Buffers do not have the same length", buffer1, buffer2); + + for(var i = 0; i < buffer1.length; i++) { + test.equal(buffer1[i], buffer2[i]); + } +} + +/** + * Module for parsing an ISO 8601 formatted string into a Date object. + */ +var ISODate = function (string) { + var match; + + if (typeof string.getTime === "function") + return string; + else if (match = string.match(/^(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|((\+|-)(\d{2}):(\d{2}))))?)?)?$/)) { + var date = new Date(); + date.setUTCFullYear(Number(match[1])); + date.setUTCMonth(Number(match[3]) - 1 || 0); + date.setUTCDate(Number(match[5]) || 0); + date.setUTCHours(Number(match[7]) || 0); + date.setUTCMinutes(Number(match[8]) || 0); + date.setUTCSeconds(Number(match[10]) || 0); + date.setUTCMilliseconds(Number("." + match[12]) * 1000 || 0); + + if (match[13] && match[13] !== "Z") { + var h = Number(match[16]) || 0, + m = Number(match[17]) || 0; + + h *= 3600000; + m *= 60000; + + var offset = h + m; + if (match[15] == "+") + offset = -offset; + + date = new Date(date.valueOf() + offset); + } + + return date; + } else + throw new Error("Invalid ISO 8601 date given.", __filename); +}; + +var _Uint8Array = null; + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + _Uint8Array = global.Uint8Array; + delete global['Uint8Array']; + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + global['Uint8Array'] = _Uint8Array; + callback(); +} + +// /** +// * @ignore +// */ +// exports.shouldCorrectlyDeserializeUsingTypedArray = function(test) { +// var motherOfAllDocuments = { +// 'string': '客家话', +// 'array': [1,2,3], +// 'hash': {'a':1, 'b':2}, +// 'date': new Date(), +// 'oid': new ObjectID(), +// 'binary': new Binary(new Buffer("hello")), +// 'int': 42, +// 'float': 33.3333, +// 'regexp': /regexp/, +// 'boolean': true, +// 'long': Long.fromNumber(100), +// 'where': new Code('this.a > i', {i:1}), +// 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), +// 'minkey': new MinKey(), +// 'maxkey': new MaxKey() +// } +// +// // Let's serialize it +// var data = BSONSE.BSON.serialize(motherOfAllDocuments, true, true, false); +// // Build a typed array +// var arr = new Uint8Array(new ArrayBuffer(data.length)); +// // Iterate over all the fields and copy +// for(var i = 0; i < data.length; i++) { +// arr[i] = data[i] +// } +// +// // Deserialize the object +// var object = BSONDE.BSON.deserialize(arr); +// // Asserts +// test.equal(motherOfAllDocuments.string, object.string); +// test.deepEqual(motherOfAllDocuments.array, object.array); +// test.deepEqual(motherOfAllDocuments.date, object.date); +// test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); +// test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); +// // Assert the values of the binary +// for(var i = 0; i < motherOfAllDocuments.binary.length(); i++) { +// test.equal(motherOfAllDocuments.binary.value[i], object.binary[i]); +// } +// test.deepEqual(motherOfAllDocuments.int, object.int); +// test.deepEqual(motherOfAllDocuments.float, object.float); +// test.deepEqual(motherOfAllDocuments.regexp, object.regexp); +// test.deepEqual(motherOfAllDocuments.boolean, object.boolean); +// test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); +// test.deepEqual(motherOfAllDocuments.where, object.where); +// test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); +// test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); +// test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); +// test.deepEqual(motherOfAllDocuments.minkey, object.minkey); +// test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); +// test.done(); +// } + +/** + * @ignore + */ +exports.shouldCorrectlySerializeUsingTypedArray = function(test) { + var motherOfAllDocuments = { + 'string': 'hello', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': new Date(), + 'oid': new ObjectID(), + 'binary': new Binary(new Buffer("hello")), + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': Long.fromNumber(100), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), + 'minkey': new MinKey(), + 'maxkey': new MaxKey() + } + + // Let's serialize it + var data = BSONSE.BSON.serialize(motherOfAllDocuments, true, false, false); + // And deserialize it again + var object = BSONSE.BSON.deserialize(data); + // Asserts + test.equal(motherOfAllDocuments.string, object.string); + test.deepEqual(motherOfAllDocuments.array, object.array); + test.deepEqual(motherOfAllDocuments.date, object.date); + test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); + // Assert the values of the binary + for(var i = 0; i < motherOfAllDocuments.binary.length(); i++) { + test.equal(motherOfAllDocuments.binary.value[i], object.binary[i]); + } + test.deepEqual(motherOfAllDocuments.int, object.int); + test.deepEqual(motherOfAllDocuments.float, object.float); + test.deepEqual(motherOfAllDocuments.regexp, object.regexp); + test.deepEqual(motherOfAllDocuments.boolean, object.boolean); + test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); + test.deepEqual(motherOfAllDocuments.where, object.where); + test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); + test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); + test.deepEqual(motherOfAllDocuments.minkey, object.minkey); + test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); + test.done(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_parser_comparision_test.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_parser_comparision_test.js new file mode 100644 index 0000000..80805a5 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_parser_comparision_test.js @@ -0,0 +1,482 @@ +var sys = require('util'), + debug = require('util').debug, + inspect = require('util').inspect, + Buffer = require('buffer').Buffer, + BSON = require('../../ext').BSON, + Buffer = require('buffer').Buffer, + BSONJS = require('../../lib/bson/bson').BSON, + BinaryParser = require('../../lib/bson/binary_parser').BinaryParser, + Long = require('../../lib/bson/long').Long, + ObjectID = require('../../lib/bson/bson').ObjectID, + Binary = require('../../lib/bson/bson').Binary, + Code = require('../../lib/bson/bson').Code, + DBRef = require('../../lib/bson/bson').DBRef, + Symbol = require('../../lib/bson/bson').Symbol, + Double = require('../../lib/bson/bson').Double, + MaxKey = require('../../lib/bson/bson').MaxKey, + MinKey = require('../../lib/bson/bson').MinKey, + Timestamp = require('../../lib/bson/bson').Timestamp, + gleak = require('../../tools/gleak'), + assert = require('assert'); + +// Parsers +var bsonC = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +var bsonJS = new BSONJS([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize simple edge value'] = function(test) { + // Simple serialization and deserialization of edge value + var doc = {doc:0x1ffffffffffffe}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:-0x1ffffffffffffe}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly execute toJSON'] = function(test) { + var a = Long.fromNumber(10); + assert.equal(10, a); + + var a = Long.fromNumber(9223372036854775807); + assert.equal(9223372036854775807, a); + + // Simple serialization and deserialization test for a Single String value + var doc = {doc:'Serialize'}; + var simple_string_serialized = bsonC.serialize(doc, true, false); + + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Serialize and Deserialize nested document'] = function(test) { + // Nested doc + var doc = {a:{b:{c:1}}}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple integer serialization/deserialization test, including testing boundary conditions'] = function(test) { + var doc = {doc:-1}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:2147483648}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:-2147483648}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization test for a Long value'] = function(test) { + var doc = {doc:Long.fromNumber(9223372036854775807)}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:Long.fromNumber(9223372036854775807)}, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:Long.fromNumber(-9223372036854775807)}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:Long.fromNumber(-9223372036854775807)}, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a Float value'] = function(test) { + var doc = {doc:2222.3333}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + + var doc = {doc:-2222.3333}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a null value'] = function(test) { + var doc = {doc:null}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a boolean value'] = function(test) { + var doc = {doc:true}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a date value'] = function(test) { + var date = new Date(); + var doc = {doc:date}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')), bsonC.deserialize(simple_string_serialized)); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a boolean value'] = function(test) { + var doc = {doc:/abcd/mi}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.equal(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + + var doc = {doc:/abcd/}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc, false, true)); + assert.equal(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a objectId value'] = function(test) { + var doc = {doc:new ObjectID()}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + var doc2 = {doc:ObjectID.createFromHexString(doc.doc.toHexString())}; + + assert.deepEqual(simple_string_serialized, bsonJS.serialize(doc2, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), bsonC.deserialize(simple_string_serialized).doc.toString()); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a Binary value'] = function(test) { + var binary = new Binary(); + var string = 'binstring' + for(var index = 0; index < string.length; index++) { binary.put(string.charAt(index)); } + + var simple_string_serialized = bsonC.serialize({doc:binary}, false, true); + assert.deepEqual(simple_string_serialized, bsonJS.serialize({doc:binary}, false, true)); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.value(), bsonC.deserialize(simple_string_serialized).doc.value()); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a Code value'] = function(test) { + var code = new Code('this.a > i', {'i': 1}); + var simple_string_serialized_2 = bsonJS.serialize({doc:code}, false, true); + var simple_string_serialized = bsonC.serialize({doc:code}, false, true); + + assert.deepEqual(simple_string_serialized, simple_string_serialized_2); + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc.scope, bsonC.deserialize(simple_string_serialized).doc.scope); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for an Object'] = function(test) { + var simple_string_serialized = bsonC.serialize({doc:{a:1, b:{c:2}}}, false, true); + var simple_string_serialized_2 = bsonJS.serialize({doc:{a:1, b:{c:2}}}, false, true); + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc, bsonC.deserialize(simple_string_serialized).doc); + + // Simple serialization and deserialization for an Array + var simple_string_serialized = bsonC.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]}, false, true); + var simple_string_serialized_2 = bsonJS.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]}, false, true); + + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + assert.deepEqual(bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc, bsonC.deserialize(simple_string_serialized).doc); + test.done(); +} + +/** + * @ignore + */ +exports['Simple serialization and deserialization for a DBRef'] = function(test) { + var oid = new ObjectID() + var oid2 = new ObjectID.createFromHexString(oid.toHexString()) + var simple_string_serialized = bsonJS.serialize({doc:new DBRef('namespace', oid2, 'integration_tests_')}, false, true); + var simple_string_serialized_2 = bsonC.serialize({doc:new DBRef('namespace', oid, 'integration_tests_')}, false, true); + + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + // Ensure we have the same values for the dbref + var object_js = bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')); + var object_c = bsonC.deserialize(simple_string_serialized); + + assert.equal(object_js.doc.namespace, object_c.doc.namespace); + assert.equal(object_js.doc.oid.toHexString(), object_c.doc.oid.toHexString()); + assert.equal(object_js.doc.db, object_c.doc.db); + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly deserialize bytes array'] = function(test) { + // Serialized document + var bytes = [47,0,0,0,2,110,97,109,101,0,6,0,0,0,80,97,116,116,121,0,16,97,103,101,0,34,0,0,0,7,95,105,100,0,76,100,12,23,11,30,39,8,89,0,0,1,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + var object = bsonC.deserialize(new Buffer(serialized_data, 'binary')); + assert.equal('Patty', object.name) + assert.equal(34, object.age) + assert.equal('4c640c170b1e270859000001', object._id.toHexString()) + test.done(); +} + +/** + * @ignore + */ +exports['Serialize utf8'] = function(test) { + var doc = { "name" : "本荘由利地域に洪水警報", "name1" : "öüóőúéáűíÖÜÓŐÚÉÁŰÍ", "name2" : "abcdedede"}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + var simple_string_serialized2 = bsonJS.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, simple_string_serialized2) + + var object = bsonC.deserialize(simple_string_serialized); + assert.equal(doc.name, object.name) + assert.equal(doc.name1, object.name1) + assert.equal(doc.name2, object.name2) + test.done(); +} + +/** + * @ignore + */ +exports['Serialize object with array'] = function(test) { + var doc = {b:[1, 2, 3]}; + var simple_string_serialized = bsonC.serialize(doc, false, true); + var simple_string_serialized_2 = bsonJS.serialize(doc, false, true); + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + + var object = bsonC.deserialize(simple_string_serialized); + assert.deepEqual(doc, object) + test.done(); +} + +/** + * @ignore + */ +exports['Test equality of an object ID'] = function(test) { + var object_id = new ObjectID(); + var object_id_2 = new ObjectID(); + assert.ok(object_id.equals(object_id)); + assert.ok(!(object_id.equals(object_id_2))) + test.done(); +} + +/** + * @ignore + */ +exports['Test same serialization for Object ID'] = function(test) { + var object_id = new ObjectID(); + var object_id2 = ObjectID.createFromHexString(object_id.toString()) + var simple_string_serialized = bsonJS.serialize({doc:object_id}, false, true); + var simple_string_serialized_2 = bsonC.serialize({doc:object_id2}, false, true); + + assert.equal(simple_string_serialized_2.length, simple_string_serialized.length); + assert.deepEqual(simple_string_serialized, simple_string_serialized_2) + var object = bsonJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')); + var object2 = bsonC.deserialize(simple_string_serialized); + assert.equal(object.doc.id, object2.doc.id) + test.done(); +} + +/** + * @ignore + */ +exports['Complex object serialization'] = function(test) { + // JS Object + var c1 = { _id: new ObjectID, comments: [], title: 'number 1' }; + var c2 = { _id: new ObjectID, comments: [], title: 'number 2' }; + var doc = { + numbers: [] + , owners: [] + , comments: [c1, c2] + , _id: new ObjectID + }; + + var simple_string_serialized = bsonJS.serialize(doc, false, true); + + // C++ Object + var c1 = { _id: ObjectID.createFromHexString(c1._id.toHexString()), comments: [], title: 'number 1' }; + var c2 = { _id: ObjectID.createFromHexString(c2._id.toHexString()), comments: [], title: 'number 2' }; + var doc = { + numbers: [] + , owners: [] + , comments: [c1, c2] + , _id: ObjectID.createFromHexString(doc._id.toHexString()) + }; + + var simple_string_serialized_2 = bsonC.serialize(doc, false, true); + + for(var i = 0; i < simple_string_serialized_2.length; i++) { + // debug(i + "[" + simple_string_serialized_2[i] + "] = [" + simple_string_serialized[i] + "]") + assert.equal(simple_string_serialized_2[i], simple_string_serialized[i]); + } + + var doc1 = bsonJS.deserialize(new Buffer(simple_string_serialized_2)); + var doc2 = bsonC.deserialize(new Buffer(simple_string_serialized_2)); + assert.equal(doc._id.id, doc1._id.id) + assert.equal(doc._id.id, doc2._id.id) + assert.equal(doc1._id.id, doc2._id.id) + + var doc = { + _id: 'testid', + key1: { code: 'test1', time: {start:1309323402727,end:1309323402727}, x:10, y:5 }, + key2: { code: 'test1', time: {start:1309323402727,end:1309323402727}, x:10, y:5 } + }; + + var simple_string_serialized = bsonJS.serialize(doc, false, true); + var simple_string_serialized_2 = bsonC.serialize(doc, false, true); + test.done(); +} + +/** + * @ignore + */ +exports['Serialize function'] = function(test) { + var doc = { + _id: 'testid', + key1: function() {} + } + + var simple_string_serialized = bsonJS.serialize(doc, false, true, true); + var simple_string_serialized_2 = bsonC.serialize(doc, false, true, true); + + // Deserialize the string + var doc1 = bsonJS.deserialize(new Buffer(simple_string_serialized_2)); + var doc2 = bsonC.deserialize(new Buffer(simple_string_serialized_2)); + assert.equal(doc1.key1.code.toString(), doc2.key1.code.toString()) + test.done(); +} + +/** + * @ignore + */ +exports['Serialize document with special operators'] = function(test) { + var doc = {"user_id":"4e9fc8d55883d90100000003","lc_status":{"$ne":"deleted"},"owner_rating":{"$exists":false}}; + var simple_string_serialized = bsonJS.serialize(doc, false, true, true); + var simple_string_serialized_2 = bsonC.serialize(doc, false, true, true); + + // Should serialize to the same value + assert.equal(simple_string_serialized_2.toString('base64'), simple_string_serialized.toString('base64')) + var doc1 = bsonJS.deserialize(simple_string_serialized_2); + var doc2 = bsonC.deserialize(simple_string_serialized); + assert.deepEqual(doc1, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Create ObjectID from hex string'] = function(test) { + // Hex Id + var hexId = new ObjectID().toString(); + var docJS = {_id: ObjectID.createFromHexString(hexId), 'funds.remaining': {$gte: 1.222}, 'transactions.id': {$ne: ObjectID.createFromHexString(hexId)}}; + var docC = {_id: ObjectID.createFromHexString(hexId), 'funds.remaining': {$gte: 1.222}, 'transactions.id': {$ne: ObjectID.createFromHexString(hexId)}}; + var docJSBin = bsonJS.serialize(docJS, false, true, true); + var docCBin = bsonC.serialize(docC, false, true, true); + assert.equal(docCBin.toString('base64'), docJSBin.toString('base64')); + test.done(); +} + +/** + * @ignore + */ +exports['Serialize big complex document'] = function(test) { + // Complex document serialization + var doc = {"DateTime": "Tue Nov 40 2011 17:27:55 GMT+0000 (WEST)","isActive": true,"Media": {"URL": "http://videos.sapo.pt/Tc85NsjaKjj8o5aV7Ubb"},"Title": "Lisboa fecha a ganhar 0.19%","SetPosition": 60,"Type": "videos","Thumbnail": [{"URL": "http://rd3.videos.sapo.pt/Tc85NsjaKjj8o5aV7Ubb/pic/320x240","Dimensions": {"Height": 240,"Width": 320}}],"Source": {"URL": "http://videos.sapo.pt","SetID": "1288","SourceID": "http://videos.sapo.pt/tvnet/rss2","SetURL": "http://noticias.sapo.pt/videos/tv-net_1288/","ItemID": "Tc85NsjaKjj8o5aV7Ubb","Name": "SAPO Vídeos"},"Category": "Tec_ciencia","Description": "Lisboa fecha a ganhar 0.19%","GalleryID": new ObjectID("4eea2a634ce8573200000000"),"InternalRefs": {"RegisterDate": "Thu Dec 15 2011 17:12:51 GMT+0000 (WEST)","ChangeDate": "Thu Dec 15 2011 17:12:51 GMT+0000 (WEST)","Hash": 332279244514},"_id": new ObjectID("4eea2a96e52778160000003a")} + var docJSBin = bsonJS.serialize(doc, false, true, true); + var docCBin = bsonC.serialize(doc, false, true, true); + assert.equal(docCBin.toString('base64'), docJSBin.toString('base64')); + test.done(); +} + +/** + * @ignore + */ +exports['Should error out due to 24 characters but not valid hexstring for ObjectID'] = function(test) { + try { + var oid = new ObjectID("tttttttttttttttttttttttt"); + test.ok(false); + } catch(err) {} + + test.done(); +} + + +/** + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_test.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_test.js new file mode 100644 index 0000000..a2a723b --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_test.js @@ -0,0 +1,1671 @@ +var mongodb = process.env['TEST_NATIVE'] != null ? require('../../lib/bson').native() : require('../../lib/bson').pure(); + +var testCase = require('nodeunit').testCase, + mongoO = require('../../lib/bson').pure(), + Buffer = require('buffer').Buffer, + gleak = require('../../tools/gleak'), + fs = require('fs'), + BSON = mongoO.BSON, + Code = mongoO.Code, + Binary = mongoO.Binary, + Timestamp = mongoO.Timestamp, + Long = mongoO.Long, + MongoReply = mongoO.MongoReply, + ObjectID = mongoO.ObjectID, + ObjectId = mongoO.ObjectId, + Symbol = mongoO.Symbol, + DBRef = mongoO.DBRef, + Double = mongoO.Double, + MinKey = mongoO.MinKey, + MaxKey = mongoO.MaxKey, + BinaryParser = mongoO.BinaryParser, + vm = require('vm'); + +var BSONSE = mongodb, + BSONDE = mongodb; + +// for tests +BSONDE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONDE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONDE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONDE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONDE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONDE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +BSONSE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONSE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONSE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONSE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONSE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONSE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +var hexStringToBinary = function(string) { + var numberofValues = string.length / 2; + var array = ""; + + for(var i = 0; i < numberofValues; i++) { + array += String.fromCharCode(parseInt(string[i*2] + string[i*2 + 1], 16)); + } + return array; +} + +var assertBuffersEqual = function(test, buffer1, buffer2) { + if(buffer1.length != buffer2.length) test.fail("Buffers do not have the same length", buffer1, buffer2); + + for(var i = 0; i < buffer1.length; i++) { + test.equal(buffer1[i], buffer2[i]); + } +} + +/** + * Module for parsing an ISO 8601 formatted string into a Date object. + */ +var ISODate = function (string) { + var match; + + if (typeof string.getTime === "function") + return string; + else if (match = string.match(/^(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|((\+|-)(\d{2}):(\d{2}))))?)?)?$/)) { + var date = new Date(); + date.setUTCFullYear(Number(match[1])); + date.setUTCMonth(Number(match[3]) - 1 || 0); + date.setUTCDate(Number(match[5]) || 0); + date.setUTCHours(Number(match[7]) || 0); + date.setUTCMinutes(Number(match[8]) || 0); + date.setUTCSeconds(Number(match[10]) || 0); + date.setUTCMilliseconds(Number("." + match[12]) * 1000 || 0); + + if (match[13] && match[13] !== "Z") { + var h = Number(match[16]) || 0, + m = Number(match[17]) || 0; + + h *= 3600000; + m *= 60000; + + var offset = h + m; + if (match[15] == "+") + offset = -offset; + + date = new Date(date.valueOf() + offset); + } + + return date; + } else + throw new Error("Invalid ISO 8601 date given.", __filename); +}; + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports['Should Correctly create ObjectID and do deep equals'] = function(test) { + var test_string = {hello: new ObjectID()}; + test_string.hello.toHexString(); + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_string, false, true); + test.deepEqual(test_string, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly get BSON types from require'] = function(test) { + var _mongodb = require('../../lib/bson'); + test.ok(_mongodb.ObjectID === ObjectID); + test.ok(_mongodb.Binary === Binary); + test.ok(_mongodb.Long === Long); + test.ok(_mongodb.Timestamp === Timestamp); + test.ok(_mongodb.Code === Code); + test.ok(_mongodb.DBRef === DBRef); + test.ok(_mongodb.Symbol === Symbol); + test.ok(_mongodb.MinKey === MinKey); + test.ok(_mongodb.MaxKey === MaxKey); + test.ok(_mongodb.Double === Double); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Deserialize object'] = function(test) { + var bytes = [95,0,0,0,2,110,115,0,42,0,0,0,105,110,116,101,103,114,97,116,105,111,110,95,116,101,115,116,115,95,46,116,101,115,116,95,105,110,100,101,120,95,105,110,102,111,114,109,97,116,105,111,110,0,8,117,110,105,113,117,101,0,0,3,107,101,121,0,12,0,0,0,16,97,0,1,0,0,0,0,2,110,97,109,101,0,4,0,0,0,97,95,49,0,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + + var object = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(new Buffer(serialized_data, 'binary')); + test.equal("a_1", object.name); + test.equal(false, object.unique); + test.equal(1, object.key.a); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Deserialize object with all types'] = function(test) { + var bytes = [26,1,0,0,7,95,105,100,0,161,190,98,75,118,169,3,0,0,3,0,0,4,97,114,114,97,121,0,26,0,0,0,16,48,0,1,0,0,0,16,49,0,2,0,0,0,16,50,0,3,0,0,0,0,2,115,116,114,105,110,103,0,6,0,0,0,104,101,108,108,111,0,3,104,97,115,104,0,19,0,0,0,16,97,0,1,0,0,0,16,98,0,2,0,0,0,0,9,100,97,116,101,0,161,190,98,75,0,0,0,0,7,111,105,100,0,161,190,98,75,90,217,18,0,0,1,0,0,5,98,105,110,97,114,121,0,7,0,0,0,2,3,0,0,0,49,50,51,16,105,110,116,0,42,0,0,0,1,102,108,111,97,116,0,223,224,11,147,169,170,64,64,11,114,101,103,101,120,112,0,102,111,111,98,97,114,0,105,0,8,98,111,111,108,101,97,110,0,1,15,119,104,101,114,101,0,25,0,0,0,12,0,0,0,116,104,105,115,46,120,32,61,61,32,51,0,5,0,0,0,0,3,100,98,114,101,102,0,37,0,0,0,2,36,114,101,102,0,5,0,0,0,116,101,115,116,0,7,36,105,100,0,161,190,98,75,2,180,1,0,0,2,0,0,0,10,110,117,108,108,0,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + + var object = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(new Buffer(serialized_data, 'binary'));//, false, true); + // Perform tests + test.equal("hello", object.string); + test.deepEqual([1,2,3], object.array); + test.equal(1, object.hash.a); + test.equal(2, object.hash.b); + test.ok(object.date != null); + test.ok(object.oid != null); + test.ok(object.binary != null); + test.equal(42, object.int); + test.equal(33.3333, object.float); + test.ok(object.regexp != null); + test.equal(true, object.boolean); + test.ok(object.where != null); + test.ok(object.dbref != null); + test.ok(object[null] == null); + test.done(); +} + +/** + * @ignore + */ +exports['Should Serialize and Deserialize String'] = function(test) { + var test_string = {hello: 'world'}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_string, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_string)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_string, false, serialized_data2, 0); + + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_string, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Serialize and Deserialize Empty String'] = function(test) { + var test_string = {hello: ''}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_string, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_string)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_string, false, serialized_data2, 0); + + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_string, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Integer'] = function(test) { + var test_number = {doc: 5}; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_number, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_number)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_number, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_number, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.deepEqual(test_number, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data2)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize null value'] = function(test) { + var test_null = {doc:null}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_null, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_null)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_null, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var object = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal(null, object.doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Number'] = function(test) { + var test_number = {doc: 5.5}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_number, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_number)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_number, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(test_number, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Integer'] = function(test) { + var test_int = {doc: 42}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_int.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + + test_int = {doc: -5600}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_int.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + + test_int = {doc: 2147483647}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_int.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + + test_int = {doc: -2147483648}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + test.deepEqual(test_int.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Object'] = function(test) { + var doc = {doc: {age: 42, name: 'Spongebob', shoe_size: 9.5}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(doc.doc.age, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.age); + test.deepEqual(doc.doc.name, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.name); + test.deepEqual(doc.doc.shoe_size, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.shoe_size); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Array'] = function(test) { + var doc = {doc: [1, 2, 'a', 'b']}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal(doc.doc[0], deserialized.doc[0]) + test.equal(doc.doc[1], deserialized.doc[1]) + test.equal(doc.doc[2], deserialized.doc[2]) + test.equal(doc.doc[3], deserialized.doc[3]) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Array with added on functions'] = function(test) { + Array.prototype.toXml = function() {}; + var doc = {doc: [1, 2, 'a', 'b']}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal(doc.doc[0], deserialized.doc[0]) + test.equal(doc.doc[1], deserialized.doc[1]) + test.equal(doc.doc[2], deserialized.doc[2]) + test.equal(doc.doc[3], deserialized.doc[3]) + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly deserialize a nested object'] = function(test) { + var doc = {doc: {doc:1}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(doc.doc.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize A Boolean'] = function(test) { + var doc = {doc: true}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.equal(doc.doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Date'] = function(test) { + var date = new Date(); + //(2009, 11, 12, 12, 00, 30) + date.setUTCDate(12); + date.setUTCFullYear(2009); + date.setUTCMonth(11 - 1); + date.setUTCHours(12); + date.setUTCMinutes(0); + date.setUTCSeconds(30); + var doc = {doc: date}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.equal(doc.date, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.date); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Date from another VM'] = function(test) { + var script = "date1 = new Date();", + ctx = vm.createContext({ + date1 : null + }); + vm.runInContext(script, ctx, 'myfile.vm'); + + var date = ctx.date1; + //(2009, 11, 12, 12, 00, 30) + date.setUTCDate(12); + date.setUTCFullYear(2009); + date.setUTCMonth(11 - 1); + date.setUTCHours(12); + date.setUTCMinutes(0); + date.setUTCSeconds(30); + var doc = {doc: date}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.equal(doc.date, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc.date); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize nested doc'] = function(test) { + var doc = { + string: "Strings are great", + decimal: 3.14159265, + bool: true, + integer: 5, + + subObject: { + moreText: "Bacon ipsum dolor.", + longKeylongKeylongKeylongKeylongKeylongKey: "Pork belly." + }, + + subArray: [1,2,3,4,5,6,7,8,9,10], + anotherString: "another string" + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Oid'] = function(test) { + var doc = {doc: new ObjectID()}; + var doc2 = {doc: ObjectID.createFromHexString(doc.doc.toHexString())}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly encode Empty Hash'] = function(test) { + var doc = {}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + test.deepEqual(doc, new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Ordered Hash'] = function(test) { + var doc = {doc: {b:1, a:2, c:3, d:4}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var decoded_hash = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data).doc; + var keys = []; + + for(var name in decoded_hash) keys.push(name); + test.deepEqual(['b', 'a', 'c', 'd'], keys); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Regular Expression'] = function(test) { + // Serialize the regular expression + var doc = {doc: /foobar/mi}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc.doc.toString(), doc2.doc.toString()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Binary object'] = function(test) { + var bin = new Binary(); + var string = 'binstring'; + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)); + } + + var doc = {doc: bin}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc.doc.value(), deserialized_data.doc.value()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a big Binary object'] = function(test) { + var data = fs.readFileSync("test/node/data/test_gs_weird_bug.png", 'binary'); + var bin = new Binary(); + bin.write(data); + var doc = {doc: bin}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc.value(), deserialized_data.doc.value()); + test.done(); +} + +/** + * @ignore + */ +exports["Should Correctly Serialize and Deserialize DBRef"] = function(test) { + var oid = new ObjectID(); + var doc = {dbref: new DBRef('namespace', oid, null)}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal("namespace", doc2.dbref.namespace); + test.deepEqual(doc2.dbref.oid.toHexString(), oid.toHexString()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize partial DBRef'] = function(test) { + var id = new ObjectID(); + var doc = {'name':'something', 'user':{'$ref':'username', '$id': id}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal('something', doc2.name); + test.equal('username', doc2.user.namespace); + test.equal(id.toString(), doc2.user.oid.toString()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize simple Int'] = function(test) { + var doc = {doc:2147483648}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, doc2.doc) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Long Integer'] = function(test) { + var doc = {doc: Long.fromNumber(9223372036854775807)}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, deserialized_data.doc); + + doc = {doc: Long.fromNumber(-9223372036854775)}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, deserialized_data.doc); + + doc = {doc: Long.fromNumber(-9223372036854775809)}; + serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, deserialized_data.doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Deserialize Large Integers as Number not Long'] = function(test) { + function roundTrip(val) { + var doc = {doc: val}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc, deserialized_data.doc); + }; + + roundTrip(Math.pow(2,52)); + roundTrip(Math.pow(2,53) - 1); + roundTrip(Math.pow(2,53)); + roundTrip(-Math.pow(2,52)); + roundTrip(-Math.pow(2,53) + 1); + roundTrip(-Math.pow(2,53)); + roundTrip(Math.pow(2,65)); // Too big for Long. + roundTrip(-Math.pow(2,65)); + roundTrip(9223372036854775807); + roundTrip(1234567890123456800); // Bigger than 2^53, stays a double. + roundTrip(-1234567890123456800); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Long Integer and Timestamp as different types'] = function(test) { + var long = Long.fromNumber(9223372036854775807); + var timestamp = Timestamp.fromNumber(9223372036854775807); + test.ok(long instanceof Long); + test.ok(!(long instanceof Timestamp)); + test.ok(timestamp instanceof Timestamp); + test.ok(!(timestamp instanceof Long)); + + var test_int = {doc: long, doc2: timestamp}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(test_int, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(test_int)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(test_int, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(test_int.doc, deserialized_data.doc); + test.done(); +} + +/** + * @ignore + */ +exports['Should Always put the id as the first item in a hash'] = function(test) { + var hash = {doc: {not_id:1, '_id':2}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(hash, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(hash)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(hash, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + var keys = []; + + for(var name in deserialized_data.doc) { + keys.push(name); + } + + test.deepEqual(['not_id', '_id'], keys); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a User defined Binary object'] = function(test) { + var bin = new Binary(); + bin.sub_type = BSON.BSON_BINARY_SUBTYPE_USER_DEFINED; + var string = 'binstring'; + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)); + } + + var doc = {doc: bin}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(deserialized_data.doc.sub_type, BSON.BSON_BINARY_SUBTYPE_USER_DEFINED); + test.deepEqual(doc.doc.value(), deserialized_data.doc.value()); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correclty Serialize and Deserialize a Code object'] = function(test) { + var doc = {'doc': {'doc2': new Code('this.a > i', {i:1})}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.doc.doc2.code, deserialized_data.doc.doc2.code); + test.deepEqual(doc.doc.doc2.scope.i, deserialized_data.doc.doc2.scope.i); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly serialize and deserialize and embedded array'] = function(test) { + var doc = {'a':0, + 'b':['tmp1', 'tmp2', 'tmp3', 'tmp4', 'tmp5', 'tmp6', 'tmp7', 'tmp8', 'tmp9', 'tmp10', 'tmp11', 'tmp12', 'tmp13', 'tmp14', 'tmp15', 'tmp16'] + }; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.a, deserialized_data.a); + test.deepEqual(doc.b, deserialized_data.b); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize UTF8'] = function(test) { + // Serialize utf8 + var doc = { "name" : "本荘由利地域に洪水警報", "name1" : "öüóőúéáűíÖÜÓŐÚÉÁŰÍ", "name2" : "abcdedede"}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize query object'] = function(test) { + var doc = { count: 'remove_with_no_callback_bug_test', query: {}, fields: null}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize empty query object'] = function(test) { + var doc = {}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize array based doc'] = function(test) { + var doc = { b: [ 1, 2, 3 ], _id: new ObjectID() }; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.b, deserialized_data.b) + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Symbol'] = function(test) { + if(Symbol != null) { + var doc = { b: [ new Symbol('test') ]}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc.b, deserialized_data.b) + test.deepEqual(doc, deserialized_data); + test.ok(deserialized_data.b[0] instanceof Symbol); + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should handle Deeply nested document'] = function(test) { + var doc = {a:{b:{c:{d:2}}}}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var deserialized_data = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, deserialized_data); + test.done(); +} + +/** + * @ignore + */ +exports['Should handle complicated all typed object'] = function(test) { + // First doc + var date = new Date(); + var oid = new ObjectID(); + var string = 'binstring' + var bin = new Binary() + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)) + } + + var doc = { + 'string': 'hello', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': date, + 'oid': oid, + 'binary': bin, + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': date.getTime(), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', oid, 'integration_tests_') + } + + // Second doc + var oid = new ObjectID.createFromHexString(oid.toHexString()); + var string = 'binstring' + var bin = new Binary() + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)) + } + + var doc2 = { + 'string': 'hello', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': date, + 'oid': oid, + 'binary': bin, + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': date.getTime(), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', oid, 'integration_tests_') + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var serialized_data2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc2, false, true); + + for(var i = 0; i < serialized_data2.length; i++) { + require('assert').equal(serialized_data2[i], serialized_data[i]) + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize Complex Nested Object'] = function(test) { + var doc = { email: 'email@email.com', + encrypted_password: 'password', + friends: [ '4db96b973d01205364000006', + '4dc77b24c5ba38be14000002' ], + location: [ 72.4930088, 23.0431957 ], + name: 'Amit Kumar', + password_salt: 'salty', + profile_fields: [], + username: 'amit', + _id: new ObjectID() } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = doc; + doc2._id = ObjectID.createFromHexString(doc2._id.toHexString()); + var serialized_data2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc2, false, true); + + for(var i = 0; i < serialized_data2.length; i++) { + require('assert').equal(serialized_data2[i], serialized_data[i]) + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly massive doc'] = function(test) { + var oid1 = new ObjectID(); + var oid2 = new ObjectID(); + + // JS doc + var doc = { dbref2: new DBRef('namespace', oid1, 'integration_tests_'), + _id: oid2 }; + + var doc2 = { dbref2: new DBRef('namespace', ObjectID.createFromHexString(oid1.toHexString()), 'integration_tests_'), + _id: new ObjectID.createFromHexString(oid2.toHexString()) }; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var serialized_data2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc2, false, true); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize/Deserialize regexp object'] = function(test) { + var doc = {'b':/foobaré/}; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var serialized_data2 = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + for(var i = 0; i < serialized_data2.length; i++) { + require('assert').equal(serialized_data2[i], serialized_data[i]) + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize/Deserialize complicated object'] = function(test) { + var doc = {a:{b:{c:[new ObjectID(), new ObjectID()]}}, d:{f:1332.3323}}; + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize/Deserialize nested object'] = function(test) { + var doc = { "_id" : { "date" : new Date(), "gid" : "6f35f74d2bea814e21000000" }, + "value" : { + "b" : { "countries" : { "--" : 386 }, "total" : 1599 }, + "bc" : { "countries" : { "--" : 3 }, "total" : 10 }, + "gp" : { "countries" : { "--" : 2 }, "total" : 13 }, + "mgc" : { "countries" : { "--" : 2 }, "total" : 14 } + } + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize/Deserialize nested object with even more nesting'] = function(test) { + var doc = { "_id" : { "date" : {a:1, b:2, c:new Date()}, "gid" : "6f35f74d2bea814e21000000" }, + "value" : { + "b" : { "countries" : { "--" : 386 }, "total" : 1599 }, + "bc" : { "countries" : { "--" : 3 }, "total" : 10 }, + "gp" : { "countries" : { "--" : 2 }, "total" : 13 }, + "mgc" : { "countries" : { "--" : 2 }, "total" : 14 } + } + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual(doc, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize empty name object'] = function(test) { + var doc = {'':'test', + 'bbbb':1}; + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.equal(doc2[''], 'test'); + test.equal(doc2['bbbb'], 1); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly handle Forced Doubles to ensure we allocate enough space for cap collections'] = function(test) { + if(Double != null) { + var doubleValue = new Double(100); + var doc = {value:doubleValue}; + + // Serialize + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual({value:100}, doc2); + } + + test.done(); +} + +/** + * @ignore + */ +exports['Should deserialize correctly'] = function(test) { + var doc = { + "_id" : new ObjectID("4e886e687ff7ef5e00000162"), + "str" : "foreign", + "type" : 2, + "timestamp" : ISODate("2011-10-02T14:00:08.383Z"), + "links" : [ + "http://www.reddit.com/r/worldnews/comments/kybm0/uk_home_secretary_calls_for_the_scrapping_of_the/" + ] + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc, doc2) + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly serialize and deserialize MinKey and MaxKey values'] = function(test) { + var doc = { + _id : new ObjectID("4e886e687ff7ef5e00000162"), + minKey : new MinKey(), + maxKey : new MaxKey() + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.deepEqual(doc, doc2) + test.ok(doc2.minKey instanceof MinKey); + test.ok(doc2.maxKey instanceof MaxKey); + test.done(); +} + +/** + * @ignore + */ +exports['Should correctly serialize Double value'] = function(test) { + var doc = { + value : new Double(34343.2222) + } + + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); + new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); + assertBuffersEqual(test, serialized_data, serialized_data2, 0); + var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + + test.ok(doc.value.valueOf(), doc2.value); + test.ok(doc.value.value, doc2.value); + test.done(); +} + +/** + * @ignore + */ +exports['ObjectID should correctly create objects'] = function(test) { + try { + var object1 = ObjectID.createFromHexString('000000000000000000000001') + var object2 = ObjectID.createFromHexString('00000000000000000000001') + test.ok(false); + } catch(err) { + test.ok(err != null); + } + + test.done(); +} + +/** + * @ignore + */ +exports['ObjectID should correctly retrieve timestamp'] = function(test) { + var testDate = new Date(); + var object1 = new ObjectID(); + test.equal(Math.floor(testDate.getTime()/1000), Math.floor(object1.getTimestamp().getTime()/1000)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly throw error on bsonparser errors'] = function(test) { + var data = new Buffer(3); + var parser = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + + // Catch to small buffer error + try { + parser.deserialize(data); + test.ok(false); + } catch(err) {} + + data = new Buffer(5); + data[0] = 0xff; + data[1] = 0xff; + // Catch illegal size + try { + parser.deserialize(data); + test.ok(false); + } catch(err) {} + + // Finish up + test.done(); +} + +/** + * A simple example showing the usage of BSON.calculateObjectSize function returning the number of BSON bytes a javascript object needs. + * + * @_class bson + * @_function BSON.calculateObjectSize + * @ignore + */ +exports['Should correctly calculate the size of a given javascript object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Calculate the size of the object without serializing the function + var size = BSON.calculateObjectSize(doc, false); + test.equal(12, size); + // Calculate the size of the object serializing the function + size = BSON.calculateObjectSize(doc, true); + // Validate the correctness + test.equal(36, size); + test.done(); +} + +/** + * A simple example showing the usage of BSON.calculateObjectSize function returning the number of BSON bytes a javascript object needs. + * + * @_class bson + * @_function calculateObjectSize + * @ignore + */ +exports['Should correctly calculate the size of a given javascript object using instance method'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Create a BSON parser instance + var bson = new BSON(); + // Calculate the size of the object without serializing the function + var size = bson.calculateObjectSize(doc, false); + test.equal(12, size); + // Calculate the size of the object serializing the function + size = bson.calculateObjectSize(doc, true); + // Validate the correctness + test.equal(36, size); + test.done(); +} + +/** + * A simple example showing the usage of BSON.serializeWithBufferAndIndex function. + * + * @_class bson + * @_function BSON.serializeWithBufferAndIndex + * @ignore + */ +exports['Should correctly serializeWithBufferAndIndex a given javascript object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Calculate the size of the document, no function serialization + var size = BSON.calculateObjectSize(doc, false); + // Allocate a buffer + var buffer = new Buffer(size); + // Serialize the object to the buffer, checking keys and not serializing functions + var index = BSON.serializeWithBufferAndIndex(doc, true, buffer, 0, false); + // Validate the correctness + test.equal(12, size); + test.equal(11, index); + + // Serialize with functions + // Calculate the size of the document, no function serialization + var size = BSON.calculateObjectSize(doc, true); + // Allocate a buffer + var buffer = new Buffer(size); + // Serialize the object to the buffer, checking keys and not serializing functions + var index = BSON.serializeWithBufferAndIndex(doc, true, buffer, 0, true); + // Validate the correctness + test.equal(36, size); + test.equal(35, index); + test.done(); +} + +/** + * A simple example showing the usage of BSON.serializeWithBufferAndIndex function. + * + * @_class bson + * @_function serializeWithBufferAndIndex + * @ignore + */ +exports['Should correctly serializeWithBufferAndIndex a given javascript object using a BSON instance'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Create a BSON parser instance + var bson = new BSON(); + // Calculate the size of the document, no function serialization + var size = bson.calculateObjectSize(doc, false); + // Allocate a buffer + var buffer = new Buffer(size); + // Serialize the object to the buffer, checking keys and not serializing functions + var index = bson.serializeWithBufferAndIndex(doc, true, buffer, 0, false); + // Validate the correctness + test.equal(12, size); + test.equal(11, index); + + // Serialize with functions + // Calculate the size of the document, no function serialization + var size = bson.calculateObjectSize(doc, true); + // Allocate a buffer + var buffer = new Buffer(size); + // Serialize the object to the buffer, checking keys and not serializing functions + var index = bson.serializeWithBufferAndIndex(doc, true, buffer, 0, true); + // Validate the correctness + test.equal(36, size); + test.equal(35, index); + test.done(); +} + +/** + * A simple example showing the usage of BSON.serialize function returning serialized BSON Buffer object. + * + * @_class bson + * @_function BSON.serialize + * @ignore + */ +exports['Should correctly serialize a given javascript object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Serialize the object to a buffer, checking keys and not serializing functions + var buffer = BSON.serialize(doc, true, true, false); + // Validate the correctness + test.equal(12, buffer.length); + + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = BSON.serialize(doc, true, true, true); + // Validate the correctness + test.equal(36, buffer.length); + test.done(); +} + +/** + * A simple example showing the usage of BSON.serialize function returning serialized BSON Buffer object. + * + * @_class bson + * @_function serialize + * @ignore + */ +exports['Should correctly serialize a given javascript object using a bson instance'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){}} + // Create a BSON parser instance + var bson = new BSON(); + // Serialize the object to a buffer, checking keys and not serializing functions + var buffer = bson.serialize(doc, true, true, false); + // Validate the correctness + test.equal(12, buffer.length); + + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = bson.serialize(doc, true, true, true); + // Validate the correctness + test.equal(36, buffer.length); + test.done(); +} + +/** + * A simple example showing the usage of BSON.deserialize function returning a deserialized Javascript function. + * + * @_class bson + * @_function BSON.deserialize + * @ignore + */ + exports['Should correctly deserialize a buffer using the BSON class level parser'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){ console.log('hello world'); }} + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = BSON.serialize(doc, true, true, true); + // Validate the correctness + test.equal(65, buffer.length); + + // Deserialize the object with no eval for the functions + var deserializedDoc = BSON.deserialize(buffer); + // Validate the correctness + test.equal('object', typeof deserializedDoc.func); + test.equal(1, deserializedDoc.a); + + // Deserialize the object with eval for the functions caching the functions + deserializedDoc = BSON.deserialize(buffer, {evalFunctions:true, cacheFunctions:true}); + // Validate the correctness + test.equal('function', typeof deserializedDoc.func); + test.equal(1, deserializedDoc.a); + test.done(); +} + +/** + * A simple example showing the usage of BSON instance deserialize function returning a deserialized Javascript function. + * + * @_class bson + * @_function deserialize + * @ignore + */ +exports['Should correctly deserialize a buffer using the BSON instance parser'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){ console.log('hello world'); }} + // Create a BSON parser instance + var bson = new BSON(); + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = bson.serialize(doc, true, true, true); + // Validate the correctness + test.equal(65, buffer.length); + + // Deserialize the object with no eval for the functions + var deserializedDoc = bson.deserialize(buffer); + // Validate the correctness + test.equal('object', typeof deserializedDoc.func); + test.equal(1, deserializedDoc.a); + + // Deserialize the object with eval for the functions caching the functions + deserializedDoc = bson.deserialize(buffer, {evalFunctions:true, cacheFunctions:true}); + // Validate the correctness + test.equal('function', typeof deserializedDoc.func); + test.equal(1, deserializedDoc.a); + test.done(); +} + +/** + * A simple example showing the usage of BSON.deserializeStream function returning deserialized Javascript objects. + * + * @_class bson + * @_function BSON.deserializeStream + * @ignore + */ +exports['Should correctly deserializeStream a buffer object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){ console.log('hello world'); }} + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = BSON.serialize(doc, true, true, true); + // Validate the correctness + test.equal(65, buffer.length); + + // The array holding the number of retuned documents + var documents = new Array(1); + // Deserialize the object with no eval for the functions + var index = BSON.deserializeStream(buffer, 0, 1, documents, 0); + // Validate the correctness + test.equal(65, index); + test.equal(1, documents.length); + test.equal(1, documents[0].a); + test.equal('object', typeof documents[0].func); + + // Deserialize the object with eval for the functions caching the functions + // The array holding the number of retuned documents + var documents = new Array(1); + // Deserialize the object with no eval for the functions + var index = BSON.deserializeStream(buffer, 0, 1, documents, 0, {evalFunctions:true, cacheFunctions:true}); + // Validate the correctness + test.equal(65, index); + test.equal(1, documents.length); + test.equal(1, documents[0].a); + test.equal('function', typeof documents[0].func); + test.done(); +} + +/** + * A simple example showing the usage of BSON instance deserializeStream function returning deserialized Javascript objects. + * + * @_class bson + * @_function deserializeStream + * @ignore + */ +exports['Should correctly deserializeStream a buffer object'] = function(test) { + // Create a simple object + var doc = {a: 1, func:function(){ console.log('hello world'); }} + // Create a BSON parser instance + var bson = new BSON(); + // Serialize the object to a buffer, checking keys and serializing functions + var buffer = bson.serialize(doc, true, true, true); + // Validate the correctness + test.equal(65, buffer.length); + + // The array holding the number of retuned documents + var documents = new Array(1); + // Deserialize the object with no eval for the functions + var index = bson.deserializeStream(buffer, 0, 1, documents, 0); + // Validate the correctness + test.equal(65, index); + test.equal(1, documents.length); + test.equal(1, documents[0].a); + test.equal('object', typeof documents[0].func); + + // Deserialize the object with eval for the functions caching the functions + // The array holding the number of retuned documents + var documents = new Array(1); + // Deserialize the object with no eval for the functions + var index = bson.deserializeStream(buffer, 0, 1, documents, 0, {evalFunctions:true, cacheFunctions:true}); + // Validate the correctness + test.equal(65, index); + test.equal(1, documents.length); + test.equal(1, documents[0].a); + test.equal('function', typeof documents[0].func); + test.done(); +} + +/** + * @ignore + */ +exports['ObjectID should have a correct cached representation of the hexString'] = function (test) { + ObjectID.cacheHexString = true; + var a = new ObjectID; + var __id = a.__id; + test.equal(__id, a.toHexString()); + + // hexString + a = new ObjectID(__id); + test.equal(__id, a.toHexString()); + + // fromHexString + a = ObjectID.createFromHexString(__id); + test.equal(a.__id, a.toHexString()); + test.equal(__id, a.toHexString()); + + // number + var genTime = a.generationTime; + a = new ObjectID(genTime); + __id = a.__id; + test.equal(__id, a.toHexString()); + + // generationTime + delete a.__id; + a.generationTime = genTime; + test.equal(__id, a.toHexString()); + + // createFromTime + a = ObjectId.createFromTime(genTime); + __id = a.__id; + test.equal(__id, a.toHexString()); + ObjectId.cacheHexString = false; + + test.done(); +} + +/** + * @ignore + */ +// 'Should Correctly Function' = function(test) { +// var doc = {b:1, func:function() { +// this.b = 2; +// }}; +// +// var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); +// +// debug("----------------------------------------------------------------------") +// debug(inspect(serialized_data)) +// +// // var serialized_data2 = new Buffer(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).calculateObjectSize(doc, false, true)); +// // new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serializeWithBufferAndIndex(doc, false, serialized_data2, 0); +// // assertBuffersEqual(test, serialized_data, serialized_data2, 0); +// var COUNT = 100000; +// +// // var b = null; +// // eval("b = function(x) { return x+x; }"); +// // var b = new Function("x", "return x+x;"); +// +// console.log(COUNT + "x (objectBSON = BSON.serialize(object))") +// start = new Date +// +// for (i=COUNT; --i>=0; ) { +// var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data, {evalFunctions: true, cacheFunctions:true}); +// } +// +// end = new Date +// console.log("time = ", end - start, "ms -", COUNT * 1000 / (end - start), " ops/sec") +// +// // debug(inspect(new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).functionCache)) +// // +// // var doc2 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data, {evalFunctions: true, cacheFunctions:true}); +// // // test.deepEqual(doc, doc2) +// // // +// // debug(inspect(doc2)) +// // doc2.func() +// // debug(inspect(doc2)) +// // +// // var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc2, false, true); +// // var doc3 = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data, {evalFunctions: true, cacheFunctions:true}); +// // +// // debug("-----------------------------------------------") +// // debug(inspect(doc3)) +// +// // var key = "0" +// // for(var i = 1; i < 10000; i++) { +// // key = key + " " + i +// // } +// +// test.done(); +// +// +// // var car = { +// // model : "Volvo", +// // country : "Sweden", +// // +// // isSwedish : function() { +// // return this.country == "Sweden"; +// // } +// // } +// +// }, + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_typed_array_test.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_typed_array_test.js new file mode 100644 index 0000000..cde83f8 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/bson_typed_array_test.js @@ -0,0 +1,392 @@ +var mongodb = require('../../lib/bson').pure(); + +var testCase = require('nodeunit').testCase, + mongoO = require('../../lib/bson').pure(), + debug = require('util').debug, + inspect = require('util').inspect, + Buffer = require('buffer').Buffer, + gleak = require('../../tools/gleak'), + fs = require('fs'), + BSON = mongoO.BSON, + Code = mongoO.Code, + Binary = mongoO.Binary, + Timestamp = mongoO.Timestamp, + Long = mongoO.Long, + MongoReply = mongoO.MongoReply, + ObjectID = mongoO.ObjectID, + Symbol = mongoO.Symbol, + DBRef = mongoO.DBRef, + Double = mongoO.Double, + MinKey = mongoO.MinKey, + MaxKey = mongoO.MaxKey, + BinaryParser = mongoO.BinaryParser, + utils = require('./tools/utils'); + +var BSONSE = mongodb, + BSONDE = mongodb; + +// for tests +BSONDE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONDE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONDE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONDE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONDE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONDE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +BSONSE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONSE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONSE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONSE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONSE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONSE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +var hexStringToBinary = function(string) { + var numberofValues = string.length / 2; + var array = ""; + + for(var i = 0; i < numberofValues; i++) { + array += String.fromCharCode(parseInt(string[i*2] + string[i*2 + 1], 16)); + } + return array; +} + +var assertBuffersEqual = function(test, buffer1, buffer2) { + if(buffer1.length != buffer2.length) test.fail("Buffers do not have the same length", buffer1, buffer2); + + for(var i = 0; i < buffer1.length; i++) { + test.equal(buffer1[i], buffer2[i]); + } +} + +/** + * Module for parsing an ISO 8601 formatted string into a Date object. + */ +var ISODate = function (string) { + var match; + + if (typeof string.getTime === "function") + return string; + else if (match = string.match(/^(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|((\+|-)(\d{2}):(\d{2}))))?)?)?$/)) { + var date = new Date(); + date.setUTCFullYear(Number(match[1])); + date.setUTCMonth(Number(match[3]) - 1 || 0); + date.setUTCDate(Number(match[5]) || 0); + date.setUTCHours(Number(match[7]) || 0); + date.setUTCMinutes(Number(match[8]) || 0); + date.setUTCSeconds(Number(match[10]) || 0); + date.setUTCMilliseconds(Number("." + match[12]) * 1000 || 0); + + if (match[13] && match[13] !== "Z") { + var h = Number(match[16]) || 0, + m = Number(match[17]) || 0; + + h *= 3600000; + m *= 60000; + + var offset = h + m; + if (match[15] == "+") + offset = -offset; + + date = new Date(date.valueOf() + offset); + } + + return date; + } else + throw new Error("Invalid ISO 8601 date given.", __filename); +}; + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports.shouldCorrectlyDeserializeUsingTypedArray = function(test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + var motherOfAllDocuments = { + 'string': '客家话', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': new Date(), + 'oid': new ObjectID(), + 'binary': new Binary(new Buffer("hello")), + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': Long.fromNumber(100), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), + 'minkey': new MinKey(), + 'maxkey': new MaxKey() + } + + // Let's serialize it + var data = BSONSE.BSON.serialize(motherOfAllDocuments, true, true, false); + // Build a typed array + var arr = new Uint8Array(new ArrayBuffer(data.length)); + // Iterate over all the fields and copy + for(var i = 0; i < data.length; i++) { + arr[i] = data[i] + } + + // Deserialize the object + var object = BSONDE.BSON.deserialize(arr); + // Asserts + test.equal(motherOfAllDocuments.string, object.string); + test.deepEqual(motherOfAllDocuments.array, object.array); + test.deepEqual(motherOfAllDocuments.date, object.date); + test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); + // Assert the values of the binary + for(var i = 0; i < motherOfAllDocuments.binary.length(); i++) { + test.equal(motherOfAllDocuments.binary.value[i], object.binary[i]); + } + test.deepEqual(motherOfAllDocuments.int, object.int); + test.deepEqual(motherOfAllDocuments.float, object.float); + test.deepEqual(motherOfAllDocuments.regexp, object.regexp); + test.deepEqual(motherOfAllDocuments.boolean, object.boolean); + test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); + test.deepEqual(motherOfAllDocuments.where, object.where); + test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); + test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); + test.deepEqual(motherOfAllDocuments.minkey, object.minkey); + test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); + test.done(); +} + +/** + * @ignore + */ +exports.shouldCorrectlySerializeUsingTypedArray = function(test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + var motherOfAllDocuments = { + 'string': 'hello', + 'array': [1,2,3], + 'hash': {'a':1, 'b':2}, + 'date': new Date(), + 'oid': new ObjectID(), + 'binary': new Binary(new Buffer("hello")), + 'int': 42, + 'float': 33.3333, + 'regexp': /regexp/, + 'boolean': true, + 'long': Long.fromNumber(100), + 'where': new Code('this.a > i', {i:1}), + 'dbref': new DBRef('namespace', new ObjectID(), 'integration_tests_'), + 'minkey': new MinKey(), + 'maxkey': new MaxKey() + } + + // Let's serialize it + var data = BSONSE.BSON.serialize(motherOfAllDocuments, true, false, false); + // And deserialize it again + var object = BSONSE.BSON.deserialize(data); + // Asserts + test.equal(motherOfAllDocuments.string, object.string); + test.deepEqual(motherOfAllDocuments.array, object.array); + test.deepEqual(motherOfAllDocuments.date, object.date); + test.deepEqual(motherOfAllDocuments.oid.toHexString(), object.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.binary.length(), object.binary.length()); + // Assert the values of the binary + for(var i = 0; i < motherOfAllDocuments.binary.length(); i++) { + test.equal(motherOfAllDocuments.binary.value[i], object.binary[i]); + } + test.deepEqual(motherOfAllDocuments.int, object.int); + test.deepEqual(motherOfAllDocuments.float, object.float); + test.deepEqual(motherOfAllDocuments.regexp, object.regexp); + test.deepEqual(motherOfAllDocuments.boolean, object.boolean); + test.deepEqual(motherOfAllDocuments.long.toNumber(), object.long); + test.deepEqual(motherOfAllDocuments.where, object.where); + test.deepEqual(motherOfAllDocuments.dbref.oid.toHexString(), object.dbref.oid.toHexString()); + test.deepEqual(motherOfAllDocuments.dbref.namespace, object.dbref.namespace); + test.deepEqual(motherOfAllDocuments.dbref.db, object.dbref.db); + test.deepEqual(motherOfAllDocuments.minkey, object.minkey); + test.deepEqual(motherOfAllDocuments.maxkey, object.maxkey); + test.done(); +} + +/** + * @ignore + */ +exports['exercise all the binary object constructor methods'] = function (test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + // Construct using array + var string = 'hello world'; + // String to array + var array = utils.stringToArrayBuffer(string); + + // Binary from array buffer + var binary = new Binary(utils.stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + test.ok(utils.assertArrayEqual(array, binary.buffer)); + + // Construct using number of chars + binary = new Binary(5); + test.ok(5, binary.buffer.length); + + // Construct using an Array + var binary = new Binary(utils.stringToArray(string)); + test.ok(string.length, binary.buffer.length); + test.ok(utils.assertArrayEqual(array, binary.buffer)); + + // Construct using a string + var binary = new Binary(string); + test.ok(string.length, binary.buffer.length); + test.ok(utils.assertArrayEqual(array, binary.buffer)); + test.done(); +}; + +/** + * @ignore + */ +exports['exercise the put binary object method for an instance when using Uint8Array'] = function (test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + // Construct using array + var string = 'hello world'; + // String to array + var array = utils.stringToArrayBuffer(string + 'a'); + + // Binary from array buffer + var binary = new Binary(utils.stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Write a byte to the array + binary.put('a') + + // Verify that the data was writtencorrectly + test.equal(string.length + 1, binary.position); + test.ok(utils.assertArrayEqual(array, binary.value(true))); + test.equal('hello worlda', binary.value()); + + // Exercise a binary with lots of space in the buffer + var binary = new Binary(); + test.ok(Binary.BUFFER_SIZE, binary.buffer.length); + + // Write a byte to the array + binary.put('a') + + // Verify that the data was writtencorrectly + test.equal(1, binary.position); + test.ok(utils.assertArrayEqual(['a'.charCodeAt(0)], binary.value(true))); + test.equal('a', binary.value()); + test.done(); +}, + +/** + * @ignore + */ +exports['exercise the write binary object method for an instance when using Uint8Array'] = function (test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + // Construct using array + var string = 'hello world'; + // Array + var writeArrayBuffer = new Uint8Array(new ArrayBuffer(1)); + writeArrayBuffer[0] = 'a'.charCodeAt(0); + var arrayBuffer = ['a'.charCodeAt(0)]; + + // Binary from array buffer + var binary = new Binary(utils.stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Write a string starting at end of buffer + binary.write('a'); + test.equal('hello worlda', binary.value()); + // Write a string starting at index 0 + binary.write('a', 0); + test.equal('aello worlda', binary.value()); + // Write a arraybuffer starting at end of buffer + binary.write(writeArrayBuffer); + test.equal('aello worldaa', binary.value()); + // Write a arraybuffer starting at position 5 + binary.write(writeArrayBuffer, 5); + test.equal('aelloaworldaa', binary.value()); + // Write a array starting at end of buffer + binary.write(arrayBuffer); + test.equal('aelloaworldaaa', binary.value()); + // Write a array starting at position 6 + binary.write(arrayBuffer, 6); + test.equal('aelloaaorldaaa', binary.value()); + test.done(); +}, + +/** + * @ignore + */ +exports['exercise the read binary object method for an instance when using Uint8Array'] = function (test) { + if(typeof ArrayBuffer == 'undefined') { + test.done(); + return; + } + + // Construct using array + var string = 'hello world'; + var array = utils.stringToArrayBuffer(string); + + // Binary from array buffer + var binary = new Binary(utils.stringToArrayBuffer(string)); + test.ok(string.length, binary.buffer.length); + + // Read the first 2 bytes + var data = binary.read(0, 2); + test.ok(utils.assertArrayEqual(utils.stringToArrayBuffer('he'), data)); + + // Read the entire field + var data = binary.read(0); + test.ok(utils.assertArrayEqual(utils.stringToArrayBuffer(string), data)); + + // Read 3 bytes + var data = binary.read(6, 5); + test.ok(utils.assertArrayEqual(utils.stringToArrayBuffer('world'), data)); + test.done(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} \ No newline at end of file diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/data/test_gs_weird_bug.png b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/data/test_gs_weird_bug.png new file mode 100644 index 0000000..1554dc3 Binary files /dev/null and b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/data/test_gs_weird_bug.png differ diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/test_full_bson.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/test_full_bson.js new file mode 100644 index 0000000..7a78347 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/test_full_bson.js @@ -0,0 +1,315 @@ +var sys = require('util'), + fs = require('fs'), + BSON = require('../../ext').BSON, + Buffer = require('buffer').Buffer, + BSONJS = require('../../lib/bson/bson').BSON, + BinaryParser = require('../../lib/bson/binary_parser').BinaryParser, + Long = require('../../lib/bson/long').Long, + ObjectID = require('../../lib/bson/bson').ObjectID, + Binary = require('../../lib/bson/bson').Binary, + Code = require('../../lib/bson/bson').Code, + DBRef = require('../../lib/bson/bson').DBRef, + Symbol = require('../../lib/bson/bson').Symbol, + Double = require('../../lib/bson/bson').Double, + MaxKey = require('../../lib/bson/bson').MaxKey, + MinKey = require('../../lib/bson/bson').MinKey, + Timestamp = require('../../lib/bson/bson').Timestamp, + gleak = require('../../tools/gleak'), + assert = require('assert'); + +// Parsers +var bsonC = new BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); +var bsonJS = new BSONJS([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]); + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports['Should Correctly Deserialize object'] = function(test) { + var bytes = [95,0,0,0,2,110,115,0,42,0,0,0,105,110,116,101,103,114,97,116,105,111,110,95,116,101,115,116,115,95,46,116,101,115,116,95,105,110,100,101,120,95,105,110,102,111,114,109,97,116,105,111,110,0,8,117,110,105,113,117,101,0,0,3,107,101,121,0,12,0,0,0,16,97,0,1,0,0,0,0,2,110,97,109,101,0,4,0,0,0,97,95,49,0,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + + var object = bsonC.deserialize(serialized_data); + assert.equal("a_1", object.name); + assert.equal(false, object.unique); + assert.equal(1, object.key.a); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Deserialize object with all types'] = function(test) { + var bytes = [26,1,0,0,7,95,105,100,0,161,190,98,75,118,169,3,0,0,3,0,0,4,97,114,114,97,121,0,26,0,0,0,16,48,0,1,0,0,0,16,49,0,2,0,0,0,16,50,0,3,0,0,0,0,2,115,116,114,105,110,103,0,6,0,0,0,104,101,108,108,111,0,3,104,97,115,104,0,19,0,0,0,16,97,0,1,0,0,0,16,98,0,2,0,0,0,0,9,100,97,116,101,0,161,190,98,75,0,0,0,0,7,111,105,100,0,161,190,98,75,90,217,18,0,0,1,0,0,5,98,105,110,97,114,121,0,7,0,0,0,2,3,0,0,0,49,50,51,16,105,110,116,0,42,0,0,0,1,102,108,111,97,116,0,223,224,11,147,169,170,64,64,11,114,101,103,101,120,112,0,102,111,111,98,97,114,0,105,0,8,98,111,111,108,101,97,110,0,1,15,119,104,101,114,101,0,25,0,0,0,12,0,0,0,116,104,105,115,46,120,32,61,61,32,51,0,5,0,0,0,0,3,100,98,114,101,102,0,37,0,0,0,2,36,114,101,102,0,5,0,0,0,116,101,115,116,0,7,36,105,100,0,161,190,98,75,2,180,1,0,0,2,0,0,0,10,110,117,108,108,0,0]; + var serialized_data = ''; + // Convert to chars + for(var i = 0; i < bytes.length; i++) { + serialized_data = serialized_data + BinaryParser.fromByte(bytes[i]); + } + + var object = bsonJS.deserialize(new Buffer(serialized_data, 'binary')); + assert.equal("hello", object.string); + assert.deepEqual([1, 2, 3], object.array); + assert.equal(1, object.hash.a); + assert.equal(2, object.hash.b); + assert.ok(object.date != null); + assert.ok(object.oid != null); + assert.ok(object.binary != null); + assert.equal(42, object.int); + assert.equal(33.3333, object.float); + assert.ok(object.regexp != null); + assert.equal(true, object.boolean); + assert.ok(object.where != null); + assert.ok(object.dbref != null); + assert.ok(object['null'] == null); + test.done(); +} + +/** + * @ignore + */ +exports['Should Serialize and Deserialize String'] = function(test) { + var test_string = {hello: 'world'} + var serialized_data = bsonC.serialize(test_string) + assert.deepEqual(test_string, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Integer'] = function(test) { + var test_number = {doc: 5} + var serialized_data = bsonC.serialize(test_number) + assert.deepEqual(test_number, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize null value'] = function(test) { + var test_null = {doc:null} + var serialized_data = bsonC.serialize(test_null) + var object = bsonC.deserialize(serialized_data); + assert.deepEqual(test_null, object); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize undefined value'] = function(test) { + var test_undefined = {doc:undefined} + var serialized_data = bsonC.serialize(test_undefined) + var object = bsonJS.deserialize(new Buffer(serialized_data, 'binary')); + assert.equal(null, object.doc) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Number'] = function(test) { + var test_number = {doc: 5.5} + var serialized_data = bsonC.serialize(test_number) + assert.deepEqual(test_number, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Integer'] = function(test) { + var test_int = {doc: 42} + var serialized_data = bsonC.serialize(test_int) + assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + + test_int = {doc: -5600} + serialized_data = bsonC.serialize(test_int) + assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + + test_int = {doc: 2147483647} + serialized_data = bsonC.serialize(test_int) + assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + + test_int = {doc: -2147483648} + serialized_data = bsonC.serialize(test_int) + assert.deepEqual(test_int, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Object'] = function(test) { + var doc = {doc: {age: 42, name: 'Spongebob', shoe_size: 9.5}} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Array'] = function(test) { + var doc = {doc: [1, 2, 'a', 'b']} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Array with added on functions'] = function(test) { + var doc = {doc: [1, 2, 'a', 'b']} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize A Boolean'] = function(test) { + var doc = {doc: true} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Date'] = function(test) { + var date = new Date() + //(2009, 11, 12, 12, 00, 30) + date.setUTCDate(12) + date.setUTCFullYear(2009) + date.setUTCMonth(11 - 1) + date.setUTCHours(12) + date.setUTCMinutes(0) + date.setUTCSeconds(30) + var doc = {doc: date} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Oid'] = function(test) { + var doc = {doc: new ObjectID()} + var serialized_data = bsonC.serialize(doc) + assert.deepEqual(doc.doc.toHexString(), bsonC.deserialize(serialized_data).doc.toHexString()) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Buffer'] = function(test) { + var doc = {doc: new Buffer("123451234512345")} + var serialized_data = bsonC.serialize(doc) + + assert.equal("123451234512345", bsonC.deserialize(serialized_data).doc.buffer.toString('ascii')); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly encode Empty Hash'] = function(test) { + var test_code = {} + var serialized_data = bsonC.serialize(test_code) + assert.deepEqual(test_code, bsonC.deserialize(serialized_data)); + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Ordered Hash'] = function(test) { + var doc = {doc: {b:1, a:2, c:3, d:4}} + var serialized_data = bsonC.serialize(doc) + var decoded_hash = bsonC.deserialize(serialized_data).doc + var keys = [] + for(var name in decoded_hash) keys.push(name) + assert.deepEqual(['b', 'a', 'c', 'd'], keys) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize Regular Expression'] = function(test) { + var doc = {doc: /foobar/mi} + var serialized_data = bsonC.serialize(doc) + var doc2 = bsonC.deserialize(serialized_data); + assert.equal(doc.doc.toString(), doc2.doc.toString()) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a Binary object'] = function(test) { + var bin = new Binary() + var string = 'binstring' + for(var index = 0; index < string.length; index++) { + bin.put(string.charAt(index)) + } + var doc = {doc: bin} + var serialized_data = bsonC.serialize(doc) + var deserialized_data = bsonC.deserialize(serialized_data); + assert.equal(doc.doc.value(), deserialized_data.doc.value()) + test.done(); +} + +/** + * @ignore + */ +exports['Should Correctly Serialize and Deserialize a big Binary object'] = function(test) { + var data = fs.readFileSync("test/node/data/test_gs_weird_bug.png", 'binary'); + var bin = new Binary() + bin.write(data) + var doc = {doc: bin} + var serialized_data = bsonC.serialize(doc) + var deserialized_data = bsonC.deserialize(serialized_data); + assert.equal(doc.doc.value(), deserialized_data.doc.value()) + test.done(); +} + +/** + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/to_bson_test.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/to_bson_test.js new file mode 100644 index 0000000..e9282e5 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/to_bson_test.js @@ -0,0 +1,109 @@ +var mongodb = process.env['TEST_NATIVE'] != null ? require('../../lib/bson').native() : require('../../lib/bson').pure(); + +var testCase = require('nodeunit').testCase, + mongoO = require('../../lib/bson').pure(), + Buffer = require('buffer').Buffer, + gleak = require('../../tools/gleak'), + fs = require('fs'), + BSON = mongoO.BSON, + Code = mongoO.Code, + Binary = mongoO.Binary, + Timestamp = mongoO.Timestamp, + Long = mongoO.Long, + MongoReply = mongoO.MongoReply, + ObjectID = mongoO.ObjectID, + Symbol = mongoO.Symbol, + DBRef = mongoO.DBRef, + Double = mongoO.Double, + MinKey = mongoO.MinKey, + MaxKey = mongoO.MaxKey, + BinaryParser = mongoO.BinaryParser; + +var BSONSE = mongodb, + BSONDE = mongodb; + +// for tests +BSONDE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONDE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONDE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONDE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONDE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONDE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +BSONSE.BSON_BINARY_SUBTYPE_DEFAULT = 0; +BSONSE.BSON_BINARY_SUBTYPE_FUNCTION = 1; +BSONSE.BSON_BINARY_SUBTYPE_BYTE_ARRAY = 2; +BSONSE.BSON_BINARY_SUBTYPE_UUID = 3; +BSONSE.BSON_BINARY_SUBTYPE_MD5 = 4; +BSONSE.BSON_BINARY_SUBTYPE_USER_DEFINED = 128; + +var hexStringToBinary = function(string) { + var numberofValues = string.length / 2; + var array = ""; + + for(var i = 0; i < numberofValues; i++) { + array += String.fromCharCode(parseInt(string[i*2] + string[i*2 + 1], 16)); + } + return array; +} + +var assertBuffersEqual = function(test, buffer1, buffer2) { + if(buffer1.length != buffer2.length) test.fail("Buffers do not have the same length", buffer1, buffer2); + + for(var i = 0; i < buffer1.length; i++) { + test.equal(buffer1[i], buffer2[i]); + } +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.setUp = function(callback) { + callback(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.tearDown = function(callback) { + callback(); +} + +/** + * @ignore + */ +exports['Should correctly handle toBson function for an object'] = function(test) { + // Test object + var doc = { + hello: new ObjectID(), + a:1 + }; + // Add a toBson method to the object + doc.toBSON = function() { + return {b:1}; + } + + // Serialize the data + var serialized_data = new BSONSE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).serialize(doc, false, true); + var deserialized_doc = new BSONDE.BSON([Long, ObjectID, Binary, Code, DBRef, Symbol, Double, Timestamp, MaxKey, MinKey]).deserialize(serialized_data); + test.deepEqual({b:1}, deserialized_doc); + test.done(); +} + +/** + * Retrieve the server information for the current + * instance of the db client + * + * @ignore + */ +exports.noGlobalsLeaked = function(test) { + var leaks = gleak.detectNew(); + test.equal(0, leaks.length, "global var leak detected: " + leaks.join(', ')); + test.done(); +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/tools/utils.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/tools/utils.js new file mode 100644 index 0000000..9d7cbe7 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/test/node/tools/utils.js @@ -0,0 +1,80 @@ +exports.assertArrayEqual = function(array1, array2) { + if(array1.length != array2.length) return false; + for(var i = 0; i < array1.length; i++) { + if(array1[i] != array2[i]) return false; + } + + return true; +} + +// String to arraybuffer +exports.stringToArrayBuffer = function(string) { + var dataBuffer = new Uint8Array(new ArrayBuffer(string.length)); + // Return the strings + for(var i = 0; i < string.length; i++) { + dataBuffer[i] = string.charCodeAt(i); + } + // Return the data buffer + return dataBuffer; +} + +// String to arraybuffer +exports.stringToArray = function(string) { + var dataBuffer = new Array(string.length); + // Return the strings + for(var i = 0; i < string.length; i++) { + dataBuffer[i] = string.charCodeAt(i); + } + // Return the data buffer + return dataBuffer; +} + +exports.Utf8 = { + // public method for url encoding + encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // public method for url decoding + decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + c = utftext.charCodeAt(i); + if(c < 128) { + string += String.fromCharCode(c); + i++; + } else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + } + return string; + } +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/gleak.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/gleak.js new file mode 100644 index 0000000..c707cfc --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/gleak.js @@ -0,0 +1,21 @@ + +var gleak = require('gleak')(); +gleak.ignore('AssertionError'); +gleak.ignore('testFullSpec_param_found'); +gleak.ignore('events'); +gleak.ignore('Uint8Array'); +gleak.ignore('Uint8ClampedArray'); +gleak.ignore('TAP_Global_Harness'); +gleak.ignore('setImmediate'); +gleak.ignore('clearImmediate'); + +gleak.ignore('DTRACE_NET_SERVER_CONNECTION'); +gleak.ignore('DTRACE_NET_STREAM_END'); +gleak.ignore('DTRACE_NET_SOCKET_READ'); +gleak.ignore('DTRACE_NET_SOCKET_WRITE'); +gleak.ignore('DTRACE_HTTP_SERVER_REQUEST'); +gleak.ignore('DTRACE_HTTP_SERVER_RESPONSE'); +gleak.ignore('DTRACE_HTTP_CLIENT_REQUEST'); +gleak.ignore('DTRACE_HTTP_CLIENT_RESPONSE'); + +module.exports = gleak; diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE new file mode 100644 index 0000000..7c435ba --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/MIT.LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008-2011 Pivotal Labs + +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/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine-html.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine-html.js new file mode 100644 index 0000000..7383401 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine-html.js @@ -0,0 +1,190 @@ +jasmine.TrivialReporter = function(doc) { + this.document = doc || document; + this.suiteDivs = {}; + this.logRunningSpecs = false; +}; + +jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { el.appendChild(child); } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { + var showPassed, showSkipped; + + this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' }, + this.createDom('div', { className: 'banner' }, + this.createDom('div', { className: 'logo' }, + this.createDom('span', { className: 'title' }, "Jasmine"), + this.createDom('span', { className: 'version' }, runner.env.versionString())), + this.createDom('div', { className: 'options' }, + "Show ", + showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), + showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") + ) + ), + + this.runnerDiv = this.createDom('div', { className: 'runner running' }, + this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), + this.runnerMessageSpan = this.createDom('span', {}, "Running..."), + this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) + ); + + this.document.body.appendChild(this.outerDiv); + + var suites = runner.suites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + var suiteDiv = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); + this.suiteDivs[suite.id] = suiteDiv; + var parentDiv = this.outerDiv; + if (suite.parentSuite) { + parentDiv = this.suiteDivs[suite.parentSuite.id]; + } + parentDiv.appendChild(suiteDiv); + } + + this.startedAt = new Date(); + + var self = this; + showPassed.onclick = function(evt) { + if (showPassed.checked) { + self.outerDiv.className += ' show-passed'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); + } + }; + + showSkipped.onclick = function(evt) { + if (showSkipped.checked) { + self.outerDiv.className += ' show-skipped'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); + } + }; +}; + +jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { + var results = runner.results(); + var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; + this.runnerDiv.setAttribute("class", className); + //do it twice for IE + this.runnerDiv.setAttribute("className", className); + var specs = runner.specs(); + var specCount = 0; + for (var i = 0; i < specs.length; i++) { + if (this.specFilter(specs[i])) { + specCount++; + } + } + var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); + message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; + this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); + + this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); +}; + +jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { + var results = suite.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.totalCount === 0) { // todo: change this to check results.skipped + status = 'skipped'; + } + this.suiteDivs[suite.id].className += " " + status; +}; + +jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { + if (this.logRunningSpecs) { + this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } +}; + +jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { + var results = spec.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + var specDiv = this.createDom('div', { className: 'spec ' + status }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(spec.getFullName()), + title: spec.getFullName() + }, spec.description)); + + + var resultItems = results.getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + specDiv.appendChild(messagesDiv); + } + + this.suiteDivs[spec.suite.id].appendChild(specDiv); +}; + +jasmine.TrivialReporter.prototype.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } +}; + +jasmine.TrivialReporter.prototype.getLocation = function() { + return this.document.location; +}; + +jasmine.TrivialReporter.prototype.specFilter = function(spec) { + var paramMap = {}; + var params = this.getLocation().search.substring(1).split('&'); + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + if (!paramMap.spec) { + return true; + } + return spec.getFullName().indexOf(paramMap.spec) === 0; +}; diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.css b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.css new file mode 100644 index 0000000..6583fe7 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.css @@ -0,0 +1,166 @@ +body { + font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; +} + + +.jasmine_reporter a:visited, .jasmine_reporter a { + color: #303; +} + +.jasmine_reporter a:hover, .jasmine_reporter a:active { + color: blue; +} + +.run_spec { + float:right; + padding-right: 5px; + font-size: .8em; + text-decoration: none; +} + +.jasmine_reporter { + margin: 0 5px; +} + +.banner { + color: #303; + background-color: #fef; + padding: 5px; +} + +.logo { + float: left; + font-size: 1.1em; + padding-left: 5px; +} + +.logo .version { + font-size: .6em; + padding-left: 1em; +} + +.runner.running { + background-color: yellow; +} + + +.options { + text-align: right; + font-size: .8em; +} + + + + +.suite { + border: 1px outset gray; + margin: 5px 0; + padding-left: 1em; +} + +.suite .suite { + margin: 5px; +} + +.suite.passed { + background-color: #dfd; +} + +.suite.failed { + background-color: #fdd; +} + +.spec { + margin: 5px; + padding-left: 1em; + clear: both; +} + +.spec.failed, .spec.passed, .spec.skipped { + padding-bottom: 5px; + border: 1px solid gray; +} + +.spec.failed { + background-color: #fbb; + border-color: red; +} + +.spec.passed { + background-color: #bfb; + border-color: green; +} + +.spec.skipped { + background-color: #bbb; +} + +.messages { + border-left: 1px dashed gray; + padding-left: 1em; + padding-right: 1em; +} + +.passed { + background-color: #cfc; + display: none; +} + +.failed { + background-color: #fbb; +} + +.skipped { + color: #777; + background-color: #eee; + display: none; +} + + +/*.resultMessage {*/ + /*white-space: pre;*/ +/*}*/ + +.resultMessage span.result { + display: block; + line-height: 2em; + color: black; +} + +.resultMessage .mismatch { + color: black; +} + +.stackTrace { + white-space: pre; + font-size: .8em; + margin-left: 10px; + max-height: 5em; + overflow: auto; + border: 1px inset red; + padding: 1em; + background: #eef; +} + +.finished-at { + padding-left: 1em; + font-size: .6em; +} + +.show-passed .passed, +.show-skipped .skipped { + display: block; +} + + +#jasmine_content { + position:fixed; + right: 100%; +} + +.runner { + border: 1px solid gray; + display: block; + margin: 5px 0; + padding: 2px 0 2px 10px; +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.js b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.js new file mode 100644 index 0000000..c3d2dc7 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine.js @@ -0,0 +1,2476 @@ +var isCommonJS = typeof window == "undefined"; + +/** + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. + * + * @namespace + */ +var jasmine = {}; +if (isCommonJS) exports.jasmine = jasmine; +/** + * @private + */ +jasmine.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); +}; + +/** + * Use jasmine.undefined instead of undefined, since undefined is just + * a plain old variable and may be redefined by somebody else. + * + * @private + */ +jasmine.undefined = jasmine.___undefined___; + +/** + * Show diagnostic messages in the console if set to true + * + */ +jasmine.VERBOSE = false; + +/** + * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. + * + */ +jasmine.DEFAULT_UPDATE_INTERVAL = 250; + +/** + * Default timeout interval in milliseconds for waitsFor() blocks. + */ +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + +jasmine.getGlobal = function() { + function getGlobal() { + return this; + } + + return getGlobal(); +}; + +/** + * Allows for bound functions to be compared. Internal use only. + * + * @ignore + * @private + * @param base {Object} bound 'this' for the function + * @param name {Function} function to find + */ +jasmine.bindOriginal_ = function(base, name) { + var original = base[name]; + if (original.apply) { + return function() { + return original.apply(base, arguments); + }; + } else { + // IE support + return jasmine.getGlobal()[name]; + } +}; + +jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); +jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); +jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); +jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); + +jasmine.MessageResult = function(values) { + this.type = 'log'; + this.values = values; + this.trace = new Error(); // todo: test better +}; + +jasmine.MessageResult.prototype.toString = function() { + var text = ""; + for (var i = 0; i < this.values.length; i++) { + if (i > 0) text += " "; + if (jasmine.isString_(this.values[i])) { + text += this.values[i]; + } else { + text += jasmine.pp(this.values[i]); + } + } + return text; +}; + +jasmine.ExpectationResult = function(params) { + this.type = 'expect'; + this.matcherName = params.matcherName; + this.passed_ = params.passed; + this.expected = params.expected; + this.actual = params.actual; + this.message = this.passed_ ? 'Passed.' : params.message; + + var trace = (params.trace || new Error(this.message)); + this.trace = this.passed_ ? '' : trace; +}; + +jasmine.ExpectationResult.prototype.toString = function () { + return this.message; +}; + +jasmine.ExpectationResult.prototype.passed = function () { + return this.passed_; +}; + +/** + * Getter for the Jasmine environment. Ensures one gets created + */ +jasmine.getEnv = function() { + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); + return env; +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isArray_ = function(value) { + return jasmine.isA_("Array", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isString_ = function(value) { + return jasmine.isA_("String", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isNumber_ = function(value) { + return jasmine.isA_("Number", value); +}; + +/** + * @ignore + * @private + * @param {String} typeName + * @param value + * @returns {Boolean} + */ +jasmine.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; +}; + +/** + * Pretty printer for expecations. Takes any object and turns it into a human-readable string. + * + * @param value {Object} an object to be outputted + * @returns {String} + */ +jasmine.pp = function(value) { + var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; +}; + +/** + * Returns true if the object is a DOM Node. + * + * @param {Object} obj object to check + * @returns {Boolean} + */ +jasmine.isDomNode = function(obj) { + return obj.nodeType > 0; +}; + +/** + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. + * + * @example + * // don't care about which function is passed in, as long as it's a function + * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); + * + * @param {Class} clazz + * @returns matchable object of the type clazz + */ +jasmine.any = function(clazz) { + return new jasmine.Matchers.Any(clazz); +}; + +/** + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. + * + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine + * expectation syntax. Spies can be checked if they were called or not and what the calling params were. + * + * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). + * + * Spies are torn down at the end of every spec. + * + * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. + * + * @example + * // a stub + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere + * + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // actual foo.not will not be called, execution stops + * spyOn(foo, 'not'); + + // foo.not spied upon, execution will continue to implementation + * spyOn(foo, 'not').andCallThrough(); + * + * // fake example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // foo.not(val) will return val + * spyOn(foo, 'not').andCallFake(function(value) {return value;}); + * + * // mock example + * foo.not(7 == 7); + * expect(foo.not).toHaveBeenCalled(); + * expect(foo.not).toHaveBeenCalledWith(true); + * + * @constructor + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj + * @param {String} name + */ +jasmine.Spy = function(name) { + /** + * The name of the spy, if provided. + */ + this.identity = name || 'unknown'; + /** + * Is this Object a spy? + */ + this.isSpy = true; + /** + * The actual function this spy stubs. + */ + this.plan = function() { + }; + /** + * Tracking of the most recent call to the spy. + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy.mostRecentCall.args = [1, 2]; + */ + this.mostRecentCall = {}; + + /** + * Holds arguments for each call to the spy, indexed by call count + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy(7, 8); + * mySpy.mostRecentCall.args = [7, 8]; + * mySpy.argsForCall[0] = [1, 2]; + * mySpy.argsForCall[1] = [7, 8]; + */ + this.argsForCall = []; + this.calls = []; +}; + +/** + * Tells a spy to call through to the actual implemenatation. + * + * @example + * var foo = { + * bar: function() { // do some stuff } + * } + * + * // defining a spy on an existing property: foo.bar + * spyOn(foo, 'bar').andCallThrough(); + */ +jasmine.Spy.prototype.andCallThrough = function() { + this.plan = this.originalValue; + return this; +}; + +/** + * For setting the return value of a spy. + * + * @example + * // defining a spy from scratch: foo() returns 'baz' + * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); + * + * // defining a spy on an existing property: foo.bar() returns 'baz' + * spyOn(foo, 'bar').andReturn('baz'); + * + * @param {Object} value + */ +jasmine.Spy.prototype.andReturn = function(value) { + this.plan = function() { + return value; + }; + return this; +}; + +/** + * For throwing an exception when a spy is called. + * + * @example + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' + * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); + * + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' + * spyOn(foo, 'bar').andThrow('baz'); + * + * @param {String} exceptionMsg + */ +jasmine.Spy.prototype.andThrow = function(exceptionMsg) { + this.plan = function() { + throw exceptionMsg; + }; + return this; +}; + +/** + * Calls an alternate implementation when a spy is called. + * + * @example + * var baz = function() { + * // do some stuff, return something + * } + * // defining a spy from scratch: foo() calls the function baz + * var foo = jasmine.createSpy('spy on foo').andCall(baz); + * + * // defining a spy on an existing property: foo.bar() calls an anonymnous function + * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); + * + * @param {Function} fakeFunc + */ +jasmine.Spy.prototype.andCallFake = function(fakeFunc) { + this.plan = fakeFunc; + return this; +}; + +/** + * Resets all of a spy's the tracking variables so that it can be used again. + * + * @example + * spyOn(foo, 'bar'); + * + * foo.bar(); + * + * expect(foo.bar.callCount).toEqual(1); + * + * foo.bar.reset(); + * + * expect(foo.bar.callCount).toEqual(0); + */ +jasmine.Spy.prototype.reset = function() { + this.wasCalled = false; + this.callCount = 0; + this.argsForCall = []; + this.calls = []; + this.mostRecentCall = {}; +}; + +jasmine.createSpy = function(name) { + + var spyObj = function() { + spyObj.wasCalled = true; + spyObj.callCount++; + var args = jasmine.util.argsToArray(arguments); + spyObj.mostRecentCall.object = this; + spyObj.mostRecentCall.args = args; + spyObj.argsForCall.push(args); + spyObj.calls.push({object: this, args: args}); + return spyObj.plan.apply(this, arguments); + }; + + var spy = new jasmine.Spy(name); + + for (var prop in spy) { + spyObj[prop] = spy[prop]; + } + + spyObj.reset(); + + return spyObj; +}; + +/** + * Determines whether an object is a spy. + * + * @param {jasmine.Spy|Object} putativeSpy + * @returns {Boolean} + */ +jasmine.isSpy = function(putativeSpy) { + return putativeSpy && putativeSpy.isSpy; +}; + +/** + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something + * large in one call. + * + * @param {String} baseName name of spy class + * @param {Array} methodNames array of names of methods to make spies + */ +jasmine.createSpyObj = function(baseName, methodNames) { + if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { + throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); + } + return obj; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the current spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.log = function() { + var spec = jasmine.getEnv().currentSpec; + spec.log.apply(spec, arguments); +}; + +/** + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. + * + * @example + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops + * + * @see jasmine.createSpy + * @param obj + * @param methodName + * @returns a Jasmine spy that can be chained with all spy methods + */ +var spyOn = function(obj, methodName) { + return jasmine.getEnv().currentSpec.spyOn(obj, methodName); +}; +if (isCommonJS) exports.spyOn = spyOn; + +/** + * Creates a Jasmine spec that will be added to the current suite. + * + * // TODO: pending tests + * + * @example + * it('should be true', function() { + * expect(true).toEqual(true); + * }); + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var it = function(desc, func) { + return jasmine.getEnv().it(desc, func); +}; +if (isCommonJS) exports.it = it; + +/** + * Creates a disabled Jasmine spec. + * + * A convenience method that allows existing specs to be disabled temporarily during development. + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var xit = function(desc, func) { + return jasmine.getEnv().xit(desc, func); +}; +if (isCommonJS) exports.xit = xit; + +/** + * Starts a chain for a Jasmine expectation. + * + * It is passed an Object that is the actual value and should chain to one of the many + * jasmine.Matchers functions. + * + * @param {Object} actual Actual value to test against and expected value + */ +var expect = function(actual) { + return jasmine.getEnv().currentSpec.expect(actual); +}; +if (isCommonJS) exports.expect = expect; + +/** + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. + * + * @param {Function} func Function that defines part of a jasmine spec. + */ +var runs = function(func) { + jasmine.getEnv().currentSpec.runs(func); +}; +if (isCommonJS) exports.runs = runs; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +var waits = function(timeout) { + jasmine.getEnv().currentSpec.waits(timeout); +}; +if (isCommonJS) exports.waits = waits; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); +}; +if (isCommonJS) exports.waitsFor = waitsFor; + +/** + * A function that is called before each spec in a suite. + * + * Used for spec setup, including validating assumptions. + * + * @param {Function} beforeEachFunction + */ +var beforeEach = function(beforeEachFunction) { + jasmine.getEnv().beforeEach(beforeEachFunction); +}; +if (isCommonJS) exports.beforeEach = beforeEach; + +/** + * A function that is called after each spec in a suite. + * + * Used for restoring any state that is hijacked during spec execution. + * + * @param {Function} afterEachFunction + */ +var afterEach = function(afterEachFunction) { + jasmine.getEnv().afterEach(afterEachFunction); +}; +if (isCommonJS) exports.afterEach = afterEach; + +/** + * Defines a suite of specifications. + * + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization + * of setup in some tests. + * + * @example + * // TODO: a simple suite + * + * // TODO: a simple suite with a nested describe block + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var describe = function(description, specDefinitions) { + return jasmine.getEnv().describe(description, specDefinitions); +}; +if (isCommonJS) exports.describe = describe; + +/** + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var xdescribe = function(description, specDefinitions) { + return jasmine.getEnv().xdescribe(description, specDefinitions); +}; +if (isCommonJS) exports.xdescribe = xdescribe; + + +// Provide the XMLHttpRequest class for IE 5.x-6.x: +jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { + function tryIt(f) { + try { + return f(); + } catch(e) { + } + return null; + } + + var xhr = tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.6.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.3.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP"); + }) || + tryIt(function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }); + + if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); + + return xhr; +} : XMLHttpRequest; +/** + * @namespace + */ +jasmine.util = {}; + +/** + * Declare that a child class inherit it's prototype from the parent class. + * + * @private + * @param {Function} childClass + * @param {Function} parentClass + */ +jasmine.util.inherit = function(childClass, parentClass) { + /** + * @private + */ + var subclass = function() { + }; + subclass.prototype = parentClass.prototype; + childClass.prototype = new subclass(); +}; + +jasmine.util.formatException = function(e) { + var lineNumber; + if (e.line) { + lineNumber = e.line; + } + else if (e.lineNumber) { + lineNumber = e.lineNumber; + } + + var file; + + if (e.sourceURL) { + file = e.sourceURL; + } + else if (e.fileName) { + file = e.fileName; + } + + var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); + + if (file && lineNumber) { + message += ' in ' + file + ' (line ' + lineNumber + ')'; + } + + return message; +}; + +jasmine.util.htmlEscape = function(str) { + if (!str) return str; + return str.replace(/&/g, '&') + .replace(//g, '>'); +}; + +jasmine.util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); + return arrayOfArgs; +}; + +jasmine.util.extend = function(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; +}; + +/** + * Environment for Jasmine + * + * @constructor + */ +jasmine.Env = function() { + this.currentSpec = null; + this.currentSuite = null; + this.currentRunner_ = new jasmine.Runner(this); + + this.reporter = new jasmine.MultiReporter(); + + this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; + this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; + this.lastUpdate = 0; + this.specFilter = function() { + return true; + }; + + this.nextSpecId_ = 0; + this.nextSuiteId_ = 0; + this.equalityTesters_ = []; + + // wrap matchers + this.matchersClass = function() { + jasmine.Matchers.apply(this, arguments); + }; + jasmine.util.inherit(this.matchersClass, jasmine.Matchers); + + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); +}; + + +jasmine.Env.prototype.setTimeout = jasmine.setTimeout; +jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; +jasmine.Env.prototype.setInterval = jasmine.setInterval; +jasmine.Env.prototype.clearInterval = jasmine.clearInterval; + +/** + * @returns an object containing jasmine version build info, if set. + */ +jasmine.Env.prototype.version = function () { + if (jasmine.version_) { + return jasmine.version_; + } else { + throw new Error('Version not set'); + } +}; + +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (!jasmine.version_) { + return "version unknown"; + } + + var version = this.version(); + var versionString = version.major + "." + version.minor + "." + version.build; + if (version.release_candidate) { + versionString += ".rc" + version.release_candidate; + } + versionString += " revision " + version.revision; + return versionString; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSpecId = function () { + return this.nextSpecId_++; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSuiteId = function () { + return this.nextSuiteId_++; +}; + +/** + * Register a reporter to receive status updates from Jasmine. + * @param {jasmine.Reporter} reporter An object which will receive status updates. + */ +jasmine.Env.prototype.addReporter = function(reporter) { + this.reporter.addReporter(reporter); +}; + +jasmine.Env.prototype.execute = function() { + this.currentRunner_.execute(); +}; + +jasmine.Env.prototype.describe = function(description, specDefinitions) { + var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); + + var parentSuite = this.currentSuite; + if (parentSuite) { + parentSuite.add(suite); + } else { + this.currentRunner_.add(suite); + } + + this.currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch(e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + this.currentSuite = parentSuite; + + return suite; +}; + +jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { + if (this.currentSuite) { + this.currentSuite.beforeEach(beforeEachFunction); + } else { + this.currentRunner_.beforeEach(beforeEachFunction); + } +}; + +jasmine.Env.prototype.currentRunner = function () { + return this.currentRunner_; +}; + +jasmine.Env.prototype.afterEach = function(afterEachFunction) { + if (this.currentSuite) { + this.currentSuite.afterEach(afterEachFunction); + } else { + this.currentRunner_.afterEach(afterEachFunction); + } + +}; + +jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { + return { + execute: function() { + } + }; +}; + +jasmine.Env.prototype.it = function(description, func) { + var spec = new jasmine.Spec(this, this.currentSuite, description); + this.currentSuite.add(spec); + this.currentSpec = spec; + + if (func) { + spec.runs(func); + } + + return spec; +}; + +jasmine.Env.prototype.xit = function(desc, func) { + return { + id: this.nextSpecId(), + runs: function() { + } + }; +}; + +jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { + return true; + } + + a.__Jasmine_been_here_before__ = b; + b.__Jasmine_been_here_before__ = a; + + var hasKey = function(obj, keyName) { + return obj !== null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in b) { + if (!hasKey(a, property) && hasKey(b, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + } + for (property in a) { + if (!hasKey(b, property) && hasKey(a, property)) { + mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + } + } + for (property in b) { + if (property == '__Jasmine_been_here_before__') continue; + if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + } + } + + if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { + mismatchValues.push("arrays were not the same length"); + } + + delete a.__Jasmine_been_here_before__; + delete b.__Jasmine_been_here_before__; + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== jasmine.undefined) return result; + } + + if (a === b) return true; + + if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { + return (a == jasmine.undefined && b == jasmine.undefined); + } + + if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() == b.getTime(); + } + + if (a instanceof jasmine.Matchers.Any) { + return a.matches(b); + } + + if (b instanceof jasmine.Matchers.Any) { + return b.matches(a); + } + + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); + } + + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + } + + //Straight check + return (a === b); +}; + +jasmine.Env.prototype.contains_ = function(haystack, needle) { + if (jasmine.isArray_(haystack)) { + for (var i = 0; i < haystack.length; i++) { + if (this.equals_(haystack[i], needle)) return true; + } + return false; + } + return haystack.indexOf(needle) >= 0; +}; + +jasmine.Env.prototype.addEqualityTester = function(equalityTester) { + this.equalityTesters_.push(equalityTester); +}; +/** No-op base class for Jasmine reporters. + * + * @constructor + */ +jasmine.Reporter = function() { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerResults = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecStarting = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecResults = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.log = function(str) { +}; + +/** + * Blocks are functions with executable code that make up a spec. + * + * @constructor + * @param {jasmine.Env} env + * @param {Function} func + * @param {jasmine.Spec} spec + */ +jasmine.Block = function(env, func, spec) { + this.env = env; + this.func = func; + this.spec = spec; +}; + +jasmine.Block.prototype.execute = function(onComplete) { + try { + this.func.apply(this.spec); + } catch (e) { + this.spec.fail(e); + } + onComplete(); +}; +/** JavaScript API reporter. + * + * @constructor + */ +jasmine.JsApiReporter = function() { + this.started = false; + this.finished = false; + this.suites_ = []; + this.results_ = {}; +}; + +jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { + this.started = true; + var suites = runner.topLevelSuites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + this.suites_.push(this.summarize_(suite)); + } +}; + +jasmine.JsApiReporter.prototype.suites = function() { + return this.suites_; +}; + +jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { + var isSuite = suiteOrSpec instanceof jasmine.Suite; + var summary = { + id: suiteOrSpec.id, + name: suiteOrSpec.description, + type: isSuite ? 'suite' : 'spec', + children: [] + }; + + if (isSuite) { + var children = suiteOrSpec.children(); + for (var i = 0; i < children.length; i++) { + summary.children.push(this.summarize_(children[i])); + } + } + return summary; +}; + +jasmine.JsApiReporter.prototype.results = function() { + return this.results_; +}; + +jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { + return this.results_[specId]; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { + this.finished = true; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { + this.results_[spec.id] = { + messages: spec.results().getItems(), + result: spec.results().failedCount > 0 ? "failed" : "passed" + }; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.log = function(str) { +}; + +jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ + var results = {}; + for (var i = 0; i < specIds.length; i++) { + var specId = specIds[i]; + results[specId] = this.summarizeResult_(this.results_[specId]); + } + return results; +}; + +jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ + var summaryMessages = []; + var messagesLength = result.messages.length; + for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { + var resultMessage = result.messages[messageIndex]; + summaryMessages.push({ + text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, + passed: resultMessage.passed ? resultMessage.passed() : true, + type: resultMessage.type, + message: resultMessage.message, + trace: { + stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined + } + }); + } + + return { + result : result.result, + messages : summaryMessages + }; +}; + +/** + * @constructor + * @param {jasmine.Env} env + * @param actual + * @param {jasmine.Spec} spec + */ +jasmine.Matchers = function(env, actual, spec, opt_isNot) { + this.env = env; + this.actual = actual; + this.spec = spec; + this.isNot = opt_isNot || false; + this.reportWasCalled_ = false; +}; + +// todo: @deprecated as of Jasmine 0.11, remove soon [xw] +jasmine.Matchers.pp = function(str) { + throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); +}; + +// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] +jasmine.Matchers.prototype.report = function(result, failing_message, details) { + throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); +}; + +jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { + for (var methodName in prototype) { + if (methodName == 'report') continue; + var orig = prototype[methodName]; + matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); + } +}; + +jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { + return function() { + var matcherArgs = jasmine.util.argsToArray(arguments); + var result = matcherFunction.apply(this, arguments); + + if (this.isNot) { + result = !result; + } + + if (this.reportWasCalled_) return result; + + var message; + if (!result) { + if (this.message) { + message = this.message.apply(this, arguments); + if (jasmine.isArray_(message)) { + message = message[this.isNot ? 1 : 0]; + } + } else { + var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; + if (matcherArgs.length > 0) { + for (var i = 0; i < matcherArgs.length; i++) { + if (i > 0) message += ","; + message += " " + jasmine.pp(matcherArgs[i]); + } + } + message += "."; + } + } + var expectationResult = new jasmine.ExpectationResult({ + matcherName: matcherName, + passed: result, + expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], + actual: this.actual, + message: message + }); + this.spec.addMatcherResult(expectationResult); + return jasmine.undefined; + }; +}; + + + + +/** + * toBe: compares the actual to the expected using === + * @param expected + */ +jasmine.Matchers.prototype.toBe = function(expected) { + return this.actual === expected; +}; + +/** + * toNotBe: compares the actual to the expected using !== + * @param expected + * @deprecated as of 1.0. Use not.toBe() instead. + */ +jasmine.Matchers.prototype.toNotBe = function(expected) { + return this.actual !== expected; +}; + +/** + * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. + * + * @param expected + */ +jasmine.Matchers.prototype.toEqual = function(expected) { + return this.env.equals_(this.actual, expected); +}; + +/** + * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual + * @param expected + * @deprecated as of 1.0. Use not.toNotEqual() instead. + */ +jasmine.Matchers.prototype.toNotEqual = function(expected) { + return !this.env.equals_(this.actual, expected); +}; + +/** + * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes + * a pattern or a String. + * + * @param expected + */ +jasmine.Matchers.prototype.toMatch = function(expected) { + return new RegExp(expected).test(this.actual); +}; + +/** + * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch + * @param expected + * @deprecated as of 1.0. Use not.toMatch() instead. + */ +jasmine.Matchers.prototype.toNotMatch = function(expected) { + return !(new RegExp(expected).test(this.actual)); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeDefined = function() { + return (this.actual !== jasmine.undefined); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeUndefined = function() { + return (this.actual === jasmine.undefined); +}; + +/** + * Matcher that compares the actual to null. + */ +jasmine.Matchers.prototype.toBeNull = function() { + return (this.actual === null); +}; + +/** + * Matcher that boolean not-nots the actual. + */ +jasmine.Matchers.prototype.toBeTruthy = function() { + return !!this.actual; +}; + + +/** + * Matcher that boolean nots the actual. + */ +jasmine.Matchers.prototype.toBeFalsy = function() { + return !this.actual; +}; + + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called. + */ +jasmine.Matchers.prototype.toHaveBeenCalled = function() { + if (arguments.length > 0) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to have been called.", + "Expected spy " + this.actual.identity + " not to have been called." + ]; + }; + + return this.actual.wasCalled; +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ +jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was not called. + * + * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead + */ +jasmine.Matchers.prototype.wasNotCalled = function() { + if (arguments.length > 0) { + throw new Error('wasNotCalled does not take arguments'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to not have been called.", + "Expected spy " + this.actual.identity + " to have been called." + ]; + }; + + return !this.actual.wasCalled; +}; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. + * + * @example + * + */ +jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + this.message = function() { + if (this.actual.callCount === 0) { + // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." + ]; + } else { + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) + ]; + } + }; + + return this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; + +/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasNotCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", + "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" + ]; + }; + + return !this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** + * Matcher that checks that the expected item is an element in the actual Array. + * + * @param {Object} expected + */ +jasmine.Matchers.prototype.toContain = function(expected) { + return this.env.contains_(this.actual, expected); +}; + +/** + * Matcher that checks that the expected item is NOT an element in the actual Array. + * + * @param {Object} expected + * @deprecated as of 1.0. Use not.toNotContain() instead. + */ +jasmine.Matchers.prototype.toNotContain = function(expected) { + return !this.env.contains_(this.actual, expected); +}; + +jasmine.Matchers.prototype.toBeLessThan = function(expected) { + return this.actual < expected; +}; + +jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { + return this.actual > expected; +}; + +/** + * Matcher that checks that the expected item is equal to the actual item + * up to a given level of decimal precision (default 2). + * + * @param {Number} expected + * @param {Number} precision + */ +jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { + if (!(precision === 0)) { + precision = precision || 2; + } + var multiplier = Math.pow(10, precision); + var actual = Math.round(this.actual * multiplier); + expected = Math.round(expected * multiplier); + return expected == actual; +}; + +/** + * Matcher that checks that the expected exception was thrown by the actual. + * + * @param {String} expected + */ +jasmine.Matchers.prototype.toThrow = function(expected) { + var result = false; + var exception; + if (typeof this.actual != 'function') { + throw new Error('Actual is not a function'); + } + try { + this.actual(); + } catch (e) { + exception = e; + } + if (exception) { + result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); + } + + var not = this.isNot ? "not " : ""; + + this.message = function() { + if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { + return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); + } else { + return "Expected function to throw an exception."; + } + }; + + return result; +}; + +jasmine.Matchers.Any = function(expectedClass) { + this.expectedClass = expectedClass; +}; + +jasmine.Matchers.Any.prototype.matches = function(other) { + if (this.expectedClass == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedClass == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedClass == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedClass == Object) { + return typeof other == 'object'; + } + + return other instanceof this.expectedClass; +}; + +jasmine.Matchers.Any.prototype.toString = function() { + return ''; +}; + +/** + * @constructor + */ +jasmine.MultiReporter = function() { + this.subReporters_ = []; +}; +jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); + +jasmine.MultiReporter.prototype.addReporter = function(reporter) { + this.subReporters_.push(reporter); +}; + +(function() { + var functionNames = [ + "reportRunnerStarting", + "reportRunnerResults", + "reportSuiteResults", + "reportSpecStarting", + "reportSpecResults", + "log" + ]; + for (var i = 0; i < functionNames.length; i++) { + var functionName = functionNames[i]; + jasmine.MultiReporter.prototype[functionName] = (function(functionName) { + return function() { + for (var j = 0; j < this.subReporters_.length; j++) { + var subReporter = this.subReporters_[j]; + if (subReporter[functionName]) { + subReporter[functionName].apply(subReporter, arguments); + } + } + }; + })(functionName); + } +})(); +/** + * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults + * + * @constructor + */ +jasmine.NestedResults = function() { + /** + * The total count of results + */ + this.totalCount = 0; + /** + * Number of passed results + */ + this.passedCount = 0; + /** + * Number of failed results + */ + this.failedCount = 0; + /** + * Was this suite/spec skipped? + */ + this.skipped = false; + /** + * @ignore + */ + this.items_ = []; +}; + +/** + * Roll up the result counts. + * + * @param result + */ +jasmine.NestedResults.prototype.rollupCounts = function(result) { + this.totalCount += result.totalCount; + this.passedCount += result.passedCount; + this.failedCount += result.failedCount; +}; + +/** + * Adds a log message. + * @param values Array of message parts which will be concatenated later. + */ +jasmine.NestedResults.prototype.log = function(values) { + this.items_.push(new jasmine.MessageResult(values)); +}; + +/** + * Getter for the results: message & results. + */ +jasmine.NestedResults.prototype.getItems = function() { + return this.items_; +}; + +/** + * Adds a result, tracking counts (total, passed, & failed) + * @param {jasmine.ExpectationResult|jasmine.NestedResults} result + */ +jasmine.NestedResults.prototype.addResult = function(result) { + if (result.type != 'log') { + if (result.items_) { + this.rollupCounts(result); + } else { + this.totalCount++; + if (result.passed()) { + this.passedCount++; + } else { + this.failedCount++; + } + } + } + this.items_.push(result); +}; + +/** + * @returns {Boolean} True if everything below passed + */ +jasmine.NestedResults.prototype.passed = function() { + return this.passedCount === this.totalCount; +}; +/** + * Base class for pretty printing for expectation results. + */ +jasmine.PrettyPrinter = function() { + this.ppNestLevel_ = 0; +}; + +/** + * Formats a value in a nice, human-readable string. + * + * @param value + */ +jasmine.PrettyPrinter.prototype.format = function(value) { + if (this.ppNestLevel_ > 40) { + throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); + } + + this.ppNestLevel_++; + try { + if (value === jasmine.undefined) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === jasmine.getGlobal()) { + this.emitScalar(''); + } else if (value instanceof jasmine.Matchers.Any) { + this.emitScalar(value.toString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (jasmine.isSpy(value)) { + this.emitScalar("spy on " + value.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (jasmine.isArray_(value) || typeof value == 'object') { + value.__Jasmine_been_here_before__ = true; + if (jasmine.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } +}; + +jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (property == '__Jasmine_been_here_before__') continue; + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + obj.__lookupGetter__(property) !== null) : false); + } +}; + +jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; + +jasmine.StringPrettyPrinter = function() { + jasmine.PrettyPrinter.call(this); + + this.string = ''; +}; +jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); + +jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); +}; + +jasmine.StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); +}; + +jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); +}; + +jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); +}; + +jasmine.StringPrettyPrinter.prototype.append = function(value) { + this.string += value; +}; +jasmine.Queue = function(env) { + this.env = env; + this.blocks = []; + this.running = false; + this.index = 0; + this.offset = 0; + this.abort = false; +}; + +jasmine.Queue.prototype.addBefore = function(block) { + this.blocks.unshift(block); +}; + +jasmine.Queue.prototype.add = function(block) { + this.blocks.push(block); +}; + +jasmine.Queue.prototype.insertNext = function(block) { + this.blocks.splice((this.index + this.offset + 1), 0, block); + this.offset++; +}; + +jasmine.Queue.prototype.start = function(onComplete) { + this.running = true; + this.onComplete = onComplete; + this.next_(); +}; + +jasmine.Queue.prototype.isRunning = function() { + return this.running; +}; + +jasmine.Queue.LOOP_DONT_RECURSE = true; + +jasmine.Queue.prototype.next_ = function() { + var self = this; + var goAgain = true; + + while (goAgain) { + goAgain = false; + + if (self.index < self.blocks.length && !this.abort) { + var calledSynchronously = true; + var completedSynchronously = false; + + var onComplete = function () { + if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { + completedSynchronously = true; + return; + } + + if (self.blocks[self.index].abort) { + self.abort = true; + } + + self.offset = 0; + self.index++; + + var now = new Date().getTime(); + if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { + self.env.lastUpdate = now; + self.env.setTimeout(function() { + self.next_(); + }, 0); + } else { + if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { + goAgain = true; + } else { + self.next_(); + } + } + }; + self.blocks[self.index].execute(onComplete); + + calledSynchronously = false; + if (completedSynchronously) { + onComplete(); + } + + } else { + self.running = false; + if (self.onComplete) { + self.onComplete(); + } + } + } +}; + +jasmine.Queue.prototype.results = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.blocks.length; i++) { + if (this.blocks[i].results) { + results.addResult(this.blocks[i].results()); + } + } + return results; +}; + + +/** + * Runner + * + * @constructor + * @param {jasmine.Env} env + */ +jasmine.Runner = function(env) { + var self = this; + self.env = env; + self.queue = new jasmine.Queue(env); + self.before_ = []; + self.after_ = []; + self.suites_ = []; +}; + +jasmine.Runner.prototype.execute = function() { + var self = this; + if (self.env.reporter.reportRunnerStarting) { + self.env.reporter.reportRunnerStarting(this); + } + self.queue.start(function () { + self.finishCallback(); + }); +}; + +jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.splice(0,0,beforeEachFunction); +}; + +jasmine.Runner.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.splice(0,0,afterEachFunction); +}; + + +jasmine.Runner.prototype.finishCallback = function() { + this.env.reporter.reportRunnerResults(this); +}; + +jasmine.Runner.prototype.addSuite = function(suite) { + this.suites_.push(suite); +}; + +jasmine.Runner.prototype.add = function(block) { + if (block instanceof jasmine.Suite) { + this.addSuite(block); + } + this.queue.add(block); +}; + +jasmine.Runner.prototype.specs = function () { + var suites = this.suites(); + var specs = []; + for (var i = 0; i < suites.length; i++) { + specs = specs.concat(suites[i].specs()); + } + return specs; +}; + +jasmine.Runner.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Runner.prototype.topLevelSuites = function() { + var topLevelSuites = []; + for (var i = 0; i < this.suites_.length; i++) { + if (!this.suites_[i].parentSuite) { + topLevelSuites.push(this.suites_[i]); + } + } + return topLevelSuites; +}; + +jasmine.Runner.prototype.results = function() { + return this.queue.results(); +}; +/** + * Internal representation of a Jasmine specification, or test. + * + * @constructor + * @param {jasmine.Env} env + * @param {jasmine.Suite} suite + * @param {String} description + */ +jasmine.Spec = function(env, suite, description) { + if (!env) { + throw new Error('jasmine.Env() required'); + } + if (!suite) { + throw new Error('jasmine.Suite() required'); + } + var spec = this; + spec.id = env.nextSpecId ? env.nextSpecId() : null; + spec.env = env; + spec.suite = suite; + spec.description = description; + spec.queue = new jasmine.Queue(env); + + spec.afterCallbacks = []; + spec.spies_ = []; + + spec.results_ = new jasmine.NestedResults(); + spec.results_.description = description; + spec.matchersClass = null; +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.suite.getFullName() + ' ' + this.description + '.'; +}; + + +jasmine.Spec.prototype.results = function() { + return this.results_; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.Spec.prototype.log = function() { + return this.results_.log(arguments); +}; + +jasmine.Spec.prototype.runs = function (func) { + var block = new jasmine.Block(this.env, func, this); + this.addToQueue(block); + return this; +}; + +jasmine.Spec.prototype.addToQueue = function (block) { + if (this.queue.isRunning()) { + this.queue.insertNext(block); + } else { + this.queue.add(block); + } +}; + +/** + * @param {jasmine.ExpectationResult} result + */ +jasmine.Spec.prototype.addMatcherResult = function(result) { + this.results_.addResult(result); +}; + +jasmine.Spec.prototype.expect = function(actual) { + var positive = new (this.getMatchersClass_())(this.env, actual, this); + positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); + return positive; +}; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +jasmine.Spec.prototype.waits = function(timeout) { + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.addToQueue(waitsFunc); + return this; +}; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + var latchFunction_ = null; + var optional_timeoutMessage_ = null; + var optional_timeout_ = null; + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + switch (typeof arg) { + case 'function': + latchFunction_ = arg; + break; + case 'string': + optional_timeoutMessage_ = arg; + break; + case 'number': + optional_timeout_ = arg; + break; + } + } + + var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); + this.addToQueue(waitsForFunc); + return this; +}; + +jasmine.Spec.prototype.fail = function (e) { + var expectationResult = new jasmine.ExpectationResult({ + passed: false, + message: e ? jasmine.util.formatException(e) : 'Exception', + trace: { stack: e.stack } + }); + this.results_.addResult(expectationResult); +}; + +jasmine.Spec.prototype.getMatchersClass_ = function() { + return this.matchersClass || this.env.matchersClass; +}; + +jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { + var parent = this.getMatchersClass_(); + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); + this.matchersClass = newMatchersClass; +}; + +jasmine.Spec.prototype.finishCallback = function() { + this.env.reporter.reportSpecResults(this); +}; + +jasmine.Spec.prototype.finish = function(onComplete) { + this.removeAllSpies(); + this.finishCallback(); + if (onComplete) { + onComplete(); + } +}; + +jasmine.Spec.prototype.after = function(doAfter) { + if (this.queue.isRunning()) { + this.queue.add(new jasmine.Block(this.env, doAfter, this)); + } else { + this.afterCallbacks.unshift(doAfter); + } +}; + +jasmine.Spec.prototype.execute = function(onComplete) { + var spec = this; + if (!spec.env.specFilter(spec)) { + spec.results_.skipped = true; + spec.finish(onComplete); + return; + } + + this.env.reporter.reportSpecStarting(this); + + spec.env.currentSpec = spec; + + spec.addBeforesAndAftersToQueue(); + + spec.queue.start(function () { + spec.finish(onComplete); + }); +}; + +jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { + var runner = this.env.currentRunner(); + var i; + + for (var suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); + } + } + for (i = 0; i < runner.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); + } + for (i = 0; i < this.afterCallbacks.length; i++) { + this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); + } + for (suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); + } + } + for (i = 0; i < runner.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); + } +}; + +jasmine.Spec.prototype.explodes = function() { + throw 'explodes function should not have been called'; +}; + +jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { + if (obj == jasmine.undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; + } + + if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { + throw methodName + '() method does not exist'; + } + + if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { + throw new Error(methodName + ' has already been spied upon'); + } + + var spyObj = jasmine.createSpy(methodName); + + this.spies_.push(spyObj); + spyObj.baseObj = obj; + spyObj.methodName = methodName; + spyObj.originalValue = obj[methodName]; + + obj[methodName] = spyObj; + + return spyObj; +}; + +jasmine.Spec.prototype.removeAllSpies = function() { + for (var i = 0; i < this.spies_.length; i++) { + var spy = this.spies_[i]; + spy.baseObj[spy.methodName] = spy.originalValue; + } + this.spies_ = []; +}; + +/** + * Internal representation of a Jasmine suite. + * + * @constructor + * @param {jasmine.Env} env + * @param {String} description + * @param {Function} specDefinitions + * @param {jasmine.Suite} parentSuite + */ +jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + var self = this; + self.id = env.nextSuiteId ? env.nextSuiteId() : null; + self.description = description; + self.queue = new jasmine.Queue(env); + self.parentSuite = parentSuite; + self.env = env; + self.before_ = []; + self.after_ = []; + self.children_ = []; + self.suites_ = []; + self.specs_ = []; +}; + +jasmine.Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + return fullName; +}; + +jasmine.Suite.prototype.finish = function(onComplete) { + this.env.reporter.reportSuiteResults(this); + this.finished = true; + if (typeof(onComplete) == 'function') { + onComplete(); + } +}; + +jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.unshift(beforeEachFunction); +}; + +jasmine.Suite.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.unshift(afterEachFunction); +}; + +jasmine.Suite.prototype.results = function() { + return this.queue.results(); +}; + +jasmine.Suite.prototype.add = function(suiteOrSpec) { + this.children_.push(suiteOrSpec); + if (suiteOrSpec instanceof jasmine.Suite) { + this.suites_.push(suiteOrSpec); + this.env.currentRunner().addSuite(suiteOrSpec); + } else { + this.specs_.push(suiteOrSpec); + } + this.queue.add(suiteOrSpec); +}; + +jasmine.Suite.prototype.specs = function() { + return this.specs_; +}; + +jasmine.Suite.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Suite.prototype.children = function() { + return this.children_; +}; + +jasmine.Suite.prototype.execute = function(onComplete) { + var self = this; + this.queue.start(function () { + self.finish(onComplete); + }); +}; +jasmine.WaitsBlock = function(env, timeout, spec) { + this.timeout = timeout; + jasmine.Block.call(this, env, null, spec); +}; + +jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); + +jasmine.WaitsBlock.prototype.execute = function (onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + } + this.env.setTimeout(function () { + onComplete(); + }, this.timeout); +}; +/** + * A block which waits for some condition to become true, with timeout. + * + * @constructor + * @extends jasmine.Block + * @param {jasmine.Env} env The Jasmine environment. + * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. + * @param {Function} latchFunction A function which returns true when the desired condition has been met. + * @param {String} message The message to display if the desired condition hasn't been met within the given time period. + * @param {jasmine.Spec} spec The Jasmine spec. + */ +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { + this.timeout = timeout || env.defaultTimeoutInterval; + this.latchFunction = latchFunction; + this.message = message; + this.totalTimeSpentWaitingForLatch = 0; + jasmine.Block.call(this, env, null, spec); +}; +jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); + +jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; + +jasmine.WaitsForBlock.prototype.execute = function(onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); + } + var latchFunctionResult; + try { + latchFunctionResult = this.latchFunction.apply(this.spec); + } catch (e) { + this.spec.fail(e); + onComplete(); + return; + } + + if (latchFunctionResult) { + onComplete(); + } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { + var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); + this.spec.fail({ + name: 'timeout', + message: message + }); + + this.abort = true; + onComplete(); + } else { + this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + var self = this; + this.env.setTimeout(function() { + self.execute(onComplete); + }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + } +}; +// Mock setTimeout, clearTimeout +// Contributed by Pivotal Computer Systems, www.pivotalsf.com + +jasmine.FakeTimer = function() { + this.reset(); + + var self = this; + self.setTimeout = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); + return self.timeoutsMade; + }; + + self.setInterval = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); + return self.timeoutsMade; + }; + + self.clearTimeout = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + + self.clearInterval = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + +}; + +jasmine.FakeTimer.prototype.reset = function() { + this.timeoutsMade = 0; + this.scheduledFunctions = {}; + this.nowMillis = 0; +}; + +jasmine.FakeTimer.prototype.tick = function(millis) { + var oldMillis = this.nowMillis; + var newMillis = oldMillis + millis; + this.runFunctionsWithinRange(oldMillis, newMillis); + this.nowMillis = newMillis; +}; + +jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { + var scheduledFunc; + var funcsToRun = []; + for (var timeoutKey in this.scheduledFunctions) { + scheduledFunc = this.scheduledFunctions[timeoutKey]; + if (scheduledFunc != jasmine.undefined && + scheduledFunc.runAtMillis >= oldMillis && + scheduledFunc.runAtMillis <= nowMillis) { + funcsToRun.push(scheduledFunc); + this.scheduledFunctions[timeoutKey] = jasmine.undefined; + } + } + + if (funcsToRun.length > 0) { + funcsToRun.sort(function(a, b) { + return a.runAtMillis - b.runAtMillis; + }); + for (var i = 0; i < funcsToRun.length; ++i) { + try { + var funcToRun = funcsToRun[i]; + this.nowMillis = funcToRun.runAtMillis; + funcToRun.funcToCall(); + if (funcToRun.recurring) { + this.scheduleFunction(funcToRun.timeoutKey, + funcToRun.funcToCall, + funcToRun.millis, + true); + } + } catch(e) { + } + } + this.runFunctionsWithinRange(oldMillis, nowMillis); + } +}; + +jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { + this.scheduledFunctions[timeoutKey] = { + runAtMillis: this.nowMillis + millis, + funcToCall: funcToCall, + recurring: recurring, + timeoutKey: timeoutKey, + millis: millis + }; +}; + +/** + * @namespace + */ +jasmine.Clock = { + defaultFakeTimer: new jasmine.FakeTimer(), + + reset: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.reset(); + }, + + tick: function(millis) { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.tick(millis); + }, + + runFunctionsWithinRange: function(oldMillis, nowMillis) { + jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); + }, + + scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { + jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); + }, + + useMock: function() { + if (!jasmine.Clock.isInstalled()) { + var spec = jasmine.getEnv().currentSpec; + spec.after(jasmine.Clock.uninstallMock); + + jasmine.Clock.installMock(); + } + }, + + installMock: function() { + jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; + }, + + uninstallMock: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.installed = jasmine.Clock.real; + }, + + real: { + setTimeout: jasmine.getGlobal().setTimeout, + clearTimeout: jasmine.getGlobal().clearTimeout, + setInterval: jasmine.getGlobal().setInterval, + clearInterval: jasmine.getGlobal().clearInterval + }, + + assertInstalled: function() { + if (!jasmine.Clock.isInstalled()) { + throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); + } + }, + + isInstalled: function() { + return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; + }, + + installed: null +}; +jasmine.Clock.installed = jasmine.Clock.real; + +//else for IE support +jasmine.getGlobal().setTimeout = function(funcToCall, millis) { + if (jasmine.Clock.installed.setTimeout.apply) { + return jasmine.Clock.installed.setTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.setTimeout(funcToCall, millis); + } +}; + +jasmine.getGlobal().setInterval = function(funcToCall, millis) { + if (jasmine.Clock.installed.setInterval.apply) { + return jasmine.Clock.installed.setInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.setInterval(funcToCall, millis); + } +}; + +jasmine.getGlobal().clearTimeout = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearTimeout(timeoutKey); + } +}; + +jasmine.getGlobal().clearInterval = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearInterval(timeoutKey); + } +}; + +jasmine.version_= { + "major": 1, + "minor": 1, + "build": 0, + "revision": 1315677058 +}; diff --git a/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine_favicon.png b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine_favicon.png new file mode 100644 index 0000000..218f3b4 Binary files /dev/null and b/test/node_modules/mongoose/node_modules/mongodb/node_modules/bson/tools/jasmine-1.1.0/jasmine_favicon.png differ diff --git a/test/node_modules/mongoose/node_modules/mongodb/package.json b/test/node_modules/mongoose/node_modules/mongodb/package.json new file mode 100644 index 0000000..a7164e5 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/package.json @@ -0,0 +1,222 @@ +{ + "name": "mongodb", + "description": "A node.js driver for MongoDB", + "keywords": [ + "mongodb", + "mongo", + "driver", + "db" + ], + "version": "1.2.8", + "author": { + "name": "Christian Amor Kvalheim", + "email": "christkv@gmail.com" + }, + "contributors": [ + { + "name": "Aaron Heckmann" + }, + { + "name": "Christoph Pojer" + }, + { + "name": "Pau Ramon Revilla" + }, + { + "name": "Nathan White" + }, + { + "name": "Emmerman" + }, + { + "name": "Seth LaForge" + }, + { + "name": "Boris Filipov" + }, + { + "name": "Stefan Schärmeli" + }, + { + "name": "Tedde Lundgren" + }, + { + "name": "renctan" + }, + { + "name": "Sergey Ukustov" + }, + { + "name": "Ciaran Jessup" + }, + { + "name": "kuno" + }, + { + "name": "srimonti" + }, + { + "name": "Erik Abele" + }, + { + "name": "Pratik Daga" + }, + { + "name": "Slobodan Utvic" + }, + { + "name": "Kristina Chodorow" + }, + { + "name": "Yonathan Randolph" + }, + { + "name": "Brian Noguchi" + }, + { + "name": "Sam Epstein" + }, + { + "name": "James Harrison Fisher" + }, + { + "name": "Vladimir Dronnikov" + }, + { + "name": "Ben Hockey" + }, + { + "name": "Henrik Johansson" + }, + { + "name": "Simon Weare" + }, + { + "name": "Alex Gorbatchev" + }, + { + "name": "Shimon Doodkin" + }, + { + "name": "Kyle Mueller" + }, + { + "name": "Eran Hammer-Lahav" + }, + { + "name": "Marcin Ciszak" + }, + { + "name": "François de Metz" + }, + { + "name": "Vinay Pulim" + }, + { + "name": "nstielau" + }, + { + "name": "Adam Wiggins" + }, + { + "name": "entrinzikyl" + }, + { + "name": "Jeremy Selier" + }, + { + "name": "Ian Millington" + }, + { + "name": "Public Keating" + }, + { + "name": "andrewjstone" + }, + { + "name": "Christopher Stott" + }, + { + "name": "Corey Jewett" + }, + { + "name": "brettkiefer" + }, + { + "name": "Rob Holland" + }, + { + "name": "Senmiao Liu" + }, + { + "name": "heroic" + }, + { + "name": "gitfy" + }, + { + "name": "Andrew Stone" + }, + { + "name": "John Le Drew" + }, + { + "name": "Lucasfilm Singapore" + }, + { + "name": "Roman Shtylman" + }, + { + "name": "Matt Self" + } + ], + "repository": { + "type": "git", + "url": "http://github.com/mongodb/node-mongodb-native.git" + }, + "bugs": { + "mail": "node-mongodb-native@googlegroups.com", + "url": "http://github.com/mongodb/node-mongodb-native/issues" + }, + "dependencies": { + "bson": "0.1.5" + }, + "devDependencies": { + "dox": "0.2.0", + "uglify-js": "1.2.5", + "ejs": "0.6.1", + "nodeunit": "0.7.4", + "github3": ">=0.3.0", + "markdown": "0.3.1", + "gleak": "0.2.3", + "step": "0.0.5", + "async": "0.1.22" + }, + "config": { + "native": false + }, + "main": "./lib/mongodb/index", + "homepage": "http://mongodb.github.com/node-mongodb-native/", + "directories": { + "lib": "./lib/mongodb" + }, + "engines": { + "node": ">=0.6.0" + }, + "scripts": { + "test": "make test_pure" + }, + "licenses": [ + { + "type": "Apache License, Version 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + ], + "readme": "Up to date documentation\n========================\n\n[Documentation](http://mongodb.github.com/node-mongodb-native/)\n\nInstall\n=======\n\nTo install the most recent release from npm, run:\n\n npm install mongodb\n\nThat may give you a warning telling you that bugs['web'] should be bugs['url'], it would be safe to ignore it (this has been fixed in the development version)\n\nTo install the latest from the repository, run::\n\n npm install path/to/node-mongodb-native\n\nCommunity\n=========\nCheck out the google group [node-mongodb-native](http://groups.google.com/group/node-mongodb-native) for questions/answers from users of the driver.\n\nTry it live\n============\n\n\nIntroduction\n============\n\nThis is a node.js driver for MongoDB. It's a port (or close to a port) of the library for ruby at http://github.com/mongodb/mongo-ruby-driver/.\n\nA simple example of inserting a document.\n\n```javascript\n var client = new Db('test', new Server(\"127.0.0.1\", 27017, {}), {w: 1}),\n test = function (err, collection) {\n collection.insert({a:2}, function(err, docs) {\n\n collection.count(function(err, count) {\n test.assertEquals(1, count);\n });\n\n // Locate all the entries using find\n collection.find().toArray(function(err, results) {\n test.assertEquals(1, results.length);\n test.assertTrue(results[0].a === 2);\n\n // Let's close the db\n client.close();\n });\n });\n };\n\n client.open(function(err, p_client) {\n client.collection('test_insert', test);\n });\n```\n\nData types\n==========\n\nTo store and retrieve the non-JSON MongoDb primitives ([ObjectID](http://www.mongodb.org/display/DOCS/Object+IDs), Long, Binary, [Timestamp](http://www.mongodb.org/display/DOCS/Timestamp+data+type), [DBRef](http://www.mongodb.org/display/DOCS/Database+References#DatabaseReferences-DBRef), Code).\n\nIn particular, every document has a unique `_id` which can be almost any type, and by default a 12-byte ObjectID is created. ObjectIDs can be represented as 24-digit hexadecimal strings, but you must convert the string back into an ObjectID before you can use it in the database. For example:\n\n```javascript\n // Get the objectID type\n var ObjectID = require('mongodb').ObjectID;\n\n var idString = '4e4e1638c85e808431000003';\n collection.findOne({_id: new ObjectID(idString)}, console.log) // ok\n collection.findOne({_id: idString}, console.log) // wrong! callback gets undefined\n```\n\nHere are the constructors the non-Javascript BSON primitive types:\n\n```javascript\n // Fetch the library\n var mongo = require('mongodb');\n // Create new instances of BSON types\n new mongo.Long(numberString)\n new mongo.ObjectID(hexString)\n new mongo.Timestamp() // the actual unique number is generated on insert.\n new mongo.DBRef(collectionName, id, dbName)\n new mongo.Binary(buffer) // takes a string or Buffer\n new mongo.Code(code, [context])\n new mongo.Symbol(string)\n new mongo.MinKey()\n new mongo.MaxKey()\n new mongo.Double(number)\t// Force double storage\n```\n\nThe C/C++ bson parser/serializer\n--------------------------------\n\nIf you are running a version of this library has the C/C++ parser compiled, to enable the driver to use the C/C++ bson parser pass it the option native_parser:true like below\n\n```javascript\n // using native_parser:\n var client = new Db('integration_tests_20',\n new Server(\"127.0.0.1\", 27017),\n {native_parser:true});\n```\n\nThe C++ parser uses the js objects both for serialization and deserialization.\n\nGitHub information\n==================\n\nThe source code is available at http://github.com/mongodb/node-mongodb-native.\nYou can either clone the repository or download a tarball of the latest release.\n\nOnce you have the source you can test the driver by running\n\n $ make test\n\nin the main directory. You will need to have a mongo instance running on localhost for the integration tests to pass.\n\nExamples\n========\n\nFor examples look in the examples/ directory. You can execute the examples using node.\n\n $ cd examples\n $ node queries.js\n\nGridStore\n=========\n\nThe GridStore class allows for storage of binary files in mongoDB using the mongoDB defined files and chunks collection definition.\n\nFor more information have a look at [Gridstore](https://github.com/mongodb/node-mongodb-native/blob/master/docs/gridfs.md)\n\nReplicasets\n===========\nFor more information about how to connect to a replicaset have a look at [Replicasets](https://github.com/mongodb/node-mongodb-native/blob/master/docs/replicaset.md)\n\nPrimary Key Factories\n---------------------\n\nDefining your own primary key factory allows you to generate your own series of id's\n(this could f.ex be to use something like ISBN numbers). The generated the id needs to be a 12 byte long \"string\".\n\nSimple example below\n\n```javascript\n // Custom factory (need to provide a 12 byte array);\n CustomPKFactory = function() {}\n CustomPKFactory.prototype = new Object();\n CustomPKFactory.createPk = function() {\n return new ObjectID(\"aaaaaaaaaaaa\");\n }\n\n var p_client = new Db('integration_tests_20', new Server(\"127.0.0.1\", 27017, {}), {'pk':CustomPKFactory});\n p_client.open(function(err, p_client) {\n p_client.dropDatabase(function(err, done) {\n p_client.createCollection('test_custom_key', function(err, collection) {\n collection.insert({'a':1}, function(err, docs) {\n collection.find({'_id':new ObjectID(\"aaaaaaaaaaaa\")}, function(err, cursor) {\n cursor.toArray(function(err, items) {\n test.assertEquals(1, items.length);\n\n // Let's close the db\n p_client.close();\n });\n });\n });\n });\n });\n });\n```\n\nStrict mode\n-----------\n\nEach database has an optional strict mode. If it is set then asking for a collection\nthat does not exist will return an Error object in the callback. Similarly if you\nattempt to create a collection that already exists. Strict is provided for convenience.\n\n```javascript\n var error_client = new Db('integration_tests_', new Server(\"127.0.0.1\", 27017, {auto_reconnect: false}), {strict:true});\n test.assertEquals(true, error_client.strict);\n\n error_client.open(function(err, error_client) {\n error_client.collection('does-not-exist', function(err, collection) {\n test.assertTrue(err instanceof Error);\n test.assertEquals(\"Collection does-not-exist does not exist. Currently in strict mode.\", err.message);\n });\n\n error_client.createCollection('test_strict_access_collection', function(err, collection) {\n error_client.collection('test_strict_access_collection', function(err, collection) {\n test.assertTrue(collection instanceof Collection);\n // Let's close the db\n error_client.close();\n });\n });\n });\n```\n\nDocumentation\n=============\n\nIf this document doesn't answer your questions, see the source of\n[Collection](https://github.com/mongodb/node-mongodb-native/blob/master/lib/mongodb/collection.js)\nor [Cursor](https://github.com/mongodb/node-mongodb-native/blob/master/lib/mongodb/cursor.js),\nor the documentation at MongoDB for query and update formats.\n\nFind\n----\n\nThe find method is actually a factory method to create\nCursor objects. A Cursor lazily uses the connection the first time\nyou call `nextObject`, `each`, or `toArray`.\n\nThe basic operation on a cursor is the `nextObject` method\nthat fetches the next matching document from the database. The convenience\nmethods `each` and `toArray` call `nextObject` until the cursor is exhausted.\n\nSignatures:\n\n```javascript\n var cursor = collection.find(query, [fields], options);\n cursor.sort(fields).limit(n).skip(m).\n\n cursor.nextObject(function(err, doc) {});\n cursor.each(function(err, doc) {});\n cursor.toArray(function(err, docs) {});\n\n cursor.rewind() // reset the cursor to its initial state.\n```\n\nUseful chainable methods of cursor. These can optionally be options of `find` instead of method calls:\n\n* `.limit(n).skip(m)` to control paging.\n* `.sort(fields)` Order by the given fields. There are several equivalent syntaxes:\n * `.sort({field1: -1, field2: 1})` descending by field1, then ascending by field2.\n * `.sort([['field1', 'desc'], ['field2', 'asc']])` same as above\n * `.sort([['field1', 'desc'], 'field2'])` same as above\n * `.sort('field1')` ascending by field1\n\nOther options of `find`:\n\n* `fields` the fields to fetch (to avoid transferring the entire document)\n* `tailable` if true, makes the cursor [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors).\n* `batchSize` The number of the subset of results to request the database\nto return for every request. This should initially be greater than 1 otherwise\nthe database will automatically close the cursor. The batch size can be set to 1\nwith `batchSize(n, function(err){})` after performing the initial query to the database.\n* `hint` See [Optimization: hint](http://www.mongodb.org/display/DOCS/Optimization#Optimization-Hint).\n* `explain` turns this into an explain query. You can also call\n`explain()` on any cursor to fetch the explanation.\n* `snapshot` prevents documents that are updated while the query is active\nfrom being returned multiple times. See more\n[details about query snapshots](http://www.mongodb.org/display/DOCS/How+to+do+Snapshotted+Queries+in+the+Mongo+Database).\n* `timeout` if false, asks MongoDb not to time out this cursor after an\ninactivity period.\n\n\nFor information on how to create queries, see the\n[MongoDB section on querying](http://www.mongodb.org/display/DOCS/Querying).\n\n```javascript\n var mongodb = require('mongodb');\n var server = new mongodb.Server(\"127.0.0.1\", 27017, {});\n new mongodb.Db('test', server, {}).open(function (error, client) {\n if (error) throw error;\n var collection = new mongodb.Collection(client, 'test_collection');\n collection.find({}, {limit:10}).toArray(function(err, docs) {\n console.dir(docs);\n });\n });\n```\n\nInsert\n------\n\nSignature:\n\n```javascript\n collection.insert(docs, options, [callback]);\n```\n\nwhere `docs` can be a single document or an array of documents.\n\nUseful options:\n\n* `safe:true` Should always set if you have a callback.\n\nSee also: [MongoDB docs for insert](http://www.mongodb.org/display/DOCS/Inserting).\n\n```javascript\n var mongodb = require('mongodb');\n var server = new mongodb.Server(\"127.0.0.1\", 27017, {});\n new mongodb.Db('test', server, {w: 1}).open(function (error, client) {\n if (error) throw error;\n var collection = new mongodb.Collection(client, 'test_collection');\n collection.insert({hello: 'world'}, {safe:true},\n function(err, objects) {\n if (err) console.warn(err.message);\n if (err && err.message.indexOf('E11000 ') !== -1) {\n // this _id was already inserted in the database\n }\n });\n });\n```\n\nNote that there's no reason to pass a callback to the insert or update commands\nunless you use the `safe:true` option. If you don't specify `safe:true`, then\nyour callback will be called immediately.\n\nUpdate; update and insert (upsert)\n----------------------------------\n\nThe update operation will update the first document that matches your query\n(or all documents that match if you use `multi:true`).\nIf `safe:true`, `upsert` is not set, and no documents match, your callback will return 0 documents updated.\n\nSee the [MongoDB docs](http://www.mongodb.org/display/DOCS/Updating) for\nthe modifier (`$inc`, `$set`, `$push`, etc.) formats.\n\nSignature:\n\n```javascript\n collection.update(criteria, objNew, options, [callback]);\n```\n\nUseful options:\n\n* `safe:true` Should always set if you have a callback.\n* `multi:true` If set, all matching documents are updated, not just the first.\n* `upsert:true` Atomically inserts the document if no documents matched.\n\nExample for `update`:\n\n```javascript\n var mongodb = require('mongodb');\n var server = new mongodb.Server(\"127.0.0.1\", 27017, {});\n new mongodb.Db('test', server, {w: 1}).open(function (error, client) {\n if (error) throw error;\n var collection = new mongodb.Collection(client, 'test_collection');\n collection.update({hi: 'here'}, {$set: {hi: 'there'}}, {safe:true},\n function(err) {\n if (err) console.warn(err.message);\n else console.log('successfully updated');\n });\n });\n```\n\nFind and modify\n---------------\n\n`findAndModify` is like `update`, but it also gives the updated document to\nyour callback. But there are a few key differences between findAndModify and\nupdate:\n\n 1. The signatures differ.\n 2. You can only findAndModify a single item, not multiple items.\n\nSignature:\n\n```javascript\n collection.findAndModify(query, sort, update, options, callback)\n```\n\nThe sort parameter is used to specify which object to operate on, if more than\none document matches. It takes the same format as the cursor sort (see\nConnection.find above).\n\nSee the\n[MongoDB docs for findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command)\nfor more details.\n\nUseful options:\n\n* `remove:true` set to a true to remove the object before returning\n* `new:true` set to true if you want to return the modified object rather than the original. Ignored for remove.\n* `upsert:true` Atomically inserts the document if no documents matched.\n\nExample for `findAndModify`:\n\n```javascript\n var mongodb = require('mongodb');\n var server = new mongodb.Server(\"127.0.0.1\", 27017, {});\n new mongodb.Db('test', server, {w: 1}).open(function (error, client) {\n if (error) throw error;\n var collection = new mongodb.Collection(client, 'test_collection');\n collection.findAndModify({hello: 'world'}, [['_id','asc']], {$set: {hi: 'there'}}, {},\n function(err, object) {\n if (err) console.warn(err.message);\n else console.dir(object); // undefined if no matching object exists.\n });\n });\n```\n\nSave\n----\n\nThe `save` method is a shorthand for upsert if the document contains an\n`_id`, or an insert if there is no `_id`.\n\nSponsors\n========\nJust as Felix Geisendörfer I'm also working on the driver for my own startup and this driver is a big project that also benefits other companies who are using MongoDB.\n\nIf your company could benefit from a even better-engineered node.js mongodb driver I would appreciate any type of sponsorship you may be able to provide. All the sponsors will get a lifetime display in this readme, priority support and help on problems and votes on the roadmap decisions for the driver. If you are interested contact me on [christkv AT g m a i l.com](mailto:christkv@gmail.com) for details.\n\nAnd I'm very thankful for code contributions. If you are interested in working on features please contact me so we can discuss API design and testing.\n\nRelease Notes\n=============\n\nSee HISTORY\n\nCredits\n=======\n\n1. [10gen](http://github.com/mongodb/mongo-ruby-driver/)\n2. [Google Closure Library](http://code.google.com/closure/library/)\n3. [Jonas Raoni Soares Silva](http://jsfromhell.com/classes/binary-parser)\n\nContributors\n============\n\nAaron Heckmann, Christoph Pojer, Pau Ramon Revilla, Nathan White, Emmerman, Seth LaForge, Boris Filipov, Stefan Schärmeli, Tedde Lundgren, renctan, Sergey Ukustov, Ciaran Jessup, kuno, srimonti, Erik Abele, Pratik Daga, Slobodan Utvic, Kristina Chodorow, Yonathan Randolph, Brian Noguchi, Sam Epstein, James Harrison Fisher, Vladimir Dronnikov, Ben Hockey, Henrik Johansson, Simon Weare, Alex Gorbatchev, Shimon Doodkin, Kyle Mueller, Eran Hammer-Lahav, Marcin Ciszak, François de Metz, Vinay Pulim, nstielau, Adam Wiggins, entrinzikyl, Jeremy Selier, Ian Millington, Public Keating, andrewjstone, Christopher Stott, Corey Jewett, brettkiefer, Rob Holland, Senmiao Liu, heroic, gitfy\n\nLicense\n=======\n\n Copyright 2009 - 2012 Christian Amor Kvalheim.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n", + "readmeFilename": "Readme.md", + "_id": "mongodb@1.2.8", + "dist": { + "shasum": "dc46547fef7cd09b13c8409392207089cfc51a94" + }, + "_from": "mongodb@1.2.8" +} diff --git a/test/node_modules/mongoose/node_modules/mongodb/upload.py b/test/node_modules/mongoose/node_modules/mongodb/upload.py new file mode 100644 index 0000000..6296fde --- /dev/null +++ b/test/node_modules/mongoose/node_modules/mongodb/upload.py @@ -0,0 +1,2347 @@ +#!/usr/bin/env python +# coding: utf-8 +# +# Copyright 2007 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tool for uploading diffs from a version control system to the codereview app. + +Usage summary: upload.py [options] [-- diff_options] [path...] + +Diff options are passed to the diff command of the underlying system. + +Supported version control systems: + Git + Mercurial + Subversion + Perforce + CVS + +It is important for Git/Mercurial users to specify a tree/node/branch to diff +against by using the '--rev' option. +""" +# This code is derived from appcfg.py in the App Engine SDK (open source), +# and from ASPN recipe #146306. + +import ConfigParser +import cookielib +import errno +import fnmatch +import getpass +import logging +import marshal +import mimetypes +import optparse +import os +import re +import socket +import subprocess +import sys +import urllib +import urllib2 +import urlparse + +# The md5 module was deprecated in Python 2.5. +try: + from hashlib import md5 +except ImportError: + from md5 import md5 + +try: + import readline +except ImportError: + pass + +try: + import keyring +except ImportError: + keyring = None + +# The logging verbosity: +# 0: Errors only. +# 1: Status messages. +# 2: Info logs. +# 3: Debug logs. +verbosity = 1 + +# The account type used for authentication. +# This line could be changed by the review server (see handler for +# upload.py). +AUTH_ACCOUNT_TYPE = "HOSTED" + +# URL of the default review server. As for AUTH_ACCOUNT_TYPE, this line could be +# changed by the review server (see handler for upload.py). +DEFAULT_REVIEW_SERVER = "codereview.10gen.com" + +# Max size of patch or base file. +MAX_UPLOAD_SIZE = 900 * 1024 + +# Constants for version control names. Used by GuessVCSName. +VCS_GIT = "Git" +VCS_MERCURIAL = "Mercurial" +VCS_SUBVERSION = "Subversion" +VCS_PERFORCE = "Perforce" +VCS_CVS = "CVS" +VCS_UNKNOWN = "Unknown" + +VCS_ABBREVIATIONS = { + VCS_MERCURIAL.lower(): VCS_MERCURIAL, + "hg": VCS_MERCURIAL, + VCS_SUBVERSION.lower(): VCS_SUBVERSION, + "svn": VCS_SUBVERSION, + VCS_PERFORCE.lower(): VCS_PERFORCE, + "p4": VCS_PERFORCE, + VCS_GIT.lower(): VCS_GIT, + VCS_CVS.lower(): VCS_CVS, +} + +# The result of parsing Subversion's [auto-props] setting. +svn_auto_props_map = None + +def GetEmail(prompt): + """Prompts the user for their email address and returns it. + + The last used email address is saved to a file and offered up as a suggestion + to the user. If the user presses enter without typing in anything the last + used email address is used. If the user enters a new address, it is saved + for next time we prompt. + + """ + last_email_file_name = os.path.expanduser("~/.last_codereview_email_address") + last_email = "" + if os.path.exists(last_email_file_name): + try: + last_email_file = open(last_email_file_name, "r") + last_email = last_email_file.readline().strip("\n") + last_email_file.close() + prompt += " [%s]" % last_email + except IOError, e: + pass + email = raw_input(prompt + ": ").strip() + if email: + try: + last_email_file = open(last_email_file_name, "w") + last_email_file.write(email) + last_email_file.close() + except IOError, e: + pass + else: + email = last_email + return email + + +def StatusUpdate(msg): + """Print a status message to stdout. + + If 'verbosity' is greater than 0, print the message. + + Args: + msg: The string to print. + """ + if verbosity > 0: + print msg + + +def ErrorExit(msg): + """Print an error message to stderr and exit.""" + print >>sys.stderr, msg + sys.exit(1) + + +class ClientLoginError(urllib2.HTTPError): + """Raised to indicate there was an error authenticating with ClientLogin.""" + + def __init__(self, url, code, msg, headers, args): + urllib2.HTTPError.__init__(self, url, code, msg, headers, None) + self.args = args + self.reason = args["Error"] + self.info = args.get("Info", None) + + +class AbstractRpcServer(object): + """Provides a common interface for a simple RPC server.""" + + def __init__(self, host, auth_function, host_override=None, extra_headers={}, + save_cookies=False, account_type=AUTH_ACCOUNT_TYPE): + """Creates a new HttpRpcServer. + + Args: + host: The host to send requests to. + auth_function: A function that takes no arguments and returns an + (email, password) tuple when called. Will be called if authentication + is required. + host_override: The host header to send to the server (defaults to host). + extra_headers: A dict of extra headers to append to every request. + save_cookies: If True, save the authentication cookies to local disk. + If False, use an in-memory cookiejar instead. Subclasses must + implement this functionality. Defaults to False. + account_type: Account type used for authentication. Defaults to + AUTH_ACCOUNT_TYPE. + """ + self.host = host + if (not self.host.startswith("http://") and + not self.host.startswith("https://")): + self.host = "http://" + self.host + self.host_override = host_override + self.auth_function = auth_function + self.authenticated = False + self.extra_headers = extra_headers + self.save_cookies = save_cookies + self.account_type = account_type + self.opener = self._GetOpener() + if self.host_override: + logging.info("Server: %s; Host: %s", self.host, self.host_override) + else: + logging.info("Server: %s", self.host) + + def _GetOpener(self): + """Returns an OpenerDirector for making HTTP requests. + + Returns: + A urllib2.OpenerDirector object. + """ + raise NotImplementedError() + + def _CreateRequest(self, url, data=None): + """Creates a new urllib request.""" + logging.debug("Creating request for: '%s' with payload:\n%s", url, data) + req = urllib2.Request(url, data=data, headers={"Accept": "text/plain"}) + if self.host_override: + req.add_header("Host", self.host_override) + for key, value in self.extra_headers.iteritems(): + req.add_header(key, value) + return req + + def _GetAuthToken(self, email, password): + """Uses ClientLogin to authenticate the user, returning an auth token. + + Args: + email: The user's email address + password: The user's password + + Raises: + ClientLoginError: If there was an error authenticating with ClientLogin. + HTTPError: If there was some other form of HTTP error. + + Returns: + The authentication token returned by ClientLogin. + """ + account_type = self.account_type + if self.host.endswith(".google.com"): + # Needed for use inside Google. + account_type = "HOSTED" + req = self._CreateRequest( + url="https://www.google.com/accounts/ClientLogin", + data=urllib.urlencode({ + "Email": email, + "Passwd": password, + "service": "ah", + "source": "rietveld-codereview-upload", + "accountType": account_type, + }), + ) + try: + response = self.opener.open(req) + response_body = response.read() + response_dict = dict(x.split("=") + for x in response_body.split("\n") if x) + return response_dict["Auth"] + except urllib2.HTTPError, e: + if e.code == 403: + body = e.read() + response_dict = dict(x.split("=", 1) for x in body.split("\n") if x) + raise ClientLoginError(req.get_full_url(), e.code, e.msg, + e.headers, response_dict) + else: + raise + + def _GetAuthCookie(self, auth_token): + """Fetches authentication cookies for an authentication token. + + Args: + auth_token: The authentication token returned by ClientLogin. + + Raises: + HTTPError: If there was an error fetching the authentication cookies. + """ + # This is a dummy value to allow us to identify when we're successful. + continue_location = "http://localhost/" + args = {"continue": continue_location, "auth": auth_token} + req = self._CreateRequest("%s/_ah/login?%s" % + (self.host, urllib.urlencode(args))) + try: + response = self.opener.open(req) + except urllib2.HTTPError, e: + response = e + if (response.code != 302 or + response.info()["location"] != continue_location): + raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, + response.headers, response.fp) + self.authenticated = True + + def _Authenticate(self): + """Authenticates the user. + + The authentication process works as follows: + 1) We get a username and password from the user + 2) We use ClientLogin to obtain an AUTH token for the user + (see http://code.google.com/apis/accounts/AuthForInstalledApps.html). + 3) We pass the auth token to /_ah/login on the server to obtain an + authentication cookie. If login was successful, it tries to redirect + us to the URL we provided. + + If we attempt to access the upload API without first obtaining an + authentication cookie, it returns a 401 response (or a 302) and + directs us to authenticate ourselves with ClientLogin. + """ + for i in range(3): + credentials = self.auth_function() + try: + auth_token = self._GetAuthToken(credentials[0], credentials[1]) + except ClientLoginError, e: + print >>sys.stderr, '' + if e.reason == "BadAuthentication": + if e.info == "InvalidSecondFactor": + print >>sys.stderr, ( + "Use an application-specific password instead " + "of your regular account password.\n" + "See http://www.google.com/" + "support/accounts/bin/answer.py?answer=185833") + else: + print >>sys.stderr, "Invalid username or password." + elif e.reason == "CaptchaRequired": + print >>sys.stderr, ( + "Please go to\n" + "https://www.google.com/accounts/DisplayUnlockCaptcha\n" + "and verify you are a human. Then try again.\n" + "If you are using a Google Apps account the URL is:\n" + "https://www.google.com/a/yourdomain.com/UnlockCaptcha") + elif e.reason == "NotVerified": + print >>sys.stderr, "Account not verified." + elif e.reason == "TermsNotAgreed": + print >>sys.stderr, "User has not agreed to TOS." + elif e.reason == "AccountDeleted": + print >>sys.stderr, "The user account has been deleted." + elif e.reason == "AccountDisabled": + print >>sys.stderr, "The user account has been disabled." + break + elif e.reason == "ServiceDisabled": + print >>sys.stderr, ("The user's access to the service has been " + "disabled.") + elif e.reason == "ServiceUnavailable": + print >>sys.stderr, "The service is not available; try again later." + else: + # Unknown error. + raise + print >>sys.stderr, '' + continue + self._GetAuthCookie(auth_token) + return + + def Send(self, request_path, payload=None, + content_type="application/octet-stream", + timeout=None, + extra_headers=None, + **kwargs): + """Sends an RPC and returns the response. + + Args: + request_path: The path to send the request to, eg /api/appversion/create. + payload: The body of the request, or None to send an empty request. + content_type: The Content-Type header to use. + timeout: timeout in seconds; default None i.e. no timeout. + (Note: for large requests on OS X, the timeout doesn't work right.) + extra_headers: Dict containing additional HTTP headers that should be + included in the request (string header names mapped to their values), + or None to not include any additional headers. + kwargs: Any keyword arguments are converted into query string parameters. + + Returns: + The response body, as a string. + """ + # TODO: Don't require authentication. Let the server say + # whether it is necessary. + if not self.authenticated: + self._Authenticate() + + old_timeout = socket.getdefaulttimeout() + socket.setdefaulttimeout(timeout) + try: + tries = 0 + while True: + tries += 1 + args = dict(kwargs) + url = "%s%s" % (self.host, request_path) + if args: + url += "?" + urllib.urlencode(args) + req = self._CreateRequest(url=url, data=payload) + req.add_header("Content-Type", content_type) + if extra_headers: + for header, value in extra_headers.items(): + req.add_header(header, value) + try: + f = self.opener.open(req) + response = f.read() + f.close() + return response + except urllib2.HTTPError, e: + if tries > 3: + raise + elif e.code == 401 or e.code == 302: + self._Authenticate() + elif e.code == 301: + # Handle permanent redirect manually. + url = e.info()["location"] + url_loc = urlparse.urlparse(url) + self.host = '%s://%s' % (url_loc[0], url_loc[1]) + elif e.code >= 500: + ErrorExit(e.read()) + else: + raise + finally: + socket.setdefaulttimeout(old_timeout) + + +class HttpRpcServer(AbstractRpcServer): + """Provides a simplified RPC-style interface for HTTP requests.""" + + def _Authenticate(self): + """Save the cookie jar after authentication.""" + super(HttpRpcServer, self)._Authenticate() + if self.save_cookies: + StatusUpdate("Saving authentication cookies to %s" % self.cookie_file) + self.cookie_jar.save() + + def _GetOpener(self): + """Returns an OpenerDirector that supports cookies and ignores redirects. + + Returns: + A urllib2.OpenerDirector object. + """ + opener = urllib2.OpenerDirector() + opener.add_handler(urllib2.ProxyHandler()) + opener.add_handler(urllib2.UnknownHandler()) + opener.add_handler(urllib2.HTTPHandler()) + opener.add_handler(urllib2.HTTPDefaultErrorHandler()) + opener.add_handler(urllib2.HTTPSHandler()) + opener.add_handler(urllib2.HTTPErrorProcessor()) + if self.save_cookies: + self.cookie_file = os.path.expanduser("~/.codereview_upload_cookies") + self.cookie_jar = cookielib.MozillaCookieJar(self.cookie_file) + if os.path.exists(self.cookie_file): + try: + self.cookie_jar.load() + self.authenticated = True + StatusUpdate("Loaded authentication cookies from %s" % + self.cookie_file) + except (cookielib.LoadError, IOError): + # Failed to load cookies - just ignore them. + pass + else: + # Create an empty cookie file with mode 600 + fd = os.open(self.cookie_file, os.O_CREAT, 0600) + os.close(fd) + # Always chmod the cookie file + os.chmod(self.cookie_file, 0600) + else: + # Don't save cookies across runs of update.py. + self.cookie_jar = cookielib.CookieJar() + opener.add_handler(urllib2.HTTPCookieProcessor(self.cookie_jar)) + return opener + + +class CondensedHelpFormatter(optparse.IndentedHelpFormatter): + """Frees more horizontal space by removing indentation from group + options and collapsing arguments between short and long, e.g. + '-o ARG, --opt=ARG' to -o --opt ARG""" + + def format_heading(self, heading): + return "%s:\n" % heading + + def format_option(self, option): + self.dedent() + res = optparse.HelpFormatter.format_option(self, option) + self.indent() + return res + + def format_option_strings(self, option): + self.set_long_opt_delimiter(" ") + optstr = optparse.HelpFormatter.format_option_strings(self, option) + optlist = optstr.split(", ") + if len(optlist) > 1: + if option.takes_value(): + # strip METAVAR from all but the last option + optlist = [x.split()[0] for x in optlist[:-1]] + optlist[-1:] + optstr = " ".join(optlist) + return optstr + + +parser = optparse.OptionParser( + usage="%prog [options] [-- diff_options] [path...]", + add_help_option=False, + formatter=CondensedHelpFormatter() +) +parser.add_option("-h", "--help", action="store_true", + help="Show this help message and exit.") +parser.add_option("-y", "--assume_yes", action="store_true", + dest="assume_yes", default=False, + help="Assume that the answer to yes/no questions is 'yes'.") +# Logging +group = parser.add_option_group("Logging options") +group.add_option("-q", "--quiet", action="store_const", const=0, + dest="verbose", help="Print errors only.") +group.add_option("-v", "--verbose", action="store_const", const=2, + dest="verbose", default=1, + help="Print info level logs.") +group.add_option("--noisy", action="store_const", const=3, + dest="verbose", help="Print all logs.") +group.add_option("--print_diffs", dest="print_diffs", action="store_true", + help="Print full diffs.") +# Review server +group = parser.add_option_group("Review server options") +group.add_option("-s", "--server", action="store", dest="server", + default=DEFAULT_REVIEW_SERVER, + metavar="SERVER", + help=("The server to upload to. The format is host[:port]. " + "Defaults to '%default'.")) +group.add_option("-e", "--email", action="store", dest="email", + metavar="EMAIL", default=None, + help="The username to use. Will prompt if omitted.") +group.add_option("-H", "--host", action="store", dest="host", + metavar="HOST", default=None, + help="Overrides the Host header sent with all RPCs.") +group.add_option("--no_cookies", action="store_false", + dest="save_cookies", default=True, + help="Do not save authentication cookies to local disk.") +group.add_option("--account_type", action="store", dest="account_type", + metavar="TYPE", default=AUTH_ACCOUNT_TYPE, + choices=["GOOGLE", "HOSTED"], + help=("Override the default account type " + "(defaults to '%default', " + "valid choices are 'GOOGLE' and 'HOSTED').")) +# Issue +group = parser.add_option_group("Issue options") +group.add_option("-t", "--title", action="store", dest="title", + help="New issue subject or new patch set title") +group.add_option("-m", "--message", action="store", dest="message", + default=None, + help="New issue description or new patch set message") +group.add_option("-F", "--file", action="store", dest="file", + default=None, help="Read the message above from file.") +group.add_option("-r", "--reviewers", action="store", dest="reviewers", + metavar="REVIEWERS", default=None, + help="Add reviewers (comma separated email addresses).") +group.add_option("--cc", action="store", dest="cc", + metavar="CC", default=None, + help="Add CC (comma separated email addresses).") +group.add_option("--private", action="store_true", dest="private", + default=False, + help="Make the issue restricted to reviewers and those CCed") +# Upload options +group = parser.add_option_group("Patch options") +group.add_option("-i", "--issue", type="int", action="store", + metavar="ISSUE", default=None, + help="Issue number to which to add. Defaults to new issue.") +group.add_option("--base_url", action="store", dest="base_url", default=None, + help="Base URL path for files (listed as \"Base URL\" when " + "viewing issue). If omitted, will be guessed automatically " + "for SVN repos and left blank for others.") +group.add_option("--download_base", action="store_true", + dest="download_base", default=False, + help="Base files will be downloaded by the server " + "(side-by-side diffs may not work on files with CRs).") +group.add_option("--rev", action="store", dest="revision", + metavar="REV", default=None, + help="Base revision/branch/tree to diff against. Use " + "rev1:rev2 range to review already committed changeset.") +group.add_option("--send_mail", action="store_true", + dest="send_mail", default=False, + help="Send notification email to reviewers.") +group.add_option("-p", "--send_patch", action="store_true", + dest="send_patch", default=False, + help="Same as --send_mail, but include diff as an " + "attachment, and prepend email subject with 'PATCH:'.") +group.add_option("--vcs", action="store", dest="vcs", + metavar="VCS", default=None, + help=("Version control system (optional, usually upload.py " + "already guesses the right VCS).")) +group.add_option("--emulate_svn_auto_props", action="store_true", + dest="emulate_svn_auto_props", default=False, + help=("Emulate Subversion's auto properties feature.")) +# Perforce-specific +group = parser.add_option_group("Perforce-specific options " + "(overrides P4 environment variables)") +group.add_option("--p4_port", action="store", dest="p4_port", + metavar="P4_PORT", default=None, + help=("Perforce server and port (optional)")) +group.add_option("--p4_changelist", action="store", dest="p4_changelist", + metavar="P4_CHANGELIST", default=None, + help=("Perforce changelist id")) +group.add_option("--p4_client", action="store", dest="p4_client", + metavar="P4_CLIENT", default=None, + help=("Perforce client/workspace")) +group.add_option("--p4_user", action="store", dest="p4_user", + metavar="P4_USER", default=None, + help=("Perforce user")) + +def GetRpcServer(server, email=None, host_override=None, save_cookies=True, + account_type=AUTH_ACCOUNT_TYPE): + """Returns an instance of an AbstractRpcServer. + + Args: + server: String containing the review server URL. + email: String containing user's email address. + host_override: If not None, string containing an alternate hostname to use + in the host header. + save_cookies: Whether authentication cookies should be saved to disk. + account_type: Account type for authentication, either 'GOOGLE' + or 'HOSTED'. Defaults to AUTH_ACCOUNT_TYPE. + + Returns: + A new AbstractRpcServer, on which RPC calls can be made. + """ + + rpc_server_class = HttpRpcServer + + # If this is the dev_appserver, use fake authentication. + host = (host_override or server).lower() + if re.match(r'(http://)?localhost([:/]|$)', host): + if email is None: + email = "test@example.com" + logging.info("Using debug user %s. Override with --email" % email) + server = rpc_server_class( + server, + lambda: (email, "password"), + host_override=host_override, + extra_headers={"Cookie": + 'dev_appserver_login="%s:False"' % email}, + save_cookies=save_cookies, + account_type=account_type) + # Don't try to talk to ClientLogin. + server.authenticated = True + return server + + def GetUserCredentials(): + """Prompts the user for a username and password.""" + # Create a local alias to the email variable to avoid Python's crazy + # scoping rules. + global keyring + local_email = email + if local_email is None: + local_email = GetEmail("Email (login for uploading to %s)" % server) + password = None + if keyring: + try: + password = keyring.get_password(host, local_email) + except: + # Sadly, we have to trap all errors here as + # gnomekeyring.IOError inherits from object. :/ + print "Failed to get password from keyring" + keyring = None + if password is not None: + print "Using password from system keyring." + else: + password = getpass.getpass("Password for %s: " % local_email) + if keyring: + answer = raw_input("Store password in system keyring?(y/N) ").strip() + if answer == "y": + keyring.set_password(host, local_email, password) + return (local_email, password) + + return rpc_server_class(server, + GetUserCredentials, + host_override=host_override, + save_cookies=save_cookies) + + +def EncodeMultipartFormData(fields, files): + """Encode form fields for multipart/form-data. + + Args: + fields: A sequence of (name, value) elements for regular form fields. + files: A sequence of (name, filename, value) elements for data to be + uploaded as files. + Returns: + (content_type, body) ready for httplib.HTTP instance. + + Source: + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 + """ + BOUNDARY = '-M-A-G-I-C---B-O-U-N-D-A-R-Y-' + CRLF = '\r\n' + lines = [] + for (key, value) in fields: + lines.append('--' + BOUNDARY) + lines.append('Content-Disposition: form-data; name="%s"' % key) + lines.append('') + if isinstance(value, unicode): + value = value.encode('utf-8') + lines.append(value) + for (key, filename, value) in files: + lines.append('--' + BOUNDARY) + lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)) + lines.append('Content-Type: %s' % GetContentType(filename)) + lines.append('') + if isinstance(value, unicode): + value = value.encode('utf-8') + lines.append(value) + lines.append('--' + BOUNDARY + '--') + lines.append('') + body = CRLF.join(lines) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + + +def GetContentType(filename): + """Helper to guess the content-type from the filename.""" + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' + + +# Use a shell for subcommands on Windows to get a PATH search. +use_shell = sys.platform.startswith("win") + +def RunShellWithReturnCodeAndStderr(command, print_output=False, + universal_newlines=True, + env=os.environ): + """Executes a command and returns the output from stdout, stderr and the return code. + + Args: + command: Command to execute. + print_output: If True, the output is printed to stdout. + If False, both stdout and stderr are ignored. + universal_newlines: Use universal_newlines flag (default: True). + + Returns: + Tuple (stdout, stderr, return code) + """ + logging.info("Running %s", command) + env = env.copy() + env['LC_MESSAGES'] = 'C' + p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + shell=use_shell, universal_newlines=universal_newlines, + env=env) + if print_output: + output_array = [] + while True: + line = p.stdout.readline() + if not line: + break + print line.strip("\n") + output_array.append(line) + output = "".join(output_array) + else: + output = p.stdout.read() + p.wait() + errout = p.stderr.read() + if print_output and errout: + print >>sys.stderr, errout + p.stdout.close() + p.stderr.close() + return output, errout, p.returncode + +def RunShellWithReturnCode(command, print_output=False, + universal_newlines=True, + env=os.environ): + """Executes a command and returns the output from stdout and the return code.""" + out, err, retcode = RunShellWithReturnCodeAndStderr(command, print_output, + universal_newlines, env) + return out, retcode + +def RunShell(command, silent_ok=False, universal_newlines=True, + print_output=False, env=os.environ): + data, retcode = RunShellWithReturnCode(command, print_output, + universal_newlines, env) + if retcode: + ErrorExit("Got error status from %s:\n%s" % (command, data)) + if not silent_ok and not data: + ErrorExit("No output from %s" % command) + return data + + +class VersionControlSystem(object): + """Abstract base class providing an interface to the VCS.""" + + def __init__(self, options): + """Constructor. + + Args: + options: Command line options. + """ + self.options = options + + def GetGUID(self): + """Return string to distinguish the repository from others, for example to + query all opened review issues for it""" + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + def PostProcessDiff(self, diff): + """Return the diff with any special post processing this VCS needs, e.g. + to include an svn-style "Index:".""" + return diff + + def GenerateDiff(self, args): + """Return the current diff as a string. + + Args: + args: Extra arguments to pass to the diff command. + """ + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + def GetUnknownFiles(self): + """Return a list of files unknown to the VCS.""" + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + def CheckForUnknownFiles(self): + """Show an "are you sure?" prompt if there are unknown files.""" + unknown_files = self.GetUnknownFiles() + if unknown_files: + print "The following files are not added to version control:" + for line in unknown_files: + print line + prompt = "Are you sure to continue?(y/N) " + answer = raw_input(prompt).strip() + if answer != "y": + ErrorExit("User aborted") + + def GetBaseFile(self, filename): + """Get the content of the upstream version of a file. + + Returns: + A tuple (base_content, new_content, is_binary, status) + base_content: The contents of the base file. + new_content: For text files, this is empty. For binary files, this is + the contents of the new file, since the diff output won't contain + information to reconstruct the current file. + is_binary: True iff the file is binary. + status: The status of the file. + """ + + raise NotImplementedError( + "abstract method -- subclass %s must override" % self.__class__) + + + def GetBaseFiles(self, diff): + """Helper that calls GetBase file for each file in the patch. + + Returns: + A dictionary that maps from filename to GetBaseFile's tuple. Filenames + are retrieved based on lines that start with "Index:" or + "Property changes on:". + """ + files = {} + for line in diff.splitlines(True): + if line.startswith('Index:') or line.startswith('Property changes on:'): + unused, filename = line.split(':', 1) + # On Windows if a file has property changes its filename uses '\' + # instead of '/'. + filename = filename.strip().replace('\\', '/') + files[filename] = self.GetBaseFile(filename) + return files + + + def UploadBaseFiles(self, issue, rpc_server, patch_list, patchset, options, + files): + """Uploads the base files (and if necessary, the current ones as well).""" + + def UploadFile(filename, file_id, content, is_binary, status, is_base): + """Uploads a file to the server.""" + file_too_large = False + if is_base: + type = "base" + else: + type = "current" + if len(content) > MAX_UPLOAD_SIZE: + print ("Not uploading the %s file for %s because it's too large." % + (type, filename)) + file_too_large = True + content = "" + checksum = md5(content).hexdigest() + if options.verbose > 0 and not file_too_large: + print "Uploading %s file for %s" % (type, filename) + url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id) + form_fields = [("filename", filename), + ("status", status), + ("checksum", checksum), + ("is_binary", str(is_binary)), + ("is_current", str(not is_base)), + ] + if file_too_large: + form_fields.append(("file_too_large", "1")) + if options.email: + form_fields.append(("user", options.email)) + ctype, body = EncodeMultipartFormData(form_fields, + [("data", filename, content)]) + response_body = rpc_server.Send(url, body, + content_type=ctype) + if not response_body.startswith("OK"): + StatusUpdate(" --> %s" % response_body) + sys.exit(1) + + patches = dict() + [patches.setdefault(v, k) for k, v in patch_list] + for filename in patches.keys(): + base_content, new_content, is_binary, status = files[filename] + file_id_str = patches.get(filename) + if file_id_str.find("nobase") != -1: + base_content = None + file_id_str = file_id_str[file_id_str.rfind("_") + 1:] + file_id = int(file_id_str) + if base_content != None: + UploadFile(filename, file_id, base_content, is_binary, status, True) + if new_content != None: + UploadFile(filename, file_id, new_content, is_binary, status, False) + + def IsImage(self, filename): + """Returns true if the filename has an image extension.""" + mimetype = mimetypes.guess_type(filename)[0] + if not mimetype: + return False + return mimetype.startswith("image/") + + def IsBinaryData(self, data): + """Returns true if data contains a null byte.""" + # Derived from how Mercurial's heuristic, see + # http://selenic.com/hg/file/848a6658069e/mercurial/util.py#l229 + return bool(data and "\0" in data) + + +class SubversionVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for Subversion.""" + + def __init__(self, options): + super(SubversionVCS, self).__init__(options) + if self.options.revision: + match = re.match(r"(\d+)(:(\d+))?", self.options.revision) + if not match: + ErrorExit("Invalid Subversion revision %s." % self.options.revision) + self.rev_start = match.group(1) + self.rev_end = match.group(3) + else: + self.rev_start = self.rev_end = None + # Cache output from "svn list -r REVNO dirname". + # Keys: dirname, Values: 2-tuple (ouput for start rev and end rev). + self.svnls_cache = {} + # Base URL is required to fetch files deleted in an older revision. + # Result is cached to not guess it over and over again in GetBaseFile(). + required = self.options.download_base or self.options.revision is not None + self.svn_base = self._GuessBase(required) + + def GetGUID(self): + return self._GetInfo("Repository UUID") + + def GuessBase(self, required): + """Wrapper for _GuessBase.""" + return self.svn_base + + def _GuessBase(self, required): + """Returns base URL for current diff. + + Args: + required: If true, exits if the url can't be guessed, otherwise None is + returned. + """ + url = self._GetInfo("URL") + if url: + scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) + guess = "" + # TODO(anatoli) - repository specific hacks should be handled by server + if netloc == "svn.python.org" and scheme == "svn+ssh": + path = "projects" + path + scheme = "http" + guess = "Python " + elif netloc.endswith(".googlecode.com"): + scheme = "http" + guess = "Google Code " + path = path + "/" + base = urlparse.urlunparse((scheme, netloc, path, params, + query, fragment)) + logging.info("Guessed %sbase = %s", guess, base) + return base + if required: + ErrorExit("Can't find URL in output from svn info") + return None + + def _GetInfo(self, key): + """Parses 'svn info' for current dir. Returns value for key or None""" + for line in RunShell(["svn", "info"]).splitlines(): + if line.startswith(key + ": "): + return line.split(":", 1)[1].strip() + + def _EscapeFilename(self, filename): + """Escapes filename for SVN commands.""" + if "@" in filename and not filename.endswith("@"): + filename = "%s@" % filename + return filename + + def GenerateDiff(self, args): + cmd = ["svn", "diff"] + if self.options.revision: + cmd += ["-r", self.options.revision] + cmd.extend(args) + data = RunShell(cmd) + count = 0 + for line in data.splitlines(): + if line.startswith("Index:") or line.startswith("Property changes on:"): + count += 1 + logging.info(line) + if not count: + ErrorExit("No valid patches found in output from svn diff") + return data + + def _CollapseKeywords(self, content, keyword_str): + """Collapses SVN keywords.""" + # svn cat translates keywords but svn diff doesn't. As a result of this + # behavior patching.PatchChunks() fails with a chunk mismatch error. + # This part was originally written by the Review Board development team + # who had the same problem (http://reviews.review-board.org/r/276/). + # Mapping of keywords to known aliases + svn_keywords = { + # Standard keywords + 'Date': ['Date', 'LastChangedDate'], + 'Revision': ['Revision', 'LastChangedRevision', 'Rev'], + 'Author': ['Author', 'LastChangedBy'], + 'HeadURL': ['HeadURL', 'URL'], + 'Id': ['Id'], + + # Aliases + 'LastChangedDate': ['LastChangedDate', 'Date'], + 'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'], + 'LastChangedBy': ['LastChangedBy', 'Author'], + 'URL': ['URL', 'HeadURL'], + } + + def repl(m): + if m.group(2): + return "$%s::%s$" % (m.group(1), " " * len(m.group(3))) + return "$%s$" % m.group(1) + keywords = [keyword + for name in keyword_str.split(" ") + for keyword in svn_keywords.get(name, [])] + return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content) + + def GetUnknownFiles(self): + status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True) + unknown_files = [] + for line in status.split("\n"): + if line and line[0] == "?": + unknown_files.append(line) + return unknown_files + + def ReadFile(self, filename): + """Returns the contents of a file.""" + file = open(filename, 'rb') + result = "" + try: + result = file.read() + finally: + file.close() + return result + + def GetStatus(self, filename): + """Returns the status of a file.""" + if not self.options.revision: + status = RunShell(["svn", "status", "--ignore-externals", + self._EscapeFilename(filename)]) + if not status: + ErrorExit("svn status returned no output for %s" % filename) + status_lines = status.splitlines() + # If file is in a cl, the output will begin with + # "\n--- Changelist 'cl_name':\n". See + # http://svn.collab.net/repos/svn/trunk/notes/changelist-design.txt + if (len(status_lines) == 3 and + not status_lines[0] and + status_lines[1].startswith("--- Changelist")): + status = status_lines[2] + else: + status = status_lines[0] + # If we have a revision to diff against we need to run "svn list" + # for the old and the new revision and compare the results to get + # the correct status for a file. + else: + dirname, relfilename = os.path.split(filename) + if dirname not in self.svnls_cache: + cmd = ["svn", "list", "-r", self.rev_start, + self._EscapeFilename(dirname) or "."] + out, err, returncode = RunShellWithReturnCodeAndStderr(cmd) + if returncode: + # Directory might not yet exist at start revison + # svn: Unable to find repository location for 'abc' in revision nnn + if re.match('^svn: Unable to find repository location for .+ in revision \d+', err): + old_files = () + else: + ErrorExit("Failed to get status for %s:\n%s" % (filename, err)) + else: + old_files = out.splitlines() + args = ["svn", "list"] + if self.rev_end: + args += ["-r", self.rev_end] + cmd = args + [self._EscapeFilename(dirname) or "."] + out, returncode = RunShellWithReturnCode(cmd) + if returncode: + ErrorExit("Failed to run command %s" % cmd) + self.svnls_cache[dirname] = (old_files, out.splitlines()) + old_files, new_files = self.svnls_cache[dirname] + if relfilename in old_files and relfilename not in new_files: + status = "D " + elif relfilename in old_files and relfilename in new_files: + status = "M " + else: + status = "A " + return status + + def GetBaseFile(self, filename): + status = self.GetStatus(filename) + base_content = None + new_content = None + + # If a file is copied its status will be "A +", which signifies + # "addition-with-history". See "svn st" for more information. We need to + # upload the original file or else diff parsing will fail if the file was + # edited. + if status[0] == "A" and status[3] != "+": + # We'll need to upload the new content if we're adding a binary file + # since diff's output won't contain it. + mimetype = RunShell(["svn", "propget", "svn:mime-type", + self._EscapeFilename(filename)], silent_ok=True) + base_content = "" + is_binary = bool(mimetype) and not mimetype.startswith("text/") + if is_binary and self.IsImage(filename): + new_content = self.ReadFile(filename) + elif (status[0] in ("M", "D", "R") or + (status[0] == "A" and status[3] == "+") or # Copied file. + (status[0] == " " and status[1] == "M")): # Property change. + args = [] + if self.options.revision: + # filename must not be escaped. We already add an ampersand here. + url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) + else: + # Don't change filename, it's needed later. + url = filename + args += ["-r", "BASE"] + cmd = ["svn"] + args + ["propget", "svn:mime-type", url] + mimetype, returncode = RunShellWithReturnCode(cmd) + if returncode: + # File does not exist in the requested revision. + # Reset mimetype, it contains an error message. + mimetype = "" + else: + mimetype = mimetype.strip() + get_base = False + # this test for binary is exactly the test prescribed by the + # official SVN docs at + # http://subversion.apache.org/faq.html#binary-files + is_binary = (bool(mimetype) and + not mimetype.startswith("text/") and + mimetype not in ("image/x-xbitmap", "image/x-xpixmap")) + if status[0] == " ": + # Empty base content just to force an upload. + base_content = "" + elif is_binary: + if self.IsImage(filename): + get_base = True + if status[0] == "M": + if not self.rev_end: + new_content = self.ReadFile(filename) + else: + url = "%s/%s@%s" % (self.svn_base, filename, self.rev_end) + new_content = RunShell(["svn", "cat", url], + universal_newlines=True, silent_ok=True) + else: + base_content = "" + else: + get_base = True + + if get_base: + if is_binary: + universal_newlines = False + else: + universal_newlines = True + if self.rev_start: + # "svn cat -r REV delete_file.txt" doesn't work. cat requires + # the full URL with "@REV" appended instead of using "-r" option. + url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) + base_content = RunShell(["svn", "cat", url], + universal_newlines=universal_newlines, + silent_ok=True) + else: + base_content, ret_code = RunShellWithReturnCode( + ["svn", "cat", self._EscapeFilename(filename)], + universal_newlines=universal_newlines) + if ret_code and status[0] == "R": + # It's a replaced file without local history (see issue208). + # The base file needs to be fetched from the server. + url = "%s/%s" % (self.svn_base, filename) + base_content = RunShell(["svn", "cat", url], + universal_newlines=universal_newlines, + silent_ok=True) + elif ret_code: + ErrorExit("Got error status from 'svn cat %s'" % filename) + if not is_binary: + args = [] + if self.rev_start: + url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start) + else: + url = filename + args += ["-r", "BASE"] + cmd = ["svn"] + args + ["propget", "svn:keywords", url] + keywords, returncode = RunShellWithReturnCode(cmd) + if keywords and not returncode: + base_content = self._CollapseKeywords(base_content, keywords) + else: + StatusUpdate("svn status returned unexpected output: %s" % status) + sys.exit(1) + return base_content, new_content, is_binary, status[0:5] + + +class GitVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for Git.""" + + def __init__(self, options): + super(GitVCS, self).__init__(options) + # Map of filename -> (hash before, hash after) of base file. + # Hashes for "no such file" are represented as None. + self.hashes = {} + # Map of new filename -> old filename for renames. + self.renames = {} + + def GetGUID(self): + revlist = RunShell("git rev-list --parents HEAD".split()).splitlines() + # M-A: Return the 1st root hash, there could be multiple when a + # subtree is merged. In that case, more analysis would need to + # be done to figure out which HEAD is the 'most representative'. + for r in revlist: + if ' ' not in r: + return r + + def PostProcessDiff(self, gitdiff): + """Converts the diff output to include an svn-style "Index:" line as well + as record the hashes of the files, so we can upload them along with our + diff.""" + # Special used by git to indicate "no such content". + NULL_HASH = "0"*40 + + def IsFileNew(filename): + return filename in self.hashes and self.hashes[filename][0] is None + + def AddSubversionPropertyChange(filename): + """Add svn's property change information into the patch if given file is + new file. + + We use Subversion's auto-props setting to retrieve its property. + See http://svnbook.red-bean.com/en/1.1/ch07.html#svn-ch-7-sect-1.3.2 for + Subversion's [auto-props] setting. + """ + if self.options.emulate_svn_auto_props and IsFileNew(filename): + svnprops = GetSubversionPropertyChanges(filename) + if svnprops: + svndiff.append("\n" + svnprops + "\n") + + svndiff = [] + filecount = 0 + filename = None + for line in gitdiff.splitlines(): + match = re.match(r"diff --git a/(.*) b/(.*)$", line) + if match: + # Add auto property here for previously seen file. + if filename is not None: + AddSubversionPropertyChange(filename) + filecount += 1 + # Intentionally use the "after" filename so we can show renames. + filename = match.group(2) + svndiff.append("Index: %s\n" % filename) + if match.group(1) != match.group(2): + self.renames[match.group(2)] = match.group(1) + else: + # The "index" line in a git diff looks like this (long hashes elided): + # index 82c0d44..b2cee3f 100755 + # We want to save the left hash, as that identifies the base file. + match = re.match(r"index (\w+)\.\.(\w+)", line) + if match: + before, after = (match.group(1), match.group(2)) + if before == NULL_HASH: + before = None + if after == NULL_HASH: + after = None + self.hashes[filename] = (before, after) + svndiff.append(line + "\n") + if not filecount: + ErrorExit("No valid patches found in output from git diff") + # Add auto property for the last seen file. + assert filename is not None + AddSubversionPropertyChange(filename) + return "".join(svndiff) + + def GenerateDiff(self, extra_args): + extra_args = extra_args[:] + if self.options.revision: + if ":" in self.options.revision: + extra_args = self.options.revision.split(":", 1) + extra_args + else: + extra_args = [self.options.revision] + extra_args + + # --no-ext-diff is broken in some versions of Git, so try to work around + # this by overriding the environment (but there is still a problem if the + # git config key "diff.external" is used). + env = os.environ.copy() + if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF'] + return RunShell( + [ "git", "diff", "--no-color", "--no-ext-diff", "--full-index", + "--ignore-submodules", "-M"] + extra_args, + env=env) + + def GetUnknownFiles(self): + status = RunShell(["git", "ls-files", "--exclude-standard", "--others"], + silent_ok=True) + return status.splitlines() + + def GetFileContent(self, file_hash, is_binary): + """Returns the content of a file identified by its git hash.""" + data, retcode = RunShellWithReturnCode(["git", "show", file_hash], + universal_newlines=not is_binary) + if retcode: + ErrorExit("Got error status from 'git show %s'" % file_hash) + return data + + def GetBaseFile(self, filename): + hash_before, hash_after = self.hashes.get(filename, (None,None)) + base_content = None + new_content = None + status = None + + if filename in self.renames: + status = "A +" # Match svn attribute name for renames. + if filename not in self.hashes: + # If a rename doesn't change the content, we never get a hash. + base_content = RunShell( + ["git", "show", "HEAD:" + filename], silent_ok=True) + elif not hash_before: + status = "A" + base_content = "" + elif not hash_after: + status = "D" + else: + status = "M" + + is_binary = self.IsBinaryData(base_content) + is_image = self.IsImage(filename) + + # Grab the before/after content if we need it. + # We should include file contents if it's text or it's an image. + if not is_binary or is_image: + # Grab the base content if we don't have it already. + if base_content is None and hash_before: + base_content = self.GetFileContent(hash_before, is_binary) + # Only include the "after" file if it's an image; otherwise it + # it is reconstructed from the diff. + if is_image and hash_after: + new_content = self.GetFileContent(hash_after, is_binary) + + return (base_content, new_content, is_binary, status) + + +class CVSVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for CVS.""" + + def __init__(self, options): + super(CVSVCS, self).__init__(options) + + def GetGUID(self): + """For now we don't know how to get repository ID for CVS""" + return + + def GetOriginalContent_(self, filename): + RunShell(["cvs", "up", filename], silent_ok=True) + # TODO need detect file content encoding + content = open(filename).read() + return content.replace("\r\n", "\n") + + def GetBaseFile(self, filename): + base_content = None + new_content = None + status = "A" + + output, retcode = RunShellWithReturnCode(["cvs", "status", filename]) + if retcode: + ErrorExit("Got error status from 'cvs status %s'" % filename) + + if output.find("Status: Locally Modified") != -1: + status = "M" + temp_filename = "%s.tmp123" % filename + os.rename(filename, temp_filename) + base_content = self.GetOriginalContent_(filename) + os.rename(temp_filename, filename) + elif output.find("Status: Locally Added"): + status = "A" + base_content = "" + elif output.find("Status: Needs Checkout"): + status = "D" + base_content = self.GetOriginalContent_(filename) + + return (base_content, new_content, self.IsBinaryData(base_content), status) + + def GenerateDiff(self, extra_args): + cmd = ["cvs", "diff", "-u", "-N"] + if self.options.revision: + cmd += ["-r", self.options.revision] + + cmd.extend(extra_args) + data, retcode = RunShellWithReturnCode(cmd) + count = 0 + if retcode in [0, 1]: + for line in data.splitlines(): + if line.startswith("Index:"): + count += 1 + logging.info(line) + + if not count: + ErrorExit("No valid patches found in output from cvs diff") + + return data + + def GetUnknownFiles(self): + data, retcode = RunShellWithReturnCode(["cvs", "diff"]) + if retcode not in [0, 1]: + ErrorExit("Got error status from 'cvs diff':\n%s" % (data,)) + unknown_files = [] + for line in data.split("\n"): + if line and line[0] == "?": + unknown_files.append(line) + return unknown_files + +class MercurialVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for Mercurial.""" + + def __init__(self, options, repo_dir): + super(MercurialVCS, self).__init__(options) + # Absolute path to repository (we can be in a subdir) + self.repo_dir = os.path.normpath(repo_dir) + # Compute the subdir + cwd = os.path.normpath(os.getcwd()) + assert cwd.startswith(self.repo_dir) + self.subdir = cwd[len(self.repo_dir):].lstrip(r"\/") + if self.options.revision: + self.base_rev = self.options.revision + else: + self.base_rev = RunShell(["hg", "parent", "-q"]).split(':')[1].strip() + + def GetGUID(self): + # See chapter "Uniquely identifying a repository" + # http://hgbook.red-bean.com/read/customizing-the-output-of-mercurial.html + info = RunShell("hg log -r0 --template {node}".split()) + return info.strip() + + def _GetRelPath(self, filename): + """Get relative path of a file according to the current directory, + given its logical path in the repo.""" + absname = os.path.join(self.repo_dir, filename) + return os.path.relpath(absname) + + def GenerateDiff(self, extra_args): + cmd = ["hg", "diff", "--git", "-r", self.base_rev] + extra_args + data = RunShell(cmd, silent_ok=True) + svndiff = [] + filecount = 0 + for line in data.splitlines(): + m = re.match("diff --git a/(\S+) b/(\S+)", line) + if m: + # Modify line to make it look like as it comes from svn diff. + # With this modification no changes on the server side are required + # to make upload.py work with Mercurial repos. + # NOTE: for proper handling of moved/copied files, we have to use + # the second filename. + filename = m.group(2) + svndiff.append("Index: %s" % filename) + svndiff.append("=" * 67) + filecount += 1 + logging.info(line) + else: + svndiff.append(line) + if not filecount: + ErrorExit("No valid patches found in output from hg diff") + return "\n".join(svndiff) + "\n" + + def GetUnknownFiles(self): + """Return a list of files unknown to the VCS.""" + args = [] + status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."], + silent_ok=True) + unknown_files = [] + for line in status.splitlines(): + st, fn = line.split(" ", 1) + if st == "?": + unknown_files.append(fn) + return unknown_files + + def GetBaseFile(self, filename): + # "hg status" and "hg cat" both take a path relative to the current subdir, + # but "hg diff" has given us the path relative to the repo root. + base_content = "" + new_content = None + is_binary = False + oldrelpath = relpath = self._GetRelPath(filename) + # "hg status -C" returns two lines for moved/copied files, one otherwise + out = RunShell(["hg", "status", "-C", "--rev", self.base_rev, relpath]) + out = out.splitlines() + # HACK: strip error message about missing file/directory if it isn't in + # the working copy + if out[0].startswith('%s: ' % relpath): + out = out[1:] + status, _ = out[0].split(' ', 1) + if len(out) > 1 and status == "A": + # Moved/copied => considered as modified, use old filename to + # retrieve base contents + oldrelpath = out[1].strip() + status = "M" + if ":" in self.base_rev: + base_rev = self.base_rev.split(":", 1)[0] + else: + base_rev = self.base_rev + if status != "A": + base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], + silent_ok=True) + is_binary = self.IsBinaryData(base_content) + if status != "R": + new_content = open(relpath, "rb").read() + is_binary = is_binary or self.IsBinaryData(new_content) + if is_binary and base_content: + # Fetch again without converting newlines + base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], + silent_ok=True, universal_newlines=False) + if not is_binary or not self.IsImage(relpath): + new_content = None + return base_content, new_content, is_binary, status + + +class PerforceVCS(VersionControlSystem): + """Implementation of the VersionControlSystem interface for Perforce.""" + + def __init__(self, options): + + def ConfirmLogin(): + # Make sure we have a valid perforce session + while True: + data, retcode = self.RunPerforceCommandWithReturnCode( + ["login", "-s"], marshal_output=True) + if not data: + ErrorExit("Error checking perforce login") + if not retcode and (not "code" in data or data["code"] != "error"): + break + print "Enter perforce password: " + self.RunPerforceCommandWithReturnCode(["login"]) + + super(PerforceVCS, self).__init__(options) + + self.p4_changelist = options.p4_changelist + if not self.p4_changelist: + ErrorExit("A changelist id is required") + if (options.revision): + ErrorExit("--rev is not supported for perforce") + + self.p4_port = options.p4_port + self.p4_client = options.p4_client + self.p4_user = options.p4_user + + ConfirmLogin() + + if not options.title: + description = self.RunPerforceCommand(["describe", self.p4_changelist], + marshal_output=True) + if description and "desc" in description: + # Rietveld doesn't support multi-line descriptions + raw_title = description["desc"].strip() + lines = raw_title.splitlines() + if len(lines): + options.title = lines[0] + + def GetGUID(self): + """For now we don't know how to get repository ID for Perforce""" + return + + def RunPerforceCommandWithReturnCode(self, extra_args, marshal_output=False, + universal_newlines=True): + args = ["p4"] + if marshal_output: + # -G makes perforce format its output as marshalled python objects + args.extend(["-G"]) + if self.p4_port: + args.extend(["-p", self.p4_port]) + if self.p4_client: + args.extend(["-c", self.p4_client]) + if self.p4_user: + args.extend(["-u", self.p4_user]) + args.extend(extra_args) + + data, retcode = RunShellWithReturnCode( + args, print_output=False, universal_newlines=universal_newlines) + if marshal_output and data: + data = marshal.loads(data) + return data, retcode + + def RunPerforceCommand(self, extra_args, marshal_output=False, + universal_newlines=True): + # This might be a good place to cache call results, since things like + # describe or fstat might get called repeatedly. + data, retcode = self.RunPerforceCommandWithReturnCode( + extra_args, marshal_output, universal_newlines) + if retcode: + ErrorExit("Got error status from %s:\n%s" % (extra_args, data)) + return data + + def GetFileProperties(self, property_key_prefix = "", command = "describe"): + description = self.RunPerforceCommand(["describe", self.p4_changelist], + marshal_output=True) + + changed_files = {} + file_index = 0 + # Try depotFile0, depotFile1, ... until we don't find a match + while True: + file_key = "depotFile%d" % file_index + if file_key in description: + filename = description[file_key] + change_type = description[property_key_prefix + str(file_index)] + changed_files[filename] = change_type + file_index += 1 + else: + break + return changed_files + + def GetChangedFiles(self): + return self.GetFileProperties("action") + + def GetUnknownFiles(self): + # Perforce doesn't detect new files, they have to be explicitly added + return [] + + def IsBaseBinary(self, filename): + base_filename = self.GetBaseFilename(filename) + return self.IsBinaryHelper(base_filename, "files") + + def IsPendingBinary(self, filename): + return self.IsBinaryHelper(filename, "describe") + + def IsBinaryHelper(self, filename, command): + file_types = self.GetFileProperties("type", command) + if not filename in file_types: + ErrorExit("Trying to check binary status of unknown file %s." % filename) + # This treats symlinks, macintosh resource files, temporary objects, and + # unicode as binary. See the Perforce docs for more details: + # http://www.perforce.com/perforce/doc.current/manuals/cmdref/o.ftypes.html + return not file_types[filename].endswith("text") + + def GetFileContent(self, filename, revision, is_binary): + file_arg = filename + if revision: + file_arg += "#" + revision + # -q suppresses the initial line that displays the filename and revision + return self.RunPerforceCommand(["print", "-q", file_arg], + universal_newlines=not is_binary) + + def GetBaseFilename(self, filename): + actionsWithDifferentBases = [ + "move/add", # p4 move + "branch", # p4 integrate (to a new file), similar to hg "add" + "add", # p4 integrate (to a new file), after modifying the new file + ] + + # We only see a different base for "add" if this is a downgraded branch + # after a file was branched (integrated), then edited. + if self.GetAction(filename) in actionsWithDifferentBases: + # -Or shows information about pending integrations/moves + fstat_result = self.RunPerforceCommand(["fstat", "-Or", filename], + marshal_output=True) + + baseFileKey = "resolveFromFile0" # I think it's safe to use only file0 + if baseFileKey in fstat_result: + return fstat_result[baseFileKey] + + return filename + + def GetBaseRevision(self, filename): + base_filename = self.GetBaseFilename(filename) + + have_result = self.RunPerforceCommand(["have", base_filename], + marshal_output=True) + if "haveRev" in have_result: + return have_result["haveRev"] + + def GetLocalFilename(self, filename): + where = self.RunPerforceCommand(["where", filename], marshal_output=True) + if "path" in where: + return where["path"] + + def GenerateDiff(self, args): + class DiffData: + def __init__(self, perforceVCS, filename, action): + self.perforceVCS = perforceVCS + self.filename = filename + self.action = action + self.base_filename = perforceVCS.GetBaseFilename(filename) + + self.file_body = None + self.base_rev = None + self.prefix = None + self.working_copy = True + self.change_summary = None + + def GenerateDiffHeader(diffData): + header = [] + header.append("Index: %s" % diffData.filename) + header.append("=" * 67) + + if diffData.base_filename != diffData.filename: + if diffData.action.startswith("move"): + verb = "rename" + else: + verb = "copy" + header.append("%s from %s" % (verb, diffData.base_filename)) + header.append("%s to %s" % (verb, diffData.filename)) + + suffix = "\t(revision %s)" % diffData.base_rev + header.append("--- " + diffData.base_filename + suffix) + if diffData.working_copy: + suffix = "\t(working copy)" + header.append("+++ " + diffData.filename + suffix) + if diffData.change_summary: + header.append(diffData.change_summary) + return header + + def GenerateMergeDiff(diffData, args): + # -du generates a unified diff, which is nearly svn format + diffData.file_body = self.RunPerforceCommand( + ["diff", "-du", diffData.filename] + args) + diffData.base_rev = self.GetBaseRevision(diffData.filename) + diffData.prefix = "" + + # We have to replace p4's file status output (the lines starting + # with +++ or ---) to match svn's diff format + lines = diffData.file_body.splitlines() + first_good_line = 0 + while (first_good_line < len(lines) and + not lines[first_good_line].startswith("@@")): + first_good_line += 1 + diffData.file_body = "\n".join(lines[first_good_line:]) + return diffData + + def GenerateAddDiff(diffData): + fstat = self.RunPerforceCommand(["fstat", diffData.filename], + marshal_output=True) + if "headRev" in fstat: + diffData.base_rev = fstat["headRev"] # Re-adding a deleted file + else: + diffData.base_rev = "0" # Brand new file + diffData.working_copy = False + rel_path = self.GetLocalFilename(diffData.filename) + diffData.file_body = open(rel_path, 'r').read() + # Replicate svn's list of changed lines + line_count = len(diffData.file_body.splitlines()) + diffData.change_summary = "@@ -0,0 +1" + if line_count > 1: + diffData.change_summary += ",%d" % line_count + diffData.change_summary += " @@" + diffData.prefix = "+" + return diffData + + def GenerateDeleteDiff(diffData): + diffData.base_rev = self.GetBaseRevision(diffData.filename) + is_base_binary = self.IsBaseBinary(diffData.filename) + # For deletes, base_filename == filename + diffData.file_body = self.GetFileContent(diffData.base_filename, + None, + is_base_binary) + # Replicate svn's list of changed lines + line_count = len(diffData.file_body.splitlines()) + diffData.change_summary = "@@ -1" + if line_count > 1: + diffData.change_summary += ",%d" % line_count + diffData.change_summary += " +0,0 @@" + diffData.prefix = "-" + return diffData + + changed_files = self.GetChangedFiles() + + svndiff = [] + filecount = 0 + for (filename, action) in changed_files.items(): + svn_status = self.PerforceActionToSvnStatus(action) + if svn_status == "SKIP": + continue + + diffData = DiffData(self, filename, action) + # Is it possible to diff a branched file? Stackoverflow says no: + # http://stackoverflow.com/questions/1771314/in-perforce-command-line-how-to-diff-a-file-reopened-for-add + if svn_status == "M": + diffData = GenerateMergeDiff(diffData, args) + elif svn_status == "A": + diffData = GenerateAddDiff(diffData) + elif svn_status == "D": + diffData = GenerateDeleteDiff(diffData) + else: + ErrorExit("Unknown file action %s (svn action %s)." % \ + (action, svn_status)) + + svndiff += GenerateDiffHeader(diffData) + + for line in diffData.file_body.splitlines(): + svndiff.append(diffData.prefix + line) + filecount += 1 + if not filecount: + ErrorExit("No valid patches found in output from p4 diff") + return "\n".join(svndiff) + "\n" + + def PerforceActionToSvnStatus(self, status): + # Mirroring the list at http://permalink.gmane.org/gmane.comp.version-control.mercurial.devel/28717 + # Is there something more official? + return { + "add" : "A", + "branch" : "A", + "delete" : "D", + "edit" : "M", # Also includes changing file types. + "integrate" : "M", + "move/add" : "M", + "move/delete": "SKIP", + "purge" : "D", # How does a file's status become "purge"? + }[status] + + def GetAction(self, filename): + changed_files = self.GetChangedFiles() + if not filename in changed_files: + ErrorExit("Trying to get base version of unknown file %s." % filename) + + return changed_files[filename] + + def GetBaseFile(self, filename): + base_filename = self.GetBaseFilename(filename) + base_content = "" + new_content = None + + status = self.PerforceActionToSvnStatus(self.GetAction(filename)) + + if status != "A": + revision = self.GetBaseRevision(base_filename) + if not revision: + ErrorExit("Couldn't find base revision for file %s" % filename) + is_base_binary = self.IsBaseBinary(base_filename) + base_content = self.GetFileContent(base_filename, + revision, + is_base_binary) + + is_binary = self.IsPendingBinary(filename) + if status != "D" and status != "SKIP": + relpath = self.GetLocalFilename(filename) + if is_binary and self.IsImage(relpath): + new_content = open(relpath, "rb").read() + + return base_content, new_content, is_binary, status + +# NOTE: The SplitPatch function is duplicated in engine.py, keep them in sync. +def SplitPatch(data): + """Splits a patch into separate pieces for each file. + + Args: + data: A string containing the output of svn diff. + + Returns: + A list of 2-tuple (filename, text) where text is the svn diff output + pertaining to filename. + """ + patches = [] + filename = None + diff = [] + for line in data.splitlines(True): + new_filename = None + if line.startswith('Index:'): + unused, new_filename = line.split(':', 1) + new_filename = new_filename.strip() + elif line.startswith('Property changes on:'): + unused, temp_filename = line.split(':', 1) + # When a file is modified, paths use '/' between directories, however + # when a property is modified '\' is used on Windows. Make them the same + # otherwise the file shows up twice. + temp_filename = temp_filename.strip().replace('\\', '/') + if temp_filename != filename: + # File has property changes but no modifications, create a new diff. + new_filename = temp_filename + if new_filename: + if filename and diff: + patches.append((filename, ''.join(diff))) + filename = new_filename + diff = [line] + continue + if diff is not None: + diff.append(line) + if filename and diff: + patches.append((filename, ''.join(diff))) + return patches + + +def UploadSeparatePatches(issue, rpc_server, patchset, data, options): + """Uploads a separate patch for each file in the diff output. + + Returns a list of [patch_key, filename] for each file. + """ + patches = SplitPatch(data) + rv = [] + for patch in patches: + if len(patch[1]) > MAX_UPLOAD_SIZE: + print ("Not uploading the patch for " + patch[0] + + " because the file is too large.") + continue + form_fields = [("filename", patch[0])] + if not options.download_base: + form_fields.append(("content_upload", "1")) + files = [("data", "data.diff", patch[1])] + ctype, body = EncodeMultipartFormData(form_fields, files) + url = "/%d/upload_patch/%d" % (int(issue), int(patchset)) + print "Uploading patch for " + patch[0] + response_body = rpc_server.Send(url, body, content_type=ctype) + lines = response_body.splitlines() + if not lines or lines[0] != "OK": + StatusUpdate(" --> %s" % response_body) + sys.exit(1) + rv.append([lines[1], patch[0]]) + return rv + + +def GuessVCSName(options): + """Helper to guess the version control system. + + This examines the current directory, guesses which VersionControlSystem + we're using, and returns an string indicating which VCS is detected. + + Returns: + A pair (vcs, output). vcs is a string indicating which VCS was detected + and is one of VCS_GIT, VCS_MERCURIAL, VCS_SUBVERSION, VCS_PERFORCE, + VCS_CVS, or VCS_UNKNOWN. + Since local perforce repositories can't be easily detected, this method + will only guess VCS_PERFORCE if any perforce options have been specified. + output is a string containing any interesting output from the vcs + detection routine, or None if there is nothing interesting. + """ + for attribute, value in options.__dict__.iteritems(): + if attribute.startswith("p4") and value != None: + return (VCS_PERFORCE, None) + + def RunDetectCommand(vcs_type, command): + """Helper to detect VCS by executing command. + + Returns: + A pair (vcs, output) or None. Throws exception on error. + """ + try: + out, returncode = RunShellWithReturnCode(command) + if returncode == 0: + return (vcs_type, out.strip()) + except OSError, (errcode, message): + if errcode != errno.ENOENT: # command not found code + raise + + # Mercurial has a command to get the base directory of a repository + # Try running it, but don't die if we don't have hg installed. + # NOTE: we try Mercurial first as it can sit on top of an SVN working copy. + res = RunDetectCommand(VCS_MERCURIAL, ["hg", "root"]) + if res != None: + return res + + # Subversion from 1.7 has a single centralized .svn folder + # ( see http://subversion.apache.org/docs/release-notes/1.7.html#wc-ng ) + # That's why we use 'svn info' instead of checking for .svn dir + res = RunDetectCommand(VCS_SUBVERSION, ["svn", "info"]) + if res != None: + return res + + # Git has a command to test if you're in a git tree. + # Try running it, but don't die if we don't have git installed. + res = RunDetectCommand(VCS_GIT, ["git", "rev-parse", + "--is-inside-work-tree"]) + if res != None: + return res + + # detect CVS repos use `cvs status && $? == 0` rules + res = RunDetectCommand(VCS_CVS, ["cvs", "status"]) + if res != None: + return res + + return (VCS_UNKNOWN, None) + + +def GuessVCS(options): + """Helper to guess the version control system. + + This verifies any user-specified VersionControlSystem (by command line + or environment variable). If the user didn't specify one, this examines + the current directory, guesses which VersionControlSystem we're using, + and returns an instance of the appropriate class. Exit with an error + if we can't figure it out. + + Returns: + A VersionControlSystem instance. Exits if the VCS can't be guessed. + """ + vcs = options.vcs + if not vcs: + vcs = os.environ.get("CODEREVIEW_VCS") + if vcs: + v = VCS_ABBREVIATIONS.get(vcs.lower()) + if v is None: + ErrorExit("Unknown version control system %r specified." % vcs) + (vcs, extra_output) = (v, None) + else: + (vcs, extra_output) = GuessVCSName(options) + + if vcs == VCS_MERCURIAL: + if extra_output is None: + extra_output = RunShell(["hg", "root"]).strip() + return MercurialVCS(options, extra_output) + elif vcs == VCS_SUBVERSION: + return SubversionVCS(options) + elif vcs == VCS_PERFORCE: + return PerforceVCS(options) + elif vcs == VCS_GIT: + return GitVCS(options) + elif vcs == VCS_CVS: + return CVSVCS(options) + + ErrorExit(("Could not guess version control system. " + "Are you in a working copy directory?")) + + +def CheckReviewer(reviewer): + """Validate a reviewer -- either a nickname or an email addres. + + Args: + reviewer: A nickname or an email address. + + Calls ErrorExit() if it is an invalid email address. + """ + if "@" not in reviewer: + return # Assume nickname + parts = reviewer.split("@") + if len(parts) > 2: + ErrorExit("Invalid email address: %r" % reviewer) + assert len(parts) == 2 + if "." not in parts[1]: + ErrorExit("Invalid email address: %r" % reviewer) + + +def LoadSubversionAutoProperties(): + """Returns the content of [auto-props] section of Subversion's config file as + a dictionary. + + Returns: + A dictionary whose key-value pair corresponds the [auto-props] section's + key-value pair. + In following cases, returns empty dictionary: + - config file doesn't exist, or + - 'enable-auto-props' is not set to 'true-like-value' in [miscellany]. + """ + if os.name == 'nt': + subversion_config = os.environ.get("APPDATA") + "\\Subversion\\config" + else: + subversion_config = os.path.expanduser("~/.subversion/config") + if not os.path.exists(subversion_config): + return {} + config = ConfigParser.ConfigParser() + config.read(subversion_config) + if (config.has_section("miscellany") and + config.has_option("miscellany", "enable-auto-props") and + config.getboolean("miscellany", "enable-auto-props") and + config.has_section("auto-props")): + props = {} + for file_pattern in config.options("auto-props"): + props[file_pattern] = ParseSubversionPropertyValues( + config.get("auto-props", file_pattern)) + return props + else: + return {} + +def ParseSubversionPropertyValues(props): + """Parse the given property value which comes from [auto-props] section and + returns a list whose element is a (svn_prop_key, svn_prop_value) pair. + + See the following doctest for example. + + >>> ParseSubversionPropertyValues('svn:eol-style=LF') + [('svn:eol-style', 'LF')] + >>> ParseSubversionPropertyValues('svn:mime-type=image/jpeg') + [('svn:mime-type', 'image/jpeg')] + >>> ParseSubversionPropertyValues('svn:eol-style=LF;svn:executable') + [('svn:eol-style', 'LF'), ('svn:executable', '*')] + """ + key_value_pairs = [] + for prop in props.split(";"): + key_value = prop.split("=") + assert len(key_value) <= 2 + if len(key_value) == 1: + # If value is not given, use '*' as a Subversion's convention. + key_value_pairs.append((key_value[0], "*")) + else: + key_value_pairs.append((key_value[0], key_value[1])) + return key_value_pairs + + +def GetSubversionPropertyChanges(filename): + """Return a Subversion's 'Property changes on ...' string, which is used in + the patch file. + + Args: + filename: filename whose property might be set by [auto-props] config. + + Returns: + A string like 'Property changes on |filename| ...' if given |filename| + matches any entries in [auto-props] section. None, otherwise. + """ + global svn_auto_props_map + if svn_auto_props_map is None: + svn_auto_props_map = LoadSubversionAutoProperties() + + all_props = [] + for file_pattern, props in svn_auto_props_map.items(): + if fnmatch.fnmatch(filename, file_pattern): + all_props.extend(props) + if all_props: + return FormatSubversionPropertyChanges(filename, all_props) + return None + + +def FormatSubversionPropertyChanges(filename, props): + """Returns Subversion's 'Property changes on ...' strings using given filename + and properties. + + Args: + filename: filename + props: A list whose element is a (svn_prop_key, svn_prop_value) pair. + + Returns: + A string which can be used in the patch file for Subversion. + + See the following doctest for example. + + >>> print FormatSubversionPropertyChanges('foo.cc', [('svn:eol-style', 'LF')]) + Property changes on: foo.cc + ___________________________________________________________________ + Added: svn:eol-style + + LF + + """ + prop_changes_lines = [ + "Property changes on: %s" % filename, + "___________________________________________________________________"] + for key, value in props: + prop_changes_lines.append("Added: " + key) + prop_changes_lines.append(" + " + value) + return "\n".join(prop_changes_lines) + "\n" + + +def RealMain(argv, data=None): + """The real main function. + + Args: + argv: Command line arguments. + data: Diff contents. If None (default) the diff is generated by + the VersionControlSystem implementation returned by GuessVCS(). + + Returns: + A 2-tuple (issue id, patchset id). + The patchset id is None if the base files are not uploaded by this + script (applies only to SVN checkouts). + """ + options, args = parser.parse_args(argv[1:]) + if options.help: + if options.verbose < 2: + # hide Perforce options + parser.epilog = "Use '--help -v' to show additional Perforce options." + parser.option_groups.remove(parser.get_option_group('--p4_port')) + parser.print_help() + sys.exit(0) + + global verbosity + verbosity = options.verbose + if verbosity >= 3: + logging.getLogger().setLevel(logging.DEBUG) + elif verbosity >= 2: + logging.getLogger().setLevel(logging.INFO) + + vcs = GuessVCS(options) + + base = options.base_url + if isinstance(vcs, SubversionVCS): + # Guessing the base field is only supported for Subversion. + # Note: Fetching base files may become deprecated in future releases. + guessed_base = vcs.GuessBase(options.download_base) + if base: + if guessed_base and base != guessed_base: + print "Using base URL \"%s\" from --base_url instead of \"%s\"" % \ + (base, guessed_base) + else: + base = guessed_base + + if not base and options.download_base: + options.download_base = True + logging.info("Enabled upload of base file") + if not options.assume_yes: + vcs.CheckForUnknownFiles() + if data is None: + data = vcs.GenerateDiff(args) + data = vcs.PostProcessDiff(data) + if options.print_diffs: + print "Rietveld diff start:*****" + print data + print "Rietveld diff end:*****" + files = vcs.GetBaseFiles(data) + if verbosity >= 1: + print "Upload server:", options.server, "(change with -s/--server)" + rpc_server = GetRpcServer(options.server, + options.email, + options.host, + options.save_cookies, + options.account_type) + form_fields = [] + + repo_guid = vcs.GetGUID() + if repo_guid: + form_fields.append(("repo_guid", repo_guid)) + if base: + b = urlparse.urlparse(base) + username, netloc = urllib.splituser(b.netloc) + if username: + logging.info("Removed username from base URL") + base = urlparse.urlunparse((b.scheme, netloc, b.path, b.params, + b.query, b.fragment)) + form_fields.append(("base", base)) + if options.issue: + form_fields.append(("issue", str(options.issue))) + if options.email: + form_fields.append(("user", options.email)) + if options.reviewers: + for reviewer in options.reviewers.split(','): + CheckReviewer(reviewer) + form_fields.append(("reviewers", options.reviewers)) + if options.cc: + for cc in options.cc.split(','): + CheckReviewer(cc) + form_fields.append(("cc", options.cc)) + + # Process --message, --title and --file. + message = options.message or "" + title = options.title or "" + if options.file: + if options.message: + ErrorExit("Can't specify both message and message file options") + file = open(options.file, 'r') + message = file.read() + file.close() + if options.issue: + prompt = "Title describing this patch set: " + else: + prompt = "New issue subject: " + title = ( + title or message.split('\n', 1)[0].strip() or raw_input(prompt).strip()) + if not title and not options.issue: + ErrorExit("A non-empty title is required for a new issue") + # For existing issues, it's fine to give a patchset an empty name. Rietveld + # doesn't accept that so use a whitespace. + title = title or " " + if len(title) > 100: + title = title[:99] + '…' + if title and not options.issue: + message = message or title + + form_fields.append(("subject", title)) + # If it's a new issue send message as description. Otherwise a new + # message is created below on upload_complete. + if message and not options.issue: + form_fields.append(("description", message)) + + # Send a hash of all the base file so the server can determine if a copy + # already exists in an earlier patchset. + base_hashes = "" + for file, info in files.iteritems(): + if not info[0] is None: + checksum = md5(info[0]).hexdigest() + if base_hashes: + base_hashes += "|" + base_hashes += checksum + ":" + file + form_fields.append(("base_hashes", base_hashes)) + if options.private: + if options.issue: + print "Warning: Private flag ignored when updating an existing issue." + else: + form_fields.append(("private", "1")) + if options.send_patch: + options.send_mail = True + if not options.download_base: + form_fields.append(("content_upload", "1")) + if len(data) > MAX_UPLOAD_SIZE: + print "Patch is large, so uploading file patches separately." + uploaded_diff_file = [] + form_fields.append(("separate_patches", "1")) + else: + uploaded_diff_file = [("data", "data.diff", data)] + ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file) + response_body = rpc_server.Send("/upload", body, content_type=ctype) + patchset = None + if not options.download_base or not uploaded_diff_file: + lines = response_body.splitlines() + if len(lines) >= 2: + msg = lines[0] + patchset = lines[1].strip() + patches = [x.split(" ", 1) for x in lines[2:]] + else: + msg = response_body + else: + msg = response_body + StatusUpdate(msg) + if not response_body.startswith("Issue created.") and \ + not response_body.startswith("Issue updated."): + sys.exit(0) + issue = msg[msg.rfind("/")+1:] + + if not uploaded_diff_file: + result = UploadSeparatePatches(issue, rpc_server, patchset, data, options) + if not options.download_base: + patches = result + + if not options.download_base: + vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options, files) + + payload = {} # payload for final request + if options.send_mail: + payload["send_mail"] = "yes" + if options.send_patch: + payload["attach_patch"] = "yes" + if options.issue and message: + payload["message"] = message + payload = urllib.urlencode(payload) + rpc_server.Send("/" + issue + "/upload_complete/" + (patchset or ""), + payload=payload) + return issue, patchset + + +def main(): + try: + logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" + "%(lineno)s %(message)s ")) + os.environ['LC_ALL'] = 'C' + RealMain(sys.argv) + except KeyboardInterrupt: + print + StatusUpdate("Interrupted.") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/test/node_modules/mongoose/node_modules/ms/.npmignore b/test/node_modules/mongoose/node_modules/ms/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/test/node_modules/mongoose/node_modules/ms/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/test/node_modules/mongoose/node_modules/ms/Makefile b/test/node_modules/mongoose/node_modules/ms/Makefile new file mode 100644 index 0000000..dded504 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/ms/Makefile @@ -0,0 +1,8 @@ + +test: + ./node_modules/.bin/mocha test/test.js + +test-browser: + ./node_modules/.bin/serve test/ + +.PHONY: test diff --git a/test/node_modules/mongoose/node_modules/ms/README.md b/test/node_modules/mongoose/node_modules/ms/README.md new file mode 100644 index 0000000..c93d43f --- /dev/null +++ b/test/node_modules/mongoose/node_modules/ms/README.md @@ -0,0 +1,65 @@ + +# ms.js + +Ever find yourself doing math in your head or writing `1000 * 60 * 60 …`? +Don't want to add obstrusive `Number` prototype extensions to your reusable +/ distributable modules and projects? + +`ms` is a tiny utility that you can leverage when your application needs to +accept a number of miliseconds as a parameter. + +If a number is supplied to `ms`, it returns it immediately (e.g: +If a string that contains the number is supplied, it returns it immediately as +a number (e.g: it returns `100` for `'100'`). + +However, if you pass a string with a number and a valid unit, hte number of +equivalent ms is returned. + +```js +ms('1d') // 86400000 +ms('10h') // 36000000 +ms('2h') // 7200000 +ms('1m') // 60000 +ms('5ms') // 5000 +ms('100') // '100' +ms(100) // 100 +``` + +## How to use + +### Node + +```js +require('ms') +``` + +### Browser + +```html + +``` + +## Credits + +(The MIT License) + +Copyright (c) 2011 Guillermo Rauch <guillermo@learnboost.com> + +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/test/node_modules/mongoose/node_modules/ms/ms.js b/test/node_modules/mongoose/node_modules/ms/ms.js new file mode 100644 index 0000000..b4621bc --- /dev/null +++ b/test/node_modules/mongoose/node_modules/ms/ms.js @@ -0,0 +1,35 @@ +/** + +# ms.js + +No more painful `setTimeout(fn, 60 * 4 * 3 * 2 * 1 * Infinity * NaN * '☃')`. + + ms('2d') // 172800000 + ms('1.5h') // 5400000 + ms('1h') // 3600000 + ms('1m') // 60000 + ms('5s') // 5000 + ms('500ms') // 500 + ms('100') // '100' + ms(100) // 100 + +**/ + +(function (g) { + var r = /(\d*.?\d+)([mshd]+)/ + , _ = {} + + _.ms = 1; + _.s = 1000; + _.m = _.s * 60; + _.h = _.m * 60; + _.d = _.h * 24; + + function ms (s) { + if (s == Number(s)) return Number(s); + r.exec(s.toLowerCase()); + return RegExp.$1 * _[RegExp.$2]; + } + + g.top ? g.ms = ms : module.exports = ms; +})(this); diff --git a/test/node_modules/mongoose/node_modules/ms/package.json b/test/node_modules/mongoose/node_modules/ms/package.json new file mode 100644 index 0000000..74ed460 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/ms/package.json @@ -0,0 +1,18 @@ +{ + "name": "ms", + "version": "0.1.0", + "description": "Tiny ms conversion utility", + "main": "./ms", + "devDependencies": { + "mocha": "*", + "expect.js": "*", + "serve": "*" + }, + "readme": "\n# ms.js\n\nEver find yourself doing math in your head or writing `1000 * 60 * 60 …`?\nDon't want to add obstrusive `Number` prototype extensions to your reusable\n/ distributable modules and projects?\n\n`ms` is a tiny utility that you can leverage when your application needs to\naccept a number of miliseconds as a parameter.\n\nIf a number is supplied to `ms`, it returns it immediately (e.g:\nIf a string that contains the number is supplied, it returns it immediately as\na number (e.g: it returns `100` for `'100'`).\n\nHowever, if you pass a string with a number and a valid unit, hte number of\nequivalent ms is returned.\n\n```js\nms('1d') // 86400000\nms('10h') // 36000000\nms('2h') // 7200000\nms('1m') // 60000\nms('5ms') // 5000\nms('100') // '100'\nms(100) // 100\n```\n\n## How to use\n\n### Node\n\n```js\nrequire('ms')\n```\n\n### Browser\n\n```html\n\n```\n\n## Credits\n\n(The MIT License)\n\nCopyright (c) 2011 Guillermo Rauch <guillermo@learnboost.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "README.md", + "_id": "ms@0.1.0", + "dist": { + "shasum": "08c99eaad448f357615641ee601e56521ca976c5" + }, + "_from": "ms@0.1.0" +} diff --git a/test/node_modules/mongoose/node_modules/ms/test/index.html b/test/node_modules/mongoose/node_modules/ms/test/index.html new file mode 100644 index 0000000..79edc40 --- /dev/null +++ b/test/node_modules/mongoose/node_modules/ms/test/index.html @@ -0,0 +1,19 @@ + + + + ms.js tests + + + + + + + + + + + + +
    + + diff --git a/test/node_modules/mongoose/node_modules/ms/test/support/jquery.js b/test/node_modules/mongoose/node_modules/ms/test/support/jquery.js new file mode 100644 index 0000000..8ccd0ea --- /dev/null +++ b/test/node_modules/mongoose/node_modules/ms/test/support/jquery.js @@ -0,0 +1,9266 @@ +/*! + * jQuery JavaScript Library v1.7.1 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Mon Nov 21 21:11:03 2011 -0500 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context ? context.ownerDocument || context : document ); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.7.1", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.add( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.fireWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).off( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery.Callbacks( "once memory" ); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array, i ) { + var len; + + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } + + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +// String to Object flags format cache +var flagsCache = {}; + +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } + } + } + } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; + } + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!memory; + } + }; + + return self; +}; + + + + +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + + Deferred: function( func ) { + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList + }, + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + return this; + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; + } + } + return obj; + } + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = sliceDeferred.call( arguments, 0 ), + i = 0, + length = args.length, + pValues = new Array( length ), + count = length, + pCount = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(), + promise = deferred.promise(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( deferred, args ); + } + }; + } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } + if ( length > 1 ) { + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return promise; + } +}); + + + + +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + marginDiv, + fragment, + tds, + events, + eventName, + i, + isSupported, + div = document.createElement( "div" ), + documentElement = document.documentElement; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
    a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( window.getComputedStyle ) { + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.style.width = "2px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + fragment.removeChild( div ); + + // Null elements to avoid leaks in IE + fragment = select = opt = marginDiv = div = input = null; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + conMarginTop, ptlm, vb, style, html, + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + conMarginTop = 1; + ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;"; + vb = "visibility:hidden;border:0;"; + style = "style='" + ptlm + "border:5px solid #000;padding:0;'"; + html = "
    " + + "" + + "
    "; + + container = document.createElement("div"); + container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
    t
    "; + tds = div.getElementsByTagName( "td" ); + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Figure out if the W3C box model works as expected + div.innerHTML = ""; + div.style.width = div.style.paddingLeft = "1px"; + jQuery.boxModel = support.boxModel = div.offsetWidth === 2; + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
    "; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } + + div.style.cssText = ptlm + vb; + div.innerHTML = html; + + outer = div.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + body.removeChild( container ); + div = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + return support; +})(); + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var privateCache, thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, + isEvents = name === "events"; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = ++jQuery.uuid; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + privateCache = thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ internalKey ] : internalKey; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the cache and need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ internalKey ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + } else { + elem[ internalKey ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, attr, name, + data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { + attr = this[0].attributes; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( this[0], name, data[ name ] ); + } + } + jQuery._data( this[0], "parsedAttrs", true ); + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var self = jQuery( this ), + args = [ parts[0], value ]; + + self.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + jQuery.isNumeric( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery._data( elem, deferDataKey ); + if ( defer && + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.fire(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); + if ( count ) { + jQuery._data( elem, key, count ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + var q; + if ( elem ) { + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + hooks = {}; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue " + type + ".run", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + count++; + tmp.add( resolve ); + } + } + resolve(); + return defer.promise(); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = ( value || "" ).split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, i, max, option, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, + i = 0; + + if ( value && elem.nodeType === 1 ) { + attrNames = value.toLowerCase().split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + + // See #9699 for explanation of this approach (setting first, then removal) + jQuery.attr( elem, name, "" ); + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.nodeValue = value + "" ); + } + }; + + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = "" + value ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); + + + + +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /\bhover(\.\S+)?\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + var attrs = elem.attributes || {}; + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || (attrs.id || {}).value === m[2]) && + (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + quick: quickParse( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, origType, namespaces, origCount, + j, events, special, handle, eventType, handleObj; + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var type = event.type || event, + namespaces = [], + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + old = null; + for ( ; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), + run_all = !event.exclusive && !event.namespace, + handlerQueue = [], + i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Determine handlers that should run if there are delegated events + // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) { + + // Pregenerate a single jQuery object for reuse with .is() + jqcur = jQuery(this); + jqcur.context = this.ownerDocument || this; + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + selMatch = {}; + matches = []; + jqcur[0] = cur; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = ( + handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) + ); + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { + event.metaKey = event.ctrlKey; + } + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady + }, + + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + ret; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on.call( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rReturn = /\r\n/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context, seed ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set, seed ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set, i, len, match, type, left; + + if ( !expr ) { + return []; + } + + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + type, found, item, filter, left, + i, pass, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + filter = Expr.filter[ type ]; + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + pass = not ^ found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + first = match[2]; + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + doneName = match[0]; + parent = elem.parentNode; + + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent[ expando ] = doneName; + } + + diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + !type && Sizzle.attr ? + result != null : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

    "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
    "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context, seed ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet, seed ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array (deprecated as of jQuery 1.7) + if ( jQuery.isArray( selectors ) ) { + var level = 1; + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { + + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} + + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /", "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + col: [ 2, "", "
    " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and