Created
February 20, 2015 16:11
-
-
Save bgrins/3b3db4c49e56d544bf9a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const {Cc, Ci, Cu, Cr} = require("chrome"); | |
Cu.importGlobalProperties(["indexedDB"]); | |
const {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); | |
/*! | |
localForage -- Offline Storage, Improved | |
Version 1.2.2 | |
https://mozilla.github.io/localForage | |
(c) 2013-2015 Mozilla, Apache License 2.0 | |
*/ | |
module.exports = (function() { | |
"use strict"; | |
// Some code originally from async_storage.js in | |
// [Gaia](https://github.com/mozilla-b2g/gaia). | |
// Originally found in https://github.com/mozilla-b2g/gaia/blob/e8f624e4cc9ea945727278039b3bc9bcb9f8667a/shared/js/async_storage.js | |
// 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]; | |
} | |
} | |
return new Promise(function(resolve, reject) { | |
var openreq = indexedDB.open(dbInfo.name, dbInfo.version); | |
openreq.onerror = function() { | |
reject(openreq.error); | |
}; | |
openreq.onupgradeneeded = function() { | |
// First time setup: create an empty object store | |
openreq.result.createObjectStore(dbInfo.storeName); | |
}; | |
openreq.onsuccess = function() { | |
dbInfo.db = openreq.result; | |
self._dbInfo = dbInfo; | |
resolve(); | |
}; | |
}); | |
} | |
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') { | |
window.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; | |
} | |
resolve(value); | |
}; | |
req.onerror = function() { | |
reject(req.error); | |
}; | |
})["catch"](reject); | |
}); | |
executeDeferedCallback(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 result = iterator(cursor.value, cursor.key, iterationNumber++); | |
if (result !== void(0)) { | |
resolve(result); | |
} else { | |
cursor["continue"](); | |
} | |
} else { | |
resolve(); | |
} | |
}; | |
req.onerror = function() { | |
reject(req.error); | |
}; | |
})["catch"](reject); | |
}); | |
executeDeferedCallback(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') { | |
window.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); | |
// 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() { | |
reject(req.error); | |
}; | |
})["catch"](reject); | |
}); | |
executeDeferedCallback(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') { | |
window.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 aborted if we've exceeded our storage | |
// space. In this case, we will reject with a specific | |
// "QuotaExceededError". | |
transaction.onabort = function(event) { | |
var error = event.target.error; | |
if (error === 'QuotaExceededError') { | |
reject(error); | |
} | |
}; | |
})["catch"](reject); | |
}); | |
executeDeferedCallback(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() { | |
reject(req.error); | |
}; | |
})["catch"](reject); | |
}); | |
executeDeferedCallback(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); | |
}); | |
} | |
} | |
function executeDeferedCallback(promise, callback) { | |
if (callback) { | |
promise.then(function(result) { | |
deferCallback(callback, result); | |
}, function(error) { | |
callback(error); | |
}); | |
} | |
} | |
// Under Chrome the callback is called before the changes (save, clear) | |
// are actually made. So we use a defer function which wait that the | |
// call stack to be empty. | |
// For more info : https://github.com/mozilla/localForage/issues/175 | |
// Pull request : https://github.com/mozilla/localForage/pull/178 | |
function deferCallback(callback, result) { | |
if (callback) { | |
return setTimeout(function() { | |
return callback(null, result); | |
}, 0); | |
} | |
} | |
var asyncStorage = { | |
_driver: 'asyncStorage', | |
_initStorage: _initStorage, | |
iterate: iterate, | |
getItem: getItem, | |
setItem: setItem, | |
removeItem: removeItem, | |
clear: clear, | |
length: length, | |
key: key, | |
keys: keys | |
}; | |
// Create an object to interface similarly to localForage. | |
// We don't need the full-blown object, since we are only | |
// ever going to use the idb driver. | |
function localForageShim() { | |
} | |
localForageShim.prototype = { | |
_ready: null, | |
_dbInfo: null, | |
_driver: 'asyncStorage', | |
_initStorage: _initStorage, | |
iterate: iterate, | |
getItem: getItem, | |
setItem: setItem, | |
removeItem: removeItem, | |
clear: clear, | |
length: length, | |
key: key, | |
keys: keys | |
}; | |
localForageShim.prototype.ready = function() { | |
var ready;; | |
if (!this._ready) { | |
ready = this._initStorage({ | |
name: "devtools-async-storage", | |
version: 1, | |
storeName: "key-values", | |
}); | |
console.log("Here", ready, this._initStorage); | |
} | |
return this._ready; | |
}; | |
return new localForageShim(); | |
})(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment