UNPKG

151 kBJavaScriptView Raw
1// PouchDB websql plugin 7.0.0
2// Since PouchDB 7.0.0, shipped as a separate plugin.
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'use strict';
1617
1618/**
1619 * Stringify/parse functions that don't operate
1620 * recursively, so they avoid call stack exceeded
1621 * errors.
1622 */
1623exports.stringify = function stringify(input) {
1624 var queue = [];
1625 queue.push({obj: input});
1626
1627 var res = '';
1628 var next, obj, prefix, val, i, arrayPrefix, keys, k, key, value, objPrefix;
1629 while ((next = queue.pop())) {
1630 obj = next.obj;
1631 prefix = next.prefix || '';
1632 val = next.val || '';
1633 res += prefix;
1634 if (val) {
1635 res += val;
1636 } else if (typeof obj !== 'object') {
1637 res += typeof obj === 'undefined' ? null : JSON.stringify(obj);
1638 } else if (obj === null) {
1639 res += 'null';
1640 } else if (Array.isArray(obj)) {
1641 queue.push({val: ']'});
1642 for (i = obj.length - 1; i >= 0; i--) {
1643 arrayPrefix = i === 0 ? '' : ',';
1644 queue.push({obj: obj[i], prefix: arrayPrefix});
1645 }
1646 queue.push({val: '['});
1647 } else { // object
1648 keys = [];
1649 for (k in obj) {
1650 if (obj.hasOwnProperty(k)) {
1651 keys.push(k);
1652 }
1653 }
1654 queue.push({val: '}'});
1655 for (i = keys.length - 1; i >= 0; i--) {
1656 key = keys[i];
1657 value = obj[key];
1658 objPrefix = (i > 0 ? ',' : '');
1659 objPrefix += JSON.stringify(key) + ':';
1660 queue.push({obj: value, prefix: objPrefix});
1661 }
1662 queue.push({val: '{'});
1663 }
1664 }
1665 return res;
1666};
1667
1668// Convenience function for the parse function.
1669// This pop function is basically copied from
1670// pouchCollate.parseIndexableString
1671function pop(obj, stack, metaStack) {
1672 var lastMetaElement = metaStack[metaStack.length - 1];
1673 if (obj === lastMetaElement.element) {
1674 // popping a meta-element, e.g. an object whose value is another object
1675 metaStack.pop();
1676 lastMetaElement = metaStack[metaStack.length - 1];
1677 }
1678 var element = lastMetaElement.element;
1679 var lastElementIndex = lastMetaElement.index;
1680 if (Array.isArray(element)) {
1681 element.push(obj);
1682 } else if (lastElementIndex === stack.length - 2) { // obj with key+value
1683 var key = stack.pop();
1684 element[key] = obj;
1685 } else {
1686 stack.push(obj); // obj with key only
1687 }
1688}
1689
1690exports.parse = function (str) {
1691 var stack = [];
1692 var metaStack = []; // stack for arrays and objects
1693 var i = 0;
1694 var collationIndex,parsedNum,numChar;
1695 var parsedString,lastCh,numConsecutiveSlashes,ch;
1696 var arrayElement, objElement;
1697 while (true) {
1698 collationIndex = str[i++];
1699 if (collationIndex === '}' ||
1700 collationIndex === ']' ||
1701 typeof collationIndex === 'undefined') {
1702 if (stack.length === 1) {
1703 return stack.pop();
1704 } else {
1705 pop(stack.pop(), stack, metaStack);
1706 continue;
1707 }
1708 }
1709 switch (collationIndex) {
1710 case ' ':
1711 case '\t':
1712 case '\n':
1713 case ':':
1714 case ',':
1715 break;
1716 case 'n':
1717 i += 3; // 'ull'
1718 pop(null, stack, metaStack);
1719 break;
1720 case 't':
1721 i += 3; // 'rue'
1722 pop(true, stack, metaStack);
1723 break;
1724 case 'f':
1725 i += 4; // 'alse'
1726 pop(false, stack, metaStack);
1727 break;
1728 case '0':
1729 case '1':
1730 case '2':
1731 case '3':
1732 case '4':
1733 case '5':
1734 case '6':
1735 case '7':
1736 case '8':
1737 case '9':
1738 case '-':
1739 parsedNum = '';
1740 i--;
1741 while (true) {
1742 numChar = str[i++];
1743 if (/[\d\.\-e\+]/.test(numChar)) {
1744 parsedNum += numChar;
1745 } else {
1746 i--;
1747 break;
1748 }
1749 }
1750 pop(parseFloat(parsedNum), stack, metaStack);
1751 break;
1752 case '"':
1753 parsedString = '';
1754 lastCh = void 0;
1755 numConsecutiveSlashes = 0;
1756 while (true) {
1757 ch = str[i++];
1758 if (ch !== '"' || (lastCh === '\\' &&
1759 numConsecutiveSlashes % 2 === 1)) {
1760 parsedString += ch;
1761 lastCh = ch;
1762 if (lastCh === '\\') {
1763 numConsecutiveSlashes++;
1764 } else {
1765 numConsecutiveSlashes = 0;
1766 }
1767 } else {
1768 break;
1769 }
1770 }
1771 pop(JSON.parse('"' + parsedString + '"'), stack, metaStack);
1772 break;
1773 case '[':
1774 arrayElement = { element: [], index: stack.length };
1775 stack.push(arrayElement.element);
1776 metaStack.push(arrayElement);
1777 break;
1778 case '{':
1779 objElement = { element: {}, index: stack.length };
1780 stack.push(objElement.element);
1781 metaStack.push(objElement);
1782 break;
1783 default:
1784 throw new Error(
1785 'unexpectedly reached end of input: ' + collationIndex);
1786 }
1787 }
1788};
1789
1790},{}],12:[function(_dereq_,module,exports){
1791(function (global){
1792'use strict';
1793
1794function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
1795
1796var getArguments = _interopDefault(_dereq_(1));
1797var immediate = _interopDefault(_dereq_(3));
1798var events = _dereq_(2);
1799var inherits = _interopDefault(_dereq_(4));
1800var Md5 = _interopDefault(_dereq_(5));
1801var uuidV4 = _interopDefault(_dereq_(6));
1802var vuvuzela = _interopDefault(_dereq_(11));
1803
1804function isBinaryObject(object) {
1805 return (typeof ArrayBuffer !== 'undefined' && object instanceof ArrayBuffer) ||
1806 (typeof Blob !== 'undefined' && object instanceof Blob);
1807}
1808
1809function cloneArrayBuffer(buff) {
1810 if (typeof buff.slice === 'function') {
1811 return buff.slice(0);
1812 }
1813 // IE10-11 slice() polyfill
1814 var target = new ArrayBuffer(buff.byteLength);
1815 var targetArray = new Uint8Array(target);
1816 var sourceArray = new Uint8Array(buff);
1817 targetArray.set(sourceArray);
1818 return target;
1819}
1820
1821function cloneBinaryObject(object) {
1822 if (object instanceof ArrayBuffer) {
1823 return cloneArrayBuffer(object);
1824 }
1825 var size = object.size;
1826 var type = object.type;
1827 // Blob
1828 if (typeof object.slice === 'function') {
1829 return object.slice(0, size, type);
1830 }
1831 // PhantomJS slice() replacement
1832 return object.webkitSlice(0, size, type);
1833}
1834
1835// most of this is borrowed from lodash.isPlainObject:
1836// https://github.com/fis-components/lodash.isplainobject/
1837// blob/29c358140a74f252aeb08c9eb28bef86f2217d4a/index.js
1838
1839var funcToString = Function.prototype.toString;
1840var objectCtorString = funcToString.call(Object);
1841
1842function isPlainObject(value) {
1843 var proto = Object.getPrototypeOf(value);
1844 /* istanbul ignore if */
1845 if (proto === null) { // not sure when this happens, but I guess it can
1846 return true;
1847 }
1848 var Ctor = proto.constructor;
1849 return (typeof Ctor == 'function' &&
1850 Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
1851}
1852
1853function clone(object) {
1854 var newObject;
1855 var i;
1856 var len;
1857
1858 if (!object || typeof object !== 'object') {
1859 return object;
1860 }
1861
1862 if (Array.isArray(object)) {
1863 newObject = [];
1864 for (i = 0, len = object.length; i < len; i++) {
1865 newObject[i] = clone(object[i]);
1866 }
1867 return newObject;
1868 }
1869
1870 // special case: to avoid inconsistencies between IndexedDB
1871 // and other backends, we automatically stringify Dates
1872 if (object instanceof Date) {
1873 return object.toISOString();
1874 }
1875
1876 if (isBinaryObject(object)) {
1877 return cloneBinaryObject(object);
1878 }
1879
1880 if (!isPlainObject(object)) {
1881 return object; // don't clone objects like Workers
1882 }
1883
1884 newObject = {};
1885 for (i in object) {
1886 /* istanbul ignore else */
1887 if (Object.prototype.hasOwnProperty.call(object, i)) {
1888 var value = clone(object[i]);
1889 if (typeof value !== 'undefined') {
1890 newObject[i] = value;
1891 }
1892 }
1893 }
1894 return newObject;
1895}
1896
1897function once(fun) {
1898 var called = false;
1899 return getArguments(function (args) {
1900 /* istanbul ignore if */
1901 if (called) {
1902 // this is a smoke test and should never actually happen
1903 throw new Error('once called more than once');
1904 } else {
1905 called = true;
1906 fun.apply(this, args);
1907 }
1908 });
1909}
1910
1911function toPromise(func) {
1912 //create the function we will be returning
1913 return getArguments(function (args) {
1914 // Clone arguments
1915 args = clone(args);
1916 var self = this;
1917 // if the last argument is a function, assume its a callback
1918 var usedCB = (typeof args[args.length - 1] === 'function') ? args.pop() : false;
1919 var promise = new Promise(function (fulfill, reject) {
1920 var resp;
1921 try {
1922 var callback = once(function (err, mesg) {
1923 if (err) {
1924 reject(err);
1925 } else {
1926 fulfill(mesg);
1927 }
1928 });
1929 // create a callback for this invocation
1930 // apply the function in the orig context
1931 args.push(callback);
1932 resp = func.apply(self, args);
1933 if (resp && typeof resp.then === 'function') {
1934 fulfill(resp);
1935 }
1936 } catch (e) {
1937 reject(e);
1938 }
1939 });
1940 // if there is a callback, call it back
1941 if (usedCB) {
1942 promise.then(function (result) {
1943 usedCB(null, result);
1944 }, usedCB);
1945 }
1946 return promise;
1947 });
1948}
1949
1950function mangle(key) {
1951 return '$' + key;
1952}
1953function unmangle(key) {
1954 return key.substring(1);
1955}
1956function Map$1() {
1957 this._store = {};
1958}
1959Map$1.prototype.get = function (key) {
1960 var mangled = mangle(key);
1961 return this._store[mangled];
1962};
1963Map$1.prototype.set = function (key, value) {
1964 var mangled = mangle(key);
1965 this._store[mangled] = value;
1966 return true;
1967};
1968Map$1.prototype.has = function (key) {
1969 var mangled = mangle(key);
1970 return mangled in this._store;
1971};
1972Map$1.prototype["delete"] = function (key) {
1973 var mangled = mangle(key);
1974 var res = mangled in this._store;
1975 delete this._store[mangled];
1976 return res;
1977};
1978Map$1.prototype.forEach = function (cb) {
1979 var keys = Object.keys(this._store);
1980 for (var i = 0, len = keys.length; i < len; i++) {
1981 var key = keys[i];
1982 var value = this._store[key];
1983 key = unmangle(key);
1984 cb(value, key);
1985 }
1986};
1987Object.defineProperty(Map$1.prototype, 'size', {
1988 get: function () {
1989 return Object.keys(this._store).length;
1990 }
1991});
1992
1993function Set$1(array) {
1994 this._store = new Map$1();
1995
1996 // init with an array
1997 if (array && Array.isArray(array)) {
1998 for (var i = 0, len = array.length; i < len; i++) {
1999 this.add(array[i]);
2000 }
2001 }
2002}
2003Set$1.prototype.add = function (key) {
2004 return this._store.set(key, true);
2005};
2006Set$1.prototype.has = function (key) {
2007 return this._store.has(key);
2008};
2009Set$1.prototype.forEach = function (cb) {
2010 this._store.forEach(function (value, key) {
2011 cb(key);
2012 });
2013};
2014Object.defineProperty(Set$1.prototype, 'size', {
2015 get: function () {
2016 return this._store.size;
2017 }
2018});
2019
2020/* global Map,Set,Symbol */
2021// Based on https://kangax.github.io/compat-table/es6/ we can sniff out
2022// incomplete Map/Set implementations which would otherwise cause our tests to fail.
2023// Notably they fail in IE11 and iOS 8.4, which this prevents.
2024function supportsMapAndSet() {
2025 if (typeof Symbol === 'undefined' || typeof Map === 'undefined' || typeof Set === 'undefined') {
2026 return false;
2027 }
2028 var prop = Object.getOwnPropertyDescriptor(Map, Symbol.species);
2029 return prop && 'get' in prop && Map[Symbol.species] === Map;
2030}
2031
2032// based on https://github.com/montagejs/collections
2033
2034var ExportedSet;
2035var ExportedMap;
2036
2037{
2038 if (supportsMapAndSet()) { // prefer built-in Map/Set
2039 ExportedSet = Set;
2040 ExportedMap = Map;
2041 } else { // fall back to our polyfill
2042 ExportedSet = Set$1;
2043 ExportedMap = Map$1;
2044 }
2045}
2046
2047// like underscore/lodash _.pick()
2048function pick(obj, arr) {
2049 var res = {};
2050 for (var i = 0, len = arr.length; i < len; i++) {
2051 var prop = arr[i];
2052 if (prop in obj) {
2053 res[prop] = obj[prop];
2054 }
2055 }
2056 return res;
2057}
2058
2059var hasLocal;
2060
2061try {
2062 localStorage.setItem('_pouch_check_localstorage', 1);
2063 hasLocal = !!localStorage.getItem('_pouch_check_localstorage');
2064} catch (e) {
2065 hasLocal = false;
2066}
2067
2068function hasLocalStorage() {
2069 return hasLocal;
2070}
2071
2072// Custom nextTick() shim for browsers. In node, this will just be process.nextTick(). We
2073
2074inherits(Changes, events.EventEmitter);
2075
2076/* istanbul ignore next */
2077function attachBrowserEvents(self) {
2078 if (hasLocalStorage()) {
2079 addEventListener("storage", function (e) {
2080 self.emit(e.key);
2081 });
2082 }
2083}
2084
2085function Changes() {
2086 events.EventEmitter.call(this);
2087 this._listeners = {};
2088
2089 attachBrowserEvents(this);
2090}
2091Changes.prototype.addListener = function (dbName, id, db, opts) {
2092 /* istanbul ignore if */
2093 if (this._listeners[id]) {
2094 return;
2095 }
2096 var self = this;
2097 var inprogress = false;
2098 function eventFunction() {
2099 /* istanbul ignore if */
2100 if (!self._listeners[id]) {
2101 return;
2102 }
2103 if (inprogress) {
2104 inprogress = 'waiting';
2105 return;
2106 }
2107 inprogress = true;
2108 var changesOpts = pick(opts, [
2109 'style', 'include_docs', 'attachments', 'conflicts', 'filter',
2110 'doc_ids', 'view', 'since', 'query_params', 'binary', 'return_docs'
2111 ]);
2112
2113 /* istanbul ignore next */
2114 function onError() {
2115 inprogress = false;
2116 }
2117
2118 db.changes(changesOpts).on('change', function (c) {
2119 if (c.seq > opts.since && !opts.cancelled) {
2120 opts.since = c.seq;
2121 opts.onChange(c);
2122 }
2123 }).on('complete', function () {
2124 if (inprogress === 'waiting') {
2125 immediate(eventFunction);
2126 }
2127 inprogress = false;
2128 }).on('error', onError);
2129 }
2130 this._listeners[id] = eventFunction;
2131 this.on(dbName, eventFunction);
2132};
2133
2134Changes.prototype.removeListener = function (dbName, id) {
2135 /* istanbul ignore if */
2136 if (!(id in this._listeners)) {
2137 return;
2138 }
2139 events.EventEmitter.prototype.removeListener.call(this, dbName,
2140 this._listeners[id]);
2141 delete this._listeners[id];
2142};
2143
2144
2145/* istanbul ignore next */
2146Changes.prototype.notifyLocalWindows = function (dbName) {
2147 //do a useless change on a storage thing
2148 //in order to get other windows's listeners to activate
2149 if (hasLocalStorage()) {
2150 localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a";
2151 }
2152};
2153
2154Changes.prototype.notify = function (dbName) {
2155 this.emit(dbName);
2156 this.notifyLocalWindows(dbName);
2157};
2158
2159function guardedConsole(method) {
2160 /* istanbul ignore else */
2161 if (typeof console !== 'undefined' && typeof console[method] === 'function') {
2162 var args = Array.prototype.slice.call(arguments, 1);
2163 console[method].apply(console, args);
2164 }
2165}
2166
2167var assign;
2168{
2169 if (typeof Object.assign === 'function') {
2170 assign = Object.assign;
2171 } else {
2172 // lite Object.assign polyfill based on
2173 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
2174 assign = function (target) {
2175 var to = Object(target);
2176
2177 for (var index = 1; index < arguments.length; index++) {
2178 var nextSource = arguments[index];
2179
2180 if (nextSource != null) { // Skip over if undefined or null
2181 for (var nextKey in nextSource) {
2182 // Avoid bugs when hasOwnProperty is shadowed
2183 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
2184 to[nextKey] = nextSource[nextKey];
2185 }
2186 }
2187 }
2188 }
2189 return to;
2190 };
2191 }
2192}
2193
2194var $inject_Object_assign = assign;
2195
2196inherits(PouchError, Error);
2197
2198function PouchError(status, error, reason) {
2199 Error.call(this, reason);
2200 this.status = status;
2201 this.name = error;
2202 this.message = reason;
2203 this.error = true;
2204}
2205
2206PouchError.prototype.toString = function () {
2207 return JSON.stringify({
2208 status: this.status,
2209 name: this.name,
2210 message: this.message,
2211 reason: this.reason
2212 });
2213};
2214
2215var UNAUTHORIZED = new PouchError(401, 'unauthorized', "Name or password is incorrect.");
2216var MISSING_BULK_DOCS = new PouchError(400, 'bad_request', "Missing JSON list of 'docs'");
2217var MISSING_DOC = new PouchError(404, 'not_found', 'missing');
2218var REV_CONFLICT = new PouchError(409, 'conflict', 'Document update conflict');
2219var INVALID_ID = new PouchError(400, 'bad_request', '_id field must contain a string');
2220var MISSING_ID = new PouchError(412, 'missing_id', '_id is required for puts');
2221var RESERVED_ID = new PouchError(400, 'bad_request', 'Only reserved document ids may start with underscore.');
2222var NOT_OPEN = new PouchError(412, 'precondition_failed', 'Database not open');
2223var UNKNOWN_ERROR = new PouchError(500, 'unknown_error', 'Database encountered an unknown error');
2224var BAD_ARG = new PouchError(500, 'badarg', 'Some query argument is invalid');
2225var INVALID_REQUEST = new PouchError(400, 'invalid_request', 'Request was invalid');
2226var QUERY_PARSE_ERROR = new PouchError(400, 'query_parse_error', 'Some query parameter is invalid');
2227var DOC_VALIDATION = new PouchError(500, 'doc_validation', 'Bad special document member');
2228var BAD_REQUEST = new PouchError(400, 'bad_request', 'Something wrong with the request');
2229var NOT_AN_OBJECT = new PouchError(400, 'bad_request', 'Document must be a JSON object');
2230var DB_MISSING = new PouchError(404, 'not_found', 'Database not found');
2231var IDB_ERROR = new PouchError(500, 'indexed_db_went_bad', 'unknown');
2232var WSQ_ERROR = new PouchError(500, 'web_sql_went_bad', 'unknown');
2233var LDB_ERROR = new PouchError(500, 'levelDB_went_went_bad', 'unknown');
2234var FORBIDDEN = new PouchError(403, 'forbidden', 'Forbidden by design doc validate_doc_update function');
2235var INVALID_REV = new PouchError(400, 'bad_request', 'Invalid rev format');
2236var FILE_EXISTS = new PouchError(412, 'file_exists', 'The database could not be created, the file already exists.');
2237var MISSING_STUB = new PouchError(412, 'missing_stub', 'A pre-existing attachment stub wasn\'t found');
2238var INVALID_URL = new PouchError(413, 'invalid_url', 'Provided URL is invalid');
2239
2240function createError(error, reason) {
2241 function CustomPouchError(reason) {
2242 // inherit error properties from our parent error manually
2243 // so as to allow proper JSON parsing.
2244 /* jshint ignore:start */
2245 for (var p in error) {
2246 if (typeof error[p] !== 'function') {
2247 this[p] = error[p];
2248 }
2249 }
2250 /* jshint ignore:end */
2251 if (reason !== undefined) {
2252 this.reason = reason;
2253 }
2254 }
2255 CustomPouchError.prototype = PouchError.prototype;
2256 return new CustomPouchError(reason);
2257}
2258
2259function tryFilter(filter, doc, req) {
2260 try {
2261 return !filter(doc, req);
2262 } catch (err) {
2263 var msg = 'Filter function threw: ' + err.toString();
2264 return createError(BAD_REQUEST, msg);
2265 }
2266}
2267
2268function filterChange(opts) {
2269 var req = {};
2270 var hasFilter = opts.filter && typeof opts.filter === 'function';
2271 req.query = opts.query_params;
2272
2273 return function filter(change) {
2274 if (!change.doc) {
2275 // CSG sends events on the changes feed that don't have documents,
2276 // this hack makes a whole lot of existing code robust.
2277 change.doc = {};
2278 }
2279
2280 var filterReturn = hasFilter && tryFilter(opts.filter, change.doc, req);
2281
2282 if (typeof filterReturn === 'object') {
2283 return filterReturn;
2284 }
2285
2286 if (filterReturn) {
2287 return false;
2288 }
2289
2290 if (!opts.include_docs) {
2291 delete change.doc;
2292 } else if (!opts.attachments) {
2293 for (var att in change.doc._attachments) {
2294 /* istanbul ignore else */
2295 if (change.doc._attachments.hasOwnProperty(att)) {
2296 change.doc._attachments[att].stub = true;
2297 }
2298 }
2299 }
2300 return true;
2301 };
2302}
2303
2304// shim for Function.prototype.name,
2305
2306// Determine id an ID is valid
2307// - invalid IDs begin with an underescore that does not begin '_design' or
2308// '_local'
2309// - any other string value is a valid id
2310// Returns the specific error object for each case
2311function invalidIdError(id) {
2312 var err;
2313 if (!id) {
2314 err = createError(MISSING_ID);
2315 } else if (typeof id !== 'string') {
2316 err = createError(INVALID_ID);
2317 } else if (/^_/.test(id) && !(/^_(design|local)/).test(id)) {
2318 err = createError(RESERVED_ID);
2319 }
2320 if (err) {
2321 throw err;
2322 }
2323}
2324
2325// Checks if a PouchDB object is "remote" or not. This is
2326
2327// originally parseUri 1.2.2, now patched by us
2328
2329// Based on https://github.com/alexdavid/scope-eval v0.0.3
2330
2331var thisAtob = function (str) {
2332 return atob(str);
2333};
2334
2335var thisBtoa = function (str) {
2336 return btoa(str);
2337};
2338
2339// Abstracts constructing a Blob object, so it also works in older
2340// browsers that don't support the native Blob constructor (e.g.
2341// old QtWebKit versions, Android < 4.4).
2342function createBlob(parts, properties) {
2343 /* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
2344 parts = parts || [];
2345 properties = properties || {};
2346 try {
2347 return new Blob(parts, properties);
2348 } catch (e) {
2349 if (e.name !== "TypeError") {
2350 throw e;
2351 }
2352 var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder :
2353 typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder :
2354 typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder :
2355 WebKitBlobBuilder;
2356 var builder = new Builder();
2357 for (var i = 0; i < parts.length; i += 1) {
2358 builder.append(parts[i]);
2359 }
2360 return builder.getBlob(properties.type);
2361 }
2362}
2363
2364// From http://stackoverflow.com/questions/14967647/ (continues on next line)
2365// encode-decode-image-with-base64-breaks-image (2013-04-21)
2366function binaryStringToArrayBuffer(bin) {
2367 var length = bin.length;
2368 var buf = new ArrayBuffer(length);
2369 var arr = new Uint8Array(buf);
2370 for (var i = 0; i < length; i++) {
2371 arr[i] = bin.charCodeAt(i);
2372 }
2373 return buf;
2374}
2375
2376function binStringToBluffer(binString, type) {
2377 return createBlob([binaryStringToArrayBuffer(binString)], {type: type});
2378}
2379
2380//Can't find original post, but this is close
2381//http://stackoverflow.com/questions/6965107/ (continues on next line)
2382//converting-between-strings-and-arraybuffers
2383function arrayBufferToBinaryString(buffer) {
2384 var binary = '';
2385 var bytes = new Uint8Array(buffer);
2386 var length = bytes.byteLength;
2387 for (var i = 0; i < length; i++) {
2388 binary += String.fromCharCode(bytes[i]);
2389 }
2390 return binary;
2391}
2392
2393// shim for browsers that don't support it
2394function readAsBinaryString(blob, callback) {
2395 var reader = new FileReader();
2396 var hasBinaryString = typeof reader.readAsBinaryString === 'function';
2397 reader.onloadend = function (e) {
2398 var result = e.target.result || '';
2399 if (hasBinaryString) {
2400 return callback(result);
2401 }
2402 callback(arrayBufferToBinaryString(result));
2403 };
2404 if (hasBinaryString) {
2405 reader.readAsBinaryString(blob);
2406 } else {
2407 reader.readAsArrayBuffer(blob);
2408 }
2409}
2410
2411function blobToBinaryString(blobOrBuffer, callback) {
2412 readAsBinaryString(blobOrBuffer, function (bin) {
2413 callback(bin);
2414 });
2415}
2416
2417function blobToBase64(blobOrBuffer, callback) {
2418 blobToBinaryString(blobOrBuffer, function (base64) {
2419 callback(thisBtoa(base64));
2420 });
2421}
2422
2423// simplified API. universal browser support is assumed
2424function readAsArrayBuffer(blob, callback) {
2425 var reader = new FileReader();
2426 reader.onloadend = function (e) {
2427 var result = e.target.result || new ArrayBuffer(0);
2428 callback(result);
2429 };
2430 reader.readAsArrayBuffer(blob);
2431}
2432
2433// this is not used in the browser
2434
2435var setImmediateShim = global.setImmediate || global.setTimeout;
2436var MD5_CHUNK_SIZE = 32768;
2437
2438function rawToBase64(raw) {
2439 return thisBtoa(raw);
2440}
2441
2442function sliceBlob(blob, start, end) {
2443 if (blob.webkitSlice) {
2444 return blob.webkitSlice(start, end);
2445 }
2446 return blob.slice(start, end);
2447}
2448
2449function appendBlob(buffer, blob, start, end, callback) {
2450 if (start > 0 || end < blob.size) {
2451 // only slice blob if we really need to
2452 blob = sliceBlob(blob, start, end);
2453 }
2454 readAsArrayBuffer(blob, function (arrayBuffer) {
2455 buffer.append(arrayBuffer);
2456 callback();
2457 });
2458}
2459
2460function appendString(buffer, string, start, end, callback) {
2461 if (start > 0 || end < string.length) {
2462 // only create a substring if we really need to
2463 string = string.substring(start, end);
2464 }
2465 buffer.appendBinary(string);
2466 callback();
2467}
2468
2469function binaryMd5(data, callback) {
2470 var inputIsString = typeof data === 'string';
2471 var len = inputIsString ? data.length : data.size;
2472 var chunkSize = Math.min(MD5_CHUNK_SIZE, len);
2473 var chunks = Math.ceil(len / chunkSize);
2474 var currentChunk = 0;
2475 var buffer = inputIsString ? new Md5() : new Md5.ArrayBuffer();
2476
2477 var append = inputIsString ? appendString : appendBlob;
2478
2479 function next() {
2480 setImmediateShim(loadNextChunk);
2481 }
2482
2483 function done() {
2484 var raw = buffer.end(true);
2485 var base64 = rawToBase64(raw);
2486 callback(base64);
2487 buffer.destroy();
2488 }
2489
2490 function loadNextChunk() {
2491 var start = currentChunk * chunkSize;
2492 var end = start + chunkSize;
2493 currentChunk++;
2494 if (currentChunk < chunks) {
2495 append(buffer, data, start, end, next);
2496 } else {
2497 append(buffer, data, start, end, done);
2498 }
2499 }
2500 loadNextChunk();
2501}
2502
2503function stringMd5(string) {
2504 return Md5.hash(string);
2505}
2506
2507function rev$$1(doc, deterministic_revs) {
2508 var clonedDoc = clone(doc);
2509 if (!deterministic_revs) {
2510 return uuidV4.v4().replace(/-/g, '').toLowerCase();
2511 }
2512
2513 delete clonedDoc._rev_tree;
2514 return stringMd5(JSON.stringify(clonedDoc));
2515}
2516
2517var uuid = uuidV4.v4;
2518
2519function toObject(array) {
2520 return array.reduce(function (obj, item) {
2521 obj[item] = true;
2522 return obj;
2523 }, {});
2524}
2525// List of top level reserved words for doc
2526var reservedWords = toObject([
2527 '_id',
2528 '_rev',
2529 '_attachments',
2530 '_deleted',
2531 '_revisions',
2532 '_revs_info',
2533 '_conflicts',
2534 '_deleted_conflicts',
2535 '_local_seq',
2536 '_rev_tree',
2537 //replication documents
2538 '_replication_id',
2539 '_replication_state',
2540 '_replication_state_time',
2541 '_replication_state_reason',
2542 '_replication_stats',
2543 // Specific to Couchbase Sync Gateway
2544 '_removed'
2545]);
2546
2547// List of reserved words that should end up the document
2548var dataWords = toObject([
2549 '_attachments',
2550 //replication documents
2551 '_replication_id',
2552 '_replication_state',
2553 '_replication_state_time',
2554 '_replication_state_reason',
2555 '_replication_stats'
2556]);
2557
2558function parseRevisionInfo(rev) {
2559 if (!/^\d+-./.test(rev)) {
2560 return createError(INVALID_REV);
2561 }
2562 var idx = rev.indexOf('-');
2563 var left = rev.substring(0, idx);
2564 var right = rev.substring(idx + 1);
2565 return {
2566 prefix: parseInt(left, 10),
2567 id: right
2568 };
2569}
2570
2571function makeRevTreeFromRevisions(revisions, opts) {
2572 var pos = revisions.start - revisions.ids.length + 1;
2573
2574 var revisionIds = revisions.ids;
2575 var ids = [revisionIds[0], opts, []];
2576
2577 for (var i = 1, len = revisionIds.length; i < len; i++) {
2578 ids = [revisionIds[i], {status: 'missing'}, [ids]];
2579 }
2580
2581 return [{
2582 pos: pos,
2583 ids: ids
2584 }];
2585}
2586
2587// Preprocess documents, parse their revisions, assign an id and a
2588// revision for new writes that are missing them, etc
2589function parseDoc(doc, newEdits, dbOpts) {
2590 if (!dbOpts) {
2591 dbOpts = {
2592 deterministic_revs: true
2593 };
2594 }
2595
2596 var nRevNum;
2597 var newRevId;
2598 var revInfo;
2599 var opts = {status: 'available'};
2600 if (doc._deleted) {
2601 opts.deleted = true;
2602 }
2603
2604 if (newEdits) {
2605 if (!doc._id) {
2606 doc._id = uuid();
2607 }
2608 newRevId = rev$$1(doc, dbOpts.deterministic_revs);
2609 if (doc._rev) {
2610 revInfo = parseRevisionInfo(doc._rev);
2611 if (revInfo.error) {
2612 return revInfo;
2613 }
2614 doc._rev_tree = [{
2615 pos: revInfo.prefix,
2616 ids: [revInfo.id, {status: 'missing'}, [[newRevId, opts, []]]]
2617 }];
2618 nRevNum = revInfo.prefix + 1;
2619 } else {
2620 doc._rev_tree = [{
2621 pos: 1,
2622 ids : [newRevId, opts, []]
2623 }];
2624 nRevNum = 1;
2625 }
2626 } else {
2627 if (doc._revisions) {
2628 doc._rev_tree = makeRevTreeFromRevisions(doc._revisions, opts);
2629 nRevNum = doc._revisions.start;
2630 newRevId = doc._revisions.ids[0];
2631 }
2632 if (!doc._rev_tree) {
2633 revInfo = parseRevisionInfo(doc._rev);
2634 if (revInfo.error) {
2635 return revInfo;
2636 }
2637 nRevNum = revInfo.prefix;
2638 newRevId = revInfo.id;
2639 doc._rev_tree = [{
2640 pos: nRevNum,
2641 ids: [newRevId, opts, []]
2642 }];
2643 }
2644 }
2645
2646 invalidIdError(doc._id);
2647
2648 doc._rev = nRevNum + '-' + newRevId;
2649
2650 var result = {metadata : {}, data : {}};
2651 for (var key in doc) {
2652 /* istanbul ignore else */
2653 if (Object.prototype.hasOwnProperty.call(doc, key)) {
2654 var specialKey = key[0] === '_';
2655 if (specialKey && !reservedWords[key]) {
2656 var error = createError(DOC_VALIDATION, key);
2657 error.message = DOC_VALIDATION.message + ': ' + key;
2658 throw error;
2659 } else if (specialKey && !dataWords[key]) {
2660 result.metadata[key.slice(1)] = doc[key];
2661 } else {
2662 result.data[key] = doc[key];
2663 }
2664 }
2665 }
2666 return result;
2667}
2668
2669// We fetch all leafs of the revision tree, and sort them based on tree length
2670// and whether they were deleted, undeleted documents with the longest revision
2671// tree (most edits) win
2672// The final sort algorithm is slightly documented in a sidebar here:
2673// http://guide.couchdb.org/draft/conflicts.html
2674function winningRev(metadata) {
2675 var winningId;
2676 var winningPos;
2677 var winningDeleted;
2678 var toVisit = metadata.rev_tree.slice();
2679 var node;
2680 while ((node = toVisit.pop())) {
2681 var tree = node.ids;
2682 var branches = tree[2];
2683 var pos = node.pos;
2684 if (branches.length) { // non-leaf
2685 for (var i = 0, len = branches.length; i < len; i++) {
2686 toVisit.push({pos: pos + 1, ids: branches[i]});
2687 }
2688 continue;
2689 }
2690 var deleted = !!tree[1].deleted;
2691 var id = tree[0];
2692 // sort by deleted, then pos, then id
2693 if (!winningId || (winningDeleted !== deleted ? winningDeleted :
2694 winningPos !== pos ? winningPos < pos : winningId < id)) {
2695 winningId = id;
2696 winningPos = pos;
2697 winningDeleted = deleted;
2698 }
2699 }
2700
2701 return winningPos + '-' + winningId;
2702}
2703
2704// Pretty much all below can be combined into a higher order function to
2705// traverse revisions
2706// The return value from the callback will be passed as context to all
2707// children of that node
2708function traverseRevTree(revs, callback) {
2709 var toVisit = revs.slice();
2710
2711 var node;
2712 while ((node = toVisit.pop())) {
2713 var pos = node.pos;
2714 var tree = node.ids;
2715 var branches = tree[2];
2716 var newCtx =
2717 callback(branches.length === 0, pos, tree[0], node.ctx, tree[1]);
2718 for (var i = 0, len = branches.length; i < len; i++) {
2719 toVisit.push({pos: pos + 1, ids: branches[i], ctx: newCtx});
2720 }
2721 }
2722}
2723
2724function sortByPos(a, b) {
2725 return a.pos - b.pos;
2726}
2727
2728function collectLeaves(revs) {
2729 var leaves = [];
2730 traverseRevTree(revs, function (isLeaf, pos, id, acc, opts) {
2731 if (isLeaf) {
2732 leaves.push({rev: pos + "-" + id, pos: pos, opts: opts});
2733 }
2734 });
2735 leaves.sort(sortByPos).reverse();
2736 for (var i = 0, len = leaves.length; i < len; i++) {
2737 delete leaves[i].pos;
2738 }
2739 return leaves;
2740}
2741
2742// returns revs of all conflicts that is leaves such that
2743// 1. are not deleted and
2744// 2. are different than winning revision
2745function collectConflicts(metadata) {
2746 var win = winningRev(metadata);
2747 var leaves = collectLeaves(metadata.rev_tree);
2748 var conflicts = [];
2749 for (var i = 0, len = leaves.length; i < len; i++) {
2750 var leaf = leaves[i];
2751 if (leaf.rev !== win && !leaf.opts.deleted) {
2752 conflicts.push(leaf.rev);
2753 }
2754 }
2755 return conflicts;
2756}
2757
2758// compact a tree by marking its non-leafs as missing,
2759// and return a list of revs to delete
2760function compactTree(metadata) {
2761 var revs = [];
2762 traverseRevTree(metadata.rev_tree, function (isLeaf, pos,
2763 revHash, ctx, opts) {
2764 if (opts.status === 'available' && !isLeaf) {
2765 revs.push(pos + '-' + revHash);
2766 opts.status = 'missing';
2767 }
2768 });
2769 return revs;
2770}
2771
2772// build up a list of all the paths to the leafs in this revision tree
2773function rootToLeaf(revs) {
2774 var paths = [];
2775 var toVisit = revs.slice();
2776 var node;
2777 while ((node = toVisit.pop())) {
2778 var pos = node.pos;
2779 var tree = node.ids;
2780 var id = tree[0];
2781 var opts = tree[1];
2782 var branches = tree[2];
2783 var isLeaf = branches.length === 0;
2784
2785 var history = node.history ? node.history.slice() : [];
2786 history.push({id: id, opts: opts});
2787 if (isLeaf) {
2788 paths.push({pos: (pos + 1 - history.length), ids: history});
2789 }
2790 for (var i = 0, len = branches.length; i < len; i++) {
2791 toVisit.push({pos: pos + 1, ids: branches[i], history: history});
2792 }
2793 }
2794 return paths.reverse();
2795}
2796
2797// for a better overview of what this is doing, read:
2798
2799function sortByPos$1(a, b) {
2800 return a.pos - b.pos;
2801}
2802
2803// classic binary search
2804function binarySearch(arr, item, comparator) {
2805 var low = 0;
2806 var high = arr.length;
2807 var mid;
2808 while (low < high) {
2809 mid = (low + high) >>> 1;
2810 if (comparator(arr[mid], item) < 0) {
2811 low = mid + 1;
2812 } else {
2813 high = mid;
2814 }
2815 }
2816 return low;
2817}
2818
2819// assuming the arr is sorted, insert the item in the proper place
2820function insertSorted(arr, item, comparator) {
2821 var idx = binarySearch(arr, item, comparator);
2822 arr.splice(idx, 0, item);
2823}
2824
2825// Turn a path as a flat array into a tree with a single branch.
2826// If any should be stemmed from the beginning of the array, that's passed
2827// in as the second argument
2828function pathToTree(path, numStemmed) {
2829 var root;
2830 var leaf;
2831 for (var i = numStemmed, len = path.length; i < len; i++) {
2832 var node = path[i];
2833 var currentLeaf = [node.id, node.opts, []];
2834 if (leaf) {
2835 leaf[2].push(currentLeaf);
2836 leaf = currentLeaf;
2837 } else {
2838 root = leaf = currentLeaf;
2839 }
2840 }
2841 return root;
2842}
2843
2844// compare the IDs of two trees
2845function compareTree(a, b) {
2846 return a[0] < b[0] ? -1 : 1;
2847}
2848
2849// Merge two trees together
2850// The roots of tree1 and tree2 must be the same revision
2851function mergeTree(in_tree1, in_tree2) {
2852 var queue = [{tree1: in_tree1, tree2: in_tree2}];
2853 var conflicts = false;
2854 while (queue.length > 0) {
2855 var item = queue.pop();
2856 var tree1 = item.tree1;
2857 var tree2 = item.tree2;
2858
2859 if (tree1[1].status || tree2[1].status) {
2860 tree1[1].status =
2861 (tree1[1].status === 'available' ||
2862 tree2[1].status === 'available') ? 'available' : 'missing';
2863 }
2864
2865 for (var i = 0; i < tree2[2].length; i++) {
2866 if (!tree1[2][0]) {
2867 conflicts = 'new_leaf';
2868 tree1[2][0] = tree2[2][i];
2869 continue;
2870 }
2871
2872 var merged = false;
2873 for (var j = 0; j < tree1[2].length; j++) {
2874 if (tree1[2][j][0] === tree2[2][i][0]) {
2875 queue.push({tree1: tree1[2][j], tree2: tree2[2][i]});
2876 merged = true;
2877 }
2878 }
2879 if (!merged) {
2880 conflicts = 'new_branch';
2881 insertSorted(tree1[2], tree2[2][i], compareTree);
2882 }
2883 }
2884 }
2885 return {conflicts: conflicts, tree: in_tree1};
2886}
2887
2888function doMerge(tree, path, dontExpand) {
2889 var restree = [];
2890 var conflicts = false;
2891 var merged = false;
2892 var res;
2893
2894 if (!tree.length) {
2895 return {tree: [path], conflicts: 'new_leaf'};
2896 }
2897
2898 for (var i = 0, len = tree.length; i < len; i++) {
2899 var branch = tree[i];
2900 if (branch.pos === path.pos && branch.ids[0] === path.ids[0]) {
2901 // Paths start at the same position and have the same root, so they need
2902 // merged
2903 res = mergeTree(branch.ids, path.ids);
2904 restree.push({pos: branch.pos, ids: res.tree});
2905 conflicts = conflicts || res.conflicts;
2906 merged = true;
2907 } else if (dontExpand !== true) {
2908 // The paths start at a different position, take the earliest path and
2909 // traverse up until it as at the same point from root as the path we
2910 // want to merge. If the keys match we return the longer path with the
2911 // other merged After stemming we dont want to expand the trees
2912
2913 var t1 = branch.pos < path.pos ? branch : path;
2914 var t2 = branch.pos < path.pos ? path : branch;
2915 var diff = t2.pos - t1.pos;
2916
2917 var candidateParents = [];
2918
2919 var trees = [];
2920 trees.push({ids: t1.ids, diff: diff, parent: null, parentIdx: null});
2921 while (trees.length > 0) {
2922 var item = trees.pop();
2923 if (item.diff === 0) {
2924 if (item.ids[0] === t2.ids[0]) {
2925 candidateParents.push(item);
2926 }
2927 continue;
2928 }
2929 var elements = item.ids[2];
2930 for (var j = 0, elementsLen = elements.length; j < elementsLen; j++) {
2931 trees.push({
2932 ids: elements[j],
2933 diff: item.diff - 1,
2934 parent: item.ids,
2935 parentIdx: j
2936 });
2937 }
2938 }
2939
2940 var el = candidateParents[0];
2941
2942 if (!el) {
2943 restree.push(branch);
2944 } else {
2945 res = mergeTree(el.ids, t2.ids);
2946 el.parent[2][el.parentIdx] = res.tree;
2947 restree.push({pos: t1.pos, ids: t1.ids});
2948 conflicts = conflicts || res.conflicts;
2949 merged = true;
2950 }
2951 } else {
2952 restree.push(branch);
2953 }
2954 }
2955
2956 // We didnt find
2957 if (!merged) {
2958 restree.push(path);
2959 }
2960
2961 restree.sort(sortByPos$1);
2962
2963 return {
2964 tree: restree,
2965 conflicts: conflicts || 'internal_node'
2966 };
2967}
2968
2969// To ensure we dont grow the revision tree infinitely, we stem old revisions
2970function stem(tree, depth) {
2971 // First we break out the tree into a complete list of root to leaf paths
2972 var paths = rootToLeaf(tree);
2973 var stemmedRevs;
2974
2975 var result;
2976 for (var i = 0, len = paths.length; i < len; i++) {
2977 // Then for each path, we cut off the start of the path based on the
2978 // `depth` to stem to, and generate a new set of flat trees
2979 var path = paths[i];
2980 var stemmed = path.ids;
2981 var node;
2982 if (stemmed.length > depth) {
2983 // only do the stemming work if we actually need to stem
2984 if (!stemmedRevs) {
2985 stemmedRevs = {}; // avoid allocating this object unnecessarily
2986 }
2987 var numStemmed = stemmed.length - depth;
2988 node = {
2989 pos: path.pos + numStemmed,
2990 ids: pathToTree(stemmed, numStemmed)
2991 };
2992
2993 for (var s = 0; s < numStemmed; s++) {
2994 var rev = (path.pos + s) + '-' + stemmed[s].id;
2995 stemmedRevs[rev] = true;
2996 }
2997 } else { // no need to actually stem
2998 node = {
2999 pos: path.pos,
3000 ids: pathToTree(stemmed, 0)
3001 };
3002 }
3003
3004 // Then we remerge all those flat trees together, ensuring that we dont
3005 // connect trees that would go beyond the depth limit
3006 if (result) {
3007 result = doMerge(result, node, true).tree;
3008 } else {
3009 result = [node];
3010 }
3011 }
3012
3013 // this is memory-heavy per Chrome profiler, avoid unless we actually stemmed
3014 if (stemmedRevs) {
3015 traverseRevTree(result, function (isLeaf, pos, revHash) {
3016 // some revisions may have been removed in a branch but not in another
3017 delete stemmedRevs[pos + '-' + revHash];
3018 });
3019 }
3020
3021 return {
3022 tree: result,
3023 revs: stemmedRevs ? Object.keys(stemmedRevs) : []
3024 };
3025}
3026
3027function merge(tree, path, depth) {
3028 var newTree = doMerge(tree, path);
3029 var stemmed = stem(newTree.tree, depth);
3030 return {
3031 tree: stemmed.tree,
3032 stemmedRevs: stemmed.revs,
3033 conflicts: newTree.conflicts
3034 };
3035}
3036
3037// return true if a rev exists in the rev tree, false otherwise
3038function revExists(revs, rev) {
3039 var toVisit = revs.slice();
3040 var splitRev = rev.split('-');
3041 var targetPos = parseInt(splitRev[0], 10);
3042 var targetId = splitRev[1];
3043
3044 var node;
3045 while ((node = toVisit.pop())) {
3046 if (node.pos === targetPos && node.ids[0] === targetId) {
3047 return true;
3048 }
3049 var branches = node.ids[2];
3050 for (var i = 0, len = branches.length; i < len; i++) {
3051 toVisit.push({pos: node.pos + 1, ids: branches[i]});
3052 }
3053 }
3054 return false;
3055}
3056
3057function getTrees(node) {
3058 return node.ids;
3059}
3060
3061// check if a specific revision of a doc has been deleted
3062// - metadata: the metadata object from the doc store
3063// - rev: (optional) the revision to check. defaults to winning revision
3064function isDeleted(metadata, rev) {
3065 if (!rev) {
3066 rev = winningRev(metadata);
3067 }
3068 var id = rev.substring(rev.indexOf('-') + 1);
3069 var toVisit = metadata.rev_tree.map(getTrees);
3070
3071 var tree;
3072 while ((tree = toVisit.pop())) {
3073 if (tree[0] === id) {
3074 return !!tree[1].deleted;
3075 }
3076 toVisit = toVisit.concat(tree[2]);
3077 }
3078}
3079
3080function isLocalId(id) {
3081 return (/^_local/).test(id);
3082}
3083
3084// returns the current leaf node for a given revision
3085function latest(rev, metadata) {
3086 var toVisit = metadata.rev_tree.slice();
3087 var node;
3088 while ((node = toVisit.pop())) {
3089 var pos = node.pos;
3090 var tree = node.ids;
3091 var id = tree[0];
3092 var opts = tree[1];
3093 var branches = tree[2];
3094 var isLeaf = branches.length === 0;
3095
3096 var history = node.history ? node.history.slice() : [];
3097 history.push({id: id, pos: pos, opts: opts});
3098
3099 if (isLeaf) {
3100 for (var i = 0, len = history.length; i < len; i++) {
3101 var historyNode = history[i];
3102 var historyRev = historyNode.pos + '-' + historyNode.id;
3103
3104 if (historyRev === rev) {
3105 // return the rev of this leaf
3106 return pos + '-' + id;
3107 }
3108 }
3109 }
3110
3111 for (var j = 0, l = branches.length; j < l; j++) {
3112 toVisit.push({pos: pos + 1, ids: branches[j], history: history});
3113 }
3114 }
3115
3116 /* istanbul ignore next */
3117 throw new Error('Unable to resolve latest revision for id ' + metadata.id + ', rev ' + rev);
3118}
3119
3120function parseBase64(data) {
3121 try {
3122 return thisAtob(data);
3123 } catch (e) {
3124 var err = createError(BAD_ARG,
3125 'Attachment is not a valid base64 string');
3126 return {error: err};
3127 }
3128}
3129
3130function preprocessString(att, blobType, callback) {
3131 var asBinary = parseBase64(att.data);
3132 if (asBinary.error) {
3133 return callback(asBinary.error);
3134 }
3135
3136 att.length = asBinary.length;
3137 if (blobType === 'blob') {
3138 att.data = binStringToBluffer(asBinary, att.content_type);
3139 } else if (blobType === 'base64') {
3140 att.data = thisBtoa(asBinary);
3141 } else { // binary
3142 att.data = asBinary;
3143 }
3144 binaryMd5(asBinary, function (result) {
3145 att.digest = 'md5-' + result;
3146 callback();
3147 });
3148}
3149
3150function preprocessBlob(att, blobType, callback) {
3151 binaryMd5(att.data, function (md5) {
3152 att.digest = 'md5-' + md5;
3153 // size is for blobs (browser), length is for buffers (node)
3154 att.length = att.data.size || att.data.length || 0;
3155 if (blobType === 'binary') {
3156 blobToBinaryString(att.data, function (binString) {
3157 att.data = binString;
3158 callback();
3159 });
3160 } else if (blobType === 'base64') {
3161 blobToBase64(att.data, function (b64) {
3162 att.data = b64;
3163 callback();
3164 });
3165 } else {
3166 callback();
3167 }
3168 });
3169}
3170
3171function preprocessAttachment(att, blobType, callback) {
3172 if (att.stub) {
3173 return callback();
3174 }
3175 if (typeof att.data === 'string') { // input is a base64 string
3176 preprocessString(att, blobType, callback);
3177 } else { // input is a blob
3178 preprocessBlob(att, blobType, callback);
3179 }
3180}
3181
3182function preprocessAttachments(docInfos, blobType, callback) {
3183
3184 if (!docInfos.length) {
3185 return callback();
3186 }
3187
3188 var docv = 0;
3189 var overallErr;
3190
3191 docInfos.forEach(function (docInfo) {
3192 var attachments = docInfo.data && docInfo.data._attachments ?
3193 Object.keys(docInfo.data._attachments) : [];
3194 var recv = 0;
3195
3196 if (!attachments.length) {
3197 return done();
3198 }
3199
3200 function processedAttachment(err) {
3201 overallErr = err;
3202 recv++;
3203 if (recv === attachments.length) {
3204 done();
3205 }
3206 }
3207
3208 for (var key in docInfo.data._attachments) {
3209 if (docInfo.data._attachments.hasOwnProperty(key)) {
3210 preprocessAttachment(docInfo.data._attachments[key],
3211 blobType, processedAttachment);
3212 }
3213 }
3214 });
3215
3216 function done() {
3217 docv++;
3218 if (docInfos.length === docv) {
3219 if (overallErr) {
3220 callback(overallErr);
3221 } else {
3222 callback();
3223 }
3224 }
3225 }
3226}
3227
3228function updateDoc(revLimit, prev, docInfo, results,
3229 i, cb, writeDoc, newEdits) {
3230
3231 if (revExists(prev.rev_tree, docInfo.metadata.rev) && !newEdits) {
3232 results[i] = docInfo;
3233 return cb();
3234 }
3235
3236 // sometimes this is pre-calculated. historically not always
3237 var previousWinningRev = prev.winningRev || winningRev(prev);
3238 var previouslyDeleted = 'deleted' in prev ? prev.deleted :
3239 isDeleted(prev, previousWinningRev);
3240 var deleted = 'deleted' in docInfo.metadata ? docInfo.metadata.deleted :
3241 isDeleted(docInfo.metadata);
3242 var isRoot = /^1-/.test(docInfo.metadata.rev);
3243
3244 if (previouslyDeleted && !deleted && newEdits && isRoot) {
3245 var newDoc = docInfo.data;
3246 newDoc._rev = previousWinningRev;
3247 newDoc._id = docInfo.metadata.id;
3248 docInfo = parseDoc(newDoc, newEdits);
3249 }
3250
3251 var merged = merge(prev.rev_tree, docInfo.metadata.rev_tree[0], revLimit);
3252
3253 var inConflict = newEdits && ((
3254 (previouslyDeleted && deleted && merged.conflicts !== 'new_leaf') ||
3255 (!previouslyDeleted && merged.conflicts !== 'new_leaf') ||
3256 (previouslyDeleted && !deleted && merged.conflicts === 'new_branch')));
3257
3258 if (inConflict) {
3259 var err = createError(REV_CONFLICT);
3260 results[i] = err;
3261 return cb();
3262 }
3263
3264 var newRev = docInfo.metadata.rev;
3265 docInfo.metadata.rev_tree = merged.tree;
3266 docInfo.stemmedRevs = merged.stemmedRevs || [];
3267 /* istanbul ignore else */
3268 if (prev.rev_map) {
3269 docInfo.metadata.rev_map = prev.rev_map; // used only by leveldb
3270 }
3271
3272 // recalculate
3273 var winningRev$$1 = winningRev(docInfo.metadata);
3274 var winningRevIsDeleted = isDeleted(docInfo.metadata, winningRev$$1);
3275
3276 // calculate the total number of documents that were added/removed,
3277 // from the perspective of total_rows/doc_count
3278 var delta = (previouslyDeleted === winningRevIsDeleted) ? 0 :
3279 previouslyDeleted < winningRevIsDeleted ? -1 : 1;
3280
3281 var newRevIsDeleted;
3282 if (newRev === winningRev$$1) {
3283 // if the new rev is the same as the winning rev, we can reuse that value
3284 newRevIsDeleted = winningRevIsDeleted;
3285 } else {
3286 // if they're not the same, then we need to recalculate
3287 newRevIsDeleted = isDeleted(docInfo.metadata, newRev);
3288 }
3289
3290 writeDoc(docInfo, winningRev$$1, winningRevIsDeleted, newRevIsDeleted,
3291 true, delta, i, cb);
3292}
3293
3294function rootIsMissing(docInfo) {
3295 return docInfo.metadata.rev_tree[0].ids[1].status === 'missing';
3296}
3297
3298function processDocs(revLimit, docInfos, api, fetchedDocs, tx, results,
3299 writeDoc, opts, overallCallback) {
3300
3301 // Default to 1000 locally
3302 revLimit = revLimit || 1000;
3303
3304 function insertDoc(docInfo, resultsIdx, callback) {
3305 // Cant insert new deleted documents
3306 var winningRev$$1 = winningRev(docInfo.metadata);
3307 var deleted = isDeleted(docInfo.metadata, winningRev$$1);
3308 if ('was_delete' in opts && deleted) {
3309 results[resultsIdx] = createError(MISSING_DOC, 'deleted');
3310 return callback();
3311 }
3312
3313 // 4712 - detect whether a new document was inserted with a _rev
3314 var inConflict = newEdits && rootIsMissing(docInfo);
3315
3316 if (inConflict) {
3317 var err = createError(REV_CONFLICT);
3318 results[resultsIdx] = err;
3319 return callback();
3320 }
3321
3322 var delta = deleted ? 0 : 1;
3323
3324 writeDoc(docInfo, winningRev$$1, deleted, deleted, false,
3325 delta, resultsIdx, callback);
3326 }
3327
3328 var newEdits = opts.new_edits;
3329 var idsToDocs = new ExportedMap();
3330
3331 var docsDone = 0;
3332 var docsToDo = docInfos.length;
3333
3334 function checkAllDocsDone() {
3335 if (++docsDone === docsToDo && overallCallback) {
3336 overallCallback();
3337 }
3338 }
3339
3340 docInfos.forEach(function (currentDoc, resultsIdx) {
3341
3342 if (currentDoc._id && isLocalId(currentDoc._id)) {
3343 var fun = currentDoc._deleted ? '_removeLocal' : '_putLocal';
3344 api[fun](currentDoc, {ctx: tx}, function (err, res) {
3345 results[resultsIdx] = err || res;
3346 checkAllDocsDone();
3347 });
3348 return;
3349 }
3350
3351 var id = currentDoc.metadata.id;
3352 if (idsToDocs.has(id)) {
3353 docsToDo--; // duplicate
3354 idsToDocs.get(id).push([currentDoc, resultsIdx]);
3355 } else {
3356 idsToDocs.set(id, [[currentDoc, resultsIdx]]);
3357 }
3358 });
3359
3360 // in the case of new_edits, the user can provide multiple docs
3361 // with the same id. these need to be processed sequentially
3362 idsToDocs.forEach(function (docs, id) {
3363 var numDone = 0;
3364
3365 function docWritten() {
3366 if (++numDone < docs.length) {
3367 nextDoc();
3368 } else {
3369 checkAllDocsDone();
3370 }
3371 }
3372 function nextDoc() {
3373 var value = docs[numDone];
3374 var currentDoc = value[0];
3375 var resultsIdx = value[1];
3376
3377 if (fetchedDocs.has(id)) {
3378 updateDoc(revLimit, fetchedDocs.get(id), currentDoc, results,
3379 resultsIdx, docWritten, writeDoc, newEdits);
3380 } else {
3381 // Ensure stemming applies to new writes as well
3382 var merged = merge([], currentDoc.metadata.rev_tree[0], revLimit);
3383 currentDoc.metadata.rev_tree = merged.tree;
3384 currentDoc.stemmedRevs = merged.stemmedRevs || [];
3385 insertDoc(currentDoc, resultsIdx, docWritten);
3386 }
3387 }
3388 nextDoc();
3389 });
3390}
3391
3392function safeJsonParse(str) {
3393 // This try/catch guards against stack overflow errors.
3394 // JSON.parse() is faster than vuvuzela.parse() but vuvuzela
3395 // cannot overflow.
3396 try {
3397 return JSON.parse(str);
3398 } catch (e) {
3399 /* istanbul ignore next */
3400 return vuvuzela.parse(str);
3401 }
3402}
3403
3404function safeJsonStringify(json) {
3405 try {
3406 return JSON.stringify(json);
3407 } catch (e) {
3408 /* istanbul ignore next */
3409 return vuvuzela.stringify(json);
3410 }
3411}
3412
3413//
3414// Parsing hex strings. Yeah.
3415//
3416// So basically we need this because of a bug in WebSQL:
3417// https://code.google.com/p/chromium/issues/detail?id=422690
3418// https://bugs.webkit.org/show_bug.cgi?id=137637
3419//
3420// UTF-8 and UTF-16 are provided as separate functions
3421// for meager performance improvements
3422//
3423
3424function decodeUtf8(str) {
3425 return decodeURIComponent(escape(str));
3426}
3427
3428function hexToInt(charCode) {
3429 // '0'-'9' is 48-57
3430 // 'A'-'F' is 65-70
3431 // SQLite will only give us uppercase hex
3432 return charCode < 65 ? (charCode - 48) : (charCode - 55);
3433}
3434
3435
3436// Example:
3437// pragma encoding=utf8;
3438// select hex('A');
3439// returns '41'
3440function parseHexUtf8(str, start, end) {
3441 var result = '';
3442 while (start < end) {
3443 result += String.fromCharCode(
3444 (hexToInt(str.charCodeAt(start++)) << 4) |
3445 hexToInt(str.charCodeAt(start++)));
3446 }
3447 return result;
3448}
3449
3450// Example:
3451// pragma encoding=utf16;
3452// select hex('A');
3453// returns '4100'
3454// notice that the 00 comes after the 41 (i.e. it's swizzled)
3455function parseHexUtf16(str, start, end) {
3456 var result = '';
3457 while (start < end) {
3458 // UTF-16, so swizzle the bytes
3459 result += String.fromCharCode(
3460 (hexToInt(str.charCodeAt(start + 2)) << 12) |
3461 (hexToInt(str.charCodeAt(start + 3)) << 8) |
3462 (hexToInt(str.charCodeAt(start)) << 4) |
3463 hexToInt(str.charCodeAt(start + 1)));
3464 start += 4;
3465 }
3466 return result;
3467}
3468
3469function parseHexString(str, encoding) {
3470 if (encoding === 'UTF-8') {
3471 return decodeUtf8(parseHexUtf8(str, 0, str.length));
3472 } else {
3473 return parseHexUtf16(str, 0, str.length);
3474 }
3475}
3476
3477function quote(str) {
3478 return "'" + str + "'";
3479}
3480
3481var ADAPTER_VERSION = 7; // used to manage migrations
3482
3483// The object stores created for each database
3484// DOC_STORE stores the document meta data, its revision history and state
3485var DOC_STORE = quote('document-store');
3486// BY_SEQ_STORE stores a particular version of a document, keyed by its
3487// sequence id
3488var BY_SEQ_STORE = quote('by-sequence');
3489// Where we store attachments
3490var ATTACH_STORE = quote('attach-store');
3491var LOCAL_STORE = quote('local-store');
3492var META_STORE = quote('metadata-store');
3493// where we store many-to-many relations between attachment
3494// digests and seqs
3495var ATTACH_AND_SEQ_STORE = quote('attach-seq-store');
3496
3497// escapeBlob and unescapeBlob are workarounds for a websql bug:
3498// https://code.google.com/p/chromium/issues/detail?id=422690
3499// https://bugs.webkit.org/show_bug.cgi?id=137637
3500// The goal is to never actually insert the \u0000 character
3501// in the database.
3502function escapeBlob(str) {
3503 /* eslint-disable no-control-regex */
3504 return str
3505 .replace(/\u0002/g, '\u0002\u0002')
3506 .replace(/\u0001/g, '\u0001\u0002')
3507 .replace(/\u0000/g, '\u0001\u0001');
3508 /* eslint-enable no-control-regex */
3509}
3510
3511function unescapeBlob(str) {
3512 /* eslint-disable no-control-regex */
3513 return str
3514 .replace(/\u0001\u0001/g, '\u0000')
3515 .replace(/\u0001\u0002/g, '\u0001')
3516 .replace(/\u0002\u0002/g, '\u0002');
3517 /* eslint-enable no-control-regex */
3518}
3519
3520function stringifyDoc(doc) {
3521 // don't bother storing the id/rev. it uses lots of space,
3522 // in persistent map/reduce especially
3523 delete doc._id;
3524 delete doc._rev;
3525 return JSON.stringify(doc);
3526}
3527
3528function unstringifyDoc(doc, id, rev) {
3529 doc = JSON.parse(doc);
3530 doc._id = id;
3531 doc._rev = rev;
3532 return doc;
3533}
3534
3535// question mark groups IN queries, e.g. 3 -> '(?,?,?)'
3536function qMarks(num) {
3537 var s = '(';
3538 while (num--) {
3539 s += '?';
3540 if (num) {
3541 s += ',';
3542 }
3543 }
3544 return s + ')';
3545}
3546
3547function select(selector, table, joiner, where, orderBy) {
3548 return 'SELECT ' + selector + ' FROM ' +
3549 (typeof table === 'string' ? table : table.join(' JOIN ')) +
3550 (joiner ? (' ON ' + joiner) : '') +
3551 (where ? (' WHERE ' +
3552 (typeof where === 'string' ? where : where.join(' AND '))) : '') +
3553 (orderBy ? (' ORDER BY ' + orderBy) : '');
3554}
3555
3556function compactRevs(revs, docId, tx) {
3557
3558 if (!revs.length) {
3559 return;
3560 }
3561
3562 var numDone = 0;
3563 var seqs = [];
3564
3565 function checkDone() {
3566 if (++numDone === revs.length) { // done
3567 deleteOrphans();
3568 }
3569 }
3570
3571 function deleteOrphans() {
3572 // find orphaned attachment digests
3573
3574 if (!seqs.length) {
3575 return;
3576 }
3577
3578 var sql = 'SELECT DISTINCT digest AS digest FROM ' +
3579 ATTACH_AND_SEQ_STORE + ' WHERE seq IN ' + qMarks(seqs.length);
3580
3581 tx.executeSql(sql, seqs, function (tx, res) {
3582
3583 var digestsToCheck = [];
3584 for (var i = 0; i < res.rows.length; i++) {
3585 digestsToCheck.push(res.rows.item(i).digest);
3586 }
3587 if (!digestsToCheck.length) {
3588 return;
3589 }
3590
3591 var sql = 'DELETE FROM ' + ATTACH_AND_SEQ_STORE +
3592 ' WHERE seq IN (' +
3593 seqs.map(function () { return '?'; }).join(',') +
3594 ')';
3595 tx.executeSql(sql, seqs, function (tx) {
3596
3597 var sql = 'SELECT digest FROM ' + ATTACH_AND_SEQ_STORE +
3598 ' WHERE digest IN (' +
3599 digestsToCheck.map(function () { return '?'; }).join(',') +
3600 ')';
3601 tx.executeSql(sql, digestsToCheck, function (tx, res) {
3602 var nonOrphanedDigests = new ExportedSet();
3603 for (var i = 0; i < res.rows.length; i++) {
3604 nonOrphanedDigests.add(res.rows.item(i).digest);
3605 }
3606 digestsToCheck.forEach(function (digest) {
3607 if (nonOrphanedDigests.has(digest)) {
3608 return;
3609 }
3610 tx.executeSql(
3611 'DELETE FROM ' + ATTACH_AND_SEQ_STORE + ' WHERE digest=?',
3612 [digest]);
3613 tx.executeSql(
3614 'DELETE FROM ' + ATTACH_STORE + ' WHERE digest=?', [digest]);
3615 });
3616 });
3617 });
3618 });
3619 }
3620
3621 // update by-seq and attach stores in parallel
3622 revs.forEach(function (rev) {
3623 var sql = 'SELECT seq FROM ' + BY_SEQ_STORE +
3624 ' WHERE doc_id=? AND rev=?';
3625
3626 tx.executeSql(sql, [docId, rev], function (tx, res) {
3627 if (!res.rows.length) { // already deleted
3628 return checkDone();
3629 }
3630 var seq = res.rows.item(0).seq;
3631 seqs.push(seq);
3632
3633 tx.executeSql(
3634 'DELETE FROM ' + BY_SEQ_STORE + ' WHERE seq=?', [seq], checkDone);
3635 });
3636 });
3637}
3638
3639function websqlError(callback) {
3640 return function (event) {
3641 guardedConsole('error', 'WebSQL threw an error', event);
3642 // event may actually be a SQLError object, so report is as such
3643 var errorNameMatch = event && event.constructor.toString()
3644 .match(/function ([^(]+)/);
3645 var errorName = (errorNameMatch && errorNameMatch[1]) || event.type;
3646 var errorReason = event.target || event.message;
3647 callback(createError(WSQ_ERROR, errorReason, errorName));
3648 };
3649}
3650
3651function getSize(opts) {
3652 if ('size' in opts) {
3653 // triggers immediate popup in iOS, fixes #2347
3654 // e.g. 5000001 asks for 5 MB, 10000001 asks for 10 MB,
3655 return opts.size * 1000000;
3656 }
3657 // In iOS, doesn't matter as long as it's <= 5000000.
3658 // Except that if you request too much, our tests fail
3659 // because of the native "do you accept?" popup.
3660 // In Android <=4.3, this value is actually used as an
3661 // honest-to-god ceiling for data, so we need to
3662 // set it to a decently high number.
3663 var isAndroid = typeof navigator !== 'undefined' &&
3664 /Android/.test(navigator.userAgent);
3665 return isAndroid ? 5000000 : 1; // in PhantomJS, if you use 0 it will crash
3666}
3667
3668function websqlBulkDocs(dbOpts, req, opts, api, db, websqlChanges, callback) {
3669 var newEdits = opts.new_edits;
3670 var userDocs = req.docs;
3671
3672 // Parse the docs, give them a sequence number for the result
3673 var docInfos = userDocs.map(function (doc) {
3674 if (doc._id && isLocalId(doc._id)) {
3675 return doc;
3676 }
3677 var newDoc = parseDoc(doc, newEdits, dbOpts);
3678 return newDoc;
3679 });
3680
3681 var docInfoErrors = docInfos.filter(function (docInfo) {
3682 return docInfo.error;
3683 });
3684 if (docInfoErrors.length) {
3685 return callback(docInfoErrors[0]);
3686 }
3687
3688 var tx;
3689 var results = new Array(docInfos.length);
3690 var fetchedDocs = new ExportedMap();
3691
3692 var preconditionErrored;
3693 function complete() {
3694 if (preconditionErrored) {
3695 return callback(preconditionErrored);
3696 }
3697 websqlChanges.notify(api._name);
3698 callback(null, results);
3699 }
3700
3701 function verifyAttachment(digest, callback) {
3702 var sql = 'SELECT count(*) as cnt FROM ' + ATTACH_STORE +
3703 ' WHERE digest=?';
3704 tx.executeSql(sql, [digest], function (tx, result) {
3705 if (result.rows.item(0).cnt === 0) {
3706 var err = createError(MISSING_STUB,
3707 'unknown stub attachment with digest ' +
3708 digest);
3709 callback(err);
3710 } else {
3711 callback();
3712 }
3713 });
3714 }
3715
3716 function verifyAttachments(finish) {
3717 var digests = [];
3718 docInfos.forEach(function (docInfo) {
3719 if (docInfo.data && docInfo.data._attachments) {
3720 Object.keys(docInfo.data._attachments).forEach(function (filename) {
3721 var att = docInfo.data._attachments[filename];
3722 if (att.stub) {
3723 digests.push(att.digest);
3724 }
3725 });
3726 }
3727 });
3728 if (!digests.length) {
3729 return finish();
3730 }
3731 var numDone = 0;
3732 var err;
3733
3734 function checkDone() {
3735 if (++numDone === digests.length) {
3736 finish(err);
3737 }
3738 }
3739 digests.forEach(function (digest) {
3740 verifyAttachment(digest, function (attErr) {
3741 if (attErr && !err) {
3742 err = attErr;
3743 }
3744 checkDone();
3745 });
3746 });
3747 }
3748
3749 function writeDoc(docInfo, winningRev$$1, winningRevIsDeleted, newRevIsDeleted,
3750 isUpdate, delta, resultsIdx, callback) {
3751
3752 function finish() {
3753 var data = docInfo.data;
3754 var deletedInt = newRevIsDeleted ? 1 : 0;
3755
3756 var id = data._id;
3757 var rev = data._rev;
3758 var json = stringifyDoc(data);
3759 var sql = 'INSERT INTO ' + BY_SEQ_STORE +
3760 ' (doc_id, rev, json, deleted) VALUES (?, ?, ?, ?);';
3761 var sqlArgs = [id, rev, json, deletedInt];
3762
3763 // map seqs to attachment digests, which
3764 // we will need later during compaction
3765 function insertAttachmentMappings(seq, callback) {
3766 var attsAdded = 0;
3767 var attsToAdd = Object.keys(data._attachments || {});
3768
3769 if (!attsToAdd.length) {
3770 return callback();
3771 }
3772 function checkDone() {
3773 if (++attsAdded === attsToAdd.length) {
3774 callback();
3775 }
3776 return false; // ack handling a constraint error
3777 }
3778 function add(att) {
3779 var sql = 'INSERT INTO ' + ATTACH_AND_SEQ_STORE +
3780 ' (digest, seq) VALUES (?,?)';
3781 var sqlArgs = [data._attachments[att].digest, seq];
3782 tx.executeSql(sql, sqlArgs, checkDone, checkDone);
3783 // second callback is for a constaint error, which we ignore
3784 // because this docid/rev has already been associated with
3785 // the digest (e.g. when new_edits == false)
3786 }
3787 for (var i = 0; i < attsToAdd.length; i++) {
3788 add(attsToAdd[i]); // do in parallel
3789 }
3790 }
3791
3792 tx.executeSql(sql, sqlArgs, function (tx, result) {
3793 var seq = result.insertId;
3794 insertAttachmentMappings(seq, function () {
3795 dataWritten(tx, seq);
3796 });
3797 }, function () {
3798 // constraint error, recover by updating instead (see #1638)
3799 var fetchSql = select('seq', BY_SEQ_STORE, null,
3800 'doc_id=? AND rev=?');
3801 tx.executeSql(fetchSql, [id, rev], function (tx, res) {
3802 var seq = res.rows.item(0).seq;
3803 var sql = 'UPDATE ' + BY_SEQ_STORE +
3804 ' SET json=?, deleted=? WHERE doc_id=? AND rev=?;';
3805 var sqlArgs = [json, deletedInt, id, rev];
3806 tx.executeSql(sql, sqlArgs, function (tx) {
3807 insertAttachmentMappings(seq, function () {
3808 dataWritten(tx, seq);
3809 });
3810 });
3811 });
3812 return false; // ack that we've handled the error
3813 });
3814 }
3815
3816 function collectResults(attachmentErr) {
3817 if (!err) {
3818 if (attachmentErr) {
3819 err = attachmentErr;
3820 callback(err);
3821 } else if (recv === attachments.length) {
3822 finish();
3823 }
3824 }
3825 }
3826
3827 var err = null;
3828 var recv = 0;
3829
3830 docInfo.data._id = docInfo.metadata.id;
3831 docInfo.data._rev = docInfo.metadata.rev;
3832 var attachments = Object.keys(docInfo.data._attachments || {});
3833
3834
3835 if (newRevIsDeleted) {
3836 docInfo.data._deleted = true;
3837 }
3838
3839 function attachmentSaved(err) {
3840 recv++;
3841 collectResults(err);
3842 }
3843
3844 attachments.forEach(function (key) {
3845 var att = docInfo.data._attachments[key];
3846 if (!att.stub) {
3847 var data = att.data;
3848 delete att.data;
3849 att.revpos = parseInt(winningRev$$1, 10);
3850 var digest = att.digest;
3851 saveAttachment(digest, data, attachmentSaved);
3852 } else {
3853 recv++;
3854 collectResults();
3855 }
3856 });
3857
3858 if (!attachments.length) {
3859 finish();
3860 }
3861
3862 function dataWritten(tx, seq) {
3863 var id = docInfo.metadata.id;
3864
3865 var revsToCompact = docInfo.stemmedRevs || [];
3866 if (isUpdate && api.auto_compaction) {
3867 revsToCompact = compactTree(docInfo.metadata).concat(revsToCompact);
3868 }
3869 if (revsToCompact.length) {
3870 compactRevs(revsToCompact, id, tx);
3871 }
3872
3873 docInfo.metadata.seq = seq;
3874 var rev = docInfo.metadata.rev;
3875 delete docInfo.metadata.rev;
3876
3877 var sql = isUpdate ?
3878 'UPDATE ' + DOC_STORE +
3879 ' SET json=?, max_seq=?, winningseq=' +
3880 '(SELECT seq FROM ' + BY_SEQ_STORE +
3881 ' WHERE doc_id=' + DOC_STORE + '.id AND rev=?) WHERE id=?'
3882 : 'INSERT INTO ' + DOC_STORE +
3883 ' (id, winningseq, max_seq, json) VALUES (?,?,?,?);';
3884 var metadataStr = safeJsonStringify(docInfo.metadata);
3885 var params = isUpdate ?
3886 [metadataStr, seq, winningRev$$1, id] :
3887 [id, seq, seq, metadataStr];
3888 tx.executeSql(sql, params, function () {
3889 results[resultsIdx] = {
3890 ok: true,
3891 id: docInfo.metadata.id,
3892 rev: rev
3893 };
3894 fetchedDocs.set(id, docInfo.metadata);
3895 callback();
3896 });
3897 }
3898 }
3899
3900 function websqlProcessDocs() {
3901 processDocs(dbOpts.revs_limit, docInfos, api, fetchedDocs, tx,
3902 results, writeDoc, opts);
3903 }
3904
3905 function fetchExistingDocs(callback) {
3906 if (!docInfos.length) {
3907 return callback();
3908 }
3909
3910 var numFetched = 0;
3911
3912 function checkDone() {
3913 if (++numFetched === docInfos.length) {
3914 callback();
3915 }
3916 }
3917
3918 docInfos.forEach(function (docInfo) {
3919 if (docInfo._id && isLocalId(docInfo._id)) {
3920 return checkDone(); // skip local docs
3921 }
3922 var id = docInfo.metadata.id;
3923 tx.executeSql('SELECT json FROM ' + DOC_STORE +
3924 ' WHERE id = ?', [id], function (tx, result) {
3925 if (result.rows.length) {
3926 var metadata = safeJsonParse(result.rows.item(0).json);
3927 fetchedDocs.set(id, metadata);
3928 }
3929 checkDone();
3930 });
3931 });
3932 }
3933
3934 function saveAttachment(digest, data, callback) {
3935 var sql = 'SELECT digest FROM ' + ATTACH_STORE + ' WHERE digest=?';
3936 tx.executeSql(sql, [digest], function (tx, result) {
3937 if (result.rows.length) { // attachment already exists
3938 return callback();
3939 }
3940 // we could just insert before selecting and catch the error,
3941 // but my hunch is that it's cheaper not to serialize the blob
3942 // from JS to C if we don't have to (TODO: confirm this)
3943 sql = 'INSERT INTO ' + ATTACH_STORE +
3944 ' (digest, body, escaped) VALUES (?,?,1)';
3945 tx.executeSql(sql, [digest, escapeBlob(data)], function () {
3946 callback();
3947 }, function () {
3948 // ignore constaint errors, means it already exists
3949 callback();
3950 return false; // ack we handled the error
3951 });
3952 });
3953 }
3954
3955 preprocessAttachments(docInfos, 'binary', function (err) {
3956 if (err) {
3957 return callback(err);
3958 }
3959 db.transaction(function (txn) {
3960 tx = txn;
3961 verifyAttachments(function (err) {
3962 if (err) {
3963 preconditionErrored = err;
3964 } else {
3965 fetchExistingDocs(websqlProcessDocs);
3966 }
3967 });
3968 }, websqlError(callback), complete);
3969 });
3970}
3971
3972var cachedDatabases = new ExportedMap();
3973
3974// openDatabase passed in through opts (e.g. for node-websql)
3975function openDatabaseWithOpts(opts) {
3976 return opts.websql(opts.name, opts.version, opts.description, opts.size);
3977}
3978
3979function openDBSafely(opts) {
3980 try {
3981 return {
3982 db: openDatabaseWithOpts(opts)
3983 };
3984 } catch (err) {
3985 return {
3986 error: err
3987 };
3988 }
3989}
3990
3991function openDB(opts) {
3992 var cachedResult = cachedDatabases.get(opts.name);
3993 if (!cachedResult) {
3994 cachedResult = openDBSafely(opts);
3995 cachedDatabases.set(opts.name, cachedResult);
3996 }
3997 return cachedResult;
3998}
3999
4000var websqlChanges = new Changes();
4001
4002function fetchAttachmentsIfNecessary(doc, opts, api, txn, cb) {
4003 var attachments = Object.keys(doc._attachments || {});
4004 if (!attachments.length) {
4005 return cb && cb();
4006 }
4007 var numDone = 0;
4008
4009 function checkDone() {
4010 if (++numDone === attachments.length && cb) {
4011 cb();
4012 }
4013 }
4014
4015 function fetchAttachment(doc, att) {
4016 var attObj = doc._attachments[att];
4017 var attOpts = {binary: opts.binary, ctx: txn};
4018 api._getAttachment(doc._id, att, attObj, attOpts, function (_, data) {
4019 doc._attachments[att] = $inject_Object_assign(
4020 pick(attObj, ['digest', 'content_type']),
4021 { data: data }
4022 );
4023 checkDone();
4024 });
4025 }
4026
4027 attachments.forEach(function (att) {
4028 if (opts.attachments && opts.include_docs) {
4029 fetchAttachment(doc, att);
4030 } else {
4031 doc._attachments[att].stub = true;
4032 checkDone();
4033 }
4034 });
4035}
4036
4037var POUCH_VERSION = 1;
4038
4039// these indexes cover the ground for most allDocs queries
4040var BY_SEQ_STORE_DELETED_INDEX_SQL =
4041 'CREATE INDEX IF NOT EXISTS \'by-seq-deleted-idx\' ON ' +
4042 BY_SEQ_STORE + ' (seq, deleted)';
4043var BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL =
4044 'CREATE UNIQUE INDEX IF NOT EXISTS \'by-seq-doc-id-rev\' ON ' +
4045 BY_SEQ_STORE + ' (doc_id, rev)';
4046var DOC_STORE_WINNINGSEQ_INDEX_SQL =
4047 'CREATE INDEX IF NOT EXISTS \'doc-winningseq-idx\' ON ' +
4048 DOC_STORE + ' (winningseq)';
4049var ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL =
4050 'CREATE INDEX IF NOT EXISTS \'attach-seq-seq-idx\' ON ' +
4051 ATTACH_AND_SEQ_STORE + ' (seq)';
4052var ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL =
4053 'CREATE UNIQUE INDEX IF NOT EXISTS \'attach-seq-digest-idx\' ON ' +
4054 ATTACH_AND_SEQ_STORE + ' (digest, seq)';
4055
4056var DOC_STORE_AND_BY_SEQ_JOINER = BY_SEQ_STORE +
4057 '.seq = ' + DOC_STORE + '.winningseq';
4058
4059var SELECT_DOCS = BY_SEQ_STORE + '.seq AS seq, ' +
4060 BY_SEQ_STORE + '.deleted AS deleted, ' +
4061 BY_SEQ_STORE + '.json AS data, ' +
4062 BY_SEQ_STORE + '.rev AS rev, ' +
4063 DOC_STORE + '.json AS metadata';
4064
4065function WebSqlPouch(opts, callback) {
4066 var api = this;
4067 var instanceId = null;
4068 var size = getSize(opts);
4069 var idRequests = [];
4070 var encoding;
4071
4072 api._name = opts.name;
4073
4074 // extend the options here, because sqlite plugin has a ton of options
4075 // and they are constantly changing, so it's more prudent to allow anything
4076 var websqlOpts = $inject_Object_assign({}, opts, {
4077 version: POUCH_VERSION,
4078 description: opts.name,
4079 size: size
4080 });
4081 var openDBResult = openDB(websqlOpts);
4082 if (openDBResult.error) {
4083 return websqlError(callback)(openDBResult.error);
4084 }
4085 var db = openDBResult.db;
4086 if (typeof db.readTransaction !== 'function') {
4087 // doesn't exist in sqlite plugin
4088 db.readTransaction = db.transaction;
4089 }
4090
4091 function dbCreated() {
4092 // note the db name in case the browser upgrades to idb
4093 if (hasLocalStorage()) {
4094 window.localStorage['_pouch__websqldb_' + api._name] = true;
4095 }
4096 callback(null, api);
4097 }
4098
4099 // In this migration, we added the 'deleted' and 'local' columns to the
4100 // by-seq and doc store tables.
4101 // To preserve existing user data, we re-process all the existing JSON
4102 // and add these values.
4103 // Called migration2 because it corresponds to adapter version (db_version) #2
4104 function runMigration2(tx, callback) {
4105 // index used for the join in the allDocs query
4106 tx.executeSql(DOC_STORE_WINNINGSEQ_INDEX_SQL);
4107
4108 tx.executeSql('ALTER TABLE ' + BY_SEQ_STORE +
4109 ' ADD COLUMN deleted TINYINT(1) DEFAULT 0', [], function () {
4110 tx.executeSql(BY_SEQ_STORE_DELETED_INDEX_SQL);
4111 tx.executeSql('ALTER TABLE ' + DOC_STORE +
4112 ' ADD COLUMN local TINYINT(1) DEFAULT 0', [], function () {
4113 tx.executeSql('CREATE INDEX IF NOT EXISTS \'doc-store-local-idx\' ON ' +
4114 DOC_STORE + ' (local, id)');
4115
4116 var sql = 'SELECT ' + DOC_STORE + '.winningseq AS seq, ' + DOC_STORE +
4117 '.json AS metadata FROM ' + BY_SEQ_STORE + ' JOIN ' + DOC_STORE +
4118 ' ON ' + BY_SEQ_STORE + '.seq = ' + DOC_STORE + '.winningseq';
4119
4120 tx.executeSql(sql, [], function (tx, result) {
4121
4122 var deleted = [];
4123 var local = [];
4124
4125 for (var i = 0; i < result.rows.length; i++) {
4126 var item = result.rows.item(i);
4127 var seq = item.seq;
4128 var metadata = JSON.parse(item.metadata);
4129 if (isDeleted(metadata)) {
4130 deleted.push(seq);
4131 }
4132 if (isLocalId(metadata.id)) {
4133 local.push(metadata.id);
4134 }
4135 }
4136 tx.executeSql('UPDATE ' + DOC_STORE + 'SET local = 1 WHERE id IN ' +
4137 qMarks(local.length), local, function () {
4138 tx.executeSql('UPDATE ' + BY_SEQ_STORE +
4139 ' SET deleted = 1 WHERE seq IN ' +
4140 qMarks(deleted.length), deleted, callback);
4141 });
4142 });
4143 });
4144 });
4145 }
4146
4147 // in this migration, we make all the local docs unversioned
4148 function runMigration3(tx, callback) {
4149 var local = 'CREATE TABLE IF NOT EXISTS ' + LOCAL_STORE +
4150 ' (id UNIQUE, rev, json)';
4151 tx.executeSql(local, [], function () {
4152 var sql = 'SELECT ' + DOC_STORE + '.id AS id, ' +
4153 BY_SEQ_STORE + '.json AS data ' +
4154 'FROM ' + BY_SEQ_STORE + ' JOIN ' +
4155 DOC_STORE + ' ON ' + BY_SEQ_STORE + '.seq = ' +
4156 DOC_STORE + '.winningseq WHERE local = 1';
4157 tx.executeSql(sql, [], function (tx, res) {
4158 var rows = [];
4159 for (var i = 0; i < res.rows.length; i++) {
4160 rows.push(res.rows.item(i));
4161 }
4162 function doNext() {
4163 if (!rows.length) {
4164 return callback(tx);
4165 }
4166 var row = rows.shift();
4167 var rev = JSON.parse(row.data)._rev;
4168 tx.executeSql('INSERT INTO ' + LOCAL_STORE +
4169 ' (id, rev, json) VALUES (?,?,?)',
4170 [row.id, rev, row.data], function (tx) {
4171 tx.executeSql('DELETE FROM ' + DOC_STORE + ' WHERE id=?',
4172 [row.id], function (tx) {
4173 tx.executeSql('DELETE FROM ' + BY_SEQ_STORE + ' WHERE seq=?',
4174 [row.seq], function () {
4175 doNext();
4176 });
4177 });
4178 });
4179 }
4180 doNext();
4181 });
4182 });
4183 }
4184
4185 // in this migration, we remove doc_id_rev and just use rev
4186 function runMigration4(tx, callback) {
4187
4188 function updateRows(rows) {
4189 function doNext() {
4190 if (!rows.length) {
4191 return callback(tx);
4192 }
4193 var row = rows.shift();
4194 var doc_id_rev = parseHexString(row.hex, encoding);
4195 var idx = doc_id_rev.lastIndexOf('::');
4196 var doc_id = doc_id_rev.substring(0, idx);
4197 var rev = doc_id_rev.substring(idx + 2);
4198 var sql = 'UPDATE ' + BY_SEQ_STORE +
4199 ' SET doc_id=?, rev=? WHERE doc_id_rev=?';
4200 tx.executeSql(sql, [doc_id, rev, doc_id_rev], function () {
4201 doNext();
4202 });
4203 }
4204 doNext();
4205 }
4206
4207 var sql = 'ALTER TABLE ' + BY_SEQ_STORE + ' ADD COLUMN doc_id';
4208 tx.executeSql(sql, [], function (tx) {
4209 var sql = 'ALTER TABLE ' + BY_SEQ_STORE + ' ADD COLUMN rev';
4210 tx.executeSql(sql, [], function (tx) {
4211 tx.executeSql(BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL, [], function (tx) {
4212 var sql = 'SELECT hex(doc_id_rev) as hex FROM ' + BY_SEQ_STORE;
4213 tx.executeSql(sql, [], function (tx, res) {
4214 var rows = [];
4215 for (var i = 0; i < res.rows.length; i++) {
4216 rows.push(res.rows.item(i));
4217 }
4218 updateRows(rows);
4219 });
4220 });
4221 });
4222 });
4223 }
4224
4225 // in this migration, we add the attach_and_seq table
4226 // for issue #2818
4227 function runMigration5(tx, callback) {
4228
4229 function migrateAttsAndSeqs(tx) {
4230 // need to actually populate the table. this is the expensive part,
4231 // so as an optimization, check first that this database even
4232 // contains attachments
4233 var sql = 'SELECT COUNT(*) AS cnt FROM ' + ATTACH_STORE;
4234 tx.executeSql(sql, [], function (tx, res) {
4235 var count = res.rows.item(0).cnt;
4236 if (!count) {
4237 return callback(tx);
4238 }
4239
4240 var offset = 0;
4241 var pageSize = 10;
4242 function nextPage() {
4243 var sql = select(
4244 SELECT_DOCS + ', ' + DOC_STORE + '.id AS id',
4245 [DOC_STORE, BY_SEQ_STORE],
4246 DOC_STORE_AND_BY_SEQ_JOINER,
4247 null,
4248 DOC_STORE + '.id '
4249 );
4250 sql += ' LIMIT ' + pageSize + ' OFFSET ' + offset;
4251 offset += pageSize;
4252 tx.executeSql(sql, [], function (tx, res) {
4253 if (!res.rows.length) {
4254 return callback(tx);
4255 }
4256 var digestSeqs = {};
4257 function addDigestSeq(digest, seq) {
4258 // uniq digest/seq pairs, just in case there are dups
4259 var seqs = digestSeqs[digest] = (digestSeqs[digest] || []);
4260 if (seqs.indexOf(seq) === -1) {
4261 seqs.push(seq);
4262 }
4263 }
4264 for (var i = 0; i < res.rows.length; i++) {
4265 var row = res.rows.item(i);
4266 var doc = unstringifyDoc(row.data, row.id, row.rev);
4267 var atts = Object.keys(doc._attachments || {});
4268 for (var j = 0; j < atts.length; j++) {
4269 var att = doc._attachments[atts[j]];
4270 addDigestSeq(att.digest, row.seq);
4271 }
4272 }
4273 var digestSeqPairs = [];
4274 Object.keys(digestSeqs).forEach(function (digest) {
4275 var seqs = digestSeqs[digest];
4276 seqs.forEach(function (seq) {
4277 digestSeqPairs.push([digest, seq]);
4278 });
4279 });
4280 if (!digestSeqPairs.length) {
4281 return nextPage();
4282 }
4283 var numDone = 0;
4284 digestSeqPairs.forEach(function (pair) {
4285 var sql = 'INSERT INTO ' + ATTACH_AND_SEQ_STORE +
4286 ' (digest, seq) VALUES (?,?)';
4287 tx.executeSql(sql, pair, function () {
4288 if (++numDone === digestSeqPairs.length) {
4289 nextPage();
4290 }
4291 });
4292 });
4293 });
4294 }
4295 nextPage();
4296 });
4297 }
4298
4299 var attachAndRev = 'CREATE TABLE IF NOT EXISTS ' +
4300 ATTACH_AND_SEQ_STORE + ' (digest, seq INTEGER)';
4301 tx.executeSql(attachAndRev, [], function (tx) {
4302 tx.executeSql(
4303 ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL, [], function (tx) {
4304 tx.executeSql(
4305 ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL, [],
4306 migrateAttsAndSeqs);
4307 });
4308 });
4309 }
4310
4311 // in this migration, we use escapeBlob() and unescapeBlob()
4312 // instead of reading out the binary as HEX, which is slow
4313 function runMigration6(tx, callback) {
4314 var sql = 'ALTER TABLE ' + ATTACH_STORE +
4315 ' ADD COLUMN escaped TINYINT(1) DEFAULT 0';
4316 tx.executeSql(sql, [], callback);
4317 }
4318
4319 // issue #3136, in this migration we need a "latest seq" as well
4320 // as the "winning seq" in the doc store
4321 function runMigration7(tx, callback) {
4322 var sql = 'ALTER TABLE ' + DOC_STORE +
4323 ' ADD COLUMN max_seq INTEGER';
4324 tx.executeSql(sql, [], function (tx) {
4325 var sql = 'UPDATE ' + DOC_STORE + ' SET max_seq=(SELECT MAX(seq) FROM ' +
4326 BY_SEQ_STORE + ' WHERE doc_id=id)';
4327 tx.executeSql(sql, [], function (tx) {
4328 // add unique index after filling, else we'll get a constraint
4329 // error when we do the ALTER TABLE
4330 var sql =
4331 'CREATE UNIQUE INDEX IF NOT EXISTS \'doc-max-seq-idx\' ON ' +
4332 DOC_STORE + ' (max_seq)';
4333 tx.executeSql(sql, [], callback);
4334 });
4335 });
4336 }
4337
4338 function checkEncoding(tx, cb) {
4339 // UTF-8 on chrome/android, UTF-16 on safari < 7.1
4340 tx.executeSql('SELECT HEX("a") AS hex', [], function (tx, res) {
4341 var hex = res.rows.item(0).hex;
4342 encoding = hex.length === 2 ? 'UTF-8' : 'UTF-16';
4343 cb();
4344 }
4345 );
4346 }
4347
4348 function onGetInstanceId() {
4349 while (idRequests.length > 0) {
4350 var idCallback = idRequests.pop();
4351 idCallback(null, instanceId);
4352 }
4353 }
4354
4355 function onGetVersion(tx, dbVersion) {
4356 if (dbVersion === 0) {
4357 // initial schema
4358
4359 var meta = 'CREATE TABLE IF NOT EXISTS ' + META_STORE +
4360 ' (dbid, db_version INTEGER)';
4361 var attach = 'CREATE TABLE IF NOT EXISTS ' + ATTACH_STORE +
4362 ' (digest UNIQUE, escaped TINYINT(1), body BLOB)';
4363 var attachAndRev = 'CREATE TABLE IF NOT EXISTS ' +
4364 ATTACH_AND_SEQ_STORE + ' (digest, seq INTEGER)';
4365 // TODO: migrate winningseq to INTEGER
4366 var doc = 'CREATE TABLE IF NOT EXISTS ' + DOC_STORE +
4367 ' (id unique, json, winningseq, max_seq INTEGER UNIQUE)';
4368 var seq = 'CREATE TABLE IF NOT EXISTS ' + BY_SEQ_STORE +
4369 ' (seq INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ' +
4370 'json, deleted TINYINT(1), doc_id, rev)';
4371 var local = 'CREATE TABLE IF NOT EXISTS ' + LOCAL_STORE +
4372 ' (id UNIQUE, rev, json)';
4373
4374 // creates
4375 tx.executeSql(attach);
4376 tx.executeSql(local);
4377 tx.executeSql(attachAndRev, [], function () {
4378 tx.executeSql(ATTACH_AND_SEQ_STORE_SEQ_INDEX_SQL);
4379 tx.executeSql(ATTACH_AND_SEQ_STORE_ATTACH_INDEX_SQL);
4380 });
4381 tx.executeSql(doc, [], function () {
4382 tx.executeSql(DOC_STORE_WINNINGSEQ_INDEX_SQL);
4383 tx.executeSql(seq, [], function () {
4384 tx.executeSql(BY_SEQ_STORE_DELETED_INDEX_SQL);
4385 tx.executeSql(BY_SEQ_STORE_DOC_ID_REV_INDEX_SQL);
4386 tx.executeSql(meta, [], function () {
4387 // mark the db version, and new dbid
4388 var initSeq = 'INSERT INTO ' + META_STORE +
4389 ' (db_version, dbid) VALUES (?,?)';
4390 instanceId = uuid();
4391 var initSeqArgs = [ADAPTER_VERSION, instanceId];
4392 tx.executeSql(initSeq, initSeqArgs, function () {
4393 onGetInstanceId();
4394 });
4395 });
4396 });
4397 });
4398 } else { // version > 0
4399
4400 var setupDone = function () {
4401 var migrated = dbVersion < ADAPTER_VERSION;
4402 if (migrated) {
4403 // update the db version within this transaction
4404 tx.executeSql('UPDATE ' + META_STORE + ' SET db_version = ' +
4405 ADAPTER_VERSION);
4406 }
4407 // notify db.id() callers
4408 var sql = 'SELECT dbid FROM ' + META_STORE;
4409 tx.executeSql(sql, [], function (tx, result) {
4410 instanceId = result.rows.item(0).dbid;
4411 onGetInstanceId();
4412 });
4413 };
4414
4415 // would love to use promises here, but then websql
4416 // ends the transaction early
4417 var tasks = [
4418 runMigration2,
4419 runMigration3,
4420 runMigration4,
4421 runMigration5,
4422 runMigration6,
4423 runMigration7,
4424 setupDone
4425 ];
4426
4427 // run each migration sequentially
4428 var i = dbVersion;
4429 var nextMigration = function (tx) {
4430 tasks[i - 1](tx, nextMigration);
4431 i++;
4432 };
4433 nextMigration(tx);
4434 }
4435 }
4436
4437 function setup() {
4438 db.transaction(function (tx) {
4439 // first check the encoding
4440 checkEncoding(tx, function () {
4441 // then get the version
4442 fetchVersion(tx);
4443 });
4444 }, websqlError(callback), dbCreated);
4445 }
4446
4447 function fetchVersion(tx) {
4448 var sql = 'SELECT sql FROM sqlite_master WHERE tbl_name = ' + META_STORE;
4449 tx.executeSql(sql, [], function (tx, result) {
4450 if (!result.rows.length) {
4451 // database hasn't even been created yet (version 0)
4452 onGetVersion(tx, 0);
4453 } else if (!/db_version/.test(result.rows.item(0).sql)) {
4454 // table was created, but without the new db_version column,
4455 // so add it.
4456 tx.executeSql('ALTER TABLE ' + META_STORE +
4457 ' ADD COLUMN db_version INTEGER', [], function () {
4458 // before version 2, this column didn't even exist
4459 onGetVersion(tx, 1);
4460 });
4461 } else { // column exists, we can safely get it
4462 tx.executeSql('SELECT db_version FROM ' + META_STORE,
4463 [], function (tx, result) {
4464 var dbVersion = result.rows.item(0).db_version;
4465 onGetVersion(tx, dbVersion);
4466 });
4467 }
4468 });
4469 }
4470
4471 setup();
4472
4473 function getMaxSeq(tx, callback) {
4474 var sql = 'SELECT MAX(seq) AS seq FROM ' + BY_SEQ_STORE;
4475 tx.executeSql(sql, [], function (tx, res) {
4476 var updateSeq = res.rows.item(0).seq || 0;
4477 callback(updateSeq);
4478 });
4479 }
4480
4481 function countDocs(tx, callback) {
4482 // count the total rows
4483 var sql = select(
4484 'COUNT(' + DOC_STORE + '.id) AS \'num\'',
4485 [DOC_STORE, BY_SEQ_STORE],
4486 DOC_STORE_AND_BY_SEQ_JOINER,
4487 BY_SEQ_STORE + '.deleted=0');
4488
4489 tx.executeSql(sql, [], function (tx, result) {
4490 callback(result.rows.item(0).num);
4491 });
4492 }
4493
4494 api._remote = false;
4495 api.type = function () {
4496 return 'websql';
4497 };
4498
4499 api._id = toPromise(function (callback) {
4500 callback(null, instanceId);
4501 });
4502
4503 api._info = function (callback) {
4504 var seq;
4505 var docCount;
4506 db.readTransaction(function (tx) {
4507 getMaxSeq(tx, function (theSeq) {
4508 seq = theSeq;
4509 });
4510 countDocs(tx, function (theDocCount) {
4511 docCount = theDocCount;
4512 });
4513 }, websqlError(callback), function () {
4514 callback(null, {
4515 doc_count: docCount,
4516 update_seq: seq,
4517 websql_encoding: encoding
4518 });
4519 });
4520 };
4521
4522 api._bulkDocs = function (req, reqOpts, callback) {
4523 websqlBulkDocs(opts, req, reqOpts, api, db, websqlChanges, callback);
4524 };
4525
4526 function latest$$1(tx, id, rev, callback, finish) {
4527 var sql = select(
4528 SELECT_DOCS,
4529 [DOC_STORE, BY_SEQ_STORE],
4530 DOC_STORE_AND_BY_SEQ_JOINER,
4531 DOC_STORE + '.id=?');
4532 var sqlArgs = [id];
4533
4534 tx.executeSql(sql, sqlArgs, function (a, results) {
4535 if (!results.rows.length) {
4536 var err = createError(MISSING_DOC, 'missing');
4537 return finish(err);
4538 }
4539 var item = results.rows.item(0);
4540 var metadata = safeJsonParse(item.metadata);
4541 callback(latest(rev, metadata));
4542 });
4543 }
4544
4545 api._get = function (id, opts, callback) {
4546 var doc;
4547 var metadata;
4548 var tx = opts.ctx;
4549 if (!tx) {
4550 return db.readTransaction(function (txn) {
4551 api._get(id, $inject_Object_assign({ctx: txn}, opts), callback);
4552 });
4553 }
4554
4555 function finish(err) {
4556 callback(err, {doc: doc, metadata: metadata, ctx: tx});
4557 }
4558
4559 var sql;
4560 var sqlArgs;
4561
4562 if (!opts.rev) {
4563 sql = select(
4564 SELECT_DOCS,
4565 [DOC_STORE, BY_SEQ_STORE],
4566 DOC_STORE_AND_BY_SEQ_JOINER,
4567 DOC_STORE + '.id=?');
4568 sqlArgs = [id];
4569 } else if (opts.latest) {
4570 latest$$1(tx, id, opts.rev, function (latestRev) {
4571 opts.latest = false;
4572 opts.rev = latestRev;
4573 api._get(id, opts, callback);
4574 }, finish);
4575 return;
4576 } else {
4577 sql = select(
4578 SELECT_DOCS,
4579 [DOC_STORE, BY_SEQ_STORE],
4580 DOC_STORE + '.id=' + BY_SEQ_STORE + '.doc_id',
4581 [BY_SEQ_STORE + '.doc_id=?', BY_SEQ_STORE + '.rev=?']);
4582 sqlArgs = [id, opts.rev];
4583 }
4584
4585 tx.executeSql(sql, sqlArgs, function (a, results) {
4586 if (!results.rows.length) {
4587 var missingErr = createError(MISSING_DOC, 'missing');
4588 return finish(missingErr);
4589 }
4590 var item = results.rows.item(0);
4591 metadata = safeJsonParse(item.metadata);
4592 if (item.deleted && !opts.rev) {
4593 var deletedErr = createError(MISSING_DOC, 'deleted');
4594 return finish(deletedErr);
4595 }
4596 doc = unstringifyDoc(item.data, metadata.id, item.rev);
4597 finish();
4598 });
4599 };
4600
4601 api._allDocs = function (opts, callback) {
4602 var results = [];
4603 var totalRows;
4604 var updateSeq;
4605
4606 var start = 'startkey' in opts ? opts.startkey : false;
4607 var end = 'endkey' in opts ? opts.endkey : false;
4608 var key = 'key' in opts ? opts.key : false;
4609 var keys = 'keys' in opts ? opts.keys : false;
4610 var descending = 'descending' in opts ? opts.descending : false;
4611 var limit = 'limit' in opts ? opts.limit : -1;
4612 var offset = 'skip' in opts ? opts.skip : 0;
4613 var inclusiveEnd = opts.inclusive_end !== false;
4614
4615 var sqlArgs = [];
4616 var criteria = [];
4617
4618 if (keys) {
4619 var destinctKeys = [];
4620 var bindingStr = "";
4621 keys.forEach(function (key) {
4622 if (destinctKeys.indexOf(key) === -1) {
4623 destinctKeys.push(key);
4624 bindingStr += '?,';
4625 }
4626 });
4627 bindingStr = bindingStr.substring(0, bindingStr.length - 1); // keys is never empty
4628 criteria.push(DOC_STORE + '.id IN (' + bindingStr + ')');
4629 sqlArgs = sqlArgs.concat(destinctKeys);
4630 } else if (key !== false) {
4631 criteria.push(DOC_STORE + '.id = ?');
4632 sqlArgs.push(key);
4633 } else if (start !== false || end !== false) {
4634 if (start !== false) {
4635 criteria.push(DOC_STORE + '.id ' + (descending ? '<=' : '>=') + ' ?');
4636 sqlArgs.push(start);
4637 }
4638 if (end !== false) {
4639 var comparator = descending ? '>' : '<';
4640 if (inclusiveEnd) {
4641 comparator += '=';
4642 }
4643 criteria.push(DOC_STORE + '.id ' + comparator + ' ?');
4644 sqlArgs.push(end);
4645 }
4646 if (key !== false) {
4647 criteria.push(DOC_STORE + '.id = ?');
4648 sqlArgs.push(key);
4649 }
4650 }
4651
4652 if (!keys) {
4653 // report deleted if keys are specified
4654 criteria.push(BY_SEQ_STORE + '.deleted = 0');
4655 }
4656
4657 db.readTransaction(function (tx) {
4658 // count the docs in parallel to other operations
4659 countDocs(tx, function (docCount) {
4660 totalRows = docCount;
4661 });
4662
4663 /* istanbul ignore if */
4664 if (opts.update_seq) {
4665 // get max sequence in parallel to other operations
4666 getMaxSeq(tx, function (theSeq) {
4667 updateSeq = theSeq;
4668 });
4669 }
4670
4671 if (limit === 0) {
4672 return;
4673 }
4674
4675 // do a single query to fetch the documents
4676 var sql = select(
4677 SELECT_DOCS,
4678 [DOC_STORE, BY_SEQ_STORE],
4679 DOC_STORE_AND_BY_SEQ_JOINER,
4680 criteria,
4681 DOC_STORE + '.id ' + (descending ? 'DESC' : 'ASC')
4682 );
4683 sql += ' LIMIT ' + limit + ' OFFSET ' + offset;
4684
4685 tx.executeSql(sql, sqlArgs, function (tx, result) {
4686 for (var i = 0, l = result.rows.length; i < l; i++) {
4687 var item = result.rows.item(i);
4688 var metadata = safeJsonParse(item.metadata);
4689 var id = metadata.id;
4690 var data = unstringifyDoc(item.data, id, item.rev);
4691 var winningRev$$1 = data._rev;
4692 var doc = {
4693 id: id,
4694 key: id,
4695 value: {rev: winningRev$$1}
4696 };
4697 if (opts.include_docs) {
4698 doc.doc = data;
4699 doc.doc._rev = winningRev$$1;
4700 if (opts.conflicts) {
4701 var conflicts = collectConflicts(metadata);
4702 if (conflicts.length) {
4703 doc.doc._conflicts = conflicts;
4704 }
4705 }
4706 fetchAttachmentsIfNecessary(doc.doc, opts, api, tx);
4707 }
4708 if (item.deleted) {
4709 if (keys) {
4710 doc.value.deleted = true;
4711 doc.doc = null;
4712 } else {
4713 // propably should not happen
4714 continue;
4715 }
4716 }
4717 if (!keys) {
4718 results.push(doc);
4719 } else {
4720 var index = keys.indexOf(id, index);
4721 do {
4722 results[index] = doc;
4723 index = keys.indexOf(id, index + 1);
4724 } while (index > -1 && index < keys.length);
4725 }
4726 }
4727 if (keys) {
4728 keys.forEach(function (key, index) {
4729 if (!results[index]) {
4730 results[index] = {key: key, error: 'not_found'};
4731 }
4732 });
4733 }
4734 });
4735 }, websqlError(callback), function () {
4736 var returnVal = {
4737 total_rows: totalRows,
4738 offset: opts.skip,
4739 rows: results
4740 };
4741
4742 /* istanbul ignore if */
4743 if (opts.update_seq) {
4744 returnVal.update_seq = updateSeq;
4745 }
4746 callback(null, returnVal);
4747 });
4748 };
4749
4750 api._changes = function (opts) {
4751 opts = clone(opts);
4752
4753 if (opts.continuous) {
4754 var id = api._name + ':' + uuid();
4755 websqlChanges.addListener(api._name, id, api, opts);
4756 websqlChanges.notify(api._name);
4757 return {
4758 cancel: function () {
4759 websqlChanges.removeListener(api._name, id);
4760 }
4761 };
4762 }
4763
4764 var descending = opts.descending;
4765
4766 // Ignore the `since` parameter when `descending` is true
4767 opts.since = opts.since && !descending ? opts.since : 0;
4768
4769 var limit = 'limit' in opts ? opts.limit : -1;
4770 if (limit === 0) {
4771 limit = 1; // per CouchDB _changes spec
4772 }
4773
4774 var results = [];
4775 var numResults = 0;
4776
4777 function fetchChanges() {
4778
4779 var selectStmt =
4780 DOC_STORE + '.json AS metadata, ' +
4781 DOC_STORE + '.max_seq AS maxSeq, ' +
4782 BY_SEQ_STORE + '.json AS winningDoc, ' +
4783 BY_SEQ_STORE + '.rev AS winningRev ';
4784
4785 var from = DOC_STORE + ' JOIN ' + BY_SEQ_STORE;
4786
4787 var joiner = DOC_STORE + '.id=' + BY_SEQ_STORE + '.doc_id' +
4788 ' AND ' + DOC_STORE + '.winningseq=' + BY_SEQ_STORE + '.seq';
4789
4790 var criteria = ['maxSeq > ?'];
4791 var sqlArgs = [opts.since];
4792
4793 if (opts.doc_ids) {
4794 criteria.push(DOC_STORE + '.id IN ' + qMarks(opts.doc_ids.length));
4795 sqlArgs = sqlArgs.concat(opts.doc_ids);
4796 }
4797
4798 var orderBy = 'maxSeq ' + (descending ? 'DESC' : 'ASC');
4799
4800 var sql = select(selectStmt, from, joiner, criteria, orderBy);
4801
4802 var filter = filterChange(opts);
4803 if (!opts.view && !opts.filter) {
4804 // we can just limit in the query
4805 sql += ' LIMIT ' + limit;
4806 }
4807
4808 var lastSeq = opts.since || 0;
4809 db.readTransaction(function (tx) {
4810 tx.executeSql(sql, sqlArgs, function (tx, result) {
4811 function reportChange(change) {
4812 return function () {
4813 opts.onChange(change);
4814 };
4815 }
4816 for (var i = 0, l = result.rows.length; i < l; i++) {
4817 var item = result.rows.item(i);
4818 var metadata = safeJsonParse(item.metadata);
4819 lastSeq = item.maxSeq;
4820
4821 var doc = unstringifyDoc(item.winningDoc, metadata.id,
4822 item.winningRev);
4823 var change = opts.processChange(doc, metadata, opts);
4824 change.seq = item.maxSeq;
4825
4826 var filtered = filter(change);
4827 if (typeof filtered === 'object') {
4828 return opts.complete(filtered);
4829 }
4830
4831 if (filtered) {
4832 numResults++;
4833 if (opts.return_docs) {
4834 results.push(change);
4835 }
4836 // process the attachment immediately
4837 // for the benefit of live listeners
4838 if (opts.attachments && opts.include_docs) {
4839 fetchAttachmentsIfNecessary(doc, opts, api, tx,
4840 reportChange(change));
4841 } else {
4842 reportChange(change)();
4843 }
4844 }
4845 if (numResults === limit) {
4846 break;
4847 }
4848 }
4849 });
4850 }, websqlError(opts.complete), function () {
4851 if (!opts.continuous) {
4852 opts.complete(null, {
4853 results: results,
4854 last_seq: lastSeq
4855 });
4856 }
4857 });
4858 }
4859
4860 fetchChanges();
4861 };
4862
4863 api._close = function (callback) {
4864 //WebSQL databases do not need to be closed
4865 callback();
4866 };
4867
4868 api._getAttachment = function (docId, attachId, attachment, opts, callback) {
4869 var res;
4870 var tx = opts.ctx;
4871 var digest = attachment.digest;
4872 var type = attachment.content_type;
4873 var sql = 'SELECT escaped, ' +
4874 'CASE WHEN escaped = 1 THEN body ELSE HEX(body) END AS body FROM ' +
4875 ATTACH_STORE + ' WHERE digest=?';
4876 tx.executeSql(sql, [digest], function (tx, result) {
4877 // websql has a bug where \u0000 causes early truncation in strings
4878 // and blobs. to work around this, we used to use the hex() function,
4879 // but that's not performant. after migration 6, we remove \u0000
4880 // and add it back in afterwards
4881 var item = result.rows.item(0);
4882 var data = item.escaped ? unescapeBlob(item.body) :
4883 parseHexString(item.body, encoding);
4884 if (opts.binary) {
4885 res = binStringToBluffer(data, type);
4886 } else {
4887 res = thisBtoa(data);
4888 }
4889 callback(null, res);
4890 });
4891 };
4892
4893 api._getRevisionTree = function (docId, callback) {
4894 db.readTransaction(function (tx) {
4895 var sql = 'SELECT json AS metadata FROM ' + DOC_STORE + ' WHERE id = ?';
4896 tx.executeSql(sql, [docId], function (tx, result) {
4897 if (!result.rows.length) {
4898 callback(createError(MISSING_DOC));
4899 } else {
4900 var data = safeJsonParse(result.rows.item(0).metadata);
4901 callback(null, data.rev_tree);
4902 }
4903 });
4904 });
4905 };
4906
4907 api._doCompaction = function (docId, revs, callback) {
4908 if (!revs.length) {
4909 return callback();
4910 }
4911 db.transaction(function (tx) {
4912
4913 // update doc store
4914 var sql = 'SELECT json AS metadata FROM ' + DOC_STORE + ' WHERE id = ?';
4915 tx.executeSql(sql, [docId], function (tx, result) {
4916 var metadata = safeJsonParse(result.rows.item(0).metadata);
4917 traverseRevTree(metadata.rev_tree, function (isLeaf, pos,
4918 revHash, ctx, opts) {
4919 var rev = pos + '-' + revHash;
4920 if (revs.indexOf(rev) !== -1) {
4921 opts.status = 'missing';
4922 }
4923 });
4924
4925 var sql = 'UPDATE ' + DOC_STORE + ' SET json = ? WHERE id = ?';
4926 tx.executeSql(sql, [safeJsonStringify(metadata), docId]);
4927 });
4928
4929 compactRevs(revs, docId, tx);
4930 }, websqlError(callback), function () {
4931 callback();
4932 });
4933 };
4934
4935 api._getLocal = function (id, callback) {
4936 db.readTransaction(function (tx) {
4937 var sql = 'SELECT json, rev FROM ' + LOCAL_STORE + ' WHERE id=?';
4938 tx.executeSql(sql, [id], function (tx, res) {
4939 if (res.rows.length) {
4940 var item = res.rows.item(0);
4941 var doc = unstringifyDoc(item.json, id, item.rev);
4942 callback(null, doc);
4943 } else {
4944 callback(createError(MISSING_DOC));
4945 }
4946 });
4947 });
4948 };
4949
4950 api._putLocal = function (doc, opts, callback) {
4951 if (typeof opts === 'function') {
4952 callback = opts;
4953 opts = {};
4954 }
4955 delete doc._revisions; // ignore this, trust the rev
4956 var oldRev = doc._rev;
4957 var id = doc._id;
4958 var newRev;
4959 if (!oldRev) {
4960 newRev = doc._rev = '0-1';
4961 } else {
4962 newRev = doc._rev = '0-' + (parseInt(oldRev.split('-')[1], 10) + 1);
4963 }
4964 var json = stringifyDoc(doc);
4965
4966 var ret;
4967 function putLocal(tx) {
4968 var sql;
4969 var values;
4970 if (oldRev) {
4971 sql = 'UPDATE ' + LOCAL_STORE + ' SET rev=?, json=? ' +
4972 'WHERE id=? AND rev=?';
4973 values = [newRev, json, id, oldRev];
4974 } else {
4975 sql = 'INSERT INTO ' + LOCAL_STORE + ' (id, rev, json) VALUES (?,?,?)';
4976 values = [id, newRev, json];
4977 }
4978 tx.executeSql(sql, values, function (tx, res) {
4979 if (res.rowsAffected) {
4980 ret = {ok: true, id: id, rev: newRev};
4981 if (opts.ctx) { // return immediately
4982 callback(null, ret);
4983 }
4984 } else {
4985 callback(createError(REV_CONFLICT));
4986 }
4987 }, function () {
4988 callback(createError(REV_CONFLICT));
4989 return false; // ack that we handled the error
4990 });
4991 }
4992
4993 if (opts.ctx) {
4994 putLocal(opts.ctx);
4995 } else {
4996 db.transaction(putLocal, websqlError(callback), function () {
4997 if (ret) {
4998 callback(null, ret);
4999 }
5000 });
5001 }
5002 };
5003
5004 api._removeLocal = function (doc, opts, callback) {
5005 if (typeof opts === 'function') {
5006 callback = opts;
5007 opts = {};
5008 }
5009 var ret;
5010
5011 function removeLocal(tx) {
5012 var sql = 'DELETE FROM ' + LOCAL_STORE + ' WHERE id=? AND rev=?';
5013 var params = [doc._id, doc._rev];
5014 tx.executeSql(sql, params, function (tx, res) {
5015 if (!res.rowsAffected) {
5016 return callback(createError(MISSING_DOC));
5017 }
5018 ret = {ok: true, id: doc._id, rev: '0-0'};
5019 if (opts.ctx) { // return immediately
5020 callback(null, ret);
5021 }
5022 });
5023 }
5024
5025 if (opts.ctx) {
5026 removeLocal(opts.ctx);
5027 } else {
5028 db.transaction(removeLocal, websqlError(callback), function () {
5029 if (ret) {
5030 callback(null, ret);
5031 }
5032 });
5033 }
5034 };
5035
5036 api._destroy = function (opts, callback) {
5037 websqlChanges.removeAllListeners(api._name);
5038 db.transaction(function (tx) {
5039 var stores = [DOC_STORE, BY_SEQ_STORE, ATTACH_STORE, META_STORE,
5040 LOCAL_STORE, ATTACH_AND_SEQ_STORE];
5041 stores.forEach(function (store) {
5042 tx.executeSql('DROP TABLE IF EXISTS ' + store, []);
5043 });
5044 }, websqlError(callback), function () {
5045 if (hasLocalStorage()) {
5046 delete window.localStorage['_pouch__websqldb_' + api._name];
5047 delete window.localStorage[api._name];
5048 }
5049 callback(null, {'ok': true});
5050 });
5051 };
5052}
5053
5054function canOpenTestDB() {
5055 try {
5056 openDatabase('_pouch_validate_websql', 1, '', 1);
5057 return true;
5058 } catch (err) {
5059 return false;
5060 }
5061}
5062
5063// WKWebView had a bug where WebSQL would throw a DOM Exception 18
5064// (see https://bugs.webkit.org/show_bug.cgi?id=137760 and
5065// https://github.com/pouchdb/pouchdb/issues/5079)
5066// This has been fixed in latest WebKit, so we try to detect it here.
5067function isValidWebSQL() {
5068 // WKWebView UA:
5069 // Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X)
5070 // AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13C75
5071 // Chrome for iOS UA:
5072 // Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en)
5073 // AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60
5074 // Mobile/9B206 Safari/7534.48.3
5075 // Firefox for iOS UA:
5076 // Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4
5077 // (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4
5078
5079 // indexedDB is null on some UIWebViews and undefined in others
5080 // see: https://bugs.webkit.org/show_bug.cgi?id=137034
5081 if (typeof indexedDB === 'undefined' || indexedDB === null ||
5082 !/iP(hone|od|ad)/.test(navigator.userAgent)) {
5083 // definitely not WKWebView, avoid creating an unnecessary database
5084 return true;
5085 }
5086 // Cache the result in LocalStorage. Reason we do this is because if we
5087 // call openDatabase() too many times, Safari craps out in SauceLabs and
5088 // starts throwing DOM Exception 14s.
5089 var hasLS = hasLocalStorage();
5090 // Include user agent in the hash, so that if Safari is upgraded, we don't
5091 // continually think it's broken.
5092 var localStorageKey = '_pouch__websqldb_valid_' + navigator.userAgent;
5093 if (hasLS && localStorage[localStorageKey]) {
5094 return localStorage[localStorageKey] === '1';
5095 }
5096 var openedTestDB = canOpenTestDB();
5097 if (hasLS) {
5098 localStorage[localStorageKey] = openedTestDB ? '1' : '0';
5099 }
5100 return openedTestDB;
5101}
5102
5103function valid() {
5104 if (typeof openDatabase !== 'function') {
5105 return false;
5106 }
5107 return isValidWebSQL();
5108}
5109
5110function openDB$1(name, version, description, size) {
5111 // Traditional WebSQL API
5112 return openDatabase(name, version, description, size);
5113}
5114
5115function WebSQLPouch(opts, callback) {
5116 var msg = 'WebSQL is deprecated and will be removed in future releases of PouchDB. ' +
5117 'Please migrate to IndexedDB: https://pouchdb.com/2018/01/23/pouchdb-6.4.2.html';
5118 guardedConsole('warn', msg);
5119 var _opts = $inject_Object_assign({
5120 websql: openDB$1
5121 }, opts);
5122
5123 WebSqlPouch.call(this, _opts, callback);
5124}
5125
5126WebSQLPouch.valid = valid;
5127
5128WebSQLPouch.use_prefix = true;
5129
5130function WebsqlPouchPlugin (PouchDB) {
5131 PouchDB.adapter('websql', WebSQLPouch, true);
5132}
5133
5134/* global PouchDB */
5135
5136if (typeof PouchDB === 'undefined') {
5137 guardedConsole('error', 'websql adapter plugin error: ' +
5138 'Cannot find global "PouchDB" object! ' +
5139 'Did you remember to include pouchdb.js?');
5140} else {
5141 PouchDB.plugin(WebsqlPouchPlugin);
5142}
5143
5144}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
5145},{"1":1,"11":11,"2":2,"3":3,"4":4,"5":5,"6":6}]},{},[12]);