latte_web
Version:
1,763 lines (1,478 loc) • 101 kB
JavaScript
(function(define) {'use strict'
define("latte_io/index.js", ["require", "exports", "module", "window"],
function(require, exports, module, window) {
var transports = require("./transports");
var latte_lib = require("latte_lib");
var debug = latte_lib.debug.info;
var parser = require("./parser");
var parseuri = function(str) {
var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
var parts = [
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
];
var src = str,
b = str.indexOf('['),
e = str.indexOf(']');
if (b != -1 && e != -1) {
str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);
}
var m = re.exec(str || ''),
uri = {},
i = 14;
while (i--) {
uri[parts[i]] = m[i] || '';
}
if (b != -1 && e != -1) {
uri.source = src;
uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');
uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');
uri.ipv6uri = true;
}
return uri;
};
var parsejson = function(data) {
var rvalidchars = /^[\],:{}\s]*$/;
var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g;
var rtrimLeft = /^\s+/;
var rtrimRight = /\s+$/;
if ('string' != typeof data || !data) {
return null;
}
data = data.replace(rtrimLeft, '').replace(rtrimRight, '');
// Attempt to parse using the native JSON parser first
if ( JSON.parse) {
return JSON.parse(data);
}
if (rvalidchars.test(data.replace(rvalidescape, '@')
.replace(rvalidtokens, ']')
.replace(rvalidbraces, ''))) {
return (new Function('return ' + data))();
}
};
var parseqs = {
encode : function (obj) {
var str = '';
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
if (str.length) str += '&';
str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);
}
}
return str;
},
decode : function(qs){
var qry = {};
var pairs = qs.split('&');
for (var i = 0, l = pairs.length; i < l; i++) {
var pair = pairs[i].split('=');
qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
return qry;
}
};
module.exports = Socket;
function noop(){}
/**
* Socket constructor.
*
* @param {String|Object} uri or options
* @param {Object} options
* @api public
*/
function Socket(uri, opts){
if (!(this instanceof Socket)) return new Socket(uri, opts);
opts = opts || {};
if (uri && 'object' == typeof uri) {
opts = uri;
uri = null;
}
if (uri) {
uri = parseuri(uri);
opts.host = uri.host;
opts.secure = uri.protocol == 'https' || uri.protocol == 'wss';
opts.port = uri.port;
opts.path = uri.path;
if (uri.query) opts.query = uri.query;
}
this.secure = null != opts.secure ? opts.secure :
(window.location && 'https:' == location.protocol);
if (opts.host) {
var match = opts.host.match(/(\[.+\])(.+)?/)
, pieces;
if (match) {
opts.hostname = match[1];
if (match[2]) opts.port = match[2].slice(1);
} else {
pieces = opts.host.split(':');
opts.hostname = pieces.shift();
if (pieces.length) opts.port = pieces.pop();
}
// if `host` does not include a port and one is not specified manually,
// use the protocol default
if (!opts.port) opts.port = this.secure ? '443' : '80';
}
this.agent = opts.agent || false;
this.hostname = opts.hostname ||
(window.location ? location.hostname : 'localhost');
this.port = opts.port || (window.location && location.port ?
location.port :
(this.secure ? 443 : 80));
this.query = opts.query || {};
if ('string' == typeof this.query) this.query = parseqs.decode(this.query);
this.upgrade = false !== opts.upgrade;
this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/';
this.forceJSONP = !!opts.forceJSONP;
this.jsonp = false !== opts.jsonp;
this.forceBase64 = !!opts.forceBase64;
this.enablesXDR = !!opts.enablesXDR;
this.timestampParam = opts.timestampParam || 't';
this.timestampRequests = opts.timestampRequests;
this.transports = opts.transports || ['polling', 'websocket'];
this.readyState = '';
this.writeBuffer = [];
this.callbackBuffer = [];
this.policyPort = opts.policyPort || 843;
this.rememberUpgrade = opts.rememberUpgrade || false;
this.binaryType = null;
this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades;
this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || true) : false;
// SSL options for Node.js client
this.pfx = opts.pfx || null;
this.key = opts.key || null;
this.passphrase = opts.passphrase || null;
this.cert = opts.cert || null;
this.ca = opts.ca || null;
this.ciphers = opts.ciphers || null;
this.rejectUnauthorized = opts.rejectUnauthorized || null;
// other options for Node.js client
var freeGlobal = typeof window == 'object' && window;
if (freeGlobal.global === freeGlobal) {
if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) {
this.extraHeaders = opts.extraHeaders;
}
}
this.open();
};
(function() {
this.priorWebsocketSuccess = false;
}).call(Socket);
/**
* Mix in `Emitter`.
*/
latte_lib.inherits(Socket, latte_lib.events);
(function() {
this.createTransport = function(name) {
debug("create transport '%s' ", name);
var query = latte_lib.clone(this.query);
query.EIO = parser.protocol;
query.transport = name;
if(this.id) query.sid = this.id;
var transport = new transports[name]({
agent: this.agent,
hostname: this.hostname,
port: this.port,
secure: this.secure,
path: this.path,
query: query,
forceJSONP: this.forceJSONP,
jsonp: this.jsonp,
forceBase64: this.forceBase64,
enablesXDR: this.enablesXDR,
timestampRequests: this.timestampRequests,
timestampParam: this.timestampParam,
policyPort: this.policyPort,
socket: this,
pfx: this.pfx,
key: this.key,
passphrase: this.passphrase,
cert: this.cert,
ca: this.ca,
ciphers: this.ciphers,
rejectUnauthorized: this.rejectUnauthorized,
perMessageDeflate: this.perMessageDeflate,
extraHeaders: this.extraHeaders
});
return transport;
}
this.open = function() {
var transport;
if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) {
transport = 'websocket';
} else if (0 === this.transports.length) {
// Emit error on next tick so it can be listened to
var self = this;
setTimeout(function() {
self.emit('error', 'No transports available');
}, 0);
return;
} else {
transport = this.transports[0];
}
this.readyState = 'opening';
// Retry with the next transport if the transport is disabled (jsonp: false)
//var transport;
try {
transport = this.createTransport(transport);
} catch (e) {
this.transports.shift();
this.open();
return;
}
transport.open();
this.setTransport(transport);
};
this.setTransport = function(transport) {
debug("setting transport %s", transport.name);
var self = this;
if(this.transport) {
debug("clearing existing transport %s", this.transport.name);
this.transport.removeAllListeners();
}
this.transport = transport;
transport
.on('drain', function(){
self.onDrain();
})
.on('packet', function(packet){
self.onPacket(packet);
})
.on('error', function(e){
self.onError(e);
})
.on('close', function(){
self.onClose('transport close');
});
};
this.probe = function (name) {
debug('probing transport "%s"', name);
var transport = this.createTransport(name, { probe: 1 })
, failed = false
, self = this;
Socket.priorWebsocketSuccess = false;
function onTransportOpen(){
if (self.onlyBinaryUpgrades) {
var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
failed = failed || upgradeLosesBinary;
}
if (failed) return;
debug('probe transport "%s" opened', name);
transport.send([{ type: 'ping', data: 'probe', options: { compress: true } }]);
transport.once('packet', function (msg) {
if (failed) return;
if ('pong' == msg.type && 'probe' == msg.data) {
debug('probe transport "%s" pong', name);
self.upgrading = true;
self.emit('upgrading', transport);
if (!transport) return;
Socket.priorWebsocketSuccess = 'websocket' == transport.name;
debug('pausing current transport "%s"', self.transport.name);
self.transport.pause(function () {
if (failed) return;
if ('closed' == self.readyState) return;
debug('changing transport and sending upgrade packet');
cleanup();
self.setTransport(transport);
transport.send([{ type: 'upgrade', options: { compress: true } }]);
self.emit('upgrade', transport);
transport = null;
self.upgrading = false;
self.flush();
});
} else {
debug('probe transport "%s" failed', name);
var err = new Error('probe error');
err.transport = transport.name;
self.emit('upgradeError', err);
}
});
}
function freezeTransport() {
if (failed) return;
// Any callback called by transport should be ignored since now
failed = true;
cleanup();
transport.close();
transport = null;
}
//Handle any error that happens while probing
function onerror(err) {
var error = new Error('probe error: ' + err);
error.transport = transport.name;
freezeTransport();
debug('probe transport "%s" failed because of error: %s', name, err);
self.emit('upgradeError', error);
}
function onTransportClose(){
onerror("transport closed");
}
//When the socket is closed while we're probing
function onclose(){
onerror("socket closed");
}
//When the socket is upgraded while we're probing
function onupgrade(to){
if (transport && to.name != transport.name) {
debug('"%s" works - aborting "%s"', to.name, transport.name);
freezeTransport();
}
}
//Remove all listeners on the transport and on self
function cleanup(){
transport.removeListener('open', onTransportOpen);
transport.removeListener('error', onerror);
transport.removeListener('close', onTransportClose);
self.removeListener('close', onclose);
self.removeListener('upgrading', onupgrade);
}
transport.once('open', onTransportOpen);
transport.once('error', onerror);
transport.once('close', onTransportClose);
this.once('close', onclose);
this.once('upgrading', onupgrade);
transport.open();
};
this.onOpen = function () {
debug('socket open');
this.readyState = 'open';
Socket.priorWebsocketSuccess = 'websocket' == this.transport.name;
this.emit('open');
this.flush();
// we check for `readyState` in case an `open`
// listener already closed the socket
if ('open' == this.readyState && this.upgrade && this.transport.pause) {
debug('starting upgrade probes');
for (var i = 0, l = this.upgrades.length; i < l; i++) {
this.probe(this.upgrades[i]);
}
}
};
this.onPacket = function (packet) {
if ('opening' == this.readyState || 'open' == this.readyState) {
debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
this.emit('packet', packet);
// Socket is live - any packet counts
this.emit('heartbeat');
switch (packet.type) {
case 'open':
this.onHandshake(parsejson(packet.data));
break;
case 'pong':
this.setPing();
this.emit('pong');
break;
case 'error':
var err = new Error('server error');
err.code = packet.data;
this.onError(err);
break;
case 'message':
this.emit('data', packet.data);
this.emit('message', packet.data);
break;
}
} else {
debug('packet received with socket readyState "%s"', this.readyState);
}
};
this.onHandshake = function (data) {
this.emit('handshake', data);
this.id = data.sid;
this.transport.query.sid = data.sid;
this.upgrades = this.filterUpgrades(data.upgrades);
this.pingInterval = data.pingInterval;
this.pingTimeout = data.pingTimeout;
this.onOpen();
// In case open handler closes socket
if ('closed' == this.readyState) return;
this.setPing();
// Prolong liveness of socket on heartbeat
this.removeListener('heartbeat', this.onHeartbeat);
this.on('heartbeat', this.onHeartbeat);
};
this.onHeartbeat = function (timeout) {
clearTimeout(this.pingTimeoutTimer);
var self = this;
self.pingTimeoutTimer = setTimeout(function () {
if ('closed' == self.readyState) return;
self.onClose('ping timeout');
}, timeout || (self.pingInterval + self.pingTimeout));
};
this.setPing = function () {
var self = this;
clearTimeout(self.pingIntervalTimer);
self.pingIntervalTimer = setTimeout(function () {
debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
self.ping();
self.onHeartbeat(self.pingTimeout);
}, self.pingInterval);
};
this.ping = function () {
var self = this;
this.sendPacket('ping', function(){
self.emit('ping');
});
};
this.onDrain = function() {
for (var i = 0; i < this.prevBufferLen; i++) {
if (this.callbackBuffer[i]) {
this.callbackBuffer[i]();
}
}
this.writeBuffer.splice(0, this.prevBufferLen);
this.callbackBuffer.splice(0, this.prevBufferLen);
// setting prevBufferLen = 0 is very important
// for example, when upgrading, upgrade packet is sent over,
// and a nonzero prevBufferLen could cause problems on `drain`
this.prevBufferLen = 0;
if (0 === this.writeBuffer.length) {
this.emit('drain');
} else {
this.flush();
}
};
this.flush = function () {
if ('closed' != this.readyState && this.transport.writable &&
!this.upgrading && this.writeBuffer.length) {
debug('flushing %d packets in socket', this.writeBuffer.length);
this.transport.send(this.writeBuffer);
// keep track of current length of writeBuffer
// splice writeBuffer and callbackBuffer on `drain`
this.prevBufferLen = this.writeBuffer.length;
this.emit('flush');
}
};
this.write =
this.send = function (msg, options, fn) {
this.sendPacket('message', msg, options, fn);
return this;
};
this.sendPacket = function (type, data, options, fn) {
if('function' == typeof data) {
fn = data;
data = undefined;
}
if ('function' == typeof options) {
fn = options;
options = null;
}
if ('closing' == this.readyState || 'closed' == this.readyState) {
return;
}
options = options || {};
options.compress = false !== options.compress;
var packet = {
type: type,
data: data,
options: options
};
this.emit('packetCreate', packet);
this.writeBuffer.push(packet);
this.callbackBuffer.push(fn);
this.flush();
};
/**
* Closes the connection.
*
* @api private
*/
this.close = function () {
if ('opening' == this.readyState || 'open' == this.readyState) {
this.readyState = 'closing';
var self = this;
if (this.writeBuffer.length) {
this.once('drain', function() {
if (this.upgrading) {
waitForUpgrade();
} else {
close();
}
});
} else if (this.upgrading) {
waitForUpgrade();
} else {
close();
}
}
function close() {
self.onClose('forced close');
debug('socket closing - telling transport to close');
self.transport.close();
}
function cleanupAndClose() {
self.removeListener('upgrade', cleanupAndClose);
self.removeListener('upgradeError', cleanupAndClose);
close();
}
function waitForUpgrade() {
// wait for upgrade to finish since we can't send packets while pausing a transport
self.once('upgrade', cleanupAndClose);
self.once('upgradeError', cleanupAndClose);
}
return this;
};
this.onError = function (err) {
debug('socket error %j', err);
Socket.priorWebsocketSuccess = false;
this.emit('error', err);
this.onClose('transport error', err);
};
this.onClose = function (reason, desc) {
if ('opening' == this.readyState || 'open' == this.readyState || 'closing' == this.readyState) {
debug('socket close with reason: "%s"', reason);
var self = this;
// clear timers
clearTimeout(this.pingIntervalTimer);
clearTimeout(this.pingTimeoutTimer);
// clean buffers in next tick, so developers can still
// grab the buffers on `close` event
setTimeout(function() {
self.writeBuffer = [];
self.callbackBuffer = [];
self.prevBufferLen = 0;
}, 0);
// stop event from firing again for transport
this.transport.removeAllListeners('close');
// ensure transport won't stay open
this.transport.close();
// ignore further transport communication
this.transport.removeAllListeners();
// set ready state
this.readyState = 'closed';
// clear session id
this.id = null;
// emit close event
this.emit('close', reason, desc);
}
};
this.filterUpgrades = function (upgrades) {
var filteredUpgrades = [];
for (var i = 0, j = upgrades.length; i<j; i++) {
if (~this.transports.indexOf(upgrades[i])) filteredUpgrades.push(upgrades[i]);
}
return filteredUpgrades;
};
}).call(Socket.prototype);
});
})(typeof define === "function"? define: function(name, reqs, factory) { factory(require, exports, module); });
(function(define) {'use strict'
define("latte_io/parser.js", ["require", "exports", "module", "window"],
function(require, exports, module, window) {
var latte_lib = require("latte_lib");
var utf8 = latte_lib.utf8;
var noop = function() {};
var after = function(count, callback, err_cb) {
var bail = false
err_cb = err_cb || noop
proxy.count = count
return (count === 0) ? callback() : proxy
function proxy(err, result) {
if (proxy.count <= 0) {
throw new Error('after called too many times')
}
--proxy.count
// after first error, rest are passed to err_cb
if (err) {
bail = true
callback(err)
// future error callbacks will go to error handler
callback = err_cb
} else if (proxy.count === 0 && !bail) {
callback(null, result)
}
}
};
var keys = Object.keys;
if(!window) {
var keys = Object.keys;
var err = { type: 'error', data: 'parser error' };
(function() {
this.protocol = 3;
var packets = this.packets = {
open: 0 // non-ws
, close: 1 // non-ws
, ping: 2
, pong: 3
, message: 4
, upgrade: 5
, noop: 6
};
var packetslist = keys(packets);
this.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
if ('function' == typeof supportsBinary) {
callback = supportsBinary;
supportsBinary = null;
}
if ('function' == typeof utf8encode ) {
callback = utf8encode;
utf8encode = null;
}
var data = (packet.data === undefined)
? undefined
: packet.data.buffer || packet.data;
if (Buffer.isBuffer(data)) {
return encodeBuffer(packet, supportsBinary, callback);
} else if (data instanceof ArrayBuffer) {
return encodeArrayBuffer(packet, supportsBinary, callback);
}
// Sending data as a utf-8 string
var encoded = packets[packet.type];
// data fragment is optional
if (undefined !== packet.data) {
encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
}
return callback('' + encoded);
};
function encodeBuffer(packet, supportsBinary, callback) {
var data = packet.data;
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var typeBuffer = new Buffer(1);
typeBuffer[0] = packets[packet.type];
return callback(Buffer.concat([typeBuffer, data]));
}
function encodeArrayBuffer(packet, supportsBinary, callback) {
var data = (packet.data === undefined)
? undefined
: packet.data.buffer || packet.data;
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var contentArray = new Uint8Array(data);
var resultBuffer = new Buffer(1 + data.byteLength);
resultBuffer[0] = packets[packet.type];
for (var i = 0; i < contentArray.length; i++){
resultBuffer[i+1] = contentArray[i];
}
return callback(resultBuffer);
}
this.encodeBase64Packet = function(packet, callback){
var data = packet.data.buffer || packet.data;
if (data instanceof ArrayBuffer) {
var buf = new Buffer(data.byteLength);
for (var i = 0; i < buf.length; i++) {
buf[i] = data[i];
}
packet.data = buf;
}
var message = 'b' + packets[packet.type];
message += packet.data.toString('base64');
return callback(message);
};
this.decodePacket = function (data, binaryType, utf8decode) {
// String data
if (typeof data == 'string' || data === undefined) {
if (data.charAt(0) == 'b') {
return exports.decodeBase64Packet(data.substr(1), binaryType);
}
var type = data.charAt(0);
if (utf8decode) {
try {
data = utf8.decode(data);
} catch (e) {
return err;
}
}
if (Number(type) != type || !packetslist[type]) {
return err;
}
if (data.length > 1) {
return { type: packetslist[type], data: data.substring(1) };
} else {
return { type: packetslist[type] };
}
}
// Binary data
if (binaryType === 'arraybuffer') {
var type = data[0];
var intArray = new Uint8Array(data.length - 1);
for (var i = 1; i < data.length; i++) {
intArray[i - 1] = data[i];
}
return { type: packetslist[type], data: intArray.buffer };
}
var type = data[0];
return { type: packetslist[type], data: data.slice(1) };
};
this.decodeBase64Packet = function(msg, binaryType) {
var type = packetslist[msg.charAt(0)];
var data = new Buffer(msg.substr(1), 'base64');
if (binaryType === 'arraybuffer') {
var abv = new Uint8Array(data.length);
for (var i = 0; i < abv.length; i++){
abv[i] = data[i];
}
data = abv.buffer;
}
return { type: type, data: data };
};
this.encodePayload = function (packets, supportsBinary, callback) {
if (typeof supportsBinary == 'function') {
callback = supportsBinary;
supportsBinary = null;
}
if (supportsBinary) {
return exports.encodePayloadAsBinary(packets, callback);
}
if (!packets.length) {
return callback('0:');
}
function setLengthHeader(message) {
return message.length + ':' + message;
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, supportsBinary, true, function(message) {
doneCallback(null, setLengthHeader(message));
});
}
map(packets, encodeOne, function(err, results) {
return callback(results.join(''));
});
};
function map(ary, each, done) {
var result = new Array(ary.length);
var next = after(ary.length, done);
var eachWithIndex = function(i, el, cb) {
each(el, function(error, msg) {
result[i] = msg;
cb(error, result);
});
};
for (var i = 0; i < ary.length; i++) {
eachWithIndex(i, ary[i], next);
}
}
this.decodePayload = function (data, binaryType, callback) {
if ('string' != typeof data) {
return exports.decodePayloadAsBinary(data, binaryType, callback);
}
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var packet;
if (data == '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
var length = ''
, n, msg;
for (var i = 0, l = data.length; i < l; i++) {
var chr = data.charAt(i);
if (':' != chr) {
length += chr;
} else {
if ('' == length || (length != (n = Number(length)))) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
msg = data.substr(i + 1, n);
if (length != msg.length) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
if (msg.length) {
packet = exports.decodePacket(msg, binaryType, true);
if (err.type == packet.type && err.data == packet.data) {
// parser error in individual packet - ignoring payload
return callback(err, 0, 1);
}
var ret = callback(packet, i + n, l);
if (false === ret) return;
}
// advance cursor
i += n;
length = '';
}
}
if (length != '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
};
function bufferToString(buffer) {
var str = '';
for (var i = 0; i < buffer.length; i++) {
str += String.fromCharCode(buffer[i]);
}
return str;
}
function stringToBuffer(string) {
var buf = new Buffer(string.length);
for (var i = 0; i < string.length; i++) {
buf.writeUInt8(string.charCodeAt(i), i);
}
return buf;
}
this.encodePayloadAsBinary = function (packets, callback) {
if (!packets.length) {
return callback(new Buffer(0));
}
function encodeOne(p, doneCallback) {
exports.encodePacket(p, true, true, function(packet) {
if (typeof packet === 'string') {
var encodingLength = '' + packet.length;
var sizeBuffer = new Buffer(encodingLength.length + 2);
sizeBuffer[0] = 0; // is a string (not true binary = 0)
for (var i = 0; i < encodingLength.length; i++) {
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
}
sizeBuffer[sizeBuffer.length - 1] = 255;
return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)]));
}
var encodingLength = '' + packet.length;
var sizeBuffer = new Buffer(encodingLength.length + 2);
sizeBuffer[0] = 1; // is binary (true binary = 1)
for (var i = 0; i < encodingLength.length; i++) {
sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
}
sizeBuffer[sizeBuffer.length - 1] = 255;
doneCallback(null, Buffer.concat([sizeBuffer, packet]));
});
}
map(packets, encodeOne, function(err, results) {
return callback(Buffer.concat(results));
});
};
this.decodePayloadAsBinary = function (data, binaryType, callback) {
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var bufferTail = data;
var buffers = [];
while (bufferTail.length > 0) {
var strLen = '';
var isString = bufferTail[0] === 0;
var numberTooLong = false;
for (var i = 1; ; i++) {
if (bufferTail[i] == 255) break;
// 310 = char length of Number.MAX_VALUE
if (strLen.length > 310) {
numberTooLong = true;
break;
}
strLen += '' + bufferTail[i];
}
if(numberTooLong) return callback(err, 0, 1);
bufferTail = bufferTail.slice(strLen.length + 1);
var msgLength = parseInt(strLen, 10);
var msg = bufferTail.slice(1, msgLength + 1);
if (isString) msg = bufferToString(msg);
buffers.push(msg);
bufferTail = bufferTail.slice(msgLength + 1);
}
var total = buffers.length;
buffers.forEach(function(buffer, i) {
callback(exports.decodePacket(buffer, binaryType, true), i, total);
});
};
}).call(module.exports);
}else{
var hasBinary = function hasBinary(data) {
function _hasBinary(obj) {
if (!obj) return false;
if ( (window.Buffer && window.Buffer.isBuffer(obj)) ||
(window.ArrayBuffer && obj instanceof ArrayBuffer) ||
(window.Blob && obj instanceof window.Blob) ||
(window.File && obj instanceof window.File)
) {
return true;
}
if (latte_lib.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
if (_hasBinary(obj[i])) {
return true;
}
}
} else if (obj && 'object' == typeof obj) {
if (obj.toJSON) {
obj = obj.toJSON();
}
for (var key in obj) {
if (obj.hasOwnProperty(key) && _hasBinary(obj[key])) {
return true;
}
}
}
return false;
}
return _hasBinary(data);
};
var sliceBuffer = function(arraybuffer, start, end) {
var bytes = arraybuffer.byteLength;
start = start || 0;
end = end || bytes;
if (arraybuffer.slice) { return arraybuffer.slice(start, end); }
if (start < 0) { start += bytes; }
if (end < 0) { end += bytes; }
if (end > bytes) { end = bytes; }
if (start >= bytes || start >= end || bytes === 0) {
return new ArrayBuffer(0);
}
var abv = new Uint8Array(arraybuffer);
var result = new Uint8Array(end - start);
for (var i = start, ii = 0; i < end; i++, ii++) {
result[ii] = abv[i];
}
return result.buffer;
};
var base64encoder = latte_lib.base64;
/**
* Check if we are running an android browser. That requires us to use
* ArrayBuffer with polling transports...
*
* http://ghinda.net/jpeg-blob-ajax-android/
*/
var isAndroid = navigator.userAgent.match(/Android/i);
/**
* Check if we are running in PhantomJS.
* Uploading a Blob with PhantomJS does not work correctly, as reported here:
* https://github.com/ariya/phantomjs/issues/11395
* @type boolean
*/
var isPhantomJS = /PhantomJS/i.test(navigator.userAgent);
/**
* When true, avoids using Blobs to encode payloads.
* @type boolean
*/
var dontSendBlobs = isAndroid || isPhantomJS;
/**
* Current protocol version.
*/
/**
* Premade error packet.
*/
var err = { type: 'error', data: 'parser error' };
/**
* Create a blob api even for blob builder when vendor prefixes exist
*/
var BlobBuilder = window.BlobBuilder
|| window.WebKitBlobBuilder
|| window.MSBlobBuilder
|| window.MozBlobBuilder;
/**
* Check if Blob constructor is supported
*/
var Blob = (function() {
if (blobSupported) {
return window.Blob;
} else if (blobBuilderSupported) {
return BlobBuilderConstructor;
} else {
return undefined;
}
})();
var blobSupported = (function() {
try {
var b = new window.Blob(['hi']);
return b.size == 2;
} catch(e) {
return false;
}
})();
/**
* Check if BlobBuilder is supported
*/
var blobBuilderSupported = BlobBuilder
&& BlobBuilder.prototype.append
&& BlobBuilder.prototype.getBlob;
var BlobBuilderConstructor = function (ary, options) {
options = options || {};
var bb = new BlobBuilder();
for (var i = 0; i < ary.length; i++) {
bb.append(ary[i]);
}
return (options.type) ? bb.getBlob(options.type) : bb.getBlob();
};
(function() {
this.protocol = 3;
var packets = this.packets = {
open: 0 // non-ws
, close: 1 // non-ws
, ping: 2
, pong: 3
, message: 4
, upgrade: 5
, noop: 6
};
var packetslist = keys(packets);
this.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
if ('function' == typeof supportsBinary) {
callback = supportsBinary;
supportsBinary = false;
}
if ('function' == typeof utf8encode) {
callback = utf8encode;
utf8encode = null;
}
var data = (packet.data === undefined)
? undefined
: packet.data.buffer || packet.data;
if (window.ArrayBuffer && data instanceof ArrayBuffer) {
return encodeArrayBuffer(packet, supportsBinary, callback);
} else if (Blob && data instanceof window.Blob) {
return encodeBlob(packet, supportsBinary, callback);
}
// might be an object with { base64: true, data: dataAsBase64String }
if (data && data.base64) {
return encodeBase64Object(packet, callback);
}
// Sending data as a utf-8 string
var encoded = packets[packet.type];
// data fragment is optional
if (undefined !== packet.data) {
encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
}
return callback('' + encoded);
};
var encodeBase64Object = function (packet, callback) {
// packet data is an object { base64: true, data: dataAsBase64String }
var message = 'b' + exports.packets[packet.type] + packet.data.data;
return callback(message);
}
var encodeArrayBuffer = function (packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var data = packet.data;
var contentArray = new Uint8Array(data);
var resultBuffer = new Uint8Array(1 + data.byteLength);
resultBuffer[0] = packets[packet.type];
for (var i = 0; i < contentArray.length; i++) {
resultBuffer[i+1] = contentArray[i];
}
return callback(resultBuffer.buffer);
}
var encodeBlobAsArrayBuffer = function (packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
var fr = new FileReader();
fr.onload = function() {
packet.data = fr.result;
exports.encodePacket(packet, supportsBinary, true, callback);
};
return fr.readAsArrayBuffer(packet.data);
}
var encodeBlob = function (packet, supportsBinary, callback) {
if (!supportsBinary) {
return exports.encodeBase64Packet(packet, callback);
}
if (dontSendBlobs) {
return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
}
var length = new Uint8Array(1);
length[0] = packets[packet.type];
var blob = new Blob([length.buffer, packet.data]);
return callback(blob);
}
var _self = this;
this.encodeBase64Packet = function(packet, callback) {
var message = 'b' + _self.packets[packet.type];
if (Blob && packet.data instanceof Blob) {
var fr = new FileReader();
fr.onload = function() {
var b64 = fr.result.split(',')[1];
callback(message + b64);
};
return fr.readAsDataURL(packet.data);
}
var b64data;
try {
b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
} catch (e) {
// iPhone Safari doesn't let you apply with typed arrays
var typed = new Uint8Array(packet.data);
var basic = new Array(typed.length);
for (var i = 0; i < typed.length; i++) {
basic[i] = typed[i];
}
b64data = String.fromCharCode.apply(null, basic);
}
message += window.btoa(b64data);
return callback(message);
};
this.decodePacket = function (data, binaryType, utf8decode) {
// String data
if (typeof data == 'string' || data === undefined) {
if (data.charAt(0) == 'b') {
return exports.decodeBase64Packet(data.substr(1), binaryType);
}
if (utf8decode) {
try {
data = utf8.decode(data);
} catch (e) {
return err;
}
}
var type = data.charAt(0);
if (Number(type) != type || !packetslist[type]) {
return err;
}
if (data.length > 1) {
return { type: packetslist[type], data: data.substring(1) };
} else {
return { type: packetslist[type] };
}
}
var asArray = new Uint8Array(data);
var type = asArray[0];
var rest = sliceBuffer(data, 1);
if (Blob && binaryType === 'blob') {
rest = new Blob([rest]);
}
return { type: packetslist[type], data: rest };
};
this.decodeBase64Packet = function(msg, binaryType) {
var type = packetslist[msg.charAt(0)];
if (!window.ArrayBuffer) {
return { type: type, data: { base64: true, data: msg.substr(1) } };
}
var data = base64encoder.decode(msg.substr(1));
if (binaryType === 'blob' && Blob) {
data = new Blob([data]);
}
return { type: type, data: data };
};
var map = function (ary, each, done) {
var result = new Array(ary.length);
var next = after(ary.length, done);
var eachWithIndex = function(i, el, cb) {
each(el, function(error, msg) {
result[i] = msg;
cb(error, result);
});
};
for (var i = 0; i < ary.length; i++) {
eachWithIndex(i, ary[i], next);
}
}
this.encodePayload = function (packets, supportsBinary, callback) {
if (typeof supportsBinary == 'function') {
callback = supportsBinary;
supportsBinary = null;
}
var isBinary = hasBinary(packets);
if (supportsBinary && isBinary) {
if (Blob && !dontSendBlobs) {
return exports.encodePayloadAsBlob(packets, callback);
}
return exports.encodePayloadAsArrayBuffer(packets, callback);
}
if (!packets.length) {
return callback('0:');
}
function setLengthHeader(message) {
return message.length + ':' + message;
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) {
doneCallback(null, setLengthHeader(message));
});
}
map(packets, encodeOne, function(err, results) {
return callback(results.join(''));
});
};
this.decodePayload = function (data, binaryType, callback) {
if (typeof data != 'string') {
return exports.decodePayloadAsBinary(data, binaryType, callback);
}
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var packet;
if (data == '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
var length = ''
, n, msg;
for (var i = 0, l = data.length; i < l; i++) {
var chr = data.charAt(i);
if (':' != chr) {
length += chr;
} else {
if ('' == length || (length != (n = Number(length)))) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
msg = data.substr(i + 1, n);
if (length != msg.length) {
// parser error - ignoring payload
return callback(err, 0, 1);
}
if (msg.length) {
packet = exports.decodePacket(msg, binaryType, true);
if (err.type == packet.type && err.data == packet.data) {
// parser error in individual packet - ignoring payload
return callback(err, 0, 1);
}
var ret = callback(packet, i + n, l);
if (false === ret) return;
}
// advance cursor
i += n;
length = '';
}
}
if (length != '') {
// parser error - ignoring payload
return callback(err, 0, 1);
}
};
this.encodePayloadAsArrayBuffer = function(packets, callback) {
if (!packets.length) {
return callback(new ArrayBuffer(0));
}
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, true, true, function(data) {
return doneCallback(null, data);
});
}
map(packets, encodeOne, function(err, encodedPackets) {
var totalLength = encodedPackets.reduce(function(acc, p) {
var len;
if (typeof p === 'string'){
len = p.length;
} else {
len = p.byteLength;
}
return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
}, 0);
var resultArray = new Uint8Array(totalLength);
var bufferIndex = 0;
encodedPackets.forEach(function(p) {
var isString = typeof p === 'string';
var ab = p;
if (isString) {
var view = new Uint8Array(p.length);
for (var i = 0; i < p.length; i++) {
view[i] = p.charCodeAt(i);
}
ab = view.buffer;
}
if (isString) { // not true binary
resultArray[bufferIndex++] = 0;
} else { // true binary
resultArray[bufferIndex++] = 1;
}
var lenStr = ab.byteLength.toString();
for (var i = 0; i < lenStr.length; i++) {
resultArray[bufferIndex++] = parseInt(lenStr[i]);
}
resultArray[bufferIndex++] = 255;
var view = new Uint8Array(ab);
for (var i = 0; i < view.length; i++) {
resultArray[bufferIndex++] = view[i];
}
});
return callback(resultArray.buffer);
});
};
this.encodePayloadAsBlob = function(packets, callback) {
function encodeOne(packet, doneCallback) {
exports.encodePacket(packet, true, true, function(encoded) {
var binaryIdentifier = new Uint8Array(1);
binaryIdentifier[0] = 1;
if (typeof encoded === 'string') {
var view = new Uint8Array(encoded.length);
for (var i = 0; i < encoded.length; i++) {
view[i] = encoded.charCodeAt(i);
}
encoded = view.buffer;
binaryIdentifier[0] = 0;
}
var len = (encoded instanceof ArrayBuffer)
? encoded.byteLength
: encoded.size;
var lenStr = len.toString();
var lengthAry = new Uint8Array(lenStr.length + 1);
for (var i = 0; i < lenStr.length; i++) {
lengthAry[i] = parseInt(lenStr[i]);
}
lengthAry[lenStr.length] = 255;
if (Blob) {
var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
doneCallback(null, blob);
}
});
}
map(packets, encodeOne, function(err, results) {
return callback(new Blob(results));
});
};
this.decodePayloadAsBinary = function (data, binaryType, callback) {
if (typeof binaryType === 'function') {
callback = binaryType;
binaryType = null;
}
var bufferTail = data;
var buffers = [];
var numberTooLong = false;
while (bufferTail.byteLength > 0) {
var tailArray = new Uint8Array(bufferTail);
var isString = tailArray[0] === 0;
var msgLength = '';
for (var i = 1; ; i++) {
if (tailArray[i] == 255) break;
if (msgLength.length > 310) {
numberTooLong = true;
break;
}
msgLength += tailArray[i];
}
if(numberTooLong) return callback(err, 0, 1);
bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
msgLength = parseInt(msgLength);
var msg = sliceBuffer(bufferTail, 0, msgLength);
if (isString) {
try {
msg = String.fromCharCode.apply(null, new Uint8Array(msg));
} catch (e) {
// iPhone Safari doesn't let you apply to typed arrays
var typed = new Uint8Array(msg);
msg = '';
for (var i = 0; i < typed.length; i++) {
msg += String.fromCharCode(typed[i]);
}
}
}
buffers.push(msg);
bufferTail = sliceBuffer(bufferTail, msgLength);
}
var total = buffers.length;
buffers.forEach(function(buffer, i) {
callback(exports.decodePacket(buffer, binaryType, true), i, total);
});
};
}).call(module.exports);
}
});
})(typeof define === "function"? define: function(name, reqs, factory) { factory(require, exports, module); });
(function(define) {'use strict'
define("latte_io/rpcSocket.js", ["require", "exports", "module", "window"],
function(require, exports, module, window) {
var latte_lib = require("latte_lib");
function Socket(url, opts) {
this.buffers = [];
this.methods = {};
this.id = 0;
this.url = url;
this.opts = opts;
this.open();
this.isRestart = 1;
};
latte_lib.inherits(Socket, latte_lib.events);
(function() {
this.close = function() {
this.isRestart = 0;
this.socket.close();
}
this.open = function() {
var Socket = require("./index");
var socket = this.socket = new Socket(this.url, this.opts);
var self = this;
socket.on("open", function() {
self.emit("open");
var buffer ;
while(buffer = self.buffers.unshift()) {
self.write(buffer);
}
});
socket.on("close", function() {
self.socket = null;
self.emit("close");
if(self.isRestart){
self.open();
}
});
socket.on("error", function(error) {
console.log(error);
});
socket.on("message", function(data) {
self.onData(data);
});
}
this.send = function(handle, params, callback) {
var self = this;
console.log(handle, params);
if(this.socket) {
this.write({
method: handle,
params: params,
id: ++self.id
});
}else{
this.buffers.push({
method: handle,
params: params,
id: ++self.id
});
}
callback && this.once(self.id, callback);
}
var backData = function(err, result, id) {
return {
error: err,
result: result,
id: id
};
}
this.write = function(data) {
this.socket.send(JSON.stringify(data));
}
this.Set = function(method, fn) {
this.methods[method] = fn;
}
this.onData = function(data) {
data = JSON.parse(data)
var self = this;
if(data.method) {
var method = self.methods[data.method];
if(method) {
if(!latte_lib.isArray(data.params)) {
data.params = [].concat(data.params);
}
data.params.push(function(err, result){
self.write(backData(err, result, data.id));
});
method.apply(self, data.params);
}
}else if(data.id) {
self.emit(data.id, data.error, data.result);
}else if(data.code) {
self.emit("error", data);
self.close();
}
}
}).call(Socket.prototype);
module.exports = Socket;
});
})(typeof define === "function"? define: function(name, reqs, factory) { factory(require, exports, module); });
(function(define) {'use strict'
define("latte_io/transport.js", ["require", "exports", "module", "window"],
function(require, exports, module, window) {
/**
* Module dependencies.
*/
var parser = require('./parser');
var latte_lib = require("latte_lib");
/**
* Module exports.
*/
module.exports = Transport;
/**
* Transport abstract constructor.
*
* @param {Object} options.
* @api private
*/
function Transport (opts) {
this.path = opts.path;
this.hostname = opts.hostname;
t