Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
1559 lines
39 KiB
JavaScript
Executable file
1559 lines
39 KiB
JavaScript
Executable file
/*! binary.js build:0.2.1, development. Copyright(c) 2012 Eric Zhang <eric@ericzhang.com> MIT Licensed */
|
|
(function(exports){
|
|
var binaryFeatures = {};
|
|
binaryFeatures.useBlobBuilder = (function(){
|
|
try {
|
|
new Blob([]);
|
|
return false;
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
})();
|
|
|
|
binaryFeatures.useArrayBufferView = !binaryFeatures.useBlobBuilder && (function(){
|
|
try {
|
|
return (new Blob([new Uint8Array([])])).size === 0;
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
})();
|
|
binaryFeatures.supportsBinaryWebsockets = (function(){
|
|
try {
|
|
var wstest = new WebSocket('ws://null');
|
|
wstest.onerror = function(){};
|
|
if (typeof(wstest.binaryType) !== "undefined") {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
wstest.close();
|
|
wstest = null;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
})();
|
|
|
|
exports.binaryFeatures = binaryFeatures;
|
|
exports.BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder || window.BlobBuilder;
|
|
|
|
function BufferBuilder(){
|
|
this._pieces = [];
|
|
this._parts = [];
|
|
}
|
|
|
|
BufferBuilder.prototype.append = function(data) {
|
|
if(typeof data === 'number') {
|
|
this._pieces.push(data);
|
|
} else {
|
|
this.flush();
|
|
this._parts.push(data);
|
|
}
|
|
};
|
|
|
|
BufferBuilder.prototype.flush = function() {
|
|
if (this._pieces.length > 0) {
|
|
var buf = new Uint8Array(this._pieces);
|
|
if(!binaryFeatures.useArrayBufferView) {
|
|
buf = buf.buffer;
|
|
}
|
|
this._parts.push(buf);
|
|
this._pieces = [];
|
|
}
|
|
};
|
|
|
|
BufferBuilder.prototype.getBuffer = function() {
|
|
this.flush();
|
|
if(binaryFeatures.useBlobBuilder) {
|
|
var builder = new BlobBuilder();
|
|
for(var i = 0, ii = this._parts.length; i < ii; i++) {
|
|
builder.append(this._parts[i]);
|
|
}
|
|
return builder.getBlob();
|
|
} else {
|
|
return new Blob(this._parts);
|
|
}
|
|
};
|
|
exports.BinaryPack = {
|
|
unpack: function(data){
|
|
var unpacker = new Unpacker(data);
|
|
return unpacker.unpack();
|
|
},
|
|
pack: function(data){
|
|
var packer = new Packer();
|
|
packer.pack(data);
|
|
var buffer = packer.getBuffer();
|
|
return buffer;
|
|
}
|
|
};
|
|
|
|
function Unpacker (data){
|
|
// Data is ArrayBuffer
|
|
this.index = 0;
|
|
this.dataBuffer = data;
|
|
this.dataView = new Uint8Array(this.dataBuffer);
|
|
this.length = this.dataBuffer.byteLength;
|
|
}
|
|
|
|
|
|
Unpacker.prototype.unpack = function(){
|
|
var type = this.unpack_uint8();
|
|
if (type < 0x80){
|
|
var positive_fixnum = type;
|
|
return positive_fixnum;
|
|
} else if ((type ^ 0xe0) < 0x20){
|
|
var negative_fixnum = (type ^ 0xe0) - 0x20;
|
|
return negative_fixnum;
|
|
}
|
|
var size;
|
|
if ((size = type ^ 0xa0) <= 0x0f){
|
|
return this.unpack_raw(size);
|
|
} else if ((size = type ^ 0xb0) <= 0x0f){
|
|
return this.unpack_string(size);
|
|
} else if ((size = type ^ 0x90) <= 0x0f){
|
|
return this.unpack_array(size);
|
|
} else if ((size = type ^ 0x80) <= 0x0f){
|
|
return this.unpack_map(size);
|
|
}
|
|
switch(type){
|
|
case 0xc0:
|
|
return null;
|
|
case 0xc1:
|
|
return undefined;
|
|
case 0xc2:
|
|
return false;
|
|
case 0xc3:
|
|
return true;
|
|
case 0xca:
|
|
return this.unpack_float();
|
|
case 0xcb:
|
|
return this.unpack_double();
|
|
case 0xcc:
|
|
return this.unpack_uint8();
|
|
case 0xcd:
|
|
return this.unpack_uint16();
|
|
case 0xce:
|
|
return this.unpack_uint32();
|
|
case 0xcf:
|
|
return this.unpack_uint64();
|
|
case 0xd0:
|
|
return this.unpack_int8();
|
|
case 0xd1:
|
|
return this.unpack_int16();
|
|
case 0xd2:
|
|
return this.unpack_int32();
|
|
case 0xd3:
|
|
return this.unpack_int64();
|
|
case 0xd4:
|
|
return undefined;
|
|
case 0xd5:
|
|
return undefined;
|
|
case 0xd6:
|
|
return undefined;
|
|
case 0xd7:
|
|
return undefined;
|
|
case 0xd8:
|
|
size = this.unpack_uint16();
|
|
return this.unpack_string(size);
|
|
case 0xd9:
|
|
size = this.unpack_uint32();
|
|
return this.unpack_string(size);
|
|
case 0xda:
|
|
size = this.unpack_uint16();
|
|
return this.unpack_raw(size);
|
|
case 0xdb:
|
|
size = this.unpack_uint32();
|
|
return this.unpack_raw(size);
|
|
case 0xdc:
|
|
size = this.unpack_uint16();
|
|
return this.unpack_array(size);
|
|
case 0xdd:
|
|
size = this.unpack_uint32();
|
|
return this.unpack_array(size);
|
|
case 0xde:
|
|
size = this.unpack_uint16();
|
|
return this.unpack_map(size);
|
|
case 0xdf:
|
|
size = this.unpack_uint32();
|
|
return this.unpack_map(size);
|
|
}
|
|
}
|
|
|
|
Unpacker.prototype.unpack_uint8 = function(){
|
|
var byte = this.dataView[this.index] & 0xff;
|
|
this.index++;
|
|
return byte;
|
|
};
|
|
|
|
Unpacker.prototype.unpack_uint16 = function(){
|
|
var bytes = this.read(2);
|
|
var uint16 =
|
|
((bytes[0] & 0xff) * 256) + (bytes[1] & 0xff);
|
|
this.index += 2;
|
|
return uint16;
|
|
}
|
|
|
|
Unpacker.prototype.unpack_uint32 = function(){
|
|
var bytes = this.read(4);
|
|
var uint32 =
|
|
((bytes[0] * 256 +
|
|
bytes[1]) * 256 +
|
|
bytes[2]) * 256 +
|
|
bytes[3];
|
|
this.index += 4;
|
|
return uint32;
|
|
}
|
|
|
|
Unpacker.prototype.unpack_uint64 = function(){
|
|
var bytes = this.read(8);
|
|
var uint64 =
|
|
((((((bytes[0] * 256 +
|
|
bytes[1]) * 256 +
|
|
bytes[2]) * 256 +
|
|
bytes[3]) * 256 +
|
|
bytes[4]) * 256 +
|
|
bytes[5]) * 256 +
|
|
bytes[6]) * 256 +
|
|
bytes[7];
|
|
this.index += 8;
|
|
return uint64;
|
|
}
|
|
|
|
|
|
Unpacker.prototype.unpack_int8 = function(){
|
|
var uint8 = this.unpack_uint8();
|
|
return (uint8 < 0x80 ) ? uint8 : uint8 - (1 << 8);
|
|
};
|
|
|
|
Unpacker.prototype.unpack_int16 = function(){
|
|
var uint16 = this.unpack_uint16();
|
|
return (uint16 < 0x8000 ) ? uint16 : uint16 - (1 << 16);
|
|
}
|
|
|
|
Unpacker.prototype.unpack_int32 = function(){
|
|
var uint32 = this.unpack_uint32();
|
|
return (uint32 < Math.pow(2, 31) ) ? uint32 :
|
|
uint32 - Math.pow(2, 32);
|
|
}
|
|
|
|
Unpacker.prototype.unpack_int64 = function(){
|
|
var uint64 = this.unpack_uint64();
|
|
return (uint64 < Math.pow(2, 63) ) ? uint64 :
|
|
uint64 - Math.pow(2, 64);
|
|
}
|
|
|
|
Unpacker.prototype.unpack_raw = function(size){
|
|
if ( this.length < this.index + size){
|
|
throw new Error('BinaryPackFailure: index is out of range'
|
|
+ ' ' + this.index + ' ' + size + ' ' + this.length);
|
|
}
|
|
var buf = this.dataBuffer.slice(this.index, this.index + size);
|
|
this.index += size;
|
|
|
|
//buf = util.bufferToString(buf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
Unpacker.prototype.unpack_string = function(size){
|
|
var bytes = this.read(size);
|
|
var i = 0, str = '', c, code;
|
|
while(i < size){
|
|
c = bytes[i];
|
|
if ( c < 128){
|
|
str += String.fromCharCode(c);
|
|
i++;
|
|
} else if ((c ^ 0xc0) < 32){
|
|
code = ((c ^ 0xc0) << 6) | (bytes[i+1] & 63);
|
|
str += String.fromCharCode(code);
|
|
i += 2;
|
|
} else {
|
|
code = ((c & 15) << 12) | ((bytes[i+1] & 63) << 6) |
|
|
(bytes[i+2] & 63);
|
|
str += String.fromCharCode(code);
|
|
i += 3;
|
|
}
|
|
}
|
|
this.index += size;
|
|
return str;
|
|
}
|
|
|
|
Unpacker.prototype.unpack_array = function(size){
|
|
var objects = new Array(size);
|
|
for(var i = 0; i < size ; i++){
|
|
objects[i] = this.unpack();
|
|
}
|
|
return objects;
|
|
}
|
|
|
|
Unpacker.prototype.unpack_map = function(size){
|
|
var map = {};
|
|
for(var i = 0; i < size ; i++){
|
|
var key = this.unpack();
|
|
var value = this.unpack();
|
|
map[key] = value;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
Unpacker.prototype.unpack_float = function(){
|
|
var uint32 = this.unpack_uint32();
|
|
var sign = uint32 >> 31;
|
|
var exp = ((uint32 >> 23) & 0xff) - 127;
|
|
var fraction = ( uint32 & 0x7fffff ) | 0x800000;
|
|
return (sign == 0 ? 1 : -1) *
|
|
fraction * Math.pow(2, exp - 23);
|
|
}
|
|
|
|
Unpacker.prototype.unpack_double = function(){
|
|
var h32 = this.unpack_uint32();
|
|
var l32 = this.unpack_uint32();
|
|
var sign = h32 >> 31;
|
|
var exp = ((h32 >> 20) & 0x7ff) - 1023;
|
|
var hfrac = ( h32 & 0xfffff ) | 0x100000;
|
|
var frac = hfrac * Math.pow(2, exp - 20) +
|
|
l32 * Math.pow(2, exp - 52);
|
|
return (sign == 0 ? 1 : -1) * frac;
|
|
}
|
|
|
|
Unpacker.prototype.read = function(length){
|
|
var j = this.index;
|
|
if (j + length <= this.length) {
|
|
return this.dataView.subarray(j, j + length);
|
|
} else {
|
|
throw new Error('BinaryPackFailure: read index out of range');
|
|
}
|
|
}
|
|
|
|
function Packer(){
|
|
this.bufferBuilder = new BufferBuilder();
|
|
}
|
|
|
|
Packer.prototype.getBuffer = function(){
|
|
return this.bufferBuilder.getBuffer();
|
|
}
|
|
|
|
Packer.prototype.pack = function(value){
|
|
var type = typeof(value);
|
|
if (type == 'string'){
|
|
this.pack_string(value);
|
|
} else if (type == 'number'){
|
|
if (Math.floor(value) === value){
|
|
this.pack_integer(value);
|
|
} else{
|
|
this.pack_double(value);
|
|
}
|
|
} else if (type == 'boolean'){
|
|
if (value === true){
|
|
this.bufferBuilder.append(0xc3);
|
|
} else if (value === false){
|
|
this.bufferBuilder.append(0xc2);
|
|
}
|
|
} else if (type == 'undefined'){
|
|
this.bufferBuilder.append(0xc0);
|
|
} else if (type == 'object'){
|
|
if (value === null){
|
|
this.bufferBuilder.append(0xc0);
|
|
} else {
|
|
var constructor = value.constructor;
|
|
if (constructor == Array){
|
|
this.pack_array(value);
|
|
} else if (constructor == Blob || constructor == File) {
|
|
this.pack_bin(value);
|
|
} else if (constructor == ArrayBuffer) {
|
|
if(binaryFeatures.useArrayBufferView) {
|
|
this.pack_bin(new Uint8Array(value));
|
|
} else {
|
|
this.pack_bin(value);
|
|
}
|
|
} else if ('BYTES_PER_ELEMENT' in value){
|
|
if(binaryFeatures.useArrayBufferView) {
|
|
this.pack_bin(new Uint8Array(value.buffer));
|
|
} else {
|
|
this.pack_bin(value.buffer);
|
|
}
|
|
} else if (constructor == Object){
|
|
this.pack_object(value);
|
|
} else if (constructor == Date){
|
|
this.pack_string(value.toString());
|
|
} else if (typeof value.toBinaryPack == 'function'){
|
|
this.bufferBuilder.append(value.toBinaryPack());
|
|
} else {
|
|
throw new Error('Type "' + constructor.toString() + '" not yet supported');
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error('Type "' + type + '" not yet supported');
|
|
}
|
|
this.bufferBuilder.flush();
|
|
}
|
|
|
|
|
|
Packer.prototype.pack_bin = function(blob){
|
|
var length = blob.length || blob.byteLength || blob.size;
|
|
if (length <= 0x0f){
|
|
this.pack_uint8(0xa0 + length);
|
|
} else if (length <= 0xffff){
|
|
this.bufferBuilder.append(0xda) ;
|
|
this.pack_uint16(length);
|
|
} else if (length <= 0xffffffff){
|
|
this.bufferBuilder.append(0xdb);
|
|
this.pack_uint32(length);
|
|
} else{
|
|
throw new Error('Invalid length');
|
|
return;
|
|
}
|
|
this.bufferBuilder.append(blob);
|
|
}
|
|
|
|
Packer.prototype.pack_string = function(str){
|
|
var length = utf8Length(str);
|
|
|
|
if (length <= 0x0f){
|
|
this.pack_uint8(0xb0 + length);
|
|
} else if (length <= 0xffff){
|
|
this.bufferBuilder.append(0xd8) ;
|
|
this.pack_uint16(length);
|
|
} else if (length <= 0xffffffff){
|
|
this.bufferBuilder.append(0xd9);
|
|
this.pack_uint32(length);
|
|
} else{
|
|
throw new Error('Invalid length');
|
|
return;
|
|
}
|
|
this.bufferBuilder.append(str);
|
|
}
|
|
|
|
Packer.prototype.pack_array = function(ary){
|
|
var length = ary.length;
|
|
if (length <= 0x0f){
|
|
this.pack_uint8(0x90 + length);
|
|
} else if (length <= 0xffff){
|
|
this.bufferBuilder.append(0xdc)
|
|
this.pack_uint16(length);
|
|
} else if (length <= 0xffffffff){
|
|
this.bufferBuilder.append(0xdd);
|
|
this.pack_uint32(length);
|
|
} else{
|
|
throw new Error('Invalid length');
|
|
}
|
|
for(var i = 0; i < length ; i++){
|
|
this.pack(ary[i]);
|
|
}
|
|
}
|
|
|
|
Packer.prototype.pack_integer = function(num){
|
|
if ( -0x20 <= num && num <= 0x7f){
|
|
this.bufferBuilder.append(num & 0xff);
|
|
} else if (0x00 <= num && num <= 0xff){
|
|
this.bufferBuilder.append(0xcc);
|
|
this.pack_uint8(num);
|
|
} else if (-0x80 <= num && num <= 0x7f){
|
|
this.bufferBuilder.append(0xd0);
|
|
this.pack_int8(num);
|
|
} else if ( 0x0000 <= num && num <= 0xffff){
|
|
this.bufferBuilder.append(0xcd);
|
|
this.pack_uint16(num);
|
|
} else if (-0x8000 <= num && num <= 0x7fff){
|
|
this.bufferBuilder.append(0xd1);
|
|
this.pack_int16(num);
|
|
} else if ( 0x00000000 <= num && num <= 0xffffffff){
|
|
this.bufferBuilder.append(0xce);
|
|
this.pack_uint32(num);
|
|
} else if (-0x80000000 <= num && num <= 0x7fffffff){
|
|
this.bufferBuilder.append(0xd2);
|
|
this.pack_int32(num);
|
|
} else if (-0x8000000000000000 <= num && num <= 0x7FFFFFFFFFFFFFFF){
|
|
this.bufferBuilder.append(0xd3);
|
|
this.pack_int64(num);
|
|
} else if (0x0000000000000000 <= num && num <= 0xFFFFFFFFFFFFFFFF){
|
|
this.bufferBuilder.append(0xcf);
|
|
this.pack_uint64(num);
|
|
} else{
|
|
throw new Error('Invalid integer');
|
|
}
|
|
}
|
|
|
|
Packer.prototype.pack_double = function(num){
|
|
var sign = 0;
|
|
if (num < 0){
|
|
sign = 1;
|
|
num = -num;
|
|
}
|
|
var exp = Math.floor(Math.log(num) / Math.LN2);
|
|
var frac0 = num / Math.pow(2, exp) - 1;
|
|
var frac1 = Math.floor(frac0 * Math.pow(2, 52));
|
|
var b32 = Math.pow(2, 32);
|
|
var h32 = (sign << 31) | ((exp+1023) << 20) |
|
|
(frac1 / b32) & 0x0fffff;
|
|
var l32 = frac1 % b32;
|
|
this.bufferBuilder.append(0xcb);
|
|
this.pack_int32(h32);
|
|
this.pack_int32(l32);
|
|
}
|
|
|
|
Packer.prototype.pack_object = function(obj){
|
|
var keys = Object.keys(obj);
|
|
var length = keys.length;
|
|
if (length <= 0x0f){
|
|
this.pack_uint8(0x80 + length);
|
|
} else if (length <= 0xffff){
|
|
this.bufferBuilder.append(0xde);
|
|
this.pack_uint16(length);
|
|
} else if (length <= 0xffffffff){
|
|
this.bufferBuilder.append(0xdf);
|
|
this.pack_uint32(length);
|
|
} else{
|
|
throw new Error('Invalid length');
|
|
}
|
|
for(var prop in obj){
|
|
if (obj.hasOwnProperty(prop)){
|
|
this.pack(prop);
|
|
this.pack(obj[prop]);
|
|
}
|
|
}
|
|
}
|
|
|
|
Packer.prototype.pack_uint8 = function(num){
|
|
this.bufferBuilder.append(num);
|
|
}
|
|
|
|
Packer.prototype.pack_uint16 = function(num){
|
|
this.bufferBuilder.append(num >> 8);
|
|
this.bufferBuilder.append(num & 0xff);
|
|
}
|
|
|
|
Packer.prototype.pack_uint32 = function(num){
|
|
var n = num & 0xffffffff;
|
|
this.bufferBuilder.append((n & 0xff000000) >>> 24);
|
|
this.bufferBuilder.append((n & 0x00ff0000) >>> 16);
|
|
this.bufferBuilder.append((n & 0x0000ff00) >>> 8);
|
|
this.bufferBuilder.append((n & 0x000000ff));
|
|
}
|
|
|
|
Packer.prototype.pack_uint64 = function(num){
|
|
var high = num / Math.pow(2, 32);
|
|
var low = num % Math.pow(2, 32);
|
|
this.bufferBuilder.append((high & 0xff000000) >>> 24);
|
|
this.bufferBuilder.append((high & 0x00ff0000) >>> 16);
|
|
this.bufferBuilder.append((high & 0x0000ff00) >>> 8);
|
|
this.bufferBuilder.append((high & 0x000000ff));
|
|
this.bufferBuilder.append((low & 0xff000000) >>> 24);
|
|
this.bufferBuilder.append((low & 0x00ff0000) >>> 16);
|
|
this.bufferBuilder.append((low & 0x0000ff00) >>> 8);
|
|
this.bufferBuilder.append((low & 0x000000ff));
|
|
}
|
|
|
|
Packer.prototype.pack_int8 = function(num){
|
|
this.bufferBuilder.append(num & 0xff);
|
|
}
|
|
|
|
Packer.prototype.pack_int16 = function(num){
|
|
this.bufferBuilder.append((num & 0xff00) >> 8);
|
|
this.bufferBuilder.append(num & 0xff);
|
|
}
|
|
|
|
Packer.prototype.pack_int32 = function(num){
|
|
this.bufferBuilder.append((num >>> 24) & 0xff);
|
|
this.bufferBuilder.append((num & 0x00ff0000) >>> 16);
|
|
this.bufferBuilder.append((num & 0x0000ff00) >>> 8);
|
|
this.bufferBuilder.append((num & 0x000000ff));
|
|
}
|
|
|
|
Packer.prototype.pack_int64 = function(num){
|
|
var high = Math.floor(num / Math.pow(2, 32));
|
|
var low = num % Math.pow(2, 32);
|
|
this.bufferBuilder.append((high & 0xff000000) >>> 24);
|
|
this.bufferBuilder.append((high & 0x00ff0000) >>> 16);
|
|
this.bufferBuilder.append((high & 0x0000ff00) >>> 8);
|
|
this.bufferBuilder.append((high & 0x000000ff));
|
|
this.bufferBuilder.append((low & 0xff000000) >>> 24);
|
|
this.bufferBuilder.append((low & 0x00ff0000) >>> 16);
|
|
this.bufferBuilder.append((low & 0x0000ff00) >>> 8);
|
|
this.bufferBuilder.append((low & 0x000000ff));
|
|
}
|
|
|
|
function _utf8Replace(m){
|
|
var code = m.charCodeAt(0);
|
|
|
|
if(code <= 0x7ff) return '00';
|
|
if(code <= 0xffff) return '000';
|
|
if(code <= 0x1fffff) return '0000';
|
|
if(code <= 0x3ffffff) return '00000';
|
|
return '000000';
|
|
}
|
|
|
|
function utf8Length(str){
|
|
if (str.length > 600) {
|
|
// Blob method faster for large strings
|
|
return (new Blob([str])).size;
|
|
} else {
|
|
return str.replace(/[^\u0000-\u007F]/g, _utf8Replace).length;
|
|
}
|
|
}
|
|
/**
|
|
* Light EventEmitter. Ported from Node.js/events.js
|
|
* Eric Zhang
|
|
*/
|
|
|
|
/**
|
|
* EventEmitter class
|
|
* Creates an object with event registering and firing methods
|
|
*/
|
|
function EventEmitter() {
|
|
// Initialise required storage variables
|
|
this._events = {};
|
|
}
|
|
|
|
var isArray = Array.isArray;
|
|
|
|
|
|
EventEmitter.prototype.addListener = function(type, listener, scope, once) {
|
|
if ('function' !== typeof listener) {
|
|
throw new Error('addListener only takes instances of Function');
|
|
}
|
|
|
|
// To avoid recursion in the case that type == "newListeners"! Before
|
|
// adding it to the listeners, first emit "newListeners".
|
|
this.emit('newListener', type, typeof listener.listener === 'function' ?
|
|
listener.listener : listener);
|
|
|
|
if (!this._events[type]) {
|
|
// Optimize the case of one listener. Don't need the extra array object.
|
|
this._events[type] = listener;
|
|
} else if (isArray(this._events[type])) {
|
|
|
|
// If we've already got an array, just append.
|
|
this._events[type].push(listener);
|
|
|
|
} else {
|
|
// Adding the second element, need to change to array.
|
|
this._events[type] = [this._events[type], listener];
|
|
}
|
|
|
|
};
|
|
|
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
|
|
|
EventEmitter.prototype.once = function(type, listener, scope) {
|
|
if ('function' !== typeof listener) {
|
|
throw new Error('.once only takes instances of Function');
|
|
}
|
|
|
|
var self = this;
|
|
function g() {
|
|
self.removeListener(type, g);
|
|
listener.apply(this, arguments);
|
|
};
|
|
|
|
g.listener = listener;
|
|
self.on(type, g);
|
|
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.removeListener = function(type, listener, scope) {
|
|
if ('function' !== typeof listener) {
|
|
throw new Error('removeListener only takes instances of Function');
|
|
}
|
|
|
|
// does not use listeners(), so no side effect of creating _events[type]
|
|
if (!this._events[type]) return this;
|
|
|
|
var list = this._events[type];
|
|
|
|
if (isArray(list)) {
|
|
var position = -1;
|
|
for (var i = 0, length = list.length; i < length; i++) {
|
|
if (list[i] === listener ||
|
|
(list[i].listener && list[i].listener === listener))
|
|
{
|
|
position = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (position < 0) return this;
|
|
list.splice(position, 1);
|
|
if (list.length == 0)
|
|
delete this._events[type];
|
|
} else if (list === listener ||
|
|
(list.listener && list.listener === listener))
|
|
{
|
|
delete this._events[type];
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
|
|
|
|
|
EventEmitter.prototype.removeAllListeners = function(type) {
|
|
if (arguments.length === 0) {
|
|
this._events = {};
|
|
return this;
|
|
}
|
|
|
|
// does not use listeners(), so no side effect of creating _events[type]
|
|
if (type && this._events && this._events[type]) this._events[type] = null;
|
|
return this;
|
|
};
|
|
|
|
EventEmitter.prototype.listeners = function(type) {
|
|
if (!this._events[type]) this._events[type] = [];
|
|
if (!isArray(this._events[type])) {
|
|
this._events[type] = [this._events[type]];
|
|
}
|
|
return this._events[type];
|
|
};
|
|
|
|
EventEmitter.prototype.emit = function(type) {
|
|
var type = arguments[0];
|
|
var handler = this._events[type];
|
|
if (!handler) return false;
|
|
|
|
if (typeof handler == 'function') {
|
|
switch (arguments.length) {
|
|
// fast cases
|
|
case 1:
|
|
handler.call(this);
|
|
break;
|
|
case 2:
|
|
handler.call(this, arguments[1]);
|
|
break;
|
|
case 3:
|
|
handler.call(this, arguments[1], arguments[2]);
|
|
break;
|
|
// slower
|
|
default:
|
|
var l = arguments.length;
|
|
var args = new Array(l - 1);
|
|
for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
|
|
handler.apply(this, args);
|
|
}
|
|
return true;
|
|
|
|
} else if (isArray(handler)) {
|
|
var l = arguments.length;
|
|
var args = new Array(l - 1);
|
|
for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
|
|
|
|
var listeners = handler.slice();
|
|
for (var i = 0, l = listeners.length; i < l; i++) {
|
|
listeners[i].apply(this, args);
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
var util = {
|
|
inherits: function(ctor, superCtor) {
|
|
ctor.super_ = superCtor;
|
|
ctor.prototype = Object.create(superCtor.prototype, {
|
|
constructor: {
|
|
value: ctor,
|
|
enumerable: false,
|
|
writable: true,
|
|
configurable: true
|
|
}
|
|
});
|
|
},
|
|
extend: function(dest, source) {
|
|
for(var key in source) {
|
|
if(source.hasOwnProperty(key)) {
|
|
dest[key] = source[key];
|
|
}
|
|
}
|
|
return dest;
|
|
},
|
|
pack: BinaryPack.pack,
|
|
unpack: BinaryPack.unpack,
|
|
setZeroTimeout: (function(global) {
|
|
var timeouts = [];
|
|
var messageName = 'zero-timeout-message';
|
|
|
|
// Like setTimeout, but only takes a function argument. There's
|
|
// no time argument (always zero) and no arguments (you have to
|
|
// use a closure).
|
|
function setZeroTimeoutPostMessage(fn) {
|
|
timeouts.push(fn);
|
|
global.postMessage(messageName, '*');
|
|
}
|
|
|
|
function handleMessage(event) {
|
|
if (event.source == global && event.data == messageName) {
|
|
if (event.stopPropagation) {
|
|
event.stopPropagation();
|
|
}
|
|
if (timeouts.length) {
|
|
timeouts.shift()();
|
|
}
|
|
}
|
|
}
|
|
if (global.addEventListener) {
|
|
global.addEventListener('message', handleMessage, true);
|
|
} else if (global.attachEvent) {
|
|
global.attachEvent('onmessage', handleMessage);
|
|
}
|
|
return setZeroTimeoutPostMessage;
|
|
}(this))
|
|
};
|
|
|
|
|
|
|
|
function Stream() {
|
|
EventEmitter.call(this);
|
|
}
|
|
|
|
util.inherits(Stream, EventEmitter);
|
|
|
|
Stream.prototype.pipe = function(dest, options) {
|
|
var source = this;
|
|
|
|
function ondata(chunk) {
|
|
if (dest.writable) {
|
|
if (false === dest.write(chunk) && source.pause) {
|
|
source.pause();
|
|
}
|
|
}
|
|
}
|
|
|
|
source.on('data', ondata);
|
|
|
|
function ondrain() {
|
|
if (source.readable && source.resume) {
|
|
source.resume();
|
|
}
|
|
}
|
|
|
|
dest.on('drain', ondrain);
|
|
|
|
// If the 'end' option is not supplied, dest.end() will be called when
|
|
// source gets the 'end' or 'close' events. Only dest.end() once.
|
|
if (!dest._isStdio && (!options || options.end !== false)) {
|
|
source.on('end', onend);
|
|
source.on('close', onclose);
|
|
}
|
|
|
|
var didOnEnd = false;
|
|
function onend() {
|
|
if (didOnEnd) return;
|
|
didOnEnd = true;
|
|
|
|
dest.end();
|
|
}
|
|
|
|
|
|
function onclose() {
|
|
if (didOnEnd) return;
|
|
didOnEnd = true;
|
|
|
|
dest.destroy();
|
|
}
|
|
|
|
// don't leave dangling pipes when there are errors.
|
|
function onerror(er) {
|
|
cleanup();
|
|
if (this.listeners('error').length === 0) {
|
|
throw er; // Unhandled stream error in pipe.
|
|
}
|
|
}
|
|
|
|
source.on('error', onerror);
|
|
dest.on('error', onerror);
|
|
|
|
// remove all the event listeners that were added.
|
|
function cleanup() {
|
|
source.removeListener('data', ondata);
|
|
dest.removeListener('drain', ondrain);
|
|
|
|
source.removeListener('end', onend);
|
|
source.removeListener('close', onclose);
|
|
|
|
source.removeListener('error', onerror);
|
|
dest.removeListener('error', onerror);
|
|
|
|
source.removeListener('end', cleanup);
|
|
source.removeListener('close', cleanup);
|
|
|
|
dest.removeListener('end', cleanup);
|
|
dest.removeListener('close', cleanup);
|
|
}
|
|
|
|
source.on('end', cleanup);
|
|
source.on('close', cleanup);
|
|
|
|
dest.on('end', cleanup);
|
|
dest.on('close', cleanup);
|
|
|
|
dest.emit('pipe', source);
|
|
|
|
// Allow for unix-like usage: A.pipe(B).pipe(C)
|
|
return dest;
|
|
};
|
|
function BlobReadStream(source, options){
|
|
Stream.call(this);
|
|
|
|
options = util.extend({
|
|
readDelay: 0,
|
|
paused: false
|
|
}, options);
|
|
|
|
this._source = source;
|
|
this._start = 0;
|
|
this._readChunkSize = options.chunkSize || source.size;
|
|
this._readDelay = options.readDelay;
|
|
|
|
this.readable = true;
|
|
this.paused = options.paused;
|
|
|
|
this._read();
|
|
}
|
|
|
|
util.inherits(BlobReadStream, Stream);
|
|
|
|
|
|
BlobReadStream.prototype.pause = function(){
|
|
this.paused = true;
|
|
};
|
|
|
|
BlobReadStream.prototype.resume = function(){
|
|
this.paused = false;
|
|
this._read();
|
|
};
|
|
|
|
BlobReadStream.prototype.destroy = function(){
|
|
this.readable = false;
|
|
clearTimeout(this._timeoutId);
|
|
};
|
|
|
|
BlobReadStream.prototype._read = function(){
|
|
|
|
var self = this;
|
|
|
|
function emitReadChunk(){
|
|
self._emitReadChunk();
|
|
}
|
|
|
|
var readDelay = this._readDelay;
|
|
if (readDelay !== 0){
|
|
this._timeoutId = setTimeout(emitReadChunk, readDelay);
|
|
} else {
|
|
util.setZeroTimeout(emitReadChunk);
|
|
}
|
|
|
|
};
|
|
|
|
BlobReadStream.prototype._emitReadChunk = function(){
|
|
|
|
if(this.paused || !this.readable) return;
|
|
|
|
var chunkSize = Math.min(this._source.size - this._start, this._readChunkSize);
|
|
|
|
if(chunkSize === 0){
|
|
this.readable = false;
|
|
this.emit("end");
|
|
return;
|
|
}
|
|
|
|
var sourceEnd = this._start + chunkSize;
|
|
var chunk = (this._source.slice || this._source.webkitSlice || this._source.mozSlice).call(this._source, this._start, sourceEnd);
|
|
|
|
this._start = sourceEnd;
|
|
this._read();
|
|
|
|
this.emit("data", chunk);
|
|
|
|
};
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
function BlobWriteStream(options){
|
|
|
|
stream.Stream.call(this);
|
|
|
|
options = _.extend({
|
|
onFull: onFull,
|
|
onEnd: function(){},
|
|
minBlockAllocSize: 0,
|
|
drainDelay:0
|
|
}, options);
|
|
|
|
this._onFull = options.onFull;
|
|
this._onEnd = options.onEnd;
|
|
this._onWrite = options.onWrite;
|
|
|
|
this._minBlockAllocSize = options.minBlockAllocSize;
|
|
this._maxBlockAllocSize = options.maxBlockAllocSize;
|
|
this._drainDelay = options.drainDelay;
|
|
|
|
this._buffer = new Buffer(options.minBlockAllocSize);
|
|
this._destination = this._buffer;
|
|
this._destinationPos = 0;
|
|
|
|
this._writeQueue = [];
|
|
this._pendingOnFull = false;
|
|
this._pendingQueueDrain = false;
|
|
|
|
this.writable = true;
|
|
this.bytesWritten = 0;
|
|
}
|
|
|
|
util.inherits(BlobWriteStream, stream.Stream);
|
|
|
|
BlobWriteStream.prototype.getBuffer = function(){
|
|
return this._buffer;
|
|
};
|
|
|
|
BlobWriteStream.prototype.write = function(data, encoding){
|
|
|
|
if(!this.writable){
|
|
throw new Error("stream is not writable");
|
|
}
|
|
|
|
if(!Buffer.isBuffer(data)){
|
|
data = new Buffer(data, encoding);
|
|
}
|
|
|
|
if(data.length){
|
|
this._writeQueue.push(data);
|
|
}
|
|
|
|
this._commit();
|
|
|
|
return this._writeQueue.length === 0;
|
|
};
|
|
|
|
BlobWriteStream.prototype._commit = function(){
|
|
|
|
var self = this;
|
|
|
|
var destination = this._destination;
|
|
var writeQueue = this._writeQueue;
|
|
|
|
var startDestinationPos = this._destinationPos;
|
|
|
|
while(writeQueue.length && destination.length){
|
|
|
|
var head = writeQueue[0];
|
|
|
|
var copySize = Math.min(destination.length, head.length);
|
|
|
|
head.copy(destination, 0, 0, copySize);
|
|
|
|
head = head.slice(copySize);
|
|
destination = destination.slice(copySize);
|
|
|
|
this.bytesWritten += copySize;
|
|
this._destinationPos += copySize;
|
|
|
|
if(head.length === 0){
|
|
writeQueue.shift();
|
|
}
|
|
else{
|
|
writeQueue[0] = head;
|
|
}
|
|
}
|
|
|
|
this._destination = destination;
|
|
|
|
bytesCommitted = this._destinationPos - startDestinationPos;
|
|
if(bytesCommitted){
|
|
if(this._onWrite){
|
|
|
|
if(writeQueue.length){
|
|
this._pendingQueueDrain = true;
|
|
}
|
|
|
|
// By locking destination the buffer is frozen and the onWrite
|
|
// callback cannot miss any write commits
|
|
this._destination = emptyBuffer;
|
|
|
|
var consumer = this._onWrite;
|
|
this._onWrite = null;
|
|
|
|
consumer.call(this, function(nextCallback){
|
|
util.setZeroTimeout(function(){
|
|
self._destination = destination;
|
|
self._onWrite = nextCallback;
|
|
self._commit();
|
|
});
|
|
}, consumer);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(writeQueue.length){
|
|
|
|
this._pendingQueueDrain = true;
|
|
this._growBuffer();
|
|
}
|
|
else if(this._pendingQueueDrain){
|
|
|
|
this._pendingQueueDrain = false;
|
|
|
|
if(this._drainDelay !== 0){
|
|
setTimeout(function(){
|
|
self.emit("drain");
|
|
}, this._drainDelay);
|
|
}
|
|
else{
|
|
util.setZeroTimeout(function(){
|
|
self.emit("drain");
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
BlobWriteStream.prototype._growBuffer = function(){
|
|
|
|
var self = this;
|
|
var writeQueue = this._writeQueue;
|
|
|
|
var requestSize = this._minBlockAllocSize;
|
|
|
|
var maxBlockAllocSize = this._maxBlockAllocSize;
|
|
var add = (maxBlockAllocSize === undefined ? function(a, b){return a + b;} : function(a, b){return Math.min(a + b, maxBlockAllocSize);});
|
|
|
|
for(var i = 0, queueLength = writeQueue.length; i < queueLength; i++){
|
|
requestSize = add(requestSize, writeQueue[i].length);
|
|
}
|
|
|
|
// Prevent concurrent onFull callbacks
|
|
if(this._pendingOnFull){
|
|
return;
|
|
}
|
|
this._pendingOnFull = true;
|
|
|
|
this._onFull(this._buffer, requestSize, function(buffer, destination){
|
|
util.setZeroTimeout(function(){
|
|
|
|
self._pendingOnFull = false;
|
|
|
|
if(!destination){
|
|
if(self.writable){
|
|
self.emit("error", new Error("buffer is full"));
|
|
}
|
|
self.destroy();
|
|
return;
|
|
}
|
|
|
|
self._buffer = buffer;
|
|
self._destination = destination;
|
|
|
|
self._commit();
|
|
});
|
|
});
|
|
};
|
|
|
|
BlobWriteStream.prototype.end = function(data, encoding){
|
|
|
|
var self = this;
|
|
|
|
function _end(){
|
|
self.writable = false;
|
|
self._onEnd();
|
|
}
|
|
|
|
if(data){
|
|
if(this.write(data, encoding)){
|
|
_end();
|
|
}else{
|
|
self.writable = false;
|
|
this.once("drain", _end);
|
|
}
|
|
}
|
|
else{
|
|
_end();
|
|
}
|
|
};
|
|
|
|
BlobWriteStream.prototype.destroy = function(){
|
|
this.writable = false;
|
|
this._pendingQueueDrain = false;
|
|
this._writeQueue = [];
|
|
};
|
|
|
|
BlobWriteStream.prototype.consume = function(consume){
|
|
|
|
this._buffer = this._buffer.slice(consume);
|
|
this._destinationPos -= consume;
|
|
};
|
|
|
|
BlobWriteStream.prototype.getCommittedSlice = function(){
|
|
return this._buffer.slice(0, this._destinationPos);
|
|
};
|
|
|
|
function onFull(buffer, extraSize, callback){
|
|
var newBuffer = new Buffer(buffer.length + extraSize);
|
|
buffer.copy(newBuffer);
|
|
callback(newBuffer, newBuffer.slice(buffer.length));
|
|
}
|
|
*/
|
|
exports.BlobReadStream = BlobReadStream;
|
|
|
|
function BinaryStream(socket, id, create, meta) {
|
|
if (!(this instanceof BinaryStream)) return new BinaryStream(options);
|
|
|
|
var self = this;
|
|
|
|
Stream.call(this);
|
|
|
|
|
|
this.id = id;
|
|
this._socket = socket;
|
|
|
|
this.writable = true;
|
|
this.readable = true;
|
|
this.paused = false;
|
|
|
|
this._closed = false;
|
|
this._ended = false;
|
|
|
|
if(create) {
|
|
// This is a stream we are creating
|
|
this._write(1, meta, this.id);
|
|
}
|
|
}
|
|
|
|
util.inherits(BinaryStream, Stream);
|
|
|
|
|
|
BinaryStream.prototype._onDrain = function() {
|
|
if(!this.paused) {
|
|
this.emit('drain');
|
|
}
|
|
};
|
|
|
|
BinaryStream.prototype._onClose = function() {
|
|
// Emit close event
|
|
if (this._closed) {
|
|
return;
|
|
}
|
|
this.readable = false;
|
|
this.writable = false;
|
|
this._closed = true;
|
|
this.emit('close');
|
|
};
|
|
|
|
BinaryStream.prototype._onError = function(error){
|
|
this.readable = false;
|
|
this.writable = false;
|
|
this.emit('error', error);
|
|
};
|
|
|
|
// Write stream
|
|
|
|
BinaryStream.prototype._onPause = function() {
|
|
// Emit pause event
|
|
this.paused = true;
|
|
this.emit('pause');
|
|
};
|
|
|
|
BinaryStream.prototype._onResume = function() {
|
|
// Emit resume event
|
|
this.paused = false;
|
|
this.emit('resume');
|
|
this.emit('drain');
|
|
};
|
|
|
|
BinaryStream.prototype._write = function(code, data, bonus) {
|
|
if (this._socket.readyState !== this._socket.constructor.OPEN) {
|
|
return false;
|
|
}
|
|
var message = util.pack([code, data, bonus]);
|
|
return this._socket.send(message) !== false;
|
|
};
|
|
|
|
BinaryStream.prototype.write = function(data) {
|
|
if(this.writable) {
|
|
var out = this._write(2, data, this.id);
|
|
return !this.paused && out;
|
|
} else {
|
|
throw new Error('Stream is not writable');
|
|
}
|
|
};
|
|
|
|
BinaryStream.prototype.end = function() {
|
|
this._ended = true;
|
|
this.readable = false;
|
|
this._write(5, null, this.id);
|
|
};
|
|
|
|
BinaryStream.prototype.destroy = BinaryStream.prototype.destroySoon = function() {
|
|
this._onClose();
|
|
this._write(6, null, this.id);
|
|
};
|
|
|
|
|
|
// Read stream
|
|
|
|
BinaryStream.prototype._onEnd = function() {
|
|
if(this._ended) {
|
|
return;
|
|
}
|
|
this._ended = true;
|
|
this.readable = false;
|
|
this.emit('end');
|
|
};
|
|
|
|
BinaryStream.prototype._onData = function(data) {
|
|
// Dispatch
|
|
this.emit('data', data);
|
|
};
|
|
|
|
BinaryStream.prototype.pause = function() {
|
|
this._onPause();
|
|
this._write(3, null, this.id);
|
|
};
|
|
|
|
BinaryStream.prototype.resume = function() {
|
|
this._onResume();
|
|
this._write(4, null, this.id);
|
|
};
|
|
|
|
|
|
function BinaryClient(socket, options) {
|
|
if (!(this instanceof BinaryClient)) return new BinaryClient(socket, options);
|
|
|
|
EventEmitter.call(this);
|
|
|
|
var self = this;
|
|
|
|
this._options = util.extend({
|
|
chunkSize: 40960
|
|
}, options);
|
|
|
|
this.streams = {};
|
|
|
|
if(typeof socket === 'string') {
|
|
this._nextId = 0;
|
|
this._socket = new WebSocket(socket);
|
|
} else {
|
|
// Use odd numbered ids for server originated streams
|
|
this._nextId = 1;
|
|
this._socket = socket;
|
|
}
|
|
|
|
this._socket.binaryType = 'arraybuffer';
|
|
|
|
this._socket.addEventListener('open', function(){
|
|
self.emit('open');
|
|
});
|
|
this._socket.addEventListener('error', function(error){
|
|
var ids = Object.keys(self.streams);
|
|
for (var i = 0, ii = ids.length; i < ii; i++) {
|
|
self.streams[ids[i]]._onError(error);
|
|
}
|
|
self.emit('error', error);
|
|
});
|
|
this._socket.addEventListener('close', function(code, message){
|
|
var ids = Object.keys(self.streams);
|
|
for (var i = 0, ii = ids.length; i < ii; i++) {
|
|
self.streams[ids[i]]._onClose();
|
|
}
|
|
self.emit('close', code, message);
|
|
});
|
|
this._socket.addEventListener('message', function(data, flags){
|
|
util.setZeroTimeout(function(){
|
|
|
|
// Message format
|
|
// [type, payload, bonus ]
|
|
//
|
|
// Reserved
|
|
// [ 0 , X , X ]
|
|
//
|
|
//
|
|
// New stream
|
|
// [ 1 , Meta , new streamId ]
|
|
//
|
|
//
|
|
// Data
|
|
// [ 2 , Data , streamId ]
|
|
//
|
|
//
|
|
// Pause
|
|
// [ 3 , null , streamId ]
|
|
//
|
|
//
|
|
// Resume
|
|
// [ 4 , null , streamId ]
|
|
//
|
|
//
|
|
// End
|
|
// [ 5 , null , streamId ]
|
|
//
|
|
//
|
|
// Close
|
|
// [ 6 , null , streamId ]
|
|
//
|
|
|
|
data = data.data;
|
|
|
|
try {
|
|
data = util.unpack(data);
|
|
} catch (ex) {
|
|
return self.emit('error', new Error('Received unparsable message: ' + ex));
|
|
}
|
|
if (!(data instanceof Array))
|
|
return self.emit('error', new Error('Received non-array message'));
|
|
if (data.length != 3)
|
|
return self.emit('error', new Error('Received message with wrong part count: ' + data.length));
|
|
if ('number' != typeof data[0])
|
|
return self.emit('error', new Error('Received message with non-number type: ' + data[0]));
|
|
|
|
switch(data[0]) {
|
|
case 0:
|
|
// Reserved
|
|
break;
|
|
case 1:
|
|
var meta = data[1];
|
|
var streamId = data[2];
|
|
var binaryStream = self._receiveStream(streamId);
|
|
self.emit('stream', binaryStream, meta);
|
|
break;
|
|
case 2:
|
|
var payload = data[1];
|
|
var streamId = data[2];
|
|
var binaryStream = self.streams[streamId];
|
|
if(binaryStream) {
|
|
binaryStream._onData(payload);
|
|
} else {
|
|
self.emit('error', new Error('Received `data` message for unknown stream: ' + streamId));
|
|
}
|
|
break;
|
|
case 3:
|
|
var streamId = data[2];
|
|
var binaryStream = self.streams[streamId];
|
|
if(binaryStream) {
|
|
binaryStream._onPause();
|
|
} else {
|
|
self.emit('error', new Error('Received `pause` message for unknown stream: ' + streamId));
|
|
}
|
|
break;
|
|
case 4:
|
|
var streamId = data[2];
|
|
var binaryStream = self.streams[streamId];
|
|
if(binaryStream) {
|
|
binaryStream._onResume();
|
|
} else {
|
|
self.emit('error', new Error('Received `resume` message for unknown stream: ' + streamId));
|
|
}
|
|
break;
|
|
case 5:
|
|
var streamId = data[2];
|
|
var binaryStream = self.streams[streamId];
|
|
if(binaryStream) {
|
|
binaryStream._onEnd();
|
|
} else {
|
|
self.emit('error', new Error('Received `end` message for unknown stream: ' + streamId));
|
|
}
|
|
break;
|
|
case 6:
|
|
var streamId = data[2];
|
|
var binaryStream = self.streams[streamId];
|
|
if(binaryStream) {
|
|
binaryStream._onClose();
|
|
} else {
|
|
self.emit('error', new Error('Received `close` message for unknown stream: ' + streamId));
|
|
}
|
|
break;
|
|
default:
|
|
self.emit('error', new Error('Unrecognized message type received: ' + data[0]));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
util.inherits(BinaryClient, EventEmitter);
|
|
|
|
BinaryClient.prototype.send = function(data, meta){
|
|
var stream = this.createStream(meta);
|
|
if(data instanceof Stream) {
|
|
data.pipe(stream);
|
|
} else if (util.isNode === true) {
|
|
if(Buffer.isBuffer(data)) {
|
|
(new BufferReadStream(data, {chunkSize: this._options.chunkSize})).pipe(stream);
|
|
} else {
|
|
stream.write(data);
|
|
}
|
|
} else if (util.isNode !== true) {
|
|
if(data.constructor == Blob || data.constructor == File) {
|
|
(new BlobReadStream(data, {chunkSize: this._options.chunkSize})).pipe(stream);
|
|
} else if (data.constructor == ArrayBuffer) {
|
|
var blob;
|
|
if(binaryFeatures.useArrayBufferView) {
|
|
data = new Uint8Array(data);
|
|
}
|
|
if(binaryFeatures.useBlobBuilder) {
|
|
var builder = new BlobBuilder();
|
|
builder.append(data);
|
|
blob = builder.getBlob()
|
|
} else {
|
|
blob = new Blob([data]);
|
|
}
|
|
(new BlobReadStream(blob, {chunkSize: this._options.chunkSize})).pipe(stream);
|
|
} else if (typeof data === 'object' && 'BYTES_PER_ELEMENT' in data) {
|
|
var blob;
|
|
if(!binaryFeatures.useArrayBufferView) {
|
|
// Warn
|
|
data = data.buffer;
|
|
}
|
|
if(binaryFeatures.useBlobBuilder) {
|
|
var builder = new BlobBuilder();
|
|
builder.append(data);
|
|
blob = builder.getBlob()
|
|
} else {
|
|
blob = new Blob([data]);
|
|
}
|
|
(new BlobReadStream(blob, {chunkSize: this._options.chunkSize})).pipe(stream);
|
|
} else {
|
|
stream.write(data);
|
|
}
|
|
}
|
|
return stream;
|
|
};
|
|
|
|
BinaryClient.prototype._receiveStream = function(streamId){
|
|
var self = this;
|
|
var binaryStream = new BinaryStream(this._socket, streamId, false);
|
|
binaryStream.on('close', function(){
|
|
delete self.streams[streamId];
|
|
});
|
|
this.streams[streamId] = binaryStream;
|
|
return binaryStream;
|
|
};
|
|
|
|
BinaryClient.prototype.createStream = function(meta){
|
|
if(this._socket.readyState !== WebSocket.OPEN) {
|
|
throw new Error('Client is not yet connected or has closed');
|
|
return;
|
|
}
|
|
var self = this;
|
|
var streamId = this._nextId;
|
|
this._nextId += 2;
|
|
var binaryStream = new BinaryStream(this._socket, streamId, true, meta);
|
|
binaryStream.on('close', function(){
|
|
delete self.streams[streamId];
|
|
});
|
|
this.streams[streamId] = binaryStream;
|
|
return binaryStream;
|
|
};
|
|
|
|
BinaryClient.prototype.close = BinaryClient.prototype.destroy = function() {
|
|
this._socket.close();
|
|
};
|
|
|
|
exports.BinaryClient = BinaryClient;
|
|
|
|
})(this);
|