(function (global, factory) { if (typeof define === "function" && define.amd) { define('localStorageWrapper', ['module', 'exports', '../utils/isLocalStorageValid', '../utils/serializer', '../utils/promise', '../utils/executeCallback', '../utils/normalizeKey', '../utils/getCallback'], factory); } else if (typeof exports !== "undefined") { factory(module, exports, require('../utils/isLocalStorageValid'), require('../utils/serializer'), require('../utils/promise'), require('../utils/executeCallback'), require('../utils/normalizeKey'), require('../utils/getCallback')); } else { var mod = { exports: {} }; factory(mod, mod.exports, global.isLocalStorageValid, global.serializer, global.promise, global.executeCallback, global.normalizeKey, global.getCallback); global.localStorageWrapper = mod.exports; } })(this, function (module, exports, _isLocalStorageValid, _serializer, _promise, _executeCallback, _normalizeKey, _getCallback) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _isLocalStorageValid2 = _interopRequireDefault(_isLocalStorageValid); var _serializer2 = _interopRequireDefault(_serializer); var _promise2 = _interopRequireDefault(_promise); var _executeCallback2 = _interopRequireDefault(_executeCallback); var _normalizeKey2 = _interopRequireDefault(_normalizeKey); var _getCallback2 = _interopRequireDefault(_getCallback); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // 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). function _getKeyPrefix(options, defaultConfig) { var keyPrefix = options.name + '/'; if (options.storeName !== defaultConfig.storeName) { keyPrefix += options.storeName + '/'; } return keyPrefix; } // Check if localStorage throws when saving an item function checkIfLocalStorageThrows() { var localStorageTestKey = '_localforage_support_test'; try { localStorage.setItem(localStorageTestKey, true); localStorage.removeItem(localStorageTestKey); return false; } catch (e) { return true; } } // Check if localStorage is usable and allows to save an item // This method checks if localStorage is usable in Safari Private Browsing // mode, or in any other case where the available quota for localStorage // is 0 and there wasn't any saved items yet. function _isLocalStorageUsable() { return !checkIfLocalStorageThrows() || localStorage.length > 0; } // 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 = _getKeyPrefix(options, self._defaultConfig); if (!_isLocalStorageUsable()) { return _promise2.default.reject(); } self._dbInfo = dbInfo; dbInfo.serializer = _serializer2.default; return _promise2.default.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); } } }); (0, _executeCallback2.default)(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; key = (0, _normalizeKey2.default)(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; }); (0, _executeCallback2.default)(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; } } }); (0, _executeCallback2.default)(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; }); (0, _executeCallback2.default)(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++) { var itemKey = localStorage.key(i); if (itemKey.indexOf(dbInfo.keyPrefix) === 0) { keys.push(itemKey.substring(dbInfo.keyPrefix.length)); } } return keys; }); (0, _executeCallback2.default)(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; }); (0, _executeCallback2.default)(promise, callback); return promise; } // Remove an item from the store, nice and simple. function removeItem(key, callback) { var self = this; key = (0, _normalizeKey2.default)(key); var promise = self.ready().then(function () { var dbInfo = self._dbInfo; localStorage.removeItem(dbInfo.keyPrefix + key); }); (0, _executeCallback2.default)(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; key = (0, _normalizeKey2.default)(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 _promise2.default(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); } } }); }); }); (0, _executeCallback2.default)(promise, callback); return promise; } function dropInstance(options, callback) { callback = _getCallback2.default.apply(this, arguments); options = typeof options !== 'function' && options || {}; if (!options.name) { var currentConfig = this.config(); options.name = options.name || currentConfig.name; options.storeName = options.storeName || currentConfig.storeName; } var self = this; var promise; if (!options.name) { promise = _promise2.default.reject('Invalid arguments'); } else { promise = new _promise2.default(function (resolve) { if (!options.storeName) { resolve(options.name + '/'); } else { resolve(_getKeyPrefix(options, self._defaultConfig)); } }).then(function (keyPrefix) { for (var i = localStorage.length - 1; i >= 0; i--) { var key = localStorage.key(i); if (key.indexOf(keyPrefix) === 0) { localStorage.removeItem(key); } } }); } (0, _executeCallback2.default)(promise, callback); return promise; } var localStorageWrapper = { _driver: 'localStorageWrapper', _initStorage: _initStorage, _support: (0, _isLocalStorageValid2.default)(), iterate: iterate, getItem: getItem, setItem: setItem, removeItem: removeItem, clear: clear, length: length, key: key, keys: keys, dropInstance: dropInstance }; exports.default = localStorageWrapper; module.exports = exports['default']; });