UNPKG

166 kBJavaScriptView Raw
1// pouchdb-find plugin 7.2.1
2// Based on Mango: https://github.com/cloudant/mango
3//
4// (c) 2012-2020 Dale Harvey and the PouchDB team
5// PouchDB may be freely distributed under the Apache license, version 2.0.
6// For all details and documentation:
7// http://pouchdb.com
8(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
9'use strict';
10
11module.exports = argsArray;
12
13function argsArray(fun) {
14 return function () {
15 var len = arguments.length;
16 if (len) {
17 var args = [];
18 var i = -1;
19 while (++i < len) {
20 args[i] = arguments[i];
21 }
22 return fun.call(this, args);
23 } else {
24 return fun.call(this, []);
25 }
26 };
27}
28},{}],2:[function(_dereq_,module,exports){
29// Copyright Joyent, Inc. and other Node contributors.
30//
31// Permission is hereby granted, free of charge, to any person obtaining a
32// copy of this software and associated documentation files (the
33// "Software"), to deal in the Software without restriction, including
34// without limitation the rights to use, copy, modify, merge, publish,
35// distribute, sublicense, and/or sell copies of the Software, and to permit
36// persons to whom the Software is furnished to do so, subject to the
37// following conditions:
38//
39// The above copyright notice and this permission notice shall be included
40// in all copies or substantial portions of the Software.
41//
42// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
43// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
45// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
46// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
47// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
48// USE OR OTHER DEALINGS IN THE SOFTWARE.
49
50var objectCreate = Object.create || objectCreatePolyfill
51var objectKeys = Object.keys || objectKeysPolyfill
52var bind = Function.prototype.bind || functionBindPolyfill
53
54function EventEmitter() {
55 if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
56 this._events = objectCreate(null);
57 this._eventsCount = 0;
58 }
59
60 this._maxListeners = this._maxListeners || undefined;
61}
62module.exports = EventEmitter;
63
64// Backwards-compat with node 0.10.x
65EventEmitter.EventEmitter = EventEmitter;
66
67EventEmitter.prototype._events = undefined;
68EventEmitter.prototype._maxListeners = undefined;
69
70// By default EventEmitters will print a warning if more than 10 listeners are
71// added to it. This is a useful default which helps finding memory leaks.
72var defaultMaxListeners = 10;
73
74var hasDefineProperty;
75try {
76 var o = {};
77 if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
78 hasDefineProperty = o.x === 0;
79} catch (err) { hasDefineProperty = false }
80if (hasDefineProperty) {
81 Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
82 enumerable: true,
83 get: function() {
84 return defaultMaxListeners;
85 },
86 set: function(arg) {
87 // check whether the input is a positive number (whose value is zero or
88 // greater and not a NaN).
89 if (typeof arg !== 'number' || arg < 0 || arg !== arg)
90 throw new TypeError('"defaultMaxListeners" must be a positive number');
91 defaultMaxListeners = arg;
92 }
93 });
94} else {
95 EventEmitter.defaultMaxListeners = defaultMaxListeners;
96}
97
98// Obviously not all Emitters should be limited to 10. This function allows
99// that to be increased. Set to zero for unlimited.
100EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
101 if (typeof n !== 'number' || n < 0 || isNaN(n))
102 throw new TypeError('"n" argument must be a positive number');
103 this._maxListeners = n;
104 return this;
105};
106
107function $getMaxListeners(that) {
108 if (that._maxListeners === undefined)
109 return EventEmitter.defaultMaxListeners;
110 return that._maxListeners;
111}
112
113EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
114 return $getMaxListeners(this);
115};
116
117// These standalone emit* functions are used to optimize calling of event
118// handlers for fast cases because emit() itself often has a variable number of
119// arguments and can be deoptimized because of that. These functions always have
120// the same number of arguments and thus do not get deoptimized, so the code
121// inside them can execute faster.
122function emitNone(handler, isFn, self) {
123 if (isFn)
124 handler.call(self);
125 else {
126 var len = handler.length;
127 var listeners = arrayClone(handler, len);
128 for (var i = 0; i < len; ++i)
129 listeners[i].call(self);
130 }
131}
132function emitOne(handler, isFn, self, arg1) {
133 if (isFn)
134 handler.call(self, arg1);
135 else {
136 var len = handler.length;
137 var listeners = arrayClone(handler, len);
138 for (var i = 0; i < len; ++i)
139 listeners[i].call(self, arg1);
140 }
141}
142function emitTwo(handler, isFn, self, arg1, arg2) {
143 if (isFn)
144 handler.call(self, arg1, arg2);
145 else {
146 var len = handler.length;
147 var listeners = arrayClone(handler, len);
148 for (var i = 0; i < len; ++i)
149 listeners[i].call(self, arg1, arg2);
150 }
151}
152function emitThree(handler, isFn, self, arg1, arg2, arg3) {
153 if (isFn)
154 handler.call(self, arg1, arg2, arg3);
155 else {
156 var len = handler.length;
157 var listeners = arrayClone(handler, len);
158 for (var i = 0; i < len; ++i)
159 listeners[i].call(self, arg1, arg2, arg3);
160 }
161}
162
163function emitMany(handler, isFn, self, args) {
164 if (isFn)
165 handler.apply(self, args);
166 else {
167 var len = handler.length;
168 var listeners = arrayClone(handler, len);
169 for (var i = 0; i < len; ++i)
170 listeners[i].apply(self, args);
171 }
172}
173
174EventEmitter.prototype.emit = function emit(type) {
175 var er, handler, len, args, i, events;
176 var doError = (type === 'error');
177
178 events = this._events;
179 if (events)
180 doError = (doError && events.error == null);
181 else if (!doError)
182 return false;
183
184 // If there is no 'error' event listener then throw.
185 if (doError) {
186 if (arguments.length > 1)
187 er = arguments[1];
188 if (er instanceof Error) {
189 throw er; // Unhandled 'error' event
190 } else {
191 // At least give some kind of context to the user
192 var err = new Error('Unhandled "error" event. (' + er + ')');
193 err.context = er;
194 throw err;
195 }
196 return false;
197 }
198
199 handler = events[type];
200
201 if (!handler)
202 return false;
203
204 var isFn = typeof handler === 'function';
205 len = arguments.length;
206 switch (len) {
207 // fast cases
208 case 1:
209 emitNone(handler, isFn, this);
210 break;
211 case 2:
212 emitOne(handler, isFn, this, arguments[1]);
213 break;
214 case 3:
215 emitTwo(handler, isFn, this, arguments[1], arguments[2]);
216 break;
217 case 4:
218 emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
219 break;
220 // slower
221 default:
222 args = new Array(len - 1);
223 for (i = 1; i < len; i++)
224 args[i - 1] = arguments[i];
225 emitMany(handler, isFn, this, args);
226 }
227
228 return true;
229};
230
231function _addListener(target, type, listener, prepend) {
232 var m;
233 var events;
234 var existing;
235
236 if (typeof listener !== 'function')
237 throw new TypeError('"listener" argument must be a function');
238
239 events = target._events;
240 if (!events) {
241 events = target._events = objectCreate(null);
242 target._eventsCount = 0;
243 } else {
244 // To avoid recursion in the case that type === "newListener"! Before
245 // adding it to the listeners, first emit "newListener".
246 if (events.newListener) {
247 target.emit('newListener', type,
248 listener.listener ? listener.listener : listener);
249
250 // Re-assign `events` because a newListener handler could have caused the
251 // this._events to be assigned to a new object
252 events = target._events;
253 }
254 existing = events[type];
255 }
256
257 if (!existing) {
258 // Optimize the case of one listener. Don't need the extra array object.
259 existing = events[type] = listener;
260 ++target._eventsCount;
261 } else {
262 if (typeof existing === 'function') {
263 // Adding the second element, need to change to array.
264 existing = events[type] =
265 prepend ? [listener, existing] : [existing, listener];
266 } else {
267 // If we've already got an array, just append.
268 if (prepend) {
269 existing.unshift(listener);
270 } else {
271 existing.push(listener);
272 }
273 }
274
275 // Check for listener leak
276 if (!existing.warned) {
277 m = $getMaxListeners(target);
278 if (m && m > 0 && existing.length > m) {
279 existing.warned = true;
280 var w = new Error('Possible EventEmitter memory leak detected. ' +
281 existing.length + ' "' + String(type) + '" listeners ' +
282 'added. Use emitter.setMaxListeners() to ' +
283 'increase limit.');
284 w.name = 'MaxListenersExceededWarning';
285 w.emitter = target;
286 w.type = type;
287 w.count = existing.length;
288 if (typeof console === 'object' && console.warn) {
289 console.warn('%s: %s', w.name, w.message);
290 }
291 }
292 }
293 }
294
295 return target;
296}
297
298EventEmitter.prototype.addListener = function addListener(type, listener) {
299 return _addListener(this, type, listener, false);
300};
301
302EventEmitter.prototype.on = EventEmitter.prototype.addListener;
303
304EventEmitter.prototype.prependListener =
305 function prependListener(type, listener) {
306 return _addListener(this, type, listener, true);
307 };
308
309function onceWrapper() {
310 if (!this.fired) {
311 this.target.removeListener(this.type, this.wrapFn);
312 this.fired = true;
313 switch (arguments.length) {
314 case 0:
315 return this.listener.call(this.target);
316 case 1:
317 return this.listener.call(this.target, arguments[0]);
318 case 2:
319 return this.listener.call(this.target, arguments[0], arguments[1]);
320 case 3:
321 return this.listener.call(this.target, arguments[0], arguments[1],
322 arguments[2]);
323 default:
324 var args = new Array(arguments.length);
325 for (var i = 0; i < args.length; ++i)
326 args[i] = arguments[i];
327 this.listener.apply(this.target, args);
328 }
329 }
330}
331
332function _onceWrap(target, type, listener) {
333 var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
334 var wrapped = bind.call(onceWrapper, state);
335 wrapped.listener = listener;
336 state.wrapFn = wrapped;
337 return wrapped;
338}
339
340EventEmitter.prototype.once = function once(type, listener) {
341 if (typeof listener !== 'function')
342 throw new TypeError('"listener" argument must be a function');
343 this.on(type, _onceWrap(this, type, listener));
344 return this;
345};
346
347EventEmitter.prototype.prependOnceListener =
348 function prependOnceListener(type, listener) {
349 if (typeof listener !== 'function')
350 throw new TypeError('"listener" argument must be a function');
351 this.prependListener(type, _onceWrap(this, type, listener));
352 return this;
353 };
354
355// Emits a 'removeListener' event if and only if the listener was removed.
356EventEmitter.prototype.removeListener =
357 function removeListener(type, listener) {
358 var list, events, position, i, originalListener;
359
360 if (typeof listener !== 'function')
361 throw new TypeError('"listener" argument must be a function');
362
363 events = this._events;
364 if (!events)
365 return this;
366
367 list = events[type];
368 if (!list)
369 return this;
370
371 if (list === listener || list.listener === listener) {
372 if (--this._eventsCount === 0)
373 this._events = objectCreate(null);
374 else {
375 delete events[type];
376 if (events.removeListener)
377 this.emit('removeListener', type, list.listener || listener);
378 }
379 } else if (typeof list !== 'function') {
380 position = -1;
381
382 for (i = list.length - 1; i >= 0; i--) {
383 if (list[i] === listener || list[i].listener === listener) {
384 originalListener = list[i].listener;
385 position = i;
386 break;
387 }
388 }
389
390 if (position < 0)
391 return this;
392
393 if (position === 0)
394 list.shift();
395 else
396 spliceOne(list, position);
397
398 if (list.length === 1)
399 events[type] = list[0];
400
401 if (events.removeListener)
402 this.emit('removeListener', type, originalListener || listener);
403 }
404
405 return this;
406 };
407
408EventEmitter.prototype.removeAllListeners =
409 function removeAllListeners(type) {
410 var listeners, events, i;
411
412 events = this._events;
413 if (!events)
414 return this;
415
416 // not listening for removeListener, no need to emit
417 if (!events.removeListener) {
418 if (arguments.length === 0) {
419 this._events = objectCreate(null);
420 this._eventsCount = 0;
421 } else if (events[type]) {
422 if (--this._eventsCount === 0)
423 this._events = objectCreate(null);
424 else
425 delete events[type];
426 }
427 return this;
428 }
429
430 // emit removeListener for all listeners on all events
431 if (arguments.length === 0) {
432 var keys = objectKeys(events);
433 var key;
434 for (i = 0; i < keys.length; ++i) {
435 key = keys[i];
436 if (key === 'removeListener') continue;
437 this.removeAllListeners(key);
438 }
439 this.removeAllListeners('removeListener');
440 this._events = objectCreate(null);
441 this._eventsCount = 0;
442 return this;
443 }
444
445 listeners = events[type];
446
447 if (typeof listeners === 'function') {
448 this.removeListener(type, listeners);
449 } else if (listeners) {
450 // LIFO order
451 for (i = listeners.length - 1; i >= 0; i--) {
452 this.removeListener(type, listeners[i]);
453 }
454 }
455
456 return this;
457 };
458
459function _listeners(target, type, unwrap) {
460 var events = target._events;
461
462 if (!events)
463 return [];
464
465 var evlistener = events[type];
466 if (!evlistener)
467 return [];
468
469 if (typeof evlistener === 'function')
470 return unwrap ? [evlistener.listener || evlistener] : [evlistener];
471
472 return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
473}
474
475EventEmitter.prototype.listeners = function listeners(type) {
476 return _listeners(this, type, true);
477};
478
479EventEmitter.prototype.rawListeners = function rawListeners(type) {
480 return _listeners(this, type, false);
481};
482
483EventEmitter.listenerCount = function(emitter, type) {
484 if (typeof emitter.listenerCount === 'function') {
485 return emitter.listenerCount(type);
486 } else {
487 return listenerCount.call(emitter, type);
488 }
489};
490
491EventEmitter.prototype.listenerCount = listenerCount;
492function listenerCount(type) {
493 var events = this._events;
494
495 if (events) {
496 var evlistener = events[type];
497
498 if (typeof evlistener === 'function') {
499 return 1;
500 } else if (evlistener) {
501 return evlistener.length;
502 }
503 }
504
505 return 0;
506}
507
508EventEmitter.prototype.eventNames = function eventNames() {
509 return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
510};
511
512// About 1.5x faster than the two-arg version of Array#splice().
513function spliceOne(list, index) {
514 for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
515 list[i] = list[k];
516 list.pop();
517}
518
519function arrayClone(arr, n) {
520 var copy = new Array(n);
521 for (var i = 0; i < n; ++i)
522 copy[i] = arr[i];
523 return copy;
524}
525
526function unwrapListeners(arr) {
527 var ret = new Array(arr.length);
528 for (var i = 0; i < ret.length; ++i) {
529 ret[i] = arr[i].listener || arr[i];
530 }
531 return ret;
532}
533
534function objectCreatePolyfill(proto) {
535 var F = function() {};
536 F.prototype = proto;
537 return new F;
538}
539function objectKeysPolyfill(obj) {
540 var keys = [];
541 for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
542 keys.push(k);
543 }
544 return k;
545}
546function functionBindPolyfill(context) {
547 var fn = this;
548 return function () {
549 return fn.apply(context, arguments);
550 };
551}
552
553},{}],3:[function(_dereq_,module,exports){
554(function (global){
555'use strict';
556var Mutation = global.MutationObserver || global.WebKitMutationObserver;
557
558var scheduleDrain;
559
560{
561 if (Mutation) {
562 var called = 0;
563 var observer = new Mutation(nextTick);
564 var element = global.document.createTextNode('');
565 observer.observe(element, {
566 characterData: true
567 });
568 scheduleDrain = function () {
569 element.data = (called = ++called % 2);
570 };
571 } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
572 var channel = new global.MessageChannel();
573 channel.port1.onmessage = nextTick;
574 scheduleDrain = function () {
575 channel.port2.postMessage(0);
576 };
577 } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
578 scheduleDrain = function () {
579
580 // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
581 // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
582 var scriptEl = global.document.createElement('script');
583 scriptEl.onreadystatechange = function () {
584 nextTick();
585
586 scriptEl.onreadystatechange = null;
587 scriptEl.parentNode.removeChild(scriptEl);
588 scriptEl = null;
589 };
590 global.document.documentElement.appendChild(scriptEl);
591 };
592 } else {
593 scheduleDrain = function () {
594 setTimeout(nextTick, 0);
595 };
596 }
597}
598
599var draining;
600var queue = [];
601//named nextTick for less confusing stack traces
602function nextTick() {
603 draining = true;
604 var i, oldQueue;
605 var len = queue.length;
606 while (len) {
607 oldQueue = queue;
608 queue = [];
609 i = -1;
610 while (++i < len) {
611 oldQueue[i]();
612 }
613 len = queue.length;
614 }
615 draining = false;
616}
617
618module.exports = immediate;
619function immediate(task) {
620 if (queue.push(task) === 1 && !draining) {
621 scheduleDrain();
622 }
623}
624
625}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
626},{}],4:[function(_dereq_,module,exports){
627if (typeof Object.create === 'function') {
628 // implementation from standard node.js 'util' module
629 module.exports = function inherits(ctor, superCtor) {
630 if (superCtor) {
631 ctor.super_ = superCtor
632 ctor.prototype = Object.create(superCtor.prototype, {
633 constructor: {
634 value: ctor,
635 enumerable: false,
636 writable: true,
637 configurable: true
638 }
639 })
640 }
641 };
642} else {
643 // old school shim for old browsers
644 module.exports = function inherits(ctor, superCtor) {
645 if (superCtor) {
646 ctor.super_ = superCtor
647 var TempCtor = function () {}
648 TempCtor.prototype = superCtor.prototype
649 ctor.prototype = new TempCtor()
650 ctor.prototype.constructor = ctor
651 }
652 }
653}
654
655},{}],5:[function(_dereq_,module,exports){
656(function (factory) {
657 if (typeof exports === 'object') {
658 // Node/CommonJS
659 module.exports = factory();
660 } else if (typeof define === 'function' && define.amd) {
661 // AMD
662 define(factory);
663 } else {
664 // Browser globals (with support for web workers)
665 var glob;
666
667 try {
668 glob = window;
669 } catch (e) {
670 glob = self;
671 }
672
673 glob.SparkMD5 = factory();
674 }
675}(function (undefined) {
676
677 'use strict';
678
679 /*
680 * Fastest md5 implementation around (JKM md5).
681 * Credits: Joseph Myers
682 *
683 * @see http://www.myersdaily.org/joseph/javascript/md5-text.html
684 * @see http://jsperf.com/md5-shootout/7
685 */
686
687 /* this function is much faster,
688 so if possible we use it. Some IEs
689 are the only ones I know of that
690 need the idiotic second function,
691 generated by an if clause. */
692 var add32 = function (a, b) {
693 return (a + b) & 0xFFFFFFFF;
694 },
695 hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
696
697
698 function cmn(q, a, b, x, s, t) {
699 a = add32(add32(a, q), add32(x, t));
700 return add32((a << s) | (a >>> (32 - s)), b);
701 }
702
703 function md5cycle(x, k) {
704 var a = x[0],
705 b = x[1],
706 c = x[2],
707 d = x[3];
708
709 a += (b & c | ~b & d) + k[0] - 680876936 | 0;
710 a = (a << 7 | a >>> 25) + b | 0;
711 d += (a & b | ~a & c) + k[1] - 389564586 | 0;
712 d = (d << 12 | d >>> 20) + a | 0;
713 c += (d & a | ~d & b) + k[2] + 606105819 | 0;
714 c = (c << 17 | c >>> 15) + d | 0;
715 b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
716 b = (b << 22 | b >>> 10) + c | 0;
717 a += (b & c | ~b & d) + k[4] - 176418897 | 0;
718 a = (a << 7 | a >>> 25) + b | 0;
719 d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
720 d = (d << 12 | d >>> 20) + a | 0;
721 c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
722 c = (c << 17 | c >>> 15) + d | 0;
723 b += (c & d | ~c & a) + k[7] - 45705983 | 0;
724 b = (b << 22 | b >>> 10) + c | 0;
725 a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
726 a = (a << 7 | a >>> 25) + b | 0;
727 d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
728 d = (d << 12 | d >>> 20) + a | 0;
729 c += (d & a | ~d & b) + k[10] - 42063 | 0;
730 c = (c << 17 | c >>> 15) + d | 0;
731 b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
732 b = (b << 22 | b >>> 10) + c | 0;
733 a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
734 a = (a << 7 | a >>> 25) + b | 0;
735 d += (a & b | ~a & c) + k[13] - 40341101 | 0;
736 d = (d << 12 | d >>> 20) + a | 0;
737 c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
738 c = (c << 17 | c >>> 15) + d | 0;
739 b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
740 b = (b << 22 | b >>> 10) + c | 0;
741
742 a += (b & d | c & ~d) + k[1] - 165796510 | 0;
743 a = (a << 5 | a >>> 27) + b | 0;
744 d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
745 d = (d << 9 | d >>> 23) + a | 0;
746 c += (d & b | a & ~b) + k[11] + 643717713 | 0;
747 c = (c << 14 | c >>> 18) + d | 0;
748 b += (c & a | d & ~a) + k[0] - 373897302 | 0;
749 b = (b << 20 | b >>> 12) + c | 0;
750 a += (b & d | c & ~d) + k[5] - 701558691 | 0;
751 a = (a << 5 | a >>> 27) + b | 0;
752 d += (a & c | b & ~c) + k[10] + 38016083 | 0;
753 d = (d << 9 | d >>> 23) + a | 0;
754 c += (d & b | a & ~b) + k[15] - 660478335 | 0;
755 c = (c << 14 | c >>> 18) + d | 0;
756 b += (c & a | d & ~a) + k[4] - 405537848 | 0;
757 b = (b << 20 | b >>> 12) + c | 0;
758 a += (b & d | c & ~d) + k[9] + 568446438 | 0;
759 a = (a << 5 | a >>> 27) + b | 0;
760 d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
761 d = (d << 9 | d >>> 23) + a | 0;
762 c += (d & b | a & ~b) + k[3] - 187363961 | 0;
763 c = (c << 14 | c >>> 18) + d | 0;
764 b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
765 b = (b << 20 | b >>> 12) + c | 0;
766 a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
767 a = (a << 5 | a >>> 27) + b | 0;
768 d += (a & c | b & ~c) + k[2] - 51403784 | 0;
769 d = (d << 9 | d >>> 23) + a | 0;
770 c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
771 c = (c << 14 | c >>> 18) + d | 0;
772 b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
773 b = (b << 20 | b >>> 12) + c | 0;
774
775 a += (b ^ c ^ d) + k[5] - 378558 | 0;
776 a = (a << 4 | a >>> 28) + b | 0;
777 d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
778 d = (d << 11 | d >>> 21) + a | 0;
779 c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
780 c = (c << 16 | c >>> 16) + d | 0;
781 b += (c ^ d ^ a) + k[14] - 35309556 | 0;
782 b = (b << 23 | b >>> 9) + c | 0;
783 a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
784 a = (a << 4 | a >>> 28) + b | 0;
785 d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
786 d = (d << 11 | d >>> 21) + a | 0;
787 c += (d ^ a ^ b) + k[7] - 155497632 | 0;
788 c = (c << 16 | c >>> 16) + d | 0;
789 b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
790 b = (b << 23 | b >>> 9) + c | 0;
791 a += (b ^ c ^ d) + k[13] + 681279174 | 0;
792 a = (a << 4 | a >>> 28) + b | 0;
793 d += (a ^ b ^ c) + k[0] - 358537222 | 0;
794 d = (d << 11 | d >>> 21) + a | 0;
795 c += (d ^ a ^ b) + k[3] - 722521979 | 0;
796 c = (c << 16 | c >>> 16) + d | 0;
797 b += (c ^ d ^ a) + k[6] + 76029189 | 0;
798 b = (b << 23 | b >>> 9) + c | 0;
799 a += (b ^ c ^ d) + k[9] - 640364487 | 0;
800 a = (a << 4 | a >>> 28) + b | 0;
801 d += (a ^ b ^ c) + k[12] - 421815835 | 0;
802 d = (d << 11 | d >>> 21) + a | 0;
803 c += (d ^ a ^ b) + k[15] + 530742520 | 0;
804 c = (c << 16 | c >>> 16) + d | 0;
805 b += (c ^ d ^ a) + k[2] - 995338651 | 0;
806 b = (b << 23 | b >>> 9) + c | 0;
807
808 a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
809 a = (a << 6 | a >>> 26) + b | 0;
810 d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
811 d = (d << 10 | d >>> 22) + a | 0;
812 c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
813 c = (c << 15 | c >>> 17) + d | 0;
814 b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
815 b = (b << 21 |b >>> 11) + c | 0;
816 a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
817 a = (a << 6 | a >>> 26) + b | 0;
818 d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
819 d = (d << 10 | d >>> 22) + a | 0;
820 c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
821 c = (c << 15 | c >>> 17) + d | 0;
822 b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
823 b = (b << 21 |b >>> 11) + c | 0;
824 a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
825 a = (a << 6 | a >>> 26) + b | 0;
826 d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
827 d = (d << 10 | d >>> 22) + a | 0;
828 c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
829 c = (c << 15 | c >>> 17) + d | 0;
830 b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
831 b = (b << 21 |b >>> 11) + c | 0;
832 a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
833 a = (a << 6 | a >>> 26) + b | 0;
834 d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
835 d = (d << 10 | d >>> 22) + a | 0;
836 c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
837 c = (c << 15 | c >>> 17) + d | 0;
838 b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
839 b = (b << 21 | b >>> 11) + c | 0;
840
841 x[0] = a + x[0] | 0;
842 x[1] = b + x[1] | 0;
843 x[2] = c + x[2] | 0;
844 x[3] = d + x[3] | 0;
845 }
846
847 function md5blk(s) {
848 var md5blks = [],
849 i; /* Andy King said do it this way. */
850
851 for (i = 0; i < 64; i += 4) {
852 md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
853 }
854 return md5blks;
855 }
856
857 function md5blk_array(a) {
858 var md5blks = [],
859 i; /* Andy King said do it this way. */
860
861 for (i = 0; i < 64; i += 4) {
862 md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
863 }
864 return md5blks;
865 }
866
867 function md51(s) {
868 var n = s.length,
869 state = [1732584193, -271733879, -1732584194, 271733878],
870 i,
871 length,
872 tail,
873 tmp,
874 lo,
875 hi;
876
877 for (i = 64; i <= n; i += 64) {
878 md5cycle(state, md5blk(s.substring(i - 64, i)));
879 }
880 s = s.substring(i - 64);
881 length = s.length;
882 tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
883 for (i = 0; i < length; i += 1) {
884 tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
885 }
886 tail[i >> 2] |= 0x80 << ((i % 4) << 3);
887 if (i > 55) {
888 md5cycle(state, tail);
889 for (i = 0; i < 16; i += 1) {
890 tail[i] = 0;
891 }
892 }
893
894 // Beware that the final length might not fit in 32 bits so we take care of that
895 tmp = n * 8;
896 tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
897 lo = parseInt(tmp[2], 16);
898 hi = parseInt(tmp[1], 16) || 0;
899
900 tail[14] = lo;
901 tail[15] = hi;
902
903 md5cycle(state, tail);
904 return state;
905 }
906
907 function md51_array(a) {
908 var n = a.length,
909 state = [1732584193, -271733879, -1732584194, 271733878],
910 i,
911 length,
912 tail,
913 tmp,
914 lo,
915 hi;
916
917 for (i = 64; i <= n; i += 64) {
918 md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
919 }
920
921 // Not sure if it is a bug, however IE10 will always produce a sub array of length 1
922 // containing the last element of the parent array if the sub array specified starts
923 // beyond the length of the parent array - weird.
924 // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
925 a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);
926
927 length = a.length;
928 tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
929 for (i = 0; i < length; i += 1) {
930 tail[i >> 2] |= a[i] << ((i % 4) << 3);
931 }
932
933 tail[i >> 2] |= 0x80 << ((i % 4) << 3);
934 if (i > 55) {
935 md5cycle(state, tail);
936 for (i = 0; i < 16; i += 1) {
937 tail[i] = 0;
938 }
939 }
940
941 // Beware that the final length might not fit in 32 bits so we take care of that
942 tmp = n * 8;
943 tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
944 lo = parseInt(tmp[2], 16);
945 hi = parseInt(tmp[1], 16) || 0;
946
947 tail[14] = lo;
948 tail[15] = hi;
949
950 md5cycle(state, tail);
951
952 return state;
953 }
954
955 function rhex(n) {
956 var s = '',
957 j;
958 for (j = 0; j < 4; j += 1) {
959 s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
960 }
961 return s;
962 }
963
964 function hex(x) {
965 var i;
966 for (i = 0; i < x.length; i += 1) {
967 x[i] = rhex(x[i]);
968 }
969 return x.join('');
970 }
971
972 // In some cases the fast add32 function cannot be used..
973 if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {
974 add32 = function (x, y) {
975 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
976 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
977 return (msw << 16) | (lsw & 0xFFFF);
978 };
979 }
980
981 // ---------------------------------------------------
982
983 /**
984 * ArrayBuffer slice polyfill.
985 *
986 * @see https://github.com/ttaubert/node-arraybuffer-slice
987 */
988
989 if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
990 (function () {
991 function clamp(val, length) {
992 val = (val | 0) || 0;
993
994 if (val < 0) {
995 return Math.max(val + length, 0);
996 }
997
998 return Math.min(val, length);
999 }
1000
1001 ArrayBuffer.prototype.slice = function (from, to) {
1002 var length = this.byteLength,
1003 begin = clamp(from, length),
1004 end = length,
1005 num,
1006 target,
1007 targetArray,
1008 sourceArray;
1009
1010 if (to !== undefined) {
1011 end = clamp(to, length);
1012 }
1013
1014 if (begin > end) {
1015 return new ArrayBuffer(0);
1016 }
1017
1018 num = end - begin;
1019 target = new ArrayBuffer(num);
1020 targetArray = new Uint8Array(target);
1021
1022 sourceArray = new Uint8Array(this, begin, num);
1023 targetArray.set(sourceArray);
1024
1025 return target;
1026 };
1027 })();
1028 }
1029
1030 // ---------------------------------------------------
1031
1032 /**
1033 * Helpers.
1034 */
1035
1036 function toUtf8(str) {
1037 if (/[\u0080-\uFFFF]/.test(str)) {
1038 str = unescape(encodeURIComponent(str));
1039 }
1040
1041 return str;
1042 }
1043
1044 function utf8Str2ArrayBuffer(str, returnUInt8Array) {
1045 var length = str.length,
1046 buff = new ArrayBuffer(length),
1047 arr = new Uint8Array(buff),
1048 i;
1049
1050 for (i = 0; i < length; i += 1) {
1051 arr[i] = str.charCodeAt(i);
1052 }
1053
1054 return returnUInt8Array ? arr : buff;
1055 }
1056
1057 function arrayBuffer2Utf8Str(buff) {
1058 return String.fromCharCode.apply(null, new Uint8Array(buff));
1059 }
1060
1061 function concatenateArrayBuffers(first, second, returnUInt8Array) {
1062 var result = new Uint8Array(first.byteLength + second.byteLength);
1063
1064 result.set(new Uint8Array(first));
1065 result.set(new Uint8Array(second), first.byteLength);
1066
1067 return returnUInt8Array ? result : result.buffer;
1068 }
1069
1070 function hexToBinaryString(hex) {
1071 var bytes = [],
1072 length = hex.length,
1073 x;
1074
1075 for (x = 0; x < length - 1; x += 2) {
1076 bytes.push(parseInt(hex.substr(x, 2), 16));
1077 }
1078
1079 return String.fromCharCode.apply(String, bytes);
1080 }
1081
1082 // ---------------------------------------------------
1083
1084 /**
1085 * SparkMD5 OOP implementation.
1086 *
1087 * Use this class to perform an incremental md5, otherwise use the
1088 * static methods instead.
1089 */
1090
1091 function SparkMD5() {
1092 // call reset to init the instance
1093 this.reset();
1094 }
1095
1096 /**
1097 * Appends a string.
1098 * A conversion will be applied if an utf8 string is detected.
1099 *
1100 * @param {String} str The string to be appended
1101 *
1102 * @return {SparkMD5} The instance itself
1103 */
1104 SparkMD5.prototype.append = function (str) {
1105 // Converts the string to utf8 bytes if necessary
1106 // Then append as binary
1107 this.appendBinary(toUtf8(str));
1108
1109 return this;
1110 };
1111
1112 /**
1113 * Appends a binary string.
1114 *
1115 * @param {String} contents The binary string to be appended
1116 *
1117 * @return {SparkMD5} The instance itself
1118 */
1119 SparkMD5.prototype.appendBinary = function (contents) {
1120 this._buff += contents;
1121 this._length += contents.length;
1122
1123 var length = this._buff.length,
1124 i;
1125
1126 for (i = 64; i <= length; i += 64) {
1127 md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
1128 }
1129
1130 this._buff = this._buff.substring(i - 64);
1131
1132 return this;
1133 };
1134
1135 /**
1136 * Finishes the incremental computation, reseting the internal state and
1137 * returning the result.
1138 *
1139 * @param {Boolean} raw True to get the raw string, false to get the hex string
1140 *
1141 * @return {String} The result
1142 */
1143 SparkMD5.prototype.end = function (raw) {
1144 var buff = this._buff,
1145 length = buff.length,
1146 i,
1147 tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1148 ret;
1149
1150 for (i = 0; i < length; i += 1) {
1151 tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
1152 }
1153
1154 this._finish(tail, length);
1155 ret = hex(this._hash);
1156
1157 if (raw) {
1158 ret = hexToBinaryString(ret);
1159 }
1160
1161 this.reset();
1162
1163 return ret;
1164 };
1165
1166 /**
1167 * Resets the internal state of the computation.
1168 *
1169 * @return {SparkMD5} The instance itself
1170 */
1171 SparkMD5.prototype.reset = function () {
1172 this._buff = '';
1173 this._length = 0;
1174 this._hash = [1732584193, -271733879, -1732584194, 271733878];
1175
1176 return this;
1177 };
1178
1179 /**
1180 * Gets the internal state of the computation.
1181 *
1182 * @return {Object} The state
1183 */
1184 SparkMD5.prototype.getState = function () {
1185 return {
1186 buff: this._buff,
1187 length: this._length,
1188 hash: this._hash
1189 };
1190 };
1191
1192 /**
1193 * Gets the internal state of the computation.
1194 *
1195 * @param {Object} state The state
1196 *
1197 * @return {SparkMD5} The instance itself
1198 */
1199 SparkMD5.prototype.setState = function (state) {
1200 this._buff = state.buff;
1201 this._length = state.length;
1202 this._hash = state.hash;
1203
1204 return this;
1205 };
1206
1207 /**
1208 * Releases memory used by the incremental buffer and other additional
1209 * resources. If you plan to use the instance again, use reset instead.
1210 */
1211 SparkMD5.prototype.destroy = function () {
1212 delete this._hash;
1213 delete this._buff;
1214 delete this._length;
1215 };
1216
1217 /**
1218 * Finish the final calculation based on the tail.
1219 *
1220 * @param {Array} tail The tail (will be modified)
1221 * @param {Number} length The length of the remaining buffer
1222 */
1223 SparkMD5.prototype._finish = function (tail, length) {
1224 var i = length,
1225 tmp,
1226 lo,
1227 hi;
1228
1229 tail[i >> 2] |= 0x80 << ((i % 4) << 3);
1230 if (i > 55) {
1231 md5cycle(this._hash, tail);
1232 for (i = 0; i < 16; i += 1) {
1233 tail[i] = 0;
1234 }
1235 }
1236
1237 // Do the final computation based on the tail and length
1238 // Beware that the final length may not fit in 32 bits so we take care of that
1239 tmp = this._length * 8;
1240 tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
1241 lo = parseInt(tmp[2], 16);
1242 hi = parseInt(tmp[1], 16) || 0;
1243
1244 tail[14] = lo;
1245 tail[15] = hi;
1246 md5cycle(this._hash, tail);
1247 };
1248
1249 /**
1250 * Performs the md5 hash on a string.
1251 * A conversion will be applied if utf8 string is detected.
1252 *
1253 * @param {String} str The string
1254 * @param {Boolean} raw True to get the raw string, false to get the hex string
1255 *
1256 * @return {String} The result
1257 */
1258 SparkMD5.hash = function (str, raw) {
1259 // Converts the string to utf8 bytes if necessary
1260 // Then compute it using the binary function
1261 return SparkMD5.hashBinary(toUtf8(str), raw);
1262 };
1263
1264 /**
1265 * Performs the md5 hash on a binary string.
1266 *
1267 * @param {String} content The binary string
1268 * @param {Boolean} raw True to get the raw string, false to get the hex string
1269 *
1270 * @return {String} The result
1271 */
1272 SparkMD5.hashBinary = function (content, raw) {
1273 var hash = md51(content),
1274 ret = hex(hash);
1275
1276 return raw ? hexToBinaryString(ret) : ret;
1277 };
1278
1279 // ---------------------------------------------------
1280
1281 /**
1282 * SparkMD5 OOP implementation for array buffers.
1283 *
1284 * Use this class to perform an incremental md5 ONLY for array buffers.
1285 */
1286 SparkMD5.ArrayBuffer = function () {
1287 // call reset to init the instance
1288 this.reset();
1289 };
1290
1291 /**
1292 * Appends an array buffer.
1293 *
1294 * @param {ArrayBuffer} arr The array to be appended
1295 *
1296 * @return {SparkMD5.ArrayBuffer} The instance itself
1297 */
1298 SparkMD5.ArrayBuffer.prototype.append = function (arr) {
1299 var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
1300 length = buff.length,
1301 i;
1302
1303 this._length += arr.byteLength;
1304
1305 for (i = 64; i <= length; i += 64) {
1306 md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
1307 }
1308
1309 this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
1310
1311 return this;
1312 };
1313
1314 /**
1315 * Finishes the incremental computation, reseting the internal state and
1316 * returning the result.
1317 *
1318 * @param {Boolean} raw True to get the raw string, false to get the hex string
1319 *
1320 * @return {String} The result
1321 */
1322 SparkMD5.ArrayBuffer.prototype.end = function (raw) {
1323 var buff = this._buff,
1324 length = buff.length,
1325 tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1326 i,
1327 ret;
1328
1329 for (i = 0; i < length; i += 1) {
1330 tail[i >> 2] |= buff[i] << ((i % 4) << 3);
1331 }
1332
1333 this._finish(tail, length);
1334 ret = hex(this._hash);
1335
1336 if (raw) {
1337 ret = hexToBinaryString(ret);
1338 }
1339
1340 this.reset();
1341
1342 return ret;
1343 };
1344
1345 /**
1346 * Resets the internal state of the computation.
1347 *
1348 * @return {SparkMD5.ArrayBuffer} The instance itself
1349 */
1350 SparkMD5.ArrayBuffer.prototype.reset = function () {
1351 this._buff = new Uint8Array(0);
1352 this._length = 0;
1353 this._hash = [1732584193, -271733879, -1732584194, 271733878];
1354
1355 return this;
1356 };
1357
1358 /**
1359 * Gets the internal state of the computation.
1360 *
1361 * @return {Object} The state
1362 */
1363 SparkMD5.ArrayBuffer.prototype.getState = function () {
1364 var state = SparkMD5.prototype.getState.call(this);
1365
1366 // Convert buffer to a string
1367 state.buff = arrayBuffer2Utf8Str(state.buff);
1368
1369 return state;
1370 };
1371
1372 /**
1373 * Gets the internal state of the computation.
1374 *
1375 * @param {Object} state The state
1376 *
1377 * @return {SparkMD5.ArrayBuffer} The instance itself
1378 */
1379 SparkMD5.ArrayBuffer.prototype.setState = function (state) {
1380 // Convert string to buffer
1381 state.buff = utf8Str2ArrayBuffer(state.buff, true);
1382
1383 return SparkMD5.prototype.setState.call(this, state);
1384 };
1385
1386 SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
1387
1388 SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
1389
1390 /**
1391 * Performs the md5 hash on an array buffer.
1392 *
1393 * @param {ArrayBuffer} arr The array buffer
1394 * @param {Boolean} raw True to get the raw string, false to get the hex one
1395 *
1396 * @return {String} The result
1397 */
1398 SparkMD5.ArrayBuffer.hash = function (arr, raw) {
1399 var hash = md51_array(new Uint8Array(arr)),
1400 ret = hex(hash);
1401
1402 return raw ? hexToBinaryString(ret) : ret;
1403 };
1404
1405 return SparkMD5;
1406}));
1407
1408},{}],6:[function(_dereq_,module,exports){
1409var v1 = _dereq_(9);
1410var v4 = _dereq_(10);
1411
1412var uuid = v4;
1413uuid.v1 = v1;
1414uuid.v4 = v4;
1415
1416module.exports = uuid;
1417
1418},{"10":10,"9":9}],7:[function(_dereq_,module,exports){
1419/**
1420 * Convert array of 16 byte values to UUID string format of the form:
1421 * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
1422 */
1423var byteToHex = [];
1424for (var i = 0; i < 256; ++i) {
1425 byteToHex[i] = (i + 0x100).toString(16).substr(1);
1426}
1427
1428function bytesToUuid(buf, offset) {
1429 var i = offset || 0;
1430 var bth = byteToHex;
1431 // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
1432 return ([bth[buf[i++]], bth[buf[i++]],
1433 bth[buf[i++]], bth[buf[i++]], '-',
1434 bth[buf[i++]], bth[buf[i++]], '-',
1435 bth[buf[i++]], bth[buf[i++]], '-',
1436 bth[buf[i++]], bth[buf[i++]], '-',
1437 bth[buf[i++]], bth[buf[i++]],
1438 bth[buf[i++]], bth[buf[i++]],
1439 bth[buf[i++]], bth[buf[i++]]]).join('');
1440}
1441
1442module.exports = bytesToUuid;
1443
1444},{}],8:[function(_dereq_,module,exports){
1445// Unique ID creation requires a high quality random # generator. In the
1446// browser this is a little complicated due to unknown quality of Math.random()
1447// and inconsistent support for the `crypto` API. We do the best we can via
1448// feature-detection
1449
1450// getRandomValues needs to be invoked in a context where "this" is a Crypto
1451// implementation. Also, find the complete implementation of crypto on IE11.
1452var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) ||
1453 (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto));
1454
1455if (getRandomValues) {
1456 // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
1457 var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef
1458
1459 module.exports = function whatwgRNG() {
1460 getRandomValues(rnds8);
1461 return rnds8;
1462 };
1463} else {
1464 // Math.random()-based (RNG)
1465 //
1466 // If all else fails, use Math.random(). It's fast, but is of unspecified
1467 // quality.
1468 var rnds = new Array(16);
1469
1470 module.exports = function mathRNG() {
1471 for (var i = 0, r; i < 16; i++) {
1472 if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
1473 rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
1474 }
1475
1476 return rnds;
1477 };
1478}
1479
1480},{}],9:[function(_dereq_,module,exports){
1481var rng = _dereq_(8);
1482var bytesToUuid = _dereq_(7);
1483
1484// **`v1()` - Generate time-based UUID**
1485//
1486// Inspired by https://github.com/LiosK/UUID.js
1487// and http://docs.python.org/library/uuid.html
1488
1489var _nodeId;
1490var _clockseq;
1491
1492// Previous uuid creation time
1493var _lastMSecs = 0;
1494var _lastNSecs = 0;
1495
1496// See https://github.com/broofa/node-uuid for API details
1497function v1(options, buf, offset) {
1498 var i = buf && offset || 0;
1499 var b = buf || [];
1500
1501 options = options || {};
1502 var node = options.node || _nodeId;
1503 var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;
1504
1505 // node and clockseq need to be initialized to random values if they're not
1506 // specified. We do this lazily to minimize issues related to insufficient
1507 // system entropy. See #189
1508 if (node == null || clockseq == null) {
1509 var seedBytes = rng();
1510 if (node == null) {
1511 // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
1512 node = _nodeId = [
1513 seedBytes[0] | 0x01,
1514 seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]
1515 ];
1516 }
1517 if (clockseq == null) {
1518 // Per 4.2.2, randomize (14 bit) clockseq
1519 clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff;
1520 }
1521 }
1522
1523 // UUID timestamps are 100 nano-second units since the Gregorian epoch,
1524 // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
1525 // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
1526 // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
1527 var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();
1528
1529 // Per 4.2.1.2, use count of uuid's generated during the current clock
1530 // cycle to simulate higher resolution clock
1531 var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;
1532
1533 // Time since last uuid creation (in msecs)
1534 var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;
1535
1536 // Per 4.2.1.2, Bump clockseq on clock regression
1537 if (dt < 0 && options.clockseq === undefined) {
1538 clockseq = clockseq + 1 & 0x3fff;
1539 }
1540
1541 // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
1542 // time interval
1543 if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
1544 nsecs = 0;
1545 }
1546
1547 // Per 4.2.1.2 Throw error if too many uuids are requested
1548 if (nsecs >= 10000) {
1549 throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec');
1550 }
1551
1552 _lastMSecs = msecs;
1553 _lastNSecs = nsecs;
1554 _clockseq = clockseq;
1555
1556 // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
1557 msecs += 12219292800000;
1558
1559 // `time_low`
1560 var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
1561 b[i++] = tl >>> 24 & 0xff;
1562 b[i++] = tl >>> 16 & 0xff;
1563 b[i++] = tl >>> 8 & 0xff;
1564 b[i++] = tl & 0xff;
1565
1566 // `time_mid`
1567 var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
1568 b[i++] = tmh >>> 8 & 0xff;
1569 b[i++] = tmh & 0xff;
1570
1571 // `time_high_and_version`
1572 b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
1573 b[i++] = tmh >>> 16 & 0xff;
1574
1575 // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
1576 b[i++] = clockseq >>> 8 | 0x80;
1577
1578 // `clock_seq_low`
1579 b[i++] = clockseq & 0xff;
1580
1581 // `node`
1582 for (var n = 0; n < 6; ++n) {
1583 b[i + n] = node[n];
1584 }
1585
1586 return buf ? buf : bytesToUuid(b);
1587}
1588
1589module.exports = v1;
1590
1591},{"7":7,"8":8}],10:[function(_dereq_,module,exports){
1592var rng = _dereq_(8);
1593var bytesToUuid = _dereq_(7);
1594
1595function v4(options, buf, offset) {
1596 var i = buf && offset || 0;
1597
1598 if (typeof(options) == 'string') {
1599 buf = options === 'binary' ? new Array(16) : null;
1600 options = null;
1601 }
1602 options = options || {};
1603
1604 var rnds = options.random || (options.rng || rng)();
1605
1606 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
1607 rnds[6] = (rnds[6] & 0x0f) | 0x40;
1608 rnds[8] = (rnds[8] & 0x3f) | 0x80;
1609
1610 // Copy bytes to buffer, if provided
1611 if (buf) {
1612 for (var ii = 0; ii < 16; ++ii) {
1613 buf[i + ii] = rnds[ii];
1614 }
1615 }
1616
1617 return buf || bytesToUuid(rnds);
1618}
1619
1620module.exports = v4;
1621
1622},{"7":7,"8":8}],11:[function(_dereq_,module,exports){
1623(function (global){
1624'use strict';
1625
1626function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
1627
1628var immediate = _interopDefault(_dereq_(3));
1629var events = _dereq_(2);
1630var uuidV4 = _interopDefault(_dereq_(6));
1631var Md5 = _interopDefault(_dereq_(5));
1632var getArguments = _interopDefault(_dereq_(1));
1633var inherits = _interopDefault(_dereq_(4));
1634
1635function isBinaryObject(object) {
1636 return (typeof ArrayBuffer !== 'undefined' && object instanceof ArrayBuffer) ||
1637 (typeof Blob !== 'undefined' && object instanceof Blob);
1638}
1639
1640function cloneArrayBuffer(buff) {
1641 if (typeof buff.slice === 'function') {
1642 return buff.slice(0);
1643 }
1644 // IE10-11 slice() polyfill
1645 var target = new ArrayBuffer(buff.byteLength);
1646 var targetArray = new Uint8Array(target);
1647 var sourceArray = new Uint8Array(buff);
1648 targetArray.set(sourceArray);
1649 return target;
1650}
1651
1652function cloneBinaryObject(object) {
1653 if (object instanceof ArrayBuffer) {
1654 return cloneArrayBuffer(object);
1655 }
1656 var size = object.size;
1657 var type = object.type;
1658 // Blob
1659 if (typeof object.slice === 'function') {
1660 return object.slice(0, size, type);
1661 }
1662 // PhantomJS slice() replacement
1663 return object.webkitSlice(0, size, type);
1664}
1665
1666// most of this is borrowed from lodash.isPlainObject:
1667// https://github.com/fis-components/lodash.isplainobject/
1668// blob/29c358140a74f252aeb08c9eb28bef86f2217d4a/index.js
1669
1670var funcToString = Function.prototype.toString;
1671var objectCtorString = funcToString.call(Object);
1672
1673function isPlainObject(value) {
1674 var proto = Object.getPrototypeOf(value);
1675 /* istanbul ignore if */
1676 if (proto === null) { // not sure when this happens, but I guess it can
1677 return true;
1678 }
1679 var Ctor = proto.constructor;
1680 return (typeof Ctor == 'function' &&
1681 Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
1682}
1683
1684function clone(object) {
1685 var newObject;
1686 var i;
1687 var len;
1688
1689 if (!object || typeof object !== 'object') {
1690 return object;
1691 }
1692
1693 if (Array.isArray(object)) {
1694 newObject = [];
1695 for (i = 0, len = object.length; i < len; i++) {
1696 newObject[i] = clone(object[i]);
1697 }
1698 return newObject;
1699 }
1700
1701 // special case: to avoid inconsistencies between IndexedDB
1702 // and other backends, we automatically stringify Dates
1703 if (object instanceof Date) {
1704 return object.toISOString();
1705 }
1706
1707 if (isBinaryObject(object)) {
1708 return cloneBinaryObject(object);
1709 }
1710
1711 if (!isPlainObject(object)) {
1712 return object; // don't clone objects like Workers
1713 }
1714
1715 newObject = {};
1716 for (i in object) {
1717 /* istanbul ignore else */
1718 if (Object.prototype.hasOwnProperty.call(object, i)) {
1719 var value = clone(object[i]);
1720 if (typeof value !== 'undefined') {
1721 newObject[i] = value;
1722 }
1723 }
1724 }
1725 return newObject;
1726}
1727
1728function once(fun) {
1729 var called = false;
1730 return getArguments(function (args) {
1731 /* istanbul ignore if */
1732 if (called) {
1733 // this is a smoke test and should never actually happen
1734 throw new Error('once called more than once');
1735 } else {
1736 called = true;
1737 fun.apply(this, args);
1738 }
1739 });
1740}
1741
1742function toPromise(func) {
1743 //create the function we will be returning
1744 return getArguments(function (args) {
1745 // Clone arguments
1746 args = clone(args);
1747 var self = this;
1748 // if the last argument is a function, assume its a callback
1749 var usedCB = (typeof args[args.length - 1] === 'function') ? args.pop() : false;
1750 var promise = new Promise(function (fulfill, reject) {
1751 var resp;
1752 try {
1753 var callback = once(function (err, mesg) {
1754 if (err) {
1755 reject(err);
1756 } else {
1757 fulfill(mesg);
1758 }
1759 });
1760 // create a callback for this invocation
1761 // apply the function in the orig context
1762 args.push(callback);
1763 resp = func.apply(self, args);
1764 if (resp && typeof resp.then === 'function') {
1765 fulfill(resp);
1766 }
1767 } catch (e) {
1768 reject(e);
1769 }
1770 });
1771 // if there is a callback, call it back
1772 if (usedCB) {
1773 promise.then(function (result) {
1774 usedCB(null, result);
1775 }, usedCB);
1776 }
1777 return promise;
1778 });
1779}
1780
1781function mangle(key) {
1782 return '$' + key;
1783}
1784function unmangle(key) {
1785 return key.substring(1);
1786}
1787function Map$1() {
1788 this._store = {};
1789}
1790Map$1.prototype.get = function (key) {
1791 var mangled = mangle(key);
1792 return this._store[mangled];
1793};
1794Map$1.prototype.set = function (key, value) {
1795 var mangled = mangle(key);
1796 this._store[mangled] = value;
1797 return true;
1798};
1799Map$1.prototype.has = function (key) {
1800 var mangled = mangle(key);
1801 return mangled in this._store;
1802};
1803Map$1.prototype["delete"] = function (key) {
1804 var mangled = mangle(key);
1805 var res = mangled in this._store;
1806 delete this._store[mangled];
1807 return res;
1808};
1809Map$1.prototype.forEach = function (cb) {
1810 var keys = Object.keys(this._store);
1811 for (var i = 0, len = keys.length; i < len; i++) {
1812 var key = keys[i];
1813 var value = this._store[key];
1814 key = unmangle(key);
1815 cb(value, key);
1816 }
1817};
1818Object.defineProperty(Map$1.prototype, 'size', {
1819 get: function () {
1820 return Object.keys(this._store).length;
1821 }
1822});
1823
1824function Set$1(array) {
1825 this._store = new Map$1();
1826
1827 // init with an array
1828 if (array && Array.isArray(array)) {
1829 for (var i = 0, len = array.length; i < len; i++) {
1830 this.add(array[i]);
1831 }
1832 }
1833}
1834Set$1.prototype.add = function (key) {
1835 return this._store.set(key, true);
1836};
1837Set$1.prototype.has = function (key) {
1838 return this._store.has(key);
1839};
1840Set$1.prototype.forEach = function (cb) {
1841 this._store.forEach(function (value, key) {
1842 cb(key);
1843 });
1844};
1845Object.defineProperty(Set$1.prototype, 'size', {
1846 get: function () {
1847 return this._store.size;
1848 }
1849});
1850
1851/* global Map,Set,Symbol */
1852// Based on https://kangax.github.io/compat-table/es6/ we can sniff out
1853// incomplete Map/Set implementations which would otherwise cause our tests to fail.
1854// Notably they fail in IE11 and iOS 8.4, which this prevents.
1855function supportsMapAndSet() {
1856 if (typeof Symbol === 'undefined' || typeof Map === 'undefined' || typeof Set === 'undefined') {
1857 return false;
1858 }
1859 var prop = Object.getOwnPropertyDescriptor(Map, Symbol.species);
1860 return prop && 'get' in prop && Map[Symbol.species] === Map;
1861}
1862
1863// based on https://github.com/montagejs/collections
1864
1865var ExportedSet;
1866var ExportedMap;
1867
1868{
1869 if (supportsMapAndSet()) { // prefer built-in Map/Set
1870 ExportedSet = Set;
1871 ExportedMap = Map;
1872 } else { // fall back to our polyfill
1873 ExportedSet = Set$1;
1874 ExportedMap = Map$1;
1875 }
1876}
1877
1878// like underscore/lodash _.pick()
1879function pick(obj, arr) {
1880 var res = {};
1881 for (var i = 0, len = arr.length; i < len; i++) {
1882 var prop = arr[i];
1883 if (prop in obj) {
1884 res[prop] = obj[prop];
1885 }
1886 }
1887 return res;
1888}
1889
1890var hasLocal;
1891
1892try {
1893 localStorage.setItem('_pouch_check_localstorage', 1);
1894 hasLocal = !!localStorage.getItem('_pouch_check_localstorage');
1895} catch (e) {
1896 hasLocal = false;
1897}
1898
1899function hasLocalStorage() {
1900 return hasLocal;
1901}
1902
1903// Custom nextTick() shim for browsers. In node, this will just be process.nextTick(). We
1904
1905inherits(Changes, events.EventEmitter);
1906
1907/* istanbul ignore next */
1908function attachBrowserEvents(self) {
1909 if (hasLocalStorage()) {
1910 addEventListener("storage", function (e) {
1911 self.emit(e.key);
1912 });
1913 }
1914}
1915
1916function Changes() {
1917 events.EventEmitter.call(this);
1918 this._listeners = {};
1919
1920 attachBrowserEvents(this);
1921}
1922Changes.prototype.addListener = function (dbName, id, db, opts) {
1923 /* istanbul ignore if */
1924 if (this._listeners[id]) {
1925 return;
1926 }
1927 var self = this;
1928 var inprogress = false;
1929 function eventFunction() {
1930 /* istanbul ignore if */
1931 if (!self._listeners[id]) {
1932 return;
1933 }
1934 if (inprogress) {
1935 inprogress = 'waiting';
1936 return;
1937 }
1938 inprogress = true;
1939 var changesOpts = pick(opts, [
1940 'style', 'include_docs', 'attachments', 'conflicts', 'filter',
1941 'doc_ids', 'view', 'since', 'query_params', 'binary', 'return_docs'
1942 ]);
1943
1944 /* istanbul ignore next */
1945 function onError() {
1946 inprogress = false;
1947 }
1948
1949 db.changes(changesOpts).on('change', function (c) {
1950 if (c.seq > opts.since && !opts.cancelled) {
1951 opts.since = c.seq;
1952 opts.onChange(c);
1953 }
1954 }).on('complete', function () {
1955 if (inprogress === 'waiting') {
1956 immediate(eventFunction);
1957 }
1958 inprogress = false;
1959 }).on('error', onError);
1960 }
1961 this._listeners[id] = eventFunction;
1962 this.on(dbName, eventFunction);
1963};
1964
1965Changes.prototype.removeListener = function (dbName, id) {
1966 /* istanbul ignore if */
1967 if (!(id in this._listeners)) {
1968 return;
1969 }
1970 events.EventEmitter.prototype.removeListener.call(this, dbName,
1971 this._listeners[id]);
1972 delete this._listeners[id];
1973};
1974
1975
1976/* istanbul ignore next */
1977Changes.prototype.notifyLocalWindows = function (dbName) {
1978 //do a useless change on a storage thing
1979 //in order to get other windows's listeners to activate
1980 if (hasLocalStorage()) {
1981 localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a";
1982 }
1983};
1984
1985Changes.prototype.notify = function (dbName) {
1986 this.emit(dbName);
1987 this.notifyLocalWindows(dbName);
1988};
1989
1990function guardedConsole(method) {
1991 /* istanbul ignore else */
1992 if (typeof console !== 'undefined' && typeof console[method] === 'function') {
1993 var args = Array.prototype.slice.call(arguments, 1);
1994 console[method].apply(console, args);
1995 }
1996}
1997
1998var assign;
1999{
2000 if (typeof Object.assign === 'function') {
2001 assign = Object.assign;
2002 } else {
2003 // lite Object.assign polyfill based on
2004 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
2005 assign = function (target) {
2006 var to = Object(target);
2007
2008 for (var index = 1; index < arguments.length; index++) {
2009 var nextSource = arguments[index];
2010
2011 if (nextSource != null) { // Skip over if undefined or null
2012 for (var nextKey in nextSource) {
2013 // Avoid bugs when hasOwnProperty is shadowed
2014 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
2015 to[nextKey] = nextSource[nextKey];
2016 }
2017 }
2018 }
2019 }
2020 return to;
2021 };
2022 }
2023}
2024
2025var $inject_Object_assign = assign;
2026
2027inherits(PouchError, Error);
2028
2029function PouchError(status, error, reason) {
2030 Error.call(this, reason);
2031 this.status = status;
2032 this.name = error;
2033 this.message = reason;
2034 this.error = true;
2035}
2036
2037PouchError.prototype.toString = function () {
2038 return JSON.stringify({
2039 status: this.status,
2040 name: this.name,
2041 message: this.message,
2042 reason: this.reason
2043 });
2044};
2045
2046var UNAUTHORIZED = new PouchError(401, 'unauthorized', "Name or password is incorrect.");
2047var MISSING_BULK_DOCS = new PouchError(400, 'bad_request', "Missing JSON list of 'docs'");
2048var MISSING_DOC = new PouchError(404, 'not_found', 'missing');
2049var REV_CONFLICT = new PouchError(409, 'conflict', 'Document update conflict');
2050var INVALID_ID = new PouchError(400, 'bad_request', '_id field must contain a string');
2051var MISSING_ID = new PouchError(412, 'missing_id', '_id is required for puts');
2052var RESERVED_ID = new PouchError(400, 'bad_request', 'Only reserved document ids may start with underscore.');
2053var NOT_OPEN = new PouchError(412, 'precondition_failed', 'Database not open');
2054var UNKNOWN_ERROR = new PouchError(500, 'unknown_error', 'Database encountered an unknown error');
2055var BAD_ARG = new PouchError(500, 'badarg', 'Some query argument is invalid');
2056var INVALID_REQUEST = new PouchError(400, 'invalid_request', 'Request was invalid');
2057var QUERY_PARSE_ERROR = new PouchError(400, 'query_parse_error', 'Some query parameter is invalid');
2058var DOC_VALIDATION = new PouchError(500, 'doc_validation', 'Bad special document member');
2059var BAD_REQUEST = new PouchError(400, 'bad_request', 'Something wrong with the request');
2060var NOT_AN_OBJECT = new PouchError(400, 'bad_request', 'Document must be a JSON object');
2061var DB_MISSING = new PouchError(404, 'not_found', 'Database not found');
2062var IDB_ERROR = new PouchError(500, 'indexed_db_went_bad', 'unknown');
2063var WSQ_ERROR = new PouchError(500, 'web_sql_went_bad', 'unknown');
2064var LDB_ERROR = new PouchError(500, 'levelDB_went_went_bad', 'unknown');
2065var FORBIDDEN = new PouchError(403, 'forbidden', 'Forbidden by design doc validate_doc_update function');
2066var INVALID_REV = new PouchError(400, 'bad_request', 'Invalid rev format');
2067var FILE_EXISTS = new PouchError(412, 'file_exists', 'The database could not be created, the file already exists.');
2068var MISSING_STUB = new PouchError(412, 'missing_stub', 'A pre-existing attachment stub wasn\'t found');
2069var INVALID_URL = new PouchError(413, 'invalid_url', 'Provided URL is invalid');
2070
2071function generateErrorFromResponse(err) {
2072
2073 if (typeof err !== 'object') {
2074 var data = err;
2075 err = UNKNOWN_ERROR;
2076 err.data = data;
2077 }
2078
2079 if ('error' in err && err.error === 'conflict') {
2080 err.name = 'conflict';
2081 err.status = 409;
2082 }
2083
2084 if (!('name' in err)) {
2085 err.name = err.error || 'unknown';
2086 }
2087
2088 if (!('status' in err)) {
2089 err.status = 500;
2090 }
2091
2092 if (!('message' in err)) {
2093 err.message = err.message || err.reason;
2094 }
2095
2096 return err;
2097}
2098
2099function flatten(arrs) {
2100 var res = [];
2101 for (var i = 0, len = arrs.length; i < len; i++) {
2102 res = res.concat(arrs[i]);
2103 }
2104 return res;
2105}
2106
2107// shim for Function.prototype.name,
2108
2109// Checks if a PouchDB object is "remote" or not. This is
2110
2111function isRemote(db) {
2112 if (typeof db._remote === 'boolean') {
2113 return db._remote;
2114 }
2115 /* istanbul ignore next */
2116 if (typeof db.type === 'function') {
2117 guardedConsole('warn',
2118 'db.type() is deprecated and will be removed in ' +
2119 'a future version of PouchDB');
2120 return db.type() === 'http';
2121 }
2122 /* istanbul ignore next */
2123 return false;
2124}
2125
2126// originally parseUri 1.2.2, now patched by us
2127
2128// Based on https://github.com/alexdavid/scope-eval v0.0.3
2129
2130// this is essentially the "update sugar" function from daleharvey/pouchdb#1388
2131// the diffFun tells us what delta to apply to the doc. it either returns
2132// the doc, or false if it doesn't need to do an update after all
2133function upsert(db, docId, diffFun) {
2134 return new Promise(function (fulfill, reject) {
2135 db.get(docId, function (err, doc) {
2136 if (err) {
2137 /* istanbul ignore next */
2138 if (err.status !== 404) {
2139 return reject(err);
2140 }
2141 doc = {};
2142 }
2143
2144 // the user might change the _rev, so save it for posterity
2145 var docRev = doc._rev;
2146 var newDoc = diffFun(doc);
2147
2148 if (!newDoc) {
2149 // if the diffFun returns falsy, we short-circuit as
2150 // an optimization
2151 return fulfill({updated: false, rev: docRev});
2152 }
2153
2154 // users aren't allowed to modify these values,
2155 // so reset them here
2156 newDoc._id = docId;
2157 newDoc._rev = docRev;
2158 fulfill(tryAndPut(db, newDoc, diffFun));
2159 });
2160 });
2161}
2162
2163function tryAndPut(db, doc, diffFun) {
2164 return db.put(doc).then(function (res) {
2165 return {
2166 updated: true,
2167 rev: res.rev
2168 };
2169 }, function (err) {
2170 /* istanbul ignore next */
2171 if (err.status !== 409) {
2172 throw err;
2173 }
2174 return upsert(db, doc._id, diffFun);
2175 });
2176}
2177
2178var thisAtob = function (str) {
2179 return atob(str);
2180};
2181
2182// Abstracts constructing a Blob object, so it also works in older
2183// browsers that don't support the native Blob constructor (e.g.
2184// old QtWebKit versions, Android < 4.4).
2185function createBlob(parts, properties) {
2186 /* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
2187 parts = parts || [];
2188 properties = properties || {};
2189 try {
2190 return new Blob(parts, properties);
2191 } catch (e) {
2192 if (e.name !== "TypeError") {
2193 throw e;
2194 }
2195 var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder :
2196 typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder :
2197 typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder :
2198 WebKitBlobBuilder;
2199 var builder = new Builder();
2200 for (var i = 0; i < parts.length; i += 1) {
2201 builder.append(parts[i]);
2202 }
2203 return builder.getBlob(properties.type);
2204 }
2205}
2206
2207// From http://stackoverflow.com/questions/14967647/ (continues on next line)
2208// encode-decode-image-with-base64-breaks-image (2013-04-21)
2209function binaryStringToArrayBuffer(bin) {
2210 var length = bin.length;
2211 var buf = new ArrayBuffer(length);
2212 var arr = new Uint8Array(buf);
2213 for (var i = 0; i < length; i++) {
2214 arr[i] = bin.charCodeAt(i);
2215 }
2216 return buf;
2217}
2218
2219function binStringToBluffer(binString, type) {
2220 return createBlob([binaryStringToArrayBuffer(binString)], {type: type});
2221}
2222
2223function b64ToBluffer(b64, type) {
2224 return binStringToBluffer(thisAtob(b64), type);
2225}
2226
2227//Can't find original post, but this is close
2228
2229// simplified API. universal browser support is assumed
2230
2231// this is not used in the browser
2232
2233var setImmediateShim = global.setImmediate || global.setTimeout;
2234
2235function stringMd5(string) {
2236 return Md5.hash(string);
2237}
2238
2239var uuid = uuidV4.v4;
2240
2241var h = Headers;
2242
2243// we restucture the supplied JSON considerably, because the official
2244// Mango API is very particular about a lot of this stuff, but we like
2245// to be liberal with what we accept in order to prevent mental
2246// breakdowns in our users
2247function massageCreateIndexRequest(requestDef) {
2248 requestDef = clone(requestDef);
2249
2250 if (!requestDef.index) {
2251 requestDef.index = {};
2252 }
2253
2254 ['type', 'name', 'ddoc'].forEach(function (key) {
2255 if (requestDef.index[key]) {
2256 requestDef[key] = requestDef.index[key];
2257 delete requestDef.index[key];
2258 }
2259 });
2260
2261 if (requestDef.fields) {
2262 requestDef.index.fields = requestDef.fields;
2263 delete requestDef.fields;
2264 }
2265
2266 if (!requestDef.type) {
2267 requestDef.type = 'json';
2268 }
2269 return requestDef;
2270}
2271
2272function dbFetch(db, path, opts, callback) {
2273 var status, ok;
2274 opts.headers = new h({'Content-type': 'application/json'});
2275 db.fetch(path, opts).then(function (response) {
2276 status = response.status;
2277 ok = response.ok;
2278 return response.json();
2279 }).then(function (json) {
2280 if (!ok) {
2281 json.status = status;
2282 var err = generateErrorFromResponse(json);
2283 callback(err);
2284 } else {
2285 callback(null, json);
2286 }
2287 })["catch"](callback);
2288}
2289
2290function createIndex(db, requestDef, callback) {
2291 requestDef = massageCreateIndexRequest(requestDef);
2292 dbFetch(db, '_index', {
2293 method: 'POST',
2294 body: JSON.stringify(requestDef)
2295 }, callback);
2296}
2297
2298function find(db, requestDef, callback) {
2299 dbFetch(db, '_find', {
2300 method: 'POST',
2301 body: JSON.stringify(requestDef)
2302 }, callback);
2303}
2304
2305function explain(db, requestDef, callback) {
2306 dbFetch(db, '_explain', {
2307 method: 'POST',
2308 body: JSON.stringify(requestDef)
2309 }, callback);
2310}
2311
2312function getIndexes(db, callback) {
2313 dbFetch(db, '_index', {
2314 method: 'GET'
2315 }, callback);
2316}
2317
2318function deleteIndex(db, indexDef, callback) {
2319
2320
2321 var ddoc = indexDef.ddoc;
2322 var type = indexDef.type || 'json';
2323 var name = indexDef.name;
2324
2325 if (!ddoc) {
2326 return callback(new Error('you must provide an index\'s ddoc'));
2327 }
2328
2329 if (!name) {
2330 return callback(new Error('you must provide an index\'s name'));
2331 }
2332
2333 var url = '_index/' + [ddoc, type, name].map(encodeURIComponent).join('/');
2334
2335 dbFetch(db, url, {method: 'DELETE'}, callback);
2336}
2337
2338// this would just be "return doc[field]", but fields
2339// can be "deep" due to dot notation
2340function getFieldFromDoc(doc, parsedField) {
2341 var value = doc;
2342 for (var i = 0, len = parsedField.length; i < len; i++) {
2343 var key = parsedField[i];
2344 value = value[key];
2345 if (!value) {
2346 break;
2347 }
2348 }
2349 return value;
2350}
2351
2352function setFieldInDoc(doc, parsedField, value) {
2353 for (var i = 0, len = parsedField.length; i < len-1; i++) {
2354 var elem = parsedField[i];
2355 doc = doc[elem] = doc[elem] || {};
2356 }
2357 doc[parsedField[len-1]] = value;
2358}
2359
2360function compare(left, right) {
2361 return left < right ? -1 : left > right ? 1 : 0;
2362}
2363
2364// Converts a string in dot notation to an array of its components, with backslash escaping
2365function parseField(fieldName) {
2366 // fields may be deep (e.g. "foo.bar.baz"), so parse
2367 var fields = [];
2368 var current = '';
2369 for (var i = 0, len = fieldName.length; i < len; i++) {
2370 var ch = fieldName[i];
2371 if (ch === '.') {
2372 if (i > 0 && fieldName[i - 1] === '\\') { // escaped delimiter
2373 current = current.substring(0, current.length - 1) + '.';
2374 } else { // not escaped, so delimiter
2375 fields.push(current);
2376 current = '';
2377 }
2378 } else { // normal character
2379 current += ch;
2380 }
2381 }
2382 fields.push(current);
2383 return fields;
2384}
2385
2386var combinationFields = ['$or', '$nor', '$not'];
2387function isCombinationalField(field) {
2388 return combinationFields.indexOf(field) > -1;
2389}
2390
2391function getKey(obj) {
2392 return Object.keys(obj)[0];
2393}
2394
2395function getValue(obj) {
2396 return obj[getKey(obj)];
2397}
2398
2399
2400// flatten an array of selectors joined by an $and operator
2401function mergeAndedSelectors(selectors) {
2402
2403 // sort to ensure that e.g. if the user specified
2404 // $and: [{$gt: 'a'}, {$gt: 'b'}], then it's collapsed into
2405 // just {$gt: 'b'}
2406 var res = {};
2407
2408 selectors.forEach(function (selector) {
2409 Object.keys(selector).forEach(function (field) {
2410 var matcher = selector[field];
2411 if (typeof matcher !== 'object') {
2412 matcher = {$eq: matcher};
2413 }
2414
2415 if (isCombinationalField(field)) {
2416 if (matcher instanceof Array) {
2417 res[field] = matcher.map(function (m) {
2418 return mergeAndedSelectors([m]);
2419 });
2420 } else {
2421 res[field] = mergeAndedSelectors([matcher]);
2422 }
2423 } else {
2424 var fieldMatchers = res[field] = res[field] || {};
2425 Object.keys(matcher).forEach(function (operator) {
2426 var value = matcher[operator];
2427
2428 if (operator === '$gt' || operator === '$gte') {
2429 return mergeGtGte(operator, value, fieldMatchers);
2430 } else if (operator === '$lt' || operator === '$lte') {
2431 return mergeLtLte(operator, value, fieldMatchers);
2432 } else if (operator === '$ne') {
2433 return mergeNe(value, fieldMatchers);
2434 } else if (operator === '$eq') {
2435 return mergeEq(value, fieldMatchers);
2436 }
2437 fieldMatchers[operator] = value;
2438 });
2439 }
2440 });
2441 });
2442
2443 return res;
2444}
2445
2446
2447
2448// collapse logically equivalent gt/gte values
2449function mergeGtGte(operator, value, fieldMatchers) {
2450 if (typeof fieldMatchers.$eq !== 'undefined') {
2451 return; // do nothing
2452 }
2453 if (typeof fieldMatchers.$gte !== 'undefined') {
2454 if (operator === '$gte') {
2455 if (value > fieldMatchers.$gte) { // more specificity
2456 fieldMatchers.$gte = value;
2457 }
2458 } else { // operator === '$gt'
2459 if (value >= fieldMatchers.$gte) { // more specificity
2460 delete fieldMatchers.$gte;
2461 fieldMatchers.$gt = value;
2462 }
2463 }
2464 } else if (typeof fieldMatchers.$gt !== 'undefined') {
2465 if (operator === '$gte') {
2466 if (value > fieldMatchers.$gt) { // more specificity
2467 delete fieldMatchers.$gt;
2468 fieldMatchers.$gte = value;
2469 }
2470 } else { // operator === '$gt'
2471 if (value > fieldMatchers.$gt) { // more specificity
2472 fieldMatchers.$gt = value;
2473 }
2474 }
2475 } else {
2476 fieldMatchers[operator] = value;
2477 }
2478}
2479
2480// collapse logically equivalent lt/lte values
2481function mergeLtLte(operator, value, fieldMatchers) {
2482 if (typeof fieldMatchers.$eq !== 'undefined') {
2483 return; // do nothing
2484 }
2485 if (typeof fieldMatchers.$lte !== 'undefined') {
2486 if (operator === '$lte') {
2487 if (value < fieldMatchers.$lte) { // more specificity
2488 fieldMatchers.$lte = value;
2489 }
2490 } else { // operator === '$gt'
2491 if (value <= fieldMatchers.$lte) { // more specificity
2492 delete fieldMatchers.$lte;
2493 fieldMatchers.$lt = value;
2494 }
2495 }
2496 } else if (typeof fieldMatchers.$lt !== 'undefined') {
2497 if (operator === '$lte') {
2498 if (value < fieldMatchers.$lt) { // more specificity
2499 delete fieldMatchers.$lt;
2500 fieldMatchers.$lte = value;
2501 }
2502 } else { // operator === '$gt'
2503 if (value < fieldMatchers.$lt) { // more specificity
2504 fieldMatchers.$lt = value;
2505 }
2506 }
2507 } else {
2508 fieldMatchers[operator] = value;
2509 }
2510}
2511
2512// combine $ne values into one array
2513function mergeNe(value, fieldMatchers) {
2514 if ('$ne' in fieldMatchers) {
2515 // there are many things this could "not" be
2516 fieldMatchers.$ne.push(value);
2517 } else { // doesn't exist yet
2518 fieldMatchers.$ne = [value];
2519 }
2520}
2521
2522// add $eq into the mix
2523function mergeEq(value, fieldMatchers) {
2524 // these all have less specificity than the $eq
2525 // TODO: check for user errors here
2526 delete fieldMatchers.$gt;
2527 delete fieldMatchers.$gte;
2528 delete fieldMatchers.$lt;
2529 delete fieldMatchers.$lte;
2530 delete fieldMatchers.$ne;
2531 fieldMatchers.$eq = value;
2532}
2533
2534//#7458: execute function mergeAndedSelectors on nested $and
2535function mergeAndedSelectorsNested(obj) {
2536 for (var prop in obj) {
2537 if (Array.isArray(obj)) {
2538 for (var i in obj) {
2539 if (obj[i]['$and']) {
2540 obj[i] = mergeAndedSelectors(obj[i]['$and']);
2541 }
2542 }
2543 }
2544 var value = obj[prop];
2545 if (typeof value === 'object') {
2546 mergeAndedSelectorsNested(value); // <- recursive call
2547 }
2548 }
2549 return obj;
2550}
2551
2552//#7458: determine id $and is present in selector (at any level)
2553function isAndInSelector(obj, isAnd) {
2554 for (var prop in obj) {
2555 if (prop === '$and') {
2556 isAnd = true;
2557 }
2558 var value = obj[prop];
2559 if (typeof value === 'object') {
2560 isAnd = isAndInSelector(value, isAnd); // <- recursive call
2561 }
2562 }
2563 return isAnd;
2564}
2565
2566//
2567// normalize the selector
2568//
2569function massageSelector(input) {
2570 var result = clone(input);
2571 var wasAnded = false;
2572 //#7458: if $and is present in selector (at any level) merge nested $and
2573 if (isAndInSelector(result, false)) {
2574 result = mergeAndedSelectorsNested(result);
2575 if ('$and' in result) {
2576 result = mergeAndedSelectors(result['$and']);
2577 }
2578 wasAnded = true;
2579 }
2580
2581 ['$or', '$nor'].forEach(function (orOrNor) {
2582 if (orOrNor in result) {
2583 // message each individual selector
2584 // e.g. {foo: 'bar'} becomes {foo: {$eq: 'bar'}}
2585 result[orOrNor].forEach(function (subSelector) {
2586 var fields = Object.keys(subSelector);
2587 for (var i = 0; i < fields.length; i++) {
2588 var field = fields[i];
2589 var matcher = subSelector[field];
2590 if (typeof matcher !== 'object' || matcher === null) {
2591 subSelector[field] = {$eq: matcher};
2592 }
2593 }
2594 });
2595 }
2596 });
2597
2598 if ('$not' in result) {
2599 //This feels a little like forcing, but it will work for now,
2600 //I would like to come back to this and make the merging of selectors a little more generic
2601 result['$not'] = mergeAndedSelectors([result['$not']]);
2602 }
2603
2604 var fields = Object.keys(result);
2605
2606 for (var i = 0; i < fields.length; i++) {
2607 var field = fields[i];
2608 var matcher = result[field];
2609
2610 if (typeof matcher !== 'object' || matcher === null) {
2611 matcher = {$eq: matcher};
2612 } else if ('$ne' in matcher && !wasAnded) {
2613 // I put these in an array, since there may be more than one
2614 // but in the "mergeAnded" operation, I already take care of that
2615 matcher.$ne = [matcher.$ne];
2616 }
2617 result[field] = matcher;
2618 }
2619
2620 return result;
2621}
2622
2623function pad(str, padWith, upToLength) {
2624 var padding = '';
2625 var targetLength = upToLength - str.length;
2626 /* istanbul ignore next */
2627 while (padding.length < targetLength) {
2628 padding += padWith;
2629 }
2630 return padding;
2631}
2632
2633function padLeft(str, padWith, upToLength) {
2634 var padding = pad(str, padWith, upToLength);
2635 return padding + str;
2636}
2637
2638var MIN_MAGNITUDE = -324; // verified by -Number.MIN_VALUE
2639var MAGNITUDE_DIGITS = 3; // ditto
2640var SEP = ''; // set to '_' for easier debugging
2641
2642function collate(a, b) {
2643
2644 if (a === b) {
2645 return 0;
2646 }
2647
2648 a = normalizeKey(a);
2649 b = normalizeKey(b);
2650
2651 var ai = collationIndex(a);
2652 var bi = collationIndex(b);
2653 if ((ai - bi) !== 0) {
2654 return ai - bi;
2655 }
2656 switch (typeof a) {
2657 case 'number':
2658 return a - b;
2659 case 'boolean':
2660 return a < b ? -1 : 1;
2661 case 'string':
2662 return stringCollate(a, b);
2663 }
2664 return Array.isArray(a) ? arrayCollate(a, b) : objectCollate(a, b);
2665}
2666
2667// couch considers null/NaN/Infinity/-Infinity === undefined,
2668// for the purposes of mapreduce indexes. also, dates get stringified.
2669function normalizeKey(key) {
2670 switch (typeof key) {
2671 case 'undefined':
2672 return null;
2673 case 'number':
2674 if (key === Infinity || key === -Infinity || isNaN(key)) {
2675 return null;
2676 }
2677 return key;
2678 case 'object':
2679 var origKey = key;
2680 if (Array.isArray(key)) {
2681 var len = key.length;
2682 key = new Array(len);
2683 for (var i = 0; i < len; i++) {
2684 key[i] = normalizeKey(origKey[i]);
2685 }
2686 /* istanbul ignore next */
2687 } else if (key instanceof Date) {
2688 return key.toJSON();
2689 } else if (key !== null) { // generic object
2690 key = {};
2691 for (var k in origKey) {
2692 if (origKey.hasOwnProperty(k)) {
2693 var val = origKey[k];
2694 if (typeof val !== 'undefined') {
2695 key[k] = normalizeKey(val);
2696 }
2697 }
2698 }
2699 }
2700 }
2701 return key;
2702}
2703
2704function indexify(key) {
2705 if (key !== null) {
2706 switch (typeof key) {
2707 case 'boolean':
2708 return key ? 1 : 0;
2709 case 'number':
2710 return numToIndexableString(key);
2711 case 'string':
2712 // We've to be sure that key does not contain \u0000
2713 // Do order-preserving replacements:
2714 // 0 -> 1, 1
2715 // 1 -> 1, 2
2716 // 2 -> 2, 2
2717 /* eslint-disable no-control-regex */
2718 return key
2719 .replace(/\u0002/g, '\u0002\u0002')
2720 .replace(/\u0001/g, '\u0001\u0002')
2721 .replace(/\u0000/g, '\u0001\u0001');
2722 /* eslint-enable no-control-regex */
2723 case 'object':
2724 var isArray = Array.isArray(key);
2725 var arr = isArray ? key : Object.keys(key);
2726 var i = -1;
2727 var len = arr.length;
2728 var result = '';
2729 if (isArray) {
2730 while (++i < len) {
2731 result += toIndexableString(arr[i]);
2732 }
2733 } else {
2734 while (++i < len) {
2735 var objKey = arr[i];
2736 result += toIndexableString(objKey) +
2737 toIndexableString(key[objKey]);
2738 }
2739 }
2740 return result;
2741 }
2742 }
2743 return '';
2744}
2745
2746// convert the given key to a string that would be appropriate
2747// for lexical sorting, e.g. within a database, where the
2748// sorting is the same given by the collate() function.
2749function toIndexableString(key) {
2750 var zero = '\u0000';
2751 key = normalizeKey(key);
2752 return collationIndex(key) + SEP + indexify(key) + zero;
2753}
2754
2755function parseNumber(str, i) {
2756 var originalIdx = i;
2757 var num;
2758 var zero = str[i] === '1';
2759 if (zero) {
2760 num = 0;
2761 i++;
2762 } else {
2763 var neg = str[i] === '0';
2764 i++;
2765 var numAsString = '';
2766 var magAsString = str.substring(i, i + MAGNITUDE_DIGITS);
2767 var magnitude = parseInt(magAsString, 10) + MIN_MAGNITUDE;
2768 /* istanbul ignore next */
2769 if (neg) {
2770 magnitude = -magnitude;
2771 }
2772 i += MAGNITUDE_DIGITS;
2773 while (true) {
2774 var ch = str[i];
2775 if (ch === '\u0000') {
2776 break;
2777 } else {
2778 numAsString += ch;
2779 }
2780 i++;
2781 }
2782 numAsString = numAsString.split('.');
2783 if (numAsString.length === 1) {
2784 num = parseInt(numAsString, 10);
2785 } else {
2786 /* istanbul ignore next */
2787 num = parseFloat(numAsString[0] + '.' + numAsString[1]);
2788 }
2789 /* istanbul ignore next */
2790 if (neg) {
2791 num = num - 10;
2792 }
2793 /* istanbul ignore next */
2794 if (magnitude !== 0) {
2795 // parseFloat is more reliable than pow due to rounding errors
2796 // e.g. Number.MAX_VALUE would return Infinity if we did
2797 // num * Math.pow(10, magnitude);
2798 num = parseFloat(num + 'e' + magnitude);
2799 }
2800 }
2801 return {num: num, length : i - originalIdx};
2802}
2803
2804// move up the stack while parsing
2805// this function moved outside of parseIndexableString for performance
2806function pop(stack, metaStack) {
2807 var obj = stack.pop();
2808
2809 if (metaStack.length) {
2810 var lastMetaElement = metaStack[metaStack.length - 1];
2811 if (obj === lastMetaElement.element) {
2812 // popping a meta-element, e.g. an object whose value is another object
2813 metaStack.pop();
2814 lastMetaElement = metaStack[metaStack.length - 1];
2815 }
2816 var element = lastMetaElement.element;
2817 var lastElementIndex = lastMetaElement.index;
2818 if (Array.isArray(element)) {
2819 element.push(obj);
2820 } else if (lastElementIndex === stack.length - 2) { // obj with key+value
2821 var key = stack.pop();
2822 element[key] = obj;
2823 } else {
2824 stack.push(obj); // obj with key only
2825 }
2826 }
2827}
2828
2829function parseIndexableString(str) {
2830 var stack = [];
2831 var metaStack = []; // stack for arrays and objects
2832 var i = 0;
2833
2834 /*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
2835 while (true) {
2836 var collationIndex = str[i++];
2837 if (collationIndex === '\u0000') {
2838 if (stack.length === 1) {
2839 return stack.pop();
2840 } else {
2841 pop(stack, metaStack);
2842 continue;
2843 }
2844 }
2845 switch (collationIndex) {
2846 case '1':
2847 stack.push(null);
2848 break;
2849 case '2':
2850 stack.push(str[i] === '1');
2851 i++;
2852 break;
2853 case '3':
2854 var parsedNum = parseNumber(str, i);
2855 stack.push(parsedNum.num);
2856 i += parsedNum.length;
2857 break;
2858 case '4':
2859 var parsedStr = '';
2860 /*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
2861 while (true) {
2862 var ch = str[i];
2863 if (ch === '\u0000') {
2864 break;
2865 }
2866 parsedStr += ch;
2867 i++;
2868 }
2869 // perform the reverse of the order-preserving replacement
2870 // algorithm (see above)
2871 /* eslint-disable no-control-regex */
2872 parsedStr = parsedStr.replace(/\u0001\u0001/g, '\u0000')
2873 .replace(/\u0001\u0002/g, '\u0001')
2874 .replace(/\u0002\u0002/g, '\u0002');
2875 /* eslint-enable no-control-regex */
2876 stack.push(parsedStr);
2877 break;
2878 case '5':
2879 var arrayElement = { element: [], index: stack.length };
2880 stack.push(arrayElement.element);
2881 metaStack.push(arrayElement);
2882 break;
2883 case '6':
2884 var objElement = { element: {}, index: stack.length };
2885 stack.push(objElement.element);
2886 metaStack.push(objElement);
2887 break;
2888 /* istanbul ignore next */
2889 default:
2890 throw new Error(
2891 'bad collationIndex or unexpectedly reached end of input: ' +
2892 collationIndex);
2893 }
2894 }
2895}
2896
2897function arrayCollate(a, b) {
2898 var len = Math.min(a.length, b.length);
2899 for (var i = 0; i < len; i++) {
2900 var sort = collate(a[i], b[i]);
2901 if (sort !== 0) {
2902 return sort;
2903 }
2904 }
2905 return (a.length === b.length) ? 0 :
2906 (a.length > b.length) ? 1 : -1;
2907}
2908function stringCollate(a, b) {
2909 // See: https://github.com/daleharvey/pouchdb/issues/40
2910 // This is incompatible with the CouchDB implementation, but its the
2911 // best we can do for now
2912 return (a === b) ? 0 : ((a > b) ? 1 : -1);
2913}
2914function objectCollate(a, b) {
2915 var ak = Object.keys(a), bk = Object.keys(b);
2916 var len = Math.min(ak.length, bk.length);
2917 for (var i = 0; i < len; i++) {
2918 // First sort the keys
2919 var sort = collate(ak[i], bk[i]);
2920 if (sort !== 0) {
2921 return sort;
2922 }
2923 // if the keys are equal sort the values
2924 sort = collate(a[ak[i]], b[bk[i]]);
2925 if (sort !== 0) {
2926 return sort;
2927 }
2928
2929 }
2930 return (ak.length === bk.length) ? 0 :
2931 (ak.length > bk.length) ? 1 : -1;
2932}
2933// The collation is defined by erlangs ordered terms
2934// the atoms null, true, false come first, then numbers, strings,
2935// arrays, then objects
2936// null/undefined/NaN/Infinity/-Infinity are all considered null
2937function collationIndex(x) {
2938 var id = ['boolean', 'number', 'string', 'object'];
2939 var idx = id.indexOf(typeof x);
2940 //false if -1 otherwise true, but fast!!!!1
2941 if (~idx) {
2942 if (x === null) {
2943 return 1;
2944 }
2945 if (Array.isArray(x)) {
2946 return 5;
2947 }
2948 return idx < 3 ? (idx + 2) : (idx + 3);
2949 }
2950 /* istanbul ignore next */
2951 if (Array.isArray(x)) {
2952 return 5;
2953 }
2954}
2955
2956// conversion:
2957// x yyy zz...zz
2958// x = 0 for negative, 1 for 0, 2 for positive
2959// y = exponent (for negative numbers negated) moved so that it's >= 0
2960// z = mantisse
2961function numToIndexableString(num) {
2962
2963 if (num === 0) {
2964 return '1';
2965 }
2966
2967 // convert number to exponential format for easier and
2968 // more succinct string sorting
2969 var expFormat = num.toExponential().split(/e\+?/);
2970 var magnitude = parseInt(expFormat[1], 10);
2971
2972 var neg = num < 0;
2973
2974 var result = neg ? '0' : '2';
2975
2976 // first sort by magnitude
2977 // it's easier if all magnitudes are positive
2978 var magForComparison = ((neg ? -magnitude : magnitude) - MIN_MAGNITUDE);
2979 var magString = padLeft((magForComparison).toString(), '0', MAGNITUDE_DIGITS);
2980
2981 result += SEP + magString;
2982
2983 // then sort by the factor
2984 var factor = Math.abs(parseFloat(expFormat[0])); // [1..10)
2985 /* istanbul ignore next */
2986 if (neg) { // for negative reverse ordering
2987 factor = 10 - factor;
2988 }
2989
2990 var factorStr = factor.toFixed(20);
2991
2992 // strip zeros from the end
2993 factorStr = factorStr.replace(/\.?0+$/, '');
2994
2995 result += SEP + factorStr;
2996
2997 return result;
2998}
2999
3000// create a comparator based on the sort object
3001function createFieldSorter(sort) {
3002
3003 function getFieldValuesAsArray(doc) {
3004 return sort.map(function (sorting) {
3005 var fieldName = getKey(sorting);
3006 var parsedField = parseField(fieldName);
3007 var docFieldValue = getFieldFromDoc(doc, parsedField);
3008 return docFieldValue;
3009 });
3010 }
3011
3012 return function (aRow, bRow) {
3013 var aFieldValues = getFieldValuesAsArray(aRow.doc);
3014 var bFieldValues = getFieldValuesAsArray(bRow.doc);
3015 var collation = collate(aFieldValues, bFieldValues);
3016 if (collation !== 0) {
3017 return collation;
3018 }
3019 // this is what mango seems to do
3020 return compare(aRow.doc._id, bRow.doc._id);
3021 };
3022}
3023
3024function filterInMemoryFields(rows, requestDef, inMemoryFields) {
3025 rows = rows.filter(function (row) {
3026 return rowFilter(row.doc, requestDef.selector, inMemoryFields);
3027 });
3028
3029 if (requestDef.sort) {
3030 // in-memory sort
3031 var fieldSorter = createFieldSorter(requestDef.sort);
3032 rows = rows.sort(fieldSorter);
3033 if (typeof requestDef.sort[0] !== 'string' &&
3034 getValue(requestDef.sort[0]) === 'desc') {
3035 rows = rows.reverse();
3036 }
3037 }
3038
3039 if ('limit' in requestDef || 'skip' in requestDef) {
3040 // have to do the limit in-memory
3041 var skip = requestDef.skip || 0;
3042 var limit = ('limit' in requestDef ? requestDef.limit : rows.length) + skip;
3043 rows = rows.slice(skip, limit);
3044 }
3045 return rows;
3046}
3047
3048function rowFilter(doc, selector, inMemoryFields) {
3049 return inMemoryFields.every(function (field) {
3050 var matcher = selector[field];
3051 var parsedField = parseField(field);
3052 var docFieldValue = getFieldFromDoc(doc, parsedField);
3053 if (isCombinationalField(field)) {
3054 return matchCominationalSelector(field, matcher, doc);
3055 }
3056
3057 return matchSelector(matcher, doc, parsedField, docFieldValue);
3058 });
3059}
3060
3061function matchSelector(matcher, doc, parsedField, docFieldValue) {
3062 if (!matcher) {
3063 // no filtering necessary; this field is just needed for sorting
3064 return true;
3065 }
3066
3067 // is matcher an object, if so continue recursion
3068 if (typeof matcher === 'object') {
3069 return Object.keys(matcher).every(function (userOperator) {
3070 var userValue = matcher[userOperator];
3071 return match(userOperator, doc, userValue, parsedField, docFieldValue);
3072 });
3073 }
3074
3075 // no more depth, No need to recurse further
3076 return matcher === docFieldValue;
3077}
3078
3079function matchCominationalSelector(field, matcher, doc) {
3080
3081 if (field === '$or') {
3082 return matcher.some(function (orMatchers) {
3083 return rowFilter(doc, orMatchers, Object.keys(orMatchers));
3084 });
3085 }
3086
3087 if (field === '$not') {
3088 return !rowFilter(doc, matcher, Object.keys(matcher));
3089 }
3090
3091 //`$nor`
3092 return !matcher.find(function (orMatchers) {
3093 return rowFilter(doc, orMatchers, Object.keys(orMatchers));
3094 });
3095
3096}
3097
3098function match(userOperator, doc, userValue, parsedField, docFieldValue) {
3099 if (!matchers[userOperator]) {
3100 throw new Error('unknown operator "' + userOperator +
3101 '" - should be one of $eq, $lte, $lt, $gt, $gte, $exists, $ne, $in, ' +
3102 '$nin, $size, $mod, $regex, $elemMatch, $type, $allMatch or $all');
3103 }
3104 return matchers[userOperator](doc, userValue, parsedField, docFieldValue);
3105}
3106
3107function fieldExists(docFieldValue) {
3108 return typeof docFieldValue !== 'undefined' && docFieldValue !== null;
3109}
3110
3111function fieldIsNotUndefined(docFieldValue) {
3112 return typeof docFieldValue !== 'undefined';
3113}
3114
3115function modField(docFieldValue, userValue) {
3116 var divisor = userValue[0];
3117 var mod = userValue[1];
3118 if (divisor === 0) {
3119 throw new Error('Bad divisor, cannot divide by zero');
3120 }
3121
3122 if (parseInt(divisor, 10) !== divisor ) {
3123 throw new Error('Divisor is not an integer');
3124 }
3125
3126 if (parseInt(mod, 10) !== mod ) {
3127 throw new Error('Modulus is not an integer');
3128 }
3129
3130 if (parseInt(docFieldValue, 10) !== docFieldValue) {
3131 return false;
3132 }
3133
3134 return docFieldValue % divisor === mod;
3135}
3136
3137function arrayContainsValue(docFieldValue, userValue) {
3138 return userValue.some(function (val) {
3139 if (docFieldValue instanceof Array) {
3140 return docFieldValue.indexOf(val) > -1;
3141 }
3142
3143 return docFieldValue === val;
3144 });
3145}
3146
3147function arrayContainsAllValues(docFieldValue, userValue) {
3148 return userValue.every(function (val) {
3149 return docFieldValue.indexOf(val) > -1;
3150 });
3151}
3152
3153function arraySize(docFieldValue, userValue) {
3154 return docFieldValue.length === userValue;
3155}
3156
3157function regexMatch(docFieldValue, userValue) {
3158 var re = new RegExp(userValue);
3159
3160 return re.test(docFieldValue);
3161}
3162
3163function typeMatch(docFieldValue, userValue) {
3164
3165 switch (userValue) {
3166 case 'null':
3167 return docFieldValue === null;
3168 case 'boolean':
3169 return typeof (docFieldValue) === 'boolean';
3170 case 'number':
3171 return typeof (docFieldValue) === 'number';
3172 case 'string':
3173 return typeof (docFieldValue) === 'string';
3174 case 'array':
3175 return docFieldValue instanceof Array;
3176 case 'object':
3177 return ({}).toString.call(docFieldValue) === '[object Object]';
3178 }
3179
3180 throw new Error(userValue + ' not supported as a type.' +
3181 'Please use one of object, string, array, number, boolean or null.');
3182
3183}
3184
3185var matchers = {
3186
3187 '$elemMatch': function (doc, userValue, parsedField, docFieldValue) {
3188 if (!Array.isArray(docFieldValue)) {
3189 return false;
3190 }
3191
3192 if (docFieldValue.length === 0) {
3193 return false;
3194 }
3195
3196 if (typeof docFieldValue[0] === 'object') {
3197 return docFieldValue.some(function (val) {
3198 return rowFilter(val, userValue, Object.keys(userValue));
3199 });
3200 }
3201
3202 return docFieldValue.some(function (val) {
3203 return matchSelector(userValue, doc, parsedField, val);
3204 });
3205 },
3206
3207 '$allMatch': function (doc, userValue, parsedField, docFieldValue) {
3208 if (!Array.isArray(docFieldValue)) {
3209 return false;
3210 }
3211
3212 /* istanbul ignore next */
3213 if (docFieldValue.length === 0) {
3214 return false;
3215 }
3216
3217 if (typeof docFieldValue[0] === 'object') {
3218 return docFieldValue.every(function (val) {
3219 return rowFilter(val, userValue, Object.keys(userValue));
3220 });
3221 }
3222
3223 return docFieldValue.every(function (val) {
3224 return matchSelector(userValue, doc, parsedField, val);
3225 });
3226 },
3227
3228 '$eq': function (doc, userValue, parsedField, docFieldValue) {
3229 return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) === 0;
3230 },
3231
3232 '$gte': function (doc, userValue, parsedField, docFieldValue) {
3233 return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) >= 0;
3234 },
3235
3236 '$gt': function (doc, userValue, parsedField, docFieldValue) {
3237 return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) > 0;
3238 },
3239
3240 '$lte': function (doc, userValue, parsedField, docFieldValue) {
3241 return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) <= 0;
3242 },
3243
3244 '$lt': function (doc, userValue, parsedField, docFieldValue) {
3245 return fieldIsNotUndefined(docFieldValue) && collate(docFieldValue, userValue) < 0;
3246 },
3247
3248 '$exists': function (doc, userValue, parsedField, docFieldValue) {
3249 //a field that is null is still considered to exist
3250 if (userValue) {
3251 return fieldIsNotUndefined(docFieldValue);
3252 }
3253
3254 return !fieldIsNotUndefined(docFieldValue);
3255 },
3256
3257 '$mod': function (doc, userValue, parsedField, docFieldValue) {
3258 return fieldExists(docFieldValue) && modField(docFieldValue, userValue);
3259 },
3260
3261 '$ne': function (doc, userValue, parsedField, docFieldValue) {
3262 return userValue.every(function (neValue) {
3263 return collate(docFieldValue, neValue) !== 0;
3264 });
3265 },
3266 '$in': function (doc, userValue, parsedField, docFieldValue) {
3267 return fieldExists(docFieldValue) && arrayContainsValue(docFieldValue, userValue);
3268 },
3269
3270 '$nin': function (doc, userValue, parsedField, docFieldValue) {
3271 return fieldExists(docFieldValue) && !arrayContainsValue(docFieldValue, userValue);
3272 },
3273
3274 '$size': function (doc, userValue, parsedField, docFieldValue) {
3275 return fieldExists(docFieldValue) && arraySize(docFieldValue, userValue);
3276 },
3277
3278 '$all': function (doc, userValue, parsedField, docFieldValue) {
3279 return Array.isArray(docFieldValue) && arrayContainsAllValues(docFieldValue, userValue);
3280 },
3281
3282 '$regex': function (doc, userValue, parsedField, docFieldValue) {
3283 return fieldExists(docFieldValue) && regexMatch(docFieldValue, userValue);
3284 },
3285
3286 '$type': function (doc, userValue, parsedField, docFieldValue) {
3287 return typeMatch(docFieldValue, userValue);
3288 }
3289};
3290
3291function getArguments$1(fun) {
3292 return function () {
3293 var len = arguments.length;
3294 var args = new Array(len);
3295 var i = -1;
3296 while (++i < len) {
3297 args[i] = arguments[i];
3298 }
3299 return fun.call(this, args);
3300 };
3301}
3302
3303function callbackify(fun) {
3304 return getArguments$1(function (args) {
3305 var cb = args.pop();
3306 var promise = fun.apply(this, args);
3307 promisedCallback(promise, cb);
3308 return promise;
3309 });
3310}
3311
3312function promisedCallback(promise, callback) {
3313 promise.then(function (res) {
3314 immediate(function () {
3315 callback(null, res);
3316 });
3317 }, function (reason) {
3318 immediate(function () {
3319 callback(reason);
3320 });
3321 });
3322 return promise;
3323}
3324
3325var flatten$1 = getArguments$1(function (args) {
3326 var res = [];
3327 for (var i = 0, len = args.length; i < len; i++) {
3328 var subArr = args[i];
3329 if (Array.isArray(subArr)) {
3330 res = res.concat(flatten$1.apply(null, subArr));
3331 } else {
3332 res.push(subArr);
3333 }
3334 }
3335 return res;
3336});
3337
3338function mergeObjects(arr) {
3339 var res = {};
3340 for (var i = 0, len = arr.length; i < len; i++) {
3341 res = $inject_Object_assign(res, arr[i]);
3342 }
3343 return res;
3344}
3345
3346// Selects a list of fields defined in dot notation from one doc
3347// and copies them to a new doc. Like underscore _.pick but supports nesting.
3348function pick$1(obj, arr) {
3349 var res = {};
3350 for (var i = 0, len = arr.length; i < len; i++) {
3351 var parsedField = parseField(arr[i]);
3352 var value = getFieldFromDoc(obj, parsedField);
3353 if (typeof value !== 'undefined') {
3354 setFieldInDoc(res, parsedField, value);
3355 }
3356 }
3357 return res;
3358}
3359
3360// e.g. ['a'], ['a', 'b'] is true, but ['b'], ['a', 'b'] is false
3361function oneArrayIsSubArrayOfOther(left, right) {
3362
3363 for (var i = 0, len = Math.min(left.length, right.length); i < len; i++) {
3364 if (left[i] !== right[i]) {
3365 return false;
3366 }
3367 }
3368 return true;
3369}
3370
3371// e.g.['a', 'b', 'c'], ['a', 'b'] is false
3372function oneArrayIsStrictSubArrayOfOther(left, right) {
3373
3374 if (left.length > right.length) {
3375 return false;
3376 }
3377
3378 return oneArrayIsSubArrayOfOther(left, right);
3379}
3380
3381// same as above, but treat the left array as an unordered set
3382// e.g. ['b', 'a'], ['a', 'b', 'c'] is true, but ['c'], ['a', 'b', 'c'] is false
3383function oneSetIsSubArrayOfOther(left, right) {
3384 left = left.slice();
3385 for (var i = 0, len = right.length; i < len; i++) {
3386 var field = right[i];
3387 if (!left.length) {
3388 break;
3389 }
3390 var leftIdx = left.indexOf(field);
3391 if (leftIdx === -1) {
3392 return false;
3393 } else {
3394 left.splice(leftIdx, 1);
3395 }
3396 }
3397 return true;
3398}
3399
3400function arrayToObject(arr) {
3401 var res = {};
3402 for (var i = 0, len = arr.length; i < len; i++) {
3403 res[arr[i]] = true;
3404 }
3405 return res;
3406}
3407
3408function max(arr, fun) {
3409 var max = null;
3410 var maxScore = -1;
3411 for (var i = 0, len = arr.length; i < len; i++) {
3412 var element = arr[i];
3413 var score = fun(element);
3414 if (score > maxScore) {
3415 maxScore = score;
3416 max = element;
3417 }
3418 }
3419 return max;
3420}
3421
3422function arrayEquals(arr1, arr2) {
3423 if (arr1.length !== arr2.length) {
3424 return false;
3425 }
3426 for (var i = 0, len = arr1.length; i < len; i++) {
3427 if (arr1[i] !== arr2[i]) {
3428 return false;
3429 }
3430 }
3431 return true;
3432}
3433
3434function uniq(arr) {
3435 var obj = {};
3436 for (var i = 0; i < arr.length; i++) {
3437 obj['$' + arr[i]] = true;
3438 }
3439 return Object.keys(obj).map(function (key) {
3440 return key.substring(1);
3441 });
3442}
3443
3444/*
3445 * Simple task queue to sequentialize actions. Assumes
3446 * callbacks will eventually fire (once).
3447 */
3448
3449
3450function TaskQueue() {
3451 this.promise = new Promise(function (fulfill) {fulfill(); });
3452}
3453TaskQueue.prototype.add = function (promiseFactory) {
3454 this.promise = this.promise["catch"](function () {
3455 // just recover
3456 }).then(function () {
3457 return promiseFactory();
3458 });
3459 return this.promise;
3460};
3461TaskQueue.prototype.finish = function () {
3462 return this.promise;
3463};
3464
3465function stringify(input) {
3466 if (!input) {
3467 return 'undefined'; // backwards compat for empty reduce
3468 }
3469 // for backwards compat with mapreduce, functions/strings are stringified
3470 // as-is. everything else is JSON-stringified.
3471 switch (typeof input) {
3472 case 'function':
3473 // e.g. a mapreduce map
3474 return input.toString();
3475 case 'string':
3476 // e.g. a mapreduce built-in _reduce function
3477 return input.toString();
3478 default:
3479 // e.g. a JSON object in the case of mango queries
3480 return JSON.stringify(input);
3481 }
3482}
3483
3484/* create a string signature for a view so we can cache it and uniq it */
3485function createViewSignature(mapFun, reduceFun) {
3486 // the "undefined" part is for backwards compatibility
3487 return stringify(mapFun) + stringify(reduceFun) + 'undefined';
3488}
3489
3490function createView(sourceDB, viewName, mapFun, reduceFun, temporary, localDocName) {
3491 var viewSignature = createViewSignature(mapFun, reduceFun);
3492
3493 var cachedViews;
3494 if (!temporary) {
3495 // cache this to ensure we don't try to update the same view twice
3496 cachedViews = sourceDB._cachedViews = sourceDB._cachedViews || {};
3497 if (cachedViews[viewSignature]) {
3498 return cachedViews[viewSignature];
3499 }
3500 }
3501
3502 var promiseForView = sourceDB.info().then(function (info) {
3503
3504 var depDbName = info.db_name + '-mrview-' +
3505 (temporary ? 'temp' : stringMd5(viewSignature));
3506
3507 // save the view name in the source db so it can be cleaned up if necessary
3508 // (e.g. when the _design doc is deleted, remove all associated view data)
3509 function diffFunction(doc) {
3510 doc.views = doc.views || {};
3511 var fullViewName = viewName;
3512 if (fullViewName.indexOf('/') === -1) {
3513 fullViewName = viewName + '/' + viewName;
3514 }
3515 var depDbs = doc.views[fullViewName] = doc.views[fullViewName] || {};
3516 /* istanbul ignore if */
3517 if (depDbs[depDbName]) {
3518 return; // no update necessary
3519 }
3520 depDbs[depDbName] = true;
3521 return doc;
3522 }
3523 return upsert(sourceDB, '_local/' + localDocName, diffFunction).then(function () {
3524 return sourceDB.registerDependentDatabase(depDbName).then(function (res) {
3525 var db = res.db;
3526 db.auto_compaction = true;
3527 var view = {
3528 name: depDbName,
3529 db: db,
3530 sourceDB: sourceDB,
3531 adapter: sourceDB.adapter,
3532 mapFun: mapFun,
3533 reduceFun: reduceFun
3534 };
3535 return view.db.get('_local/lastSeq')["catch"](function (err) {
3536 /* istanbul ignore if */
3537 if (err.status !== 404) {
3538 throw err;
3539 }
3540 }).then(function (lastSeqDoc) {
3541 view.seq = lastSeqDoc ? lastSeqDoc.seq : 0;
3542 if (cachedViews) {
3543 view.db.once('destroyed', function () {
3544 delete cachedViews[viewSignature];
3545 });
3546 }
3547 return view;
3548 });
3549 });
3550 });
3551 });
3552
3553 if (cachedViews) {
3554 cachedViews[viewSignature] = promiseForView;
3555 }
3556 return promiseForView;
3557}
3558
3559function QueryParseError(message) {
3560 this.status = 400;
3561 this.name = 'query_parse_error';
3562 this.message = message;
3563 this.error = true;
3564 try {
3565 Error.captureStackTrace(this, QueryParseError);
3566 } catch (e) {}
3567}
3568
3569inherits(QueryParseError, Error);
3570
3571function NotFoundError(message) {
3572 this.status = 404;
3573 this.name = 'not_found';
3574 this.message = message;
3575 this.error = true;
3576 try {
3577 Error.captureStackTrace(this, NotFoundError);
3578 } catch (e) {}
3579}
3580
3581inherits(NotFoundError, Error);
3582
3583function BuiltInError(message) {
3584 this.status = 500;
3585 this.name = 'invalid_value';
3586 this.message = message;
3587 this.error = true;
3588 try {
3589 Error.captureStackTrace(this, BuiltInError);
3590 } catch (e) {}
3591}
3592
3593inherits(BuiltInError, Error);
3594
3595function promisedCallback$1(promise, callback) {
3596 if (callback) {
3597 promise.then(function (res) {
3598 immediate(function () {
3599 callback(null, res);
3600 });
3601 }, function (reason) {
3602 immediate(function () {
3603 callback(reason);
3604 });
3605 });
3606 }
3607 return promise;
3608}
3609
3610function callbackify$1(fun) {
3611 return getArguments(function (args) {
3612 var cb = args.pop();
3613 var promise = fun.apply(this, args);
3614 if (typeof cb === 'function') {
3615 promisedCallback$1(promise, cb);
3616 }
3617 return promise;
3618 });
3619}
3620
3621// Promise finally util similar to Q.finally
3622function fin(promise, finalPromiseFactory) {
3623 return promise.then(function (res) {
3624 return finalPromiseFactory().then(function () {
3625 return res;
3626 });
3627 }, function (reason) {
3628 return finalPromiseFactory().then(function () {
3629 throw reason;
3630 });
3631 });
3632}
3633
3634function sequentialize(queue, promiseFactory) {
3635 return function () {
3636 var args = arguments;
3637 var that = this;
3638 return queue.add(function () {
3639 return promiseFactory.apply(that, args);
3640 });
3641 };
3642}
3643
3644// uniq an array of strings, order not guaranteed
3645// similar to underscore/lodash _.uniq
3646function uniq$1(arr) {
3647 var theSet = new ExportedSet(arr);
3648 var result = new Array(theSet.size);
3649 var index = -1;
3650 theSet.forEach(function (value) {
3651 result[++index] = value;
3652 });
3653 return result;
3654}
3655
3656function mapToKeysArray(map) {
3657 var result = new Array(map.size);
3658 var index = -1;
3659 map.forEach(function (value, key) {
3660 result[++index] = key;
3661 });
3662 return result;
3663}
3664
3665var persistentQueues = {};
3666var tempViewQueue = new TaskQueue();
3667var CHANGES_BATCH_SIZE = 50;
3668
3669function parseViewName(name) {
3670 // can be either 'ddocname/viewname' or just 'viewname'
3671 // (where the ddoc name is the same)
3672 return name.indexOf('/') === -1 ? [name, name] : name.split('/');
3673}
3674
3675function isGenOne(changes) {
3676 // only return true if the current change is 1-
3677 // and there are no other leafs
3678 return changes.length === 1 && /^1-/.test(changes[0].rev);
3679}
3680
3681function emitError(db, e) {
3682 try {
3683 db.emit('error', e);
3684 } catch (err) {
3685 guardedConsole('error',
3686 'The user\'s map/reduce function threw an uncaught error.\n' +
3687 'You can debug this error by doing:\n' +
3688 'myDatabase.on(\'error\', function (err) { debugger; });\n' +
3689 'Please double-check your map/reduce function.');
3690 guardedConsole('error', e);
3691 }
3692}
3693
3694/**
3695 * Returns an "abstract" mapreduce object of the form:
3696 *
3697 * {
3698 * query: queryFun,
3699 * viewCleanup: viewCleanupFun
3700 * }
3701 *
3702 * Arguments are:
3703 *
3704 * localDoc: string
3705 * This is for the local doc that gets saved in order to track the
3706 * "dependent" DBs and clean them up for viewCleanup. It should be
3707 * unique, so that indexer plugins don't collide with each other.
3708 * mapper: function (mapFunDef, emit)
3709 * Returns a map function based on the mapFunDef, which in the case of
3710 * normal map/reduce is just the de-stringified function, but may be
3711 * something else, such as an object in the case of pouchdb-find.
3712 * reducer: function (reduceFunDef)
3713 * Ditto, but for reducing. Modules don't have to support reducing
3714 * (e.g. pouchdb-find).
3715 * ddocValidator: function (ddoc, viewName)
3716 * Throws an error if the ddoc or viewName is not valid.
3717 * This could be a way to communicate to the user that the configuration for the
3718 * indexer is invalid.
3719 */
3720function createAbstractMapReduce(localDocName, mapper, reducer, ddocValidator) {
3721
3722 function tryMap(db, fun, doc) {
3723 // emit an event if there was an error thrown by a map function.
3724 // putting try/catches in a single function also avoids deoptimizations.
3725 try {
3726 fun(doc);
3727 } catch (e) {
3728 emitError(db, e);
3729 }
3730 }
3731
3732 function tryReduce(db, fun, keys, values, rereduce) {
3733 // same as above, but returning the result or an error. there are two separate
3734 // functions to avoid extra memory allocations since the tryCode() case is used
3735 // for custom map functions (common) vs this function, which is only used for
3736 // custom reduce functions (rare)
3737 try {
3738 return {output : fun(keys, values, rereduce)};
3739 } catch (e) {
3740 emitError(db, e);
3741 return {error: e};
3742 }
3743 }
3744
3745 function sortByKeyThenValue(x, y) {
3746 var keyCompare = collate(x.key, y.key);
3747 return keyCompare !== 0 ? keyCompare : collate(x.value, y.value);
3748 }
3749
3750 function sliceResults(results, limit, skip) {
3751 skip = skip || 0;
3752 if (typeof limit === 'number') {
3753 return results.slice(skip, limit + skip);
3754 } else if (skip > 0) {
3755 return results.slice(skip);
3756 }
3757 return results;
3758 }
3759
3760 function rowToDocId(row) {
3761 var val = row.value;
3762 // Users can explicitly specify a joined doc _id, or it
3763 // defaults to the doc _id that emitted the key/value.
3764 var docId = (val && typeof val === 'object' && val._id) || row.id;
3765 return docId;
3766 }
3767
3768 function readAttachmentsAsBlobOrBuffer(res) {
3769 res.rows.forEach(function (row) {
3770 var atts = row.doc && row.doc._attachments;
3771 if (!atts) {
3772 return;
3773 }
3774 Object.keys(atts).forEach(function (filename) {
3775 var att = atts[filename];
3776 atts[filename].data = b64ToBluffer(att.data, att.content_type);
3777 });
3778 });
3779 }
3780
3781 function postprocessAttachments(opts) {
3782 return function (res) {
3783 if (opts.include_docs && opts.attachments && opts.binary) {
3784 readAttachmentsAsBlobOrBuffer(res);
3785 }
3786 return res;
3787 };
3788 }
3789
3790 function addHttpParam(paramName, opts, params, asJson) {
3791 // add an http param from opts to params, optionally json-encoded
3792 var val = opts[paramName];
3793 if (typeof val !== 'undefined') {
3794 if (asJson) {
3795 val = encodeURIComponent(JSON.stringify(val));
3796 }
3797 params.push(paramName + '=' + val);
3798 }
3799 }
3800
3801 function coerceInteger(integerCandidate) {
3802 if (typeof integerCandidate !== 'undefined') {
3803 var asNumber = Number(integerCandidate);
3804 // prevents e.g. '1foo' or '1.1' being coerced to 1
3805 if (!isNaN(asNumber) && asNumber === parseInt(integerCandidate, 10)) {
3806 return asNumber;
3807 } else {
3808 return integerCandidate;
3809 }
3810 }
3811 }
3812
3813 function coerceOptions(opts) {
3814 opts.group_level = coerceInteger(opts.group_level);
3815 opts.limit = coerceInteger(opts.limit);
3816 opts.skip = coerceInteger(opts.skip);
3817 return opts;
3818 }
3819
3820 function checkPositiveInteger(number) {
3821 if (number) {
3822 if (typeof number !== 'number') {
3823 return new QueryParseError('Invalid value for integer: "' +
3824 number + '"');
3825 }
3826 if (number < 0) {
3827 return new QueryParseError('Invalid value for positive integer: ' +
3828 '"' + number + '"');
3829 }
3830 }
3831 }
3832
3833 function checkQueryParseError(options, fun) {
3834 var startkeyName = options.descending ? 'endkey' : 'startkey';
3835 var endkeyName = options.descending ? 'startkey' : 'endkey';
3836
3837 if (typeof options[startkeyName] !== 'undefined' &&
3838 typeof options[endkeyName] !== 'undefined' &&
3839 collate(options[startkeyName], options[endkeyName]) > 0) {
3840 throw new QueryParseError('No rows can match your key range, ' +
3841 'reverse your start_key and end_key or set {descending : true}');
3842 } else if (fun.reduce && options.reduce !== false) {
3843 if (options.include_docs) {
3844 throw new QueryParseError('{include_docs:true} is invalid for reduce');
3845 } else if (options.keys && options.keys.length > 1 &&
3846 !options.group && !options.group_level) {
3847 throw new QueryParseError('Multi-key fetches for reduce views must use ' +
3848 '{group: true}');
3849 }
3850 }
3851 ['group_level', 'limit', 'skip'].forEach(function (optionName) {
3852 var error = checkPositiveInteger(options[optionName]);
3853 if (error) {
3854 throw error;
3855 }
3856 });
3857 }
3858
3859 function httpQuery(db, fun, opts) {
3860 // List of parameters to add to the PUT request
3861 var params = [];
3862 var body;
3863 var method = 'GET';
3864 var ok, status;
3865
3866 // If opts.reduce exists and is defined, then add it to the list
3867 // of parameters.
3868 // If reduce=false then the results are that of only the map function
3869 // not the final result of map and reduce.
3870 addHttpParam('reduce', opts, params);
3871 addHttpParam('include_docs', opts, params);
3872 addHttpParam('attachments', opts, params);
3873 addHttpParam('limit', opts, params);
3874 addHttpParam('descending', opts, params);
3875 addHttpParam('group', opts, params);
3876 addHttpParam('group_level', opts, params);
3877 addHttpParam('skip', opts, params);
3878 addHttpParam('stale', opts, params);
3879 addHttpParam('conflicts', opts, params);
3880 addHttpParam('startkey', opts, params, true);
3881 addHttpParam('start_key', opts, params, true);
3882 addHttpParam('endkey', opts, params, true);
3883 addHttpParam('end_key', opts, params, true);
3884 addHttpParam('inclusive_end', opts, params);
3885 addHttpParam('key', opts, params, true);
3886 addHttpParam('update_seq', opts, params);
3887
3888 // Format the list of parameters into a valid URI query string
3889 params = params.join('&');
3890 params = params === '' ? '' : '?' + params;
3891
3892 // If keys are supplied, issue a POST to circumvent GET query string limits
3893 // see http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
3894 if (typeof opts.keys !== 'undefined') {
3895 var MAX_URL_LENGTH = 2000;
3896 // according to http://stackoverflow.com/a/417184/680742,
3897 // the de facto URL length limit is 2000 characters
3898
3899 var keysAsString =
3900 'keys=' + encodeURIComponent(JSON.stringify(opts.keys));
3901 if (keysAsString.length + params.length + 1 <= MAX_URL_LENGTH) {
3902 // If the keys are short enough, do a GET. we do this to work around
3903 // Safari not understanding 304s on POSTs (see pouchdb/pouchdb#1239)
3904 params += (params[0] === '?' ? '&' : '?') + keysAsString;
3905 } else {
3906 method = 'POST';
3907 if (typeof fun === 'string') {
3908 body = {keys: opts.keys};
3909 } else { // fun is {map : mapfun}, so append to this
3910 fun.keys = opts.keys;
3911 }
3912 }
3913 }
3914
3915 // We are referencing a query defined in the design doc
3916 if (typeof fun === 'string') {
3917 var parts = parseViewName(fun);
3918 return db.fetch('_design/' + parts[0] + '/_view/' + parts[1] + params, {
3919 headers: new h({'Content-Type': 'application/json'}),
3920 method: method,
3921 body: JSON.stringify(body)
3922 }).then(function (response) {
3923 ok = response.ok;
3924 status = response.status;
3925 return response.json();
3926 }).then(function (result) {
3927 if (!ok) {
3928 result.status = status;
3929 throw generateErrorFromResponse(result);
3930 }
3931 // fail the entire request if the result contains an error
3932 result.rows.forEach(function (row) {
3933 /* istanbul ignore if */
3934 if (row.value && row.value.error && row.value.error === "builtin_reduce_error") {
3935 throw new Error(row.reason);
3936 }
3937 });
3938 return result;
3939 }).then(postprocessAttachments(opts));
3940 }
3941
3942 // We are using a temporary view, terrible for performance, good for testing
3943 body = body || {};
3944 Object.keys(fun).forEach(function (key) {
3945 if (Array.isArray(fun[key])) {
3946 body[key] = fun[key];
3947 } else {
3948 body[key] = fun[key].toString();
3949 }
3950 });
3951
3952 return db.fetch('_temp_view' + params, {
3953 headers: new h({'Content-Type': 'application/json'}),
3954 method: 'POST',
3955 body: JSON.stringify(body)
3956 }).then(function (response) {
3957 ok = response.ok;
3958 status = response.status;
3959 return response.json();
3960 }).then(function (result) {
3961 if (!ok) {
3962 result.status = status;
3963 throw generateErrorFromResponse(result);
3964 }
3965 return result;
3966 }).then(postprocessAttachments(opts));
3967 }
3968
3969 // custom adapters can define their own api._query
3970 // and override the default behavior
3971 /* istanbul ignore next */
3972 function customQuery(db, fun, opts) {
3973 return new Promise(function (resolve, reject) {
3974 db._query(fun, opts, function (err, res) {
3975 if (err) {
3976 return reject(err);
3977 }
3978 resolve(res);
3979 });
3980 });
3981 }
3982
3983 // custom adapters can define their own api._viewCleanup
3984 // and override the default behavior
3985 /* istanbul ignore next */
3986 function customViewCleanup(db) {
3987 return new Promise(function (resolve, reject) {
3988 db._viewCleanup(function (err, res) {
3989 if (err) {
3990 return reject(err);
3991 }
3992 resolve(res);
3993 });
3994 });
3995 }
3996
3997 function defaultsTo(value) {
3998 return function (reason) {
3999 /* istanbul ignore else */
4000 if (reason.status === 404) {
4001 return value;
4002 } else {
4003 throw reason;
4004 }
4005 };
4006 }
4007
4008 // returns a promise for a list of docs to update, based on the input docId.
4009 // the order doesn't matter, because post-3.2.0, bulkDocs
4010 // is an atomic operation in all three adapters.
4011 function getDocsToPersist(docId, view, docIdsToChangesAndEmits) {
4012 var metaDocId = '_local/doc_' + docId;
4013 var defaultMetaDoc = {_id: metaDocId, keys: []};
4014 var docData = docIdsToChangesAndEmits.get(docId);
4015 var indexableKeysToKeyValues = docData[0];
4016 var changes = docData[1];
4017
4018 function getMetaDoc() {
4019 if (isGenOne(changes)) {
4020 // generation 1, so we can safely assume initial state
4021 // for performance reasons (avoids unnecessary GETs)
4022 return Promise.resolve(defaultMetaDoc);
4023 }
4024 return view.db.get(metaDocId)["catch"](defaultsTo(defaultMetaDoc));
4025 }
4026
4027 function getKeyValueDocs(metaDoc) {
4028 if (!metaDoc.keys.length) {
4029 // no keys, no need for a lookup
4030 return Promise.resolve({rows: []});
4031 }
4032 return view.db.allDocs({
4033 keys: metaDoc.keys,
4034 include_docs: true
4035 });
4036 }
4037
4038 function processKeyValueDocs(metaDoc, kvDocsRes) {
4039 var kvDocs = [];
4040 var oldKeys = new ExportedSet();
4041
4042 for (var i = 0, len = kvDocsRes.rows.length; i < len; i++) {
4043 var row = kvDocsRes.rows[i];
4044 var doc = row.doc;
4045 if (!doc) { // deleted
4046 continue;
4047 }
4048 kvDocs.push(doc);
4049 oldKeys.add(doc._id);
4050 doc._deleted = !indexableKeysToKeyValues.has(doc._id);
4051 if (!doc._deleted) {
4052 var keyValue = indexableKeysToKeyValues.get(doc._id);
4053 if ('value' in keyValue) {
4054 doc.value = keyValue.value;
4055 }
4056 }
4057 }
4058 var newKeys = mapToKeysArray(indexableKeysToKeyValues);
4059 newKeys.forEach(function (key) {
4060 if (!oldKeys.has(key)) {
4061 // new doc
4062 var kvDoc = {
4063 _id: key
4064 };
4065 var keyValue = indexableKeysToKeyValues.get(key);
4066 if ('value' in keyValue) {
4067 kvDoc.value = keyValue.value;
4068 }
4069 kvDocs.push(kvDoc);
4070 }
4071 });
4072 metaDoc.keys = uniq$1(newKeys.concat(metaDoc.keys));
4073 kvDocs.push(metaDoc);
4074
4075 return kvDocs;
4076 }
4077
4078 return getMetaDoc().then(function (metaDoc) {
4079 return getKeyValueDocs(metaDoc).then(function (kvDocsRes) {
4080 return processKeyValueDocs(metaDoc, kvDocsRes);
4081 });
4082 });
4083 }
4084
4085 // updates all emitted key/value docs and metaDocs in the mrview database
4086 // for the given batch of documents from the source database
4087 function saveKeyValues(view, docIdsToChangesAndEmits, seq) {
4088 var seqDocId = '_local/lastSeq';
4089 return view.db.get(seqDocId)[
4090 "catch"](defaultsTo({_id: seqDocId, seq: 0}))
4091 .then(function (lastSeqDoc) {
4092 var docIds = mapToKeysArray(docIdsToChangesAndEmits);
4093 return Promise.all(docIds.map(function (docId) {
4094 return getDocsToPersist(docId, view, docIdsToChangesAndEmits);
4095 })).then(function (listOfDocsToPersist) {
4096 var docsToPersist = flatten(listOfDocsToPersist);
4097 lastSeqDoc.seq = seq;
4098 docsToPersist.push(lastSeqDoc);
4099 // write all docs in a single operation, update the seq once
4100 return view.db.bulkDocs({docs : docsToPersist});
4101 });
4102 });
4103 }
4104
4105 function getQueue(view) {
4106 var viewName = typeof view === 'string' ? view : view.name;
4107 var queue = persistentQueues[viewName];
4108 if (!queue) {
4109 queue = persistentQueues[viewName] = new TaskQueue();
4110 }
4111 return queue;
4112 }
4113
4114 function updateView(view) {
4115 return sequentialize(getQueue(view), function () {
4116 return updateViewInQueue(view);
4117 })();
4118 }
4119
4120 function updateViewInQueue(view) {
4121 // bind the emit function once
4122 var mapResults;
4123 var doc;
4124
4125 function emit(key, value) {
4126 var output = {id: doc._id, key: normalizeKey(key)};
4127 // Don't explicitly store the value unless it's defined and non-null.
4128 // This saves on storage space, because often people don't use it.
4129 if (typeof value !== 'undefined' && value !== null) {
4130 output.value = normalizeKey(value);
4131 }
4132 mapResults.push(output);
4133 }
4134
4135 var mapFun = mapper(view.mapFun, emit);
4136
4137 var currentSeq = view.seq || 0;
4138
4139 function processChange(docIdsToChangesAndEmits, seq) {
4140 return function () {
4141 return saveKeyValues(view, docIdsToChangesAndEmits, seq);
4142 };
4143 }
4144
4145 var queue = new TaskQueue();
4146
4147 function processNextBatch() {
4148 return view.sourceDB.changes({
4149 return_docs: true,
4150 conflicts: true,
4151 include_docs: true,
4152 style: 'all_docs',
4153 since: currentSeq,
4154 limit: CHANGES_BATCH_SIZE
4155 }).then(processBatch);
4156 }
4157
4158 function processBatch(response) {
4159 var results = response.results;
4160 if (!results.length) {
4161 return;
4162 }
4163 var docIdsToChangesAndEmits = createDocIdsToChangesAndEmits(results);
4164 queue.add(processChange(docIdsToChangesAndEmits, currentSeq));
4165 if (results.length < CHANGES_BATCH_SIZE) {
4166 return;
4167 }
4168 return processNextBatch();
4169 }
4170
4171 function createDocIdsToChangesAndEmits(results) {
4172 var docIdsToChangesAndEmits = new ExportedMap();
4173 for (var i = 0, len = results.length; i < len; i++) {
4174 var change = results[i];
4175 if (change.doc._id[0] !== '_') {
4176 mapResults = [];
4177 doc = change.doc;
4178
4179 if (!doc._deleted) {
4180 tryMap(view.sourceDB, mapFun, doc);
4181 }
4182 mapResults.sort(sortByKeyThenValue);
4183
4184 var indexableKeysToKeyValues = createIndexableKeysToKeyValues(mapResults);
4185 docIdsToChangesAndEmits.set(change.doc._id, [
4186 indexableKeysToKeyValues,
4187 change.changes
4188 ]);
4189 }
4190 currentSeq = change.seq;
4191 }
4192 return docIdsToChangesAndEmits;
4193 }
4194
4195 function createIndexableKeysToKeyValues(mapResults) {
4196 var indexableKeysToKeyValues = new ExportedMap();
4197 var lastKey;
4198 for (var i = 0, len = mapResults.length; i < len; i++) {
4199 var emittedKeyValue = mapResults[i];
4200 var complexKey = [emittedKeyValue.key, emittedKeyValue.id];
4201 if (i > 0 && collate(emittedKeyValue.key, lastKey) === 0) {
4202 complexKey.push(i); // dup key+id, so make it unique
4203 }
4204 indexableKeysToKeyValues.set(toIndexableString(complexKey), emittedKeyValue);
4205 lastKey = emittedKeyValue.key;
4206 }
4207 return indexableKeysToKeyValues;
4208 }
4209
4210 return processNextBatch().then(function () {
4211 return queue.finish();
4212 }).then(function () {
4213 view.seq = currentSeq;
4214 });
4215 }
4216
4217 function reduceView(view, results, options) {
4218 if (options.group_level === 0) {
4219 delete options.group_level;
4220 }
4221
4222 var shouldGroup = options.group || options.group_level;
4223
4224 var reduceFun = reducer(view.reduceFun);
4225
4226 var groups = [];
4227 var lvl = isNaN(options.group_level) ? Number.POSITIVE_INFINITY :
4228 options.group_level;
4229 results.forEach(function (e) {
4230 var last = groups[groups.length - 1];
4231 var groupKey = shouldGroup ? e.key : null;
4232
4233 // only set group_level for array keys
4234 if (shouldGroup && Array.isArray(groupKey)) {
4235 groupKey = groupKey.slice(0, lvl);
4236 }
4237
4238 if (last && collate(last.groupKey, groupKey) === 0) {
4239 last.keys.push([e.key, e.id]);
4240 last.values.push(e.value);
4241 return;
4242 }
4243 groups.push({
4244 keys: [[e.key, e.id]],
4245 values: [e.value],
4246 groupKey: groupKey
4247 });
4248 });
4249 results = [];
4250 for (var i = 0, len = groups.length; i < len; i++) {
4251 var e = groups[i];
4252 var reduceTry = tryReduce(view.sourceDB, reduceFun, e.keys, e.values, false);
4253 if (reduceTry.error && reduceTry.error instanceof BuiltInError) {
4254 // CouchDB returns an error if a built-in errors out
4255 throw reduceTry.error;
4256 }
4257 results.push({
4258 // CouchDB just sets the value to null if a non-built-in errors out
4259 value: reduceTry.error ? null : reduceTry.output,
4260 key: e.groupKey
4261 });
4262 }
4263 // no total_rows/offset when reducing
4264 return {rows: sliceResults(results, options.limit, options.skip)};
4265 }
4266
4267 function queryView(view, opts) {
4268 return sequentialize(getQueue(view), function () {
4269 return queryViewInQueue(view, opts);
4270 })();
4271 }
4272
4273 function queryViewInQueue(view, opts) {
4274 var totalRows;
4275 var shouldReduce = view.reduceFun && opts.reduce !== false;
4276 var skip = opts.skip || 0;
4277 if (typeof opts.keys !== 'undefined' && !opts.keys.length) {
4278 // equivalent query
4279 opts.limit = 0;
4280 delete opts.keys;
4281 }
4282
4283 function fetchFromView(viewOpts) {
4284 viewOpts.include_docs = true;
4285 return view.db.allDocs(viewOpts).then(function (res) {
4286 totalRows = res.total_rows;
4287 return res.rows.map(function (result) {
4288
4289 // implicit migration - in older versions of PouchDB,
4290 // we explicitly stored the doc as {id: ..., key: ..., value: ...}
4291 // this is tested in a migration test
4292 /* istanbul ignore next */
4293 if ('value' in result.doc && typeof result.doc.value === 'object' &&
4294 result.doc.value !== null) {
4295 var keys = Object.keys(result.doc.value).sort();
4296 // this detection method is not perfect, but it's unlikely the user
4297 // emitted a value which was an object with these 3 exact keys
4298 var expectedKeys = ['id', 'key', 'value'];
4299 if (!(keys < expectedKeys || keys > expectedKeys)) {
4300 return result.doc.value;
4301 }
4302 }
4303
4304 var parsedKeyAndDocId = parseIndexableString(result.doc._id);
4305 return {
4306 key: parsedKeyAndDocId[0],
4307 id: parsedKeyAndDocId[1],
4308 value: ('value' in result.doc ? result.doc.value : null)
4309 };
4310 });
4311 });
4312 }
4313
4314 function onMapResultsReady(rows) {
4315 var finalResults;
4316 if (shouldReduce) {
4317 finalResults = reduceView(view, rows, opts);
4318 } else {
4319 finalResults = {
4320 total_rows: totalRows,
4321 offset: skip,
4322 rows: rows
4323 };
4324 }
4325 /* istanbul ignore if */
4326 if (opts.update_seq) {
4327 finalResults.update_seq = view.seq;
4328 }
4329 if (opts.include_docs) {
4330 var docIds = uniq$1(rows.map(rowToDocId));
4331
4332 return view.sourceDB.allDocs({
4333 keys: docIds,
4334 include_docs: true,
4335 conflicts: opts.conflicts,
4336 attachments: opts.attachments,
4337 binary: opts.binary
4338 }).then(function (allDocsRes) {
4339 var docIdsToDocs = new ExportedMap();
4340 allDocsRes.rows.forEach(function (row) {
4341 docIdsToDocs.set(row.id, row.doc);
4342 });
4343 rows.forEach(function (row) {
4344 var docId = rowToDocId(row);
4345 var doc = docIdsToDocs.get(docId);
4346 if (doc) {
4347 row.doc = doc;
4348 }
4349 });
4350 return finalResults;
4351 });
4352 } else {
4353 return finalResults;
4354 }
4355 }
4356
4357 if (typeof opts.keys !== 'undefined') {
4358 var keys = opts.keys;
4359 var fetchPromises = keys.map(function (key) {
4360 var viewOpts = {
4361 startkey : toIndexableString([key]),
4362 endkey : toIndexableString([key, {}])
4363 };
4364 /* istanbul ignore if */
4365 if (opts.update_seq) {
4366 viewOpts.update_seq = true;
4367 }
4368 return fetchFromView(viewOpts);
4369 });
4370 return Promise.all(fetchPromises).then(flatten).then(onMapResultsReady);
4371 } else { // normal query, no 'keys'
4372 var viewOpts = {
4373 descending : opts.descending
4374 };
4375 /* istanbul ignore if */
4376 if (opts.update_seq) {
4377 viewOpts.update_seq = true;
4378 }
4379 var startkey;
4380 var endkey;
4381 if ('start_key' in opts) {
4382 startkey = opts.start_key;
4383 }
4384 if ('startkey' in opts) {
4385 startkey = opts.startkey;
4386 }
4387 if ('end_key' in opts) {
4388 endkey = opts.end_key;
4389 }
4390 if ('endkey' in opts) {
4391 endkey = opts.endkey;
4392 }
4393 if (typeof startkey !== 'undefined') {
4394 viewOpts.startkey = opts.descending ?
4395 toIndexableString([startkey, {}]) :
4396 toIndexableString([startkey]);
4397 }
4398 if (typeof endkey !== 'undefined') {
4399 var inclusiveEnd = opts.inclusive_end !== false;
4400 if (opts.descending) {
4401 inclusiveEnd = !inclusiveEnd;
4402 }
4403
4404 viewOpts.endkey = toIndexableString(
4405 inclusiveEnd ? [endkey, {}] : [endkey]);
4406 }
4407 if (typeof opts.key !== 'undefined') {
4408 var keyStart = toIndexableString([opts.key]);
4409 var keyEnd = toIndexableString([opts.key, {}]);
4410 if (viewOpts.descending) {
4411 viewOpts.endkey = keyStart;
4412 viewOpts.startkey = keyEnd;
4413 } else {
4414 viewOpts.startkey = keyStart;
4415 viewOpts.endkey = keyEnd;
4416 }
4417 }
4418 if (!shouldReduce) {
4419 if (typeof opts.limit === 'number') {
4420 viewOpts.limit = opts.limit;
4421 }
4422 viewOpts.skip = skip;
4423 }
4424 return fetchFromView(viewOpts).then(onMapResultsReady);
4425 }
4426 }
4427
4428 function httpViewCleanup(db) {
4429 return db.fetch('_view_cleanup', {
4430 headers: new h({'Content-Type': 'application/json'}),
4431 method: 'POST'
4432 }).then(function (response) {
4433 return response.json();
4434 });
4435 }
4436
4437 function localViewCleanup(db) {
4438 return db.get('_local/' + localDocName).then(function (metaDoc) {
4439 var docsToViews = new ExportedMap();
4440 Object.keys(metaDoc.views).forEach(function (fullViewName) {
4441 var parts = parseViewName(fullViewName);
4442 var designDocName = '_design/' + parts[0];
4443 var viewName = parts[1];
4444 var views = docsToViews.get(designDocName);
4445 if (!views) {
4446 views = new ExportedSet();
4447 docsToViews.set(designDocName, views);
4448 }
4449 views.add(viewName);
4450 });
4451 var opts = {
4452 keys : mapToKeysArray(docsToViews),
4453 include_docs : true
4454 };
4455 return db.allDocs(opts).then(function (res) {
4456 var viewsToStatus = {};
4457 res.rows.forEach(function (row) {
4458 var ddocName = row.key.substring(8); // cuts off '_design/'
4459 docsToViews.get(row.key).forEach(function (viewName) {
4460 var fullViewName = ddocName + '/' + viewName;
4461 /* istanbul ignore if */
4462 if (!metaDoc.views[fullViewName]) {
4463 // new format, without slashes, to support PouchDB 2.2.0
4464 // migration test in pouchdb's browser.migration.js verifies this
4465 fullViewName = viewName;
4466 }
4467 var viewDBNames = Object.keys(metaDoc.views[fullViewName]);
4468 // design doc deleted, or view function nonexistent
4469 var statusIsGood = row.doc && row.doc.views &&
4470 row.doc.views[viewName];
4471 viewDBNames.forEach(function (viewDBName) {
4472 viewsToStatus[viewDBName] =
4473 viewsToStatus[viewDBName] || statusIsGood;
4474 });
4475 });
4476 });
4477 var dbsToDelete = Object.keys(viewsToStatus).filter(
4478 function (viewDBName) { return !viewsToStatus[viewDBName]; });
4479 var destroyPromises = dbsToDelete.map(function (viewDBName) {
4480 return sequentialize(getQueue(viewDBName), function () {
4481 return new db.constructor(viewDBName, db.__opts).destroy();
4482 })();
4483 });
4484 return Promise.all(destroyPromises).then(function () {
4485 return {ok: true};
4486 });
4487 });
4488 }, defaultsTo({ok: true}));
4489 }
4490
4491 function queryPromised(db, fun, opts) {
4492 /* istanbul ignore next */
4493 if (typeof db._query === 'function') {
4494 return customQuery(db, fun, opts);
4495 }
4496 if (isRemote(db)) {
4497 return httpQuery(db, fun, opts);
4498 }
4499
4500 if (typeof fun !== 'string') {
4501 // temp_view
4502 checkQueryParseError(opts, fun);
4503
4504 tempViewQueue.add(function () {
4505 var createViewPromise = createView(
4506 /* sourceDB */ db,
4507 /* viewName */ 'temp_view/temp_view',
4508 /* mapFun */ fun.map,
4509 /* reduceFun */ fun.reduce,
4510 /* temporary */ true,
4511 /* localDocName */ localDocName);
4512 return createViewPromise.then(function (view) {
4513 return fin(updateView(view).then(function () {
4514 return queryView(view, opts);
4515 }), function () {
4516 return view.db.destroy();
4517 });
4518 });
4519 });
4520 return tempViewQueue.finish();
4521 } else {
4522 // persistent view
4523 var fullViewName = fun;
4524 var parts = parseViewName(fullViewName);
4525 var designDocName = parts[0];
4526 var viewName = parts[1];
4527 return db.get('_design/' + designDocName).then(function (doc) {
4528 var fun = doc.views && doc.views[viewName];
4529
4530 if (!fun) {
4531 // basic validator; it's assumed that every subclass would want this
4532 throw new NotFoundError('ddoc ' + doc._id + ' has no view named ' +
4533 viewName);
4534 }
4535
4536 ddocValidator(doc, viewName);
4537 checkQueryParseError(opts, fun);
4538
4539 var createViewPromise = createView(
4540 /* sourceDB */ db,
4541 /* viewName */ fullViewName,
4542 /* mapFun */ fun.map,
4543 /* reduceFun */ fun.reduce,
4544 /* temporary */ false,
4545 /* localDocName */ localDocName);
4546 return createViewPromise.then(function (view) {
4547 if (opts.stale === 'ok' || opts.stale === 'update_after') {
4548 if (opts.stale === 'update_after') {
4549 immediate(function () {
4550 updateView(view);
4551 });
4552 }
4553 return queryView(view, opts);
4554 } else { // stale not ok
4555 return updateView(view).then(function () {
4556 return queryView(view, opts);
4557 });
4558 }
4559 });
4560 });
4561 }
4562 }
4563
4564 function abstractQuery(fun, opts, callback) {
4565 var db = this;
4566 if (typeof opts === 'function') {
4567 callback = opts;
4568 opts = {};
4569 }
4570 opts = opts ? coerceOptions(opts) : {};
4571
4572 if (typeof fun === 'function') {
4573 fun = {map : fun};
4574 }
4575
4576 var promise = Promise.resolve().then(function () {
4577 return queryPromised(db, fun, opts);
4578 });
4579 promisedCallback$1(promise, callback);
4580 return promise;
4581 }
4582
4583 var abstractViewCleanup = callbackify$1(function () {
4584 var db = this;
4585 /* istanbul ignore next */
4586 if (typeof db._viewCleanup === 'function') {
4587 return customViewCleanup(db);
4588 }
4589 if (isRemote(db)) {
4590 return httpViewCleanup(db);
4591 }
4592 return localViewCleanup(db);
4593 });
4594
4595 return {
4596 query: abstractQuery,
4597 viewCleanup: abstractViewCleanup
4598 };
4599}
4600
4601//
4602// One thing about these mappers:
4603//
4604// Per the advice of John-David Dalton (http://youtu.be/NthmeLEhDDM),
4605// what you want to do in this case is optimize for the smallest possible
4606// function, since that's the thing that gets run over and over again.
4607//
4608// This code would be a lot simpler if all the if/elses were inside
4609// the function, but it would also be a lot less performant.
4610//
4611
4612
4613function createDeepMultiMapper(fields, emit) {
4614 return function (doc) {
4615 var toEmit = [];
4616 for (var i = 0, iLen = fields.length; i < iLen; i++) {
4617 var parsedField = parseField(fields[i]);
4618 var value = doc;
4619 for (var j = 0, jLen = parsedField.length; j < jLen; j++) {
4620 var key = parsedField[j];
4621 value = value[key];
4622 if (typeof value === 'undefined') {
4623 return; // don't emit
4624 }
4625 }
4626 toEmit.push(value);
4627 }
4628 emit(toEmit);
4629 };
4630}
4631
4632function createDeepSingleMapper(field, emit) {
4633 var parsedField = parseField(field);
4634 return function (doc) {
4635 var value = doc;
4636 for (var i = 0, len = parsedField.length; i < len; i++) {
4637 var key = parsedField[i];
4638 value = value[key];
4639 if (typeof value === 'undefined') {
4640 return; // do nothing
4641 }
4642 }
4643 emit(value);
4644 };
4645}
4646
4647function createShallowSingleMapper(field, emit) {
4648 return function (doc) {
4649 emit(doc[field]);
4650 };
4651}
4652
4653function createShallowMultiMapper(fields, emit) {
4654 return function (doc) {
4655 var toEmit = [];
4656 for (var i = 0, len = fields.length; i < len; i++) {
4657 toEmit.push(doc[fields[i]]);
4658 }
4659 emit(toEmit);
4660 };
4661}
4662
4663function checkShallow(fields) {
4664 for (var i = 0, len = fields.length; i < len; i++) {
4665 var field = fields[i];
4666 if (field.indexOf('.') !== -1) {
4667 return false;
4668 }
4669 }
4670 return true;
4671}
4672
4673function createMapper(fields, emit) {
4674 var isShallow = checkShallow(fields);
4675 var isSingle = fields.length === 1;
4676
4677 // notice we try to optimize for the most common case,
4678 // i.e. single shallow indexes
4679 if (isShallow) {
4680 if (isSingle) {
4681 return createShallowSingleMapper(fields[0], emit);
4682 } else { // multi
4683 return createShallowMultiMapper(fields, emit);
4684 }
4685 } else { // deep
4686 if (isSingle) {
4687 return createDeepSingleMapper(fields[0], emit);
4688 } else { // multi
4689 return createDeepMultiMapper(fields, emit);
4690 }
4691 }
4692}
4693
4694function mapper(mapFunDef, emit) {
4695 // mapFunDef is a list of fields
4696
4697 var fields = Object.keys(mapFunDef.fields);
4698
4699 return createMapper(fields, emit);
4700}
4701
4702/* istanbul ignore next */
4703function reducer(/*reduceFunDef*/) {
4704 throw new Error('reduce not supported');
4705}
4706
4707function ddocValidator(ddoc, viewName) {
4708 var view = ddoc.views[viewName];
4709 // This doesn't actually need to be here apparently, but
4710 // I feel safer keeping it.
4711 /* istanbul ignore if */
4712 if (!view.map || !view.map.fields) {
4713 throw new Error('ddoc ' + ddoc._id +' with view ' + viewName +
4714 ' doesn\'t have map.fields defined. ' +
4715 'maybe it wasn\'t created by this plugin?');
4716 }
4717}
4718
4719var abstractMapper = createAbstractMapReduce(
4720 /* localDocName */ 'indexes',
4721 mapper,
4722 reducer,
4723 ddocValidator
4724);
4725
4726function abstractMapper$1 (db) {
4727 return db._customFindAbstractMapper || abstractMapper;
4728}
4729
4730// normalize the "sort" value
4731function massageSort(sort) {
4732 if (!Array.isArray(sort)) {
4733 throw new Error('invalid sort json - should be an array');
4734 }
4735 return sort.map(function (sorting) {
4736 if (typeof sorting === 'string') {
4737 var obj = {};
4738 obj[sorting] = 'asc';
4739 return obj;
4740 } else {
4741 return sorting;
4742 }
4743 });
4744}
4745
4746function massageUseIndex(useIndex) {
4747 var cleanedUseIndex = [];
4748 if (typeof useIndex === 'string') {
4749 cleanedUseIndex.push(useIndex);
4750 } else {
4751 cleanedUseIndex = useIndex;
4752 }
4753
4754 return cleanedUseIndex.map(function (name) {
4755 return name.replace('_design/', '');
4756 });
4757}
4758
4759function massageIndexDef(indexDef) {
4760 indexDef.fields = indexDef.fields.map(function (field) {
4761 if (typeof field === 'string') {
4762 var obj = {};
4763 obj[field] = 'asc';
4764 return obj;
4765 }
4766 return field;
4767 });
4768 return indexDef;
4769}
4770
4771function getKeyFromDoc(doc, index) {
4772 var res = [];
4773 for (var i = 0; i < index.def.fields.length; i++) {
4774 var field = getKey(index.def.fields[i]);
4775 res.push(doc[field]);
4776 }
4777 return res;
4778}
4779
4780// have to do this manually because REASONS. I don't know why
4781// CouchDB didn't implement inclusive_start
4782function filterInclusiveStart(rows, targetValue, index) {
4783 var indexFields = index.def.fields;
4784 for (var i = 0, len = rows.length; i < len; i++) {
4785 var row = rows[i];
4786
4787 // shave off any docs at the beginning that are <= the
4788 // target value
4789
4790 var docKey = getKeyFromDoc(row.doc, index);
4791 if (indexFields.length === 1) {
4792 docKey = docKey[0]; // only one field, not multi-field
4793 } else { // more than one field in index
4794 // in the case where e.g. the user is searching {$gt: {a: 1}}
4795 // but the index is [a, b], then we need to shorten the doc key
4796 while (docKey.length > targetValue.length) {
4797 docKey.pop();
4798 }
4799 }
4800 //ABS as we just looking for values that don't match
4801 if (Math.abs(collate(docKey, targetValue)) > 0) {
4802 // no need to filter any further; we're past the key
4803 break;
4804 }
4805 }
4806 return i > 0 ? rows.slice(i) : rows;
4807}
4808
4809function reverseOptions(opts) {
4810 var newOpts = clone(opts);
4811 delete newOpts.startkey;
4812 delete newOpts.endkey;
4813 delete newOpts.inclusive_start;
4814 delete newOpts.inclusive_end;
4815
4816 if ('endkey' in opts) {
4817 newOpts.startkey = opts.endkey;
4818 }
4819 if ('startkey' in opts) {
4820 newOpts.endkey = opts.startkey;
4821 }
4822 if ('inclusive_start' in opts) {
4823 newOpts.inclusive_end = opts.inclusive_start;
4824 }
4825 if ('inclusive_end' in opts) {
4826 newOpts.inclusive_start = opts.inclusive_end;
4827 }
4828 return newOpts;
4829}
4830
4831function validateIndex(index) {
4832 var ascFields = index.fields.filter(function (field) {
4833 return getValue(field) === 'asc';
4834 });
4835 if (ascFields.length !== 0 && ascFields.length !== index.fields.length) {
4836 throw new Error('unsupported mixed sorting');
4837 }
4838}
4839
4840function validateSort(requestDef, index) {
4841 if (index.defaultUsed && requestDef.sort) {
4842 var noneIdSorts = requestDef.sort.filter(function (sortItem) {
4843 return Object.keys(sortItem)[0] !== '_id';
4844 }).map(function (sortItem) {
4845 return Object.keys(sortItem)[0];
4846 });
4847
4848 if (noneIdSorts.length > 0) {
4849 throw new Error('Cannot sort on field(s) "' + noneIdSorts.join(',') +
4850 '" when using the default index');
4851 }
4852 }
4853
4854 if (index.defaultUsed) {
4855 return;
4856 }
4857}
4858
4859function validateFindRequest(requestDef) {
4860 if (typeof requestDef.selector !== 'object') {
4861 throw new Error('you must provide a selector when you find()');
4862 }
4863
4864 /*var selectors = requestDef.selector['$and'] || [requestDef.selector];
4865 for (var i = 0; i < selectors.length; i++) {
4866 var selector = selectors[i];
4867 var keys = Object.keys(selector);
4868 if (keys.length === 0) {
4869 throw new Error('invalid empty selector');
4870 }
4871 //var selection = selector[keys[0]];
4872 /*if (Object.keys(selection).length !== 1) {
4873 throw new Error('invalid selector: ' + JSON.stringify(selection) +
4874 ' - it must have exactly one key/value');
4875 }
4876 }*/
4877}
4878
4879// determine the maximum number of fields
4880// we're going to need to query, e.g. if the user
4881// has selection ['a'] and sorting ['a', 'b'], then we
4882// need to use the longer of the two: ['a', 'b']
4883function getUserFields(selector, sort) {
4884 var selectorFields = Object.keys(selector);
4885 var sortFields = sort? sort.map(getKey) : [];
4886 var userFields;
4887 if (selectorFields.length >= sortFields.length) {
4888 userFields = selectorFields;
4889 } else {
4890 userFields = sortFields;
4891 }
4892
4893 if (sortFields.length === 0) {
4894 return {
4895 fields: userFields
4896 };
4897 }
4898
4899 // sort according to the user's preferred sorting
4900 userFields = userFields.sort(function (left, right) {
4901 var leftIdx = sortFields.indexOf(left);
4902 if (leftIdx === -1) {
4903 leftIdx = Number.MAX_VALUE;
4904 }
4905 var rightIdx = sortFields.indexOf(right);
4906 if (rightIdx === -1) {
4907 rightIdx = Number.MAX_VALUE;
4908 }
4909 return leftIdx < rightIdx ? -1 : leftIdx > rightIdx ? 1 : 0;
4910 });
4911
4912 return {
4913 fields: userFields,
4914 sortOrder: sort.map(getKey)
4915 };
4916}
4917
4918function createIndex$1(db, requestDef) {
4919 requestDef = massageCreateIndexRequest(requestDef);
4920 var originalIndexDef = clone(requestDef.index);
4921 requestDef.index = massageIndexDef(requestDef.index);
4922
4923 validateIndex(requestDef.index);
4924
4925 // calculating md5 is expensive - memoize and only
4926 // run if required
4927 var md5;
4928 function getMd5() {
4929 return md5 || (md5 = stringMd5(JSON.stringify(requestDef)));
4930 }
4931
4932 var viewName = requestDef.name || ('idx-' + getMd5());
4933
4934 var ddocName = requestDef.ddoc || ('idx-' + getMd5());
4935 var ddocId = '_design/' + ddocName;
4936
4937 var hasInvalidLanguage = false;
4938 var viewExists = false;
4939
4940 function updateDdoc(doc) {
4941 if (doc._rev && doc.language !== 'query') {
4942 hasInvalidLanguage = true;
4943 }
4944 doc.language = 'query';
4945 doc.views = doc.views || {};
4946
4947 viewExists = !!doc.views[viewName];
4948
4949 if (viewExists) {
4950 return false;
4951 }
4952
4953 doc.views[viewName] = {
4954 map: {
4955 fields: mergeObjects(requestDef.index.fields)
4956 },
4957 reduce: '_count',
4958 options: {
4959 def: originalIndexDef
4960 }
4961 };
4962
4963 return doc;
4964 }
4965
4966 db.constructor.emit('debug', ['find', 'creating index', ddocId]);
4967
4968 return upsert(db, ddocId, updateDdoc).then(function () {
4969 if (hasInvalidLanguage) {
4970 throw new Error('invalid language for ddoc with id "' +
4971 ddocId +
4972 '" (should be "query")');
4973 }
4974 }).then(function () {
4975 // kick off a build
4976 // TODO: abstract-pouchdb-mapreduce should support auto-updating
4977 // TODO: should also use update_after, but pouchdb/pouchdb#3415 blocks me
4978 var signature = ddocName + '/' + viewName;
4979 return abstractMapper$1(db).query.call(db, signature, {
4980 limit: 0,
4981 reduce: false
4982 }).then(function () {
4983 return {
4984 id: ddocId,
4985 name: viewName,
4986 result: viewExists ? 'exists' : 'created'
4987 };
4988 });
4989 });
4990}
4991
4992function getIndexes$1(db) {
4993 // just search through all the design docs and filter in-memory.
4994 // hopefully there aren't that many ddocs.
4995 return db.allDocs({
4996 startkey: '_design/',
4997 endkey: '_design/\uffff',
4998 include_docs: true
4999 }).then(function (allDocsRes) {
5000 var res = {
5001 indexes: [{
5002 ddoc: null,
5003 name: '_all_docs',
5004 type: 'special',
5005 def: {
5006 fields: [{_id: 'asc'}]
5007 }
5008 }]
5009 };
5010
5011 res.indexes = flatten$1(res.indexes, allDocsRes.rows.filter(function (row) {
5012 return row.doc.language === 'query';
5013 }).map(function (row) {
5014 var viewNames = row.doc.views !== undefined ? Object.keys(row.doc.views) : [];
5015
5016 return viewNames.map(function (viewName) {
5017 var view = row.doc.views[viewName];
5018 return {
5019 ddoc: row.id,
5020 name: viewName,
5021 type: 'json',
5022 def: massageIndexDef(view.options.def)
5023 };
5024 });
5025 }));
5026
5027 // these are sorted by view name for some reason
5028 res.indexes.sort(function (left, right) {
5029 return compare(left.name, right.name);
5030 });
5031 res.total_rows = res.indexes.length;
5032 return res;
5033 });
5034}
5035
5036// couchdb lowest collation value
5037var COLLATE_LO = null;
5038
5039// couchdb highest collation value (TODO: well not really, but close enough amirite)
5040var COLLATE_HI = {"\uffff": {}};
5041
5042// couchdb second-lowest collation value
5043
5044function checkFieldInIndex(index, field) {
5045 var indexFields = index.def.fields.map(getKey);
5046 for (var i = 0, len = indexFields.length; i < len; i++) {
5047 var indexField = indexFields[i];
5048 if (field === indexField) {
5049 return true;
5050 }
5051 }
5052 return false;
5053}
5054
5055// so when you do e.g. $eq/$eq, we can do it entirely in the database.
5056// but when you do e.g. $gt/$eq, the first part can be done
5057// in the database, but the second part has to be done in-memory,
5058// because $gt has forced us to lose precision.
5059// so that's what this determines
5060function userOperatorLosesPrecision(selector, field) {
5061 var matcher = selector[field];
5062 var userOperator = getKey(matcher);
5063
5064 return userOperator !== '$eq';
5065}
5066
5067// sort the user fields by their position in the index,
5068// if they're in the index
5069function sortFieldsByIndex(userFields, index) {
5070 var indexFields = index.def.fields.map(getKey);
5071
5072 return userFields.slice().sort(function (a, b) {
5073 var aIdx = indexFields.indexOf(a);
5074 var bIdx = indexFields.indexOf(b);
5075 if (aIdx === -1) {
5076 aIdx = Number.MAX_VALUE;
5077 }
5078 if (bIdx === -1) {
5079 bIdx = Number.MAX_VALUE;
5080 }
5081 return compare(aIdx, bIdx);
5082 });
5083}
5084
5085// first pass to try to find fields that will need to be sorted in-memory
5086function getBasicInMemoryFields(index, selector, userFields) {
5087
5088 userFields = sortFieldsByIndex(userFields, index);
5089
5090 // check if any of the user selectors lose precision
5091 var needToFilterInMemory = false;
5092 for (var i = 0, len = userFields.length; i < len; i++) {
5093 var field = userFields[i];
5094 if (needToFilterInMemory || !checkFieldInIndex(index, field)) {
5095 return userFields.slice(i);
5096 }
5097 if (i < len - 1 && userOperatorLosesPrecision(selector, field)) {
5098 needToFilterInMemory = true;
5099 }
5100 }
5101 return [];
5102}
5103
5104function getInMemoryFieldsFromNe(selector) {
5105 var fields = [];
5106 Object.keys(selector).forEach(function (field) {
5107 var matcher = selector[field];
5108 Object.keys(matcher).forEach(function (operator) {
5109 if (operator === '$ne') {
5110 fields.push(field);
5111 }
5112 });
5113 });
5114 return fields;
5115}
5116
5117function getInMemoryFields(coreInMemoryFields, index, selector, userFields) {
5118 var result = flatten$1(
5119 // in-memory fields reported as necessary by the query planner
5120 coreInMemoryFields,
5121 // combine with another pass that checks for any we may have missed
5122 getBasicInMemoryFields(index, selector, userFields),
5123 // combine with another pass that checks for $ne's
5124 getInMemoryFieldsFromNe(selector)
5125 );
5126
5127 return sortFieldsByIndex(uniq(result), index);
5128}
5129
5130// check that at least one field in the user's query is represented
5131// in the index. order matters in the case of sorts
5132function checkIndexFieldsMatch(indexFields, sortOrder, fields) {
5133 if (sortOrder) {
5134 // array has to be a strict subarray of index array. furthermore,
5135 // the sortOrder fields need to all be represented in the index
5136 var sortMatches = oneArrayIsStrictSubArrayOfOther(sortOrder, indexFields);
5137 var selectorMatches = oneArrayIsSubArrayOfOther(fields, indexFields);
5138
5139 return sortMatches && selectorMatches;
5140 }
5141
5142 // all of the user's specified fields still need to be
5143 // on the left side of the index array, although the order
5144 // doesn't matter
5145 return oneSetIsSubArrayOfOther(fields, indexFields);
5146}
5147
5148var logicalMatchers = ['$eq', '$gt', '$gte', '$lt', '$lte'];
5149function isNonLogicalMatcher(matcher) {
5150 return logicalMatchers.indexOf(matcher) === -1;
5151}
5152
5153// check all the index fields for usages of '$ne'
5154// e.g. if the user queries {foo: {$ne: 'foo'}, bar: {$eq: 'bar'}},
5155// then we can neither use an index on ['foo'] nor an index on
5156// ['foo', 'bar'], but we can use an index on ['bar'] or ['bar', 'foo']
5157function checkFieldsLogicallySound(indexFields, selector) {
5158 var firstField = indexFields[0];
5159 var matcher = selector[firstField];
5160
5161 if (typeof matcher === 'undefined') {
5162 /* istanbul ignore next */
5163 return true;
5164 }
5165
5166 var isInvalidNe = Object.keys(matcher).length === 1 &&
5167 getKey(matcher) === '$ne';
5168
5169 return !isInvalidNe;
5170}
5171
5172function checkIndexMatches(index, sortOrder, fields, selector) {
5173
5174 var indexFields = index.def.fields.map(getKey);
5175
5176 var fieldsMatch = checkIndexFieldsMatch(indexFields, sortOrder, fields);
5177
5178 if (!fieldsMatch) {
5179 return false;
5180 }
5181
5182 return checkFieldsLogicallySound(indexFields, selector);
5183}
5184
5185//
5186// the algorithm is very simple:
5187// take all the fields the user supplies, and if those fields
5188// are a strict subset of the fields in some index,
5189// then use that index
5190//
5191//
5192function findMatchingIndexes(selector, userFields, sortOrder, indexes) {
5193
5194 return indexes.reduce(function (res, index) {
5195 var indexMatches = checkIndexMatches(index, sortOrder, userFields, selector);
5196 if (indexMatches) {
5197 res.push(index);
5198 }
5199 return res;
5200 }, []);
5201}
5202
5203// find the best index, i.e. the one that matches the most fields
5204// in the user's query
5205function findBestMatchingIndex(selector, userFields, sortOrder, indexes, useIndex) {
5206
5207 var matchingIndexes = findMatchingIndexes(selector, userFields, sortOrder, indexes);
5208
5209 if (matchingIndexes.length === 0) {
5210 if (useIndex) {
5211 throw {
5212 error: "no_usable_index",
5213 message: "There is no index available for this selector."
5214 };
5215 }
5216 //return `all_docs` as a default index;
5217 //I'm assuming that _all_docs is always first
5218 var defaultIndex = indexes[0];
5219 defaultIndex.defaultUsed = true;
5220 return defaultIndex;
5221 }
5222 if (matchingIndexes.length === 1 && !useIndex) {
5223 return matchingIndexes[0];
5224 }
5225
5226 var userFieldsMap = arrayToObject(userFields);
5227
5228 function scoreIndex(index) {
5229 var indexFields = index.def.fields.map(getKey);
5230 var score = 0;
5231 for (var i = 0, len = indexFields.length; i < len; i++) {
5232 var indexField = indexFields[i];
5233 if (userFieldsMap[indexField]) {
5234 score++;
5235 }
5236 }
5237 return score;
5238 }
5239
5240 if (useIndex) {
5241 var useIndexDdoc = '_design/' + useIndex[0];
5242 var useIndexName = useIndex.length === 2 ? useIndex[1] : false;
5243 var index = matchingIndexes.find(function (index) {
5244 if (useIndexName && index.ddoc === useIndexDdoc && useIndexName === index.name) {
5245 return true;
5246 }
5247
5248 if (index.ddoc === useIndexDdoc) {
5249 /* istanbul ignore next */
5250 return true;
5251 }
5252
5253 return false;
5254 });
5255
5256 if (!index) {
5257 throw {
5258 error: "unknown_error",
5259 message: "Could not find that index or could not use that index for the query"
5260 };
5261 }
5262 return index;
5263 }
5264
5265 return max(matchingIndexes, scoreIndex);
5266}
5267
5268function getSingleFieldQueryOptsFor(userOperator, userValue) {
5269 switch (userOperator) {
5270 case '$eq':
5271 return {key: userValue};
5272 case '$lte':
5273 return {endkey: userValue};
5274 case '$gte':
5275 return {startkey: userValue};
5276 case '$lt':
5277 return {
5278 endkey: userValue,
5279 inclusive_end: false
5280 };
5281 case '$gt':
5282 return {
5283 startkey: userValue,
5284 inclusive_start: false
5285 };
5286 }
5287
5288 return {
5289 startkey: COLLATE_LO
5290 };
5291}
5292
5293function getSingleFieldCoreQueryPlan(selector, index) {
5294 var field = getKey(index.def.fields[0]);
5295 //ignoring this because the test to exercise the branch is skipped at the moment
5296 /* istanbul ignore next */
5297 var matcher = selector[field] || {};
5298 var inMemoryFields = [];
5299
5300 var userOperators = Object.keys(matcher);
5301
5302 var combinedOpts;
5303
5304 userOperators.forEach(function (userOperator) {
5305
5306 if (isNonLogicalMatcher(userOperator)) {
5307 inMemoryFields.push(field);
5308 }
5309
5310 var userValue = matcher[userOperator];
5311
5312 var newQueryOpts = getSingleFieldQueryOptsFor(userOperator, userValue);
5313
5314 if (combinedOpts) {
5315 combinedOpts = mergeObjects([combinedOpts, newQueryOpts]);
5316 } else {
5317 combinedOpts = newQueryOpts;
5318 }
5319 });
5320
5321 return {
5322 queryOpts: combinedOpts,
5323 inMemoryFields: inMemoryFields
5324 };
5325}
5326
5327function getMultiFieldCoreQueryPlan(userOperator, userValue) {
5328 switch (userOperator) {
5329 case '$eq':
5330 return {
5331 startkey: userValue,
5332 endkey: userValue
5333 };
5334 case '$lte':
5335 return {
5336 endkey: userValue
5337 };
5338 case '$gte':
5339 return {
5340 startkey: userValue
5341 };
5342 case '$lt':
5343 return {
5344 endkey: userValue,
5345 inclusive_end: false
5346 };
5347 case '$gt':
5348 return {
5349 startkey: userValue,
5350 inclusive_start: false
5351 };
5352 }
5353}
5354
5355function getMultiFieldQueryOpts(selector, index) {
5356
5357 var indexFields = index.def.fields.map(getKey);
5358
5359 var inMemoryFields = [];
5360 var startkey = [];
5361 var endkey = [];
5362 var inclusiveStart;
5363 var inclusiveEnd;
5364
5365
5366 function finish(i) {
5367
5368 if (inclusiveStart !== false) {
5369 startkey.push(COLLATE_LO);
5370 }
5371 if (inclusiveEnd !== false) {
5372 endkey.push(COLLATE_HI);
5373 }
5374 // keep track of the fields where we lost specificity,
5375 // and therefore need to filter in-memory
5376 inMemoryFields = indexFields.slice(i);
5377 }
5378
5379 for (var i = 0, len = indexFields.length; i < len; i++) {
5380 var indexField = indexFields[i];
5381
5382 var matcher = selector[indexField];
5383
5384 if (!matcher || !Object.keys(matcher).length) { // fewer fields in user query than in index
5385 finish(i);
5386 break;
5387 } else if (i > 0) {
5388 if (Object.keys(matcher).some(isNonLogicalMatcher)) { // non-logical are ignored
5389 finish(i);
5390 break;
5391 }
5392 var usingGtlt = (
5393 '$gt' in matcher || '$gte' in matcher ||
5394 '$lt' in matcher || '$lte' in matcher);
5395 var previousKeys = Object.keys(selector[indexFields[i - 1]]);
5396 var previousWasEq = arrayEquals(previousKeys, ['$eq']);
5397 var previousWasSame = arrayEquals(previousKeys, Object.keys(matcher));
5398 var gtltLostSpecificity = usingGtlt && !previousWasEq && !previousWasSame;
5399 if (gtltLostSpecificity) {
5400 finish(i);
5401 break;
5402 }
5403 }
5404
5405 var userOperators = Object.keys(matcher);
5406
5407 var combinedOpts = null;
5408
5409 for (var j = 0; j < userOperators.length; j++) {
5410 var userOperator = userOperators[j];
5411 var userValue = matcher[userOperator];
5412
5413 var newOpts = getMultiFieldCoreQueryPlan(userOperator, userValue);
5414
5415 if (combinedOpts) {
5416 combinedOpts = mergeObjects([combinedOpts, newOpts]);
5417 } else {
5418 combinedOpts = newOpts;
5419 }
5420 }
5421
5422 startkey.push('startkey' in combinedOpts ? combinedOpts.startkey : COLLATE_LO);
5423 endkey.push('endkey' in combinedOpts ? combinedOpts.endkey : COLLATE_HI);
5424 if ('inclusive_start' in combinedOpts) {
5425 inclusiveStart = combinedOpts.inclusive_start;
5426 }
5427 if ('inclusive_end' in combinedOpts) {
5428 inclusiveEnd = combinedOpts.inclusive_end;
5429 }
5430 }
5431
5432 var res = {
5433 startkey: startkey,
5434 endkey: endkey
5435 };
5436
5437 if (typeof inclusiveStart !== 'undefined') {
5438 res.inclusive_start = inclusiveStart;
5439 }
5440 if (typeof inclusiveEnd !== 'undefined') {
5441 res.inclusive_end = inclusiveEnd;
5442 }
5443
5444 return {
5445 queryOpts: res,
5446 inMemoryFields: inMemoryFields
5447 };
5448}
5449
5450function getDefaultQueryPlan(selector) {
5451 //using default index, so all fields need to be done in memory
5452 return {
5453 queryOpts: {startkey: null},
5454 inMemoryFields: [Object.keys(selector)]
5455 };
5456}
5457
5458function getCoreQueryPlan(selector, index) {
5459 if (index.defaultUsed) {
5460 return getDefaultQueryPlan(selector, index);
5461 }
5462
5463 if (index.def.fields.length === 1) {
5464 // one field in index, so the value was indexed as a singleton
5465 return getSingleFieldCoreQueryPlan(selector, index);
5466 }
5467 // else index has multiple fields, so the value was indexed as an array
5468 return getMultiFieldQueryOpts(selector, index);
5469}
5470
5471function planQuery(request, indexes) {
5472
5473 var selector = request.selector;
5474 var sort = request.sort;
5475
5476 var userFieldsRes = getUserFields(selector, sort);
5477
5478 var userFields = userFieldsRes.fields;
5479 var sortOrder = userFieldsRes.sortOrder;
5480 var index = findBestMatchingIndex(selector, userFields, sortOrder, indexes, request.use_index);
5481
5482 var coreQueryPlan = getCoreQueryPlan(selector, index);
5483 var queryOpts = coreQueryPlan.queryOpts;
5484 var coreInMemoryFields = coreQueryPlan.inMemoryFields;
5485
5486 var inMemoryFields = getInMemoryFields(coreInMemoryFields, index, selector, userFields);
5487
5488 var res = {
5489 queryOpts: queryOpts,
5490 index: index,
5491 inMemoryFields: inMemoryFields
5492 };
5493 return res;
5494}
5495
5496function indexToSignature(index) {
5497 // remove '_design/'
5498 return index.ddoc.substring(8) + '/' + index.name;
5499}
5500
5501function doAllDocs(db, originalOpts) {
5502 var opts = clone(originalOpts);
5503
5504 // CouchDB responds in weird ways when you provide a non-string to _id;
5505 // we mimic the behavior for consistency. See issue66 tests for details.
5506
5507 if (opts.descending) {
5508 if ('endkey' in opts && typeof opts.endkey !== 'string') {
5509 opts.endkey = '';
5510 }
5511 if ('startkey' in opts && typeof opts.startkey !== 'string') {
5512 opts.limit = 0;
5513 }
5514 } else {
5515 if ('startkey' in opts && typeof opts.startkey !== 'string') {
5516 opts.startkey = '';
5517 }
5518 if ('endkey' in opts && typeof opts.endkey !== 'string') {
5519 opts.limit = 0;
5520 }
5521 }
5522 if ('key' in opts && typeof opts.key !== 'string') {
5523 opts.limit = 0;
5524 }
5525
5526 return db.allDocs(opts)
5527 .then(function (res) {
5528 // filter out any design docs that _all_docs might return
5529 res.rows = res.rows.filter(function (row) {
5530 return !/^_design\//.test(row.id);
5531 });
5532 return res;
5533 });
5534}
5535
5536function find$1(db, requestDef, explain) {
5537 if (requestDef.selector) {
5538 requestDef.selector = massageSelector(requestDef.selector);
5539 }
5540
5541 if (requestDef.sort) {
5542 requestDef.sort = massageSort(requestDef.sort);
5543 }
5544
5545 if (requestDef.use_index) {
5546 requestDef.use_index = massageUseIndex(requestDef.use_index);
5547 }
5548
5549 validateFindRequest(requestDef);
5550
5551 return getIndexes$1(db).then(function (getIndexesRes) {
5552
5553 db.constructor.emit('debug', ['find', 'planning query', requestDef]);
5554 var queryPlan = planQuery(requestDef, getIndexesRes.indexes);
5555 db.constructor.emit('debug', ['find', 'query plan', queryPlan]);
5556
5557 var indexToUse = queryPlan.index;
5558
5559 validateSort(requestDef, indexToUse);
5560
5561 var opts = $inject_Object_assign({
5562 include_docs: true,
5563 reduce: false
5564 }, queryPlan.queryOpts);
5565
5566 if ('startkey' in opts && 'endkey' in opts &&
5567 collate(opts.startkey, opts.endkey) > 0) {
5568 // can't possibly return any results, startkey > endkey
5569 /* istanbul ignore next */
5570 return {docs: []};
5571 }
5572
5573 var isDescending = requestDef.sort &&
5574 typeof requestDef.sort[0] !== 'string' &&
5575 getValue(requestDef.sort[0]) === 'desc';
5576
5577 if (isDescending) {
5578 // either all descending or all ascending
5579 opts.descending = true;
5580 opts = reverseOptions(opts);
5581 }
5582
5583 if (!queryPlan.inMemoryFields.length) {
5584 // no in-memory filtering necessary, so we can let the
5585 // database do the limit/skip for us
5586 if ('limit' in requestDef) {
5587 opts.limit = requestDef.limit;
5588 }
5589 if ('skip' in requestDef) {
5590 opts.skip = requestDef.skip;
5591 }
5592 }
5593
5594 if (explain) {
5595 return Promise.resolve(queryPlan, opts);
5596 }
5597
5598 return Promise.resolve().then(function () {
5599 if (indexToUse.name === '_all_docs') {
5600 return doAllDocs(db, opts);
5601 } else {
5602 var signature = indexToSignature(indexToUse);
5603 return abstractMapper$1(db).query.call(db, signature, opts);
5604 }
5605 }).then(function (res) {
5606 if (opts.inclusive_start === false) {
5607 // may have to manually filter the first one,
5608 // since couchdb has no true inclusive_start option
5609 res.rows = filterInclusiveStart(res.rows, opts.startkey, indexToUse);
5610 }
5611
5612 if (queryPlan.inMemoryFields.length) {
5613 // need to filter some stuff in-memory
5614 res.rows = filterInMemoryFields(res.rows, requestDef, queryPlan.inMemoryFields);
5615 }
5616
5617 var resp = {
5618 docs: res.rows.map(function (row) {
5619 var doc = row.doc;
5620 if (requestDef.fields) {
5621 return pick$1(doc, requestDef.fields);
5622 }
5623 return doc;
5624 })
5625 };
5626
5627 if (indexToUse.defaultUsed) {
5628 resp.warning = 'no matching index found, create an index to optimize query time';
5629 }
5630
5631 return resp;
5632 });
5633 });
5634}
5635
5636function explain$1(db, requestDef) {
5637 return find$1(db, requestDef, true)
5638 .then(function (queryPlan) {
5639 return {
5640 dbname: db.name,
5641 index: queryPlan.index,
5642 selector: requestDef.selector,
5643 range: {
5644 start_key: queryPlan.queryOpts.startkey,
5645 end_key: queryPlan.queryOpts.endkey
5646 },
5647 opts: {
5648 use_index: requestDef.use_index || [],
5649 bookmark: "nil", //hardcoded to match CouchDB since its not supported,
5650 limit: requestDef.limit,
5651 skip: requestDef.skip,
5652 sort: requestDef.sort || {},
5653 fields: requestDef.fields,
5654 conflicts: false, //hardcoded to match CouchDB since its not supported,
5655 r: [49] // hardcoded to match CouchDB since its not support
5656 },
5657 limit: requestDef.limit,
5658 skip: requestDef.skip || 0,
5659 fields: requestDef.fields
5660 };
5661 });
5662}
5663
5664function deleteIndex$1(db, index) {
5665
5666 if (!index.ddoc) {
5667 throw new Error('you must supply an index.ddoc when deleting');
5668 }
5669
5670 if (!index.name) {
5671 throw new Error('you must supply an index.name when deleting');
5672 }
5673
5674 var docId = index.ddoc;
5675 var viewName = index.name;
5676
5677 function deltaFun(doc) {
5678 if (Object.keys(doc.views).length === 1 && doc.views[viewName]) {
5679 // only one view in this ddoc, delete the whole ddoc
5680 return {_id: docId, _deleted: true};
5681 }
5682 // more than one view here, just remove the view
5683 delete doc.views[viewName];
5684 return doc;
5685 }
5686
5687 return upsert(db, docId, deltaFun).then(function () {
5688 return abstractMapper$1(db).viewCleanup.apply(db);
5689 }).then(function () {
5690 return {ok: true};
5691 });
5692}
5693
5694var createIndexAsCallback = callbackify(createIndex$1);
5695var findAsCallback = callbackify(find$1);
5696var explainAsCallback = callbackify(explain$1);
5697var getIndexesAsCallback = callbackify(getIndexes$1);
5698var deleteIndexAsCallback = callbackify(deleteIndex$1);
5699
5700var plugin = {};
5701plugin.createIndex = toPromise(function (requestDef, callback) {
5702
5703 if (typeof requestDef !== 'object') {
5704 return callback(new Error('you must provide an index to create'));
5705 }
5706
5707 var createIndex$$1 = isRemote(this) ?
5708 createIndex : createIndexAsCallback;
5709 createIndex$$1(this, requestDef, callback);
5710});
5711
5712plugin.find = toPromise(function (requestDef, callback) {
5713
5714 if (typeof callback === 'undefined') {
5715 callback = requestDef;
5716 requestDef = undefined;
5717 }
5718
5719 if (typeof requestDef !== 'object') {
5720 return callback(new Error('you must provide search parameters to find()'));
5721 }
5722
5723 var find$$1 = isRemote(this) ? find : findAsCallback;
5724 find$$1(this, requestDef, callback);
5725});
5726
5727plugin.explain = toPromise(function (requestDef, callback) {
5728
5729 if (typeof callback === 'undefined') {
5730 callback = requestDef;
5731 requestDef = undefined;
5732 }
5733
5734 if (typeof requestDef !== 'object') {
5735 return callback(new Error('you must provide search parameters to explain()'));
5736 }
5737
5738 var find$$1 = isRemote(this) ? explain : explainAsCallback;
5739 find$$1(this, requestDef, callback);
5740});
5741
5742plugin.getIndexes = toPromise(function (callback) {
5743
5744 var getIndexes$$1 = isRemote(this) ? getIndexes : getIndexesAsCallback;
5745 getIndexes$$1(this, callback);
5746});
5747
5748plugin.deleteIndex = toPromise(function (indexDef, callback) {
5749
5750 if (typeof indexDef !== 'object') {
5751 return callback(new Error('you must provide an index to delete'));
5752 }
5753
5754 var deleteIndex$$1 = isRemote(this) ?
5755 deleteIndex : deleteIndexAsCallback;
5756 deleteIndex$$1(this, indexDef, callback);
5757});
5758
5759/* global PouchDB */
5760
5761if (typeof PouchDB === 'undefined') {
5762 guardedConsole('error', 'pouchdb-find plugin error: ' +
5763 'Cannot find global "PouchDB" object! ' +
5764 'Did you remember to include pouchdb.js?');
5765} else {
5766 PouchDB.plugin(plugin);
5767}
5768
5769}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
5770},{"1":1,"2":2,"3":3,"4":4,"5":5,"6":6}]},{},[11]);