(function(e){if("function"==typeof bootstrap)bootstrap("nedb",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeNedb=e}else"undefined"!=typeof window?window.Nedb=e():global.Nedb=e()})(function(){var define,ses,bootstrap,module,exports; return (function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); console.trace(); } } // If we've already got an array, just append. this._events[type].push(listener); } else { // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { var self = this; self.on(type, function g() { self.removeListener(type, g); listener.apply(this, arguments); }); return this; }; EventEmitter.prototype.removeListener = function(type, listener) { if ('function' !== typeof listener) { throw new Error('removeListener only takes instances of Function'); } // does not use listeners(), so no side effect of creating _events[type] if (!this._events || !this._events[type]) return this; var list = this._events[type]; if (isArray(list)) { var i = indexOf(list, listener); if (i < 0) return this; list.splice(i, 1); if (list.length == 0) delete this._events[type]; } else if (this._events[type] === listener) { delete this._events[type]; } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { if (arguments.length === 0) { this._events = {}; return this; } // does not use listeners(), so no side effect of creating _events[type] if (type && this._events && this._events[type]) this._events[type] = null; return this; }; EventEmitter.prototype.listeners = function(type) { if (!this._events) this._events = {}; if (!this._events[type]) this._events[type] = []; if (!isArray(this._events[type])) { this._events[type] = [this._events[type]]; } return this._events[type]; }; })(require("__browserify_process")) },{"__browserify_process":3}],2:[function(require,module,exports){ var events = require('events'); exports.isArray = isArray; exports.isDate = function(obj){return Object.prototype.toString.call(obj) === '[object Date]'}; exports.isRegExp = function(obj){return Object.prototype.toString.call(obj) === '[object RegExp]'}; exports.print = function () {}; exports.puts = function () {}; exports.debug = function() {}; exports.inspect = function(obj, showHidden, depth, colors) { var seen = []; var stylize = function(str, styleType) { // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics var styles = { 'bold' : [1, 22], 'italic' : [3, 23], 'underline' : [4, 24], 'inverse' : [7, 27], 'white' : [37, 39], 'grey' : [90, 39], 'black' : [30, 39], 'blue' : [34, 39], 'cyan' : [36, 39], 'green' : [32, 39], 'magenta' : [35, 39], 'red' : [31, 39], 'yellow' : [33, 39] }; var style = { 'special': 'cyan', 'number': 'blue', 'boolean': 'yellow', 'undefined': 'grey', 'null': 'bold', 'string': 'green', 'date': 'magenta', // "name": intentionally not styling 'regexp': 'red' }[styleType]; if (style) { return '\033[' + styles[style][0] + 'm' + str + '\033[' + styles[style][1] + 'm'; } else { return str; } }; if (! colors) { stylize = function(str, styleType) { return str; }; } function format(value, recurseTimes) { // Provide a hook for user-specified inspect functions. // Check that value is an object with an inspect function on it if (value && typeof value.inspect === 'function' && // Filter out the util module, it's inspect function is special value !== exports && // Also filter out any prototype objects using the circular check. !(value.constructor && value.constructor.prototype === value)) { return value.inspect(recurseTimes); } // Primitive types cannot have properties switch (typeof value) { case 'undefined': return stylize('undefined', 'undefined'); case 'string': var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') .replace(/'/g, "\\'") .replace(/\\"/g, '"') + '\''; return stylize(simple, 'string'); case 'number': return stylize('' + value, 'number'); case 'boolean': return stylize('' + value, 'boolean'); } // For some reason typeof null is "object", so special case here. if (value === null) { return stylize('null', 'null'); } // Look up the keys of the object. var visible_keys = Object_keys(value); var keys = showHidden ? Object_getOwnPropertyNames(value) : visible_keys; // Functions without properties can be shortcutted. if (typeof value === 'function' && keys.length === 0) { if (isRegExp(value)) { return stylize('' + value, 'regexp'); } else { var name = value.name ? ': ' + value.name : ''; return stylize('[Function' + name + ']', 'special'); } } // Dates without properties can be shortcutted if (isDate(value) && keys.length === 0) { return stylize(value.toUTCString(), 'date'); } var base, type, braces; // Determine the object type if (isArray(value)) { type = 'Array'; braces = ['[', ']']; } else { type = 'Object'; braces = ['{', '}']; } // Make functions say that they are functions if (typeof value === 'function') { var n = value.name ? ': ' + value.name : ''; base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']'; } else { base = ''; } // Make dates with properties first say the date if (isDate(value)) { base = ' ' + value.toUTCString(); } if (keys.length === 0) { return braces[0] + base + braces[1]; } if (recurseTimes < 0) { if (isRegExp(value)) { return stylize('' + value, 'regexp'); } else { return stylize('[Object]', 'special'); } } seen.push(value); var output = keys.map(function(key) { var name, str; if (value.__lookupGetter__) { if (value.__lookupGetter__(key)) { if (value.__lookupSetter__(key)) { str = stylize('[Getter/Setter]', 'special'); } else { str = stylize('[Getter]', 'special'); } } else { if (value.__lookupSetter__(key)) { str = stylize('[Setter]', 'special'); } } } if (visible_keys.indexOf(key) < 0) { name = '[' + key + ']'; } if (!str) { if (seen.indexOf(value[key]) < 0) { if (recurseTimes === null) { str = format(value[key]); } else { str = format(value[key], recurseTimes - 1); } if (str.indexOf('\n') > -1) { if (isArray(value)) { str = str.split('\n').map(function(line) { return ' ' + line; }).join('\n').substr(2); } else { str = '\n' + str.split('\n').map(function(line) { return ' ' + line; }).join('\n'); } } } else { str = stylize('[Circular]', 'special'); } } if (typeof name === 'undefined') { if (type === 'Array' && key.match(/^\d+$/)) { return str; } name = JSON.stringify('' + key); if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { name = name.substr(1, name.length - 2); name = stylize(name, 'name'); } else { name = name.replace(/'/g, "\\'") .replace(/\\"/g, '"') .replace(/(^"|"$)/g, "'"); name = stylize(name, 'string'); } } return name + ': ' + str; }); seen.pop(); var numLinesEst = 0; var length = output.reduce(function(prev, cur) { numLinesEst++; if (cur.indexOf('\n') >= 0) numLinesEst++; return prev + cur.length + 1; }, 0); if (length > 50) { output = braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n ') + ' ' + braces[1]; } else { output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; } return output; } return format(obj, (typeof depth === 'undefined' ? 2 : depth)); }; function isArray(ar) { return ar instanceof Array || Array.isArray(ar) || (ar && ar !== Object.prototype && isArray(ar.__proto__)); } function isRegExp(re) { return re instanceof RegExp || (typeof re === 'object' && Object.prototype.toString.call(re) === '[object RegExp]'); } function isDate(d) { if (d instanceof Date) return true; if (typeof d !== 'object') return false; var properties = Date.prototype && Object_getOwnPropertyNames(Date.prototype); var proto = d.__proto__ && Object_getOwnPropertyNames(d.__proto__); return JSON.stringify(proto) === JSON.stringify(properties); } function pad(n) { return n < 10 ? '0' + n.toString(10) : n.toString(10); } var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // 26 Feb 16:19:34 function timestamp() { var d = new Date(); var time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(':'); return [d.getDate(), months[d.getMonth()], time].join(' '); } exports.log = function (msg) {}; exports.pump = null; var Object_keys = Object.keys || function (obj) { var res = []; for (var key in obj) res.push(key); return res; }; var Object_getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) { var res = []; for (var key in obj) { if (Object.hasOwnProperty.call(obj, key)) res.push(key); } return res; }; var Object_create = Object.create || function (prototype, properties) { // from es5-shim var object; if (prototype === null) { object = { '__proto__' : null }; } else { if (typeof prototype !== 'object') { throw new TypeError( 'typeof prototype[' + (typeof prototype) + '] != \'object\'' ); } var Type = function () {}; Type.prototype = prototype; object = new Type(); object.__proto__ = prototype; } if (typeof properties !== 'undefined' && Object.defineProperties) { Object.defineProperties(object, properties); } return object; }; exports.inherits = function(ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object_create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; var formatRegExp = /%[sdj%]/g; exports.format = function(f) { if (typeof f !== 'string') { var objects = []; for (var i = 0; i < arguments.length; i++) { objects.push(exports.inspect(arguments[i])); } return objects.join(' '); } var i = 1; var args = arguments; var len = args.length; var str = String(f).replace(formatRegExp, function(x) { if (x === '%%') return '%'; if (i >= len) return x; switch (x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]); case '%j': return JSON.stringify(args[i++]); default: return x; } }); for(var x = args[i]; i < len; x = args[++i]){ if (x === null || typeof x !== 'object') { str += ' ' + x; } else { str += ' ' + exports.inspect(x); } } return str; }; },{"events":1}],3:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; process.nextTick = (function () { var canSetImmediate = typeof window !== 'undefined' && window.setImmediate; var canPost = typeof window !== 'undefined' && window.postMessage && window.addEventListener ; if (canSetImmediate) { return function (f) { return window.setImmediate(f) }; } if (canPost) { var queue = []; window.addEventListener('message', function (ev) { if (ev.source === window && ev.data === 'process-tick') { ev.stopPropagation(); if (queue.length > 0) { var fn = queue.shift(); fn(); } } }, true); return function nextTick(fn) { queue.push(fn); window.postMessage('process-tick', '*'); }; } return function nextTick(fn) { setTimeout(fn, 0); }; })(); process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.binding = function (name) { throw new Error('process.binding is not supported'); } // TODO(shtylman) process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; },{}],4:[function(require,module,exports){ (function(){/** * Specific customUtils for the browser, where we don't have access to the Crypto and Buffer modules */ /** * Taken from the crypto-browserify module * https://github.com/dominictarr/crypto-browserify * NOTE: Math.random() does not guarantee "cryptographic quality" but we actually don't need it */ function randomBytes (size) { var bytes = new Array(size); var r; for (var i = 0, r; i < size; i++) { if ((i & 0x03) == 0) r = Math.random() * 0x100000000; bytes[i] = r >>> ((i & 0x03) << 3) & 0xff; } return bytes; } /** * Taken from the base64-js module * https://github.com/beatgammit/base64-js/ */ function byteArrayToBase64 (uint8) { var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' , extraBytes = uint8.length % 3 // if we have 1 byte left, pad 2 bytes , output = "" , temp, length, i; function tripletToBase64 (num) { return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]; }; // go through the array every three bytes, we'll deal with trailing stuff later for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]); output += tripletToBase64(temp); } // pad the end with zeros, but make sure to not forget the extra bytes switch (extraBytes) { case 1: temp = uint8[uint8.length - 1]; output += lookup[temp >> 2]; output += lookup[(temp << 4) & 0x3F]; output += '=='; break; case 2: temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]); output += lookup[temp >> 10]; output += lookup[(temp >> 4) & 0x3F]; output += lookup[(temp << 2) & 0x3F]; output += '='; break; } return output; } /** * Return a random alphanumerical string of length len * There is a very small probability (less than 1/1,000,000) for the length to be less than len * (il the base64 conversion yields too many pluses and slashes) but * that's not an issue here * The probability of a collision is extremely small (need 3*10^12 documents to have one chance in a million of a collision) * See http://en.wikipedia.org/wiki/Birthday_problem */ function uid (len) { return byteArrayToBase64(randomBytes(Math.ceil(Math.max(8, len * 2)))).replace(/[+\/]/g, '').slice(0, len); } module.exports.uid = uid; })() },{}],5:[function(require,module,exports){ var customUtils = require('./customUtils') , model = require('./model') , async = require('async') , Executor = require('./executor') , Index = require('./indexes') , util = require('util') , _ = require('underscore') , Persistence = require('./persistence') ; /** * Create a new collection * @param {String} options.filename Optional, datastore will be in-memory only if not provided * @param {Boolean} options.inMemoryOnly Optional, default to false * @param {Boolean} options.nodeWebkitAppName Optional, specify the name of your NW app if you want options.filename to be relative to the directory where * Node Webkit stores application data such as cookies and local storage (the best place to store data in my opinion) * @param {Boolean} options.autoload Optional, defaults to false */ function Datastore (options) { var filename; // Retrocompatibility with v0.6 and before if (typeof options === 'string') { filename = options; this.inMemoryOnly = false; // Default } else { options = options || {}; filename = options.filename; this.inMemoryOnly = options.inMemoryOnly || false; this.autoload = options.autoload || false; } // Determine whether in memory or persistent if (!filename || typeof filename !== 'string' || filename.length === 0) { this.filename = null; this.inMemoryOnly = true; } else { this.filename = filename; } // Persistence handling this.persistence = new Persistence({ db: this, nodeWebkitAppName: options.nodeWebkitAppName }); // This new executor is ready if we don't use persistence // If we do, it will only be ready once loadDatabase is called this.executor = new Executor(); if (this.inMemoryOnly) { this.executor.ready = true; } // Indexed by field name, dot notation can be used // _id is always indexed and since _ids are generated randomly the underlying // binary is always well-balanced this.indexes = {}; this.indexes._id = new Index({ fieldName: '_id', unique: true }); if (this.autoload) { this.loadDatabase(); } } /** * Load the database from the datafile, and trigger the execution of buffered commands if any */ Datastore.prototype.loadDatabase = function () { this.executor.push({ this: this.persistence, fn: this.persistence.loadDatabase, arguments: arguments }, true); }; /** * Get an array of all the data in the database */ Datastore.prototype.getAllData = function () { return this.indexes._id.getAll(); }; /** * Reset all currently defined indexes */ Datastore.prototype.resetIndexes = function (newData) { var self = this; Object.keys(this.indexes).forEach(function (i) { self.indexes[i].reset(newData); }); }; /** * Ensure an index is kept for this field. Same parameters as lib/indexes * For now this function is synchronous, we need to test how much time it takes * We use an async API for consistency with the rest of the code * @param {String} options.fieldName * @param {Boolean} options.unique * @param {Boolean} options.sparse * @param {Function} cb Optional callback, signature: err */ Datastore.prototype.ensureIndex = function (options, cb) { var callback = cb || function () {}; options = options || {}; if (!options.fieldName) { return callback({ missingFieldName: true }); } if (this.indexes[options.fieldName]) { return callback(null); } this.indexes[options.fieldName] = new Index(options); try { this.indexes[options.fieldName].insert(this.getAllData()); } catch (e) { delete this.indexes[options.fieldName]; return callback(e); } return callback(null); }; /** * Add one or several document(s) to all indexes */ Datastore.prototype.addToIndexes = function (doc) { var i, failingIndex, error , keys = Object.keys(this.indexes) ; for (i = 0; i < keys.length; i += 1) { try { this.indexes[keys[i]].insert(doc); } catch (e) { failingIndex = i; error = e; break; } } // If an error happened, we need to rollback the insert on all other indexes if (error) { for (i = 0; i < failingIndex; i += 1) { this.indexes[keys[i]].remove(doc); } throw error; } }; /** * Remove one or several document(s) from all indexes */ Datastore.prototype.removeFromIndexes = function (doc) { var self = this; Object.keys(this.indexes).forEach(function (i) { self.indexes[i].remove(doc); }); }; /** * Update one or several documents in all indexes * If one update violates a constraint, all changes are rolled back */ Datastore.prototype.updateIndexes = function (oldDoc, newDoc) { var i, failingIndex, error , keys = Object.keys(this.indexes) ; for (i = 0; i < keys.length; i += 1) { try { this.indexes[keys[i]].update(oldDoc, newDoc); } catch (e) { failingIndex = i; error = e; break; } } // If an error happened, we need to rollback the insert on all other indexes if (error) { for (i = 0; i < failingIndex; i += 1) { this.indexes[keys[i]].revertUpdate(oldDoc, newDoc); } throw error; } }; /** * Return the list of candidates for a given query * Crude implementation for now, we return the candidates given by the first usable index if any * We try the following query types, in this order: basic match, $in match, comparison match * One way to make it better would be to enable the use of multiple indexes if the first usable index * returns too much data. I may do it in the future. */ Datastore.prototype.getCandidates = function (query) { var indexNames = Object.keys(this.indexes) , usableQueryKeys; // For a basic match usableQueryKeys = []; Object.keys(query).forEach(function (k) { if (typeof query[k] === 'string' || typeof query[k] === 'number' || typeof query[k] === 'boolean' || util.isDate(query[k]) || query[k] === null) { usableQueryKeys.push(k); } }); usableQueryKeys = _.intersection(usableQueryKeys, indexNames); if (usableQueryKeys.length > 0) { return this.indexes[usableQueryKeys[0]].getMatching(query[usableQueryKeys[0]]); } // For a $in match usableQueryKeys = []; Object.keys(query).forEach(function (k) { if (query[k] && query[k].hasOwnProperty('$in')) { usableQueryKeys.push(k); } }); usableQueryKeys = _.intersection(usableQueryKeys, indexNames); if (usableQueryKeys.length > 0) { return this.indexes[usableQueryKeys[0]].getMatching(query[usableQueryKeys[0]].$in); } // For a comparison match usableQueryKeys = []; Object.keys(query).forEach(function (k) { if (query[k] && (query[k].hasOwnProperty('$lt') || query[k].hasOwnProperty('$lte') || query[k].hasOwnProperty('$gt') || query[k].hasOwnProperty('$gte'))) { usableQueryKeys.push(k); } }); usableQueryKeys = _.intersection(usableQueryKeys, indexNames); if (usableQueryKeys.length > 0) { return this.indexes[usableQueryKeys[0]].getBetweenBounds(query[usableQueryKeys[0]]); } // By default, return all the DB data return this.getAllData(); }; /** * Insert a new document * @param {Function} cb Optional callback, signature: err, insertedDoc * * @api private Use Datastore.insert which has the same signature */ Datastore.prototype._insert = function (newDoc, cb) { var callback = cb || function () {} , self = this , insertedDoc ; // Ensure the document has the right format try { newDoc._id = customUtils.uid(16); model.checkObject(newDoc); insertedDoc = model.deepCopy(newDoc); } catch (e) { return callback(e); } // Insert in all indexes (also serves to ensure uniqueness) try { self.addToIndexes(insertedDoc); } catch (e) { return callback(e); } this.persistence.persistNewState([newDoc], function (err) { if (err) { return callback(err); } return callback(null, newDoc); }); }; Datastore.prototype.insert = function () { this.executor.push({ this: this, fn: this._insert, arguments: arguments }); }; /** * Find all documents matching the query * @param {Object} query MongoDB-style query * * @api private Use find */ Datastore.prototype._find = function (query, callback) { var res = [] , self = this , candidates = this.getCandidates(query) , i ; try { for (i = 0; i < candidates.length; i += 1) { if (model.match(candidates[i], query)) { res.push(model.deepCopy(candidates[i])); } } } catch (err) { return callback(err); } return callback(null, res); }; Datastore.prototype.find = function () { this.executor.push({ this: this, fn: this._find, arguments: arguments }); }; /** * Find one document matching the query * @param {Object} query MongoDB-style query * * @api private Use findOne */ Datastore.prototype._findOne = function (query, callback) { var self = this , candidates = this.getCandidates(query) , i ; try { for (i = 0; i < candidates.length; i += 1) { if (model.match(candidates[i], query)) { return callback(null, model.deepCopy(candidates[i])); } } } catch (err) { return callback(err); } return callback(null, null); }; Datastore.prototype.findOne = function () { this.executor.push({ this: this, fn: this._findOne, arguments: arguments }); }; /** * Update all docs matching query * For now, very naive implementation (recalculating the whole database) * @param {Object} query * @param {Object} updateQuery * @param {Object} options Optional options * options.multi If true, can update multiple documents (defaults to false) * options.upsert If true, document is inserted if the query doesn't match anything * @param {Function} cb Optional callback, signature: err, numReplaced, upsert (set to true if the update was in fact an upsert) * * @api private Use Datastore.update which has the same signature */ Datastore.prototype._update = function (query, updateQuery, options, cb) { var callback , self = this , numReplaced = 0 , multi, upsert , updatedDocs = [] , candidates , i ; if (typeof options === 'function') { cb = options; options = {}; } callback = cb || function () {}; multi = options.multi !== undefined ? options.multi : false; upsert = options.upsert !== undefined ? options.upsert : false; async.waterfall([ function (cb) { // If upsert option is set, check whether we need to insert the doc if (!upsert) { return cb(); } self._findOne(query, function (err, doc) { if (err) { return callback(err); } if (doc) { return cb(); } else { // The upserted document is the query (since for now queries have the same structure as // documents), modified by the updateQuery return self._insert(model.modify(query, updateQuery), function (err) { if (err) { return callback(err); } return callback(null, 1, true); }); } }); } , function () { // Perform the update var modifiedDoc; candidates = self.getCandidates(query); try { for (i = 0; i < candidates.length; i += 1) { if (model.match(candidates[i], query) && (multi || numReplaced === 0)) { numReplaced += 1; modifiedDoc = model.modify(candidates[i], updateQuery); self.updateIndexes(candidates[i], modifiedDoc); updatedDocs.push(modifiedDoc); } } } catch (err) { return callback(err); } self.persistence.persistNewState(updatedDocs, function (err) { if (err) { return callback(err); } return callback(null, numReplaced); }); } ]); }; Datastore.prototype.update = function () { this.executor.push({ this: this, fn: this._update, arguments: arguments }); }; /** * Remove all docs matching the query * For now very naive implementation (similar to update) * @param {Object} query * @param {Object} options Optional options * options.multi If true, can update multiple documents (defaults to false) * @param {Function} cb Optional callback, signature: err, numRemoved * * @api private Use Datastore.remove which has the same signature */ Datastore.prototype._remove = function (query, options, cb) { var callback , self = this , numRemoved = 0 , multi , removedDocs = [] , candidates = this.getCandidates(query) ; if (typeof options === 'function') { cb = options; options = {}; } callback = cb || function () {}; multi = options.multi !== undefined ? options.multi : false; try { candidates.forEach(function (d) { if (model.match(d, query) && (multi || numRemoved === 0)) { numRemoved += 1; removedDocs.push({ $$deleted: true, _id: d._id }); self.removeFromIndexes(d); } }); } catch (err) { return callback(err); } self.persistence.persistNewState(removedDocs, function (err) { if (err) { return callback(err); } return callback(null, numRemoved); }); }; Datastore.prototype.remove = function () { this.executor.push({ this: this, fn: this._remove, arguments: arguments }); }; module.exports = Datastore; },{"./customUtils":4,"./executor":6,"./indexes":7,"./model":8,"./persistence":9,"async":10,"underscore":15,"util":2}],6:[function(require,module,exports){ (function(){/** * Responsible for sequentially executing actions on the database */ var async = require('async') ; function Executor () { this.buffer = []; this.ready = false; // This queue will execute all commands, one-by-one in order this.queue = async.queue(function (task, cb) { var callback , lastArg = task.arguments[task.arguments.length - 1] , i, newArguments = [] ; // task.arguments is an array-like object on which adding a new field doesn't work, so we transform it into a real array for (i = 0; i < task.arguments.length; i += 1) { newArguments.push(task.arguments[i]); } // Always tell the queue task is complete. Execute callback if any was given. if (typeof lastArg === 'function') { callback = function () { lastArg.apply(null, arguments); cb(); }; newArguments[newArguments.length - 1] = callback; } else { callback = function () { cb(); }; newArguments.push(callback); } task.fn.apply(task.this, newArguments); }, 1); } /** * If executor is ready, queue task (and process it immediately if executor was idle) * If not, buffer task for later processing * @param {Object} task * task.this - Object to use as this * task.fn - Function to execute * task.arguments - Array of arguments * @param {Boolean} forceQueuing Optional (defaults to false) force executor to queue task even if it is not ready */ Executor.prototype.push = function (task, forceQueuing) { if (this.ready || forceQueuing) { this.queue.push(task); } else { this.buffer.push(task); } }; /** * Queue all tasks in buffer (in the same order they came in) * Automatically sets executor as ready */ Executor.prototype.processBuffer = function () { var i; this.ready = true; for (i = 0; i < this.buffer.length; i += 1) { this.queue.push(this.buffer[i]); } this.buffer = []; }; // Interface module.exports = Executor; })() },{"async":10}],7:[function(require,module,exports){ var BinarySearchTree = require('binary-search-tree').AVLTree , model = require('./model') , _ = require('underscore') , util = require('util') ; /** * Two indexed pointers are equal iif they point to the same place */ function checkValueEquality (a, b) { return a === b; } /** * Create a new index * @param {String} options.fieldName On which field should the index apply (can use dot notation to index on sub fields) * @param {Boolean} options.unique Optional, enforce a unique constraint (default: false) * @param {Boolean} options.sparse Optional, allow a sparse index (we can have documents for which fieldName is undefined) (default: false) */ function Index (options) { this.fieldName = options.fieldName; this.unique = options.unique || false; this.sparse = options.sparse || false; this.treeOptions = { unique: this.unique, compareKeys: model.compareThings, checkValueEquality: checkValueEquality }; this.reset(); // No data in the beginning } /** * Reset an index * @param {Document or Array of documents} newData Optional, data to initialize the index with * If an error is thrown during insertion, the index is not modified */ Index.prototype.reset = function (newData) { this.tree = new BinarySearchTree(this.treeOptions); if (newData) { this.insert(newData); } }; /** * Insert a new document in the index * If an array is passed, we insert all its elements (if one insertion fails the index is not modified) * O(log(n)) */ Index.prototype.insert = function (doc) { var key, self = this; if (util.isArray(doc)) { this.insertMultipleDocs(doc); return; } key = model.getDotValue(doc, this.fieldName); // We don't index documents that don't contain the field if the index is sparse if (key === undefined && this.sparse) { return; } this.tree.insert(key, doc); }; /** * Insert an array of documents in the index * If a constraint is violated, an error should be thrown and the changes rolled back */ Index.prototype.insertMultipleDocs = function (docs) { var i, error, failingI; for (i = 0; i < docs.length; i += 1) { try { this.insert(docs[i]); } catch (e) { error = e; failingI = i; break; } } if (error) { for (i = 0; i < failingI; i += 1) { this.remove(docs[i]); } throw error; } }; /** * Remove a document from the index * If an array is passed, we remove all its elements * The remove operation is safe with regards to the 'unique' constraint * O(log(n)) */ Index.prototype.remove = function (doc) { var key, self = this; if (util.isArray(doc)) { doc.forEach(function (d) { self.remove(d); }); return; } key = model.getDotValue(doc, this.fieldName); if (key === undefined && this.sparse) { return; } this.tree.delete(key, doc); }; /** * Update a document in the index * If a constraint is violated, changes are rolled back and an error thrown * Naive implementation, still in O(log(n)) */ Index.prototype.update = function (oldDoc, newDoc) { if (util.isArray(oldDoc)) { this.updateMultipleDocs(oldDoc); return; } this.remove(oldDoc); try { this.insert(newDoc); } catch (e) { this.insert(oldDoc); throw e; } }; /** * Update multiple documents in the index * If a constraint is violated, the changes need to be rolled back * and an error thrown * @param {Array of oldDoc, newDoc pairs} pairs */ Index.prototype.updateMultipleDocs = function (pairs) { var i, failingI, error; for (i = 0; i < pairs.length; i += 1) { this.remove(pairs[i].oldDoc); } for (i = 0; i < pairs.length; i += 1) { try { this.insert(pairs[i].newDoc); } catch (e) { error = e; failingI = i; break; } } // If an error was raised, roll back changes in the inverse order if (error) { for (i = 0; i < failingI; i += 1) { this.remove(pairs[i].newDoc); } for (i = 0; i < pairs.length; i += 1) { this.insert(pairs[i].oldDoc); } throw error; } }; /** * Revert an update */ Index.prototype.revertUpdate = function (oldDoc, newDoc) { var revert = []; if (!util.isArray(oldDoc)) { this.update(newDoc, oldDoc); } else { oldDoc.forEach(function (pair) { revert.push({ oldDoc: pair.newDoc, newDoc: pair.oldDoc }); }); this.update(revert); } }; // Append all elements in toAppend to array function append (array, toAppend) { var i; for (i = 0; i < toAppend.length; i += 1) { array.push(toAppend[i]); } } /** * Get all documents in index whose key match value (if it is a Thing) or one of the elements of value (if it is an array of Things) * @param {Thing} value Value to match the key against * @return {Array of documents} */ Index.prototype.getMatching = function (value) { var res, self = this; if (!util.isArray(value)) { return this.tree.search(value); } else { res = []; value.forEach(function (v) { append(res, self.getMatching(v)); }); return res; } }; /** * Get all documents in index whose key is between bounds are they are defined by query * Documents are sorted by key * @param {Query} query * @return {Array of documents} */ Index.prototype.getBetweenBounds = function (query) { return this.tree.betweenBounds(query); }; /** * Get all elements in the index * @return {Array of documents} */ Index.prototype.getAll = function () { var res = []; this.tree.executeOnEveryNode(function (node) { var i; for (i = 0; i < node.data.length; i += 1) { res.push(node.data[i]); } }); return res; }; // Interface module.exports = Index; },{"./model":8,"binary-search-tree":11,"underscore":15,"util":2}],8:[function(require,module,exports){ /** * Handle models (i.e. docs) * Serialization/deserialization * Copying */ var dateToJSON = function () { return { $$date: this.getTime() }; } , originalDateToJSON = Date.prototype.toJSON , util = require('util') , _ = require('underscore') , modifierFunctions = {} , lastStepModifierFunctions = {} , comparisonFunctions = {} , logicalOperators = {} ; /** * Check a key, throw an error if the key is non valid * @param {String} k key * @param {Model} v value, needed to treat the Date edge case * Non-treatable edge cases here: if part of the object if of the form { $$date: number } or { $$deleted: true } * Its serialized-then-deserialized version it will transformed into a Date object * But you really need to want it to trigger such behaviour, even when warned not to use '$' at the beginning of the field names... */ function checkKey (k, v) { if (k[0] === '$' && !(k === '$$date' && typeof v === 'number') && !(k === '$$deleted' && v === true)) { throw 'Field names cannot begin with the $ character'; } if (k.indexOf('.') !== -1) { throw 'Field names cannot contain a .'; } } /** * Check a DB object and throw an error if it's not valid * Works by applying the above checkKey function to all fields recursively */ function checkObject (obj) { if (util.isArray(obj)) { obj.forEach(function (o) { checkObject(o); }); } if (typeof obj === 'object' && obj !== null) { Object.keys(obj).forEach(function (k) { checkKey(k, obj[k]); checkObject(obj[k]); }); } } /** * Serialize an object to be persisted to a one-line string * For serialization/deserialization, we use the native JSON parser and not eval or Function * That gives us less freedom but data entered in the database may come from users * so eval and the like are not safe * Accepted primitive types: Number, String, Boolean, Date, null * Accepted secondary types: Objects, Arrays */ function serialize (obj) { var res; // Keep track of the fact that this is a Date object Date.prototype.toJSON = dateToJSON; res = JSON.stringify(obj, function (k, v) { checkKey(k, v); if (typeof v === undefined) { return null; } if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null) { return v; } return v; }); // Return Date to its original state Date.prototype.toJSON = originalDateToJSON; return res; } /** * From a one-line representation of an object generate by the serialize function * Return the object itself */ function deserialize (rawData) { return JSON.parse(rawData, function (k, v) { if (k === '$$date') { return new Date(v); } if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null) { return v; } if (v && v.$$date) { return v.$$date; } return v; }); } /** * Deep copy a DB object */ function deepCopy (obj) { var res; if ( typeof obj === 'boolean' || typeof obj === 'number' || typeof obj === 'string' || obj === null || (util.isDate(obj)) ) { return obj; } if (util.isArray(obj)) { res = []; obj.forEach(function (o) { res.push(o); }); return res; } if (typeof obj === 'object') { res = {}; Object.keys(obj).forEach(function (k) { res[k] = deepCopy(obj[k]); }); return res; } return undefined; // For now everything else is undefined. We should probably throw an error instead } /** * Utility functions for comparing things * Assumes type checking was already done (a and b already have the same type) * compareNSB works for numbers, strings and booleans */ function compareNSB (a, b) { if (a < b) { return -1; } if (a > b) { return 1; } return 0; } function compareArrays (a, b) { var i, comp; for (i = 0; i < Math.min(a.length, b.length); i += 1) { comp = compareThings(a[i], b[i]); if (comp !== 0) { return comp; } } // Common section was identical, longest one wins return compareNSB(a.length, b.length); } /** * Compare { things U undefined } * Things are defined as any native types (string, number, boolean, null, date) and objects * We need to compare with undefined as it will be used in indexes * In the case of objects and arrays, we compare the serialized versions * If two objects dont have the same type, the (arbitrary) type hierarchy is: undefined, null, number, strings, boolean, dates, arrays, objects * Return -1 if a < b, 1 if a > b and 0 if a = b (note that equality here is NOT the same as defined in areThingsEqual!) */ function compareThings (a, b) { var aKeys, bKeys, comp, i; // undefined if (a === undefined) { return b === undefined ? 0 : -1; } if (b === undefined) { return a === undefined ? 0 : 1; } // null if (a === null) { return b === null ? 0 : -1; } if (b === null) { return a === null ? 0 : 1; } // Numbers if (typeof a === 'number') { return typeof b === 'number' ? compareNSB(a, b) : -1; } if (typeof b === 'number') { return typeof a === 'number' ? compareNSB(a, b) : 1; } // Strings if (typeof a === 'string') { return typeof b === 'string' ? compareNSB(a, b) : -1; } if (typeof b === 'string') { return typeof a === 'string' ? compareNSB(a, b) : 1; } // Booleans if (typeof a === 'boolean') { return typeof b === 'boolean' ? compareNSB(a, b) : -1; } if (typeof b === 'boolean') { return typeof a === 'boolean' ? compareNSB(a, b) : 1; } // Dates if (util.isDate(a)) { return util.isDate(b) ? compareNSB(a.getTime(), b.getTime()) : -1; } if (util.isDate(b)) { return util.isDate(a) ? compareNSB(a.getTime(), b.getTime()) : 1; } // Arrays (first element is most significant and so on) if (util.isArray(a)) { return util.isArray(b) ? compareArrays(a, b) : -1; } if (util.isArray(b)) { return util.isArray(a) ? compareArrays(a, b) : 1; } // Objects aKeys = Object.keys(a).sort(); bKeys = Object.keys(b).sort(); for (i = 0; i < Math.min(aKeys.length, bKeys.length); i += 1) { comp = compareThings(a[aKeys[i]], b[bKeys[i]]); if (comp !== 0) { return comp; } } return compareNSB(aKeys.length, bKeys.length); } // ============================================================== // Updating documents // ============================================================== /** * The signature of modifier functions is as follows * Their structure is always the same: recursively follow the dot notation while creating * the nested documents if needed, then apply the "last step modifier" * @param {Object} obj The model to modify * @param {String} field Can contain dots, in that case that means we will set a subfield recursively * @param {Model} value */ /** * Set a field to a new value */ lastStepModifierFunctions.$set = function (obj, field, value) { obj[field] = value; }; /** * Push an element to the end of an array field */ lastStepModifierFunctions.$push = function (obj, field, value) { // Create the array if it doesn't exist if (!obj.hasOwnProperty(field)) { obj[field] = []; } if (!util.isArray(obj[field])) { throw "Can't $push an element on non-array values"; } if (value !== null && typeof value === 'object' && value.$each) { if (Object.keys(value).length > 1) { throw "Can't use another field in conjunction with $each"; } if (!util.isArray(value.$each)) { throw "$each requires an array value"; } value.$each.forEach(function (v) { obj[field].push(v); }); } else { obj[field].push(value); } }; /** * Add an element to an array field only if it is not already in it * No modification if the element is already in the array * Note that it doesn't check whether the original array contains duplicates */ lastStepModifierFunctions.$addToSet = function (obj, field, value) { var addToSet = true; // Create the array if it doesn't exist if (!obj.hasOwnProperty(field)) { obj[field] = []; } if (!util.isArray(obj[field])) { throw "Can't $addToSet an element on non-array values"; } if (value !== null && typeof value === 'object' && value.$each) { if (Object.keys(value).length > 1) { throw "Can't use another field in conjunction with $each"; } if (!util.isArray(value.$each)) { throw "$each requires an array value"; } value.$each.forEach(function (v) { lastStepModifierFunctions.$addToSet(obj, field, v); }); } else { obj[field].forEach(function (v) { if (compareThings(v, value) === 0) { addToSet = false; } }); if (addToSet) { obj[field].push(value); } } }; /** * Remove the first or last element of an array */ lastStepModifierFunctions.$pop = function (obj, field, value) { if (!util.isArray(obj[field])) { throw "Can't $pop an element from non-array values"; } if (typeof value !== 'number') { throw value + " isn't an integer, can't use it with $pop"; } if (value === 0) { return; } if (value > 0) { obj[field] = obj[field].slice(0, obj[field].length - 1); } else { obj[field] = obj[field].slice(1); } }; /** * Increment a numeric field's value */ lastStepModifierFunctions.$inc = function (obj, field, value) { if (typeof value !== 'number') { throw value + " must be a number"; } if (typeof obj[field] !== 'number') { if (!_.has(obj, field)) { obj[field] = value; } else { throw "Don't use the $inc modifier on non-number fields"; } } else { obj[field] += value; } }; // Given its name, create the complete modifier function function createModifierFunction (modifier) { return function (obj, field, value) { var fieldParts = typeof field === 'string' ? field.split('.') : field; if (fieldParts.length === 1) { lastStepModifierFunctions[modifier](obj, field, value); } else { obj[fieldParts[0]] = obj[fieldParts[0]] || {}; modifierFunctions[modifier](obj[fieldParts[0]], fieldParts.slice(1), value); } }; } // Actually create all modifier functions Object.keys(lastStepModifierFunctions).forEach(function (modifier) { modifierFunctions[modifier] = createModifierFunction(modifier); }); /** * Modify a DB object according to an update query * For now the updateQuery only replaces the object */ function modify (obj, updateQuery) { var keys = Object.keys(updateQuery) , firstChars = _.map(keys, function (item) { return item[0]; }) , dollarFirstChars = _.filter(firstChars, function (c) { return c === '$'; }) , newDoc, modifiers ; if (keys.indexOf('_id') !== -1 && updateQuery._id !== obj._id) { throw "You cannot change a document's _id"; } if (dollarFirstChars.length !== 0 && dollarFirstChars.length !== firstChars.length) { throw "You cannot mix modifiers and normal fields"; } if (dollarFirstChars.length === 0) { // Simply replace the object with the update query contents newDoc = deepCopy(updateQuery); newDoc._id = obj._id; } else { // Apply modifiers modifiers = _.uniq(keys); newDoc = deepCopy(obj); modifiers.forEach(function (m) { var keys; if (!modifierFunctions[m]) { throw "Unknown modifier " + m; } try { keys = Object.keys(updateQuery[m]); } catch (e) { throw "Modifier " + m + "'s argument must be an object"; } keys.forEach(function (k) { modifierFunctions[m](newDoc, k, updateQuery[m][k]); }); }); } // Check result is valid and return it checkObject(newDoc); if (obj._id !== newDoc._id) { throw "You can't change a document's _id"; } return newDoc; }; // ============================================================== // Finding documents // ============================================================== /** * Get a value from object with dot notation * @param {Object} obj * @param {String} field */ function getDotValue (obj, field) { var fieldParts = typeof field === 'string' ? field.split('.') : field; if (!obj) { return undefined; } // field cannot be empty so that means we should return undefined so that nothing can match if (fieldParts.length === 1) { return obj[fieldParts[0]]; } else { return getDotValue(obj[fieldParts[0]], fieldParts.slice(1)); } } /** * Check whether 'things' are equal * Things are defined as any native types (string, number, boolean, null, date) and objects * In the case of object, we check deep equality * Returns true if they are, false otherwise */ function areThingsEqual (a, b) { var aKeys , bKeys , i; // Strings, booleans, numbers, null if (a === null || typeof a === 'string' || typeof a === 'boolean' || typeof a === 'number' || b === null || typeof b === 'string' || typeof b === 'boolean' || typeof b === 'number') { return a === b; } // Dates if (util.isDate(a) || util.isDate(b)) { return util.isDate(a) && util.isDate(b) && a.getTime() === b.getTime(); } // Arrays (no match since arrays are used as a $in) // undefined (no match since they mean field doesn't exist and can't be serialized) if (util.isArray(a) || util.isArray(b) || a === undefined || b === undefined) { return false; } // General objects (check for deep equality) // a and b should be objects at this point try { aKeys = Object.keys(a); bKeys = Object.keys(b); } catch (e) { return false; } if (aKeys.length !== bKeys.length) { return false; } for (i = 0; i < aKeys.length; i += 1) { if (bKeys.indexOf(aKeys[i]) === -1) { return false; } if (!areThingsEqual(a[aKeys[i]], b[aKeys[i]])) { return false; } } return true; } /** * Check that two values are comparable */ function areComparable (a, b) { if (typeof a !== 'string' && typeof a !== 'number' && !util.isDate(a) && typeof b !== 'string' && typeof b !== 'number' && !util.isDate(b)) { return false; } if (typeof a !== typeof b) { return false; } return true; } /** * Arithmetic and comparison operators * @param {Native value} a Value in the object * @param {Native value} b Value in the query */ comparisonFunctions.$lt = function (a, b) { return areComparable(a, b) && a < b; }; comparisonFunctions.$lte = function (a, b) { return areComparable(a, b) && a <= b; }; comparisonFunctions.$gt = function (a, b) { return areComparable(a, b) && a > b; }; comparisonFunctions.$gte = function (a, b) { return areComparable(a, b) && a >= b; }; comparisonFunctions.$ne = function (a, b) { if (!a) { return true; } return !areThingsEqual(a, b); }; comparisonFunctions.$in = function (a, b) { var i; if (!util.isArray(b)) { throw "$in operator called with a non-array"; } for (i = 0; i < b.length; i += 1) { if (areThingsEqual(a, b[i])) { return true; } } return false; }; comparisonFunctions.$nin = function (a, b) { if (!util.isArray(b)) { throw "$nin operator called with a non-array"; } return !comparisonFunctions.$in(a, b); }; comparisonFunctions.$regex = function (a, b) { if (!util.isRegExp(b)) { throw "$regex operator called with non regular expression"; } if (typeof a !== 'string') { return false } else { return b.test(a); } }; comparisonFunctions.$exists = function (value, exists) { if (exists || exists === '') { // This will be true for all values of exists except false, null, undefined and 0 exists = true; // That's strange behaviour (we should only use true/false) but that's the way Mongo does it... } else { exists = false; } if (value === undefined) { return !exists } else { return exists; } }; /** * Match any of the subqueries * @param {Model} obj * @param {Array of Queries} query */ logicalOperators.$or = function (obj, query) { var i; if (!util.isArray(query)) { throw "$or operator used without an array"; } for (i = 0; i < query.length; i += 1) { if (match(obj, query[i])) { return true; } } return false; }; /** * Match all of the subqueries * @param {Model} obj * @param {Array of Queries} query */ logicalOperators.$and = function (obj, query) { var i; if (!util.isArray(query)) { throw "$and operator used without an array"; } for (i = 0; i < query.length; i += 1) { if (!match(obj, query[i])) { return false; } } return true; }; /** * Inverted match of the query * @param {Model} obj * @param {Query} query */ logicalOperators.$not = function (obj, query) { return !match(obj, query); }; /** * Tell if a given document matches a query * @param {Object} obj Document to check * @param {Object} query */ function match (obj, query) { var queryKeys = Object.keys(query) , i ; for (i = 0; i < queryKeys.length; i += 1) { if (!matchQueryPart(obj, queryKeys[i], query[queryKeys[i]])) { return false; } } return true; }; /** * Match an object against a specific { key: value } part of a query */ function matchQueryPart (obj, queryKey, queryValue) { var objValue = getDotValue(obj, queryKey) , i , keys, firstChars, dollarFirstChars ; // Query part begins with a logical operator: apply it if (queryKey[0] === '$') { if (!logicalOperators[queryKey]) { throw "Unknown logical operator " + queryKey; } return logicalOperators[queryKey](obj, queryValue); } // Check if the object value is an array treat it as an array of { obj, query } // Where there needs to be at least one match if (util.isArray(objValue)) { for (i = 0; i < objValue.length; i += 1) { if (matchQueryPart({ k: objValue[i] }, 'k', queryValue)) { return true; } // k here could be any string } return false; } // queryValue is an actual object. Determine whether it contains comparison operators // or only normal fields. Mixed objects are not allowed if (queryValue !== null && typeof queryValue === 'object' && !util.isRegExp(queryValue)) { keys = Object.keys(queryValue); firstChars = _.map(keys, function (item) { return item[0]; }); dollarFirstChars = _.filter(firstChars, function (c) { return c === '$'; }); if (dollarFirstChars.length !== 0 && dollarFirstChars.length !== firstChars.length) { throw "You cannot mix operators and normal fields"; } // queryValue is an object of this form: { $comparisonOperator1: value1, ... } if (dollarFirstChars.length > 0) { for (i = 0; i < keys.length; i += 1) { if (!comparisonFunctions[keys[i]]) { throw "Unknown comparison function " + keys[i]; } if (!comparisonFunctions[keys[i]](objValue, queryValue[keys[i]])) { return false; } } return true; } } // Using regular expressions with basic querying if (util.isRegExp(queryValue)) { return comparisonFunctions.$regex(objValue, queryValue); } // queryValue is either a native value or a normal object // Basic matching is possible if (!areThingsEqual(objValue, queryValue)) { return false; } return true; } // Interface module.exports.serialize = serialize; module.exports.deserialize = deserialize; module.exports.deepCopy = deepCopy; module.exports.checkObject = checkObject; module.exports.modify = modify; module.exports.getDotValue = getDotValue; module.exports.match = match; module.exports.areThingsEqual = areThingsEqual; module.exports.compareThings = compareThings; },{"underscore":15,"util":2}],9:[function(require,module,exports){ /** * Handle every persistence-related task * The interface Datastore expects to be implemented is * * Persistence.loadDatabase(callback) and callback has signature err * * Persistence.persistNewState(newDocs, callback) where newDocs is an array of documents and callback has signature err * * Shim for the browser */ /** * Create a new Persistence object for database options.db * For now, no browser persistence supported, in-memory only mode forced * @param {Datastore} options.db */ function Persistence (options) { this.db = options.db; this.db.inMemoryOnly = true; this.db.filename = null; this.inMemoryOnly = true; }; /** * No persistence in the browser (for now) */ Persistence.prototype.persistNewState = function (newDocs, cb) { if (cb) { return cb(); } }; /** * No persistence in the browser (for now) */ Persistence.prototype.loadDatabase = function (cb) { if (cb) { return cb(); } }; // Interface module.exports = Persistence; },{}],10:[function(require,module,exports){ (function(process){/*global setImmediate: false, setTimeout: false, console: false */ (function () { var async = {}; // global on the server, window in the browser var root, previous_async; root = this; if (root != null) { previous_async = root.async; } async.noConflict = function () { root.async = previous_async; return async; }; function only_once(fn) { var called = false; return function() { if (called) throw new Error("Callback was already called."); called = true; fn.apply(root, arguments); } } //// cross-browser compatiblity functions //// var _each = 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 = []; _each(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); } _each(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)) { if (typeof setImmediate === 'function') { async.nextTick = function (fn) { // not a direct alias for IE10 compatibility setImmediate(fn); }; async.setImmediate = async.nextTick; } else { async.nextTick = function (fn) { setTimeout(fn, 0); }; async.setImmediate = async.nextTick; } } else { async.nextTick = process.nextTick; if (typeof setImmediate !== 'undefined') { async.setImmediate = setImmediate; } else { async.setImmediate = async.nextTick; } } async.each = function (arr, iterator, callback) { callback = callback || function () {}; if (!arr.length) { return callback(); } var completed = 0; _each(arr, function (x) { iterator(x, only_once(function (err) { if (err) { callback(err); callback = function () {}; } else { completed += 1; if (completed >= arr.length) { callback(null); } } })); }); }; async.forEach = async.each; async.eachSeries = 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.forEachSeries = async.eachSeries; async.eachLimit = function (arr, limit, iterator, callback) { var fn = _eachLimit(limit); fn.apply(null, [arr, iterator, callback]); }; async.forEachLimit = async.eachLimit; var _eachLimit = function (limit) { return function (arr, 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.each].concat(args)); }; }; var doParallelLimit = function(limit, fn) { return function () { var args = Array.prototype.slice.call(arguments); return fn.apply(null, [_eachLimit(limit)].concat(args)); }; }; var doSeries = function (fn) { return function () { var args = Array.prototype.slice.call(arguments); return fn.apply(null, [async.eachSeries].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); async.mapLimit = function (arr, limit, iterator, callback) { return _mapLimit(limit)(arr, iterator, callback); }; var _mapLimit = function(limit) { return doParallelLimit(limit, _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.eachSeries(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.each(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.each(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 () { _each(listeners.slice(0), function (fn) { fn(); }); }; addListener(function () { if (_keys(results).length === keys.length) { callback(null, results); callback = function () {}; } }); _each(keys, function (k) { var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k]; var taskCallback = function (err) { var args = Array.prototype.slice.call(arguments, 1); if (args.length <= 1) { args = args[0]; } if (err) { var safeResults = {}; _each(_keys(results), function(rkey) { safeResults[rkey] = results[rkey]; }); safeResults[k] = args; callback(err, safeResults); // stop subsequent errors hitting callback multiple times callback = function () {}; } else { results[k] = args; async.setImmediate(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.constructor !== Array) { var err = new Error('First argument to waterfall must be an array of functions'); return callback(err); } if (!tasks.length) { return callback(); } var wrapIterator = function (iterator) { return function (err) { if (err) { callback.apply(null, arguments); 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.setImmediate(function () { iterator.apply(null, args); }); } }; }; wrapIterator(async.iterator(tasks))(); }; var _parallel = function(eachfn, tasks, callback) { callback = callback || function () {}; if (tasks.constructor === Array) { eachfn.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 = {}; eachfn.each(_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.parallel = function (tasks, callback) { _parallel({ map: async.map, each: async.each }, tasks, callback); }; async.parallelLimit = function(tasks, limit, callback) { _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback); }; 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.eachSeries(_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.doWhilst = function (iterator, test, callback) { iterator(function (err) { if (err) { return callback(err); } if (test()) { async.doWhilst(iterator, test, 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.doUntil = function (iterator, test, callback) { iterator(function (err) { if (err) { return callback(err); } if (!test()) { async.doUntil(iterator, test, callback); } else { callback(); } }); }; async.queue = function (worker, concurrency) { if (concurrency === undefined) { concurrency = 1; } function _insert(q, data, pos, callback) { if(data.constructor !== Array) { data = [data]; } _each(data, function(task) { var item = { data: task, callback: typeof callback === 'function' ? callback : null }; if (pos) { q.tasks.unshift(item); } else { q.tasks.push(item); } if (q.saturated && q.tasks.length === concurrency) { q.saturated(); } async.setImmediate(q.process); }); } var workers = 0; var q = { tasks: [], concurrency: concurrency, saturated: null, empty: null, drain: null, push: function (data, callback) { _insert(q, data, false, callback); }, unshift: function (data, callback) { _insert(q, data, true, callback); }, 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; var next = function () { workers -= 1; if (task.callback) { task.callback.apply(task, arguments); } if (q.drain && q.tasks.length + workers === 0) { q.drain(); } q.process(); }; var cb = only_once(next); worker(task.data, cb); } }, length: function () { return q.tasks.length; }, running: function () { return workers; } }; return q; }; async.cargo = function (worker, payload) { var working = false, tasks = []; var cargo = { tasks: tasks, payload: payload, saturated: null, empty: null, drain: null, push: function (data, callback) { if(data.constructor !== Array) { data = [data]; } _each(data, function(task) { tasks.push({ data: task, callback: typeof callback === 'function' ? callback : null }); if (cargo.saturated && tasks.length === payload) { cargo.saturated(); } }); async.setImmediate(cargo.process); }, process: function process() { if (working) return; if (tasks.length === 0) { if(cargo.drain) cargo.drain(); return; } var ts = typeof payload === 'number' ? tasks.splice(0, payload) : tasks.splice(0); var ds = _map(ts, function (task) { return task.data; }); if(cargo.empty) cargo.empty(); working = true; worker(ds, function () { working = false; var args = arguments; _each(ts, function (data) { if (data.callback) { data.callback.apply(null, args); } }); process(); }); }, length: function () { return tasks.length; }, running: function () { return working; } }; return cargo; }; 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]) { _each(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.memo = memo; memoized.unmemoized = fn; return memoized; }; async.unmemoize = function (fn) { return function () { return (fn.unmemoized || fn).apply(null, arguments); }; }; async.times = function (count, iterator, callback) { var counter = []; for (var i = 0; i < count; i++) { counter.push(i); } return async.map(counter, iterator, callback); }; async.timesSeries = function (count, iterator, callback) { var counter = []; for (var i = 0; i < count; i++) { counter.push(i); } return async.mapSeries(counter, iterator, callback); }; async.compose = function (/* functions... */) { var fns = Array.prototype.reverse.call(arguments); return function () { var that = this; var args = Array.prototype.slice.call(arguments); var callback = args.pop(); async.reduce(fns, args, function (newargs, fn, cb) { fn.apply(that, newargs.concat([function () { var err = arguments[0]; var nextargs = Array.prototype.slice.call(arguments, 1); cb(err, nextargs); }])) }, function (err, results) { callback.apply(that, [err].concat(results)); }); }; }; var _applyEach = function (eachfn, fns /*args...*/) { var go = function () { var that = this; var args = Array.prototype.slice.call(arguments); var callback = args.pop(); return eachfn(fns, function (fn, cb) { fn.apply(that, args.concat([cb])); }, callback); }; if (arguments.length > 2) { var args = Array.prototype.slice.call(arguments, 2); return go.apply(this, args); } else { return go; } }; async.applyEach = doParallel(_applyEach); async.applyEachSeries = doSeries(_applyEach); async.forever = function (fn, callback) { function next(err) { if (err) { if (callback) { return callback(err); } throw err; } fn(next); } next(); }; // AMD / RequireJS if (typeof define !== 'undefined' && define.amd) { define([], function () { return async; }); } // Node.js else if (typeof module !== 'undefined' && module.exports) { module.exports = async; } // included directly via