mirror of
https://github.com/localForage/localForage.git
synced 2026-01-18 14:31:57 +00:00
469 lines
14 KiB
JavaScript
469 lines
14 KiB
JavaScript
(function() {
|
|
'use strict';
|
|
|
|
var dummyStorage = {};
|
|
|
|
// Config the localStorage backend, using options set in the config.
|
|
function _initStorage(options) {
|
|
var self = this;
|
|
|
|
var dbInfo = {};
|
|
if (options) {
|
|
for (var i in options) {
|
|
dbInfo[i] = options[i];
|
|
}
|
|
}
|
|
|
|
dummyStorage[dbInfo.name] = dbInfo.db = {};
|
|
|
|
self._dbInfo = dbInfo;
|
|
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;
|
|
|
|
function clear(callback) {
|
|
var self = this;
|
|
var promise = new Promise(function(resolve, reject) {
|
|
self
|
|
.ready()
|
|
.then(function() {
|
|
var db = self._dbInfo.db;
|
|
|
|
for (var key in db) {
|
|
if (db.hasOwnProperty(key)) {
|
|
delete db[key];
|
|
// db[key] = undefined;
|
|
}
|
|
}
|
|
|
|
resolve();
|
|
})
|
|
.catch(reject);
|
|
});
|
|
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
|
|
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() {
|
|
try {
|
|
var db = self._dbInfo.db;
|
|
var result = db[key];
|
|
|
|
if (result) {
|
|
result = _deserialize(result);
|
|
}
|
|
|
|
resolve(result);
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
})
|
|
.catch(reject);
|
|
});
|
|
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
|
|
function iterate(callback) {
|
|
var self = this;
|
|
|
|
var promise = new Promise(function(resolve, reject) {
|
|
self
|
|
.ready()
|
|
.then(function() {
|
|
try {
|
|
var db = self._dbInfo.db;
|
|
|
|
for (var key in db) {
|
|
var result = db[key];
|
|
|
|
if (result) {
|
|
result = _deserialize(result);
|
|
}
|
|
|
|
callback(result, key);
|
|
}
|
|
|
|
resolve();
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
})
|
|
.catch(reject);
|
|
});
|
|
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
|
|
function key(n, callback) {
|
|
var self = this;
|
|
var promise = new Promise(function(resolve, reject) {
|
|
self
|
|
.ready()
|
|
.then(function() {
|
|
var db = self._dbInfo.db;
|
|
var result = null;
|
|
var index = 0;
|
|
|
|
for (var key in db) {
|
|
if (db.hasOwnProperty(key) && db[key] !== undefined) {
|
|
if (n === index) {
|
|
result = key;
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
|
|
resolve(result);
|
|
})
|
|
.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 db = self._dbInfo.db;
|
|
var keys = [];
|
|
|
|
for (var key in db) {
|
|
if (db.hasOwnProperty(key)) {
|
|
keys.push(key);
|
|
}
|
|
}
|
|
|
|
resolve(keys);
|
|
})
|
|
.catch(reject);
|
|
});
|
|
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
|
|
function length(callback) {
|
|
var self = this;
|
|
var promise = new Promise(function(resolve, reject) {
|
|
self
|
|
.keys()
|
|
.then(function(keys) {
|
|
resolve(keys.length);
|
|
})
|
|
.catch(reject);
|
|
});
|
|
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
|
|
function removeItem(key, callback) {
|
|
var self = this;
|
|
|
|
// Cast the key to a string, as that's all we can set as a key.
|
|
if (typeof key !== 'string') {
|
|
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 db = self._dbInfo.db;
|
|
if (db.hasOwnProperty(key)) {
|
|
delete db[key];
|
|
// db[key] = undefined;
|
|
}
|
|
|
|
resolve();
|
|
})
|
|
.catch(reject);
|
|
});
|
|
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
|
|
function setItem(key, value, callback) {
|
|
var self = this;
|
|
|
|
// Cast the key to a string, as that's all we can set as a key.
|
|
if (typeof key !== 'string') {
|
|
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() {
|
|
// 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) {
|
|
reject(error);
|
|
} else {
|
|
try {
|
|
var db = self._dbInfo.db;
|
|
db[key] = value;
|
|
resolve(originalValue);
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}
|
|
});
|
|
})
|
|
.catch(reject);
|
|
});
|
|
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
|
|
// _serialize just like in 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) {
|
|
window.console.error(
|
|
"Couldn't convert value into a JSON " + 'string: ',
|
|
value
|
|
);
|
|
|
|
callback(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
// _deserialize just like in LocalStorage
|
|
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);
|
|
}
|
|
}
|
|
|
|
// _bufferToString just like in LocalStorage
|
|
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;
|
|
}
|
|
|
|
function executeCallback(promise, callback) {
|
|
if (callback) {
|
|
promise.then(
|
|
function(result) {
|
|
callback(null, result);
|
|
},
|
|
function(error) {
|
|
callback(error);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
var dummyStorageDriver = {
|
|
_driver: 'dummyStorageDriver',
|
|
_initStorage: _initStorage,
|
|
// _supports: function() { return true; }
|
|
iterate: iterate,
|
|
getItem: getItem,
|
|
setItem: setItem,
|
|
removeItem: removeItem,
|
|
clear: clear,
|
|
length: length,
|
|
key: key,
|
|
keys: keys
|
|
};
|
|
|
|
if (typeof define === 'function' && define.amd) {
|
|
define('dummyStorageDriver', function() {
|
|
return dummyStorageDriver;
|
|
});
|
|
} else if (
|
|
typeof module !== 'undefined' &&
|
|
module.exports &&
|
|
typeof require !== 'undefined'
|
|
) {
|
|
module.exports = dummyStorageDriver;
|
|
} else {
|
|
this.dummyStorageDriver = dummyStorageDriver;
|
|
}
|
|
}.call(window));
|