UNPKG

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