diff --git a/ajax/libs/localforage/0.9.1/localforage.js b/ajax/libs/localforage/0.9.1/localforage.js new file mode 100644 index 000000000..765e33078 --- /dev/null +++ b/ajax/libs/localforage/0.9.1/localforage.js @@ -0,0 +1,2224 @@ +/*! + localForage -- Offline Storage, Improved + Version 0.9.1 + http://mozilla.github.io/localForage + (c) 2013-2014 Mozilla, Apache License 2.0 +*/ +(function() { +var define, requireModule, require, requirejs; + +(function() { + var registry = {}, seen = {}; + + define = function(name, deps, callback) { + registry[name] = { deps: deps, callback: callback }; + }; + + requirejs = require = requireModule = function(name) { + requirejs._eak_seen = registry; + + if (seen[name]) { return seen[name]; } + seen[name] = {}; + + if (!registry[name]) { + throw new Error("Could not find module " + name); + } + + var mod = registry[name], + deps = mod.deps, + callback = mod.callback, + reified = [], + exports; + + for (var i=0, l=deps.length; i= 0; i--) { + bufferView[i] = serializedString.charCodeAt(i); + } + + // Return the right type based on the code/type set during + // serialization. + switch (type) { + case TYPE_ARRAYBUFFER: + return buffer; + case TYPE_BLOB: + return new Blob([buffer]); + 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); + } + } + + // Converts a buffer to a string to store, serialized, in the backend + // storage library. + function _bufferToString(buffer) { + var str = ''; + var uint16Array = new Uint16Array(buffer); + + try { + str = String.fromCharCode.apply(null, uint16Array); + } catch (e) { + // This is a fallback implementation in case the first one does + // not work. This is required to get the phantomjs passing... + for (var i = 0; i < uint16Array.length; i++) { + str += String.fromCharCode(uint16Array[i]); + } + } + + return str; + } + + // 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() { + var str = _bufferToString(this.result); + + callback(SERIALIZED_MARKER + TYPE_BLOB + str); + }; + + fileReader.readAsArrayBuffer(value); + } else { + try { + callback(JSON.stringify(value)); + } catch (e) { + if (this.console && this.console.error) { + this.console.error("Couldn't convert value into a JSON string: ", value); + } + + callback(null, e); + } + } + } + + // 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 _this = this; + return new Promise(function(resolve, reject) { + _this.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; + + _serialize(value, function(value, error) { + if (error) { + if (callback) { + callback(null, error); + } + + reject(error); + } else { + try { + localStorage.setItem(keyPrefix + key, value); + } catch (e) { + // localStorage capacity exceeded. + // TODO: Make this a specific error/event. + if (e.name === 'QuotaExceededError' || + e.name === 'NS_ERROR_DOM_QUOTA_REACHED') { + if (callback) { + callback(null, e); + } + + reject(e); + } + } + + if (callback) { + callback(originalValue); + } + + resolve(originalValue); + } + }); + }); + }); + } + + var localStorageWrapper = { + _driver: 'localStorageWrapper', + _initStorage: _initStorage, + // Default API, from Gaia/localStorage. + getItem: getItem, + setItem: setItem, + removeItem: removeItem, + clear: clear, + length: length, + key: key, + keys: keys + }; + + if (typeof define === 'function' && define.amd) { + define('localStorageWrapper', function() { + return localStorageWrapper; + }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = localStorageWrapper; + } else { + this.localStorageWrapper = localStorageWrapper; + } +}).call(this); +/* + * Includes code from: + * + * base64-arraybuffer + * https://github.com/niklasvh/base64-arraybuffer + * + * Copyright (c) 2012 Niklas von Hertzen + * Licensed under the MIT license. + */ +(function() { + 'use strict'; + + // Sadly, the best way to save binary data in WebSQL is Base64 serializing + // it, 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+/'; + + // Promises! + var Promise = (typeof module !== 'undefined' && module.exports) ? + require('promise') : this.Promise; + + var openDatabase = this.openDatabase; + var db = null; + var dbInfo = {}; + + 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; + + // 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 _this = this; + + if (options) { + for (var i in options) { + dbInfo[i] = typeof(options[i]) !== 'string' ? options[i].toString() : options[i]; + } + } + + return new Promise(function(resolve) { + // Open the database; the openDatabase API will automatically + // create it for us if it doesn't exist. + try { + db = openDatabase(dbInfo.name, dbInfo.version, + dbInfo.description, dbInfo.size); + } catch (e) { + return _this.setDriver('localStorageWrapper').then(resolve); + } + + // Create our key/value table if it doesn't exist. + db.transaction(function(t) { + t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + + ' (id INTEGER PRIMARY KEY, key unique, value)', [], function() { + resolve(); + }, null); + }); + }); + } + + function getItem(key, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + 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 = _deserialize(result); + } + + if (callback) { + callback(result); + } + + resolve(result); + }, function(t, error) { + if (callback) { + callback(null, error); + } + + reject(error); + }); + }); + }); + }); + } + + function setItem(key, value, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.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; + + _serialize(value, function(value, error) { + if (error) { + reject(error); + } else { + db.transaction(function(t) { + t.executeSql('INSERT OR REPLACE INTO ' + dbInfo.storeName + + ' (key, value) VALUES (?, ?)', [key, value], function() { + if (callback) { + callback(originalValue); + } + + resolve(originalValue); + }, function(t, error) { + if (callback) { + callback(null, 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. + if (callback) { + callback(null, sqlError); + } + + reject(sqlError); + } + }); + } + }); + }); + }); + } + + function removeItem(key, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + db.transaction(function(t) { + t.executeSql('DELETE FROM ' + dbInfo.storeName + + ' WHERE key = ?', [key], function() { + if (callback) { + callback(); + } + + resolve(); + }, function(t, error) { + if (callback) { + callback(error); + } + + reject(error); + }); + }); + }); + }); + } + + // Deletes every item in the table. + // TODO: Find out if this resets the AUTO_INCREMENT number. + function clear(callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + db.transaction(function(t) { + t.executeSql('DELETE FROM ' + dbInfo.storeName, [], function() { + if (callback) { + callback(); + } + + resolve(); + }, function(t, error) { + if (callback) { + callback(error); + } + + reject(error); + }); + }); + }); + }); + } + + // Does a simple `COUNT(key)` to get the number of items stored in + // localForage. + function length(callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + 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; + + if (callback) { + callback(result); + } + + resolve(result); + }, function(t, error) { + if (callback) { + callback(null, error); + } + + reject(error); + }); + }); + }); + }); + } + + // 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 _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + 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; + + if (callback) { + callback(result); + } + + resolve(result); + }, function(t, error) { + if (callback) { + callback(null, error); + } + + reject(error); + }); + }); + }); + }); + } + + function keys(callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + db.transaction(function(t) { + t.executeSql('SELECT key FROM ' + dbInfo.storeName, [], + function(t, results) { + var length = results.rows.length; + var keys = []; + + for (var i = 0; i < length; i++) { + keys.push(results.rows.item(i).key); + } + + if (callback) { + callback(keys); + } + + resolve(keys); + }, function(t, error) { + if (callback) { + callback(null, error); + } + + reject(error); + }); + }); + }); + }); + } + + // 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 i; + var base64String = ''; + + 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; + } + + // 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); + + // 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 the right type based on the code/type set during + // serialization. + switch (type) { + case TYPE_ARRAYBUFFER: + return buffer; + case TYPE_BLOB: + return new Blob([buffer]); + 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); + } + } + + // 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() { + var str = _bufferToString(this.result); + + callback(SERIALIZED_MARKER + TYPE_BLOB + str); + }; + + fileReader.readAsArrayBuffer(value); + } else { + try { + callback(JSON.stringify(value)); + } catch (e) { + if (this.console && this.console.error) { + this.console.error("Couldn't convert value into a JSON string: ", value); + } + + callback(null, e); + } + } + } + + var webSQLStorage = { + _driver: 'webSQLStorage', + _initStorage: _initStorage, + getItem: getItem, + setItem: setItem, + removeItem: removeItem, + clear: clear, + length: length, + key: key, + keys: keys + }; + + if (typeof define === 'function' && define.amd) { + define('webSQLStorage', function() { + return webSQLStorage; + }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = webSQLStorage; + } else { + this.webSQLStorage = webSQLStorage; + } +}).call(this); +(function() { + 'use strict'; + + // Promises! + var Promise = (typeof module !== 'undefined' && module.exports) ? + require('promise') : this.Promise; + + // Avoid those magic constants! + var MODULE_TYPE_DEFINE = 1; + var MODULE_TYPE_EXPORT = 2; + var MODULE_TYPE_WINDOW = 3; + + // Attaching to window (i.e. no module loader) is the assumed, + // simple default. + var moduleType = MODULE_TYPE_WINDOW; + + // Find out what kind of module setup we have; if none, we'll just attach + // localForage to the main window. + if (typeof define === 'function' && define.amd) { + moduleType = MODULE_TYPE_DEFINE; + } else if (typeof module !== 'undefined' && module.exports) { + moduleType = MODULE_TYPE_EXPORT; + } + + // Initialize IndexedDB; fall back to vendor-prefixed versions if needed. + var indexedDB = indexedDB || this.indexedDB || this.webkitIndexedDB || + this.mozIndexedDB || this.OIndexedDB || + this.msIndexedDB; + + var supportsIndexedDB = indexedDB && + typeof indexedDB.open === 'function' && + indexedDB.open('_localforage_spec_test', 1) + .onupgradeneeded === null; + + // Check for WebSQL. + var openDatabase = this.openDatabase; + + // Check for localStorage. + var supportsLocalStorage = (function() { + try { + return localStorage && typeof localStorage.setItem === 'function'; + } catch (e) { + return false; + } + })(); + + // 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. + var _this = this; + var localForage = { + INDEXEDDB: 'asyncStorage', + LOCALSTORAGE: 'localStorageWrapper', + WEBSQL: 'webSQLStorage', + + _config: { + description: '', + 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 + }, + + // 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. + config: function(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) { + this._config[i] = options[i]; + } + + return true; + } else if (typeof(options) === 'string') { + return this._config[options]; + } else { + return this._config; + } + }, + + driver: function() { + return this._driver || null; + }, + + _ready: false, + + _driverSet: null, + + setDriver: function(driverName, callback, errorCallback) { + var self = this; + + this._driverSet = new Promise(function(resolve, reject) { + if ((!supportsIndexedDB && + driverName === localForage.INDEXEDDB) || + (!openDatabase && driverName === localForage.WEBSQL) || + (!supportsLocalStorage && + driverName === localForage.LOCALSTORAGE)) { + + if (errorCallback) { + errorCallback(); + } + + reject(localForage); + + return; + } + + self._ready = null; + + // We allow localForage to be declared as a module or as a + // library available without AMD/require.js. + if (moduleType === MODULE_TYPE_DEFINE) { + require([driverName], function(lib) { + self._extend(lib); + + if (callback) { + callback(); + } + resolve(); + }); + + return; + } else if (moduleType === MODULE_TYPE_EXPORT) { + // Making it browserify friendly + var driver; + switch (driverName) { + case self.INDEXEDDB: + driver = require('./drivers/indexeddb'); + break; + case self.LOCALSTORAGE: + driver = require('./drivers/localstorage'); + break; + case self.WEBSQL: + driver = require('./drivers/websql'); + } + + self._extend(driver); + } else { + self._extend(_this[driverName]); + } + + if (callback) { + callback(); + } + + resolve(); + }); + + return this._driverSet; + }, + + ready: function(callback) { + var ready = new Promise(function(resolve) { + localForage._driverSet.then(function() { + if (localForage._ready === null) { + localForage._ready = localForage._initStorage( + localForage._config); + } + + localForage._ready.then(resolve); + }); + }); + + ready.then(callback, callback); + + return ready; + }, + + _extend: function(libraryMethodsAndProperties) { + for (var i in libraryMethodsAndProperties) { + if (libraryMethodsAndProperties.hasOwnProperty(i)) { + this[i] = libraryMethodsAndProperties[i]; + } + } + } + }; + + // Select our storage library. + var storageLibrary; + // 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 + if (supportsIndexedDB) { + storageLibrary = localForage.INDEXEDDB; + } else if (openDatabase) { // WebSQL is available, so we'll use that. + storageLibrary = localForage.WEBSQL; + } else if (supportsLocalStorage) { // If nothing else is available, + // we try to use localStorage. + storageLibrary = localForage.LOCALSTORAGE; + } + + // If window.localForageConfig is set, use it for configuration. + if (this.localForageConfig) { + localForage.config = this.localForageConfig; + } + + // Set the (default) driver, or report the error. + if (storageLibrary) { + localForage.setDriver(storageLibrary); + } else { + localForage._ready = Promise.reject( + new Error('No available storage method found.')); + } + + // We allow localForage to be declared as a module or as a library + // available without AMD/require.js. + if (moduleType === MODULE_TYPE_DEFINE) { + define(function() { + return localForage; + }); + } else if (moduleType === MODULE_TYPE_EXPORT) { + module.exports = localForage; + } else { + this.localforage = localForage; + } +}).call(this); diff --git a/ajax/libs/localforage/0.9.1/localforage.min.js b/ajax/libs/localforage/0.9.1/localforage.min.js new file mode 100644 index 000000000..368967cf0 --- /dev/null +++ b/ajax/libs/localforage/0.9.1/localforage.min.js @@ -0,0 +1 @@ +!function(){var a,b,c,d;!function(){var e={},f={};a=function(a,b,c){e[a]={deps:b,callback:c}},d=c=b=function(a){function c(b){if("."!==b.charAt(0))return b;for(var c=b.split("/"),d=a.split("/").slice(0,-1),e=0,f=c.length;f>e;e++){var g=c[e];if(".."===g)d.pop();else{if("."===g)continue;d.push(g)}}return d.join("/")}if(d._eak_seen=e,f[a])return f[a];if(f[a]={},!e[a])throw new Error("Could not find module "+a);for(var g,h=e[a],i=h.deps,j=h.callback,k=[],l=0,m=i.length;m>l;l++)k.push("exports"===i[l]?g={}:b(c(i[l])));var n=j.apply(this,k);return f[a]=g||n}}(),a("promise/all",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to all.");return new b(function(b,c){function d(a){return function(b){f(a,b)}}function f(a,c){h[a]=c,0===--i&&b(h)}var g,h=[],i=a.length;0===i&&b([]);for(var j=0;ja?(b&&b(null),void d(null)):void c.ready().then(function(){var c=k.transaction(l.storeName,"readonly").objectStore(l.storeName),f=!1,g=c.openCursor();g.onsuccess=function(){var c=g.result;return c?void(0===a?(b&&b(c.key),d(c.key)):f?(b&&b(c.key),d(c.key)):(f=!0,c.advance(a))):(b&&b(null),void d(null))},g.onerror=function(){b&&b(null,g.error),e(g.error)}},function(a){e(a)})})}function h(a){var b=this;return new j(function(c,d){b.ready().then(function(){var b=k.transaction(l.storeName,"readonly").objectStore(l.storeName),e=b.openCursor(),f=[];e.onsuccess=function(){var b=e.result;return b?(f.push(b.key),void b.continue()):(a&&a(f),void c(f))},e.onerror=function(){a&&a(null,e.error),d(e.error)}},function(a){d(a)})})}function i(a,b){return a?setTimeout(function(){return a(b)},0):void 0}var j="undefined"!=typeof module&&module.exports?require("promise"):this.Promise,k=null,l={},m=m||this.indexedDB||this.webkitIndexedDB||this.mozIndexedDB||this.OIndexedDB||this.msIndexedDB;if(m){var n={_driver:"asyncStorage",_initStorage:a,getItem:b,setItem:c,removeItem:d,clear:e,length:f,key:g,keys:h};"function"==typeof define&&define.amd?define("asyncStorage",function(){return n}):"undefined"!=typeof module&&module.exports?module.exports=n:this.asyncStorage=n}}.call(this),function(){"use strict";function a(a){if(a)for(var b in a)m[b]=a[b];return l=m.name+"/",n.resolve()}function b(a){var b=this;return new n(function(c){b.ready().then(function(){o.clear(),a&&a(),c()})})}function c(a,b){var c=this;return new n(function(d,e){c.ready().then(function(){try{var c=o.getItem(l+a);c&&(c=h(c)),b&&b(c),d(c)}catch(f){b&&b(null,f),e(f)}})})}function d(a,b){var c=this;return new n(function(d){c.ready().then(function(){var c;try{c=o.key(a)}catch(e){c=null}c&&(c=c.substring(l.length)),b&&b(c),d(c)})})}function e(a){var b=this;return new n(function(c){b.ready().then(function(){for(var b=o.length,d=[],e=0;b>e;e++)d.push(o.key(e).substring(l.length));a&&a(d),c(d)})})}function f(a){var b=this;return new n(function(c){b.ready().then(function(){var b=o.length;a&&a(b),c(b)})})}function g(a,b){var c=this;return new n(function(d){c.ready().then(function(){o.removeItem(l+a),b&&b(),d()})})}function h(a){if(a.substring(0,r)!==q)return JSON.parse(a);for(var b=a.substring(D),c=a.substring(r,D),d=new ArrayBuffer(2*b.length),e=new Uint16Array(d),f=b.length-1;f>=0;f--)e[f]=b.charCodeAt(f);switch(c){case s:return d;case t:return new Blob([d]);case u:return new Int8Array(d);case v:return new Uint8Array(d);case w:return new Uint8ClampedArray(d);case x:return new Int16Array(d);case z:return new Uint16Array(d);case y:return new Int32Array(d);case A:return new Uint32Array(d);case B:return new Float32Array(d);case C:return new Float64Array(d);default:throw new Error("Unkown type: "+c)}}function i(a){var b="",c=new Uint16Array(a);try{b=String.fromCharCode.apply(null,c)}catch(d){for(var e=0;eg;g++)f.push(d.rows.item(g).key);a&&a(f),c(f)},function(b,c){a&&a(null,c),d(c)})})})})}function i(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=l[(3&c[b])<<4|c[b+1]>>4],d+=l[(15&c[b+1])<<2|c[b+2]>>6],d+=l[63&c[b+2]];return c.length%3===2?d=d.substring(0,d.length-1)+"=":c.length%3===1&&(d=d.substring(0,d.length-2)+"=="),d}function j(a){if(a.substring(0,r)!==q)return JSON.parse(a);var b,c,d,e,f,g=a.substring(D),h=a.substring(r,D),i=.75*g.length,j=g.length,k=0;"="===g[g.length-1]&&(i--,"="===g[g.length-2]&&i--);var m=new ArrayBuffer(i),n=new Uint8Array(m);for(b=0;j>b;b+=4)c=l.indexOf(g[b]),d=l.indexOf(g[b+1]),e=l.indexOf(g[b+2]),f=l.indexOf(g[b+3]),n[k++]=c<<2|d>>4,n[k++]=(15&d)<<4|e>>2,n[k++]=(3&e)<<6|63&f;switch(h){case s:return m;case t:return new Blob([m]);case u:return new Int8Array(m);case v:return new Uint8Array(m);case w:return new Uint8ClampedArray(m);case x:return new Int16Array(m);case z:return new Uint16Array(m);case y:return new Int32Array(m);case A:return new Uint32Array(m);case B:return new Float32Array(m);case C:return new Float64Array(m);default:throw new Error("Unkown type: "+h)}}function k(a,b){var c="";if(a&&(c=a.toString()),a&&("[object ArrayBuffer]"===a.toString()||a.buffer&&"[object ArrayBuffer]"===a.buffer.toString())){var d,e=q;a instanceof ArrayBuffer?(d=a,e+=s):(d=a.buffer,"[object Int8Array]"===c?e+=u:"[object Uint8Array]"===c?e+=v:"[object Uint8ClampedArray]"===c?e+=w:"[object Int16Array]"===c?e+=x:"[object Uint16Array]"===c?e+=z:"[object Int32Array]"===c?e+=y:"[object Uint32Array]"===c?e+=A:"[object Float32Array]"===c?e+=B:"[object Float64Array]"===c?e+=C:b(new Error("Failed to get type for BinaryArray"))),b(e+i(d))}else if("[object Blob]"===c){var f=new FileReader;f.onload=function(){var a=i(this.result);b(q+t+a)},f.readAsArrayBuffer(a)}else try{b(JSON.stringify(a))}catch(g){this.console&&this.console.error&&this.console.error("Couldn't convert value into a JSON string: ",a),b(null,g)}}var l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",m="undefined"!=typeof module&&module.exports?require("promise"):this.Promise,n=this.openDatabase,o=null,p={},q="__lfsc__:",r=q.length,s="arbf",t="blob",u="si08",v="ui08",w="uic8",x="si16",y="si32",z="ur16",A="ui32",B="fl32",C="fl64",D=r+s.length;if(n){var E={_driver:"webSQLStorage",_initStorage:a,getItem:b,setItem:c,removeItem:d,clear:e,length:f,key:g,keys:h};"function"==typeof define&&define.amd?define("webSQLStorage",function(){return E}):"undefined"!=typeof module&&module.exports?module.exports=E:this.webSQLStorage=E}}.call(this),function(){"use strict";var a="undefined"!=typeof module&&module.exports?require("promise"):this.Promise,b=1,c=2,d=3,e=d;"function"==typeof define&&define.amd?e=b:"undefined"!=typeof module&&module.exports&&(e=c);var f,g=g||this.indexedDB||this.webkitIndexedDB||this.mozIndexedDB||this.OIndexedDB||this.msIndexedDB,h=g&&"function"==typeof g.open&&null===g.open("_localforage_spec_test",1).onupgradeneeded,i=this.openDatabase,j=function(){try{return localStorage&&"function"==typeof localStorage.setItem}catch(a){return!1}}(),k=this,l={INDEXEDDB:"asyncStorage",LOCALSTORAGE:"localStorageWrapper",WEBSQL:"webSQLStorage",_config:{description:"",name:"localforage",size:4980736,storeName:"keyvaluepairs",version:1},config:function(a){if("object"==typeof a){if(this._ready)return new Error("Can't call config() after localforage has been used.");for(var b in a)this._config[b]=a[b];return!0}return"string"==typeof a?this._config[a]:this._config},driver:function(){return this._driver||null},_ready:!1,_driverSet:null,setDriver:function(d,f,g){var m=this;return this._driverSet=new a(function(a,n){if(!h&&d===l.INDEXEDDB||!i&&d===l.WEBSQL||!j&&d===l.LOCALSTORAGE)return g&&g(),void n(l);if(m._ready=null,e===b)return void require([d],function(b){m._extend(b),f&&f(),a()});if(e===c){var o;switch(d){case m.INDEXEDDB:o=require("./drivers/indexeddb");break;case m.LOCALSTORAGE:o=require("./drivers/localstorage");break;case m.WEBSQL:o=require("./drivers/websql")}m._extend(o)}else m._extend(k[d]);f&&f(),a()}),this._driverSet},ready:function(b){var c=new a(function(a){l._driverSet.then(function(){null===l._ready&&(l._ready=l._initStorage(l._config)),l._ready.then(a)})});return c.then(b,b),c},_extend:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b])}};h?f=l.INDEXEDDB:i?f=l.WEBSQL:j&&(f=l.LOCALSTORAGE),this.localForageConfig&&(l.config=this.localForageConfig),f?l.setDriver(f):l._ready=a.reject(new Error("No available storage method found.")),e===b?define(function(){return l}):e===c?module.exports=l:this.localforage=l}.call(this); \ No newline at end of file diff --git a/ajax/libs/localforage/0.9.1/localforage.nopromises.js b/ajax/libs/localforage/0.9.1/localforage.nopromises.js new file mode 100644 index 000000000..c653b2082 --- /dev/null +++ b/ajax/libs/localforage/0.9.1/localforage.nopromises.js @@ -0,0 +1,1541 @@ +/*! + localForage -- Offline Storage, Improved + Version 0.9.1 + http://mozilla.github.io/localForage + (c) 2013-2014 Mozilla, Apache License 2.0 +*/ +// Some code originally from async_storage.js in +// [Gaia](https://github.com/mozilla-b2g/gaia). +(function() { + 'use strict'; + + // Originally found in https://github.com/mozilla-b2g/gaia/blob/e8f624e4cc9ea945727278039b3bc9bcb9f8667a/shared/js/async_storage.js + + // Promises! + var Promise = (typeof module !== 'undefined' && module.exports) ? + require('promise') : this.Promise; + + var db = null; + var dbInfo = {}; + + // 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; + } + + // Open the IndexedDB database (automatically creates one if one didn't + // previously exist), using any options set in the config. + function _initStorage(options) { + 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() { + db = openreq.result; + resolve(); + }; + }); + } + + function getItem(key, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + var store = 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; + } + + deferCallback(callback,value); + + resolve(value); + }; + + req.onerror = function() { + if (callback) { + callback(null, req.error); + } + + reject(req.error); + }; + }, function(err) { + reject(err) ; + }); + }); + } + + function setItem(key, value, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + var store = db.transaction(dbInfo.storeName, 'readwrite') + .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); + req.onsuccess = 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; + } + + deferCallback(callback, value); + + resolve(value); + }; + req.onerror = function() { + if (callback) { + callback(null, req.error); + } + + reject(req.error); + }; + }, function(err) { + reject(err) ; + }); + }); + } + + function removeItem(key, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + var store = db.transaction(dbInfo.storeName, 'readwrite') + .objectStore(dbInfo.storeName); + + // We use `['delete']` instead of `.delete` because IE 8 will + // throw a fit if it sees the reserved word "delete" in this + // scenario. + // See: https://github.com/mozilla/localForage/pull/67 + // + // This can be removed once we no longer care about IE 8, for + // what that's worth. + var req = store['delete'](key); + req.onsuccess = function() { + + deferCallback(callback); + + resolve(); + }; + + req.onerror = function() { + if (callback) { + callback(req.error); + } + + 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". + req.onabort = function(event) { + var error = event.target.error; + if (error === 'QuotaExceededError') { + if (callback) { + callback(error); + } + + reject(error); + } + }; + }, function(err) { + reject(err) ; + }); + }); + } + + function clear(callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + var store = db.transaction(dbInfo.storeName, 'readwrite') + .objectStore(dbInfo.storeName); + var req = store.clear(); + + req.onsuccess = function() { + deferCallback(callback); + + resolve(); + }; + + req.onerror = function() { + if (callback) { + callback(null, req.error); + } + + reject(req.error); + }; + }, function(err) { + reject(err) ; + }); + }); + } + + function length(callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + var store = db.transaction(dbInfo.storeName, 'readonly') + .objectStore(dbInfo.storeName); + var req = store.count(); + + req.onsuccess = function() { + if (callback) { + callback(req.result); + } + + resolve(req.result); + }; + + req.onerror = function() { + if (callback) { + callback(null, req.error); + } + + reject(req.error); + }; + }, function(err) { + reject(err) ; + }); + }); + } + + function key(n, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + if (n < 0) { + if (callback) { + callback(null); + } + + resolve(null); + + return; + } + + _this.ready().then(function() { + var store = 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 + if (callback) { + callback(null); + } + + resolve(null); + + return; + } + + if (n === 0) { + // We have the first key, return it if that's what they + // wanted. + if (callback) { + callback(cursor.key); + } + + 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. + if (callback) { + callback(cursor.key); + } + + resolve(cursor.key); + } + } + }; + + req.onerror = function() { + if (callback) { + callback(null, req.error); + } + + reject(req.error); + }; + }, function(err) { + reject(err) ; + }); + }); + } + + function keys(callback) { + var _this = this; + + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + var store = db.transaction(dbInfo.storeName, 'readonly') + .objectStore(dbInfo.storeName); + + var req = store.openCursor(); + var keys = []; + + req.onsuccess = function() { + var cursor = req.result; + + if (!cursor) { + if (callback) { + callback(keys); + } + + resolve(keys); + return; + } + + keys.push(cursor.key); + cursor.continue(); + }; + + req.onerror = function() { + if (callback) { + callback(null, req.error); + } + + reject(req.error); + }; + }, function(err) { + reject(err) ; + }); + }); + } + + // 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, value) { + if (callback) { + return setTimeout(function() { + return callback(value); + }, 0); + } + } + + var asyncStorage = { + _driver: 'asyncStorage', + _initStorage: _initStorage, + getItem: getItem, + setItem: setItem, + removeItem: removeItem, + clear: clear, + length: length, + key: key, + keys: keys + }; + + if (typeof define === 'function' && define.amd) { + define('asyncStorage', function() { + return asyncStorage; + }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = asyncStorage; + } else { + this.asyncStorage = asyncStorage; + } +}).call(this); +// 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() { + 'use strict'; + + var keyPrefix = ''; + var dbInfo = {}; + // Promises! + var Promise = (typeof module !== 'undefined' && module.exports) ? + require('promise') : this.Promise; + 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) { + if (options) { + for (var i in options) { + dbInfo[i] = options[i]; + } + } + + keyPrefix = dbInfo.name + '/'; + + return Promise.resolve(); + } + + 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; + + // Remove all keys from the datastore, effectively destroying all data in + // the app's key/value store! + function clear(callback) { + var _this = this; + return new Promise(function(resolve) { + _this.ready().then(function() { + localStorage.clear(); + + if (callback) { + callback(); + } + + resolve(); + }); + }); + } + + // 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 _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + try { + var result = localStorage.getItem(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 = _deserialize(result); + } + + if (callback) { + callback(result); + } + + resolve(result); + } catch (e) { + if (callback) { + callback(null, e); + } + + reject(e); + } + }); + }); + } + + // Same as localStorage's key() method, except takes a callback. + function key(n, callback) { + var _this = this; + return new Promise(function(resolve) { + _this.ready().then(function() { + 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(keyPrefix.length); + } + + if (callback) { + callback(result); + } + resolve(result); + }); + }); + } + + function keys(callback) { + var _this = this; + return new Promise(function(resolve) { + _this.ready().then(function() { + var length = localStorage.length; + var keys = []; + + for (var i = 0; i < length; i++) { + keys.push(localStorage.key(i).substring(keyPrefix.length)); + } + + if (callback) { + callback(keys); + } + + resolve(keys); + }); + }); + } + + // Supply the number of keys in the datastore to the callback function. + function length(callback) { + var _this = this; + return new Promise(function(resolve) { + _this.ready().then(function() { + var result = localStorage.length; + + if (callback) { + callback(result); + } + + resolve(result); + }); + }); + } + + // Remove an item from the store, nice and simple. + function removeItem(key, callback) { + var _this = this; + return new Promise(function(resolve) { + _this.ready().then(function() { + localStorage.removeItem(keyPrefix + key); + + if (callback) { + callback(); + } + + resolve(); + }); + }); + } + + // 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); + + // Fill the string into a ArrayBuffer. + // 2 bytes for each char. + var buffer = new ArrayBuffer(serializedString.length * 2); + var bufferView = new Uint16Array(buffer); + for (var i = serializedString.length - 1; i >= 0; i--) { + bufferView[i] = serializedString.charCodeAt(i); + } + + // Return the right type based on the code/type set during + // serialization. + switch (type) { + case TYPE_ARRAYBUFFER: + return buffer; + case TYPE_BLOB: + return new Blob([buffer]); + 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); + } + } + + // Converts a buffer to a string to store, serialized, in the backend + // storage library. + function _bufferToString(buffer) { + var str = ''; + var uint16Array = new Uint16Array(buffer); + + try { + str = String.fromCharCode.apply(null, uint16Array); + } catch (e) { + // This is a fallback implementation in case the first one does + // not work. This is required to get the phantomjs passing... + for (var i = 0; i < uint16Array.length; i++) { + str += String.fromCharCode(uint16Array[i]); + } + } + + return str; + } + + // 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() { + var str = _bufferToString(this.result); + + callback(SERIALIZED_MARKER + TYPE_BLOB + str); + }; + + fileReader.readAsArrayBuffer(value); + } else { + try { + callback(JSON.stringify(value)); + } catch (e) { + if (this.console && this.console.error) { + this.console.error("Couldn't convert value into a JSON string: ", value); + } + + callback(null, e); + } + } + } + + // 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 _this = this; + return new Promise(function(resolve, reject) { + _this.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; + + _serialize(value, function(value, error) { + if (error) { + if (callback) { + callback(null, error); + } + + reject(error); + } else { + try { + localStorage.setItem(keyPrefix + key, value); + } catch (e) { + // localStorage capacity exceeded. + // TODO: Make this a specific error/event. + if (e.name === 'QuotaExceededError' || + e.name === 'NS_ERROR_DOM_QUOTA_REACHED') { + if (callback) { + callback(null, e); + } + + reject(e); + } + } + + if (callback) { + callback(originalValue); + } + + resolve(originalValue); + } + }); + }); + }); + } + + var localStorageWrapper = { + _driver: 'localStorageWrapper', + _initStorage: _initStorage, + // Default API, from Gaia/localStorage. + getItem: getItem, + setItem: setItem, + removeItem: removeItem, + clear: clear, + length: length, + key: key, + keys: keys + }; + + if (typeof define === 'function' && define.amd) { + define('localStorageWrapper', function() { + return localStorageWrapper; + }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = localStorageWrapper; + } else { + this.localStorageWrapper = localStorageWrapper; + } +}).call(this); +/* + * Includes code from: + * + * base64-arraybuffer + * https://github.com/niklasvh/base64-arraybuffer + * + * Copyright (c) 2012 Niklas von Hertzen + * Licensed under the MIT license. + */ +(function() { + 'use strict'; + + // Sadly, the best way to save binary data in WebSQL is Base64 serializing + // it, 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+/'; + + // Promises! + var Promise = (typeof module !== 'undefined' && module.exports) ? + require('promise') : this.Promise; + + var openDatabase = this.openDatabase; + var db = null; + var dbInfo = {}; + + 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; + + // 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 _this = this; + + if (options) { + for (var i in options) { + dbInfo[i] = typeof(options[i]) !== 'string' ? options[i].toString() : options[i]; + } + } + + return new Promise(function(resolve) { + // Open the database; the openDatabase API will automatically + // create it for us if it doesn't exist. + try { + db = openDatabase(dbInfo.name, dbInfo.version, + dbInfo.description, dbInfo.size); + } catch (e) { + return _this.setDriver('localStorageWrapper').then(resolve); + } + + // Create our key/value table if it doesn't exist. + db.transaction(function(t) { + t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + + ' (id INTEGER PRIMARY KEY, key unique, value)', [], function() { + resolve(); + }, null); + }); + }); + } + + function getItem(key, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + 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 = _deserialize(result); + } + + if (callback) { + callback(result); + } + + resolve(result); + }, function(t, error) { + if (callback) { + callback(null, error); + } + + reject(error); + }); + }); + }); + }); + } + + function setItem(key, value, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.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; + + _serialize(value, function(value, error) { + if (error) { + reject(error); + } else { + db.transaction(function(t) { + t.executeSql('INSERT OR REPLACE INTO ' + dbInfo.storeName + + ' (key, value) VALUES (?, ?)', [key, value], function() { + if (callback) { + callback(originalValue); + } + + resolve(originalValue); + }, function(t, error) { + if (callback) { + callback(null, 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. + if (callback) { + callback(null, sqlError); + } + + reject(sqlError); + } + }); + } + }); + }); + }); + } + + function removeItem(key, callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + db.transaction(function(t) { + t.executeSql('DELETE FROM ' + dbInfo.storeName + + ' WHERE key = ?', [key], function() { + if (callback) { + callback(); + } + + resolve(); + }, function(t, error) { + if (callback) { + callback(error); + } + + reject(error); + }); + }); + }); + }); + } + + // Deletes every item in the table. + // TODO: Find out if this resets the AUTO_INCREMENT number. + function clear(callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + db.transaction(function(t) { + t.executeSql('DELETE FROM ' + dbInfo.storeName, [], function() { + if (callback) { + callback(); + } + + resolve(); + }, function(t, error) { + if (callback) { + callback(error); + } + + reject(error); + }); + }); + }); + }); + } + + // Does a simple `COUNT(key)` to get the number of items stored in + // localForage. + function length(callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + 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; + + if (callback) { + callback(result); + } + + resolve(result); + }, function(t, error) { + if (callback) { + callback(null, error); + } + + reject(error); + }); + }); + }); + }); + } + + // 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 _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + 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; + + if (callback) { + callback(result); + } + + resolve(result); + }, function(t, error) { + if (callback) { + callback(null, error); + } + + reject(error); + }); + }); + }); + }); + } + + function keys(callback) { + var _this = this; + return new Promise(function(resolve, reject) { + _this.ready().then(function() { + db.transaction(function(t) { + t.executeSql('SELECT key FROM ' + dbInfo.storeName, [], + function(t, results) { + var length = results.rows.length; + var keys = []; + + for (var i = 0; i < length; i++) { + keys.push(results.rows.item(i).key); + } + + if (callback) { + callback(keys); + } + + resolve(keys); + }, function(t, error) { + if (callback) { + callback(null, error); + } + + reject(error); + }); + }); + }); + }); + } + + // 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 i; + var base64String = ''; + + 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; + } + + // 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); + + // 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 the right type based on the code/type set during + // serialization. + switch (type) { + case TYPE_ARRAYBUFFER: + return buffer; + case TYPE_BLOB: + return new Blob([buffer]); + 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); + } + } + + // 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() { + var str = _bufferToString(this.result); + + callback(SERIALIZED_MARKER + TYPE_BLOB + str); + }; + + fileReader.readAsArrayBuffer(value); + } else { + try { + callback(JSON.stringify(value)); + } catch (e) { + if (this.console && this.console.error) { + this.console.error("Couldn't convert value into a JSON string: ", value); + } + + callback(null, e); + } + } + } + + var webSQLStorage = { + _driver: 'webSQLStorage', + _initStorage: _initStorage, + getItem: getItem, + setItem: setItem, + removeItem: removeItem, + clear: clear, + length: length, + key: key, + keys: keys + }; + + if (typeof define === 'function' && define.amd) { + define('webSQLStorage', function() { + return webSQLStorage; + }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = webSQLStorage; + } else { + this.webSQLStorage = webSQLStorage; + } +}).call(this); +(function() { + 'use strict'; + + // Promises! + var Promise = (typeof module !== 'undefined' && module.exports) ? + require('promise') : this.Promise; + + // Avoid those magic constants! + var MODULE_TYPE_DEFINE = 1; + var MODULE_TYPE_EXPORT = 2; + var MODULE_TYPE_WINDOW = 3; + + // Attaching to window (i.e. no module loader) is the assumed, + // simple default. + var moduleType = MODULE_TYPE_WINDOW; + + // Find out what kind of module setup we have; if none, we'll just attach + // localForage to the main window. + if (typeof define === 'function' && define.amd) { + moduleType = MODULE_TYPE_DEFINE; + } else if (typeof module !== 'undefined' && module.exports) { + moduleType = MODULE_TYPE_EXPORT; + } + + // Initialize IndexedDB; fall back to vendor-prefixed versions if needed. + var indexedDB = indexedDB || this.indexedDB || this.webkitIndexedDB || + this.mozIndexedDB || this.OIndexedDB || + this.msIndexedDB; + + var supportsIndexedDB = indexedDB && + typeof indexedDB.open === 'function' && + indexedDB.open('_localforage_spec_test', 1) + .onupgradeneeded === null; + + // Check for WebSQL. + var openDatabase = this.openDatabase; + + // Check for localStorage. + var supportsLocalStorage = (function() { + try { + return localStorage && typeof localStorage.setItem === 'function'; + } catch (e) { + return false; + } + })(); + + // 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. + var _this = this; + var localForage = { + INDEXEDDB: 'asyncStorage', + LOCALSTORAGE: 'localStorageWrapper', + WEBSQL: 'webSQLStorage', + + _config: { + description: '', + 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 + }, + + // 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. + config: function(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) { + this._config[i] = options[i]; + } + + return true; + } else if (typeof(options) === 'string') { + return this._config[options]; + } else { + return this._config; + } + }, + + driver: function() { + return this._driver || null; + }, + + _ready: false, + + _driverSet: null, + + setDriver: function(driverName, callback, errorCallback) { + var self = this; + + this._driverSet = new Promise(function(resolve, reject) { + if ((!supportsIndexedDB && + driverName === localForage.INDEXEDDB) || + (!openDatabase && driverName === localForage.WEBSQL) || + (!supportsLocalStorage && + driverName === localForage.LOCALSTORAGE)) { + + if (errorCallback) { + errorCallback(); + } + + reject(localForage); + + return; + } + + self._ready = null; + + // We allow localForage to be declared as a module or as a + // library available without AMD/require.js. + if (moduleType === MODULE_TYPE_DEFINE) { + require([driverName], function(lib) { + self._extend(lib); + + if (callback) { + callback(); + } + resolve(); + }); + + return; + } else if (moduleType === MODULE_TYPE_EXPORT) { + // Making it browserify friendly + var driver; + switch (driverName) { + case self.INDEXEDDB: + driver = require('./drivers/indexeddb'); + break; + case self.LOCALSTORAGE: + driver = require('./drivers/localstorage'); + break; + case self.WEBSQL: + driver = require('./drivers/websql'); + } + + self._extend(driver); + } else { + self._extend(_this[driverName]); + } + + if (callback) { + callback(); + } + + resolve(); + }); + + return this._driverSet; + }, + + ready: function(callback) { + var ready = new Promise(function(resolve) { + localForage._driverSet.then(function() { + if (localForage._ready === null) { + localForage._ready = localForage._initStorage( + localForage._config); + } + + localForage._ready.then(resolve); + }); + }); + + ready.then(callback, callback); + + return ready; + }, + + _extend: function(libraryMethodsAndProperties) { + for (var i in libraryMethodsAndProperties) { + if (libraryMethodsAndProperties.hasOwnProperty(i)) { + this[i] = libraryMethodsAndProperties[i]; + } + } + } + }; + + // Select our storage library. + var storageLibrary; + // 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 + if (supportsIndexedDB) { + storageLibrary = localForage.INDEXEDDB; + } else if (openDatabase) { // WebSQL is available, so we'll use that. + storageLibrary = localForage.WEBSQL; + } else if (supportsLocalStorage) { // If nothing else is available, + // we try to use localStorage. + storageLibrary = localForage.LOCALSTORAGE; + } + + // If window.localForageConfig is set, use it for configuration. + if (this.localForageConfig) { + localForage.config = this.localForageConfig; + } + + // Set the (default) driver, or report the error. + if (storageLibrary) { + localForage.setDriver(storageLibrary); + } else { + localForage._ready = Promise.reject( + new Error('No available storage method found.')); + } + + // We allow localForage to be declared as a module or as a library + // available without AMD/require.js. + if (moduleType === MODULE_TYPE_DEFINE) { + define(function() { + return localForage; + }); + } else if (moduleType === MODULE_TYPE_EXPORT) { + module.exports = localForage; + } else { + this.localforage = localForage; + } +}).call(this); diff --git a/ajax/libs/localforage/0.9.1/localforage.nopromises.min.js b/ajax/libs/localforage/0.9.1/localforage.nopromises.min.js new file mode 100644 index 000000000..0d6168370 --- /dev/null +++ b/ajax/libs/localforage/0.9.1/localforage.nopromises.min.js @@ -0,0 +1 @@ +(function(){"use strict";function a(a){if(a)for(var b in a)l[b]=a[b];return new j(function(a,b){var c=m.open(l.name,l.version);c.onerror=function(){b(c.error)},c.onupgradeneeded=function(){c.result.createObjectStore(l.storeName)},c.onsuccess=function(){k=c.result,a()}})}function b(a,b){var c=this;return new j(function(d,e){c.ready().then(function(){var c=k.transaction(l.storeName,"readonly").objectStore(l.storeName),f=c.get(a);f.onsuccess=function(){var a=f.result;void 0===a&&(a=null),i(b,a),d(a)},f.onerror=function(){b&&b(null,f.error),e(f.error)}},function(a){e(a)})})}function c(a,b,c){var d=this;return new j(function(e,f){d.ready().then(function(){var d=k.transaction(l.storeName,"readwrite").objectStore(l.storeName);null===b&&(b=void 0);var g=d.put(b,a);g.onsuccess=function(){void 0===b&&(b=null),i(c,b),e(b)},g.onerror=function(){c&&c(null,g.error),f(g.error)}},function(a){f(a)})})}function d(a,b){var c=this;return new j(function(d,e){c.ready().then(function(){var c=k.transaction(l.storeName,"readwrite").objectStore(l.storeName),f=c["delete"](a);f.onsuccess=function(){i(b),d()},f.onerror=function(){b&&b(f.error),e(f.error)},f.onabort=function(a){var c=a.target.error;"QuotaExceededError"===c&&(b&&b(c),e(c))}},function(a){e(a)})})}function e(a){var b=this;return new j(function(c,d){b.ready().then(function(){var b=k.transaction(l.storeName,"readwrite").objectStore(l.storeName),e=b.clear();e.onsuccess=function(){i(a),c()},e.onerror=function(){a&&a(null,e.error),d(e.error)}},function(a){d(a)})})}function f(a){var b=this;return new j(function(c,d){b.ready().then(function(){var b=k.transaction(l.storeName,"readonly").objectStore(l.storeName),e=b.count();e.onsuccess=function(){a&&a(e.result),c(e.result)},e.onerror=function(){a&&a(null,e.error),d(e.error)}},function(a){d(a)})})}function g(a,b){var c=this;return new j(function(d,e){return 0>a?(b&&b(null),void d(null)):void c.ready().then(function(){var c=k.transaction(l.storeName,"readonly").objectStore(l.storeName),f=!1,g=c.openCursor();g.onsuccess=function(){var c=g.result;return c?void(0===a?(b&&b(c.key),d(c.key)):f?(b&&b(c.key),d(c.key)):(f=!0,c.advance(a))):(b&&b(null),void d(null))},g.onerror=function(){b&&b(null,g.error),e(g.error)}},function(a){e(a)})})}function h(a){var b=this;return new j(function(c,d){b.ready().then(function(){var b=k.transaction(l.storeName,"readonly").objectStore(l.storeName),e=b.openCursor(),f=[];e.onsuccess=function(){var b=e.result;return b?(f.push(b.key),void b.continue()):(a&&a(f),void c(f))},e.onerror=function(){a&&a(null,e.error),d(e.error)}},function(a){d(a)})})}function i(a,b){return a?setTimeout(function(){return a(b)},0):void 0}var j="undefined"!=typeof module&&module.exports?require("promise"):this.Promise,k=null,l={},m=m||this.indexedDB||this.webkitIndexedDB||this.mozIndexedDB||this.OIndexedDB||this.msIndexedDB;if(m){var n={_driver:"asyncStorage",_initStorage:a,getItem:b,setItem:c,removeItem:d,clear:e,length:f,key:g,keys:h};"function"==typeof define&&define.amd?define("asyncStorage",function(){return n}):"undefined"!=typeof module&&module.exports?module.exports=n:this.asyncStorage=n}}).call(this),function(){"use strict";function a(a){if(a)for(var b in a)m[b]=a[b];return l=m.name+"/",n.resolve()}function b(a){var b=this;return new n(function(c){b.ready().then(function(){o.clear(),a&&a(),c()})})}function c(a,b){var c=this;return new n(function(d,e){c.ready().then(function(){try{var c=o.getItem(l+a);c&&(c=h(c)),b&&b(c),d(c)}catch(f){b&&b(null,f),e(f)}})})}function d(a,b){var c=this;return new n(function(d){c.ready().then(function(){var c;try{c=o.key(a)}catch(e){c=null}c&&(c=c.substring(l.length)),b&&b(c),d(c)})})}function e(a){var b=this;return new n(function(c){b.ready().then(function(){for(var b=o.length,d=[],e=0;b>e;e++)d.push(o.key(e).substring(l.length));a&&a(d),c(d)})})}function f(a){var b=this;return new n(function(c){b.ready().then(function(){var b=o.length;a&&a(b),c(b)})})}function g(a,b){var c=this;return new n(function(d){c.ready().then(function(){o.removeItem(l+a),b&&b(),d()})})}function h(a){if(a.substring(0,r)!==q)return JSON.parse(a);for(var b=a.substring(D),c=a.substring(r,D),d=new ArrayBuffer(2*b.length),e=new Uint16Array(d),f=b.length-1;f>=0;f--)e[f]=b.charCodeAt(f);switch(c){case s:return d;case t:return new Blob([d]);case u:return new Int8Array(d);case v:return new Uint8Array(d);case w:return new Uint8ClampedArray(d);case x:return new Int16Array(d);case z:return new Uint16Array(d);case y:return new Int32Array(d);case A:return new Uint32Array(d);case B:return new Float32Array(d);case C:return new Float64Array(d);default:throw new Error("Unkown type: "+c)}}function i(a){var b="",c=new Uint16Array(a);try{b=String.fromCharCode.apply(null,c)}catch(d){for(var e=0;eg;g++)f.push(d.rows.item(g).key);a&&a(f),c(f)},function(b,c){a&&a(null,c),d(c)})})})})}function i(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=l[(3&c[b])<<4|c[b+1]>>4],d+=l[(15&c[b+1])<<2|c[b+2]>>6],d+=l[63&c[b+2]];return c.length%3===2?d=d.substring(0,d.length-1)+"=":c.length%3===1&&(d=d.substring(0,d.length-2)+"=="),d}function j(a){if(a.substring(0,r)!==q)return JSON.parse(a);var b,c,d,e,f,g=a.substring(D),h=a.substring(r,D),i=.75*g.length,j=g.length,k=0;"="===g[g.length-1]&&(i--,"="===g[g.length-2]&&i--);var m=new ArrayBuffer(i),n=new Uint8Array(m);for(b=0;j>b;b+=4)c=l.indexOf(g[b]),d=l.indexOf(g[b+1]),e=l.indexOf(g[b+2]),f=l.indexOf(g[b+3]),n[k++]=c<<2|d>>4,n[k++]=(15&d)<<4|e>>2,n[k++]=(3&e)<<6|63&f;switch(h){case s:return m;case t:return new Blob([m]);case u:return new Int8Array(m);case v:return new Uint8Array(m);case w:return new Uint8ClampedArray(m);case x:return new Int16Array(m);case z:return new Uint16Array(m);case y:return new Int32Array(m);case A:return new Uint32Array(m);case B:return new Float32Array(m);case C:return new Float64Array(m);default:throw new Error("Unkown type: "+h)}}function k(a,b){var c="";if(a&&(c=a.toString()),a&&("[object ArrayBuffer]"===a.toString()||a.buffer&&"[object ArrayBuffer]"===a.buffer.toString())){var d,e=q;a instanceof ArrayBuffer?(d=a,e+=s):(d=a.buffer,"[object Int8Array]"===c?e+=u:"[object Uint8Array]"===c?e+=v:"[object Uint8ClampedArray]"===c?e+=w:"[object Int16Array]"===c?e+=x:"[object Uint16Array]"===c?e+=z:"[object Int32Array]"===c?e+=y:"[object Uint32Array]"===c?e+=A:"[object Float32Array]"===c?e+=B:"[object Float64Array]"===c?e+=C:b(new Error("Failed to get type for BinaryArray"))),b(e+i(d))}else if("[object Blob]"===c){var f=new FileReader;f.onload=function(){var a=i(this.result);b(q+t+a)},f.readAsArrayBuffer(a)}else try{b(JSON.stringify(a))}catch(g){this.console&&this.console.error&&this.console.error("Couldn't convert value into a JSON string: ",a),b(null,g)}}var l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",m="undefined"!=typeof module&&module.exports?require("promise"):this.Promise,n=this.openDatabase,o=null,p={},q="__lfsc__:",r=q.length,s="arbf",t="blob",u="si08",v="ui08",w="uic8",x="si16",y="si32",z="ur16",A="ui32",B="fl32",C="fl64",D=r+s.length;if(n){var E={_driver:"webSQLStorage",_initStorage:a,getItem:b,setItem:c,removeItem:d,clear:e,length:f,key:g,keys:h};"function"==typeof define&&define.amd?define("webSQLStorage",function(){return E}):"undefined"!=typeof module&&module.exports?module.exports=E:this.webSQLStorage=E}}.call(this),function(){"use strict";var a="undefined"!=typeof module&&module.exports?require("promise"):this.Promise,b=1,c=2,d=3,e=d;"function"==typeof define&&define.amd?e=b:"undefined"!=typeof module&&module.exports&&(e=c);var f,g=g||this.indexedDB||this.webkitIndexedDB||this.mozIndexedDB||this.OIndexedDB||this.msIndexedDB,h=g&&"function"==typeof g.open&&null===g.open("_localforage_spec_test",1).onupgradeneeded,i=this.openDatabase,j=function(){try{return localStorage&&"function"==typeof localStorage.setItem}catch(a){return!1}}(),k=this,l={INDEXEDDB:"asyncStorage",LOCALSTORAGE:"localStorageWrapper",WEBSQL:"webSQLStorage",_config:{description:"",name:"localforage",size:4980736,storeName:"keyvaluepairs",version:1},config:function(a){if("object"==typeof a){if(this._ready)return new Error("Can't call config() after localforage has been used.");for(var b in a)this._config[b]=a[b];return!0}return"string"==typeof a?this._config[a]:this._config},driver:function(){return this._driver||null},_ready:!1,_driverSet:null,setDriver:function(d,f,g){var m=this;return this._driverSet=new a(function(a,n){if(!h&&d===l.INDEXEDDB||!i&&d===l.WEBSQL||!j&&d===l.LOCALSTORAGE)return g&&g(),void n(l);if(m._ready=null,e===b)return void require([d],function(b){m._extend(b),f&&f(),a()});if(e===c){var o;switch(d){case m.INDEXEDDB:o=require("./drivers/indexeddb");break;case m.LOCALSTORAGE:o=require("./drivers/localstorage");break;case m.WEBSQL:o=require("./drivers/websql")}m._extend(o)}else m._extend(k[d]);f&&f(),a()}),this._driverSet},ready:function(b){var c=new a(function(a){l._driverSet.then(function(){null===l._ready&&(l._ready=l._initStorage(l._config)),l._ready.then(a)})});return c.then(b,b),c},_extend:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b])}};h?f=l.INDEXEDDB:i?f=l.WEBSQL:j&&(f=l.LOCALSTORAGE),this.localForageConfig&&(l.config=this.localForageConfig),f?l.setDriver(f):l._ready=a.reject(new Error("No available storage method found.")),e===b?define(function(){return l}):e===c?module.exports=l:this.localforage=l}.call(this); \ No newline at end of file diff --git a/ajax/libs/localforage/package.json b/ajax/libs/localforage/package.json new file mode 100644 index 000000000..b7d3f5746 --- /dev/null +++ b/ajax/libs/localforage/package.json @@ -0,0 +1,22 @@ +{ + "name": "localforage", + "author": "Mozilla", + "description": "Offline storage, improved.", + "keywords": [ + "indexeddb", + "localstorage", + "storage", + "websql" + ], + "version": "0.9.1", + "homepage": "https://github.com/mozilla/localForage", + "repository": { + "type": "git", + "url": "git://github.com/mozilla/localForage.git" + }, + "filename": "localforage.min.js", + "licence": "Apache-2.0", + "bugs": { + "url": "http://github.com/mozilla/localForage/issues" + } +}