UNPKG

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