"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var createBlob_1 = require("./createBlob"); var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/; var SERIALIZED_MARKER_LENGTH = "__lfsc__:" /* SERIALIZED_MARKER */.length; var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + "arbf" /* TYPE_ARRAYBUFFER */.length; //tslint:disable:no-magic-numbers no-bitwise prefer-switch no-unbound-method var toString = Object.prototype.toString; function stringToBuffer(serializedString) { // Fill the string into a ArrayBuffer. var bufferLength = serializedString.length * 0.75; var len = serializedString.length; if (serializedString[serializedString.length - 1] === '=') { bufferLength--; if (serializedString[serializedString.length - 2] === '=') { bufferLength--; } } var buffer = new ArrayBuffer(bufferLength); var bytes = new Uint8Array(buffer); for (var i = 0, p = 0; i < len; i += 4) { var encoded1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" /* BASE_CHARS */.indexOf(serializedString[i]); var encoded2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" /* BASE_CHARS */.indexOf(serializedString[i + 1]); var encoded3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" /* BASE_CHARS */.indexOf(serializedString[i + 2]); var encoded4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" /* BASE_CHARS */.indexOf(serializedString[i + 3]); bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); } return buffer; } exports.stringToBuffer = stringToBuffer; /** * 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 base64String = ''; for (var i = 0; i < bytes.length; i += 3) { /*jslint bitwise: true */ base64String += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" /* BASE_CHARS */[bytes[i] >> 2]; base64String += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" /* BASE_CHARS */[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; base64String += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" /* BASE_CHARS */[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; base64String += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" /* 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; } exports.bufferToString = bufferToString; /** * 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. * @param value * @param callback */ function serialize(value, callback) { var valueType = ''; if (value) { valueType = toString.call(value); } // Cannot use `value instanceof ArrayBuffer` or such here, as these // checks fail when running the tests using casper.js... if (value && (valueType === '[object ArrayBuffer]' || (value.buffer && toString.call(value.buffer) === '[object ArrayBuffer]'))) { // Convert binary arrays to a string and prefix the string with // a special marker. var buffer = void 0; var marker = "__lfsc__:" /* SERIALIZED_MARKER */; if (value instanceof ArrayBuffer) { buffer = value; marker += "arbf" /* TYPE_ARRAYBUFFER */; } else { buffer = value.buffer; if (valueType === '[object Int8Array]') { marker += "si08" /* TYPE_INT8ARRAY */; } else if (valueType === '[object Uint8Array]') { marker += "ui08" /* TYPE_UINT8ARRAY */; } else if (valueType === '[object Uint8ClampedArray]') { marker += "uic8" /* TYPE_UINT8CLAMPEDARRAY */; } else if (valueType === '[object Int16Array]') { marker += "si16" /* TYPE_INT16ARRAY */; } else if (valueType === '[object Uint16Array]') { marker += "ur16" /* TYPE_UINT16ARRAY */; } else if (valueType === '[object Int32Array]') { marker += "si32" /* TYPE_INT32ARRAY */; } else if (valueType === '[object Uint32Array]') { marker += "ui32" /* TYPE_UINT32ARRAY */; } else if (valueType === '[object Float32Array]') { marker += "fl32" /* TYPE_FLOAT32ARRAY */; } else if (valueType === '[object Float64Array]') { marker += "fl64" /* TYPE_FLOAT64ARRAY */; } else { callback(new Error('Failed to get type for BinaryArray')); } } callback(marker + bufferToString(buffer)); } else if (valueType === '[object Blob]') { // Convert the blob to a binaryArray and then to a string. var fileReader = new FileReader(); fileReader.onload = function () { // Backwards-compatible prefix for the blob type. //tslint:disable-next-line:restrict-plus-operands var str = "~~local_forage_type~" /* BLOB_TYPE_PREFIX */ + value.type + "~" + bufferToString(this.result); callback("__lfsc__:" /* SERIALIZED_MARKER */ + "blob" /* TYPE_BLOB */ + str); }; fileReader.readAsArrayBuffer(value); } else { try { callback(JSON.stringify(value)); } catch (e) { console.error('Couldn\'t convert value into a JSON string: ', value); callback(null, e); } } } exports.serialize = serialize; /** * 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. * @param value */ 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) !== "__lfsc__:" /* 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); var blobType; // Backwards-compatible blob type serialization strategy. // DBs created with older versions of localForage will simply not have the blob type. if (type === "blob" /* TYPE_BLOB */ && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) { var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX); blobType = matcher[1]; serializedString = serializedString.substring(matcher[0].length); } var buffer = stringToBuffer(serializedString); // Return the right type based on the code/type set during // serialization. switch (type) { case "arbf" /* TYPE_ARRAYBUFFER */: return buffer; case "blob" /* TYPE_BLOB */: return createBlob_1.createBlob([buffer], { type: blobType }); case "si08" /* TYPE_INT8ARRAY */: return new Int8Array(buffer); case "ui08" /* TYPE_UINT8ARRAY */: return new Uint8Array(buffer); case "uic8" /* TYPE_UINT8CLAMPEDARRAY */: return new Uint8ClampedArray(buffer); case "si16" /* TYPE_INT16ARRAY */: return new Int16Array(buffer); case "ur16" /* TYPE_UINT16ARRAY */: return new Uint16Array(buffer); case "si32" /* TYPE_INT32ARRAY */: return new Int32Array(buffer); case "ui32" /* TYPE_UINT32ARRAY */: return new Uint32Array(buffer); case "fl32" /* TYPE_FLOAT32ARRAY */: return new Float32Array(buffer); case "fl64" /* TYPE_FLOAT64ARRAY */: return new Float64Array(buffer); default: throw new Error('Unkown type: ' + type); } } exports.deserialize = deserialize; //# sourceMappingURL=index.js.map