UNPKG

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