7 var isArray = require('isarray');
8 var isBuf = require('./is-buffer');
9 var toString = Object.prototype.toString;
10 var withNativeBlob = typeof Blob === 'function' || (typeof Blob !== 'undefined' && toString.call(Blob) === '[object BlobConstructor]');
11 var withNativeFile = typeof File === 'function' || (typeof File !== 'undefined' && toString.call(File) === '[object FileConstructor]');
14 * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder.
15 * Anything with blobs or files should be fed through removeBlobs before coming
18 * @param {Object} packet - socket.io event packet
19 * @return {Object} with deconstructed packet and list of buffers
23 exports.deconstructPacket = function(packet) {
25 var packetData = packet.data;
27 pack.data = _deconstructPacket(packetData, buffers);
28 pack.attachments = buffers.length; // number of binary 'attachments'
29 return {packet: pack, buffers: buffers};
32 function _deconstructPacket(data, buffers) {
33 if (!data) return data;
36 var placeholder = { _placeholder: true, num: buffers.length };
39 } else if (isArray(data)) {
40 var newData = new Array(data.length);
41 for (var i = 0; i < data.length; i++) {
42 newData[i] = _deconstructPacket(data[i], buffers);
45 } else if (typeof data === 'object' && !(data instanceof Date)) {
47 for (var key in data) {
48 newData[key] = _deconstructPacket(data[key], buffers);
56 * Reconstructs a binary packet from its placeholder packet and buffers
58 * @param {Object} packet - event packet with placeholders
59 * @param {Array} buffers - binary buffers to put in placeholder positions
60 * @return {Object} reconstructed packet
64 exports.reconstructPacket = function(packet, buffers) {
65 packet.data = _reconstructPacket(packet.data, buffers);
66 packet.attachments = undefined; // no longer useful
70 function _reconstructPacket(data, buffers) {
71 if (!data) return data;
73 if (data && data._placeholder) {
74 return buffers[data.num]; // appropriate buffer (should be natural order anyway)
75 } else if (isArray(data)) {
76 for (var i = 0; i < data.length; i++) {
77 data[i] = _reconstructPacket(data[i], buffers);
79 } else if (typeof data === 'object') {
80 for (var key in data) {
81 data[key] = _reconstructPacket(data[key], buffers);
89 * Asynchronously removes Blobs or Files from data via
90 * FileReader's readAsArrayBuffer method. Used before encoding
91 * data as msgpack. Calls callback with the blobless data.
93 * @param {Object} data
94 * @param {Function} callback
98 exports.removeBlobs = function(data, callback) {
99 function _removeBlobs(obj, curKey, containingObject) {
100 if (!obj) return obj;
103 if ((withNativeBlob && obj instanceof Blob) ||
104 (withNativeFile && obj instanceof File)) {
108 var fileReader = new FileReader();
109 fileReader.onload = function() { // this.result == arraybuffer
110 if (containingObject) {
111 containingObject[curKey] = this.result;
114 bloblessData = this.result;
117 // if nothing pending its callback time
118 if(! --pendingBlobs) {
119 callback(bloblessData);
123 fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer
124 } else if (isArray(obj)) { // handle array
125 for (var i = 0; i < obj.length; i++) {
126 _removeBlobs(obj[i], i, obj);
128 } else if (typeof obj === 'object' && !isBuf(obj)) { // and object
129 for (var key in obj) {
130 _removeBlobs(obj[key], key, obj);
135 var pendingBlobs = 0;
136 var bloblessData = data;
137 _removeBlobs(bloblessData);
139 callback(bloblessData);