if ( 'Promise' in ( typeof window !== 'undefined' ? window : self ) ) { /*! localForage -- Offline Storage, Improved Version 1.3.1 https://mozilla.github.io/localForage (c) 2013-2015 Mozilla, Apache License 2.0 */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["localforage"] = factory(); else root["localforage"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; exports.__esModule = true; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } (function () { 'use strict'; // Custom drivers are stored here when `defineDriver()` is called. // They are shared across all instances of localForage. var CustomDrivers = {}; var DriverType = { INDEXEDDB: 'asyncStorage', LOCALSTORAGE: 'localStorageWrapper', WEBSQL: 'webSQLStorage' }; var DefaultDriverOrder = [DriverType.INDEXEDDB, DriverType.WEBSQL, DriverType.LOCALSTORAGE]; var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem']; var DefaultConfig = { description: '', driver: DefaultDriverOrder.slice(), name: 'localforage', // Default DB size is _JUST UNDER_ 5MB, as it's the highest size // we can use without a prompt. size: 4980736, storeName: 'keyvaluepairs', version: 1.0 }; // Check to see if IndexedDB is available and if it is the latest // implementation; it's our preferred backend library. We use "_spec_test" // as the name of the database because it's not the one we'll operate on, // but it's useful to make sure its using the right spec. // See: https://github.com/mozilla/localForage/issues/128 var driverSupport = (function (self) { // Initialize IndexedDB; fall back to vendor-prefixed versions // if needed. var indexedDB = indexedDB || self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.OIndexedDB || self.msIndexedDB; var result = {}; result[DriverType.WEBSQL] = !!self.openDatabase; result[DriverType.INDEXEDDB] = !!(function () { // We mimic PouchDB here; just UA test for Safari (which, as of // iOS 8/Yosemite, doesn't properly support IndexedDB). // IndexedDB support is broken and different from Blink's. // This is faster than the test case (and it's sync), so we just // do this. *SIGH* // http://bl.ocks.org/nolanlawson/raw/c83e9039edf2278047e9/ // // We test for openDatabase because IE Mobile identifies itself // as Safari. Oh the lulz... if (typeof self.openDatabase !== 'undefined' && self.navigator && self.navigator.userAgent && /Safari/.test(self.navigator.userAgent) && !/Chrome/.test(self.navigator.userAgent)) { return false; } try { return indexedDB && typeof indexedDB.open === 'function' && // Some Samsung/HTC Android 4.0-4.3 devices // have older IndexedDB specs; if this isn't available // their IndexedDB is too old for us to use. // (Replaces the onupgradeneeded test.) typeof self.IDBKeyRange !== 'undefined'; } catch (e) { return false; } })(); result[DriverType.LOCALSTORAGE] = !!(function () { try { return self.localStorage && 'setItem' in self.localStorage && self.localStorage.setItem; } catch (e) { return false; } })(); return result; })(this); var isArray = Array.isArray || function (arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; function callWhenReady(localForageInstance, libraryMethod) { localForageInstance[libraryMethod] = function () { var _args = arguments; return localForageInstance.ready().then(function () { return localForageInstance[libraryMethod].apply(localForageInstance, _args); }); }; } function extend() { for (var i = 1; i < arguments.length; i++) { var arg = arguments[i]; if (arg) { for (var key in arg) { if (arg.hasOwnProperty(key)) { if (isArray(arg[key])) { arguments[0][key] = arg[key].slice(); } else { arguments[0][key] = arg[key]; } } } } } return arguments[0]; } function isLibraryDriver(driverName) { for (var driver in DriverType) { if (DriverType.hasOwnProperty(driver) && DriverType[driver] === driverName) { return true; } } return false; } var LocalForage = (function () { function LocalForage(options) { _classCallCheck(this, LocalForage); this.INDEXEDDB = DriverType.INDEXEDDB; this.LOCALSTORAGE = DriverType.LOCALSTORAGE; this.WEBSQL = DriverType.WEBSQL; this._defaultConfig = extend({}, DefaultConfig); this._config = extend({}, this._defaultConfig, options); this._driverSet = null; this._initDriver = null; this._ready = false; this._dbInfo = null; this._wrapLibraryMethodsWithReady(); this.setDriver(this._config.driver); } // The actual localForage object that we expose as a module or via a // global. It's extended by pulling in one of our other libraries. // Set any config values for localForage; can be called anytime before // the first API call (e.g. `getItem`, `setItem`). // We loop through options so we don't overwrite existing config // values. LocalForage.prototype.config = function config(options) { // If the options argument is an object, we use it to set values. // Otherwise, we return either a specified config value or all // config values. if (typeof options === 'object') { // If localforage is ready and fully initialized, we can't set // any new configuration values. Instead, we return an error. if (this._ready) { return new Error("Can't call config() after localforage " + 'has been used.'); } for (var i in options) { if (i === 'storeName') { options[i] = options[i].replace(/\W/g, '_'); } this._config[i] = options[i]; } // after all config options are set and // the driver option is used, try setting it if ('driver' in options && options.driver) { this.setDriver(this._config.driver); } return true; } else if (typeof options === 'string') { return this._config[options]; } else { return this._config; } }; // Used to define a custom driver, shared across all instances of // localForage. LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) { var promise = new Promise(function (resolve, reject) { try { var driverName = driverObject._driver; var complianceError = new Error('Custom driver not compliant; see ' + 'https://mozilla.github.io/localForage/#definedriver'); var namingError = new Error('Custom driver name already in use: ' + driverObject._driver); // A driver name should be defined and not overlap with the // library-defined, default drivers. if (!driverObject._driver) { reject(complianceError); return; } if (isLibraryDriver(driverObject._driver)) { reject(namingError); return; } var customDriverMethods = LibraryMethods.concat('_initStorage'); for (var i = 0; i < customDriverMethods.length; i++) { var customDriverMethod = customDriverMethods[i]; if (!customDriverMethod || !driverObject[customDriverMethod] || typeof driverObject[customDriverMethod] !== 'function') { reject(complianceError); return; } } var supportPromise = Promise.resolve(true); if ('_support' in driverObject) { if (driverObject._support && typeof driverObject._support === 'function') { supportPromise = driverObject._support(); } else { supportPromise = Promise.resolve(!!driverObject._support); } } supportPromise.then(function (supportResult) { driverSupport[driverName] = supportResult; CustomDrivers[driverName] = driverObject; resolve(); }, reject); } catch (e) { reject(e); } }); promise.then(callback, errorCallback); return promise; }; LocalForage.prototype.driver = function driver() { return this._driver || null; }; LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) { var self = this; var getDriverPromise = (function () { if (isLibraryDriver(driverName)) { switch (driverName) { case self.INDEXEDDB: return new Promise(function (resolve, reject) { resolve(__webpack_require__(1)); }); case self.LOCALSTORAGE: return new Promise(function (resolve, reject) { resolve(__webpack_require__(2)); }); case self.WEBSQL: return new Promise(function (resolve, reject) { resolve(__webpack_require__(4)); }); } } else if (CustomDrivers[driverName]) { return Promise.resolve(CustomDrivers[driverName]); } return Promise.reject(new Error('Driver not found.')); })(); getDriverPromise.then(callback, errorCallback); return getDriverPromise; }; LocalForage.prototype.getSerializer = function getSerializer(callback) { var serializerPromise = new Promise(function (resolve, reject) { resolve(__webpack_require__(3)); }); if (callback && typeof callback === 'function') { serializerPromise.then(function (result) { callback(result); }); } return serializerPromise; }; LocalForage.prototype.ready = function ready(callback) { var self = this; var promise = self._driverSet.then(function () { if (self._ready === null) { self._ready = self._initDriver(); } return self._ready; }); promise.then(callback, callback); return promise; }; LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) { var self = this; if (!isArray(drivers)) { drivers = [drivers]; } var supportedDrivers = this._getSupportedDrivers(drivers); function setDriverToConfig() { self._config.driver = self.driver(); } function initDriver(supportedDrivers) { return function () { var currentDriverIndex = 0; function driverPromiseLoop() { while (currentDriverIndex < supportedDrivers.length) { var driverName = supportedDrivers[currentDriverIndex]; currentDriverIndex++; self._dbInfo = null; self._ready = null; return self.getDriver(driverName).then(function (driver) { self._extend(driver); setDriverToConfig(); self._ready = self._initStorage(self._config); return self._ready; })['catch'](driverPromiseLoop); } setDriverToConfig(); var error = new Error('No available storage method found.'); self._driverSet = Promise.reject(error); return self._driverSet; } return driverPromiseLoop(); }; } // There might be a driver initialization in progress // so wait for it to finish in order to avoid a possible // race condition to set _dbInfo var oldDriverSetDone = this._driverSet !== null ? this._driverSet['catch'](function () { return Promise.resolve(); }) : Promise.resolve(); this._driverSet = oldDriverSetDone.then(function () { var driverName = supportedDrivers[0]; self._dbInfo = null; self._ready = null; return self.getDriver(driverName).then(function (driver) { self._driver = driver._driver; setDriverToConfig(); self._wrapLibraryMethodsWithReady(); self._initDriver = initDriver(supportedDrivers); }); })['catch'](function () { setDriverToConfig(); var error = new Error('No available storage method found.'); self._driverSet = Promise.reject(error); return self._driverSet; }); this._driverSet.then(callback, errorCallback); return this._driverSet; }; LocalForage.prototype.supports = function supports(driverName) { return !!driverSupport[driverName]; }; LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) { extend(this, libraryMethodsAndProperties); }; LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) { var supportedDrivers = []; for (var i = 0, len = drivers.length; i < len; i++) { var driverName = drivers[i]; if (this.supports(driverName)) { supportedDrivers.push(driverName); } } return supportedDrivers; }; LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() { // Add a stub for each driver API method that delays the call to the // corresponding driver method until localForage is ready. These stubs // will be replaced by the driver methods as soon as the driver is // loaded, so there is no performance impact. for (var i = 0; i < LibraryMethods.length; i++) { callWhenReady(this, LibraryMethods[i]); } }; LocalForage.prototype.createInstance = function createInstance(options) { return new LocalForage(options); }; return LocalForage; })(); var localForage = new LocalForage(); exports['default'] = localForage; }).call(typeof window !== 'undefined' ? window : self); module.exports = exports['default']; /***/ }, /* 1 */ /***/ function(module, exports) { // Some code originally from async_storage.js in // [Gaia](https://github.com/mozilla-b2g/gaia). 'use strict'; exports.__esModule = true; (function () { 'use strict'; var globalObject = this; // Initialize IndexedDB; fall back to vendor-prefixed versions if needed. var indexedDB = indexedDB || this.indexedDB || this.webkitIndexedDB || this.mozIndexedDB || this.OIndexedDB || this.msIndexedDB; // If IndexedDB isn't available, we get outta here! if (!indexedDB) { return; } var DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support'; var supportsBlobs; var dbContexts; // Abstracts constructing a Blob object, so it also works in older // browsers that don't support the native Blob constructor. (i.e. // old QtWebKit versions, at least). function _createBlob(parts, properties) { parts = parts || []; properties = properties || {}; try { return new Blob(parts, properties); } catch (e) { if (e.name !== 'TypeError') { throw e; } var BlobBuilder = globalObject.BlobBuilder || globalObject.MSBlobBuilder || globalObject.MozBlobBuilder || globalObject.WebKitBlobBuilder; var builder = new BlobBuilder(); for (var i = 0; i < parts.length; i += 1) { builder.append(parts[i]); } return builder.getBlob(properties.type); } } // Transform a binary string to an array buffer, because otherwise // weird stuff happens when you try to work with the binary string directly. // It is known. // From http://stackoverflow.com/questions/14967647/ (continues on next line) // encode-decode-image-with-base64-breaks-image (2013-04-21) function _binStringToArrayBuffer(bin) { var length = bin.length; var buf = new ArrayBuffer(length); var arr = new Uint8Array(buf); for (var i = 0; i < length; i++) { arr[i] = bin.charCodeAt(i); } return buf; } // Fetch a blob using ajax. This reveals bugs in Chrome < 43. // For details on all this junk: // https://github.com/nolanlawson/state-of-binary-data-in-the-browser#readme function _blobAjax(url) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.withCredentials = true; xhr.responseType = 'arraybuffer'; xhr.onreadystatechange = function () { if (xhr.readyState !== 4) { return; } if (xhr.status === 200) { return resolve({ response: xhr.response, type: xhr.getResponseHeader('Content-Type') }); } reject({ status: xhr.status, response: xhr.response }); }; xhr.send(); }); } // // Detect blob support. Chrome didn't support it until version 38. // In version 37 they had a broken version where PNGs (and possibly // other binary types) aren't stored correctly, because when you fetch // them, the content type is always null. // // Furthermore, they have some outstanding bugs where blobs occasionally // are read by FileReader as null, or by ajax as 404s. // // Sadly we use the 404 bug to detect the FileReader bug, so if they // get fixed independently and released in different versions of Chrome, // then the bug could come back. So it's worthwhile to watch these issues: // 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916 // FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836 // function _checkBlobSupportWithoutCaching(idb) { return new Promise(function (resolve, reject) { var blob = _createBlob([''], { type: 'image/png' }); var txn = idb.transaction([DETECT_BLOB_SUPPORT_STORE], 'readwrite'); txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key'); txn.oncomplete = function () { // have to do it in a separate transaction, else the correct // content type is always returned var blobTxn = idb.transaction([DETECT_BLOB_SUPPORT_STORE], 'readwrite'); var getBlobReq = blobTxn.objectStore(DETECT_BLOB_SUPPORT_STORE).get('key'); getBlobReq.onerror = reject; getBlobReq.onsuccess = function (e) { var storedBlob = e.target.result; var url = URL.createObjectURL(storedBlob); _blobAjax(url).then(function (res) { resolve(!!(res && res.type === 'image/png')); }, function () { resolve(false); }).then(function () { URL.revokeObjectURL(url); }); }; }; })['catch'](function () { return false; // error, so assume unsupported }); } function _checkBlobSupport(idb) { if (typeof supportsBlobs === 'boolean') { return Promise.resolve(supportsBlobs); } return _checkBlobSupportWithoutCaching(idb).then(function (value) { supportsBlobs = value; return supportsBlobs; }); } // encode a blob for indexeddb engines that don't support blobs function _encodeBlob(blob) { return new Promise(function (resolve, reject) { var reader = new FileReader(); reader.onerror = reject; reader.onloadend = function (e) { var base64 = btoa(e.target.result || ''); resolve({ __local_forage_encoded_blob: true, data: base64, type: blob.type }); }; reader.readAsBinaryString(blob); }); } // decode an encoded blob function _decodeBlob(encodedBlob) { var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data)); return _createBlob([arrayBuff], { type: encodedBlob.type }); } // is this one of our fancy encoded blobs? function _isEncodedBlob(value) { return value && value.__local_forage_encoded_blob; } // Open the IndexedDB database (automatically creates one if one didn't // previously exist), using any options set in the config. function _initStorage(options) { var self = this; var dbInfo = { db: null }; if (options) { for (var i in options) { dbInfo[i] = options[i]; } } // Initialize a singleton container for all running localForages. if (!dbContexts) { dbContexts = {}; } // Get the current context of the database; var dbContext = dbContexts[dbInfo.name]; // ...or create a new context. if (!dbContext) { dbContext = { // Running localForages sharing a database. forages: [], // Shared database. db: null }; // Register the new context in the global container. dbContexts[dbInfo.name] = dbContext; } // Register itself as a running localForage in the current context. dbContext.forages.push(this); // Create an array of readiness of the related localForages. var readyPromises = []; function ignoreErrors() { // Don't handle errors here, // just makes sure related localForages aren't pending. return Promise.resolve(); } for (var j = 0; j < dbContext.forages.length; j++) { var forage = dbContext.forages[j]; if (forage !== this) { // Don't wait for itself... readyPromises.push(forage.ready()['catch'](ignoreErrors)); } } // Take a snapshot of the related localForages. var forages = dbContext.forages.slice(0); // Initialize the connection process only when // all the related localForages aren't pending. return Promise.all(readyPromises).then(function () { dbInfo.db = dbContext.db; // Get the connection or open a new one without upgrade. return _getOriginalConnection(dbInfo); }).then(function (db) { dbInfo.db = db; if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) { // Reopen the database for upgrading. return _getUpgradedConnection(dbInfo); } return db; }).then(function (db) { dbInfo.db = dbContext.db = db; self._dbInfo = dbInfo; // Share the final connection amongst related localForages. for (var k = 0; k < forages.length; k++) { var forage = forages[k]; if (forage !== self) { // Self is already up-to-date. forage._dbInfo.db = dbInfo.db; forage._dbInfo.version = dbInfo.version; } } }); } function _getOriginalConnection(dbInfo) { return _getConnection(dbInfo, false); } function _getUpgradedConnection(dbInfo) { return _getConnection(dbInfo, true); } function _getConnection(dbInfo, upgradeNeeded) { return new Promise(function (resolve, reject) { if (dbInfo.db) { if (upgradeNeeded) { dbInfo.db.close(); } else { return resolve(dbInfo.db); } } var dbArgs = [dbInfo.name]; if (upgradeNeeded) { dbArgs.push(dbInfo.version); } var openreq = indexedDB.open.apply(indexedDB, dbArgs); if (upgradeNeeded) { openreq.onupgradeneeded = function (e) { var db = openreq.result; try { db.createObjectStore(dbInfo.storeName); if (e.oldVersion <= 1) { // Added when support for blob shims was added db.createObjectStore(DETECT_BLOB_SUPPORT_STORE); } } catch (ex) { if (ex.name === 'ConstraintError') { globalObject.console.warn('The database "' + dbInfo.name + '"' + ' has been upgraded from version ' + e.oldVersion + ' to version ' + e.newVersion + ', but the storage "' + dbInfo.storeName + '" already exists.'); } else { throw ex; } } }; } openreq.onerror = function () { reject(openreq.error); }; openreq.onsuccess = function () { resolve(openreq.result); }; }); } function _isUpgradeNeeded(dbInfo, defaultVersion) { if (!dbInfo.db) { return true; } var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName); var isDowngrade = dbInfo.version < dbInfo.db.version; var isUpgrade = dbInfo.version > dbInfo.db.version; if (isDowngrade) { // If the version is not the default one // then warn for impossible downgrade. if (dbInfo.version !== defaultVersion) { globalObject.console.warn('The database "' + dbInfo.name + '"' + ' can\'t be downgraded from version ' + dbInfo.db.version + ' to version ' + dbInfo.version + '.'); } // Align the versions to prevent errors. dbInfo.version = dbInfo.db.version; } if (isUpgrade || isNewStore) { // If the store is new then increment the version (if needed). // This will trigger an "upgradeneeded" event which is required // for creating a store. if (isNewStore) { var incVersion = dbInfo.db.version + 1; if (incVersion > dbInfo.version) { dbInfo.version = incVersion; } } return true; } return false; } function getItem(key, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly').objectStore(dbInfo.storeName); var req = store.get(key); req.onsuccess = function () { var value = req.result; if (value === undefined) { value = null; } if (_isEncodedBlob(value)) { value = _decodeBlob(value); } resolve(value); }; req.onerror = function () { reject(req.error); }; })['catch'](reject); }); executeCallback(promise, callback); return promise; } // Iterate over all items stored in database. function iterate(iterator, callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly').objectStore(dbInfo.storeName); var req = store.openCursor(); var iterationNumber = 1; req.onsuccess = function () { var cursor = req.result; if (cursor) { var value = cursor.value; if (_isEncodedBlob(value)) { value = _decodeBlob(value); } var result = iterator(value, cursor.key, iterationNumber++); if (result !== void 0) { resolve(result); } else { cursor['continue'](); } } else { resolve(); } }; req.onerror = function () { reject(req.error); }; })['catch'](reject); }); executeCallback(promise, callback); return promise; } function setItem(key, value, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = new Promise(function (resolve, reject) { var dbInfo; self.ready().then(function () { dbInfo = self._dbInfo; if (value instanceof Blob) { return _checkBlobSupport(dbInfo.db).then(function (blobSupport) { if (blobSupport) { return value; } return _encodeBlob(value); }); } return value; }).then(function (value) { var transaction = dbInfo.db.transaction(dbInfo.storeName, 'readwrite'); var store = transaction.objectStore(dbInfo.storeName); // The reason we don't _save_ null is because IE 10 does // not support saving the `null` type in IndexedDB. How // ironic, given the bug below! // See: https://github.com/mozilla/localForage/issues/161 if (value === null) { value = undefined; } var req = store.put(value, key); transaction.oncomplete = function () { // Cast to undefined so the value passed to // callback/promise is the same as what one would get out // of `getItem()` later. This leads to some weirdness // (setItem('foo', undefined) will return `null`), but // it's not my fault localStorage is our baseline and that // it's weird. if (value === undefined) { value = null; } resolve(value); }; transaction.onabort = transaction.onerror = function () { var err = req.error ? req.error : req.transaction.error; reject(err); }; })['catch'](reject); }); executeCallback(promise, callback); return promise; } function removeItem(key, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; var transaction = dbInfo.db.transaction(dbInfo.storeName, 'readwrite'); var store = transaction.objectStore(dbInfo.storeName); // We use a Grunt task to make this safe for IE and some // versions of Android (including those used by Cordova). // Normally IE won't like `.delete()` and will insist on // using `['delete']()`, but we have a build step that // fixes this for us now. var req = store['delete'](key); transaction.oncomplete = function () { resolve(); }; transaction.onerror = function () { reject(req.error); }; // The request will be also be aborted if we've exceeded our storage // space. transaction.onabort = function () { var err = req.error ? req.error : req.transaction.error; reject(err); }; })['catch'](reject); }); executeCallback(promise, callback); return promise; } function clear(callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; var transaction = dbInfo.db.transaction(dbInfo.storeName, 'readwrite'); var store = transaction.objectStore(dbInfo.storeName); var req = store.clear(); transaction.oncomplete = function () { resolve(); }; transaction.onabort = transaction.onerror = function () { var err = req.error ? req.error : req.transaction.error; reject(err); }; })['catch'](reject); }); executeCallback(promise, callback); return promise; } function length(callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly').objectStore(dbInfo.storeName); var req = store.count(); req.onsuccess = function () { resolve(req.result); }; req.onerror = function () { reject(req.error); }; })['catch'](reject); }); executeCallback(promise, callback); return promise; } function key(n, callback) { var self = this; var promise = new Promise(function (resolve, reject) { if (n < 0) { resolve(null); return; } self.ready().then(function () { var dbInfo = self._dbInfo; var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly').objectStore(dbInfo.storeName); var advanced = false; var req = store.openCursor(); req.onsuccess = function () { var cursor = req.result; if (!cursor) { // this means there weren't enough keys resolve(null); return; } if (n === 0) { // We have the first key, return it if that's what they // wanted. resolve(cursor.key); } else { if (!advanced) { // Otherwise, ask the cursor to skip ahead n // records. advanced = true; cursor.advance(n); } else { // When we get here, we've got the nth key. resolve(cursor.key); } } }; req.onerror = function () { reject(req.error); }; })['catch'](reject); }); executeCallback(promise, callback); return promise; } function keys(callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly').objectStore(dbInfo.storeName); var req = store.openCursor(); var keys = []; req.onsuccess = function () { var cursor = req.result; if (!cursor) { resolve(keys); return; } keys.push(cursor.key); cursor['continue'](); }; req.onerror = function () { reject(req.error); }; })['catch'](reject); }); executeCallback(promise, callback); return promise; } function executeCallback(promise, callback) { if (callback) { promise.then(function (result) { callback(null, result); }, function (error) { callback(error); }); } } var asyncStorage = { _driver: 'asyncStorage', _initStorage: _initStorage, iterate: iterate, getItem: getItem, setItem: setItem, removeItem: removeItem, clear: clear, length: length, key: key, keys: keys }; exports['default'] = asyncStorage; }).call(typeof window !== 'undefined' ? window : self); module.exports = exports['default']; /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { // If IndexedDB isn't available, we'll fall back to localStorage. // Note that this will have considerable performance and storage // side-effects (all data will be serialized on save and only data that // can be converted to a string via `JSON.stringify()` will be saved). 'use strict'; exports.__esModule = true; (function () { 'use strict'; var globalObject = this; var localStorage = null; // If the app is running inside a Google Chrome packaged webapp, or some // other context where localStorage isn't available, we don't use // localStorage. This feature detection is preferred over the old // `if (window.chrome && window.chrome.runtime)` code. // See: https://github.com/mozilla/localForage/issues/68 try { // If localStorage isn't available, we get outta here! // This should be inside a try catch if (!this.localStorage || !('setItem' in this.localStorage)) { return; } // Initialize localStorage and create a variable to use throughout // the code. localStorage = this.localStorage; } catch (e) { return; } // Config the localStorage backend, using options set in the config. function _initStorage(options) { var self = this; var dbInfo = {}; if (options) { for (var i in options) { dbInfo[i] = options[i]; } } dbInfo.keyPrefix = dbInfo.name + '/'; if (dbInfo.storeName !== self._defaultConfig.storeName) { dbInfo.keyPrefix += dbInfo.storeName + '/'; } self._dbInfo = dbInfo; return new Promise(function (resolve, reject) { resolve(__webpack_require__(3)); }).then(function (lib) { dbInfo.serializer = lib; return Promise.resolve(); }); } // Remove all keys from the datastore, effectively destroying all data in // the app's key/value store! function clear(callback) { var self = this; var promise = self.ready().then(function () { var keyPrefix = self._dbInfo.keyPrefix; for (var i = localStorage.length - 1; i >= 0; i--) { var key = localStorage.key(i); if (key.indexOf(keyPrefix) === 0) { localStorage.removeItem(key); } } }); executeCallback(promise, callback); return promise; } // Retrieve an item from the store. Unlike the original async_storage // library in Gaia, we don't modify return values at all. If a key's value // is `undefined`, we pass that value to the callback function. function getItem(key, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = self.ready().then(function () { var dbInfo = self._dbInfo; var result = localStorage.getItem(dbInfo.keyPrefix + key); // If a result was found, parse it from the serialized // string into a JS object. If result isn't truthy, the key // is likely undefined and we'll pass it straight to the // callback. if (result) { result = dbInfo.serializer.deserialize(result); } return result; }); executeCallback(promise, callback); return promise; } // Iterate over all items in the store. function iterate(iterator, callback) { var self = this; var promise = self.ready().then(function () { var dbInfo = self._dbInfo; var keyPrefix = dbInfo.keyPrefix; var keyPrefixLength = keyPrefix.length; var length = localStorage.length; // We use a dedicated iterator instead of the `i` variable below // so other keys we fetch in localStorage aren't counted in // the `iterationNumber` argument passed to the `iterate()` // callback. // // See: github.com/mozilla/localForage/pull/435#discussion_r38061530 var iterationNumber = 1; for (var i = 0; i < length; i++) { var key = localStorage.key(i); if (key.indexOf(keyPrefix) !== 0) { continue; } var value = localStorage.getItem(key); // If a result was found, parse it from the serialized // string into a JS object. If result isn't truthy, the // key is likely undefined and we'll pass it straight // to the iterator. if (value) { value = dbInfo.serializer.deserialize(value); } value = iterator(value, key.substring(keyPrefixLength), iterationNumber++); if (value !== void 0) { return value; } } }); executeCallback(promise, callback); return promise; } // Same as localStorage's key() method, except takes a callback. function key(n, callback) { var self = this; var promise = self.ready().then(function () { var dbInfo = self._dbInfo; var result; try { result = localStorage.key(n); } catch (error) { result = null; } // Remove the prefix from the key, if a key is found. if (result) { result = result.substring(dbInfo.keyPrefix.length); } return result; }); executeCallback(promise, callback); return promise; } function keys(callback) { var self = this; var promise = self.ready().then(function () { var dbInfo = self._dbInfo; var length = localStorage.length; var keys = []; for (var i = 0; i < length; i++) { if (localStorage.key(i).indexOf(dbInfo.keyPrefix) === 0) { keys.push(localStorage.key(i).substring(dbInfo.keyPrefix.length)); } } return keys; }); executeCallback(promise, callback); return promise; } // Supply the number of keys in the datastore to the callback function. function length(callback) { var self = this; var promise = self.keys().then(function (keys) { return keys.length; }); executeCallback(promise, callback); return promise; } // Remove an item from the store, nice and simple. function removeItem(key, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = self.ready().then(function () { var dbInfo = self._dbInfo; localStorage.removeItem(dbInfo.keyPrefix + key); }); executeCallback(promise, callback); return promise; } // Set a key's value and run an optional callback once the value is set. // Unlike Gaia's implementation, the callback function is passed the value, // in case you want to operate on that value only after you're sure it // saved, or something like that. function setItem(key, value, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = self.ready().then(function () { // Convert undefined values to null. // https://github.com/mozilla/localForage/pull/42 if (value === undefined) { value = null; } // Save the original value to pass to the callback. var originalValue = value; return new Promise(function (resolve, reject) { var dbInfo = self._dbInfo; dbInfo.serializer.serialize(value, function (value, error) { if (error) { reject(error); } else { try { localStorage.setItem(dbInfo.keyPrefix + key, value); resolve(originalValue); } catch (e) { // localStorage capacity exceeded. // TODO: Make this a specific error/event. if (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') { reject(e); } reject(e); } } }); }); }); executeCallback(promise, callback); return promise; } function executeCallback(promise, callback) { if (callback) { promise.then(function (result) { callback(null, result); }, function (error) { callback(error); }); } } var localStorageWrapper = { _driver: 'localStorageWrapper', _initStorage: _initStorage, // Default API, from Gaia/localStorage. iterate: iterate, getItem: getItem, setItem: setItem, removeItem: removeItem, clear: clear, length: length, key: key, keys: keys }; exports['default'] = localStorageWrapper; }).call(typeof window !== 'undefined' ? window : self); module.exports = exports['default']; /***/ }, /* 3 */ /***/ function(module, exports) { 'use strict'; exports.__esModule = true; (function () { 'use strict'; // Sadly, the best way to save binary data in WebSQL/localStorage is serializing // it to Base64, so this is how we store it to prevent very strange errors with less // verbose ways of binary <-> string data storage. var BASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; var BLOB_TYPE_PREFIX = '~~local_forage_type~'; var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/; var SERIALIZED_MARKER = '__lfsc__:'; var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length; // OMG the serializations! var TYPE_ARRAYBUFFER = 'arbf'; var TYPE_BLOB = 'blob'; var TYPE_INT8ARRAY = 'si08'; var TYPE_UINT8ARRAY = 'ui08'; var TYPE_UINT8CLAMPEDARRAY = 'uic8'; var TYPE_INT16ARRAY = 'si16'; var TYPE_INT32ARRAY = 'si32'; var TYPE_UINT16ARRAY = 'ur16'; var TYPE_UINT32ARRAY = 'ui32'; var TYPE_FLOAT32ARRAY = 'fl32'; var TYPE_FLOAT64ARRAY = 'fl64'; var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length; // Get out of our habit of using `window` inline, at least. var globalObject = this; // Abstracts constructing a Blob object, so it also works in older // browsers that don't support the native Blob constructor. (i.e. // old QtWebKit versions, at least). function _createBlob(parts, properties) { parts = parts || []; properties = properties || {}; try { return new Blob(parts, properties); } catch (err) { if (err.name !== 'TypeError') { throw err; } var BlobBuilder = globalObject.BlobBuilder || globalObject.MSBlobBuilder || globalObject.MozBlobBuilder || globalObject.WebKitBlobBuilder; var builder = new BlobBuilder(); for (var i = 0; i < parts.length; i += 1) { builder.append(parts[i]); } return builder.getBlob(properties.type); } } // Serialize a value, afterwards executing a callback (which usually // instructs the `setItem()` callback/promise to be executed). This is how // we store binary data with localStorage. function serialize(value, callback) { var valueString = ''; if (value) { valueString = value.toString(); } // Cannot use `value instanceof ArrayBuffer` or such here, as these // checks fail when running the tests using casper.js... // // TODO: See why those tests fail and use a better solution. if (value && (value.toString() === '[object ArrayBuffer]' || value.buffer && value.buffer.toString() === '[object ArrayBuffer]')) { // Convert binary arrays to a string and prefix the string with // a special marker. var buffer; var marker = SERIALIZED_MARKER; if (value instanceof ArrayBuffer) { buffer = value; marker += TYPE_ARRAYBUFFER; } else { buffer = value.buffer; if (valueString === '[object Int8Array]') { marker += TYPE_INT8ARRAY; } else if (valueString === '[object Uint8Array]') { marker += TYPE_UINT8ARRAY; } else if (valueString === '[object Uint8ClampedArray]') { marker += TYPE_UINT8CLAMPEDARRAY; } else if (valueString === '[object Int16Array]') { marker += TYPE_INT16ARRAY; } else if (valueString === '[object Uint16Array]') { marker += TYPE_UINT16ARRAY; } else if (valueString === '[object Int32Array]') { marker += TYPE_INT32ARRAY; } else if (valueString === '[object Uint32Array]') { marker += TYPE_UINT32ARRAY; } else if (valueString === '[object Float32Array]') { marker += TYPE_FLOAT32ARRAY; } else if (valueString === '[object Float64Array]') { marker += TYPE_FLOAT64ARRAY; } else { callback(new Error('Failed to get type for BinaryArray')); } } callback(marker + bufferToString(buffer)); } else if (valueString === '[object Blob]') { // Conver the blob to a binaryArray and then to a string. var fileReader = new FileReader(); fileReader.onload = function () { // Backwards-compatible prefix for the blob type. var str = BLOB_TYPE_PREFIX + value.type + '~' + bufferToString(this.result); callback(SERIALIZED_MARKER + TYPE_BLOB + str); }; fileReader.readAsArrayBuffer(value); } else { try { callback(JSON.stringify(value)); } catch (e) { console.error("Couldn't convert value into a JSON string: ", value); callback(null, e); } } } // Deserialize data we've inserted into a value column/field. We place // special markers into our strings to mark them as encoded; this isn't // as nice as a meta field, but it's the only sane thing we can do whilst // keeping localStorage support intact. // // Oftentimes this will just deserialize JSON content, but if we have a // special marker (SERIALIZED_MARKER, defined above), we will extract // some kind of arraybuffer/binary data/typed array out of the string. function deserialize(value) { // If we haven't marked this string as being specially serialized (i.e. // something other than serialized JSON), we can just return it and be // done with it. if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) { return JSON.parse(value); } // The following code deals with deserializing some kind of Blob or // TypedArray. First we separate out the type of data we're dealing // with from the data itself. var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH); var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH); var blobType; // Backwards-compatible blob type serialization strategy. // DBs created with older versions of localForage will simply not have the blob type. if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) { var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX); blobType = matcher[1]; serializedString = serializedString.substring(matcher[0].length); } var buffer = stringToBuffer(serializedString); // Return the right type based on the code/type set during // serialization. switch (type) { case TYPE_ARRAYBUFFER: return buffer; case TYPE_BLOB: return _createBlob([buffer], { type: blobType }); case TYPE_INT8ARRAY: return new Int8Array(buffer); case TYPE_UINT8ARRAY: return new Uint8Array(buffer); case TYPE_UINT8CLAMPEDARRAY: return new Uint8ClampedArray(buffer); case TYPE_INT16ARRAY: return new Int16Array(buffer); case TYPE_UINT16ARRAY: return new Uint16Array(buffer); case TYPE_INT32ARRAY: return new Int32Array(buffer); case TYPE_UINT32ARRAY: return new Uint32Array(buffer); case TYPE_FLOAT32ARRAY: return new Float32Array(buffer); case TYPE_FLOAT64ARRAY: return new Float64Array(buffer); default: throw new Error('Unkown type: ' + type); } } function stringToBuffer(serializedString) { // Fill the string into a ArrayBuffer. var bufferLength = serializedString.length * 0.75; var len = serializedString.length; var i; var p = 0; var encoded1, encoded2, encoded3, encoded4; if (serializedString[serializedString.length - 1] === '=') { bufferLength--; if (serializedString[serializedString.length - 2] === '=') { bufferLength--; } } var buffer = new ArrayBuffer(bufferLength); var bytes = new Uint8Array(buffer); for (i = 0; i < len; i += 4) { encoded1 = BASE_CHARS.indexOf(serializedString[i]); encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]); encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]); encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]); /*jslint bitwise: true */ bytes[p++] = encoded1 << 2 | encoded2 >> 4; bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2; bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63; } return buffer; } // Converts a buffer to a string to store, serialized, in the backend // storage library. function bufferToString(buffer) { // base64-arraybuffer var bytes = new Uint8Array(buffer); var base64String = ''; var i; for (i = 0; i < bytes.length; i += 3) { /*jslint bitwise: true */ base64String += BASE_CHARS[bytes[i] >> 2]; base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4]; base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6]; base64String += BASE_CHARS[bytes[i + 2] & 63]; } if (bytes.length % 3 === 2) { base64String = base64String.substring(0, base64String.length - 1) + '='; } else if (bytes.length % 3 === 1) { base64String = base64String.substring(0, base64String.length - 2) + '=='; } return base64String; } var localforageSerializer = { serialize: serialize, deserialize: deserialize, stringToBuffer: stringToBuffer, bufferToString: bufferToString }; exports['default'] = localforageSerializer; }).call(typeof window !== 'undefined' ? window : self); module.exports = exports['default']; /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { /* * Includes code from: * * base64-arraybuffer * https://github.com/niklasvh/base64-arraybuffer * * Copyright (c) 2012 Niklas von Hertzen * Licensed under the MIT license. */ 'use strict'; exports.__esModule = true; (function () { 'use strict'; var globalObject = this; var openDatabase = this.openDatabase; // If WebSQL methods aren't available, we can stop now. if (!openDatabase) { return; } // Open the WebSQL database (automatically creates one if one didn't // previously exist), using any options set in the config. function _initStorage(options) { var self = this; var dbInfo = { db: null }; if (options) { for (var i in options) { dbInfo[i] = typeof options[i] !== 'string' ? options[i].toString() : options[i]; } } var dbInfoPromise = new Promise(function (resolve, reject) { // Open the database; the openDatabase API will automatically // create it for us if it doesn't exist. try { dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size); } catch (e) { return self.setDriver(self.LOCALSTORAGE).then(function () { return self._initStorage(options); }).then(resolve)['catch'](reject); } // Create our key/value table if it doesn't exist. dbInfo.db.transaction(function (t) { t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + ' (id INTEGER PRIMARY KEY, key unique, value)', [], function () { self._dbInfo = dbInfo; resolve(); }, function (t, error) { reject(error); }); }); }); return new Promise(function (resolve, reject) { resolve(__webpack_require__(3)); }).then(function (lib) { dbInfo.serializer = lib; return dbInfoPromise; }); } function getItem(key, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; dbInfo.db.transaction(function (t) { t.executeSql('SELECT * FROM ' + dbInfo.storeName + ' WHERE key = ? LIMIT 1', [key], function (t, results) { var result = results.rows.length ? results.rows.item(0).value : null; // Check to see if this is serialized content we need to // unpack. if (result) { result = dbInfo.serializer.deserialize(result); } resolve(result); }, function (t, error) { reject(error); }); }); })['catch'](reject); }); executeCallback(promise, callback); return promise; } function iterate(iterator, callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; dbInfo.db.transaction(function (t) { t.executeSql('SELECT * FROM ' + dbInfo.storeName, [], function (t, results) { var rows = results.rows; var length = rows.length; for (var i = 0; i < length; i++) { var item = rows.item(i); var result = item.value; // Check to see if this is serialized content // we need to unpack. if (result) { result = dbInfo.serializer.deserialize(result); } result = iterator(result, item.key, i + 1); // void(0) prevents problems with redefinition // of `undefined`. if (result !== void 0) { resolve(result); return; } } resolve(); }, function (t, error) { reject(error); }); }); })['catch'](reject); }); executeCallback(promise, callback); return promise; } function setItem(key, value, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = new Promise(function (resolve, reject) { self.ready().then(function () { // The localStorage API doesn't return undefined values in an // "expected" way, so undefined is always cast to null in all // drivers. See: https://github.com/mozilla/localForage/pull/42 if (value === undefined) { value = null; } // Save the original value to pass to the callback. var originalValue = value; var dbInfo = self._dbInfo; dbInfo.serializer.serialize(value, function (value, error) { if (error) { reject(error); } else { dbInfo.db.transaction(function (t) { t.executeSql('INSERT OR REPLACE INTO ' + dbInfo.storeName + ' (key, value) VALUES (?, ?)', [key, value], function () { resolve(originalValue); }, function (t, error) { reject(error); }); }, function (sqlError) { // The transaction failed; check // to see if it's a quota error. if (sqlError.code === sqlError.QUOTA_ERR) { // We reject the callback outright for now, but // it's worth trying to re-run the transaction. // Even if the user accepts the prompt to use // more storage on Safari, this error will // be called. // // TODO: Try to re-run the transaction. reject(sqlError); } }); } }); })['catch'](reject); }); executeCallback(promise, callback); return promise; } function removeItem(key, callback) { var self = this; // Cast the key to a string, as that's all we can set as a key. if (typeof key !== 'string') { globalObject.console.warn(key + ' used as a key, but it is not a string.'); key = String(key); } var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; dbInfo.db.transaction(function (t) { t.executeSql('DELETE FROM ' + dbInfo.storeName + ' WHERE key = ?', [key], function () { resolve(); }, function (t, error) { reject(error); }); }); })['catch'](reject); }); executeCallback(promise, callback); return promise; } // Deletes every item in the table. // TODO: Find out if this resets the AUTO_INCREMENT number. function clear(callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; dbInfo.db.transaction(function (t) { t.executeSql('DELETE FROM ' + dbInfo.storeName, [], function () { resolve(); }, function (t, error) { reject(error); }); }); })['catch'](reject); }); executeCallback(promise, callback); return promise; } // Does a simple `COUNT(key)` to get the number of items stored in // localForage. function length(callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; dbInfo.db.transaction(function (t) { // Ahhh, SQL makes this one soooooo easy. t.executeSql('SELECT COUNT(key) as c FROM ' + dbInfo.storeName, [], function (t, results) { var result = results.rows.item(0).c; resolve(result); }, function (t, error) { reject(error); }); }); })['catch'](reject); }); executeCallback(promise, callback); return promise; } // Return the key located at key index X; essentially gets the key from a // `WHERE id = ?`. This is the most efficient way I can think to implement // this rarely-used (in my experience) part of the API, but it can seem // inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so // the ID of each key will change every time it's updated. Perhaps a stored // procedure for the `setItem()` SQL would solve this problem? // TODO: Don't change ID on `setItem()`. function key(n, callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; dbInfo.db.transaction(function (t) { t.executeSql('SELECT key FROM ' + dbInfo.storeName + ' WHERE id = ? LIMIT 1', [n + 1], function (t, results) { var result = results.rows.length ? results.rows.item(0).key : null; resolve(result); }, function (t, error) { reject(error); }); }); })['catch'](reject); }); executeCallback(promise, callback); return promise; } function keys(callback) { var self = this; var promise = new Promise(function (resolve, reject) { self.ready().then(function () { var dbInfo = self._dbInfo; dbInfo.db.transaction(function (t) { t.executeSql('SELECT key FROM ' + dbInfo.storeName, [], function (t, results) { var keys = []; for (var i = 0; i < results.rows.length; i++) { keys.push(results.rows.item(i).key); } resolve(keys); }, function (t, error) { reject(error); }); }); })['catch'](reject); }); executeCallback(promise, callback); return promise; } function executeCallback(promise, callback) { if (callback) { promise.then(function (result) { callback(null, result); }, function (error) { callback(error); }); } } var webSQLStorage = { _driver: 'webSQLStorage', _initStorage: _initStorage, iterate: iterate, getItem: getItem, setItem: setItem, removeItem: removeItem, clear: clear, length: length, key: key, keys: keys }; exports['default'] = webSQLStorage; }).call(typeof window !== 'undefined' ? window : self); module.exports = exports['default']; /***/ } /******/ ]) }); ; }