pouchdb-live-find
Version:
Live PouchDB queries that update automatically as changes come in!
3,198 lines • 88.4 kB
JavaScript
(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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
var collate = require('pouchdb-collate').collate;
var selectorCore = require('pouchdb-selector-core');
var filterInMemoryFields = selectorCore.filterInMemoryFields;
var utils = require('./utils');
var compare = selectorCore.compare;
var getUserFields = utils.getUserFields;
var getKey = selectorCore.getKey;
var getValue = selectorCore.getValue;
var parseField = selectorCore.parseField;
exports.memoryFilter = function(docs, requestDef) {
var userFieldsRes = getUserFields(requestDef.selector, requestDef.sort);
var userFields = userFieldsRes.fields;
var rows = docs.map(function(doc) { return {doc: doc}; });
return filterInMemoryFields(rows, requestDef, userFields)
.map(function(row) {
return row.doc;
});
};
// create a comparator based on the sort object
exports.createFieldSorter = function(sort) {
function getFieldValuesAsArray(doc) {
return sort.map(function (sorting) {
var fieldName = getKey(sorting);
var parsedField = parseField(fieldName);
var docFieldValue = getFieldFromDoc(doc, parsedField);
return docFieldValue;
});
}
var directions = sort.map(function(sorting) {
if (typeof sorting !== 'string' &&
getValue(sorting) === 'desc') {
return -1;
}
return 1;
});
return function (aRow, bRow) {
var aFieldValues = getFieldValuesAsArray(aRow);
var bFieldValues = getFieldValuesAsArray(bRow);
for(var i=0, len=directions.length; i<len; i++) {
var collation = collate(aFieldValues[i], bFieldValues[i]);
if (collation !== 0) {
return collation * directions[i];
}
}
// this is what mango seems to do
return compare(aRow._id, bRow._id) * directions[0];
};
};
function getFieldFromDoc(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;
}
},{"./utils":3,"pouchdb-collate":9,"pouchdb-selector-core":13}],2:[function(require,module,exports){
'use strict';
var EventEmitter = require('events');
var helpers = require('./helpers');
var collate = require('pouchdb-collate').collate;
var selectorCore = require('pouchdb-selector-core');
var utils = require('./utils');
var getValue = selectorCore.getValue;
var massageSelector = selectorCore.massageSelector;
var massageSort = utils.massageSort;
function liveFind(requestDef) {
if(typeof this.find !== 'function') {
throw new Error('ERROR: PouchDB Find is a requirement for LiveFind and must be loaded first.');
}
var db = this;
var cancelled = false;
var lookup = {};
var emitter = new EventEmitter();
var docList = [];
var aggregate = requestDef.aggregate || false;
// Normalize the request options
var fields = requestDef.fields;
var stripId = false, stripRev = false;
if(fields) {
// _id is a necessary field to process the docs
if(fields.indexOf('_id') === -1) {
fields.unshift('_id');
stripId = true;
}
// We need the _rev to sort out changes but can strip it later
if(fields.indexOf('_rev') === -1) {
fields.push('_rev');
stripRev = true;
}
}
var selector;
if(requestDef.selector) {
selector = massageSelector(requestDef.selector);
}
var sort, sortFn;
if(requestDef.sort) {
sort = massageSort(requestDef.sort);
sortFn = helpers.createFieldSorter(sort);
}
var skip = parseInt(requestDef.skip, 10) || 0;
var limit = parseInt(requestDef.limit, 10) || 0;
var findRequest = {
selector: selector,
// sort: sort,
fields: fields
};
var ready = db.find(findRequest)
.then(function(results) {
results.docs.forEach(function(doc) {
addResult(doc);
});
emitter.emit('ready');
})
.catch(function(err) {
emitter.emit('error', err);
cancel();
throw err;
});
// We will use just one change listener for all live queries.
// We need to keep track of how many queries are running.
// When the last live query finishes we will cancel the listener.
if(!db._changeListener) {
listen();
}
if(!db._activeQueries) {
db._activeQueries = 1;
} else {
db._activeQueries++;
}
db._changeListener
.on('change', changeHandler)
.on('error', errorHandler);
emitter.cancel = cancel;
// Bind the `find` query promise to our emitter object
// so we know when the initial query is done and can catch errors
emitter.then = ready.then.bind(ready);
emitter.catch = ready.catch.bind(ready);
emitter.sort = function(list) {
if(!sort) {
return list;
}
return sortList(list);
};
emitter.paginate = paginate;
function changeHandler(change) {
ready.then(function() {
if(change.doc) {
processChange(change.doc);
}
});
}
function errorHandler(err) {
emitter.emit('error', err);
}
function cancel() {
if(!cancelled) {
db._activeQueries --;
if(!db._activeQueries) {
db._changeListener.cancel();
delete db._changeListener;
} else {
db._changeListener.removeListener('change', changeHandler);
db._changeListener.removeListener('error', errorHandler);
}
emitter.emit('cancelled');
emitter.removeAllListeners();
cancelled = true;
}
}
function listen() {
db._changeListener = db.changes({live: true, retry: true, include_docs: true, since: 'now'});
}
function filterDoc(doc) {
var result = helpers.memoryFilter([doc], {selector: selector});
if(result.length) {
return result[0];
}
return null;
}
function pickFields(doc) {
if (fields) {
var output = utils.pick(doc, fields);
if(stripId) {
delete output._id;
}
if(stripRev) {
delete output._rev;
}
return output;
}
return doc;
}
// This processes the initial results of the query
function addResult(doc) {
lookup[doc._id] = doc._rev;
var id = doc._id;
var rev = doc._rev;
if(stripId) {
delete doc._id;
}
if(stripRev) {
delete doc._rev;
}
return addAction(doc, id, rev);
}
function processChange(doc) {
// Don't fire an update if this rev has already been processed
if(lookup[doc._id] === doc._rev) {
// console.warn('A change was fired twice. This shouldn\'t happen.');
return;
}
var id = doc._id;
var rev = doc._rev;
if(doc._deleted && lookup[id]) {
lookup[id] = null;
return removeAction({_id: id, _rev: rev, deleted: true}, id, rev);
}
var outputDoc = filterDoc(doc);
if(!outputDoc && lookup[id]) {
lookup[id] = null;
return removeAction({_id: id, _rev: rev}, id, rev);
}
if(outputDoc && !lookup[id]) {
lookup[id] = rev;
return addAction(pickFields(outputDoc), id, rev);
}
if(outputDoc && lookup[id]) {
lookup[id] = rev;
return updateAction(pickFields(outputDoc), id, rev);
}
}
function removeAction(doc, id, rev) {
var list;
if(aggregate) {
docList = docList.filter(function(item) {
return item._id !== doc._id;
});
list = formatList(docList);
}
emitter.emit('update', { action: 'REMOVE', id: id, rev: rev, doc: doc }, list);
}
function addAction(doc, id, rev) {
var list;
if(aggregate) {
docList = docList.concat(doc);
list = formatList(docList);
}
emitter.emit('update', { action: 'ADD', id: id, rev: rev, doc: doc }, list);
}
function updateAction(doc, id, rev) {
var list;
if(aggregate) {
docList = docList.map(function(item) {
return item._id === doc._id ? doc : item;
});
list = formatList(docList);
}
emitter.emit('update', { action: 'UPDATE', id: id, rev: rev, doc: doc }, list);
}
/* function sortList(list) {
list = list.sort(sortFn);
if (typeof sort[0] !== 'string' &&
getValue(sort[0]) === 'desc') {
list = list.reverse();
}
return list;
} */
function sortList(list) {
return list.sort(sortFn);
}
// Applies sort, skip, and limit to a list
function formatList(list) {
if(sort) {
list = sortList(list);
}
if(skip || limit) {
if(limit) {
list = list.slice(skip, skip + limit);
} else {
list = list.slice(skip);
}
}
return list;
}
function paginate(options) {
if(!aggregate || !options || typeof options !== 'object') {
return;
}
if(options.skip != null) {
skip = parseInt(options.skip, 10) || 0;
}
if(options.limit != null) {
limit = parseInt(options.limit, 10) || 0;
}
if(options.sort && options.sort instanceof Array) {
sort = massageSort(options.sort);
sortFn = helpers.createFieldSorter(sort);
}
return formatList(docList);
}
return emitter;
}
exports.liveFind = liveFind;
if (typeof window !== 'undefined' && window.PouchDB) {
window.PouchDB.plugin(exports);
}
},{"./helpers":1,"./utils":3,"events":5,"pouchdb-collate":9,"pouchdb-selector-core":13}],3:[function(require,module,exports){
'use strict';
var selectorCore = require('pouchdb-selector-core');
var getKey = selectorCore.getKey;
var getFieldFromDoc = selectorCore.getFieldFromDoc;
var setFieldInDoc = selectorCore.setFieldInDoc;
var parseField = selectorCore.parseField;
// 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']
exports.getUserFields = function(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)
};
};
// normalize the "sort" value
exports.massageSort = function(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;
}
});
};
// 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 = parseField(arr[i]);
var value = getFieldFromDoc(obj, parsedField);
if (typeof value !== 'undefined') {
setFieldInDoc(res, parsedField, value);
}
}
return res;
};
},{"pouchdb-selector-core":13}],4:[function(require,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, []);
}
};
}
},{}],5:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
function EventEmitter() {
this._events = this._events || {};
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
if (!isNumber(n) || n < 0 || isNaN(n))
throw TypeError('n must be a positive number');
this._maxListeners = n;
return this;
};
EventEmitter.prototype.emit = function(type) {
var er, handler, len, args, i, listeners;
if (!this._events)
this._events = {};
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._events.error ||
(isObject(this._events.error) && !this._events.error.length)) {
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
// At least give some kind of context to the user
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
throw err;
}
}
}
handler = this._events[type];
if (isUndefined(handler))
return false;
if (isFunction(handler)) {
switch (arguments.length) {
// fast cases
case 1:
handler.call(this);
break;
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
args = Array.prototype.slice.call(arguments, 1);
handler.apply(this, args);
}
} else if (isObject(handler)) {
args = Array.prototype.slice.call(arguments, 1);
listeners = handler.slice();
len = listeners.length;
for (i = 0; i < len; i++)
listeners[i].apply(this, args);
}
return true;
};
EventEmitter.prototype.addListener = function(type, listener) {
var m;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events)
this._events = {};
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (this._events.newListener)
this.emit('newListener', type,
isFunction(listener.listener) ?
listener.listener : listener);
if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
else if (isObject(this._events[type]))
// 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];
// Check for listener leak
if (isObject(this._events[type]) && !this._events[type].warned) {
if (!isUndefined(this._maxListeners)) {
m = this._maxListeners;
} else {
m = EventEmitter.defaultMaxListeners;
}
if (m && m > 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);
if (typeof console.trace === 'function') {
// not supported in IE 10
console.trace();
}
}
}
return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.once = function(type, listener) {
if (!isFunction(listener))
throw TypeError('listener must be a function');
var fired = false;
function g() {
this.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(this, arguments);
}
}
g.listener = listener;
this.on(type, g);
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
var list, position, length, i;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events || !this._events[type])
return this;
list = this._events[type];
length = list.length;
position = -1;
if (list === listener ||
(isFunction(list.listener) && list.listener === listener)) {
delete this._events[type];
if (this._events.removeListener)
this.emit('removeListener', type, listener);
} else if (isObject(list)) {
for (i = length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list.length = 0;
delete this._events[type];
} else {
list.splice(position, 1);
}
if (this._events.removeListener)
this.emit('removeListener', type, listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners = function(type) {
var key, listeners;
if (!this._events)
return this;
// not listening for removeListener, no need to emit
if (!this._events.removeListener) {
if (arguments.length === 0)
this._events = {};
else if (this._events[type])
delete this._events[type];
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
for (key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}
listeners = this._events[type];
if (isFunction(listeners)) {
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
while (listeners.length)
this.removeListener(type, listeners[listeners.length - 1]);
}
delete this._events[type];
return this;
};
EventEmitter.prototype.listeners = function(type) {
var ret;
if (!this._events || !this._events[type])
ret = [];
else if (isFunction(this._events[type]))
ret = [this._events[type]];
else
ret = this._events[type].slice();
return ret;
};
EventEmitter.prototype.listenerCount = function(type) {
if (this._events) {
var evlistener = this._events[type];
if (isFunction(evlistener))
return 1;
else if (evlistener)
return evlistener.length;
}
return 0;
};
EventEmitter.listenerCount = function(emitter, type) {
return emitter.listenerCount(type);
};
function isFunction(arg) {
return typeof arg === 'function';
}
function isNumber(arg) {
return typeof arg === 'number';
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
function isUndefined(arg) {
return arg === void 0;
}
},{}],6:[function(require,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 <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var scriptEl = global.document.createElement('script');
scriptEl.onreadystatechange = function () {
nextTick();
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
global.document.documentElement.appendChild(scriptEl);
};
} else {
scheduleDrain = function () {
setTimeout(nextTick, 0);
};
}
}
var draining;
var queue = [];
//named nextTick for less confusing stack traces
function nextTick() {
draining = true;
var i, oldQueue;
var len = queue.length;
while (len) {
oldQueue = queue;
queue = [];
i = -1;
while (++i < len) {
oldQueue[i]();
}
len = queue.length;
}
draining = false;
}
module.exports = immediate;
function immediate(task) {
if (queue.push(task) === 1 && !draining) {
scheduleDrain();
}
}
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],7:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{}],8:[function(require,module,exports){
'use strict';
var immediate = require('immediate');
/* istanbul ignore next */
function INTERNAL() {}
var handlers = {};
var REJECTED = ['REJECTED'];
var FULFILLED = ['FULFILLED'];
var PENDING = ['PENDING'];
module.exports = Promise;
function Promise(resolver) {
if (typeof resolver !== 'function') {
throw new TypeError('resolver must be a function');
}
this.state = PENDING;
this.queue = [];
this.outcome = void 0;
if (resolver !== INTERNAL) {
safelyResolveThenable(this, resolver);
}
}
Promise.prototype["catch"] = function (onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.then = function (onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
typeof onRejected !== 'function' && this.state === REJECTED) {
return this;
}
var promise = new this.constructor(INTERNAL);
if (this.state !== PENDING) {
var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
unwrap(promise, resolver, this.outcome);
} else {
this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
}
return promise;
};
function QueueItem(promise, onFulfilled, onRejected) {
this.promise = promise;
if (typeof onFulfilled === 'function') {
this.onFulfilled = onFulfilled;
this.callFulfilled = this.otherCallFulfilled;
}
if (typeof onRejected === 'function') {
this.onRejected = onRejected;
this.callRejected = this.otherCallRejected;
}
}
QueueItem.prototype.callFulfilled = function (value) {
handlers.resolve(this.promise, value);
};
QueueItem.prototype.otherCallFulfilled = function (value) {
unwrap(this.promise, this.onFulfilled, value);
};
QueueItem.prototype.callRejected = function (value) {
handlers.reject(this.promise, value);
};
QueueItem.prototype.otherCallRejected = function (value) {
unwrap(this.promise, this.onRejected, value);
};
function unwrap(promise, func, value) {
immediate(function () {
var returnValue;
try {
returnValue = func(value);
} catch (e) {
return handlers.reject(promise, e);
}
if (returnValue === promise) {
handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
} else {
handlers.resolve(promise, returnValue);
}
});
}
handlers.resolve = function (self, value) {
var result = tryCatch(getThen, value);
if (result.status === 'error') {
return handlers.reject(self, result.value);
}
var thenable = result.value;
if (thenable) {
safelyResolveThenable(self, thenable);
} else {
self.state = FULFILLED;
self.outcome = value;
var i = -1;
var len = self.queue.length;
while (++i < len) {
self.queue[i].callFulfilled(value);
}
}
return self;
};
handlers.reject = function (self, error) {
self.state = REJECTED;
self.outcome = error;
var i = -1;
var len = self.queue.length;
while (++i < len) {
self.queue[i].callRejected(error);
}
return self;
};
function getThen(obj) {
// Make sure we only access the accessor once as required by the spec
var then = obj && obj.then;
if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
return function appyThen() {
then.apply(obj, arguments);
};
}
}
function safelyResolveThenable(self, thenable) {
// Either fulfill, reject or reject with error
var called = false;
function onError(value) {
if (called) {
return;
}
called = true;
handlers.reject(self, value);
}
function onSuccess(value) {
if (called) {
return;
}
called = true;
handlers.resolve(self, value);
}
function tryToUnwrap() {
thenable(onSuccess, onError);
}
var result = tryCatch(tryToUnwrap);
if (result.status === 'error') {
onError(result.value);
}
}
function tryCatch(func, value) {
var out = {};
try {
out.value = func(value);
out.status = 'success';
} catch (e) {
out.status = 'error';
out.value = e;
}
return out;
}
Promise.resolve = resolve;
function resolve(value) {
if (value instanceof this) {
return value;
}
return handlers.resolve(new this(INTERNAL), value);
}
Promise.reject = reject;
function reject(reason) {
var promise = new this(INTERNAL);
return handlers.reject(promise, reason);
}
Promise.all = all;
function all(iterable) {
var self = this;
if (Object.prototype.toString.call(iterable) !== '[object Array]') {
return this.reject(new TypeError('must be an array'));
}
var len = iterable.length;
var called = false;
if (!len) {
return this.resolve([]);
}
var values = new Array(len);
var resolved = 0;
var i = -1;
var promise = new this(INTERNAL);
while (++i < len) {
allResolver(iterable[i], i);
}
return promise;
function allResolver(value, i) {
self.resolve(value).then(resolveFromAll, function (error) {
if (!called) {
called = true;
handlers.reject(promise, error);
}
});
function resolveFromAll(outValue) {
values[i] = outValue;
if (++resolved === len && !called) {
called = true;
handlers.resolve(promise, values);
}
}
}
}
Promise.race = race;
function race(iterable) {
var self = this;
if (Object.prototype.toString.call(iterable) !== '[object Array]') {
return this.reject(new TypeError('must be an array'));
}
var len = iterable.length;
var called = false;
if (!len) {
return this.resolve([]);
}
var i = -1;
var promise = new this(INTERNAL);
while (++i < len) {
resolver(iterable[i]);
}
return promise;
function resolver(value) {
self.resolve(value).then(function (response) {
if (!called) {
called = true;
handlers.resolve(promise, response);
}
}, function (error) {
if (!called) {
called = true;
handlers.reject(promise, error);
}
});
}
}
},{"immediate":6}],9:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function pad(str, padWith, upToLength) {
var padding = '';
var targetLength = upToLength - str.length;
/* istanbul ignore next */
while (padding.length < targetLength) {
padding += padWith;
}
return padding;
}
function padLeft(str, padWith, upToLength) {
var padding = pad(str, padWith, upToLength);
return padding + str;
}
var MIN_MAGNITUDE = -324; // verified by -Number.MIN_VALUE
var MAGNITUDE_DIGITS = 3; // ditto
var SEP = ''; // set to '_' for easier debugging
function collate(a, b) {
if (a === b) {
return 0;
}
a = normalizeKey(a);
b = normalizeKey(b);
var ai = collationIndex(a);
var bi = collationIndex(b);
if ((ai - bi) !== 0) {
return ai - bi;
}
switch (typeof a) {
case 'number':
return a - b;
case 'boolean':
return a < b ? -1 : 1;
case 'string':
return stringCollate(a, b);
}
return Array.isArray(a) ? arrayCollate(a, b) : objectCollate(a, b);
}
// couch considers null/NaN/Infinity/-Infinity === undefined,
// for the purposes of mapreduce indexes. also, dates get stringified.
function normalizeKey(key) {
switch (typeof key) {
case 'undefined':
return null;
case 'number':
if (key === Infinity || key === -Infinity || isNaN(key)) {
return null;
}
return key;
case 'object':
var origKey = key;
if (Array.isArray(key)) {
var len = key.length;
key = new Array(len);
for (var i = 0; i < len; i++) {
key[i] = normalizeKey(origKey[i]);
}
/* istanbul ignore next */
} else if (key instanceof Date) {
return key.toJSON();
} else if (key !== null) { // generic object
key = {};
for (var k in origKey) {
if (origKey.hasOwnProperty(k)) {
var val = origKey[k];
if (typeof val !== 'undefined') {
key[k] = normalizeKey(val);
}
}
}
}
}
return key;
}
function indexify(key) {
if (key !== null) {
switch (typeof key) {
case 'boolean':
return key ? 1 : 0;
case 'number':
return numToIndexableString(key);
case 'string':
// We've to be sure that key does not contain \u0000
// Do order-preserving replacements:
// 0 -> 1, 1
// 1 -> 1, 2
// 2 -> 2, 2
return key
.replace(/\u0002/g, '\u0002\u0002')
.replace(/\u0001/g, '\u0001\u0002')
.replace(/\u0000/g, '\u0001\u0001');
case 'object':
var isArray = Array.isArray(key);
var arr = isArray ? key : Object.keys(key);
var i = -1;
var len = arr.length;
var result = '';
if (isArray) {
while (++i < len) {
result += toIndexableString(arr[i]);
}
} else {
while (++i < len) {
var objKey = arr[i];
result += toIndexableString(objKey) +
toIndexableString(key[objKey]);
}
}
return result;
}
}
return '';
}
// convert the given key to a string that would be appropriate
// for lexical sorting, e.g. within a database, where the
// sorting is the same given by the collate() function.
function toIndexableString(key) {
var zero = '\u0000';
key = normalizeKey(key);
return collationIndex(key) + SEP + indexify(key) + zero;
}
function parseNumber(str, i) {
var originalIdx = i;
var num;
var zero = str[i] === '1';
if (zero) {
num = 0;
i++;
} else {
var neg = str[i] === '0';
i++;
var numAsString = '';
var magAsString = str.substring(i, i + MAGNITUDE_DIGITS);
var magnitude = parseInt(magAsString, 10) + MIN_MAGNITUDE;
/* istanbul ignore next */
if (neg) {
magnitude = -magnitude;
}
i += MAGNITUDE_DIGITS;
while (true) {
var ch = str[i];
if (ch === '\u0000') {
break;
} else {
numAsString += ch;
}
i++;
}
numAsString = numAsString.split('.');
if (numAsString.length === 1) {
num = parseInt(numAsString, 10);
} else {
/* istanbul ignore next */
num = parseFloat(numAsString[0] + '.' + numAsString[1]);
}
/* istanbul ignore next */
if (neg) {
num = num - 10;
}
/* istanbul ignore next */
if (magnitude !== 0) {
// parseFloat is more reliable than pow due to rounding errors
// e.g. Number.MAX_VALUE would return Infinity if we did
// num * Math.pow(10, magnitude);
num = parseFloat(num + 'e' + magnitude);
}
}
return {num: num, length : i - originalIdx};
}
// move up the stack while parsing
// this function moved outside of parseIndexableString for performance
function pop(stack, metaStack) {
var obj = stack.pop();
if (metaStack.length) {
var lastMetaElement = metaStack[metaStack.length - 1];
if (obj === lastMetaElement.element) {
// popping a meta-element, e.g. an object whose value is another object
metaStack.pop();
lastMetaElement = metaStack[metaStack.length - 1];
}
var element = lastMetaElement.element;
var lastElementIndex = lastMetaElement.index;
if (Array.isArray(element)) {
element.push(obj);
} else if (lastElementIndex === stack.length - 2) { // obj with key+value
var key = stack.pop();
element[key] = obj;
} else {
stack.push(obj); // obj with key only
}
}
}
function parseIndexableString(str) {
var stack = [];
var metaStack = []; // stack for arrays and objects
var i = 0;
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while (true) {
var collationIndex = str[i++];
if (collationIndex === '\u0000') {
if (stack.length === 1) {
return stack.pop();
} else {
pop(stack, metaStack);
continue;
}
}
switch (collationIndex) {
case '1':
stack.push(null);
break;
case '2':
stack.push(str[i] === '1');
i++;
break;
case '3':
var parsedNum = parseNumber(str, i);
stack.push(parsedNum.num);
i += parsedNum.length;
break;
case '4':
var parsedStr = '';
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while (true) {
var ch = str[i];
if (ch === '\u0000') {
break;
}
parsedStr += ch;
i++;
}
// perform the reverse of the order-preserving replacement
// algorithm (see above)
parsedStr = parsedStr.replace(/\u0001\u0001/g, '\u0000')
.replace(/\u0001\u0002/g, '\u0001')
.replace(/\u0002\u0002/g, '\u0002');
stack.push(parsedStr);
break;
case '5':
var arrayElement = { element: [], index: stack.length };
stack.push(arrayElement.element);
metaStack.push(arrayElement);
break;
case '6':
var objElement = { element: {}, index: stack.length };
stack.push(objElement.element);
metaStack.push(objElement);
break;
/* istanbul ignore next */
default:
throw new Error(
'bad collationIndex or unexpectedly reached end of input: ' +
collationIndex);
}
}
}
function arrayCollate(a, b) {
var len = Math.min(a.length, b.length);
for (var i = 0; i < len; i++) {
var sort = collate(a[i], b[i]);
if (sort !== 0) {
return sort;
}
}
return (a.length === b.length) ? 0 :
(a.length > b.length) ? 1 : -1;
}
function stringCollate(a, b) {
// See: https://github.com/daleharvey/pouchdb/issues/40
// This is incompatible with the CouchDB implementation, but its the
// best we can do for now
return (a === b) ? 0 : ((a > b) ? 1 : -1);
}
function objectCollate(a, b) {
var ak = Object.keys(a), bk = Object.keys(b);
var len = Math.min(ak.length, bk.length);
for (var i = 0; i < len; i++) {
// First sort the keys
var sort = collate(ak[i], bk[i]);
if (sort !== 0) {
return sort;
}
// if the keys are equal sort the values
sort = collate(a[ak[i]], b[bk[i]]);
if (sort !== 0) {
return sort;
}
}
return (ak.length === bk.length) ? 0 :
(ak.length > bk.length) ? 1 : -1;
}
// The collation is defined by erlangs ordered terms
// the atoms null, true, false come first, then numbers, strings,
// arrays, then objects
// null/undefined/NaN/Infinity/-Infinity are all considered null
function collationIndex(x) {
var id = ['boolean', 'number', 'string', 'object'];
var idx = id.indexOf(typeof x);
//false if -1 otherwise true, but fast!!!!1
if (~idx) {
if (x === null) {
return 1;
}
if (Array.isArray(x)) {
return 5;
}
return idx < 3 ? (idx + 2) : (idx + 3);
}
/* istanbul ignore next */
if (Array.isArray(x)) {
return 5;
}
}
// conversion:
// x yyy zz...zz
// x = 0 for negative, 1 for 0, 2 for positive
// y = exponent (for negative numbers negated) moved so that it's >= 0
// z = mantisse
function numToIndexableString(num) {
if (num === 0) {
return '1';
}
// convert number to exponential format for easier and
// more succinct string sorting
var expFormat = num.toExponential().split(/e\+?/);
var magnitude = parseInt(expFormat[1], 10);
var neg = num < 0;
var result = neg ? '0' : '2';
// first sort by magnitude
// it's easier if all magnitudes are positive
var magForComparison = ((neg ? -magnitude : magnitude) - MIN_MAGNITUDE);
var magString = padLeft((magForComparison).toString(), '0', MAGNITUDE_DIGITS);
result += SEP + magString;
// then sort by the factor
var factor = Math.abs(parseFloat(expFormat[0])); // [1..10)
/* istanbul ignore next */
if (neg) { // for negative reverse ordering
factor = 10 - factor;
}
var factorStr = factor.toFixed(20);
// strip zeros from the end
factorStr = factorStr.replace(/\.?0+$/, '');
result += SEP + factorStr;
return result;
}
exports.collate = collate;
exports.normalizeKey = normalizeKey;
exports.toIndexableString = toIndexableString;
exports.parseIndexableString = parseIndexableString;
},{}],10:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function mangle(key) {
return '$' + key;
}
function unmangle(key) {
return key.substring(1);
}
function Map$1() {
this._store = {};
}
Map$1.prototype.get = function (key) {
var mangled = mangle(key);
return this._store[mangled];
};
Map$1.prototype.set = function (key, value) {
var mangled = mangle(key);
this._store[mangled] = value;
return true;
};
Map$1.prototype.has = function (key) {
var mangled = mangle(key);
return mangled in this._store;
};
Map$1.prototype.delete = function (key) {
var mangled = mangle(key);
var res = mangled in this._store;
delete this._store[mangled];
return res;
};
Map$1.prototype.forEach = function (cb) {
var keys = Object.keys(this._store);
for (var i = 0, len = keys.length; i < len; i++) {
var key = keys[i];
var value = this._store[key];
key = unmangle(key);
cb(value, key);
}
};
Object.defineProperty(Map$1.prototype, 'size', {
get: function () {
return Object.keys(this._store).length;
}
});
function Set$1(array) {
this._store = new Map$1();
// init with an array
if (array && Array.isArray(array)) {
for (var i = 0, len = array.length; i < len; i++) {
this.add(array[i]);
}
}
}
Set$1.prototype.add = function (key) {
return this._store.set(key, true);
};
Set$1.prototype.has = function (key) {
return this._store.has(key);
};
Set$1.prototype.forEach = function (cb) {
this._store.forEach(function (value, key) {
cb(key);
});
};
Object.defineProperty(Set$1.prototype, 'size', {
get: function () {
return this._store.size;
}
});
/* global Map,Set,Symbol */
// Based on https://kangax.github.io/compat-table/es6/ we can sniff out
// incomplete Map/Set implementations which would otherwise cause our tests to fail.
// Notably they fail in IE11 and iOS 8.4, which this prevents.
function supportsMapAndSet() {
if (typeof Symbol === 'undefined' || typeof Map === 'undefined' || typeof Set === 'undefined') {
return false;
}
var prop = Object.getOwnPropertyDescriptor(Map, Symbol.species);
return prop && 'get' in prop && Map[Symbol.species] === Map;
}
// based on https://github.com/montagejs/collections
/* global Map,Set */
{
if (supportsMapAndSet()) { // prefer built-in Map/Set
exports.Set = Set;
exports.Map = Map;
} else { // fall back to our polyfill
exports.Set = Set$1;
exports.Map = Map$1;
}
}
},{}],11:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var inherits = _interopDefault(require('inherits'));
inherits(PouchError, Error);
function PouchError(status, error, reason) {
Error.call(this, reason);
this.status = status;
this.name = error;
this.message = reason;
this.error = true;
}
PouchError.prototype.toString = function () {
return JSON.stringify({
status: this.status,
name: this.name,
message: this.message,
reason: this.reason
});
};
var UNAUTHORIZED = new PouchError(401, 'unauthorized', "Name or password is incorrect.");
var MISSING_BULK_DOCS = new PouchError(400, 'bad_request', "Missing JSON list of 'docs'");
var MISSING_DOC = new PouchError(404, 'not_found', 'missing');
var REV_CONFLICT = new PouchError(409, 'conflict', 'Document update conflict');
var INVALID_ID = new PouchError(400, 'bad_request', '_id field must contain a string');
var MISSING_ID = new PouchError(412, 'missing_id', '_id is required for puts');
var RESERVED_ID = new PouchError(400, 'bad_request', 'Only reserved document ids may start with underscore.');
var NOT_OPEN = new PouchError(412, 'precondition_failed', 'Database not open');
var UNKNOWN_ERROR = new PouchError(500, 'unknown_error', 'Database encountered an unknown error');
var BAD_ARG = new PouchError(500, 'badarg', 'Some query argument is invalid');
var INVALID_REQUEST = new PouchError(400, 'invalid_request', 'Request was invalid');
var QUERY_PARSE_ERROR = new PouchError(400, 'query_parse_error', 'Some query parameter is invalid');
var DOC_VALIDATION = new PouchError(500, 'doc_validation', 'Bad special document member');
var BAD_REQUEST = new PouchError(400, 'bad_request', 'Something wrong with the request');
var NOT_AN_OBJECT = new PouchError(400, 'bad_request', 'Document must be a JSON object');
var DB_MISSING = new PouchError(404, 'not_found', 'Database not found');
var IDB_ERROR = new PouchError(500, 'indexed_db_went_bad', 'unknown');
var WSQ_ERROR = new PouchError(500, 'web_sql_went_bad', 'unknown');
var LDB_ERROR = new PouchError(500, 'levelDB_went_went_bad', 'unknown');
var FORBIDDEN = new PouchError(403, 'forbidden', 'Forbidden by design doc validate_doc_update function');
var INVALID_REV = new PouchError(400, 'bad_request', 'Invalid rev format');
var FILE_EXISTS = new PouchError(412, 'file_exists', 'The database could not be created, the file already exists.');
var MISSING_STUB = new PouchError(412, 'missing_stub', 'A pre-existing attachment stub wasn\'t found');
var INVALID_URL = new PouchError(413, 'invalid_url', 'Provided URL is invalid');
function createError(error, reason) {
function CustomPouchError(reason) {
// inherit error properties from our parent error manually
// so as to allow proper JSON parsing.
/* jshint ignore:start */
for (var p in error) {
if (typeof error[p] !== 'function') {
this[p] = error[p];
}
}
/* jshint ignore:end */
if (reason !== undefined) {
this.reason = reason;
}
}
CustomPouchError.prototype = PouchError.prototype;
return new CustomPouchError(reason);
}
function generateErrorFromResponse(err) {
if (typeof err !== 'object') {
var data = err;
err = UNKNOWN_ERROR;
err.data = data;
}
if ('error' in err && err.error === 'conflict') {
err.name = 'conflict';
err.status = 409;
}
if (!('name' in err)) {
err.name = err.error || 'unknown';
}
if (!('status' in err)) {
err.status = 500;
}
if (!('message' in err)) {
err.message = err.message || err.reason;
}
return err;
}
exports.UNAUTHORIZED = UNAUTHORIZED;
exports.MISSING_BULK_DOCS = MISSING_BULK_DOCS;
exports.MISSING_DOC = MISSING_DOC;
exports.REV_CONFLICT = REV_CONFLICT;
exports.INVALID_ID = INVALID_ID;
exports.MISSING_ID = MISSING_ID;
exports.RESERVED_ID = RESERVED_ID;
exports.NOT_OPEN = NOT_OPEN;
exports.UNKNOWN_ERROR = UNKNOWN_ERROR;
exports.BAD_ARG = BAD_ARG;
exports.INVALID_REQUEST = INVALID_REQUEST;
exports.QUERY_PARSE_ERROR = QUERY_PARSE_ERROR;
exports.DOC_VALIDATION = DOC_VALIDATION;
exports.BAD_REQUEST = BAD_REQUEST;
exports.NOT_AN_OBJECT = NOT_AN_OBJECT;
exports.DB_MISSING = DB_MISSING;
exports.WSQ_ERROR = WSQ_ERROR;
exports.LDB_ERROR = LDB_ERROR;
exports.FORBIDDEN = FORBIDDEN;
exports.INVALID_REV = INVALID_REV;
exports.FILE_EXISTS = FILE_EXISTS;
exports.MISSING_STUB = MISSING_STUB;
exports.IDB_ERROR = IDB_ERROR;
exports.INVALID_URL = INVALID_URL;
exports.createError = createError;
exports.generateErrorFromResponse = generateErrorFromResponse;
},{"inherits":7}],12:[function(require,module,exports){
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var lie = _interopDefault(require('lie'));
/* istanbul ignore next */
var PouchPromise = typeof Promise === 'function' ? Promise : lie;
module.exports = PouchPromise;
},{"lie":8}],13:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var pouchdbUtils = require('pouchdb-utils');
var pouchdbCollate = require('pouchdb-collate');
// this would just be "return doc[field]", but fields
// can be "deep" due to dot notation
function getFieldFromDoc(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;
}
function setFieldInDoc(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;
}
function compare(left, right) {
return left < right ? -1 : left > right ? 1 : 0;
}
// Converts a string in dot notation to an array of its components, with backslash escaping
function parseField(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;
}
var combinationFields = ['$or', '$nor', '$not'];
function isCombinationalField(field) {
return combinationFields.indexOf(field) > -1;
}
function getKey(obj) {
return Object.keys(obj)[0];
}
function getValue(obj) {
return obj[getKey(obj)];
}
// 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;
}
// 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;
}
//
// normalize the selector
//
function massageSelector(input) {
var result = pouchdbUtils.clone(input);
var wasAnded = false;
if ('$and' in result) {
result = mergeAndedSelectors(result['$and']);
wasAnded = true;
}
['$or', '$nor'].forEach(function (orOrNor) {
if (orOrNor in result) {
// message each individual selector
// e.g. {foo: 'bar'} becomes {foo: {$eq: 'bar'}}
result[orOrNor].forEach(function (subSelector) {
var fields = Object.keys(subSelector);
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
var matcher = subSelector[field];
if (typeof matcher !== 'object' || matcher === null) {
subSelector[field] = {$eq: matcher};
}
}
});
}
});
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;
}
// 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 = pouchdbCollate.collate(aFieldValues, bFieldValues);
if (collation !== 0) {
return collation;
}
// this is what mango seems to do
return 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) {
if (isDesignDoc(doc)) {
return false;
}
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 isDesignDoc(doc) {
return /^_design\//.test(doc._id);
}
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 (!Array.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) && pouchdbCollate.collate(docFieldValue, userValue) === 0;
},
'$gte': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && pouchdbCollate.collate(docFieldValue, userValue) >= 0;
},
'$gt': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && pouchdbCollate.collate(docFieldValue, userValue) > 0;
},
'$lte': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && pouchdbCollate.collate(docFieldValue, userValue) <= 0;
},
'$lt': function (doc, userValue, parsedField, docFieldValue) {
return fieldIsNotUndefined(docFieldValue) && pouchdbCollate.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 pouchdbCollate.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 Array.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);
}
};
// return true if the given doc matches the supplied selector
function matchesSelector(doc, selector) {
/* istanbul ignore if */
if (typeof selector !== 'object') {
// match the CouchDB error message
throw 'Selector error: expected a JSON object';
}
selector = massageSelector(selector);
var row = {
'doc': doc
};
var rowsMatched = filterInMemoryFields([row], { 'selector': selector }, Object.keys(selector));
return rowsMatched && rowsMatched.length === 1;
}
exports.massageSelector = massageSelector;
exports.matchesSelector = matchesSelector;
exports.filterInMemoryFields = filterInMemoryFields;
exports.createFieldSorter = createFieldSorter;
exports.rowFilter = rowFilter;
exports.isCombinationalField = isCombinationalField;
exports.getKey = getKey;
exports.getValue = getValue;
exports.getFieldFromDoc = getFieldFromDoc;
exports.setFieldInDoc = setFieldInDoc;
exports.compare = compare;
exports.parseField = parseField;
},{"pouchdb-collate":9,"pouchdb-utils":14}],14:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var Promise = _interopDefault(require('pouchdb-promise'));
var getArguments = _interopDefault(require('argsarray'));
var pouchdbCollections = require('pouchdb-collections');
var events = require('events');
var inherits = _interopDefault(require('inherits'));
var immediate = _interopDefault(require('immediate'));
var pouchdbErrors = require('pouchdb-errors');
function isBinaryObject(object) {
return (typeof ArrayBuffer !== 'undefined' && object instanceof ArrayBuffer) ||
(typeof Blob !== 'undefined' && object instanceof Blob);
}
function cloneArrayBuffer(buff) {
if (typeof buff.slice === 'function') {
return buff.slice(0);
}
// IE10-11 slice() polyfill
var target = new ArrayBuffer(buff.byteLength);
var targetArray = new Uint8Array(target);
var sourceArray = new Uint8Array(buff);
targetArray.set(sourceArray);
return target;
}
function cloneBinaryObject(object) {
if (object instanceof ArrayBuffer) {
return cloneArrayBuffer(object);
}
var size = object.size;
var type = object.type;
// Blob
if (typeof object.slice === 'function') {
return object.slice(0, size, type);
}
// PhantomJS slice() replacement
return object.webkitSlice(0, size, type);
}
// most of this is borrowed from lodash.isPlainObject:
// https://github.com/fis-components/lodash.isplainobject/
// blob/29c358140a74f252aeb08c9eb28bef86f2217d4a/index.js
var funcToString = Function.prototype.toString;
var objectCtorString = funcToString.call(Object);
function isPlainObject(value) {
var proto = Object.getPrototypeOf(value);
/* istanbul ignore if */
if (proto === null) { // not sure when this happens, but I guess it can
return true;
}
var Ctor = proto.constructor;
return (typeof Ctor == 'function' &&
Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
}
function clone(object) {
var newObject;
var i;
var len;
if (!object || typeof object !== 'object') {
return object;
}
if (Array.isArray(object)) {
newObject = [];
for (i = 0, len = object.length; i < len; i++) {
newObject[i] = clone(object[i]);
}
return newObject;
}
// special case: to avoid inconsistencies between IndexedDB
// and other backends, we automatically stringify Dates
if (object instanceof Date) {
return object.toISOString();
}
if (isBinaryObject(object)) {
return cloneBinaryObject(object);
}
if (!isPlainObject(object)) {
return object; // don't clone objects like Workers
}
newObject = {};
for (i in object) {
/* istanbul ignore else */
if (Object.prototype.hasOwnProperty.call(object, i)) {
var value = clone(object[i]);
if (typeof value !== 'undefined') {
newObject[i] = value;
}
}
}
return newObject;
}
function once(fun) {
var called = false;
return getArguments(function (args) {
/* istanbul ignore if */
if (called) {
// this is a smoke test and should never actually happen
throw new Error('once called more than once');
} else {
called = true;
fun.apply(this, args);
}
});
}
function toPromise(func) {
//create the function we will be returning
return getArguments(function (args) {
// Clone arguments
args = clone(args);
var self = this;
// if the last argument is a function, assume its a callback
var usedCB = (typeof args[args.length - 1] === 'function') ? args.pop() : false;
var promise = new Promise(function (fulfill, reject) {
var resp;
try {
var callback = 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);
resp = func.apply(self, args);
if (resp && typeof resp.then === 'function') {
fulfill(resp);
}
} catch (e) {
reject(e);
}
});
// if there is a callback, call it back
if (usedCB) {
promise.then(function (result) {
usedCB(null, result);
}, usedCB);
}
return promise;
});
}
function logApiCall(self, name, args) {
/* istanbul ignore if */
if (self.constructor.listeners('debug').length) {
var logArgs = ['api', self.name, name];
for (var i = 0; i < args.length - 1; i++) {
logArgs.push(args[i]);
}
self.constructor.emit('debug', logArgs);
// override the callback itself to log the response
var origCallback = args[args.length - 1];
args[args.length - 1] = function (err, res) {
var responseArgs = ['api', self.name, name];
responseArgs = responseArgs.concat(
err ? ['error', err] : ['success', res]
);
self.constructor.emit('debug', responseArgs);
origCallback(err, res);
};
}
}
function adapterFun(name, callback) {
return toPromise(getArguments(function (args) {
if (this._closed) {
return Promise.reject(new Error('database is closed'));
}
if (this._destroyed) {
return Promise.reject(new Error('database is destroyed'));
}
var self = this;
logApiCall(self, name, args);
if (!this.taskqueue.isReady) {
return new Promise(function (fulfill, reject) {
self.taskqueue.addTask(function (failed) {
if (failed) {
reject(failed);
} else {
fulfill(self[name].apply(self, args));
}
});
});
}
return callback.apply(this, args);
}));
}
// like underscore/lodash _.pick()
function pick(obj, arr) {
var res = {};
for (var i = 0, len = arr.length; i < len; i++) {
var prop = arr[i];
if (prop in obj) {
res[prop] = obj[prop];
}
}
return res;
}
// Most browsers throttle concurrent requests at 6, so it's silly
// to shim _bulk_get by trying to launch potentially hundreds of requests
// and then letting the majority time out. We can handle this ourselves.
var MAX_NUM_CONCURRENT_REQUESTS = 6;
function identityFunction(x) {
return x;
}
function formatResultForOpenRevsGet(result) {
return [{
ok: result
}];
}
// shim for P/CouchDB adapters that don't directly implement _bulk_get
function bulkGet(db, opts, callback) {
var requests = opts.docs;
// consolidate into one request per doc if possible
var requestsById = new pouchdbCollections.Map();
requests.forEach(function (request) {
if (requestsById.has(request.id)) {
requestsById.get(request.id).push(request);
} else {
requestsById.set(request.id, [request]);
}
});
var numDocs = requestsById.size;
var numDone = 0;
var perDocResults = new Array(numDocs);
function collapseResultsAndFinish() {
var results = [];
perDocResults.forEach(function (res) {
res.docs.forEach(function (info) {
results.push({
id: res.id,
docs: [info]
});
});
});
callback(null, {results: results});
}
function checkDone() {
if (++numDone === numDocs) {
collapseResultsAndFinish();
}
}
function gotResult(docIndex, id, docs) {
perDocResults[docIndex] = {id: id, docs: docs};
checkDone();
}
var allRequests = [];
requestsById.forEach(function (value, key) {
allRequests.push(key);
});
var i = 0;
function nextBatch() {
if (i >= allRequests.length) {
return;
}
var upTo = Math.min(i + MAX_NUM_CONCURRENT_REQUESTS, allRequests.length);
var batch = allRequests.slice(i, upTo);
processBatch(batch, i);
i += batch.length;
}
function processBatch(batch, offset) {
batch.forEach(function (docId, j) {
var docIdx = offset + j;
var docRequests = requestsById.get(docId);
// just use the first request as the "template"
// TODO: The _bulk_get API allows for more subtle use cases than this,
// but for now it is unlikely that there will be a mix of different
// "atts_since" or "attachments" in the same request, since it's just
// replicate.js that is using this for the moment.
// Also, atts_since is aspirational, since we don't support it yet.
var docOpts = pick(docRequests[0], ['atts_since', 'attachments']);
docOpts.open_revs = docRequests.map(function (request) {
// rev is optional, open_revs disallowed
return request.rev;
});
// remove falsey / undefined revisions
docOpts.open_revs = docOpts.open_revs.filter(identityFunction);
var formatResult = identityFunction;
if (docOpts.open_revs.length === 0) {
delete docOpts.open_revs;
// when fetching only the "winning" leaf,
// transform the result so it looks like an open_revs
// request
formatResult = formatResultForOpenRevsGet;
}
// globally-supplied options
['revs', 'attachments', 'binary', 'ajax', 'latest'].forEach(function (param) {
if (param in opts) {
docOpts[param] = opts[param];
}
});
db.get(docId, docOpts, function (err, res) {
var result;
/* istanbul ignore if */
if (err) {
result = [{error: err}];
} else {
result = formatResult(res);
}
gotResult(docIdx, docId, result);
nextBatch();
});
});
}
nextBatch();
}
function isChromeApp() {
return (typeof chrome !== "undefined" &&
typeof chrome.storage !== "undefined" &&
typeof chrome.storage.local !== "undefined");
}
var hasLocal;
if (isChromeApp()) {
hasLocal = false;
} else {
try {
localStorage.setItem('_pouch_check_localstorage', 1);
hasLocal = !!localStorage.getItem('_pouch_check_localstorage');
} catch (e) {
hasLocal = false;
}
}
function hasLocalStorage() {
return hasLocal;
}
// Custom nextTick() shim for browsers. In node, this will just be process.nextTick(). We
// avoid using process.nextTick() directly because the polyfill is very large and we don't
// need all of it (see: https://github.com/defunctzombie/node-process).
// "immediate" 3.0.8 is used by lie, and it's a smaller version of the latest "immediate"
// package, so it's the one we use.
// When we use nextTick() in our codebase, we only care about not releasing Zalgo
// (see: http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony).
// Microtask vs macrotask doesn't matter to us. So we're free to use the fastest
// (least latency) option, which is "immediate" due to use of microtasks.
// All of our nextTicks are isolated to this one function so we can easily swap out one
// implementation for another.
inherits(Changes, events.EventEmitter);
/* istanbul ignore next */
function attachBrowserEvents(self) {
if (isChromeApp()) {
chrome.storage.onChanged.addListener(function (e) {
// make sure it's event addressed to us
if (e.db_name != null) {
//object only has oldValue, newValue members
self.emit(e.dbName.newValue);
}
});
} else if (hasLocalStorage()) {
if (typeof addEventListener !== 'undefined') {
addEventListener("storage", function (e) {
self.emit(e.key);
});
} else { // old IE
window.attachEvent("storage", function (e) {
self.emit(e.key);
});
}
}
}
function Changes() {
events.EventEmitter.call(this);
this._listeners = {};
attachBrowserEvents(this);
}
Changes.prototype.addListener = function (dbName, id, db, opts) {
/* istanbul ignore if */
if (this._listeners[id]) {
return;
}
var self = this;
var inprogress = false;
function eventFunction() {
/* istanbul ignore if */
if (!self._listeners[id]) {
return;
}
if (inprogress) {
inprogress = 'waiting';
return;
}
inprogress = true;
var changesOpts = pick(opts, [
'style', 'include_docs', 'attachments', 'conflicts', 'filter',
'doc_ids', 'view', 'since', 'query_params', 'binary'
]);
/* istanbul ignore next */
function onError() {
inprogress = false;
}
db.changes(changesOpts).on('change', function (c) {
if (c.seq > opts.since && !opts.cancelled) {
opts.since = c.seq;
opts.onChange(c);
}
}).on('complete', function () {
if (inprogress === 'waiting') {
immediate(eventFunction);
}
inprogress = false;
}).on('error', onError);
}
this._listeners[id] = eventFunction;
this.on(dbName, eventFunction);
};
Changes.prototype.removeListener = function (dbName, id) {
/* istanbul ignore if */
if (!(id in this._listeners)) {
return;
}
events.EventEmitter.prototype.removeListener.call(this, dbName,
this._listeners[id]);
delete this._listeners[id];
};
/* istanbul ignore next */
Changes.prototype.notifyLocalWindows = function (dbName) {
//do a useless change on a storage thing
//in order to get other windows's listeners to activate
if (isChromeApp()) {
chrome.storage.local.set({dbName: dbName});
} else if (hasLocalStorage()) {
localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a";
}
};
Changes.prototype.notify = function (dbName) {
this.emit(dbName);
this.notifyLocalWindows(dbName);
};
function guardedConsole(method) {
/* istanbul ignore else */
if (console !== 'undefined' && method in console) {
var args = Array.prototype.slice.call(arguments, 1);
console[method].apply(console, args);
}
}
function randomNumber(min, max) {
var maxTimeout = 600000; // Hard-coded default of 10 minutes
min = parseInt(min, 10) || 0;
max = parseInt(max, 10);
if (max !== max || max <= min) {
max = (min || 1) << 1; //doubling
} else {
max = max + 1;
}
// In order to not exceed maxTimeout, pick a random value between half of maxTimeout and maxTimeout
if (max > maxTimeout) {
min = maxTimeout >> 1; // divide by two
max = maxTimeout;
}
var ratio = Math.random();
var range = max - min;
return ~~(range * ratio + min); // ~~ coerces to an int, but fast.
}
function defaultBackOff(min) {
var max = 0;
if (!min) {
max = 2000;
}
return randomNumber(min, max);
}
// designed to give info to browser users, who are disturbed
// when they see http errors in the console
function explainError(status, str) {
guardedConsole('info', 'The above ' + status + ' is totally normal. ' + str);
}
var assign;
{
if (typeof Object.assign === 'function') {
assign = Object.assign;
} else {
// lite Object.assign polyfill based on
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
assign = function (target) {
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}
}
var assign$1 = assign;
function tryFilter(filter, doc, req) {
try {
return !filter(doc, req);
} catch (err) {
var msg = 'Filter function threw: ' + err.toString();
return pouchdbErrors.createError(pouchdbErrors.BAD_REQUEST, msg);
}
}
function filterChange(opts) {
var req = {};
var hasFilter = opts.filter && typeof opts.filter === 'function';
req.query = opts.query_params;
return function filter(change) {
if (!change.doc) {
// CSG sends events on the changes feed that don't have documents,
// this hack makes a whole lot of existing code robust.
change.doc = {};
}
var filterReturn = hasFilter && tryFilter(opts.filter, change.doc, req);
if (typeof filterReturn === 'object') {
return filterReturn;
}
if (filterReturn) {
return false;
}
if (!opts.include_docs) {
delete change.doc;
} else if (!opts.attachments) {
for (var att in change.doc._attachments) {
/* istanbul ignore else */
if (change.doc._attachments.hasOwnProperty(att)) {
change.doc._attachments[att].stub = true;
}
}
}
return true;
};
}
function flatten(arrs) {
var res = [];
for (var i = 0, len = arrs.length; i < len; i++) {
res = res.concat(arrs[i]);
}
return res;
}
// shim for Function.prototype.name,
// for browsers that don't support it like IE
/* istanbul ignore next */
function f() {}
var hasName = f.name;
var res;
// We dont run coverage in IE
/* istanbul ignore else */
if (hasName) {
res = function (fun) {
return fun.name;
};
} else {
res = function (fun) {
return fun.toString().match(/^\s*function\s*(\S*)\s*\(/)[1];
};
}
var res$1 = res;
// Determine id an ID is valid
// - invalid IDs begin with an underescore that does not begin '_design' or
// '_local'
// - any other string value is a valid id
// Returns the specific error object for each case
function invalidIdError(id) {
var err;
if (!id) {
err = pouchdbErrors.createError(pouchdbErrors.MISSING_ID);
} else if (typeof id !== 'string') {
err = pouchdbErrors.createError(pouchdbErrors.INVALID_ID);
} else if (/^_/.test(id) && !(/^_(design|local)/).test(id)) {
err = pouchdbErrors.createError(pouchdbErrors.RESERVED_ID);
}
if (err) {
throw err;
}
}
function isCordova() {
return (typeof cordova !== "undefined" ||
typeof PhoneGap !== "undefined" ||
typeof phonegap !== "undefined");
}
// Checks if a PouchDB object is "remote" or not. This is
// designed to opt-in to certain optimizations, such as
// avoiding checks for "dependentDbs" and other things that
// we know only apply to local databases. In general, "remote"
// should be true for the http adapter, and for third-party
// adapters with similar expensive boundaries to cross for
// every API call, such as socket-pouch and worker-pouch.
// Previously, this was handled via db.type() === 'http'
// which is now deprecated.
function isRemote(db) {
if (typeof db._remote === 'boolean') {
return db._remote;
}
/* istanbul ignore next */
if (typeof db.type === 'function') {
guardedConsole('warn',
'db.type() is deprecated and will be removed in ' +
'a future version of PouchDB');
return db.type() === 'http';
}
/* istanbul ignore next */
return false;
}
function listenerCount(ee, type) {
return 'listenerCount' in ee ? ee.listenerCount(type) :
events.EventEmitter.listenerCount(ee, type);
}
function parseDesignDocFunctionName(s) {
if (!s) {
return null;
}
var parts = s.split('/');
if (parts.length === 2) {
return parts;
}
if (parts.length === 1) {
return [s, s];
}
return null;
}
function normalizeDesignDocFunctionName(s) {
var normalized = parseDesignDocFunctionName(s);
return normalized ? normalized.join('/') : null;
}
// originally parseUri 1.2.2, now patched by us
// (c) Steven Levithan <stevenlevithan.com>
// MIT License
var keys = ["source", "protocol", "authority", "userInfo", "user", "password",
"host", "port", "relative", "path", "directory", "file", "query", "anchor"];
var qName ="queryKey";
var qParser = /(?:^|&)([^&=]*)=?([^&]*)/g;
// use the "loose" parser
/* jshint maxlen: false */
var parser = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
function parseUri(str) {
var m = parser.exec(str);
var uri = {};
var i = 14;
while (i--) {
var key = keys[i];
var value = m[i] || "";
var encoded = ['user', 'password'].indexOf(key) !== -1;
uri[key] = encoded ? decodeURIComponent(value) : value;
}
uri[qName] = {};
uri[keys[12]].replace(qParser, function ($0, $1, $2) {
if ($1) {
uri[qName][$1] = $2;
}
});
return uri;
}
// Based on https://github.com/alexdavid/scope-eval v0.0.3
// (source: https://unpkg.com/scope-eval@0.0.3/scope_eval.js)
// This is basically just a wrapper around new Function()
function scopeEval(source, scope) {
var keys = [];
var values = [];
for (var key in scope) {
if (scope.hasOwnProperty(key)) {
keys.push(key);
values.push(scope[key]);
}
}
keys.push(source);
return Function.apply(null, keys).apply(null, values);
}
// this is essentially the "update sugar" function from daleharvey/pouchdb#1388
// the diffFun tells us what delta to apply to the doc. it either returns
// the doc, or false if it doesn't need to do an update after all
function upsert(db, docId, diffFun) {
return new Promise(function (fulfill, reject) {
db.get(docId, function (err, doc) {
if (err) {
/* istanbul ignore next */
if (err.status !== 404) {
return reject(err);
}
doc = {};
}
// the user might change the _rev, so save it for posterity
var docRev = doc._rev;
var newDoc = diffFun(doc);
if (!newDoc) {
// if the diffFun returns falsy, we short-circuit as
// an optimization
return fulfill({updated: false, rev: docRev});
}
// users aren't allowed to modify these values,
// so reset them here
newDoc._id = docId;
newDoc._rev = docRev;
fulfill(tryAndPut(db, newDoc, diffFun));
});
});
}
function tryAndPut(db, doc, diffFun) {
return db.put(doc).then(function (res) {
return {
updated: true,
rev: res.rev
};
}, function (err) {
/* istanbul ignore next */
if (err.status !== 409) {
throw err;
}
return upsert(db, doc._id, diffFun);
});
}
// BEGIN Math.uuid.js
/*!
Math.uuid.js (v1.4)
http://www.broofa.com
mailto:robert@broofa.com
Copyright (c) 2010 Robert Kieffer
Dual licensed under the MIT and GPL licenses.
*/
/*
* Generate a random uuid.
*
* USAGE: Math.uuid(length, radix)
* length - the desired number of characters
* radix - the number of allowable values for each character.
*
* EXAMPLES:
* // No arguments - returns RFC4122, version 4 ID
* >>> Math.uuid()
* "92329D39-6F5C-4520-ABFC-AAB64544E172"
*
* // One argument - returns ID of the specified length
* >>> Math.uuid(15) // 15 character ID (default base=62)
* "VcydxgltxrVZSTV"
*
* // Two arguments - returns ID of the specified length, and radix.
* // (Radix must be <= 62)
* >>> Math.uuid(8, 2) // 8 character ID (base=2)
* "01001010"
* >>> Math.uuid(8, 10) // 8 character ID (base=10)
* "47473046"
* >>> Math.uuid(8, 16) // 8 character ID (base=16)
* "098F4D35"
*/
var chars = (
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz'
).split('');
function getValue(radix) {
return 0 | Math.random() * radix;
}
function uuid(len, radix) {
radix = radix || chars.length;
var out = '';
var i = -1;
if (len) {
// Compact form
while (++i < len) {
out += chars[getValue(radix)];
}
return out;
}
// rfc4122, version 4 form
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
while (++i < 36) {
switch (i) {
case 8:
case 13:
case 18:
case 23:
out += '-';
break;
case 19:
out += chars[(getValue(16) & 0x3) | 0x8];
break;
default:
out += chars[getValue(16)];
}
}
return out;
}
exports.adapterFun = adapterFun;
exports.bulkGetShim = bulkGet;
exports.changesHandler = Changes;
exports.clone = clone;
exports.defaultBackOff = defaultBackOff;
exports.explainError = explainError;
exports.assign = assign$1;
exports.filterChange = filterChange;
exports.flatten = flatten;
exports.functionName = res$1;
exports.guardedConsole = guardedConsole;
exports.hasLocalStorage = hasLocalStorage;
exports.invalidIdError = invalidIdError;
exports.isChromeApp = isChromeApp;
exports.isCordova = isCordova;
exports.isRemote = isRemote;
exports.listenerCount = listenerCount;
exports.nextTick = immediate;
exports.normalizeDdocFunctionName = normalizeDesignDocFunctionName;
exports.once = once;
exports.parseDdocFunctionName = parseDesignDocFunctionName;
exports.parseUri = parseUri;
exports.pick = pick;
exports.scopeEval = scopeEval;
exports.toPromise = toPromise;
exports.upsert = upsert;
exports.uuid = uuid;
},{"argsarray":4,"events":5,"immediate":6,"inherits":7,"pouchdb-collections":10,"pouchdb-errors":11,"pouchdb-promise":12}]},{},[2]);