localForage/test/dummyStorageDriver.js
2017-12-18 08:42:12 +02:00

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));