UNPKG

latte_web

Version:
1,763 lines (1,478 loc) 101 kB
(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