com.vaadin.polymer.public.bower_components.pouchdb-find.dist.pouchdb.find.js Maven / Gradle / Ivy
The newest version!
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pouchdbFind = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
return results.slice(skip);
}
return results;
}
function rowToDocId(row) {
var val = row.value;
// Users can explicitly specify a joined doc _id, or it
// defaults to the doc _id that emitted the key/value.
var docId = (val && typeof val === 'object' && val._id) || row.id;
return docId;
}
function emitError(db, e) {
try {
db.emit('error', e);
} catch (err) {
console.error(
'The user\'s map/reduce function threw an uncaught error.\n' +
'You can debug this error by doing:\n' +
'myDatabase.on(\'error\', function (err) { debugger; });\n' +
'Please double-check your map/reduce function.');
console.error(e);
}
}
function tryCode(db, fun, args) {
// emit an event if there was an error thrown by a map/reduce function.
// putting try/catches in a single function also avoids deoptimizations.
try {
return {
output : fun.apply(null, args)
};
} catch (e) {
emitError(db, e);
return {error: e};
}
}
function checkQueryParseError(options, fun) {
var startkeyName = options.descending ? 'endkey' : 'startkey';
var endkeyName = options.descending ? 'startkey' : 'endkey';
if (typeof options[startkeyName] !== 'undefined' &&
typeof options[endkeyName] !== 'undefined' &&
collate(options[startkeyName], options[endkeyName]) > 0) {
throw new QueryParseError('No rows can match your key range, reverse your ' +
'start_key and end_key or set {descending : true}');
} else if (fun.reduce && options.reduce !== false) {
if (options.include_docs) {
throw new QueryParseError('{include_docs:true} is invalid for reduce');
} else if (options.keys && options.keys.length > 1 &&
!options.group && !options.group_level) {
throw new QueryParseError('Multi-key fetches for reduce views must use {group: true}');
}
}
if (options.group_level) {
if (typeof options.group_level !== 'number') {
throw new QueryParseError('Invalid value for integer: "' + options.group_level + '"');
}
if (options.group_level < 0) {
throw new QueryParseError('Invalid value for positive integer: ' +
'"' + options.group_level + '"');
}
}
}
function defaultsTo(value) {
return function (reason) {
/* istanbul ignore else */
if (reason.status === 404) {
return value;
} else {
throw reason;
}
};
}
function createIndexer(def) {
var pluginName = def.name;
var mapper = def.mapper;
var reducer = def.reducer;
var ddocValidator = def.ddocValidator;
// returns a promise for a list of docs to update, based on the input docId.
// the order doesn't matter, because post-3.2.0, bulkDocs
// is an atomic operation in all three adapters.
function getDocsToPersist(docId, view, docIdsToChangesAndEmits) {
var metaDocId = '_local/doc_' + docId;
var defaultMetaDoc = {_id: metaDocId, keys: []};
var docData = docIdsToChangesAndEmits[docId];
var indexableKeysToKeyValues = docData.indexableKeysToKeyValues;
var changes = docData.changes;
function getMetaDoc() {
if (isGenOne(changes)) {
// generation 1, so we can safely assume initial state
// for performance reasons (avoids unnecessary GETs)
return Promise.resolve(defaultMetaDoc);
}
return view.db.get(metaDocId)["catch"](defaultsTo(defaultMetaDoc));
}
function getKeyValueDocs(metaDoc) {
if (!metaDoc.keys.length) {
// no keys, no need for a lookup
return Promise.resolve({rows: []});
}
return view.db.allDocs({
keys: metaDoc.keys,
include_docs: true
});
}
function processKvDocs(metaDoc, kvDocsRes) {
var kvDocs = [];
var oldKeysMap = {};
for (var i = 0, len = kvDocsRes.rows.length; i < len; i++) {
var row = kvDocsRes.rows[i];
var doc = row.doc;
if (!doc) { // deleted
continue;
}
kvDocs.push(doc);
oldKeysMap[doc._id] = true;
doc._deleted = !indexableKeysToKeyValues[doc._id];
if (!doc._deleted) {
var keyValue = indexableKeysToKeyValues[doc._id];
if ('value' in keyValue) {
doc.value = keyValue.value;
}
}
}
var newKeys = Object.keys(indexableKeysToKeyValues);
newKeys.forEach(function (key) {
if (!oldKeysMap[key]) {
// new doc
var kvDoc = {
_id: key
};
var keyValue = indexableKeysToKeyValues[key];
if ('value' in keyValue) {
kvDoc.value = keyValue.value;
}
kvDocs.push(kvDoc);
}
});
metaDoc.keys = utils.uniq(newKeys.concat(metaDoc.keys));
kvDocs.push(metaDoc);
return kvDocs;
}
return getMetaDoc().then(function (metaDoc) {
return getKeyValueDocs(metaDoc).then(function (kvDocsRes) {
return processKvDocs(metaDoc, kvDocsRes);
});
});
}
// updates all emitted key/value docs and metaDocs in the mrview database
// for the given batch of documents from the source database
function saveKeyValues(view, docIdsToChangesAndEmits, seq) {
var seqDocId = '_local/lastSeq';
return view.db.get(seqDocId)[
"catch"](defaultsTo({_id: seqDocId, seq: 0}))
.then(function (lastSeqDoc) {
var docIds = Object.keys(docIdsToChangesAndEmits);
return Promise.all(docIds.map(function (docId) {
return getDocsToPersist(docId, view, docIdsToChangesAndEmits);
})).then(function (listOfDocsToPersist) {
var docsToPersist = utils.flatten(listOfDocsToPersist);
lastSeqDoc.seq = seq;
docsToPersist.push(lastSeqDoc);
// write all docs in a single operation, update the seq once
return view.db.bulkDocs({docs : docsToPersist});
});
});
}
function getQueue(view) {
var viewName = typeof view === 'string' ? view : view.name;
var queue = persistentQueues[viewName];
if (!queue) {
queue = persistentQueues[viewName] = new TaskQueue();
}
return queue;
}
function updateView(view) {
return utils.sequentialize(getQueue(view), function () {
return updateViewInQueue(view);
})();
}
function updateViewInQueue(view) {
// bind the emit function once
var mapResults;
var doc;
function emit(key, value) {
var output = {id: doc._id, key: normalizeKey(key)};
// Don't explicitly store the value unless it's defined and non-null.
// This saves on storage space, because often people don't use it.
if (typeof value !== 'undefined' && value !== null) {
output.value = normalizeKey(value);
}
mapResults.push(output);
}
var mapFun = mapper(view.mapFun, emit);
var currentSeq = view.seq || 0;
function processChange(docIdsToChangesAndEmits, seq) {
return function () {
return saveKeyValues(view, docIdsToChangesAndEmits, seq);
};
}
var queue = new TaskQueue();
return new Promise(function (resolve, reject) {
function complete() {
queue.finish().then(function () {
view.seq = currentSeq;
resolve();
});
}
function processNextBatch() {
view.sourceDB.changes({
conflicts: true,
include_docs: true,
style: 'all_docs',
since: currentSeq,
limit: CHANGES_BATCH_SIZE
}).on('complete', function (response) {
var results = response.results;
if (!results.length) {
return complete();
}
var docIdsToChangesAndEmits = {};
for (var i = 0, l = results.length; i < l; i++) {
var change = results[i];
if (change.doc._id[0] !== '_') {
mapResults = [];
doc = change.doc;
if (!doc._deleted) {
tryCode(view.sourceDB, mapFun, [doc]);
}
mapResults.sort(sortByKeyThenValue);
var indexableKeysToKeyValues = {};
var lastKey;
for (var j = 0, jl = mapResults.length; j < jl; j++) {
var obj = mapResults[j];
var complexKey = [obj.key, obj.id];
if (collate(obj.key, lastKey) === 0) {
complexKey.push(j); // dup key+id, so make it unique
}
var indexableKey = toIndexableString(complexKey);
indexableKeysToKeyValues[indexableKey] = obj;
lastKey = obj.key;
}
docIdsToChangesAndEmits[change.doc._id] = {
indexableKeysToKeyValues: indexableKeysToKeyValues,
changes: change.changes
};
}
currentSeq = change.seq;
}
queue.add(processChange(docIdsToChangesAndEmits, currentSeq));
if (results.length < CHANGES_BATCH_SIZE) {
return complete();
}
return processNextBatch();
}).on('error', onError);
/* istanbul ignore next */
function onError(err) {
reject(err);
}
}
processNextBatch();
});
}
function reduceView(view, results, options) {
if (options.group_level === 0) {
delete options.group_level;
}
var shouldGroup = options.group || options.group_level;
var reduceFun = reducer(view.reduceFun);
var groups = [];
var lvl = options.group_level;
results.forEach(function (e) {
var last = groups[groups.length - 1];
var key = shouldGroup ? e.key : null;
// only set group_level for array keys
if (shouldGroup && Array.isArray(key) && typeof lvl === 'number') {
key = key.length > lvl ? key.slice(0, lvl) : key;
}
if (last && collate(last.key[0][0], key) === 0) {
last.key.push([key, e.id]);
last.value.push(e.value);
return;
}
groups.push({key: [
[key, e.id]
], value: [e.value]});
});
for (var i = 0, len = groups.length; i < len; i++) {
var e = groups[i];
var reduceTry = tryCode(view.sourceDB, reduceFun, [e.key, e.value, false]);
// TODO: can't do instanceof BuiltInError because this class is buried
// in mapreduce.js
if (reduceTry.error && /BuiltInError/.test(reduceTry.error.constructor)) {
// CouchDB returns an error if a built-in errors out
throw reduceTry.error;
}
// CouchDB just sets the value to null if a non-built-in errors out
e.value = reduceTry.error ? null : reduceTry.output;
e.key = e.key[0][0];
}
// no total_rows/offset when reducing
return {rows: sliceResults(groups, options.limit, options.skip)};
}
function queryView(view, opts) {
return utils.sequentialize(getQueue(view), function () {
return queryViewInQueue(view, opts);
})();
}
function queryViewInQueue(view, opts) {
var totalRows;
var shouldReduce = view.reduceFun && opts.reduce !== false;
var skip = opts.skip || 0;
if (typeof opts.keys !== 'undefined' && !opts.keys.length) {
// equivalent query
opts.limit = 0;
delete opts.keys;
}
function fetchFromView(viewOpts) {
viewOpts.include_docs = true;
return view.db.allDocs(viewOpts).then(function (res) {
totalRows = res.total_rows;
return res.rows.map(function (result) {
// implicit migration - in older versions of PouchDB,
// we explicitly stored the doc as {id: ..., key: ..., value: ...}
// this is tested in a migration test
/* istanbul ignore next */
if ('value' in result.doc && typeof result.doc.value === 'object' &&
result.doc.value !== null) {
var keys = Object.keys(result.doc.value).sort();
// this detection method is not perfect, but it's unlikely the user
// emitted a value which was an object with these 3 exact keys
var expectedKeys = ['id', 'key', 'value'];
if (!(keys < expectedKeys || keys > expectedKeys)) {
return result.doc.value;
}
}
var parsedKeyAndDocId = pouchCollate.parseIndexableString(result.doc._id);
return {
key: parsedKeyAndDocId[0],
id: parsedKeyAndDocId[1],
value: ('value' in result.doc ? result.doc.value : null)
};
});
});
}
function onMapResultsReady(rows) {
var finalResults;
if (shouldReduce) {
finalResults = reduceView(view, rows, opts);
} else {
finalResults = {
total_rows: totalRows,
offset: skip,
rows: rows
};
}
if (opts.include_docs) {
var docIds = utils.uniq(rows.map(rowToDocId));
return view.sourceDB.allDocs({
keys: docIds,
include_docs: true,
conflicts: opts.conflicts,
attachments: opts.attachments,
binary: opts.binary
}).then(function (allDocsRes) {
var docIdsToDocs = {};
allDocsRes.rows.forEach(function (row) {
if (row.doc) {
docIdsToDocs['$' + row.id] = row.doc;
}
});
rows.forEach(function (row) {
var docId = rowToDocId(row);
var doc = docIdsToDocs['$' + docId];
if (doc) {
row.doc = doc;
}
});
return finalResults;
});
} else {
return finalResults;
}
}
var flatten = function (array) {
return array.reduce(function (prev, cur) {
return prev.concat(cur);
});
};
if (typeof opts.keys !== 'undefined') {
var keys = opts.keys;
var fetchPromises = keys.map(function (key) {
var viewOpts = {
startkey : toIndexableString([key]),
endkey : toIndexableString([key, {}])
};
return fetchFromView(viewOpts);
});
return Promise.all(fetchPromises).then(flatten).then(onMapResultsReady);
} else { // normal query, no 'keys'
var viewOpts = {
descending : opts.descending
};
if (typeof opts.startkey !== 'undefined') {
viewOpts.startkey = opts.descending ?
toIndexableString([opts.startkey, {}]) :
toIndexableString([opts.startkey]);
}
if (typeof opts.endkey !== 'undefined') {
var inclusiveEnd = opts.inclusive_end !== false;
if (opts.descending) {
inclusiveEnd = !inclusiveEnd;
}
viewOpts.endkey = toIndexableString(inclusiveEnd ? [opts.endkey, {}] : [opts.endkey]);
}
if (typeof opts.key !== 'undefined') {
var keyStart = toIndexableString([opts.key]);
var keyEnd = toIndexableString([opts.key, {}]);
if (viewOpts.descending) {
viewOpts.endkey = keyStart;
viewOpts.startkey = keyEnd;
} else {
viewOpts.startkey = keyStart;
viewOpts.endkey = keyEnd;
}
}
if (!shouldReduce) {
if (typeof opts.limit === 'number') {
viewOpts.limit = opts.limit;
}
viewOpts.skip = skip;
}
return fetchFromView(viewOpts).then(onMapResultsReady);
}
}
function localViewCleanup(db) {
return db.get('_local/' + pluginName).then(function (metaDoc) {
var docsToViews = {};
Object.keys(metaDoc.views).forEach(function (fullViewName) {
var parts = parseViewName(fullViewName);
var designDocName = '_design/' + parts[0];
var viewName = parts[1];
docsToViews[designDocName] = docsToViews[designDocName] || {};
docsToViews[designDocName][viewName] = true;
});
var opts = {
keys : Object.keys(docsToViews),
include_docs : true
};
return db.allDocs(opts).then(function (res) {
var viewsToStatus = {};
res.rows.forEach(function (row) {
var ddocName = row.key.substring(8);
Object.keys(docsToViews[row.key]).forEach(function (viewName) {
var fullViewName = ddocName + '/' + viewName;
/* istanbul ignore if */
if (!metaDoc.views[fullViewName]) {
// new format, without slashes, to support PouchDB 2.2.0
// migration test in pouchdb's browser.migration.js verifies this
fullViewName = viewName;
}
var viewDBNames = Object.keys(metaDoc.views[fullViewName]);
// design doc deleted, or view function nonexistent
var statusIsGood = row.doc && row.doc.views && row.doc.views[viewName];
viewDBNames.forEach(function (viewDBName) {
viewsToStatus[viewDBName] = viewsToStatus[viewDBName] || statusIsGood;
});
});
});
var dbsToDelete = Object.keys(viewsToStatus).filter(function (viewDBName) {
return !viewsToStatus[viewDBName];
});
var destroyPromises = dbsToDelete.map(function (viewDBName) {
return utils.sequentialize(getQueue(viewDBName), function () {
return new db.constructor(viewDBName, db.__opts).destroy();
})();
});
return Promise.all(destroyPromises).then(function () {
return {ok: true};
});
});
}, defaultsTo({ok: true}));
}
function queryPromised(db, fun, opts) {
if (typeof fun !== 'string') {
// temp_view
checkQueryParseError(opts, fun);
var createViewOpts = {
db : db,
viewName : 'temp_view/temp_view',
map : fun.map,
reduce : fun.reduce,
temporary : true,
pluginName: pluginName
};
tempViewQueue.add(function () {
return createView(createViewOpts).then(function (view) {
function cleanup() {
return view.db.destroy();
}
return utils.fin(updateView(view).then(function () {
return queryView(view, opts);
}), cleanup);
});
});
return tempViewQueue.finish();
} else {
// persistent view
var fullViewName = fun;
var parts = parseViewName(fullViewName);
var designDocName = parts[0];
var viewName = parts[1];
return db.get('_design/' + designDocName).then(function (doc) {
var fun = doc.views && doc.views[viewName];
if (!fun) {
// basic validator; it's assumed that every subclass would want this
throw new NotFoundError('ddoc ' + doc._id + ' has no view named ' +
viewName);
}
ddocValidator(doc, viewName);
checkQueryParseError(opts, fun);
var createViewOpts = {
db : db,
viewName : fullViewName,
map : fun.map,
reduce : fun.reduce,
pluginName: pluginName
};
return createView(createViewOpts).then(function (view) {
if (opts.stale === 'ok' || opts.stale === 'update_after') {
if (opts.stale === 'update_after') {
process.nextTick(function () {
updateView(view);
});
}
return queryView(view, opts);
} else { // stale not ok
return updateView(view).then(function () {
return queryView(view, opts);
});
}
});
});
}
}
var query = function (fun, opts, callback) {
var db = this;
if (typeof opts === 'function') {
callback = opts;
opts = {};
}
opts = utils.extend(true, {}, opts);
if (typeof fun === 'function') {
fun = {map : fun};
}
var promise = Promise.resolve().then(function () {
return queryPromised(db, fun, opts);
});
utils.promisedCallback(promise, callback);
return promise;
};
var viewCleanup = utils.callbackify(function () {
var db = this;
return localViewCleanup(db);
});
return {
query: query,
viewCleanup: viewCleanup
};
}
module.exports = createIndexer;
}).call(this,_dereq_(34))
},{"1":1,"26":26,"3":3,"34":34,"5":5}],3:[function(_dereq_,module,exports){
'use strict';
/*
* Simple task queue to sequentialize actions. Assumes callbacks will eventually fire (once).
*/
var Promise = _dereq_(5).Promise;
function TaskQueue() {
this.promise = new Promise(function (fulfill) {fulfill(); });
}
TaskQueue.prototype.add = function (promiseFactory) {
this.promise = this.promise["catch"](function () {
// just recover
}).then(function () {
return promiseFactory();
});
return this.promise;
};
TaskQueue.prototype.finish = function () {
return this.promise;
};
module.exports = TaskQueue;
},{"5":5}],4:[function(_dereq_,module,exports){
'use strict';
var upsert = _dereq_(31).upsert;
module.exports = function (db, doc, diffFun) {
return upsert.apply(db, [doc, diffFun]);
};
},{"31":31}],5:[function(_dereq_,module,exports){
(function (process){
'use strict';
/* istanbul ignore if */
exports.Promise = _dereq_(29);
exports.inherits = _dereq_(23);
exports.extend = _dereq_(28);
var argsarray = _dereq_(18);
/* istanbul ignore next */
exports.promisedCallback = function (promise, callback) {
if (callback) {
promise.then(function (res) {
process.nextTick(function () {
callback(null, res);
});
}, function (reason) {
process.nextTick(function () {
callback(reason);
});
});
}
return promise;
};
/* istanbul ignore next */
exports.callbackify = function (fun) {
return argsarray(function (args) {
var cb = args.pop();
var promise = fun.apply(this, args);
if (typeof cb === 'function') {
exports.promisedCallback(promise, cb);
}
return promise;
});
};
// Promise finally util similar to Q.finally
/* istanbul ignore next */
exports.fin = function (promise, cb) {
return promise.then(function (res) {
var promise2 = cb();
if (typeof promise2.then === 'function') {
return promise2.then(function () {
return res;
});
}
return res;
}, function (reason) {
var promise2 = cb();
if (typeof promise2.then === 'function') {
return promise2.then(function () {
throw reason;
});
}
throw reason;
});
};
exports.sequentialize = function (queue, promiseFactory) {
return function () {
var args = arguments;
var that = this;
return queue.add(function () {
return promiseFactory.apply(that, args);
});
};
};
exports.flatten = function (arrs) {
var res = [];
for (var i = 0, len = arrs.length; i < len; i++) {
res = res.concat(arrs[i]);
}
return res;
};
// uniq an array of strings, order not guaranteed
// similar to underscore/lodash _.uniq
exports.uniq = function (arr) {
var map = {};
for (var i = 0, len = arr.length; i < len; i++) {
map['$' + arr[i]] = true;
}
var keys = Object.keys(map);
var output = new Array(keys.length);
for (i = 0, len = keys.length; i < len; i++) {
output[i] = keys[i].substring(1);
}
return output;
};
var crypto = _dereq_(19);
var Md5 = _dereq_(35);
exports.MD5 = function (string) {
/* istanbul ignore else */
if (!process.browser) {
return crypto.createHash('md5').update(string).digest('hex');
} else {
return Md5.hash(string);
}
};
}).call(this,_dereq_(34))
},{"18":18,"19":19,"23":23,"28":28,"29":29,"34":34,"35":35}],6:[function(_dereq_,module,exports){
'use strict';
var massageCreateIndexRequest = _dereq_(16);
function createIndex(db, requestDef, callback) {
requestDef = massageCreateIndexRequest(requestDef);
db.request({
method: 'POST',
url: '_index',
body: requestDef
}, callback);
}
function find(db, requestDef, callback) {
db.request({
method: 'POST',
url: '_find',
body: requestDef
}, callback);
}
function getIndexes(db, callback) {
db.request({
method: 'GET',
url: '_index'
}, callback);
}
function deleteIndex(db, indexDef, callback) {
var ddoc = indexDef.ddoc;
var type = indexDef.type || 'json';
var name = indexDef.name;
if (!ddoc) {
return callback(new Error('you must provide an index\'s ddoc'));
}
if (!name) {
return callback(new Error('you must provide an index\'s name'));
}
var url = '_index/' + [ddoc, type, name].map(encodeURIComponent).join('/');
db.request({
method: 'DELETE',
url: url
}, callback);
}
exports.createIndex = createIndex;
exports.find = find;
exports.getIndexes = getIndexes;
exports.deleteIndex = deleteIndex;
},{"16":16}],7:[function(_dereq_,module,exports){
'use strict';
var localUtils = _dereq_(15);
var abstractMapReduce = _dereq_(2);
var parseField = localUtils.parseField;
//
// One thing about these mappers:
//
// Per the advice of John-David Dalton (http://youtu.be/NthmeLEhDDM),
// what you want to do in this case is optimize for the smallest possible
// function, since that's the thing that gets run over and over again.
//
// This code would be a lot simpler if all the if/elses were inside
// the function, but it would also be a lot less performant.
//
function createDeepMultiMapper(fields, emit) {
return function (doc) {
var toEmit = [];
for (var i = 0, iLen = fields.length; i < iLen; i++) {
var parsedField = parseField(fields[i]);
var value = doc;
for (var j = 0, jLen = parsedField.length; j < jLen; j++) {
var key = parsedField[j];
value = value[key];
if (!value) {
break;
}
}
toEmit.push(value);
}
emit(toEmit);
};
}
function createDeepSingleMapper(field, emit) {
var parsedField = parseField(field);
return function (doc) {
var value = doc;
for (var i = 0, len = parsedField.length; i < len; i++) {
var key = parsedField[i];
value = value[key];
if (!value) {
return; // do nothing
}
}
emit(value);
};
}
function createShallowSingleMapper(field, emit) {
return function (doc) {
emit(doc[field]);
};
}
function createShallowMultiMapper(fields, emit) {
return function (doc) {
var toEmit = [];
for (var i = 0, len = fields.length; i < len; i++) {
toEmit.push(doc[fields[i]]);
}
emit(toEmit);
};
}
function checkShallow(fields) {
for (var i = 0, len = fields.length; i < len; i++) {
var field = fields[i];
if (field.indexOf('.') !== -1) {
return false;
}
}
return true;
}
function createMapper(fields, emit) {
var isShallow = checkShallow(fields);
var isSingle = fields.length === 1;
// notice we try to optimize for the most common case,
// i.e. single shallow indexes
if (isShallow) {
if (isSingle) {
return createShallowSingleMapper(fields[0], emit);
} else { // multi
return createShallowMultiMapper(fields, emit);
}
} else { // deep
if (isSingle) {
return createDeepSingleMapper(fields[0], emit);
} else { // multi
return createDeepMultiMapper(fields, emit);
}
}
}
function mapper(mapFunDef, emit) {
// mapFunDef is a list of fields
var fields = Object.keys(mapFunDef.fields);
return createMapper(fields, emit);
}
/* istanbul ignore next */
function reducer(/*reduceFunDef*/) {
throw new Error('reduce not supported');
}
function ddocValidator(ddoc, viewName) {
var view = ddoc.views[viewName];
// This doesn't actually need to be here apparently, but
// I feel safer keeping it.
/* istanbul ignore if */
if (!view.map || !view.map.fields) {
throw new Error('ddoc ' + ddoc._id +' with view ' + viewName +
' doesn\'t have map.fields defined. ' +
'maybe it wasn\'t created by this plugin?');
}
}
var abstractMapper = abstractMapReduce({
name: 'indexes',
mapper: mapper,
reducer: reducer,
ddocValidator: ddocValidator
});
module.exports = abstractMapper;
},{"15":15,"2":2}],8:[function(_dereq_,module,exports){
'use strict';
var utils = _dereq_(17);
var log = utils.log;
var pouchUpsert = _dereq_(31);
var abstractMapper = _dereq_(7);
var localUtils = _dereq_(15);
var validateIndex = localUtils.validateIndex;
var massageIndexDef = localUtils.massageIndexDef;
var massageCreateIndexRequest = _dereq_(16);
function upsert(db, docId, diffFun) {
return pouchUpsert.upsert.call(db, docId, diffFun);
}
function createIndex(db, requestDef) {
requestDef = massageCreateIndexRequest(requestDef);
var originalIndexDef = utils.clone(requestDef.index);
requestDef.index = massageIndexDef(requestDef.index);
validateIndex(requestDef.index);
var md5 = utils.MD5(JSON.stringify(requestDef));
var viewName = requestDef.name || ('idx-' + md5);
var ddocName = requestDef.ddoc || ('idx-' + md5);
var ddocId = '_design/' + ddocName;
var hasInvalidLanguage = false;
var viewExists = false;
function updateDdoc(doc) {
if (doc._rev && doc.language !== 'query') {
hasInvalidLanguage = true;
}
doc.language = 'query';
doc.views = doc.views || {};
viewExists = !!doc.views[viewName];
if (viewExists) {
return false;
}
doc.views[viewName] = {
map: {
fields: utils.mergeObjects(requestDef.index.fields)
},
reduce: '_count',
options: {
def: originalIndexDef
}
};
return doc;
}
log('creating index', ddocId);
return upsert(db, ddocId, updateDdoc).then(function () {
if (hasInvalidLanguage) {
throw new Error('invalid language for ddoc with id "' +
ddocId +
'" (should be "query")');
}
}).then(function () {
// kick off a build
// TODO: abstract-pouchdb-mapreduce should support auto-updating
// TODO: should also use update_after, but pouchdb/pouchdb#3415 blocks me
var signature = ddocName + '/' + viewName;
return abstractMapper.query.call(db, signature, {
limit: 0,
reduce: false
}).then(function () {
return {
id: ddocId,
name: viewName,
result: viewExists ? 'exists' : 'created'
};
});
});
}
module.exports = createIndex;
},{"15":15,"16":16,"17":17,"31":31,"7":7}],9:[function(_dereq_,module,exports){
'use strict';
var abstractMapper = _dereq_(7);
var upsert = _dereq_(4);
function deleteIndex(db, index) {
if (!index.ddoc) {
throw new Error('you must supply an index.ddoc when deleting');
}
if (!index.name) {
throw new Error('you must supply an index.name when deleting');
}
var docId = index.ddoc;
var viewName = index.name;
function deltaFun (doc) {
if (Object.keys(doc.views).length === 1 && doc.views[viewName]) {
// only one view in this ddoc, delete the whole ddoc
return {_id: docId, _deleted: true};
}
// more than one view here, just remove the view
delete doc.views[viewName];
return doc;
}
return upsert(db, docId, deltaFun).then(function () {
return abstractMapper.viewCleanup.apply(db);
}).then(function () {
return {ok: true};
});
}
module.exports = deleteIndex;
},{"4":4,"7":7}],10:[function(_dereq_,module,exports){
'use strict';
//
// Do an in-memory filtering of rows that aren't covered by the index.
// E.g. if the user is asking for foo=1 and bar=2, but the index
// only covers "foo", then this in-memory filter would take care of
// "bar".
//
var isArray = _dereq_(24);
var collate = _dereq_(26).collate;
var localUtils = _dereq_(15);
var isCombinationalField = localUtils.isCombinationalField;
var getKey = localUtils.getKey;
var getValue = localUtils.getValue;
var parseField = localUtils.parseField;
var utils = _dereq_(17);
var getFieldFromDoc = utils.getFieldFromDoc;
// create a comparator based on the sort object
function createFieldSorter(sort) {
function getFieldValuesAsArray(doc) {
return sort.map(function (sorting) {
var fieldName = getKey(sorting);
var parsedField = parseField(fieldName);
var docFieldValue = getFieldFromDoc(doc, parsedField);
return docFieldValue;
});
}
return function (aRow, bRow) {
var aFieldValues = getFieldValuesAsArray(aRow.doc);
var bFieldValues = getFieldValuesAsArray(bRow.doc);
var collation = collate(aFieldValues, bFieldValues);
if (collation !== 0) {
return collation;
}
// this is what mango seems to do
return utils.compare(aRow.doc._id, bRow.doc._id);
};
}
function filterInMemoryFields (rows, requestDef, inMemoryFields) {
rows = rows.filter(function (row) {
return rowFilter(row.doc, requestDef.selector, inMemoryFields);
});
if (requestDef.sort) {
// in-memory sort
var fieldSorter = createFieldSorter(requestDef.sort);
rows = rows.sort(fieldSorter);
if (typeof requestDef.sort[0] !== 'string' &&
getValue(requestDef.sort[0]) === 'desc') {
rows = rows.reverse();
}
}
if ('limit' in requestDef || 'skip' in requestDef) {
// have to do the limit in-memory
var skip = requestDef.skip || 0;
var limit = ('limit' in requestDef ? requestDef.limit : rows.length) + skip;
rows = rows.slice(skip, limit);
}
return rows;
}
function rowFilter (doc, selector, inMemoryFields) {
return inMemoryFields.every(function (field) {
var matcher = selector[field];
var parsedField = parseField(field);
var docFieldValue = getFieldFromDoc(doc, parsedField);
if (isCombinationalField(field)) {
return matchCominationalSelector(field, matcher, doc);
}
return matchSelector(matcher, doc, parsedField, docFieldValue);
});
}
function matchSelector (matcher, doc, parsedField, docFieldValue) {
if (!matcher) {
// no filtering necessary; this field is just needed for sorting
return true;
}
return Object.keys(matcher).every(function (userOperator) {
var userValue = matcher[userOperator];
return match(userOperator, doc, userValue, parsedField, docFieldValue);
});
}
function matchCominationalSelector (field, matcher, doc) {
if (field === '$or') {
return matcher.some(function (orMatchers) {
return rowFilter(doc, orMatchers, Object.keys(orMatchers));
});
}
if (field === '$not') {
return !rowFilter(doc, matcher, Object.keys(matcher));
}
//`$nor`
return !matcher.find(function (orMatchers) {
return rowFilter(doc, orMatchers, Object.keys(orMatchers));
});
}
function match(userOperator, doc, userValue, parsedField, docFieldValue) {
if (!matchers[userOperator]) {
throw new Error('unknown operator "' + userOperator +
'" - should be one of $eq, $lte, $lt, $gt, $gte, $exists, $ne, $in, ' +
'$nin, $size, $mod, $regex, $elemMatch, $type or $all');
}
return matchers[userOperator](doc, userValue, parsedField, docFieldValue);
}
function fieldExists(docFieldValue) {
return typeof docFieldValue !== 'undefined' && docFieldValue !== null;
}
function fieldIsNotUndefined(docFieldValue) {
return typeof docFieldValue !== 'undefined';
}
function modField (docFieldValue, userValue) {
var divisor = userValue[0];
var mod = userValue[1];
if (divisor === 0) {
throw new Error('Bad divisor, cannot divide by zero');
}
if (parseInt(divisor, 10) !== divisor ) {
throw new Error('Divisor is not an integer');
}
if (parseInt(mod, 10) !== mod ) {
throw new Error('Modulus is not an integer');
}
if (parseInt(docFieldValue, 10) !== docFieldValue) {
return false;
}
return docFieldValue % divisor === mod;
}
function arrayContainsValue (docFieldValue, userValue) {
return userValue.some(function (val) {
if (docFieldValue instanceof Array) {
return docFieldValue.indexOf(val) > -1;
}
return docFieldValue === val;
});
}
function arrayContainsAllValues (docFieldValue, userValue) {
return userValue.every(function (val) {
return docFieldValue.indexOf(val) > -1;
});
}
function arraySize (docFieldValue, userValue) {
return docFieldValue.length === userValue;
}
function regexMatch(docFieldValue, userValue) {
var re = new RegExp(userValue);
return re.test(docFieldValue);
}
function typeMatch(docFieldValue, userValue) {
switch (userValue) {
case 'null':
return docFieldValue === null;
case 'boolean':
return typeof(docFieldValue) === 'boolean';
case 'number':
return typeof(docFieldValue) === 'number';
case 'string':
return typeof(docFieldValue) === 'string';
case 'array':
return docFieldValue instanceof Array;
case 'object':
return ({}).toString.call(docFieldValue) === '[object Object]';
}
throw new Error(userValue + ' not supported as a type.' +
'Please use one of object, string, array, number, boolean or null.');
}
var matchers = {
'$elemMatch': function (doc, userValue, parsedField, docFieldValue) {
if (!isArray(docFieldValue)) {
return false;
}
if (docFieldValue.length === 0) {
return false;
}
if (typeof docFieldValue[0] === 'object') {
return docFieldValue.some(function (val) {
return rowFilter(val, userValue, Object.keys(userValue));
});
}
return docFieldValue.some(function (val) {
return matchSelector(userValue, doc, parsedField, val);
});
},
'$eq': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) === 0;
},
'$gte': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) >= 0;
},
'$gt': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) > 0;
},
'$lte': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) <= 0;
},
'$lt': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) < 0;
},
'$exists': function (doc, userValue, parsedField, docFieldValue) {
//a field that is null is still considered to exist
if (userValue) {
return fieldIsNotUndefined(docFieldValue);
}
return !fieldIsNotUndefined(docFieldValue);
},
'$mod': function (doc, userValue, parsedField, docFieldValue) {
return fieldExists(docFieldValue) && modField(docFieldValue, userValue);
},
'$ne': function (doc, userValue, parsedField, docFieldValue) {
return userValue.every(function (neValue) {
return collate(docFieldValue, neValue) !== 0;
});
},
'$in': function (doc, userValue, parsedField, docFieldValue) {
return fieldExists(docFieldValue) && arrayContainsValue(docFieldValue, userValue);
},
'$nin': function (doc, userValue, parsedField, docFieldValue) {
return fieldExists(docFieldValue) && !arrayContainsValue(docFieldValue, userValue);
},
'$size': function (doc, userValue, parsedField, docFieldValue) {
return fieldExists(docFieldValue) && arraySize(docFieldValue, userValue);
},
'$all': function (doc, userValue, parsedField, docFieldValue) {
return isArray(docFieldValue) && arrayContainsAllValues(docFieldValue, userValue);
},
'$regex': function (doc, userValue, parsedField, docFieldValue) {
return fieldExists(docFieldValue) && regexMatch(docFieldValue, userValue);
},
'$type': function (doc, userValue, parsedField, docFieldValue) {
return typeMatch(docFieldValue, userValue);
}
};
module.exports = filterInMemoryFields;
},{"15":15,"17":17,"24":24,"26":26}],11:[function(_dereq_,module,exports){
'use strict';
var utils = _dereq_(17);
var clone = utils.clone;
var getIndexes = _dereq_(13);
var collate = _dereq_(26).collate;
var abstractMapper = _dereq_(7);
var planQuery = _dereq_(12);
var localUtils = _dereq_(15);
var filterInMemoryFields = _dereq_(10);
var massageSelector = localUtils.massageSelector;
var massageSort = localUtils.massageSort;
var getValue = localUtils.getValue;
var validateFindRequest = localUtils.validateFindRequest;
var validateSort = localUtils.validateSort;
var reverseOptions = localUtils.reverseOptions;
var filterInclusiveStart = localUtils.filterInclusiveStart;
var Promise = utils.Promise;
function indexToSignature(index) {
// remove '_design/'
return index.ddoc.substring(8) + '/' + index.name;
}
function doAllDocs(db, originalOpts) {
var opts = clone(originalOpts);
// CouchDB responds in weird ways when you provide a non-string to _id;
// we mimic the behavior for consistency. See issue66 tests for details.
if (opts.descending) {
if ('endkey' in opts && typeof opts.endkey !== 'string') {
opts.endkey = '';
}
if ('startkey' in opts && typeof opts.startkey !== 'string') {
opts.limit = 0;
}
} else {
if ('startkey' in opts && typeof opts.startkey !== 'string') {
opts.startkey = '';
}
if ('endkey' in opts && typeof opts.endkey !== 'string') {
opts.limit = 0;
}
}
if ('key' in opts && typeof opts.key !== 'string') {
opts.limit = 0;
}
return db.allDocs(opts);
}
function find(db, requestDef) {
if (requestDef.selector) {
requestDef.selector = massageSelector(requestDef.selector);
}
if (requestDef.sort) {
requestDef.sort = massageSort(requestDef.sort);
}
validateFindRequest(requestDef);
return getIndexes(db).then(function (getIndexesRes) {
var queryPlan = planQuery(requestDef, getIndexesRes.indexes);
var indexToUse = queryPlan.index;
validateSort(requestDef, indexToUse);
var opts = utils.extend(true, {
include_docs: true,
reduce: false
}, queryPlan.queryOpts);
if ('startkey' in opts && 'endkey' in opts &&
collate(opts.startkey, opts.endkey) > 0) {
// can't possibly return any results, startkey > endkey
return {docs: []};
}
var isDescending = requestDef.sort &&
typeof requestDef.sort[0] !== 'string' &&
getValue(requestDef.sort[0]) === 'desc';
if (isDescending) {
// either all descending or all ascending
opts.descending = true;
opts = reverseOptions(opts);
}
if (!queryPlan.inMemoryFields.length) {
// no in-memory filtering necessary, so we can let the
// database do the limit/skip for us
if ('limit' in requestDef) {
opts.limit = requestDef.limit;
}
if ('skip' in requestDef) {
opts.skip = requestDef.skip;
}
}
return Promise.resolve().then(function () {
if (indexToUse.name === '_all_docs') {
return doAllDocs(db, opts);
} else {
var signature = indexToSignature(indexToUse);
return abstractMapper.query.call(db, signature, opts);
}
}).then(function (res) {
if (opts.inclusive_start === false) {
// may have to manually filter the first one,
// since couchdb has no true inclusive_start option
res.rows = filterInclusiveStart(res.rows, opts.startkey, indexToUse);
}
if (queryPlan.inMemoryFields.length) {
// need to filter some stuff in-memory
res.rows = filterInMemoryFields(res.rows, requestDef, queryPlan.inMemoryFields);
}
var resp = {
docs: res.rows.map(function (row) {
var doc = row.doc;
if (requestDef.fields) {
return utils.pick(doc, requestDef.fields);
}
return doc;
})
};
if (indexToUse.defaultUsed) {
resp.warning = 'no matching index found, create an index to optimize query time';
}
return resp;
});
});
}
module.exports = find;
},{"10":10,"12":12,"13":13,"15":15,"17":17,"26":26,"7":7}],12:[function(_dereq_,module,exports){
'use strict';
var utils = _dereq_(17);
var log = utils.log;
var localUtils = _dereq_(15);
var getKey = localUtils.getKey;
var getUserFields = localUtils.getUserFields;
// couchdb lowest collation value
var COLLATE_LO = null;
// couchdb highest collation value (TODO: well not really, but close enough amirite)
var COLLATE_HI = {"\uffff": {}};
// couchdb second-lowest collation value
function checkFieldInIndex(index, field) {
var indexFields = index.def.fields.map(getKey);
for (var i = 0, len = indexFields.length; i < len; i++) {
var indexField = indexFields[i];
if (field === indexField) {
return true;
}
}
return false;
}
// so when you do e.g. $eq/$eq, we can do it entirely in the database.
// but when you do e.g. $gt/$eq, the first part can be done
// in the database, but the second part has to be done in-memory,
// because $gt has forced us to lose precision.
// so that's what this determines
function userOperatorLosesPrecision(selector, field) {
var matcher = selector[field];
var userOperator = getKey(matcher);
return userOperator !== '$eq';
}
// sort the user fields by their position in the index,
// if they're in the index
function sortFieldsByIndex(userFields, index) {
var indexFields = index.def.fields.map(getKey);
return userFields.slice().sort(function (a, b) {
var aIdx = indexFields.indexOf(a);
var bIdx = indexFields.indexOf(b);
if (aIdx === -1) {
aIdx = Number.MAX_VALUE;
}
if (bIdx === -1) {
bIdx = Number.MAX_VALUE;
}
return utils.compare(aIdx, bIdx);
});
}
// first pass to try to find fields that will need to be sorted in-memory
function getBasicInMemoryFields(index, selector, userFields) {
userFields = sortFieldsByIndex(userFields, index);
// check if any of the user selectors lose precision
var needToFilterInMemory = false;
for (var i = 0, len = userFields.length; i < len; i++) {
var field = userFields[i];
if (needToFilterInMemory || !checkFieldInIndex(index, field)) {
return userFields.slice(i);
}
if (i < len - 1 && userOperatorLosesPrecision(selector, field)) {
needToFilterInMemory = true;
}
}
return [];
}
function getInMemoryFieldsFromNe(selector) {
var fields = [];
Object.keys(selector).forEach(function (field) {
var matcher = selector[field];
Object.keys(matcher).forEach(function (operator) {
if (operator === '$ne') {
fields.push(field);
}
});
});
return fields;
}
function getInMemoryFields(coreInMemoryFields, index, selector, userFields) {
var result = utils.flatten(
// in-memory fields reported as necessary by the query planner
coreInMemoryFields,
// combine with another pass that checks for any we may have missed
getBasicInMemoryFields(index, selector, userFields),
// combine with another pass that checks for $ne's
getInMemoryFieldsFromNe(selector)
);
return sortFieldsByIndex(utils.uniq(result), index);
}
// check that at least one field in the user's query is represented
// in the index. order matters in the case of sorts
function checkIndexFieldsMatch(indexFields, sortOrder, fields) {
if (sortOrder) {
// array has to be a strict subarray of index array. furthermore,
// the sortOrder fields need to all be represented in the index
var sortMatches = utils.oneArrayIsStrictSubArrayOfOther(sortOrder, indexFields);
var selectorMatches = utils.oneArrayIsSubArrayOfOther(fields, indexFields);
return sortMatches && selectorMatches;
}
// all of the user's specified fields still need to be
// on the left side of the index array, although the order
// doesn't matter
return utils.oneSetIsSubArrayOfOther(fields, indexFields);
}
var logicalMatchers = ['$eq', '$gt', '$gte', '$lt', '$lte'];
function isNonLogicalMatcher (matcher) {
return logicalMatchers.indexOf(matcher) === -1;
}
// check all the index fields for usages of '$ne'
// e.g. if the user queries {foo: {$ne: 'foo'}, bar: {$eq: 'bar'}},
// then we can neither use an index on ['foo'] nor an index on
// ['foo', 'bar'], but we can use an index on ['bar'] or ['bar', 'foo']
function checkFieldsLogicallySound(indexFields, selector) {
var firstField = indexFields[0];
var matcher = selector[firstField];
var hasLogicalOperator = Object.keys(matcher).some(function (matcherKey) {
return !(isNonLogicalMatcher(matcherKey));
});
if (!hasLogicalOperator) {
return false;
}
var isInvalidNe = Object.keys(matcher).length === 1 &&
getKey(matcher) === '$ne';
return !isInvalidNe;
}
function checkIndexMatches(index, sortOrder, fields, selector) {
var indexFields = index.def.fields.map(getKey);
var fieldsMatch = checkIndexFieldsMatch(indexFields, sortOrder, fields);
if (!fieldsMatch) {
return false;
}
return checkFieldsLogicallySound(indexFields, selector);
}
//
// the algorithm is very simple:
// take all the fields the user supplies, and if those fields
// are a strict subset of the fields in some index,
// then use that index
//
//
function findMatchingIndexes(selector, userFields, sortOrder, indexes) {
return indexes.reduce(function (res, index) {
var indexMatches = checkIndexMatches(index, sortOrder, userFields, selector);
if (indexMatches) {
res.push(index);
}
return res;
}, []);
}
// find the best index, i.e. the one that matches the most fields
// in the user's query
function findBestMatchingIndex(selector, userFields, sortOrder, indexes) {
var matchingIndexes = findMatchingIndexes(selector, userFields, sortOrder, indexes);
if (matchingIndexes.length === 0) {
//return `all_docs` as a default index;
//I'm assuming that _all_docs is always first
var defaultIndex = indexes[0];
defaultIndex.defaultUsed = true;
return defaultIndex;
}
if (matchingIndexes.length === 1) {
return matchingIndexes[0];
}
var userFieldsMap = utils.arrayToObject(userFields);
function scoreIndex(index) {
var indexFields = index.def.fields.map(getKey);
var score = 0;
for (var i = 0, len = indexFields.length; i < len; i++) {
var indexField = indexFields[i];
if (userFieldsMap[indexField]) {
score++;
}
}
return score;
}
return utils.max(matchingIndexes, scoreIndex);
}
function getSingleFieldQueryOptsFor(userOperator, userValue) {
switch (userOperator) {
case '$eq':
return {key: userValue};
case '$lte':
return {endkey: userValue};
case '$gte':
return {startkey: userValue};
case '$lt':
return {
endkey: userValue,
inclusive_end: false
};
case '$gt':
return {
startkey: userValue,
inclusive_start: false
};
}
}
function getSingleFieldCoreQueryPlan(selector, index) {
var field = getKey(index.def.fields[0]);
var matcher = selector[field];
var inMemoryFields = [];
var userOperators = Object.keys(matcher);
var combinedOpts;
userOperators.forEach(function (userOperator) {
if (isNonLogicalMatcher(userOperator)) {
inMemoryFields.push(field);
return;
}
var userValue = matcher[userOperator];
var newQueryOpts = getSingleFieldQueryOptsFor(userOperator, userValue);
if (combinedOpts) {
combinedOpts = utils.mergeObjects([combinedOpts, newQueryOpts]);
} else {
combinedOpts = newQueryOpts;
}
});
return {
queryOpts: combinedOpts,
inMemoryFields: inMemoryFields
};
}
function getMultiFieldCoreQueryPlan(userOperator, userValue) {
switch (userOperator) {
case '$eq':
return {
startkey: userValue,
endkey: userValue
};
case '$lte':
return {
endkey: userValue
};
case '$gte':
return {
startkey: userValue
};
case '$lt':
return {
endkey: userValue,
inclusive_end: false
};
case '$gt':
return {
startkey: userValue,
inclusive_start: false
};
}
}
function getMultiFieldQueryOpts(selector, index) {
var indexFields = index.def.fields.map(getKey);
var inMemoryFields = [];
var startkey = [];
var endkey = [];
var inclusiveStart;
var inclusiveEnd;
function finish(i) {
if (inclusiveStart !== false) {
startkey.push(COLLATE_LO);
}
if (inclusiveEnd !== false) {
endkey.push(COLLATE_HI);
}
// keep track of the fields where we lost specificity,
// and therefore need to filter in-memory
inMemoryFields = indexFields.slice(i);
}
for (var i = 0, len = indexFields.length; i < len; i++) {
var indexField = indexFields[i];
var matcher = selector[indexField];
if (!matcher) { // fewer fields in user query than in index
finish(i);
break;
} else if (i > 0) {
if ('$ne' in matcher) { // unusable $ne index
finish(i);
break;
}
var usingGtlt = (
'$gt' in matcher || '$gte' in matcher ||
'$lt' in matcher || '$lte' in matcher);
var previousKeys = Object.keys(selector[indexFields[i - 1]]);
var previousWasEq = utils.arrayEquals(previousKeys, ['$eq']);
var previousWasSame = utils.arrayEquals(previousKeys, Object.keys(matcher));
var gtltLostSpecificity = usingGtlt && !previousWasEq && !previousWasSame;
if (gtltLostSpecificity) {
finish(i);
break;
}
}
var userOperators = Object.keys(matcher);
var combinedOpts = null;
for (var j = 0; j < userOperators.length; j++) {
var userOperator = userOperators[j];
var userValue = matcher[userOperator];
var newOpts = getMultiFieldCoreQueryPlan(userOperator, userValue);
if (combinedOpts) {
combinedOpts = utils.mergeObjects([combinedOpts, newOpts]);
} else {
combinedOpts = newOpts;
}
}
startkey.push('startkey' in combinedOpts ? combinedOpts.startkey : COLLATE_LO);
endkey.push('endkey' in combinedOpts ? combinedOpts.endkey : COLLATE_HI);
if ('inclusive_start' in combinedOpts) {
inclusiveStart = combinedOpts.inclusive_start;
}
if ('inclusive_end' in combinedOpts) {
inclusiveEnd = combinedOpts.inclusive_end;
}
}
var res = {
startkey: startkey,
endkey: endkey
};
if (typeof inclusiveStart !== 'undefined') {
res.inclusive_start = inclusiveStart;
}
if (typeof inclusiveEnd !== 'undefined') {
res.inclusive_end = inclusiveEnd;
}
return {
queryOpts: res,
inMemoryFields: inMemoryFields
};
}
function getDefaultQueryPlan () {
return {
queryOpts: {startkey: null},
//getInMemoryFields will do the work here later
inMemoryFields: []
};
}
function getCoreQueryPlan(selector, index) {
if (index.defaultUsed) {
return getDefaultQueryPlan(selector, index);
}
if (index.def.fields.length === 1) {
// one field in index, so the value was indexed as a singleton
return getSingleFieldCoreQueryPlan(selector, index);
}
// else index has multiple fields, so the value was indexed as an array
return getMultiFieldQueryOpts(selector, index);
}
function planQuery(request, indexes) {
log('planning query', request);
var selector = request.selector;
var sort = request.sort;
var userFieldsRes = getUserFields(selector, sort);
var userFields = userFieldsRes.fields;
var sortOrder = userFieldsRes.sortOrder;
var index = findBestMatchingIndex(selector, userFields, sortOrder, indexes);
var coreQueryPlan = getCoreQueryPlan(selector, index);
var queryOpts = coreQueryPlan.queryOpts;
var coreInMemoryFields = coreQueryPlan.inMemoryFields;
var inMemoryFields = getInMemoryFields(coreInMemoryFields, index, selector, userFields);
var res = {
queryOpts: queryOpts,
index: index,
inMemoryFields: inMemoryFields
};
log('query plan', res);
return res;
}
module.exports = planQuery;
},{"15":15,"17":17}],13:[function(_dereq_,module,exports){
'use strict';
var utils = _dereq_(17);
var localUtils = _dereq_(15);
var massageIndexDef = localUtils.massageIndexDef;
function getIndexes(db) {
// just search through all the design docs and filter in-memory.
// hopefully there aren't that many ddocs.
return db.allDocs({
startkey: '_design/',
endkey: '_design/\uffff',
include_docs: true
}).then(function (allDocsRes) {
var res = {
indexes: [{
ddoc: null,
name: '_all_docs',
type: 'special',
def: {
fields: [{_id: 'asc'}]
}
}]
};
res.indexes = utils.flatten(res.indexes, allDocsRes.rows.filter(function (row) {
return row.doc.language === 'query';
}).map(function (row) {
var viewNames = row.doc.views !== undefined ? Object.keys(row.doc.views) : [];
return viewNames.map(function (viewName) {
var view = row.doc.views[viewName];
return {
ddoc: row.id,
name: viewName,
type: 'json',
def: massageIndexDef(view.options.def)
};
});
}));
// these are sorted by view name for some reason
res.indexes.sort(function (left, right) {
return utils.compare(left.name, right.name);
});
res.total_rows = res.indexes.length;
return res;
});
}
module.exports = getIndexes;
},{"15":15,"17":17}],14:[function(_dereq_,module,exports){
'use strict';
var utils = _dereq_(17);
var callbackify = utils.callbackify;
exports.createIndex = callbackify(_dereq_(8));
exports.find = callbackify(_dereq_(11));
exports.getIndexes = callbackify(_dereq_(13));
exports.deleteIndex = callbackify(_dereq_(9));
},{"11":11,"13":13,"17":17,"8":8,"9":9}],15:[function(_dereq_,module,exports){
'use strict';
var utils = _dereq_(17);
var collate = _dereq_(26);
function getKey(obj) {
return Object.keys(obj)[0];
}
function getValue(obj) {
return obj[getKey(obj)];
}
// normalize the "sort" value
function massageSort(sort) {
if (!Array.isArray(sort)) {
throw new Error('invalid sort json - should be an array');
}
return sort.map(function (sorting) {
if (typeof sorting === 'string') {
var obj = {};
obj[sorting] = 'asc';
return obj;
} else {
return sorting;
}
});
}
var combinationFields = ['$or', '$nor', '$not'];
function isCombinationalField (field) {
return combinationFields.indexOf(field) > -1;
}
// collapse logically equivalent gt/gte values
function mergeGtGte(operator, value, fieldMatchers) {
if (typeof fieldMatchers.$eq !== 'undefined') {
return; // do nothing
}
if (typeof fieldMatchers.$gte !== 'undefined') {
if (operator === '$gte') {
if (value > fieldMatchers.$gte) { // more specificity
fieldMatchers.$gte = value;
}
} else { // operator === '$gt'
if (value >= fieldMatchers.$gte) { // more specificity
delete fieldMatchers.$gte;
fieldMatchers.$gt = value;
}
}
} else if (typeof fieldMatchers.$gt !== 'undefined') {
if (operator === '$gte') {
if (value > fieldMatchers.$gt) { // more specificity
delete fieldMatchers.$gt;
fieldMatchers.$gte = value;
}
} else { // operator === '$gt'
if (value > fieldMatchers.$gt) { // more specificity
fieldMatchers.$gt = value;
}
}
} else {
fieldMatchers[operator] = value;
}
}
// collapse logically equivalent lt/lte values
function mergeLtLte(operator, value, fieldMatchers) {
if (typeof fieldMatchers.$eq !== 'undefined') {
return; // do nothing
}
if (typeof fieldMatchers.$lte !== 'undefined') {
if (operator === '$lte') {
if (value < fieldMatchers.$lte) { // more specificity
fieldMatchers.$lte = value;
}
} else { // operator === '$gt'
if (value <= fieldMatchers.$lte) { // more specificity
delete fieldMatchers.$lte;
fieldMatchers.$lt = value;
}
}
} else if (typeof fieldMatchers.$lt !== 'undefined') {
if (operator === '$lte') {
if (value < fieldMatchers.$lt) { // more specificity
delete fieldMatchers.$lt;
fieldMatchers.$lte = value;
}
} else { // operator === '$gt'
if (value < fieldMatchers.$lt) { // more specificity
fieldMatchers.$lt = value;
}
}
} else {
fieldMatchers[operator] = value;
}
}
// combine $ne values into one array
function mergeNe(value, fieldMatchers) {
if ('$ne' in fieldMatchers) {
// there are many things this could "not" be
fieldMatchers.$ne.push(value);
} else { // doesn't exist yet
fieldMatchers.$ne = [value];
}
}
// add $eq into the mix
function mergeEq(value, fieldMatchers) {
// these all have less specificity than the $eq
// TODO: check for user errors here
delete fieldMatchers.$gt;
delete fieldMatchers.$gte;
delete fieldMatchers.$lt;
delete fieldMatchers.$lte;
delete fieldMatchers.$ne;
fieldMatchers.$eq = value;
}
// flatten an array of selectors joined by an $and operator
function mergeAndedSelectors(selectors) {
// sort to ensure that e.g. if the user specified
// $and: [{$gt: 'a'}, {$gt: 'b'}], then it's collapsed into
// just {$gt: 'b'}
var res = {};
selectors.forEach(function (selector) {
Object.keys(selector).forEach(function (field) {
var matcher = selector[field];
if (typeof matcher !== 'object') {
matcher = {$eq: matcher};
}
if (isCombinationalField(field)) {
if (matcher instanceof Array) {
res[field] = matcher.map(function (m) {
return mergeAndedSelectors([m]);
});
} else {
res[field] = mergeAndedSelectors([matcher]);
}
} else {
var fieldMatchers = res[field] = res[field] || {};
Object.keys(matcher).forEach(function (operator) {
var value = matcher[operator];
if (operator === '$gt' || operator === '$gte') {
return mergeGtGte(operator, value, fieldMatchers);
} else if (operator === '$lt' || operator === '$lte') {
return mergeLtLte(operator, value, fieldMatchers);
} else if (operator === '$ne') {
return mergeNe(value, fieldMatchers);
} else if (operator === '$eq') {
return mergeEq(value, fieldMatchers);
}
fieldMatchers[operator] = value;
});
}
});
});
return res;
}
//
// normalize the selector
//
function massageSelector(input) {
var result = utils.clone(input);
var wasAnded = false;
if ('$and' in result) {
result = mergeAndedSelectors(result['$and']);
wasAnded = true;
}
if ('$not' in result) {
//This feels a little like forcing, but it will work for now,
//I would like to come back to this and make the merging of selectors a little more generic
result['$not'] = mergeAndedSelectors([result['$not']]);
}
var fields = Object.keys(result);
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
var matcher = result[field];
if (typeof matcher !== 'object' || matcher === null) {
matcher = {$eq: matcher};
} else if ('$ne' in matcher && !wasAnded) {
// I put these in an array, since there may be more than one
// but in the "mergeAnded" operation, I already take care of that
matcher.$ne = [matcher.$ne];
}
result[field] = matcher;
}
return result;
}
function massageIndexDef(indexDef) {
indexDef.fields = indexDef.fields.map(function (field) {
if (typeof field === 'string') {
var obj = {};
obj[field] = 'asc';
return obj;
}
return field;
});
return indexDef;
}
function getKeyFromDoc(doc, index) {
var res = [];
for (var i = 0; i < index.def.fields.length; i++) {
var field = getKey(index.def.fields[i]);
res.push(doc[field]);
}
return res;
}
// have to do this manually because REASONS. I don't know why
// CouchDB didn't implement inclusive_start
function filterInclusiveStart(rows, targetValue, index) {
var indexFields = index.def.fields;
for (var i = 0, len = rows.length; i < len; i++) {
var row = rows[i];
// shave off any docs at the beginning that are <= the
// target value
var docKey = getKeyFromDoc(row.doc, index);
if (indexFields.length === 1) {
docKey = docKey[0]; // only one field, not multi-field
} else { // more than one field in index
// in the case where e.g. the user is searching {$gt: {a: 1}}
// but the index is [a, b], then we need to shorten the doc key
while (docKey.length > targetValue.length) {
docKey.pop();
}
}
//ABS as we just looking for values that don't match
if (Math.abs(collate.collate(docKey, targetValue)) > 0) {
// no need to filter any further; we're past the key
break;
}
}
return i > 0 ? rows.slice(i) : rows;
}
function reverseOptions(opts) {
var newOpts = utils.clone(opts);
delete newOpts.startkey;
delete newOpts.endkey;
delete newOpts.inclusive_start;
delete newOpts.inclusive_end;
if ('endkey' in opts) {
newOpts.startkey = opts.endkey;
}
if ('startkey' in opts) {
newOpts.endkey = opts.startkey;
}
if ('inclusive_start' in opts) {
newOpts.inclusive_end = opts.inclusive_start;
}
if ('inclusive_end' in opts) {
newOpts.inclusive_start = opts.inclusive_end;
}
return newOpts;
}
function validateIndex(index) {
var ascFields = index.fields.filter(function (field) {
return getValue(field) === 'asc';
});
if (ascFields.length !== 0 && ascFields.length !== index.fields.length) {
throw new Error('unsupported mixed sorting');
}
}
function validateSort (requestDef, index) {
if (index.defaultUsed && requestDef.sort) {
var noneIdSorts = requestDef.sort.filter(function (sortItem) {
return Object.keys(sortItem)[0] !== '_id';
}).map(function (sortItem) {
return Object.keys(sortItem)[0];
});
if (noneIdSorts.length > 0) {
throw new Error('Cannot sort on field(s) "' + noneIdSorts.join(',') +
'" when using the default index');
}
}
if (index.defaultUsed) {
return;
}
}
function validateFindRequest(requestDef) {
if (typeof requestDef.selector !== 'object') {
throw new Error('you must provide a selector when you find()');
}
/*var selectors = requestDef.selector['$and'] || [requestDef.selector];
for (var i = 0; i < selectors.length; i++) {
var selector = selectors[i];
var keys = Object.keys(selector);
if (keys.length === 0) {
throw new Error('invalid empty selector');
}
//var selection = selector[keys[0]];
/*if (Object.keys(selection).length !== 1) {
throw new Error('invalid selector: ' + JSON.stringify(selection) +
' - it must have exactly one key/value');
}
}*/
}
// determine the maximum number of fields
// we're going to need to query, e.g. if the user
// has selection ['a'] and sorting ['a', 'b'], then we
// need to use the longer of the two: ['a', 'b']
function getUserFields(selector, sort) {
var selectorFields = Object.keys(selector);
var sortFields = sort? sort.map(getKey) : [];
var userFields;
if (selectorFields.length >= sortFields.length) {
userFields = selectorFields;
} else {
userFields = sortFields;
}
if (sortFields.length === 0) {
return {
fields: userFields
};
}
// sort according to the user's preferred sorting
userFields = userFields.sort(function (left, right) {
var leftIdx = sortFields.indexOf(left);
if (leftIdx === -1) {
leftIdx = Number.MAX_VALUE;
}
var rightIdx = sortFields.indexOf(right);
if (rightIdx === -1) {
rightIdx = Number.MAX_VALUE;
}
return leftIdx < rightIdx ? -1 : leftIdx > rightIdx ? 1 : 0;
});
return {
fields: userFields,
sortOrder: sort.map(getKey)
};
}
module.exports = {
getKey: getKey,
getValue: getValue,
massageSort: massageSort,
massageSelector: massageSelector,
validateIndex: validateIndex,
validateFindRequest: validateFindRequest,
validateSort: validateSort,
reverseOptions: reverseOptions,
filterInclusiveStart: filterInclusiveStart,
massageIndexDef: massageIndexDef,
parseField: utils.parseField,
getUserFields: getUserFields,
isCombinationalField: isCombinationalField
};
},{"17":17,"26":26}],16:[function(_dereq_,module,exports){
'use strict';
var utils = _dereq_(17);
var clone = utils.clone;
// we restucture the supplied JSON considerably, because the official
// Mango API is very particular about a lot of this stuff, but we like
// to be liberal with what we accept in order to prevent mental
// breakdowns in our users
module.exports = function (requestDef) {
requestDef = clone(requestDef);
if (!requestDef.index) {
requestDef.index = {};
}
['type', 'name', 'ddoc'].forEach(function (key) {
if (requestDef.index[key]) {
requestDef[key] = requestDef.index[key];
delete requestDef.index[key];
}
});
if (requestDef.fields) {
requestDef.index.fields = requestDef.fields;
delete requestDef.fields;
}
if (!requestDef.type) {
requestDef.type = 'json';
}
return requestDef;
};
},{"17":17}],17:[function(_dereq_,module,exports){
(function (process){
'use strict';
var Promise = _dereq_(29);
/* istanbul ignore next */
exports.once = function (fun) {
var called = false;
return exports.getArguments(function (args) {
if (called) {
console.trace();
throw new Error('once called more than once');
} else {
called = true;
fun.apply(this, args);
}
});
};
/* istanbul ignore next */
exports.getArguments = function (fun) {
return function () {
var len = arguments.length;
var args = new Array(len);
var i = -1;
while (++i < len) {
args[i] = arguments[i];
}
return fun.call(this, args);
};
};
/* istanbul ignore next */
exports.toPromise = function (func) {
//create the function we will be returning
return exports.getArguments(function (args) {
var self = this;
var tempCB = (typeof args[args.length - 1] === 'function') ? args.pop() : false;
// if the last argument is a function, assume its a callback
var usedCB;
if (tempCB) {
// if it was a callback, create a new callback which calls it,
// but do so async so we don't trap any errors
usedCB = function (err, resp) {
process.nextTick(function () {
tempCB(err, resp);
});
};
}
var promise = new Promise(function (fulfill, reject) {
try {
var callback = exports.once(function (err, mesg) {
if (err) {
reject(err);
} else {
fulfill(mesg);
}
});
// create a callback for this invocation
// apply the function in the orig context
args.push(callback);
func.apply(self, args);
} catch (e) {
reject(e);
}
});
// if there is a callback, call it back
if (usedCB) {
promise.then(function (result) {
usedCB(null, result);
}, usedCB);
}
promise.cancel = function () {
return this;
};
return promise;
});
};
exports.inherits = _dereq_(23);
exports.Promise = Promise;
exports.clone = function (obj) {
return exports.extend(true, {}, obj);
};
exports.extend = _dereq_(28);
exports.callbackify = function (fun) {
return exports.getArguments(function (args) {
var cb = args.pop();
var promise = fun.apply(this, args);
exports.promisedCallback(promise, cb);
return promise;
});
};
exports.promisedCallback = function (promise, callback) {
promise.then(function (res) {
process.nextTick(function () {
callback(null, res);
});
}, function (reason) {
process.nextTick(function () {
callback(reason);
});
});
return promise;
};
var crypto = _dereq_(19);
var Md5 = _dereq_(35);
exports.MD5 = function (string) {
/* istanbul ignore else */
if (!process.browser) {
return crypto.createHash('md5').update(string).digest('hex');
} else {
return Md5.hash(string);
}
};
exports.flatten = exports.getArguments(function (args) {
var res = [];
for (var i = 0, len = args.length; i < len; i++) {
var subArr = args[i];
if (Array.isArray(subArr)) {
res = res.concat(exports.flatten.apply(null, subArr));
} else {
res.push(subArr);
}
}
return res;
});
exports.mergeObjects = function (arr) {
var res = {};
for (var i = 0, len = arr.length; i < len; i++) {
res = exports.extend(true, res, arr[i]);
}
return res;
};
// this would just be "return doc[field]", but fields
// can be "deep" due to dot notation
exports.getFieldFromDoc = function (doc, parsedField) {
var value = doc;
for (var i = 0, len = parsedField.length; i < len; i++) {
var key = parsedField[i];
value = value[key];
if (!value) {
break;
}
}
return value;
};
exports.setFieldInDoc = function (doc, parsedField, value) {
for (var i = 0, len = parsedField.length; i < len-1; i++) {
var elem = parsedField[i];
doc = doc[elem] = {};
}
doc[parsedField[len-1]] = value;
};
// Converts a string in dot notation to an array of its components, with backslash escaping
exports.parseField = function (fieldName) {
// fields may be deep (e.g. "foo.bar.baz"), so parse
var fields = [];
var current = '';
for (var i = 0, len = fieldName.length; i < len; i++) {
var ch = fieldName[i];
if (ch === '.') {
if (i > 0 && fieldName[i - 1] === '\\') { // escaped delimiter
current = current.substring(0, current.length - 1) + '.';
} else { // not escaped, so delimiter
fields.push(current);
current = '';
}
} else { // normal character
current += ch;
}
}
fields.push(current);
return fields;
};
// Selects a list of fields defined in dot notation from one doc
// and copies them to a new doc. Like underscore _.pick but supports nesting.
exports.pick = function (obj, arr) {
var res = {};
for (var i = 0, len = arr.length; i < len; i++) {
var parsedField = exports.parseField(arr[i]);
var value = exports.getFieldFromDoc(obj, parsedField);
if(typeof value !== 'undefined') {
exports.setFieldInDoc(res, parsedField, value);
}
}
return res;
};
// e.g. ['a'], ['a', 'b'] is true, but ['b'], ['a', 'b'] is false
exports.oneArrayIsSubArrayOfOther = function (left, right) {
for (var i = 0, len = Math.min(left.length, right.length); i < len; i++) {
if (left[i] !== right[i]) {
return false;
}
}
return true;
};
// e.g.['a', 'b', 'c'], ['a', 'b'] is false
exports.oneArrayIsStrictSubArrayOfOther = function (left, right) {
if (left.length > right.length) {
return false;
}
return exports.oneArrayIsSubArrayOfOther(left, right);
};
// same as above, but treat the left array as an unordered set
// e.g. ['b', 'a'], ['a', 'b', 'c'] is true, but ['c'], ['a', 'b', 'c'] is false
exports.oneSetIsSubArrayOfOther = function (left, right) {
left = left.slice();
for (var i = 0, len = right.length; i < len; i++) {
var field = right[i];
if (!left.length) {
break;
}
var leftIdx = left.indexOf(field);
if (leftIdx === -1) {
return false;
} else {
left.splice(leftIdx, 1);
}
}
return true;
};
exports.compare = function (left, right) {
return left < right ? -1 : left > right ? 1 : 0;
};
exports.arrayToObject = function (arr) {
var res = {};
for (var i = 0, len = arr.length; i < len; i++) {
res[arr[i]] = true;
}
return res;
};
exports.max = function (arr, fun) {
var max = null;
var maxScore = -1;
for (var i = 0, len = arr.length; i < len; i++) {
var element = arr[i];
var score = fun(element);
if (score > maxScore) {
maxScore = score;
max = element;
}
}
return max;
};
exports.arrayEquals = function (arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
}
for (var i = 0, len = arr1.length; i < len; i++) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
};
exports.uniq = function(arr) {
var obj = {};
for (var i = 0; i < arr.length; i++) {
obj['$' + arr[i]] = true;
}
return Object.keys(obj).map(function (key) {
return key.substring(1);
});
};
exports.log = _dereq_(20)('pouchdb:find');
}).call(this,_dereq_(34))
},{"19":19,"20":20,"23":23,"28":28,"29":29,"34":34,"35":35}],18:[function(_dereq_,module,exports){
'use strict';
module.exports = argsArray;
function argsArray(fun) {
return function () {
var len = arguments.length;
if (len) {
var args = [];
var i = -1;
while (++i < len) {
args[i] = arguments[i];
}
return fun.call(this, args);
} else {
return fun.call(this, []);
}
};
}
},{}],19:[function(_dereq_,module,exports){
},{}],20:[function(_dereq_,module,exports){
/**
* This is the web browser implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = _dereq_(21);
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = 'undefined' != typeof chrome
&& 'undefined' != typeof chrome.storage
? chrome.storage.local
: localstorage();
/**
* Colors.
*/
exports.colors = [
'lightseagreen',
'forestgreen',
'goldenrod',
'dodgerblue',
'darkorchid',
'crimson'
];
/**
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
* and the Firebug extension (any Firefox version) are known
* to support "%c" CSS customizations.
*
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
function useColors() {
// is webkit? http://stackoverflow.com/a/16459606/376773
return ('WebkitAppearance' in document.documentElement.style) ||
// is firebug? http://stackoverflow.com/a/398120/376773
(window.console && (console.firebug || (console.exception && console.table))) ||
// is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
}
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
exports.formatters.j = function(v) {
return JSON.stringify(v);
};
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs() {
var args = arguments;
var useColors = this.useColors;
args[0] = (useColors ? '%c' : '')
+ this.namespace
+ (useColors ? ' %c' : ' ')
+ args[0]
+ (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff);
if (!useColors) return args;
var c = 'color: ' + this.color;
args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
// the final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
var index = 0;
var lastC = 0;
args[0].replace(/%[a-z%]/g, function(match) {
if ('%%' === match) return;
index++;
if ('%c' === match) {
// we only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
return args;
}
/**
* Invokes `console.log()` when available.
* No-op when `console.log` is not a "function".
*
* @api public
*/
function log() {
// this hackery is required for IE8/9, where
// the `console.log` function doesn't have 'apply'
return 'object' === typeof console
&& console.log
&& Function.prototype.apply.call(console.log, console, arguments);
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (null == namespaces) {
exports.storage.removeItem('debug');
} else {
exports.storage.debug = namespaces;
}
} catch(e) {}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
var r;
try {
r = exports.storage.debug;
} catch(e) {}
return r;
}
/**
* Enable namespaces listed in `localStorage.debug` initially.
*/
exports.enable(load());
/**
* Localstorage attempts to return the localstorage.
*
* This is necessary because safari throws
* when a user disables cookies/localstorage
* and you attempt to access it.
*
* @return {LocalStorage}
* @api private
*/
function localstorage(){
try {
return window.localStorage;
} catch (e) {}
}
},{"21":21}],21:[function(_dereq_,module,exports){
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = debug;
exports.coerce = coerce;
exports.disable = disable;
exports.enable = enable;
exports.enabled = enabled;
exports.humanize = _dereq_(25);
/**
* The currently active debug mode names, and names to skip.
*/
exports.names = [];
exports.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lowercased letter, i.e. "n".
*/
exports.formatters = {};
/**
* Previously assigned color.
*/
var prevColor = 0;
/**
* Previous log timestamp.
*/
var prevTime;
/**
* Select a color.
*
* @return {Number}
* @api private
*/
function selectColor() {
return exports.colors[prevColor++ % exports.colors.length];
}
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function debug(namespace) {
// define the `disabled` version
function disabled() {
}
disabled.enabled = false;
// define the `enabled` version
function enabled() {
var self = enabled;
// set `diff` timestamp
var curr = +new Date();
var ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
// add the `color` if not set
if (null == self.useColors) self.useColors = exports.useColors();
if (null == self.color && self.useColors) self.color = selectColor();
var args = Array.prototype.slice.call(arguments);
args[0] = exports.coerce(args[0]);
if ('string' !== typeof args[0]) {
// anything else let's inspect with %o
args = ['%o'].concat(args);
}
// apply any `formatters` transformations
var index = 0;
args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
// if we encounter an escaped % then don't increase the array index
if (match === '%%') return match;
index++;
var formatter = exports.formatters[format];
if ('function' === typeof formatter) {
var val = args[index];
match = formatter.call(self, val);
// now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});
if ('function' === typeof exports.formatArgs) {
args = exports.formatArgs.apply(self, args);
}
var logFn = enabled.log || exports.log || console.log.bind(console);
logFn.apply(self, args);
}
enabled.enabled = true;
var fn = exports.enabled(namespace) ? enabled : disabled;
fn.namespace = namespace;
return fn;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
exports.save(namespaces);
var split = (namespaces || '').split(/[\s,]+/);
var len = split.length;
for (var i = 0; i < len; i++) {
if (!split[i]) continue; // ignore empty strings
namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') {
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else {
exports.names.push(new RegExp('^' + namespaces + '$'));
}
}
}
/**
* Disable debug output.
*
* @api public
*/
function disable() {
exports.enable('');
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
var i, len;
for (i = 0, len = exports.skips.length; i < len; i++) {
if (exports.skips[i].test(name)) {
return false;
}
}
for (i = 0, len = exports.names.length; i < len; i++) {
if (exports.names[i].test(name)) {
return true;
}
}
return false;
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) return val.stack || val.message;
return val;
}
},{"25":25}],22:[function(_dereq_,module,exports){
(function (global){
'use strict';
var Mutation = global.MutationObserver || global.WebKitMutationObserver;
var scheduleDrain;
{
if (Mutation) {
var called = 0;
var observer = new Mutation(nextTick);
var element = global.document.createTextNode('');
observer.observe(element, {
characterData: true
});
scheduleDrain = function () {
element.data = (called = ++called % 2);
};
} else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
var channel = new global.MessageChannel();
channel.port1.onmessage = nextTick;
scheduleDrain = function () {
channel.port2.postMessage(0);
};
} else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
scheduleDrain = function () {
// Create a