UNPKG

97.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
6
7function createCommonjsModule(fn) {
8 var module = { exports: {} };
9 return fn(module, module.exports), module.exports;
10}
11
12function commonjsRequire (target) {
13 throw new Error('Could not dynamically require "' + target + '". Please configure the dynamicRequireTargets option of @rollup/plugin-commonjs appropriately for this require call to behave properly.');
14}
15
16/*!
17 localForage -- Offline Storage, Improved
18 Version 1.9.0
19 https://localforage.github.io/localForage
20 (c) 2013-2017 Mozilla, Apache License 2.0
21*/
22
23var localforage = createCommonjsModule(function (module, exports) {
24(function(f){{module.exports=f();}})(function(){return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof commonjsRequire=="function"&&commonjsRequire;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw (f.code="MODULE_NOT_FOUND", f)}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r);}return n[o].exports}var i=typeof commonjsRequire=="function"&&commonjsRequire;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
25(function (global){
26var Mutation = global.MutationObserver || global.WebKitMutationObserver;
27
28var scheduleDrain;
29
30{
31 if (Mutation) {
32 var called = 0;
33 var observer = new Mutation(nextTick);
34 var element = global.document.createTextNode('');
35 observer.observe(element, {
36 characterData: true
37 });
38 scheduleDrain = function () {
39 element.data = (called = ++called % 2);
40 };
41 } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
42 var channel = new global.MessageChannel();
43 channel.port1.onmessage = nextTick;
44 scheduleDrain = function () {
45 channel.port2.postMessage(0);
46 };
47 } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
48 scheduleDrain = function () {
49
50 // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
51 // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
52 var scriptEl = global.document.createElement('script');
53 scriptEl.onreadystatechange = function () {
54 nextTick();
55
56 scriptEl.onreadystatechange = null;
57 scriptEl.parentNode.removeChild(scriptEl);
58 scriptEl = null;
59 };
60 global.document.documentElement.appendChild(scriptEl);
61 };
62 } else {
63 scheduleDrain = function () {
64 setTimeout(nextTick, 0);
65 };
66 }
67}
68
69var draining;
70var queue = [];
71//named nextTick for less confusing stack traces
72function nextTick() {
73 draining = true;
74 var i, oldQueue;
75 var len = queue.length;
76 while (len) {
77 oldQueue = queue;
78 queue = [];
79 i = -1;
80 while (++i < len) {
81 oldQueue[i]();
82 }
83 len = queue.length;
84 }
85 draining = false;
86}
87
88module.exports = immediate;
89function immediate(task) {
90 if (queue.push(task) === 1 && !draining) {
91 scheduleDrain();
92 }
93}
94
95}).call(this,typeof commonjsGlobal !== "undefined" ? commonjsGlobal : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
96},{}],2:[function(_dereq_,module,exports){
97var immediate = _dereq_(1);
98
99/* istanbul ignore next */
100function INTERNAL() {}
101
102var handlers = {};
103
104var REJECTED = ['REJECTED'];
105var FULFILLED = ['FULFILLED'];
106var PENDING = ['PENDING'];
107
108module.exports = Promise;
109
110function Promise(resolver) {
111 if (typeof resolver !== 'function') {
112 throw new TypeError('resolver must be a function');
113 }
114 this.state = PENDING;
115 this.queue = [];
116 this.outcome = void 0;
117 if (resolver !== INTERNAL) {
118 safelyResolveThenable(this, resolver);
119 }
120}
121
122Promise.prototype["catch"] = function (onRejected) {
123 return this.then(null, onRejected);
124};
125Promise.prototype.then = function (onFulfilled, onRejected) {
126 if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
127 typeof onRejected !== 'function' && this.state === REJECTED) {
128 return this;
129 }
130 var promise = new this.constructor(INTERNAL);
131 if (this.state !== PENDING) {
132 var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
133 unwrap(promise, resolver, this.outcome);
134 } else {
135 this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
136 }
137
138 return promise;
139};
140function QueueItem(promise, onFulfilled, onRejected) {
141 this.promise = promise;
142 if (typeof onFulfilled === 'function') {
143 this.onFulfilled = onFulfilled;
144 this.callFulfilled = this.otherCallFulfilled;
145 }
146 if (typeof onRejected === 'function') {
147 this.onRejected = onRejected;
148 this.callRejected = this.otherCallRejected;
149 }
150}
151QueueItem.prototype.callFulfilled = function (value) {
152 handlers.resolve(this.promise, value);
153};
154QueueItem.prototype.otherCallFulfilled = function (value) {
155 unwrap(this.promise, this.onFulfilled, value);
156};
157QueueItem.prototype.callRejected = function (value) {
158 handlers.reject(this.promise, value);
159};
160QueueItem.prototype.otherCallRejected = function (value) {
161 unwrap(this.promise, this.onRejected, value);
162};
163
164function unwrap(promise, func, value) {
165 immediate(function () {
166 var returnValue;
167 try {
168 returnValue = func(value);
169 } catch (e) {
170 return handlers.reject(promise, e);
171 }
172 if (returnValue === promise) {
173 handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
174 } else {
175 handlers.resolve(promise, returnValue);
176 }
177 });
178}
179
180handlers.resolve = function (self, value) {
181 var result = tryCatch(getThen, value);
182 if (result.status === 'error') {
183 return handlers.reject(self, result.value);
184 }
185 var thenable = result.value;
186
187 if (thenable) {
188 safelyResolveThenable(self, thenable);
189 } else {
190 self.state = FULFILLED;
191 self.outcome = value;
192 var i = -1;
193 var len = self.queue.length;
194 while (++i < len) {
195 self.queue[i].callFulfilled(value);
196 }
197 }
198 return self;
199};
200handlers.reject = function (self, error) {
201 self.state = REJECTED;
202 self.outcome = error;
203 var i = -1;
204 var len = self.queue.length;
205 while (++i < len) {
206 self.queue[i].callRejected(error);
207 }
208 return self;
209};
210
211function getThen(obj) {
212 // Make sure we only access the accessor once as required by the spec
213 var then = obj && obj.then;
214 if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
215 return function appyThen() {
216 then.apply(obj, arguments);
217 };
218 }
219}
220
221function safelyResolveThenable(self, thenable) {
222 // Either fulfill, reject or reject with error
223 var called = false;
224 function onError(value) {
225 if (called) {
226 return;
227 }
228 called = true;
229 handlers.reject(self, value);
230 }
231
232 function onSuccess(value) {
233 if (called) {
234 return;
235 }
236 called = true;
237 handlers.resolve(self, value);
238 }
239
240 function tryToUnwrap() {
241 thenable(onSuccess, onError);
242 }
243
244 var result = tryCatch(tryToUnwrap);
245 if (result.status === 'error') {
246 onError(result.value);
247 }
248}
249
250function tryCatch(func, value) {
251 var out = {};
252 try {
253 out.value = func(value);
254 out.status = 'success';
255 } catch (e) {
256 out.status = 'error';
257 out.value = e;
258 }
259 return out;
260}
261
262Promise.resolve = resolve;
263function resolve(value) {
264 if (value instanceof this) {
265 return value;
266 }
267 return handlers.resolve(new this(INTERNAL), value);
268}
269
270Promise.reject = reject;
271function reject(reason) {
272 var promise = new this(INTERNAL);
273 return handlers.reject(promise, reason);
274}
275
276Promise.all = all;
277function all(iterable) {
278 var self = this;
279 if (Object.prototype.toString.call(iterable) !== '[object Array]') {
280 return this.reject(new TypeError('must be an array'));
281 }
282
283 var len = iterable.length;
284 var called = false;
285 if (!len) {
286 return this.resolve([]);
287 }
288
289 var values = new Array(len);
290 var resolved = 0;
291 var i = -1;
292 var promise = new this(INTERNAL);
293
294 while (++i < len) {
295 allResolver(iterable[i], i);
296 }
297 return promise;
298 function allResolver(value, i) {
299 self.resolve(value).then(resolveFromAll, function (error) {
300 if (!called) {
301 called = true;
302 handlers.reject(promise, error);
303 }
304 });
305 function resolveFromAll(outValue) {
306 values[i] = outValue;
307 if (++resolved === len && !called) {
308 called = true;
309 handlers.resolve(promise, values);
310 }
311 }
312 }
313}
314
315Promise.race = race;
316function race(iterable) {
317 var self = this;
318 if (Object.prototype.toString.call(iterable) !== '[object Array]') {
319 return this.reject(new TypeError('must be an array'));
320 }
321
322 var len = iterable.length;
323 var called = false;
324 if (!len) {
325 return this.resolve([]);
326 }
327
328 var i = -1;
329 var promise = new this(INTERNAL);
330
331 while (++i < len) {
332 resolver(iterable[i]);
333 }
334 return promise;
335 function resolver(value) {
336 self.resolve(value).then(function (response) {
337 if (!called) {
338 called = true;
339 handlers.resolve(promise, response);
340 }
341 }, function (error) {
342 if (!called) {
343 called = true;
344 handlers.reject(promise, error);
345 }
346 });
347 }
348}
349
350},{"1":1}],3:[function(_dereq_,module,exports){
351(function (global){
352if (typeof global.Promise !== 'function') {
353 global.Promise = _dereq_(2);
354}
355
356}).call(this,typeof commonjsGlobal !== "undefined" ? commonjsGlobal : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
357},{"2":2}],4:[function(_dereq_,module,exports){
358
359var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
360
361function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
362
363function getIDB() {
364 /* global indexedDB,webkitIndexedDB,mozIndexedDB,OIndexedDB,msIndexedDB */
365 try {
366 if (typeof indexedDB !== 'undefined') {
367 return indexedDB;
368 }
369 if (typeof webkitIndexedDB !== 'undefined') {
370 return webkitIndexedDB;
371 }
372 if (typeof mozIndexedDB !== 'undefined') {
373 return mozIndexedDB;
374 }
375 if (typeof OIndexedDB !== 'undefined') {
376 return OIndexedDB;
377 }
378 if (typeof msIndexedDB !== 'undefined') {
379 return msIndexedDB;
380 }
381 } catch (e) {
382 return;
383 }
384}
385
386var idb = getIDB();
387
388function isIndexedDBValid() {
389 try {
390 // Initialize IndexedDB; fall back to vendor-prefixed versions
391 // if needed.
392 if (!idb || !idb.open) {
393 return false;
394 }
395 // We mimic PouchDB here;
396 //
397 // We test for openDatabase because IE Mobile identifies itself
398 // as Safari. Oh the lulz...
399 var isSafari = typeof openDatabase !== 'undefined' && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform);
400
401 var hasFetch = typeof fetch === 'function' && fetch.toString().indexOf('[native code') !== -1;
402
403 // Safari <10.1 does not meet our requirements for IDB support
404 // (see: https://github.com/pouchdb/pouchdb/issues/5572).
405 // Safari 10.1 shipped with fetch, we can use that to detect it.
406 // Note: this creates issues with `window.fetch` polyfills and
407 // overrides; see:
408 // https://github.com/localForage/localForage/issues/856
409 return (!isSafari || hasFetch) && typeof indexedDB !== 'undefined' &&
410 // some outdated implementations of IDB that appear on Samsung
411 // and HTC Android devices <4.4 are missing IDBKeyRange
412 // See: https://github.com/mozilla/localForage/issues/128
413 // See: https://github.com/mozilla/localForage/issues/272
414 typeof IDBKeyRange !== 'undefined';
415 } catch (e) {
416 return false;
417 }
418}
419
420// Abstracts constructing a Blob object, so it also works in older
421// browsers that don't support the native Blob constructor. (i.e.
422// old QtWebKit versions, at least).
423// Abstracts constructing a Blob object, so it also works in older
424// browsers that don't support the native Blob constructor. (i.e.
425// old QtWebKit versions, at least).
426function createBlob(parts, properties) {
427 /* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
428 parts = parts || [];
429 properties = properties || {};
430 try {
431 return new Blob(parts, properties);
432 } catch (e) {
433 if (e.name !== 'TypeError') {
434 throw e;
435 }
436 var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder : typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder : typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder : WebKitBlobBuilder;
437 var builder = new Builder();
438 for (var i = 0; i < parts.length; i += 1) {
439 builder.append(parts[i]);
440 }
441 return builder.getBlob(properties.type);
442 }
443}
444
445// This is CommonJS because lie is an external dependency, so Rollup
446// can just ignore it.
447if (typeof Promise === 'undefined') {
448 // In the "nopromises" build this will just throw if you don't have
449 // a global promise object, but it would throw anyway later.
450 _dereq_(3);
451}
452var Promise$1 = Promise;
453
454function executeCallback(promise, callback) {
455 if (callback) {
456 promise.then(function (result) {
457 callback(null, result);
458 }, function (error) {
459 callback(error);
460 });
461 }
462}
463
464function executeTwoCallbacks(promise, callback, errorCallback) {
465 if (typeof callback === 'function') {
466 promise.then(callback);
467 }
468
469 if (typeof errorCallback === 'function') {
470 promise["catch"](errorCallback);
471 }
472}
473
474function normalizeKey(key) {
475 // Cast the key to a string, as that's all we can set as a key.
476 if (typeof key !== 'string') {
477 console.warn(key + ' used as a key, but it is not a string.');
478 key = String(key);
479 }
480
481 return key;
482}
483
484function getCallback() {
485 if (arguments.length && typeof arguments[arguments.length - 1] === 'function') {
486 return arguments[arguments.length - 1];
487 }
488}
489
490// Some code originally from async_storage.js in
491// [Gaia](https://github.com/mozilla-b2g/gaia).
492
493var DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support';
494var supportsBlobs = void 0;
495var dbContexts = {};
496var toString = Object.prototype.toString;
497
498// Transaction Modes
499var READ_ONLY = 'readonly';
500var READ_WRITE = 'readwrite';
501
502// Transform a binary string to an array buffer, because otherwise
503// weird stuff happens when you try to work with the binary string directly.
504// It is known.
505// From http://stackoverflow.com/questions/14967647/ (continues on next line)
506// encode-decode-image-with-base64-breaks-image (2013-04-21)
507function _binStringToArrayBuffer(bin) {
508 var length = bin.length;
509 var buf = new ArrayBuffer(length);
510 var arr = new Uint8Array(buf);
511 for (var i = 0; i < length; i++) {
512 arr[i] = bin.charCodeAt(i);
513 }
514 return buf;
515}
516
517//
518// Blobs are not supported in all versions of IndexedDB, notably
519// Chrome <37 and Android <5. In those versions, storing a blob will throw.
520//
521// Various other blob bugs exist in Chrome v37-42 (inclusive).
522// Detecting them is expensive and confusing to users, and Chrome 37-42
523// is at very low usage worldwide, so we do a hacky userAgent check instead.
524//
525// content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120
526// 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916
527// FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836
528//
529// Code borrowed from PouchDB. See:
530// https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js
531//
532function _checkBlobSupportWithoutCaching(idb) {
533 return new Promise$1(function (resolve) {
534 var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE);
535 var blob = createBlob(['']);
536 txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key');
537
538 txn.onabort = function (e) {
539 // If the transaction aborts now its due to not being able to
540 // write to the database, likely due to the disk being full
541 e.preventDefault();
542 e.stopPropagation();
543 resolve(false);
544 };
545
546 txn.oncomplete = function () {
547 var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);
548 var matchedEdge = navigator.userAgent.match(/Edge\//);
549 // MS Edge pretends to be Chrome 42:
550 // https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx
551 resolve(matchedEdge || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43);
552 };
553 })["catch"](function () {
554 return false; // error, so assume unsupported
555 });
556}
557
558function _checkBlobSupport(idb) {
559 if (typeof supportsBlobs === 'boolean') {
560 return Promise$1.resolve(supportsBlobs);
561 }
562 return _checkBlobSupportWithoutCaching(idb).then(function (value) {
563 supportsBlobs = value;
564 return supportsBlobs;
565 });
566}
567
568function _deferReadiness(dbInfo) {
569 var dbContext = dbContexts[dbInfo.name];
570
571 // Create a deferred object representing the current database operation.
572 var deferredOperation = {};
573
574 deferredOperation.promise = new Promise$1(function (resolve, reject) {
575 deferredOperation.resolve = resolve;
576 deferredOperation.reject = reject;
577 });
578
579 // Enqueue the deferred operation.
580 dbContext.deferredOperations.push(deferredOperation);
581
582 // Chain its promise to the database readiness.
583 if (!dbContext.dbReady) {
584 dbContext.dbReady = deferredOperation.promise;
585 } else {
586 dbContext.dbReady = dbContext.dbReady.then(function () {
587 return deferredOperation.promise;
588 });
589 }
590}
591
592function _advanceReadiness(dbInfo) {
593 var dbContext = dbContexts[dbInfo.name];
594
595 // Dequeue a deferred operation.
596 var deferredOperation = dbContext.deferredOperations.pop();
597
598 // Resolve its promise (which is part of the database readiness
599 // chain of promises).
600 if (deferredOperation) {
601 deferredOperation.resolve();
602 return deferredOperation.promise;
603 }
604}
605
606function _rejectReadiness(dbInfo, err) {
607 var dbContext = dbContexts[dbInfo.name];
608
609 // Dequeue a deferred operation.
610 var deferredOperation = dbContext.deferredOperations.pop();
611
612 // Reject its promise (which is part of the database readiness
613 // chain of promises).
614 if (deferredOperation) {
615 deferredOperation.reject(err);
616 return deferredOperation.promise;
617 }
618}
619
620function _getConnection(dbInfo, upgradeNeeded) {
621 return new Promise$1(function (resolve, reject) {
622 dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext();
623
624 if (dbInfo.db) {
625 if (upgradeNeeded) {
626 _deferReadiness(dbInfo);
627 dbInfo.db.close();
628 } else {
629 return resolve(dbInfo.db);
630 }
631 }
632
633 var dbArgs = [dbInfo.name];
634
635 if (upgradeNeeded) {
636 dbArgs.push(dbInfo.version);
637 }
638
639 var openreq = idb.open.apply(idb, dbArgs);
640
641 if (upgradeNeeded) {
642 openreq.onupgradeneeded = function (e) {
643 var db = openreq.result;
644 try {
645 db.createObjectStore(dbInfo.storeName);
646 if (e.oldVersion <= 1) {
647 // Added when support for blob shims was added
648 db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);
649 }
650 } catch (ex) {
651 if (ex.name === 'ConstraintError') {
652 console.warn('The database "' + dbInfo.name + '"' + ' has been upgraded from version ' + e.oldVersion + ' to version ' + e.newVersion + ', but the storage "' + dbInfo.storeName + '" already exists.');
653 } else {
654 throw ex;
655 }
656 }
657 };
658 }
659
660 openreq.onerror = function (e) {
661 e.preventDefault();
662 reject(openreq.error);
663 };
664
665 openreq.onsuccess = function () {
666 resolve(openreq.result);
667 _advanceReadiness(dbInfo);
668 };
669 });
670}
671
672function _getOriginalConnection(dbInfo) {
673 return _getConnection(dbInfo, false);
674}
675
676function _getUpgradedConnection(dbInfo) {
677 return _getConnection(dbInfo, true);
678}
679
680function _isUpgradeNeeded(dbInfo, defaultVersion) {
681 if (!dbInfo.db) {
682 return true;
683 }
684
685 var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName);
686 var isDowngrade = dbInfo.version < dbInfo.db.version;
687 var isUpgrade = dbInfo.version > dbInfo.db.version;
688
689 if (isDowngrade) {
690 // If the version is not the default one
691 // then warn for impossible downgrade.
692 if (dbInfo.version !== defaultVersion) {
693 console.warn('The database "' + dbInfo.name + '"' + " can't be downgraded from version " + dbInfo.db.version + ' to version ' + dbInfo.version + '.');
694 }
695 // Align the versions to prevent errors.
696 dbInfo.version = dbInfo.db.version;
697 }
698
699 if (isUpgrade || isNewStore) {
700 // If the store is new then increment the version (if needed).
701 // This will trigger an "upgradeneeded" event which is required
702 // for creating a store.
703 if (isNewStore) {
704 var incVersion = dbInfo.db.version + 1;
705 if (incVersion > dbInfo.version) {
706 dbInfo.version = incVersion;
707 }
708 }
709
710 return true;
711 }
712
713 return false;
714}
715
716// encode a blob for indexeddb engines that don't support blobs
717function _encodeBlob(blob) {
718 return new Promise$1(function (resolve, reject) {
719 var reader = new FileReader();
720 reader.onerror = reject;
721 reader.onloadend = function (e) {
722 var base64 = btoa(e.target.result || '');
723 resolve({
724 __local_forage_encoded_blob: true,
725 data: base64,
726 type: blob.type
727 });
728 };
729 reader.readAsBinaryString(blob);
730 });
731}
732
733// decode an encoded blob
734function _decodeBlob(encodedBlob) {
735 var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data));
736 return createBlob([arrayBuff], { type: encodedBlob.type });
737}
738
739// is this one of our fancy encoded blobs?
740function _isEncodedBlob(value) {
741 return value && value.__local_forage_encoded_blob;
742}
743
744// Specialize the default `ready()` function by making it dependent
745// on the current database operations. Thus, the driver will be actually
746// ready when it's been initialized (default) *and* there are no pending
747// operations on the database (initiated by some other instances).
748function _fullyReady(callback) {
749 var self = this;
750
751 var promise = self._initReady().then(function () {
752 var dbContext = dbContexts[self._dbInfo.name];
753
754 if (dbContext && dbContext.dbReady) {
755 return dbContext.dbReady;
756 }
757 });
758
759 executeTwoCallbacks(promise, callback, callback);
760 return promise;
761}
762
763// Try to establish a new db connection to replace the
764// current one which is broken (i.e. experiencing
765// InvalidStateError while creating a transaction).
766function _tryReconnect(dbInfo) {
767 _deferReadiness(dbInfo);
768
769 var dbContext = dbContexts[dbInfo.name];
770 var forages = dbContext.forages;
771
772 for (var i = 0; i < forages.length; i++) {
773 var forage = forages[i];
774 if (forage._dbInfo.db) {
775 forage._dbInfo.db.close();
776 forage._dbInfo.db = null;
777 }
778 }
779 dbInfo.db = null;
780
781 return _getOriginalConnection(dbInfo).then(function (db) {
782 dbInfo.db = db;
783 if (_isUpgradeNeeded(dbInfo)) {
784 // Reopen the database for upgrading.
785 return _getUpgradedConnection(dbInfo);
786 }
787 return db;
788 }).then(function (db) {
789 // store the latest db reference
790 // in case the db was upgraded
791 dbInfo.db = dbContext.db = db;
792 for (var i = 0; i < forages.length; i++) {
793 forages[i]._dbInfo.db = db;
794 }
795 })["catch"](function (err) {
796 _rejectReadiness(dbInfo, err);
797 throw err;
798 });
799}
800
801// FF doesn't like Promises (micro-tasks) and IDDB store operations,
802// so we have to do it with callbacks
803function createTransaction(dbInfo, mode, callback, retries) {
804 if (retries === undefined) {
805 retries = 1;
806 }
807
808 try {
809 var tx = dbInfo.db.transaction(dbInfo.storeName, mode);
810 callback(null, tx);
811 } catch (err) {
812 if (retries > 0 && (!dbInfo.db || err.name === 'InvalidStateError' || err.name === 'NotFoundError')) {
813 return Promise$1.resolve().then(function () {
814 if (!dbInfo.db || err.name === 'NotFoundError' && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) {
815 // increase the db version, to create the new ObjectStore
816 if (dbInfo.db) {
817 dbInfo.version = dbInfo.db.version + 1;
818 }
819 // Reopen the database for upgrading.
820 return _getUpgradedConnection(dbInfo);
821 }
822 }).then(function () {
823 return _tryReconnect(dbInfo).then(function () {
824 createTransaction(dbInfo, mode, callback, retries - 1);
825 });
826 })["catch"](callback);
827 }
828
829 callback(err);
830 }
831}
832
833function createDbContext() {
834 return {
835 // Running localForages sharing a database.
836 forages: [],
837 // Shared database.
838 db: null,
839 // Database readiness (promise).
840 dbReady: null,
841 // Deferred operations on the database.
842 deferredOperations: []
843 };
844}
845
846// Open the IndexedDB database (automatically creates one if one didn't
847// previously exist), using any options set in the config.
848function _initStorage(options) {
849 var self = this;
850 var dbInfo = {
851 db: null
852 };
853
854 if (options) {
855 for (var i in options) {
856 dbInfo[i] = options[i];
857 }
858 }
859
860 // Get the current context of the database;
861 var dbContext = dbContexts[dbInfo.name];
862
863 // ...or create a new context.
864 if (!dbContext) {
865 dbContext = createDbContext();
866 // Register the new context in the global container.
867 dbContexts[dbInfo.name] = dbContext;
868 }
869
870 // Register itself as a running localForage in the current context.
871 dbContext.forages.push(self);
872
873 // Replace the default `ready()` function with the specialized one.
874 if (!self._initReady) {
875 self._initReady = self.ready;
876 self.ready = _fullyReady;
877 }
878
879 // Create an array of initialization states of the related localForages.
880 var initPromises = [];
881
882 function ignoreErrors() {
883 // Don't handle errors here,
884 // just makes sure related localForages aren't pending.
885 return Promise$1.resolve();
886 }
887
888 for (var j = 0; j < dbContext.forages.length; j++) {
889 var forage = dbContext.forages[j];
890 if (forage !== self) {
891 // Don't wait for itself...
892 initPromises.push(forage._initReady()["catch"](ignoreErrors));
893 }
894 }
895
896 // Take a snapshot of the related localForages.
897 var forages = dbContext.forages.slice(0);
898
899 // Initialize the connection process only when
900 // all the related localForages aren't pending.
901 return Promise$1.all(initPromises).then(function () {
902 dbInfo.db = dbContext.db;
903 // Get the connection or open a new one without upgrade.
904 return _getOriginalConnection(dbInfo);
905 }).then(function (db) {
906 dbInfo.db = db;
907 if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) {
908 // Reopen the database for upgrading.
909 return _getUpgradedConnection(dbInfo);
910 }
911 return db;
912 }).then(function (db) {
913 dbInfo.db = dbContext.db = db;
914 self._dbInfo = dbInfo;
915 // Share the final connection amongst related localForages.
916 for (var k = 0; k < forages.length; k++) {
917 var forage = forages[k];
918 if (forage !== self) {
919 // Self is already up-to-date.
920 forage._dbInfo.db = dbInfo.db;
921 forage._dbInfo.version = dbInfo.version;
922 }
923 }
924 });
925}
926
927function getItem(key, callback) {
928 var self = this;
929
930 key = normalizeKey(key);
931
932 var promise = new Promise$1(function (resolve, reject) {
933 self.ready().then(function () {
934 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
935 if (err) {
936 return reject(err);
937 }
938
939 try {
940 var store = transaction.objectStore(self._dbInfo.storeName);
941 var req = store.get(key);
942
943 req.onsuccess = function () {
944 var value = req.result;
945 if (value === undefined) {
946 value = null;
947 }
948 if (_isEncodedBlob(value)) {
949 value = _decodeBlob(value);
950 }
951 resolve(value);
952 };
953
954 req.onerror = function () {
955 reject(req.error);
956 };
957 } catch (e) {
958 reject(e);
959 }
960 });
961 })["catch"](reject);
962 });
963
964 executeCallback(promise, callback);
965 return promise;
966}
967
968// Iterate over all items stored in database.
969function iterate(iterator, callback) {
970 var self = this;
971
972 var promise = new Promise$1(function (resolve, reject) {
973 self.ready().then(function () {
974 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
975 if (err) {
976 return reject(err);
977 }
978
979 try {
980 var store = transaction.objectStore(self._dbInfo.storeName);
981 var req = store.openCursor();
982 var iterationNumber = 1;
983
984 req.onsuccess = function () {
985 var cursor = req.result;
986
987 if (cursor) {
988 var value = cursor.value;
989 if (_isEncodedBlob(value)) {
990 value = _decodeBlob(value);
991 }
992 var result = iterator(value, cursor.key, iterationNumber++);
993
994 // when the iterator callback returns any
995 // (non-`undefined`) value, then we stop
996 // the iteration immediately
997 if (result !== void 0) {
998 resolve(result);
999 } else {
1000 cursor["continue"]();
1001 }
1002 } else {
1003 resolve();
1004 }
1005 };
1006
1007 req.onerror = function () {
1008 reject(req.error);
1009 };
1010 } catch (e) {
1011 reject(e);
1012 }
1013 });
1014 })["catch"](reject);
1015 });
1016
1017 executeCallback(promise, callback);
1018
1019 return promise;
1020}
1021
1022function setItem(key, value, callback) {
1023 var self = this;
1024
1025 key = normalizeKey(key);
1026
1027 var promise = new Promise$1(function (resolve, reject) {
1028 var dbInfo;
1029 self.ready().then(function () {
1030 dbInfo = self._dbInfo;
1031 if (toString.call(value) === '[object Blob]') {
1032 return _checkBlobSupport(dbInfo.db).then(function (blobSupport) {
1033 if (blobSupport) {
1034 return value;
1035 }
1036 return _encodeBlob(value);
1037 });
1038 }
1039 return value;
1040 }).then(function (value) {
1041 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
1042 if (err) {
1043 return reject(err);
1044 }
1045
1046 try {
1047 var store = transaction.objectStore(self._dbInfo.storeName);
1048
1049 // The reason we don't _save_ null is because IE 10 does
1050 // not support saving the `null` type in IndexedDB. How
1051 // ironic, given the bug below!
1052 // See: https://github.com/mozilla/localForage/issues/161
1053 if (value === null) {
1054 value = undefined;
1055 }
1056
1057 var req = store.put(value, key);
1058
1059 transaction.oncomplete = function () {
1060 // Cast to undefined so the value passed to
1061 // callback/promise is the same as what one would get out
1062 // of `getItem()` later. This leads to some weirdness
1063 // (setItem('foo', undefined) will return `null`), but
1064 // it's not my fault localStorage is our baseline and that
1065 // it's weird.
1066 if (value === undefined) {
1067 value = null;
1068 }
1069
1070 resolve(value);
1071 };
1072 transaction.onabort = transaction.onerror = function () {
1073 var err = req.error ? req.error : req.transaction.error;
1074 reject(err);
1075 };
1076 } catch (e) {
1077 reject(e);
1078 }
1079 });
1080 })["catch"](reject);
1081 });
1082
1083 executeCallback(promise, callback);
1084 return promise;
1085}
1086
1087function removeItem(key, callback) {
1088 var self = this;
1089
1090 key = normalizeKey(key);
1091
1092 var promise = new Promise$1(function (resolve, reject) {
1093 self.ready().then(function () {
1094 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
1095 if (err) {
1096 return reject(err);
1097 }
1098
1099 try {
1100 var store = transaction.objectStore(self._dbInfo.storeName);
1101 // We use a Grunt task to make this safe for IE and some
1102 // versions of Android (including those used by Cordova).
1103 // Normally IE won't like `.delete()` and will insist on
1104 // using `['delete']()`, but we have a build step that
1105 // fixes this for us now.
1106 var req = store["delete"](key);
1107 transaction.oncomplete = function () {
1108 resolve();
1109 };
1110
1111 transaction.onerror = function () {
1112 reject(req.error);
1113 };
1114
1115 // The request will be also be aborted if we've exceeded our storage
1116 // space.
1117 transaction.onabort = function () {
1118 var err = req.error ? req.error : req.transaction.error;
1119 reject(err);
1120 };
1121 } catch (e) {
1122 reject(e);
1123 }
1124 });
1125 })["catch"](reject);
1126 });
1127
1128 executeCallback(promise, callback);
1129 return promise;
1130}
1131
1132function clear(callback) {
1133 var self = this;
1134
1135 var promise = new Promise$1(function (resolve, reject) {
1136 self.ready().then(function () {
1137 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
1138 if (err) {
1139 return reject(err);
1140 }
1141
1142 try {
1143 var store = transaction.objectStore(self._dbInfo.storeName);
1144 var req = store.clear();
1145
1146 transaction.oncomplete = function () {
1147 resolve();
1148 };
1149
1150 transaction.onabort = transaction.onerror = function () {
1151 var err = req.error ? req.error : req.transaction.error;
1152 reject(err);
1153 };
1154 } catch (e) {
1155 reject(e);
1156 }
1157 });
1158 })["catch"](reject);
1159 });
1160
1161 executeCallback(promise, callback);
1162 return promise;
1163}
1164
1165function length(callback) {
1166 var self = this;
1167
1168 var promise = new Promise$1(function (resolve, reject) {
1169 self.ready().then(function () {
1170 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
1171 if (err) {
1172 return reject(err);
1173 }
1174
1175 try {
1176 var store = transaction.objectStore(self._dbInfo.storeName);
1177 var req = store.count();
1178
1179 req.onsuccess = function () {
1180 resolve(req.result);
1181 };
1182
1183 req.onerror = function () {
1184 reject(req.error);
1185 };
1186 } catch (e) {
1187 reject(e);
1188 }
1189 });
1190 })["catch"](reject);
1191 });
1192
1193 executeCallback(promise, callback);
1194 return promise;
1195}
1196
1197function key(n, callback) {
1198 var self = this;
1199
1200 var promise = new Promise$1(function (resolve, reject) {
1201 if (n < 0) {
1202 resolve(null);
1203
1204 return;
1205 }
1206
1207 self.ready().then(function () {
1208 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
1209 if (err) {
1210 return reject(err);
1211 }
1212
1213 try {
1214 var store = transaction.objectStore(self._dbInfo.storeName);
1215 var advanced = false;
1216 var req = store.openKeyCursor();
1217
1218 req.onsuccess = function () {
1219 var cursor = req.result;
1220 if (!cursor) {
1221 // this means there weren't enough keys
1222 resolve(null);
1223
1224 return;
1225 }
1226
1227 if (n === 0) {
1228 // We have the first key, return it if that's what they
1229 // wanted.
1230 resolve(cursor.key);
1231 } else {
1232 if (!advanced) {
1233 // Otherwise, ask the cursor to skip ahead n
1234 // records.
1235 advanced = true;
1236 cursor.advance(n);
1237 } else {
1238 // When we get here, we've got the nth key.
1239 resolve(cursor.key);
1240 }
1241 }
1242 };
1243
1244 req.onerror = function () {
1245 reject(req.error);
1246 };
1247 } catch (e) {
1248 reject(e);
1249 }
1250 });
1251 })["catch"](reject);
1252 });
1253
1254 executeCallback(promise, callback);
1255 return promise;
1256}
1257
1258function keys(callback) {
1259 var self = this;
1260
1261 var promise = new Promise$1(function (resolve, reject) {
1262 self.ready().then(function () {
1263 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
1264 if (err) {
1265 return reject(err);
1266 }
1267
1268 try {
1269 var store = transaction.objectStore(self._dbInfo.storeName);
1270 var req = store.openKeyCursor();
1271 var keys = [];
1272
1273 req.onsuccess = function () {
1274 var cursor = req.result;
1275
1276 if (!cursor) {
1277 resolve(keys);
1278 return;
1279 }
1280
1281 keys.push(cursor.key);
1282 cursor["continue"]();
1283 };
1284
1285 req.onerror = function () {
1286 reject(req.error);
1287 };
1288 } catch (e) {
1289 reject(e);
1290 }
1291 });
1292 })["catch"](reject);
1293 });
1294
1295 executeCallback(promise, callback);
1296 return promise;
1297}
1298
1299function dropInstance(options, callback) {
1300 callback = getCallback.apply(this, arguments);
1301
1302 var currentConfig = this.config();
1303 options = typeof options !== 'function' && options || {};
1304 if (!options.name) {
1305 options.name = options.name || currentConfig.name;
1306 options.storeName = options.storeName || currentConfig.storeName;
1307 }
1308
1309 var self = this;
1310 var promise;
1311 if (!options.name) {
1312 promise = Promise$1.reject('Invalid arguments');
1313 } else {
1314 var isCurrentDb = options.name === currentConfig.name && self._dbInfo.db;
1315
1316 var dbPromise = isCurrentDb ? Promise$1.resolve(self._dbInfo.db) : _getOriginalConnection(options).then(function (db) {
1317 var dbContext = dbContexts[options.name];
1318 var forages = dbContext.forages;
1319 dbContext.db = db;
1320 for (var i = 0; i < forages.length; i++) {
1321 forages[i]._dbInfo.db = db;
1322 }
1323 return db;
1324 });
1325
1326 if (!options.storeName) {
1327 promise = dbPromise.then(function (db) {
1328 _deferReadiness(options);
1329
1330 var dbContext = dbContexts[options.name];
1331 var forages = dbContext.forages;
1332
1333 db.close();
1334 for (var i = 0; i < forages.length; i++) {
1335 var forage = forages[i];
1336 forage._dbInfo.db = null;
1337 }
1338
1339 var dropDBPromise = new Promise$1(function (resolve, reject) {
1340 var req = idb.deleteDatabase(options.name);
1341
1342 req.onerror = req.onblocked = function (err) {
1343 var db = req.result;
1344 if (db) {
1345 db.close();
1346 }
1347 reject(err);
1348 };
1349
1350 req.onsuccess = function () {
1351 var db = req.result;
1352 if (db) {
1353 db.close();
1354 }
1355 resolve(db);
1356 };
1357 });
1358
1359 return dropDBPromise.then(function (db) {
1360 dbContext.db = db;
1361 for (var i = 0; i < forages.length; i++) {
1362 var _forage = forages[i];
1363 _advanceReadiness(_forage._dbInfo);
1364 }
1365 })["catch"](function (err) {
1366 (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {});
1367 throw err;
1368 });
1369 });
1370 } else {
1371 promise = dbPromise.then(function (db) {
1372 if (!db.objectStoreNames.contains(options.storeName)) {
1373 return;
1374 }
1375
1376 var newVersion = db.version + 1;
1377
1378 _deferReadiness(options);
1379
1380 var dbContext = dbContexts[options.name];
1381 var forages = dbContext.forages;
1382
1383 db.close();
1384 for (var i = 0; i < forages.length; i++) {
1385 var forage = forages[i];
1386 forage._dbInfo.db = null;
1387 forage._dbInfo.version = newVersion;
1388 }
1389
1390 var dropObjectPromise = new Promise$1(function (resolve, reject) {
1391 var req = idb.open(options.name, newVersion);
1392
1393 req.onerror = function (err) {
1394 var db = req.result;
1395 db.close();
1396 reject(err);
1397 };
1398
1399 req.onupgradeneeded = function () {
1400 var db = req.result;
1401 db.deleteObjectStore(options.storeName);
1402 };
1403
1404 req.onsuccess = function () {
1405 var db = req.result;
1406 db.close();
1407 resolve(db);
1408 };
1409 });
1410
1411 return dropObjectPromise.then(function (db) {
1412 dbContext.db = db;
1413 for (var j = 0; j < forages.length; j++) {
1414 var _forage2 = forages[j];
1415 _forage2._dbInfo.db = db;
1416 _advanceReadiness(_forage2._dbInfo);
1417 }
1418 })["catch"](function (err) {
1419 (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {});
1420 throw err;
1421 });
1422 });
1423 }
1424 }
1425
1426 executeCallback(promise, callback);
1427 return promise;
1428}
1429
1430var asyncStorage = {
1431 _driver: 'asyncStorage',
1432 _initStorage: _initStorage,
1433 _support: isIndexedDBValid(),
1434 iterate: iterate,
1435 getItem: getItem,
1436 setItem: setItem,
1437 removeItem: removeItem,
1438 clear: clear,
1439 length: length,
1440 key: key,
1441 keys: keys,
1442 dropInstance: dropInstance
1443};
1444
1445function isWebSQLValid() {
1446 return typeof openDatabase === 'function';
1447}
1448
1449// Sadly, the best way to save binary data in WebSQL/localStorage is serializing
1450// it to Base64, so this is how we store it to prevent very strange errors with less
1451// verbose ways of binary <-> string data storage.
1452var BASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
1453
1454var BLOB_TYPE_PREFIX = '~~local_forage_type~';
1455var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/;
1456
1457var SERIALIZED_MARKER = '__lfsc__:';
1458var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length;
1459
1460// OMG the serializations!
1461var TYPE_ARRAYBUFFER = 'arbf';
1462var TYPE_BLOB = 'blob';
1463var TYPE_INT8ARRAY = 'si08';
1464var TYPE_UINT8ARRAY = 'ui08';
1465var TYPE_UINT8CLAMPEDARRAY = 'uic8';
1466var TYPE_INT16ARRAY = 'si16';
1467var TYPE_INT32ARRAY = 'si32';
1468var TYPE_UINT16ARRAY = 'ur16';
1469var TYPE_UINT32ARRAY = 'ui32';
1470var TYPE_FLOAT32ARRAY = 'fl32';
1471var TYPE_FLOAT64ARRAY = 'fl64';
1472var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length;
1473
1474var toString$1 = Object.prototype.toString;
1475
1476function stringToBuffer(serializedString) {
1477 // Fill the string into a ArrayBuffer.
1478 var bufferLength = serializedString.length * 0.75;
1479 var len = serializedString.length;
1480 var i;
1481 var p = 0;
1482 var encoded1, encoded2, encoded3, encoded4;
1483
1484 if (serializedString[serializedString.length - 1] === '=') {
1485 bufferLength--;
1486 if (serializedString[serializedString.length - 2] === '=') {
1487 bufferLength--;
1488 }
1489 }
1490
1491 var buffer = new ArrayBuffer(bufferLength);
1492 var bytes = new Uint8Array(buffer);
1493
1494 for (i = 0; i < len; i += 4) {
1495 encoded1 = BASE_CHARS.indexOf(serializedString[i]);
1496 encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]);
1497 encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]);
1498 encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]);
1499
1500 /*jslint bitwise: true */
1501 bytes[p++] = encoded1 << 2 | encoded2 >> 4;
1502 bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
1503 bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
1504 }
1505 return buffer;
1506}
1507
1508// Converts a buffer to a string to store, serialized, in the backend
1509// storage library.
1510function bufferToString(buffer) {
1511 // base64-arraybuffer
1512 var bytes = new Uint8Array(buffer);
1513 var base64String = '';
1514 var i;
1515
1516 for (i = 0; i < bytes.length; i += 3) {
1517 /*jslint bitwise: true */
1518 base64String += BASE_CHARS[bytes[i] >> 2];
1519 base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
1520 base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
1521 base64String += BASE_CHARS[bytes[i + 2] & 63];
1522 }
1523
1524 if (bytes.length % 3 === 2) {
1525 base64String = base64String.substring(0, base64String.length - 1) + '=';
1526 } else if (bytes.length % 3 === 1) {
1527 base64String = base64String.substring(0, base64String.length - 2) + '==';
1528 }
1529
1530 return base64String;
1531}
1532
1533// Serialize a value, afterwards executing a callback (which usually
1534// instructs the `setItem()` callback/promise to be executed). This is how
1535// we store binary data with localStorage.
1536function serialize(value, callback) {
1537 var valueType = '';
1538 if (value) {
1539 valueType = toString$1.call(value);
1540 }
1541
1542 // Cannot use `value instanceof ArrayBuffer` or such here, as these
1543 // checks fail when running the tests using casper.js...
1544 //
1545 // TODO: See why those tests fail and use a better solution.
1546 if (value && (valueType === '[object ArrayBuffer]' || value.buffer && toString$1.call(value.buffer) === '[object ArrayBuffer]')) {
1547 // Convert binary arrays to a string and prefix the string with
1548 // a special marker.
1549 var buffer;
1550 var marker = SERIALIZED_MARKER;
1551
1552 if (value instanceof ArrayBuffer) {
1553 buffer = value;
1554 marker += TYPE_ARRAYBUFFER;
1555 } else {
1556 buffer = value.buffer;
1557
1558 if (valueType === '[object Int8Array]') {
1559 marker += TYPE_INT8ARRAY;
1560 } else if (valueType === '[object Uint8Array]') {
1561 marker += TYPE_UINT8ARRAY;
1562 } else if (valueType === '[object Uint8ClampedArray]') {
1563 marker += TYPE_UINT8CLAMPEDARRAY;
1564 } else if (valueType === '[object Int16Array]') {
1565 marker += TYPE_INT16ARRAY;
1566 } else if (valueType === '[object Uint16Array]') {
1567 marker += TYPE_UINT16ARRAY;
1568 } else if (valueType === '[object Int32Array]') {
1569 marker += TYPE_INT32ARRAY;
1570 } else if (valueType === '[object Uint32Array]') {
1571 marker += TYPE_UINT32ARRAY;
1572 } else if (valueType === '[object Float32Array]') {
1573 marker += TYPE_FLOAT32ARRAY;
1574 } else if (valueType === '[object Float64Array]') {
1575 marker += TYPE_FLOAT64ARRAY;
1576 } else {
1577 callback(new Error('Failed to get type for BinaryArray'));
1578 }
1579 }
1580
1581 callback(marker + bufferToString(buffer));
1582 } else if (valueType === '[object Blob]') {
1583 // Conver the blob to a binaryArray and then to a string.
1584 var fileReader = new FileReader();
1585
1586 fileReader.onload = function () {
1587 // Backwards-compatible prefix for the blob type.
1588 var str = BLOB_TYPE_PREFIX + value.type + '~' + bufferToString(this.result);
1589
1590 callback(SERIALIZED_MARKER + TYPE_BLOB + str);
1591 };
1592
1593 fileReader.readAsArrayBuffer(value);
1594 } else {
1595 try {
1596 callback(JSON.stringify(value));
1597 } catch (e) {
1598 console.error("Couldn't convert value into a JSON string: ", value);
1599
1600 callback(null, e);
1601 }
1602 }
1603}
1604
1605// Deserialize data we've inserted into a value column/field. We place
1606// special markers into our strings to mark them as encoded; this isn't
1607// as nice as a meta field, but it's the only sane thing we can do whilst
1608// keeping localStorage support intact.
1609//
1610// Oftentimes this will just deserialize JSON content, but if we have a
1611// special marker (SERIALIZED_MARKER, defined above), we will extract
1612// some kind of arraybuffer/binary data/typed array out of the string.
1613function deserialize(value) {
1614 // If we haven't marked this string as being specially serialized (i.e.
1615 // something other than serialized JSON), we can just return it and be
1616 // done with it.
1617 if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) {
1618 return JSON.parse(value);
1619 }
1620
1621 // The following code deals with deserializing some kind of Blob or
1622 // TypedArray. First we separate out the type of data we're dealing
1623 // with from the data itself.
1624 var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH);
1625 var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH);
1626
1627 var blobType;
1628 // Backwards-compatible blob type serialization strategy.
1629 // DBs created with older versions of localForage will simply not have the blob type.
1630 if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) {
1631 var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX);
1632 blobType = matcher[1];
1633 serializedString = serializedString.substring(matcher[0].length);
1634 }
1635 var buffer = stringToBuffer(serializedString);
1636
1637 // Return the right type based on the code/type set during
1638 // serialization.
1639 switch (type) {
1640 case TYPE_ARRAYBUFFER:
1641 return buffer;
1642 case TYPE_BLOB:
1643 return createBlob([buffer], { type: blobType });
1644 case TYPE_INT8ARRAY:
1645 return new Int8Array(buffer);
1646 case TYPE_UINT8ARRAY:
1647 return new Uint8Array(buffer);
1648 case TYPE_UINT8CLAMPEDARRAY:
1649 return new Uint8ClampedArray(buffer);
1650 case TYPE_INT16ARRAY:
1651 return new Int16Array(buffer);
1652 case TYPE_UINT16ARRAY:
1653 return new Uint16Array(buffer);
1654 case TYPE_INT32ARRAY:
1655 return new Int32Array(buffer);
1656 case TYPE_UINT32ARRAY:
1657 return new Uint32Array(buffer);
1658 case TYPE_FLOAT32ARRAY:
1659 return new Float32Array(buffer);
1660 case TYPE_FLOAT64ARRAY:
1661 return new Float64Array(buffer);
1662 default:
1663 throw new Error('Unkown type: ' + type);
1664 }
1665}
1666
1667var localforageSerializer = {
1668 serialize: serialize,
1669 deserialize: deserialize,
1670 stringToBuffer: stringToBuffer,
1671 bufferToString: bufferToString
1672};
1673
1674/*
1675 * Includes code from:
1676 *
1677 * base64-arraybuffer
1678 * https://github.com/niklasvh/base64-arraybuffer
1679 *
1680 * Copyright (c) 2012 Niklas von Hertzen
1681 * Licensed under the MIT license.
1682 */
1683
1684function createDbTable(t, dbInfo, callback, errorCallback) {
1685 t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + ' ' + '(id INTEGER PRIMARY KEY, key unique, value)', [], callback, errorCallback);
1686}
1687
1688// Open the WebSQL database (automatically creates one if one didn't
1689// previously exist), using any options set in the config.
1690function _initStorage$1(options) {
1691 var self = this;
1692 var dbInfo = {
1693 db: null
1694 };
1695
1696 if (options) {
1697 for (var i in options) {
1698 dbInfo[i] = typeof options[i] !== 'string' ? options[i].toString() : options[i];
1699 }
1700 }
1701
1702 var dbInfoPromise = new Promise$1(function (resolve, reject) {
1703 // Open the database; the openDatabase API will automatically
1704 // create it for us if it doesn't exist.
1705 try {
1706 dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size);
1707 } catch (e) {
1708 return reject(e);
1709 }
1710
1711 // Create our key/value table if it doesn't exist.
1712 dbInfo.db.transaction(function (t) {
1713 createDbTable(t, dbInfo, function () {
1714 self._dbInfo = dbInfo;
1715 resolve();
1716 }, function (t, error) {
1717 reject(error);
1718 });
1719 }, reject);
1720 });
1721
1722 dbInfo.serializer = localforageSerializer;
1723 return dbInfoPromise;
1724}
1725
1726function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) {
1727 t.executeSql(sqlStatement, args, callback, function (t, error) {
1728 if (error.code === error.SYNTAX_ERR) {
1729 t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name = ?", [dbInfo.storeName], function (t, results) {
1730 if (!results.rows.length) {
1731 // if the table is missing (was deleted)
1732 // re-create it table and retry
1733 createDbTable(t, dbInfo, function () {
1734 t.executeSql(sqlStatement, args, callback, errorCallback);
1735 }, errorCallback);
1736 } else {
1737 errorCallback(t, error);
1738 }
1739 }, errorCallback);
1740 } else {
1741 errorCallback(t, error);
1742 }
1743 }, errorCallback);
1744}
1745
1746function getItem$1(key, callback) {
1747 var self = this;
1748
1749 key = normalizeKey(key);
1750
1751 var promise = new Promise$1(function (resolve, reject) {
1752 self.ready().then(function () {
1753 var dbInfo = self._dbInfo;
1754 dbInfo.db.transaction(function (t) {
1755 tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName + ' WHERE key = ? LIMIT 1', [key], function (t, results) {
1756 var result = results.rows.length ? results.rows.item(0).value : null;
1757
1758 // Check to see if this is serialized content we need to
1759 // unpack.
1760 if (result) {
1761 result = dbInfo.serializer.deserialize(result);
1762 }
1763
1764 resolve(result);
1765 }, function (t, error) {
1766 reject(error);
1767 });
1768 });
1769 })["catch"](reject);
1770 });
1771
1772 executeCallback(promise, callback);
1773 return promise;
1774}
1775
1776function iterate$1(iterator, callback) {
1777 var self = this;
1778
1779 var promise = new Promise$1(function (resolve, reject) {
1780 self.ready().then(function () {
1781 var dbInfo = self._dbInfo;
1782
1783 dbInfo.db.transaction(function (t) {
1784 tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName, [], function (t, results) {
1785 var rows = results.rows;
1786 var length = rows.length;
1787
1788 for (var i = 0; i < length; i++) {
1789 var item = rows.item(i);
1790 var result = item.value;
1791
1792 // Check to see if this is serialized content
1793 // we need to unpack.
1794 if (result) {
1795 result = dbInfo.serializer.deserialize(result);
1796 }
1797
1798 result = iterator(result, item.key, i + 1);
1799
1800 // void(0) prevents problems with redefinition
1801 // of `undefined`.
1802 if (result !== void 0) {
1803 resolve(result);
1804 return;
1805 }
1806 }
1807
1808 resolve();
1809 }, function (t, error) {
1810 reject(error);
1811 });
1812 });
1813 })["catch"](reject);
1814 });
1815
1816 executeCallback(promise, callback);
1817 return promise;
1818}
1819
1820function _setItem(key, value, callback, retriesLeft) {
1821 var self = this;
1822
1823 key = normalizeKey(key);
1824
1825 var promise = new Promise$1(function (resolve, reject) {
1826 self.ready().then(function () {
1827 // The localStorage API doesn't return undefined values in an
1828 // "expected" way, so undefined is always cast to null in all
1829 // drivers. See: https://github.com/mozilla/localForage/pull/42
1830 if (value === undefined) {
1831 value = null;
1832 }
1833
1834 // Save the original value to pass to the callback.
1835 var originalValue = value;
1836
1837 var dbInfo = self._dbInfo;
1838 dbInfo.serializer.serialize(value, function (value, error) {
1839 if (error) {
1840 reject(error);
1841 } else {
1842 dbInfo.db.transaction(function (t) {
1843 tryExecuteSql(t, dbInfo, 'INSERT OR REPLACE INTO ' + dbInfo.storeName + ' ' + '(key, value) VALUES (?, ?)', [key, value], function () {
1844 resolve(originalValue);
1845 }, function (t, error) {
1846 reject(error);
1847 });
1848 }, function (sqlError) {
1849 // The transaction failed; check
1850 // to see if it's a quota error.
1851 if (sqlError.code === sqlError.QUOTA_ERR) {
1852 // We reject the callback outright for now, but
1853 // it's worth trying to re-run the transaction.
1854 // Even if the user accepts the prompt to use
1855 // more storage on Safari, this error will
1856 // be called.
1857 //
1858 // Try to re-run the transaction.
1859 if (retriesLeft > 0) {
1860 resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1]));
1861 return;
1862 }
1863 reject(sqlError);
1864 }
1865 });
1866 }
1867 });
1868 })["catch"](reject);
1869 });
1870
1871 executeCallback(promise, callback);
1872 return promise;
1873}
1874
1875function setItem$1(key, value, callback) {
1876 return _setItem.apply(this, [key, value, callback, 1]);
1877}
1878
1879function removeItem$1(key, callback) {
1880 var self = this;
1881
1882 key = normalizeKey(key);
1883
1884 var promise = new Promise$1(function (resolve, reject) {
1885 self.ready().then(function () {
1886 var dbInfo = self._dbInfo;
1887 dbInfo.db.transaction(function (t) {
1888 tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName + ' WHERE key = ?', [key], function () {
1889 resolve();
1890 }, function (t, error) {
1891 reject(error);
1892 });
1893 });
1894 })["catch"](reject);
1895 });
1896
1897 executeCallback(promise, callback);
1898 return promise;
1899}
1900
1901// Deletes every item in the table.
1902// TODO: Find out if this resets the AUTO_INCREMENT number.
1903function clear$1(callback) {
1904 var self = this;
1905
1906 var promise = new Promise$1(function (resolve, reject) {
1907 self.ready().then(function () {
1908 var dbInfo = self._dbInfo;
1909 dbInfo.db.transaction(function (t) {
1910 tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName, [], function () {
1911 resolve();
1912 }, function (t, error) {
1913 reject(error);
1914 });
1915 });
1916 })["catch"](reject);
1917 });
1918
1919 executeCallback(promise, callback);
1920 return promise;
1921}
1922
1923// Does a simple `COUNT(key)` to get the number of items stored in
1924// localForage.
1925function length$1(callback) {
1926 var self = this;
1927
1928 var promise = new Promise$1(function (resolve, reject) {
1929 self.ready().then(function () {
1930 var dbInfo = self._dbInfo;
1931 dbInfo.db.transaction(function (t) {
1932 // Ahhh, SQL makes this one soooooo easy.
1933 tryExecuteSql(t, dbInfo, 'SELECT COUNT(key) as c FROM ' + dbInfo.storeName, [], function (t, results) {
1934 var result = results.rows.item(0).c;
1935 resolve(result);
1936 }, function (t, error) {
1937 reject(error);
1938 });
1939 });
1940 })["catch"](reject);
1941 });
1942
1943 executeCallback(promise, callback);
1944 return promise;
1945}
1946
1947// Return the key located at key index X; essentially gets the key from a
1948// `WHERE id = ?`. This is the most efficient way I can think to implement
1949// this rarely-used (in my experience) part of the API, but it can seem
1950// inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so
1951// the ID of each key will change every time it's updated. Perhaps a stored
1952// procedure for the `setItem()` SQL would solve this problem?
1953// TODO: Don't change ID on `setItem()`.
1954function key$1(n, callback) {
1955 var self = this;
1956
1957 var promise = new Promise$1(function (resolve, reject) {
1958 self.ready().then(function () {
1959 var dbInfo = self._dbInfo;
1960 dbInfo.db.transaction(function (t) {
1961 tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName + ' WHERE id = ? LIMIT 1', [n + 1], function (t, results) {
1962 var result = results.rows.length ? results.rows.item(0).key : null;
1963 resolve(result);
1964 }, function (t, error) {
1965 reject(error);
1966 });
1967 });
1968 })["catch"](reject);
1969 });
1970
1971 executeCallback(promise, callback);
1972 return promise;
1973}
1974
1975function keys$1(callback) {
1976 var self = this;
1977
1978 var promise = new Promise$1(function (resolve, reject) {
1979 self.ready().then(function () {
1980 var dbInfo = self._dbInfo;
1981 dbInfo.db.transaction(function (t) {
1982 tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName, [], function (t, results) {
1983 var keys = [];
1984
1985 for (var i = 0; i < results.rows.length; i++) {
1986 keys.push(results.rows.item(i).key);
1987 }
1988
1989 resolve(keys);
1990 }, function (t, error) {
1991 reject(error);
1992 });
1993 });
1994 })["catch"](reject);
1995 });
1996
1997 executeCallback(promise, callback);
1998 return promise;
1999}
2000
2001// https://www.w3.org/TR/webdatabase/#databases
2002// > There is no way to enumerate or delete the databases available for an origin from this API.
2003function getAllStoreNames(db) {
2004 return new Promise$1(function (resolve, reject) {
2005 db.transaction(function (t) {
2006 t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'", [], function (t, results) {
2007 var storeNames = [];
2008
2009 for (var i = 0; i < results.rows.length; i++) {
2010 storeNames.push(results.rows.item(i).name);
2011 }
2012
2013 resolve({
2014 db: db,
2015 storeNames: storeNames
2016 });
2017 }, function (t, error) {
2018 reject(error);
2019 });
2020 }, function (sqlError) {
2021 reject(sqlError);
2022 });
2023 });
2024}
2025
2026function dropInstance$1(options, callback) {
2027 callback = getCallback.apply(this, arguments);
2028
2029 var currentConfig = this.config();
2030 options = typeof options !== 'function' && options || {};
2031 if (!options.name) {
2032 options.name = options.name || currentConfig.name;
2033 options.storeName = options.storeName || currentConfig.storeName;
2034 }
2035
2036 var self = this;
2037 var promise;
2038 if (!options.name) {
2039 promise = Promise$1.reject('Invalid arguments');
2040 } else {
2041 promise = new Promise$1(function (resolve) {
2042 var db;
2043 if (options.name === currentConfig.name) {
2044 // use the db reference of the current instance
2045 db = self._dbInfo.db;
2046 } else {
2047 db = openDatabase(options.name, '', '', 0);
2048 }
2049
2050 if (!options.storeName) {
2051 // drop all database tables
2052 resolve(getAllStoreNames(db));
2053 } else {
2054 resolve({
2055 db: db,
2056 storeNames: [options.storeName]
2057 });
2058 }
2059 }).then(function (operationInfo) {
2060 return new Promise$1(function (resolve, reject) {
2061 operationInfo.db.transaction(function (t) {
2062 function dropTable(storeName) {
2063 return new Promise$1(function (resolve, reject) {
2064 t.executeSql('DROP TABLE IF EXISTS ' + storeName, [], function () {
2065 resolve();
2066 }, function (t, error) {
2067 reject(error);
2068 });
2069 });
2070 }
2071
2072 var operations = [];
2073 for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) {
2074 operations.push(dropTable(operationInfo.storeNames[i]));
2075 }
2076
2077 Promise$1.all(operations).then(function () {
2078 resolve();
2079 })["catch"](function (e) {
2080 reject(e);
2081 });
2082 }, function (sqlError) {
2083 reject(sqlError);
2084 });
2085 });
2086 });
2087 }
2088
2089 executeCallback(promise, callback);
2090 return promise;
2091}
2092
2093var webSQLStorage = {
2094 _driver: 'webSQLStorage',
2095 _initStorage: _initStorage$1,
2096 _support: isWebSQLValid(),
2097 iterate: iterate$1,
2098 getItem: getItem$1,
2099 setItem: setItem$1,
2100 removeItem: removeItem$1,
2101 clear: clear$1,
2102 length: length$1,
2103 key: key$1,
2104 keys: keys$1,
2105 dropInstance: dropInstance$1
2106};
2107
2108function isLocalStorageValid() {
2109 try {
2110 return typeof localStorage !== 'undefined' && 'setItem' in localStorage &&
2111 // in IE8 typeof localStorage.setItem === 'object'
2112 !!localStorage.setItem;
2113 } catch (e) {
2114 return false;
2115 }
2116}
2117
2118function _getKeyPrefix(options, defaultConfig) {
2119 var keyPrefix = options.name + '/';
2120
2121 if (options.storeName !== defaultConfig.storeName) {
2122 keyPrefix += options.storeName + '/';
2123 }
2124 return keyPrefix;
2125}
2126
2127// Check if localStorage throws when saving an item
2128function checkIfLocalStorageThrows() {
2129 var localStorageTestKey = '_localforage_support_test';
2130
2131 try {
2132 localStorage.setItem(localStorageTestKey, true);
2133 localStorage.removeItem(localStorageTestKey);
2134
2135 return false;
2136 } catch (e) {
2137 return true;
2138 }
2139}
2140
2141// Check if localStorage is usable and allows to save an item
2142// This method checks if localStorage is usable in Safari Private Browsing
2143// mode, or in any other case where the available quota for localStorage
2144// is 0 and there wasn't any saved items yet.
2145function _isLocalStorageUsable() {
2146 return !checkIfLocalStorageThrows() || localStorage.length > 0;
2147}
2148
2149// Config the localStorage backend, using options set in the config.
2150function _initStorage$2(options) {
2151 var self = this;
2152 var dbInfo = {};
2153 if (options) {
2154 for (var i in options) {
2155 dbInfo[i] = options[i];
2156 }
2157 }
2158
2159 dbInfo.keyPrefix = _getKeyPrefix(options, self._defaultConfig);
2160
2161 if (!_isLocalStorageUsable()) {
2162 return Promise$1.reject();
2163 }
2164
2165 self._dbInfo = dbInfo;
2166 dbInfo.serializer = localforageSerializer;
2167
2168 return Promise$1.resolve();
2169}
2170
2171// Remove all keys from the datastore, effectively destroying all data in
2172// the app's key/value store!
2173function clear$2(callback) {
2174 var self = this;
2175 var promise = self.ready().then(function () {
2176 var keyPrefix = self._dbInfo.keyPrefix;
2177
2178 for (var i = localStorage.length - 1; i >= 0; i--) {
2179 var key = localStorage.key(i);
2180
2181 if (key.indexOf(keyPrefix) === 0) {
2182 localStorage.removeItem(key);
2183 }
2184 }
2185 });
2186
2187 executeCallback(promise, callback);
2188 return promise;
2189}
2190
2191// Retrieve an item from the store. Unlike the original async_storage
2192// library in Gaia, we don't modify return values at all. If a key's value
2193// is `undefined`, we pass that value to the callback function.
2194function getItem$2(key, callback) {
2195 var self = this;
2196
2197 key = normalizeKey(key);
2198
2199 var promise = self.ready().then(function () {
2200 var dbInfo = self._dbInfo;
2201 var result = localStorage.getItem(dbInfo.keyPrefix + key);
2202
2203 // If a result was found, parse it from the serialized
2204 // string into a JS object. If result isn't truthy, the key
2205 // is likely undefined and we'll pass it straight to the
2206 // callback.
2207 if (result) {
2208 result = dbInfo.serializer.deserialize(result);
2209 }
2210
2211 return result;
2212 });
2213
2214 executeCallback(promise, callback);
2215 return promise;
2216}
2217
2218// Iterate over all items in the store.
2219function iterate$2(iterator, callback) {
2220 var self = this;
2221
2222 var promise = self.ready().then(function () {
2223 var dbInfo = self._dbInfo;
2224 var keyPrefix = dbInfo.keyPrefix;
2225 var keyPrefixLength = keyPrefix.length;
2226 var length = localStorage.length;
2227
2228 // We use a dedicated iterator instead of the `i` variable below
2229 // so other keys we fetch in localStorage aren't counted in
2230 // the `iterationNumber` argument passed to the `iterate()`
2231 // callback.
2232 //
2233 // See: github.com/mozilla/localForage/pull/435#discussion_r38061530
2234 var iterationNumber = 1;
2235
2236 for (var i = 0; i < length; i++) {
2237 var key = localStorage.key(i);
2238 if (key.indexOf(keyPrefix) !== 0) {
2239 continue;
2240 }
2241 var value = localStorage.getItem(key);
2242
2243 // If a result was found, parse it from the serialized
2244 // string into a JS object. If result isn't truthy, the
2245 // key is likely undefined and we'll pass it straight
2246 // to the iterator.
2247 if (value) {
2248 value = dbInfo.serializer.deserialize(value);
2249 }
2250
2251 value = iterator(value, key.substring(keyPrefixLength), iterationNumber++);
2252
2253 if (value !== void 0) {
2254 return value;
2255 }
2256 }
2257 });
2258
2259 executeCallback(promise, callback);
2260 return promise;
2261}
2262
2263// Same as localStorage's key() method, except takes a callback.
2264function key$2(n, callback) {
2265 var self = this;
2266 var promise = self.ready().then(function () {
2267 var dbInfo = self._dbInfo;
2268 var result;
2269 try {
2270 result = localStorage.key(n);
2271 } catch (error) {
2272 result = null;
2273 }
2274
2275 // Remove the prefix from the key, if a key is found.
2276 if (result) {
2277 result = result.substring(dbInfo.keyPrefix.length);
2278 }
2279
2280 return result;
2281 });
2282
2283 executeCallback(promise, callback);
2284 return promise;
2285}
2286
2287function keys$2(callback) {
2288 var self = this;
2289 var promise = self.ready().then(function () {
2290 var dbInfo = self._dbInfo;
2291 var length = localStorage.length;
2292 var keys = [];
2293
2294 for (var i = 0; i < length; i++) {
2295 var itemKey = localStorage.key(i);
2296 if (itemKey.indexOf(dbInfo.keyPrefix) === 0) {
2297 keys.push(itemKey.substring(dbInfo.keyPrefix.length));
2298 }
2299 }
2300
2301 return keys;
2302 });
2303
2304 executeCallback(promise, callback);
2305 return promise;
2306}
2307
2308// Supply the number of keys in the datastore to the callback function.
2309function length$2(callback) {
2310 var self = this;
2311 var promise = self.keys().then(function (keys) {
2312 return keys.length;
2313 });
2314
2315 executeCallback(promise, callback);
2316 return promise;
2317}
2318
2319// Remove an item from the store, nice and simple.
2320function removeItem$2(key, callback) {
2321 var self = this;
2322
2323 key = normalizeKey(key);
2324
2325 var promise = self.ready().then(function () {
2326 var dbInfo = self._dbInfo;
2327 localStorage.removeItem(dbInfo.keyPrefix + key);
2328 });
2329
2330 executeCallback(promise, callback);
2331 return promise;
2332}
2333
2334// Set a key's value and run an optional callback once the value is set.
2335// Unlike Gaia's implementation, the callback function is passed the value,
2336// in case you want to operate on that value only after you're sure it
2337// saved, or something like that.
2338function setItem$2(key, value, callback) {
2339 var self = this;
2340
2341 key = normalizeKey(key);
2342
2343 var promise = self.ready().then(function () {
2344 // Convert undefined values to null.
2345 // https://github.com/mozilla/localForage/pull/42
2346 if (value === undefined) {
2347 value = null;
2348 }
2349
2350 // Save the original value to pass to the callback.
2351 var originalValue = value;
2352
2353 return new Promise$1(function (resolve, reject) {
2354 var dbInfo = self._dbInfo;
2355 dbInfo.serializer.serialize(value, function (value, error) {
2356 if (error) {
2357 reject(error);
2358 } else {
2359 try {
2360 localStorage.setItem(dbInfo.keyPrefix + key, value);
2361 resolve(originalValue);
2362 } catch (e) {
2363 // localStorage capacity exceeded.
2364 // TODO: Make this a specific error/event.
2365 if (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
2366 reject(e);
2367 }
2368 reject(e);
2369 }
2370 }
2371 });
2372 });
2373 });
2374
2375 executeCallback(promise, callback);
2376 return promise;
2377}
2378
2379function dropInstance$2(options, callback) {
2380 callback = getCallback.apply(this, arguments);
2381
2382 options = typeof options !== 'function' && options || {};
2383 if (!options.name) {
2384 var currentConfig = this.config();
2385 options.name = options.name || currentConfig.name;
2386 options.storeName = options.storeName || currentConfig.storeName;
2387 }
2388
2389 var self = this;
2390 var promise;
2391 if (!options.name) {
2392 promise = Promise$1.reject('Invalid arguments');
2393 } else {
2394 promise = new Promise$1(function (resolve) {
2395 if (!options.storeName) {
2396 resolve(options.name + '/');
2397 } else {
2398 resolve(_getKeyPrefix(options, self._defaultConfig));
2399 }
2400 }).then(function (keyPrefix) {
2401 for (var i = localStorage.length - 1; i >= 0; i--) {
2402 var key = localStorage.key(i);
2403
2404 if (key.indexOf(keyPrefix) === 0) {
2405 localStorage.removeItem(key);
2406 }
2407 }
2408 });
2409 }
2410
2411 executeCallback(promise, callback);
2412 return promise;
2413}
2414
2415var localStorageWrapper = {
2416 _driver: 'localStorageWrapper',
2417 _initStorage: _initStorage$2,
2418 _support: isLocalStorageValid(),
2419 iterate: iterate$2,
2420 getItem: getItem$2,
2421 setItem: setItem$2,
2422 removeItem: removeItem$2,
2423 clear: clear$2,
2424 length: length$2,
2425 key: key$2,
2426 keys: keys$2,
2427 dropInstance: dropInstance$2
2428};
2429
2430var sameValue = function sameValue(x, y) {
2431 return x === y || typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y);
2432};
2433
2434var includes = function includes(array, searchElement) {
2435 var len = array.length;
2436 var i = 0;
2437 while (i < len) {
2438 if (sameValue(array[i], searchElement)) {
2439 return true;
2440 }
2441 i++;
2442 }
2443
2444 return false;
2445};
2446
2447var isArray = Array.isArray || function (arg) {
2448 return Object.prototype.toString.call(arg) === '[object Array]';
2449};
2450
2451// Drivers are stored here when `defineDriver()` is called.
2452// They are shared across all instances of localForage.
2453var DefinedDrivers = {};
2454
2455var DriverSupport = {};
2456
2457var DefaultDrivers = {
2458 INDEXEDDB: asyncStorage,
2459 WEBSQL: webSQLStorage,
2460 LOCALSTORAGE: localStorageWrapper
2461};
2462
2463var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQL._driver, DefaultDrivers.LOCALSTORAGE._driver];
2464
2465var OptionalDriverMethods = ['dropInstance'];
2466
2467var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem'].concat(OptionalDriverMethods);
2468
2469var DefaultConfig = {
2470 description: '',
2471 driver: DefaultDriverOrder.slice(),
2472 name: 'localforage',
2473 // Default DB size is _JUST UNDER_ 5MB, as it's the highest size
2474 // we can use without a prompt.
2475 size: 4980736,
2476 storeName: 'keyvaluepairs',
2477 version: 1.0
2478};
2479
2480function callWhenReady(localForageInstance, libraryMethod) {
2481 localForageInstance[libraryMethod] = function () {
2482 var _args = arguments;
2483 return localForageInstance.ready().then(function () {
2484 return localForageInstance[libraryMethod].apply(localForageInstance, _args);
2485 });
2486 };
2487}
2488
2489function extend() {
2490 for (var i = 1; i < arguments.length; i++) {
2491 var arg = arguments[i];
2492
2493 if (arg) {
2494 for (var _key in arg) {
2495 if (arg.hasOwnProperty(_key)) {
2496 if (isArray(arg[_key])) {
2497 arguments[0][_key] = arg[_key].slice();
2498 } else {
2499 arguments[0][_key] = arg[_key];
2500 }
2501 }
2502 }
2503 }
2504 }
2505
2506 return arguments[0];
2507}
2508
2509var LocalForage = function () {
2510 function LocalForage(options) {
2511 _classCallCheck(this, LocalForage);
2512
2513 for (var driverTypeKey in DefaultDrivers) {
2514 if (DefaultDrivers.hasOwnProperty(driverTypeKey)) {
2515 var driver = DefaultDrivers[driverTypeKey];
2516 var driverName = driver._driver;
2517 this[driverTypeKey] = driverName;
2518
2519 if (!DefinedDrivers[driverName]) {
2520 // we don't need to wait for the promise,
2521 // since the default drivers can be defined
2522 // in a blocking manner
2523 this.defineDriver(driver);
2524 }
2525 }
2526 }
2527
2528 this._defaultConfig = extend({}, DefaultConfig);
2529 this._config = extend({}, this._defaultConfig, options);
2530 this._driverSet = null;
2531 this._initDriver = null;
2532 this._ready = false;
2533 this._dbInfo = null;
2534
2535 this._wrapLibraryMethodsWithReady();
2536 this.setDriver(this._config.driver)["catch"](function () {});
2537 }
2538
2539 // Set any config values for localForage; can be called anytime before
2540 // the first API call (e.g. `getItem`, `setItem`).
2541 // We loop through options so we don't overwrite existing config
2542 // values.
2543
2544
2545 LocalForage.prototype.config = function config(options) {
2546 // If the options argument is an object, we use it to set values.
2547 // Otherwise, we return either a specified config value or all
2548 // config values.
2549 if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
2550 // If localforage is ready and fully initialized, we can't set
2551 // any new configuration values. Instead, we return an error.
2552 if (this._ready) {
2553 return new Error("Can't call config() after localforage " + 'has been used.');
2554 }
2555
2556 for (var i in options) {
2557 if (i === 'storeName') {
2558 options[i] = options[i].replace(/\W/g, '_');
2559 }
2560
2561 if (i === 'version' && typeof options[i] !== 'number') {
2562 return new Error('Database version must be a number.');
2563 }
2564
2565 this._config[i] = options[i];
2566 }
2567
2568 // after all config options are set and
2569 // the driver option is used, try setting it
2570 if ('driver' in options && options.driver) {
2571 return this.setDriver(this._config.driver);
2572 }
2573
2574 return true;
2575 } else if (typeof options === 'string') {
2576 return this._config[options];
2577 } else {
2578 return this._config;
2579 }
2580 };
2581
2582 // Used to define a custom driver, shared across all instances of
2583 // localForage.
2584
2585
2586 LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) {
2587 var promise = new Promise$1(function (resolve, reject) {
2588 try {
2589 var driverName = driverObject._driver;
2590 var complianceError = new Error('Custom driver not compliant; see ' + 'https://mozilla.github.io/localForage/#definedriver');
2591
2592 // A driver name should be defined and not overlap with the
2593 // library-defined, default drivers.
2594 if (!driverObject._driver) {
2595 reject(complianceError);
2596 return;
2597 }
2598
2599 var driverMethods = LibraryMethods.concat('_initStorage');
2600 for (var i = 0, len = driverMethods.length; i < len; i++) {
2601 var driverMethodName = driverMethods[i];
2602
2603 // when the property is there,
2604 // it should be a method even when optional
2605 var isRequired = !includes(OptionalDriverMethods, driverMethodName);
2606 if ((isRequired || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== 'function') {
2607 reject(complianceError);
2608 return;
2609 }
2610 }
2611
2612 var configureMissingMethods = function configureMissingMethods() {
2613 var methodNotImplementedFactory = function methodNotImplementedFactory(methodName) {
2614 return function () {
2615 var error = new Error('Method ' + methodName + ' is not implemented by the current driver');
2616 var promise = Promise$1.reject(error);
2617 executeCallback(promise, arguments[arguments.length - 1]);
2618 return promise;
2619 };
2620 };
2621
2622 for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) {
2623 var optionalDriverMethod = OptionalDriverMethods[_i];
2624 if (!driverObject[optionalDriverMethod]) {
2625 driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod);
2626 }
2627 }
2628 };
2629
2630 configureMissingMethods();
2631
2632 var setDriverSupport = function setDriverSupport(support) {
2633 if (DefinedDrivers[driverName]) {
2634 console.info('Redefining LocalForage driver: ' + driverName);
2635 }
2636 DefinedDrivers[driverName] = driverObject;
2637 DriverSupport[driverName] = support;
2638 // don't use a then, so that we can define
2639 // drivers that have simple _support methods
2640 // in a blocking manner
2641 resolve();
2642 };
2643
2644 if ('_support' in driverObject) {
2645 if (driverObject._support && typeof driverObject._support === 'function') {
2646 driverObject._support().then(setDriverSupport, reject);
2647 } else {
2648 setDriverSupport(!!driverObject._support);
2649 }
2650 } else {
2651 setDriverSupport(true);
2652 }
2653 } catch (e) {
2654 reject(e);
2655 }
2656 });
2657
2658 executeTwoCallbacks(promise, callback, errorCallback);
2659 return promise;
2660 };
2661
2662 LocalForage.prototype.driver = function driver() {
2663 return this._driver || null;
2664 };
2665
2666 LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) {
2667 var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error('Driver not found.'));
2668
2669 executeTwoCallbacks(getDriverPromise, callback, errorCallback);
2670 return getDriverPromise;
2671 };
2672
2673 LocalForage.prototype.getSerializer = function getSerializer(callback) {
2674 var serializerPromise = Promise$1.resolve(localforageSerializer);
2675 executeTwoCallbacks(serializerPromise, callback);
2676 return serializerPromise;
2677 };
2678
2679 LocalForage.prototype.ready = function ready(callback) {
2680 var self = this;
2681
2682 var promise = self._driverSet.then(function () {
2683 if (self._ready === null) {
2684 self._ready = self._initDriver();
2685 }
2686
2687 return self._ready;
2688 });
2689
2690 executeTwoCallbacks(promise, callback, callback);
2691 return promise;
2692 };
2693
2694 LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) {
2695 var self = this;
2696
2697 if (!isArray(drivers)) {
2698 drivers = [drivers];
2699 }
2700
2701 var supportedDrivers = this._getSupportedDrivers(drivers);
2702
2703 function setDriverToConfig() {
2704 self._config.driver = self.driver();
2705 }
2706
2707 function extendSelfWithDriver(driver) {
2708 self._extend(driver);
2709 setDriverToConfig();
2710
2711 self._ready = self._initStorage(self._config);
2712 return self._ready;
2713 }
2714
2715 function initDriver(supportedDrivers) {
2716 return function () {
2717 var currentDriverIndex = 0;
2718
2719 function driverPromiseLoop() {
2720 while (currentDriverIndex < supportedDrivers.length) {
2721 var driverName = supportedDrivers[currentDriverIndex];
2722 currentDriverIndex++;
2723
2724 self._dbInfo = null;
2725 self._ready = null;
2726
2727 return self.getDriver(driverName).then(extendSelfWithDriver)["catch"](driverPromiseLoop);
2728 }
2729
2730 setDriverToConfig();
2731 var error = new Error('No available storage method found.');
2732 self._driverSet = Promise$1.reject(error);
2733 return self._driverSet;
2734 }
2735
2736 return driverPromiseLoop();
2737 };
2738 }
2739
2740 // There might be a driver initialization in progress
2741 // so wait for it to finish in order to avoid a possible
2742 // race condition to set _dbInfo
2743 var oldDriverSetDone = this._driverSet !== null ? this._driverSet["catch"](function () {
2744 return Promise$1.resolve();
2745 }) : Promise$1.resolve();
2746
2747 this._driverSet = oldDriverSetDone.then(function () {
2748 var driverName = supportedDrivers[0];
2749 self._dbInfo = null;
2750 self._ready = null;
2751
2752 return self.getDriver(driverName).then(function (driver) {
2753 self._driver = driver._driver;
2754 setDriverToConfig();
2755 self._wrapLibraryMethodsWithReady();
2756 self._initDriver = initDriver(supportedDrivers);
2757 });
2758 })["catch"](function () {
2759 setDriverToConfig();
2760 var error = new Error('No available storage method found.');
2761 self._driverSet = Promise$1.reject(error);
2762 return self._driverSet;
2763 });
2764
2765 executeTwoCallbacks(this._driverSet, callback, errorCallback);
2766 return this._driverSet;
2767 };
2768
2769 LocalForage.prototype.supports = function supports(driverName) {
2770 return !!DriverSupport[driverName];
2771 };
2772
2773 LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) {
2774 extend(this, libraryMethodsAndProperties);
2775 };
2776
2777 LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) {
2778 var supportedDrivers = [];
2779 for (var i = 0, len = drivers.length; i < len; i++) {
2780 var driverName = drivers[i];
2781 if (this.supports(driverName)) {
2782 supportedDrivers.push(driverName);
2783 }
2784 }
2785 return supportedDrivers;
2786 };
2787
2788 LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() {
2789 // Add a stub for each driver API method that delays the call to the
2790 // corresponding driver method until localForage is ready. These stubs
2791 // will be replaced by the driver methods as soon as the driver is
2792 // loaded, so there is no performance impact.
2793 for (var i = 0, len = LibraryMethods.length; i < len; i++) {
2794 callWhenReady(this, LibraryMethods[i]);
2795 }
2796 };
2797
2798 LocalForage.prototype.createInstance = function createInstance(options) {
2799 return new LocalForage(options);
2800 };
2801
2802 return LocalForage;
2803}();
2804
2805// The actual localForage object that we expose as a module or via a
2806// global. It's extended by pulling in one of our other libraries.
2807
2808
2809var localforage_js = new LocalForage();
2810
2811module.exports = localforage_js;
2812
2813},{"3":3}]},{},[4])(4)
2814});
2815});
2816
2817/** @hidden */
2818const Drivers = {
2819 SecureStorage: 'ionicSecureStorage',
2820 IndexedDB: localforage.INDEXEDDB,
2821 LocalStorage: localforage.LOCALSTORAGE
2822};
2823const defaultConfig = {
2824 name: '_ionicstorage',
2825 storeName: '_ionickv',
2826 dbKey: '_ionickey',
2827 driverOrder: [
2828 Drivers.SecureStorage,
2829 Drivers.IndexedDB,
2830 Drivers.LocalStorage
2831 ]
2832};
2833class Storage {
2834 /**
2835 * Create a new Storage instance using the order of drivers and any additional config
2836 * options to pass to LocalForage.
2837 *
2838 * Possible default driverOrder options are: ['indexeddb', 'localstorage'] and the
2839 * default is that exact ordering.
2840 *
2841 * When using Ionic Secure Storage (enterprise only), use ['ionicSecureStorage', 'indexeddb', 'localstorage'] to ensure
2842 * Secure Storage is used when available, or fall back to IndexedDB or LocalStorage on the web.
2843 */
2844 constructor(config = defaultConfig) {
2845 this._db = null;
2846 this._secureStorageDriver = null;
2847 const actualConfig = Object.assign({}, defaultConfig, config || {});
2848 this._config = actualConfig;
2849 }
2850 async create() {
2851 const db = localforage.createInstance(this._config);
2852 this._db = db;
2853 await db.setDriver(this._config.driverOrder || []);
2854 return this;
2855 }
2856 /**
2857 * Define a new Driver. Must be called before
2858 * initializing the database. Example:
2859 *
2860 * await storage.defineDriver(myDriver);
2861 * await storage.create();
2862 */
2863 async defineDriver(driver) {
2864 if (driver._driver === Drivers.SecureStorage) {
2865 this._secureStorageDriver = driver;
2866 }
2867 return localforage.defineDriver(driver);
2868 }
2869 /**
2870 * Get the name of the driver being used.
2871 * @returns Name of the driver
2872 */
2873 get driver() {
2874 var _a;
2875 return ((_a = this._db) === null || _a === void 0 ? void 0 : _a.driver()) || null;
2876 }
2877 assertDb() {
2878 if (!this._db) {
2879 throw new Error('Database not created. Must call create() first');
2880 }
2881 return this._db;
2882 }
2883 /**
2884 * Get the value associated with the given key.
2885 * @param key the key to identify this value
2886 * @returns Returns a promise with the value of the given key
2887 */
2888 get(key) {
2889 const db = this.assertDb();
2890 return db.getItem(key);
2891 }
2892 /**
2893 * Set the value for the given key.
2894 * @param key the key to identify this value
2895 * @param value the value for this key
2896 * @returns Returns a promise that resolves when the key and value are set
2897 */
2898 set(key, value) {
2899 const db = this.assertDb();
2900 return db.setItem(key, value);
2901 }
2902 /**
2903 * Remove any value associated with this key.
2904 * @param key the key to identify this value
2905 * @returns Returns a promise that resolves when the value is removed
2906 */
2907 remove(key) {
2908 const db = this.assertDb();
2909 return db.removeItem(key);
2910 }
2911 /**
2912 * Clear the entire key value store. WARNING: HOT!
2913 * @returns Returns a promise that resolves when the store is cleared
2914 */
2915 clear() {
2916 const db = this.assertDb();
2917 return db.clear();
2918 }
2919 /**
2920 * @returns Returns a promise that resolves with the number of keys stored.
2921 */
2922 length() {
2923 const db = this.assertDb();
2924 return db.length();
2925 }
2926 /**
2927 * @returns Returns a promise that resolves with the keys in the store.
2928 */
2929 keys() {
2930 const db = this.assertDb();
2931 return db.keys();
2932 }
2933 /**
2934 * Iterate through each key,value pair.
2935 * @param iteratorCallback a callback of the form (value, key, iterationNumber)
2936 * @returns Returns a promise that resolves when the iteration has finished.
2937 */
2938 forEach(iteratorCallback) {
2939 const db = this.assertDb();
2940 return db.iterate(iteratorCallback);
2941 }
2942 setEncryptionKey(key) {
2943 var _a;
2944 if (!this._secureStorageDriver) {
2945 throw new Error('@ionic-enterprise/secure-storage not installed. Encryption support not available');
2946 }
2947 else {
2948 (_a = this._secureStorageDriver) === null || _a === void 0 ? void 0 : _a.setEncryptionKey(key);
2949 }
2950 }
2951}
2952
2953exports.Drivers = Drivers;
2954exports.Storage = Storage;
2955//# sourceMappingURL=ionic-storage.cjs.js.map