diff options
Diffstat (limited to 'www/lib/localforage/src/drivers/indexeddb.js')
| -rw-r--r-- | www/lib/localforage/src/drivers/indexeddb.js | 692 |
1 files changed, 0 insertions, 692 deletions
diff --git a/www/lib/localforage/src/drivers/indexeddb.js b/www/lib/localforage/src/drivers/indexeddb.js deleted file mode 100644 index 51f936dc..00000000 --- a/www/lib/localforage/src/drivers/indexeddb.js +++ /dev/null @@ -1,692 +0,0 @@ -import createBlob from '../utils/createBlob'; -import idb from '../utils/idb'; -import Promise from '../utils/promise'; -import executeCallback from '../utils/executeCallback'; -import executeTwoCallbacks from '../utils/executeTwoCallbacks'; - -// Some code originally from async_storage.js in -// [Gaia](https://github.com/mozilla-b2g/gaia). - -var DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support'; -var supportsBlobs; -var dbContexts; -var toString = Object.prototype.toString; - -// 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; -} - -// -// Blobs are not supported in all versions of IndexedDB, notably -// Chrome <37 and Android <5. In those versions, storing a blob will throw. -// -// Various other blob bugs exist in Chrome v37-42 (inclusive). -// Detecting them is expensive and confusing to users, and Chrome 37-42 -// is at very low usage worldwide, so we do a hacky userAgent check instead. -// -// content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120 -// 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916 -// FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836 -// -// Code borrowed from PouchDB. See: -// https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js -// -function _checkBlobSupportWithoutCaching(idb) { - return new Promise(function(resolve) { - var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, 'readwrite'); - var blob = createBlob(['']); - txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key'); - - txn.onabort = function(e) { - // If the transaction aborts now its due to not being able to - // write to the database, likely due to the disk being full - e.preventDefault(); - e.stopPropagation(); - resolve(false); - }; - - txn.oncomplete = function() { - var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/); - var matchedEdge = navigator.userAgent.match(/Edge\//); - // MS Edge pretends to be Chrome 42: - // https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx - resolve(matchedEdge || !matchedChrome || - parseInt(matchedChrome[1], 10) >= 43); - }; - }).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; - }); -} - -function _deferReadiness(dbInfo) { - var dbContext = dbContexts[dbInfo.name]; - - // Create a deferred object representing the current database operation. - var deferredOperation = {}; - - deferredOperation.promise = new Promise(function(resolve) { - deferredOperation.resolve = resolve; - }); - - // Enqueue the deferred operation. - dbContext.deferredOperations.push(deferredOperation); - - // Chain its promise to the database readiness. - if (!dbContext.dbReady) { - dbContext.dbReady = deferredOperation.promise; - } else { - dbContext.dbReady = dbContext.dbReady.then(function() { - return deferredOperation.promise; - }); - } -} - -function _advanceReadiness(dbInfo) { - var dbContext = dbContexts[dbInfo.name]; - - // Dequeue a deferred operation. - var deferredOperation = dbContext.deferredOperations.pop(); - - // Resolve its promise (which is part of the database readiness - // chain of promises). - if (deferredOperation) { - deferredOperation.resolve(); - } -} - -function _getConnection(dbInfo, upgradeNeeded) { - return new Promise(function(resolve, reject) { - - if (dbInfo.db) { - if (upgradeNeeded) { - _deferReadiness(dbInfo); - dbInfo.db.close(); - } else { - return resolve(dbInfo.db); - } - } - - var dbArgs = [dbInfo.name]; - - if (upgradeNeeded) { - dbArgs.push(dbInfo.version); - } - - var openreq = idb.open.apply(idb, 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') { - 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(e) { - e.preventDefault(); - reject(openreq.error); - }; - - openreq.onsuccess = function() { - resolve(openreq.result); - _advanceReadiness(dbInfo); - }; - }); -} - -function _getOriginalConnection(dbInfo) { - return _getConnection(dbInfo, false); -} - -function _getUpgradedConnection(dbInfo) { - return _getConnection(dbInfo, true); -} - -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) { - 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; -} - -// 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; -} - -// Specialize the default `ready()` function by making it dependent -// on the current database operations. Thus, the driver will be actually -// ready when it's been initialized (default) *and* there are no pending -// operations on the database (initiated by some other instances). -function _fullyReady(callback) { - var self = this; - - var promise = self._initReady().then(function() { - var dbContext = dbContexts[self._dbInfo.name]; - - if (dbContext && dbContext.dbReady) { - return dbContext.dbReady; - } - }); - - executeTwoCallbacks(promise, callback, callback); - return promise; -} - -// 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, - // Database readiness (promise). - dbReady: null, - // Deferred operations on the database. - deferredOperations: [] - }; - // 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(self); - - // Replace the default `ready()` function with the specialized one. - if (!self._initReady) { - self._initReady = self.ready; - self.ready = _fullyReady; - } - - // Create an array of initialization states of the related localForages. - var initPromises = []; - - 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 !== self) { // Don't wait for itself... - initPromises.push(forage._initReady().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(initPromises).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 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') { - 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') { - 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 (toString.call(value) === '[object 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); - var req = store.put(value, key); - - // 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; - } - - 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') { - 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; -} - -var asyncStorage = { - _driver: 'asyncStorage', - _initStorage: _initStorage, - iterate: iterate, - getItem: getItem, - setItem: setItem, - removeItem: removeItem, - clear: clear, - length: length, - key: key, - keys: keys -}; -export default asyncStorage; |
