twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

binary.js (4090B)


      1 /*global Blob,File*/
      2 
      3 /**
      4  * Module requirements
      5  */
      6 
      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]');
     12 
     13 /**
     14  * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder.
     15  * Anything with blobs or files should be fed through removeBlobs before coming
     16  * here.
     17  *
     18  * @param {Object} packet - socket.io event packet
     19  * @return {Object} with deconstructed packet and list of buffers
     20  * @api public
     21  */
     22 
     23 exports.deconstructPacket = function(packet) {
     24   var buffers = [];
     25   var packetData = packet.data;
     26   var pack = packet;
     27   pack.data = _deconstructPacket(packetData, buffers);
     28   pack.attachments = buffers.length; // number of binary 'attachments'
     29   return {packet: pack, buffers: buffers};
     30 };
     31 
     32 function _deconstructPacket(data, buffers) {
     33   if (!data) return data;
     34 
     35   if (isBuf(data)) {
     36     var placeholder = { _placeholder: true, num: buffers.length };
     37     buffers.push(data);
     38     return placeholder;
     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);
     43     }
     44     return newData;
     45   } else if (typeof data === 'object' && !(data instanceof Date)) {
     46     var newData = {};
     47     for (var key in data) {
     48       newData[key] = _deconstructPacket(data[key], buffers);
     49     }
     50     return newData;
     51   }
     52   return data;
     53 }
     54 
     55 /**
     56  * Reconstructs a binary packet from its placeholder packet and buffers
     57  *
     58  * @param {Object} packet - event packet with placeholders
     59  * @param {Array} buffers - binary buffers to put in placeholder positions
     60  * @return {Object} reconstructed packet
     61  * @api public
     62  */
     63 
     64 exports.reconstructPacket = function(packet, buffers) {
     65   packet.data = _reconstructPacket(packet.data, buffers);
     66   packet.attachments = undefined; // no longer useful
     67   return packet;
     68 };
     69 
     70 function _reconstructPacket(data, buffers) {
     71   if (!data) return data;
     72 
     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);
     78     }
     79   } else if (typeof data === 'object') {
     80     for (var key in data) {
     81       data[key] = _reconstructPacket(data[key], buffers);
     82     }
     83   }
     84 
     85   return data;
     86 }
     87 
     88 /**
     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.
     92  *
     93  * @param {Object} data
     94  * @param {Function} callback
     95  * @api private
     96  */
     97 
     98 exports.removeBlobs = function(data, callback) {
     99   function _removeBlobs(obj, curKey, containingObject) {
    100     if (!obj) return obj;
    101 
    102     // convert any blob
    103     if ((withNativeBlob && obj instanceof Blob) ||
    104         (withNativeFile && obj instanceof File)) {
    105       pendingBlobs++;
    106 
    107       // async filereader
    108       var fileReader = new FileReader();
    109       fileReader.onload = function() { // this.result == arraybuffer
    110         if (containingObject) {
    111           containingObject[curKey] = this.result;
    112         }
    113         else {
    114           bloblessData = this.result;
    115         }
    116 
    117         // if nothing pending its callback time
    118         if(! --pendingBlobs) {
    119           callback(bloblessData);
    120         }
    121       };
    122 
    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);
    127       }
    128     } else if (typeof obj === 'object' && !isBuf(obj)) { // and object
    129       for (var key in obj) {
    130         _removeBlobs(obj[key], key, obj);
    131       }
    132     }
    133   }
    134 
    135   var pendingBlobs = 0;
    136   var bloblessData = data;
    137   _removeBlobs(bloblessData);
    138   if (!pendingBlobs) {
    139     callback(bloblessData);
    140   }
    141 };