|
|
/*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); } };
|