/*global Blob,File*/
|
|
|
|
/**
|
|
* Module requirements
|
|
*/
|
|
|
|
var isArray = require('isarray');
|
|
var isBuf = require('./is-buffer');
|
|
|
|
/**
|
|
* Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder.
|
|
* Anything with blobs or files should be fed through removeBlobs before coming
|
|
* here.
|
|
*
|
|
* @param {Object} packet - socket.io event packet
|
|
* @return {Object} with deconstructed packet and list of buffers
|
|
* @api public
|
|
*/
|
|
|
|
exports.deconstructPacket = function(packet){
|
|
var buffers = [];
|
|
var packetData = packet.data;
|
|
|
|
function _deconstructPacket(data) {
|
|
if (!data) return data;
|
|
|
|
if (isBuf(data)) {
|
|
var placeholder = { _placeholder: true, num: buffers.length };
|
|
buffers.push(data);
|
|
return placeholder;
|
|
} else if (isArray(data)) {
|
|
var newData = new Array(data.length);
|
|
for (var i = 0; i < data.length; i++) {
|
|
newData[i] = _deconstructPacket(data[i]);
|
|
}
|
|
return newData;
|
|
} else if ('object' == typeof data && !(data instanceof Date)) {
|
|
var newData = {};
|
|
for (var key in data) {
|
|
newData[key] = _deconstructPacket(data[key]);
|
|
}
|
|
return newData;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
var pack = packet;
|
|
pack.data = _deconstructPacket(packetData);
|
|
pack.attachments = buffers.length; // number of binary 'attachments'
|
|
return {packet: pack, buffers: buffers};
|
|
};
|
|
|
|
/**
|
|
* Reconstructs a binary packet from its placeholder packet and buffers
|
|
*
|
|
* @param {Object} packet - event packet with placeholders
|
|
* @param {Array} buffers - binary buffers to put in placeholder positions
|
|
* @return {Object} reconstructed packet
|
|
* @api public
|
|
*/
|
|
|
|
exports.reconstructPacket = function(packet, buffers) {
|
|
var curPlaceHolder = 0;
|
|
|
|
function _reconstructPacket(data) {
|
|
if (data && data._placeholder) {
|
|
var buf = buffers[data.num]; // appropriate buffer (should be natural order anyway)
|
|
return buf;
|
|
} else if (isArray(data)) {
|
|
for (var i = 0; i < data.length; i++) {
|
|
data[i] = _reconstructPacket(data[i]);
|
|
}
|
|
return data;
|
|
} else if (data && 'object' == typeof data) {
|
|
for (var key in data) {
|
|
data[key] = _reconstructPacket(data[key]);
|
|
}
|
|
return data;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
packet.data = _reconstructPacket(packet.data);
|
|
packet.attachments = undefined; // no longer useful
|
|
return packet;
|
|
};
|
|
|
|
/**
|
|
* Asynchronously removes Blobs or Files from data via
|
|
* FileReader's readAsArrayBuffer method. Used before encoding
|
|
* data as msgpack. Calls callback with the blobless data.
|
|
*
|
|
* @param {Object} data
|
|
* @param {Function} callback
|
|
* @api private
|
|
*/
|
|
|
|
exports.removeBlobs = function(data, callback) {
|
|
function _removeBlobs(obj, curKey, containingObject) {
|
|
if (!obj) return obj;
|
|
|
|
// convert any blob
|
|
if ((global.Blob && obj instanceof Blob) ||
|
|
(global.File && obj instanceof File)) {
|
|
pendingBlobs++;
|
|
|
|
// async filereader
|
|
var fileReader = new FileReader();
|
|
fileReader.onload = function() { // this.result == arraybuffer
|
|
if (containingObject) {
|
|
containingObject[curKey] = this.result;
|
|
}
|
|
else {
|
|
bloblessData = this.result;
|
|
}
|
|
|
|
// if nothing pending its callback time
|
|
if(! --pendingBlobs) {
|
|
callback(bloblessData);
|
|
}
|
|
};
|
|
|
|
fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer
|
|
} else if (isArray(obj)) { // handle array
|
|
for (var i = 0; i < obj.length; i++) {
|
|
_removeBlobs(obj[i], i, obj);
|
|
}
|
|
} else if (obj && 'object' == typeof obj && !isBuf(obj)) { // and object
|
|
for (var key in obj) {
|
|
_removeBlobs(obj[key], key, obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
var pendingBlobs = 0;
|
|
var bloblessData = data;
|
|
_removeBlobs(bloblessData);
|
|
if (!pendingBlobs) {
|
|
callback(bloblessData);
|
|
}
|
|
};
|