From 6ab4b1bab2164e0cb539a59b72362651f4d44d23 Mon Sep 17 00:00:00 2001 From: Darren Date: Wed, 4 Jun 2014 14:55:04 +0100 Subject: [PATCH] engine.io update --- client/assets/libs/engine.io.js | 4588 ++++++++++++++++--------------- package.json | 2 +- 2 files changed, 2434 insertions(+), 2156 deletions(-) diff --git a/client/assets/libs/engine.io.js b/client/assets/libs/engine.io.js index f38b4df..635f33e 100644 --- a/client/assets/libs/engine.io.js +++ b/client/assets/libs/engine.io.js @@ -1,1876 +1,1412 @@ -;(function(){ +!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.eio=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o [ `:` ] - * - * Example: - * - * 5:hello world - * 3 - * 4 + * Sends a packet. * + * @param {String} packet type. + * @param {String} data. + * @param {Function} callback function. * @api private */ -exports.encodePacket = function (packet) { - var encoded = packets[packet.type]; - - // data fragment is optional - if (undefined !== packet.data) { - encoded += String(packet.data); - } - - return '' + encoded; +Socket.prototype.sendPacket = function (type, data, fn) { + var packet = { type: type, data: data }; + this.emit('packetCreate', packet); + this.writeBuffer.push(packet); + this.callbackBuffer.push(fn); + this.flush(); }; /** - * Decodes a packet. + * Closes the connection. * - * @return {Object} with `type` and `data` (if any) * @api private */ -exports.decodePacket = function (data) { - var type = data.charAt(0); - - if (Number(type) != type || !packetslist[type]) { - return err; +Socket.prototype.close = function () { + if ('opening' == this.readyState || 'open' == this.readyState) { + this.onClose('forced close'); + debug('socket closing - telling transport to close'); + this.transport.close(); } - if (data.length > 1) { - return { type: packetslist[type], data: data.substring(1) }; - } else { - return { type: packetslist[type] }; - } + return this; }; /** - * Encodes multiple messages (payload). + * Called upon transport error * - * :data - * - * Example: - * - * 11:hello world2:hi - * - * @param {Array} packets * @api private */ -exports.encodePayload = function (packets) { - if (!packets.length) { - return '0:'; - } - - var encoded = ''; - var message; - - for (var i = 0, l = packets.length; i < l; i++) { - message = exports.encodePacket(packets[i]); - encoded += message.length + ':' + message; - } - - return encoded; +Socket.prototype.onError = function (err) { + debug('socket error %j', err); + Socket.priorWebsocketSuccess = false; + this.emit('error', err); + this.onClose('transport error', err); }; -/* - * Decodes data when a payload is maybe expected. +/** + * Called upon transport close. * - * @param {String} data, callback method - * @api public + * @api private */ -exports.decodePayload = function (data, callback) { - 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); +Socket.prototype.onClose = function (reason, desc) { + if ('opening' == this.readyState || 'open' == this.readyState) { + debug('socket close with reason: "%s"', reason); + var self = this; - if (':' != chr) { - length += chr; - } else { - if ('' == length || (length != (n = Number(length)))) { - // parser error - ignoring payload - return callback(err, 0, 1); - } + // clear timers + clearTimeout(this.pingIntervalTimer); + clearTimeout(this.pingTimeoutTimer); - msg = data.substr(i + 1, n); + // 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); - if (length != msg.length) { - // parser error - ignoring payload - return callback(err, 0, 1); - } + // stop event from firing again for transport + this.transport.removeAllListeners('close'); - if (msg.length) { - packet = exports.decodePacket(msg); + // ensure transport won't stay open + this.transport.close(); - if (err.type == packet.type && err.data == packet.data) { - // parser error in individual packet - ignoring payload - return callback(err, 0, 1); - } + // ignore further transport communication + this.transport.removeAllListeners(); - var ret = callback(packet, i + n, l); - if (false === ret) return; - } + // set ready state + this.readyState = 'closed'; - // advance cursor - i += n; - length = ''; - } - } + // clear session id + this.id = null; - if (length != '') { - // parser error - ignoring payload - return callback(err, 0, 1); + // emit close event + this.emit('close', reason, desc); } - }; -}); -require.register("LearnBoost-engine.io-protocol/lib/keys.js", function(exports, require, module){ - /** - * Gets the keys for an object. + * Filters upgrades, returning only those matching client transports. * - * @return {Array} keys + * @param {Array} server upgrades * @api private + * */ -module.exports = Object.keys || function keys (obj){ - var arr = []; - var has = Object.prototype.hasOwnProperty; - - for (var i in obj) { - if (has.call(obj, i)) { - arr.push(i); - } +Socket.prototype.filterUpgrades = function (upgrades) { + var filteredUpgrades = []; + for (var i = 0, j = upgrades.length; i= hour) return (ms / hour).toFixed(1) + 'h'; - if (ms >= min) return (ms / min).toFixed(1) + 'm'; - if (ms >= sec) return (ms / sec | 0) + 's'; - return ms + 'ms'; + return this; }; /** - * Returns true if the given mode name is enabled, false otherwise. + * Sends multiple packets. * - * @param {String} name - * @return {Boolean} - * @api public + * @param {Array} packets + * @api private */ -debug.enabled = function(name) { - for (var i = 0, len = debug.skips.length; i < len; i++) { - if (debug.skips[i].test(name)) { - return false; - } - } - for (var i = 0, len = debug.names.length; i < len; i++) { - if (debug.names[i].test(name)) { - return true; - } +Transport.prototype.send = function(packets){ + if ('open' == this.readyState) { + this.write(packets); + } else { + throw new Error('Transport not open'); } - return false; }; /** - * Coerce `val`. + * Called upon open + * + * @api private */ -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} - -// persist - -if (window.localStorage) debug.enable(localStorage.debug); - -}); -require.register("engine.io/lib/index.js", function(exports, require, module){ - -module.exports = require('./socket'); +Transport.prototype.onOpen = function () { + this.readyState = 'open'; + this.writable = true; + this.emit('open'); +}; /** - * Exports parser - * - * @api public + * Called with data. * + * @param {String} data + * @api private */ -module.exports.parser = require('engine.io-parser'); -}); -require.register("engine.io/lib/socket.js", function(exports, require, module){ +Transport.prototype.onData = function (data) { + this.onPacket(parser.decodePacket(data, this.socket.binaryType)); +}; + /** - * Module dependencies. + * Called with a decoded packet. */ -var util = require('./util') - , transports = require('./transports') - , Emitter = require('./emitter') - , debug = require('debug')('engine-client:socket') - , index = require('indexof') - , parser = require('engine.io-parser'); +Transport.prototype.onPacket = function (packet) { + this.emit('packet', packet); +}; /** - * Module exports. + * Called upon close. + * + * @api private */ -module.exports = Socket; +Transport.prototype.onClose = function () { + this.readyState = 'closed'; + this.emit('close'); +}; +},{"emitter":13,"engine.io-parser":14}],5:[function(_dereq_,module,exports){ +(function (global){ /** - * Global reference. + * Module dependencies */ -var global = util.global(); +var XMLHttpRequest = _dereq_('xmlhttprequest'); +var XHR = _dereq_('./polling-xhr'); +var JSONP = _dereq_('./polling-jsonp'); +var websocket = _dereq_('./websocket'); /** - * Noop function. - * - * @api private + * Export transports. */ -function noop () {}; +exports.polling = polling; +exports.websocket = websocket; /** - * Socket constructor. + * Polling transport polymorphic constructor. + * Decides on xhr vs jsonp based on feature detection. * - * @param {String|Object} uri or options - * @param {Object} options - * @api public + * @api private */ -function Socket(uri, opts){ - if (!(this instanceof Socket)) return new Socket(uri, opts); +function polling(opts){ + var xhr; + var xd = false; - opts = opts || {}; + if (global.location) { + var isSSL = 'https:' == location.protocol; + var port = location.port; - if ('object' == typeof uri) { - opts = uri; - uri = null; - } + // some user agents have empty `location.port` + if (!port) { + port = isSSL ? 443 : 80; + } - if (uri) { - uri = util.parseUri(uri); - opts.host = uri.host; - opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; - opts.port = uri.port; - if (uri.query) opts.query = uri.query; + xd = opts.hostname != location.hostname || port != opts.port; } - this.secure = null != opts.secure ? opts.secure : - (global.location && 'https:' == location.protocol); + opts.xdomain = xd; + xhr = new XMLHttpRequest(opts); - if (opts.host) { - var pieces = opts.host.split(':'); - opts.hostname = pieces.shift(); - if (pieces.length) opts.port = pieces.pop(); + if ('open' in xhr && !opts.forceJSONP) { + return new XHR(opts); + } else { + return new JSONP(opts); } +} - this.hostname = opts.hostname || - (global.location ? location.hostname : 'localhost'); - this.port = opts.port || (global.location && location.port ? - location.port : - (this.secure ? 443 : 80)); - this.query = opts.query || {}; - if ('string' == typeof this.query) this.query = util.qsParse(this.query); - this.upgrade = false !== opts.upgrade; - this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; - this.forceJSONP = !!opts.forceJSONP; - this.timestampParam = opts.timestampParam || 't'; - this.timestampRequests = !!opts.timestampRequests; - this.flashPath = opts.flashPath || ''; - this.transports = opts.transports || ['polling', 'websocket', 'flashsocket']; - this.readyState = ''; - this.writeBuffer = []; - this.callbackBuffer = []; - this.policyPort = opts.policyPort || 843; - this.open(); +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./polling-jsonp":6,"./polling-xhr":7,"./websocket":9,"xmlhttprequest":10}],6:[function(_dereq_,module,exports){ +(function (global){ - Socket.sockets.push(this); - Socket.sockets.evs.emit('add', this); -}; +/** + * Module requirements. + */ + +var Polling = _dereq_('./polling'); +var inherit = _dereq_('inherits'); /** - * Mix in `Emitter`. + * Module exports. */ -Emitter(Socket.prototype); +module.exports = JSONPPolling; /** - * Protocol version. - * - * @api public + * Cached regular expressions. */ -Socket.protocol = parser.protocol; // this is an int +var rNewline = /\n/g; +var rEscapedNewline = /\\n/g; + +/** + * Global JSONP callbacks. + */ + +var callbacks; /** - * Static EventEmitter. + * Callbacks count. */ -Socket.sockets = []; -Socket.sockets.evs = new Emitter; +var index = 0; /** - * Expose deps for legacy compatibility - * and standalone browser access. + * Noop. */ -Socket.Socket = Socket; -Socket.Transport = require('./transport'); -Socket.Emitter = require('./emitter'); -Socket.transports = require('./transports'); -Socket.util = require('./util'); -Socket.parser = require('engine.io-parser'); +function empty () { } /** - * Creates transport of the given type. + * JSONP Polling constructor. * - * @param {String} transport name - * @return {Transport} - * @api private + * @param {Object} opts. + * @api public */ -Socket.prototype.createTransport = function (name) { - debug('creating transport "%s"', name); - var query = clone(this.query); +function JSONPPolling (opts) { + Polling.call(this, opts); - // append engine.io protocol identifier - query.EIO = parser.protocol; + this.query = this.query || {}; - // transport name - query.transport = name; + // define global callbacks array if not present + // we do this here (lazily) to avoid unneeded global pollution + if (!callbacks) { + // we need to consider multiple engines in the same page + if (!global.___eio) global.___eio = []; + callbacks = global.___eio; + } - // session id if we already have one - if (this.id) query.sid = this.id; + // callback identifier + this.index = callbacks.length; - var transport = new transports[name]({ - hostname: this.hostname, - port: this.port, - secure: this.secure, - path: this.path, - query: query, - forceJSONP: this.forceJSONP, - timestampRequests: this.timestampRequests, - timestampParam: this.timestampParam, - flashPath: this.flashPath, - policyPort: this.policyPort + // add callback to jsonp global + var self = this; + callbacks.push(function (msg) { + self.onData(msg); }); - return transport; -}; + // append to query string + this.query.j = this.index; -function clone (obj) { - var o = {}; - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - o[i] = obj[i]; - } + // prevent spurious errors from being emitted when the window is unloaded + if (global.document && global.addEventListener) { + global.addEventListener('beforeunload', function () { + if (self.script) self.script.onerror = empty; + }); } - return o; } /** - * Initializes transport to use and starts probe. + * Inherits from Polling. + */ + +inherit(JSONPPolling, Polling); + +/* + * JSONP only supports binary as base64 encoded strings + */ + +JSONPPolling.prototype.supportsBinary = false; + +/** + * Closes the socket. * * @api private */ -Socket.prototype.open = function () { - this.readyState = 'opening'; - var transport = this.createTransport(this.transports[0]); - transport.open(); - this.setTransport(transport); +JSONPPolling.prototype.doClose = function () { + if (this.script) { + this.script.parentNode.removeChild(this.script); + this.script = null; + } + + if (this.form) { + this.form.parentNode.removeChild(this.form); + this.form = null; + } + + Polling.prototype.doClose.call(this); }; /** - * Sets the current transport. Disables the existing one (if any). + * Starts a poll cycle. * * @api private */ -Socket.prototype.setTransport = function (transport) { +JSONPPolling.prototype.doPoll = function () { var self = this; + var script = document.createElement('script'); - if (this.transport) { - debug('clearing existing transport'); - this.transport.removeAllListeners(); + if (this.script) { + this.script.parentNode.removeChild(this.script); + this.script = null; } - // set up transport - this.transport = transport; + script.async = true; + script.src = this.uri(); + script.onerror = function(e){ + self.onError('jsonp poll error',e); + }; - // set up transport listeners - 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'); - }); + var insertAt = document.getElementsByTagName('script')[0]; + insertAt.parentNode.insertBefore(script, insertAt); + this.script = script; + + var isUAgecko = 'undefined' != typeof navigator && /gecko/i.test(navigator.userAgent); + + if (isUAgecko) { + setTimeout(function () { + var iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + document.body.removeChild(iframe); + }, 100); + } }; /** - * Probes a transport. + * Writes with a hidden iframe. * - * @param {String} transport name + * @param {String} data to send + * @param {Function} called upon flush. * @api private */ -Socket.prototype.probe = function (name) { - debug('probing transport "%s"', name); - var transport = this.createTransport(name, { probe: 1 }) - , failed = false - , self = this; +JSONPPolling.prototype.doWrite = function (data, fn) { + var self = this; - transport.once('open', function () { - if (failed) return; + if (!this.form) { + var form = document.createElement('form'); + var area = document.createElement('textarea'); + var id = this.iframeId = 'eio_iframe_' + this.index; + var iframe; - debug('probe transport "%s" opened', name); - transport.send([{ type: 'ping', data: 'probe' }]); - 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); + form.className = 'socketio'; + form.style.position = 'absolute'; + form.style.top = '-1000px'; + form.style.left = '-1000px'; + form.target = id; + form.method = 'POST'; + form.setAttribute('accept-charset', 'utf-8'); + area.name = 'd'; + form.appendChild(area); + document.body.appendChild(form); - debug('pausing current transport "%s"', self.transport.name); - self.transport.pause(function () { - if (failed) return; - if ('closed' == self.readyState || 'closing' == self.readyState) { - return; - } - debug('changing transport and sending upgrade packet'); - transport.removeListener('error', onerror); - self.emit('upgrade', transport); - self.setTransport(transport); - transport.send([{ type: 'upgrade' }]); - 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('error', err); - } - }); - }); - - transport.once('error', onerror); - function onerror(err) { - if (failed) return; - - // Any callback called by transport should be ignored since now - failed = true; - - var error = new Error('probe error: ' + err); - error.transport = transport.name; - - transport.close(); - transport = null; - - debug('probe transport "%s" failed because of error: %s', name, err); - - self.emit('error', error); - }; - - transport.open(); - - this.once('close', function () { - if (transport) { - debug('socket closed prematurely - aborting probe'); - failed = true; - transport.close(); - transport = null; - } - }); - - this.once('upgrading', function (to) { - if (transport && to.name != transport.name) { - debug('"%s" works - aborting "%s"', to.name, transport.name); - transport.close(); - transport = null; - } - }); -}; - -/** - * Called when connection is deemed open. - * - * @api public - */ - -Socket.prototype.onOpen = function () { - debug('socket open'); - this.readyState = 'open'; - this.emit('open'); - this.onopen && this.onopen.call(this); - this.flush(); - - // we check for `readyState` in case an `open` - // listener alreay 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.form = form; + this.area = area; } -}; - -/** - * Handles a packet. - * - * @api private - */ - -Socket.prototype.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(util.parseJSON(packet.data)); - break; - - case 'pong': - this.setPing(); - break; - case 'error': - var err = new Error('server error'); - err.code = packet.data; - this.emit('error', err); - break; + this.form.action = this.uri(); - case 'message': - this.emit('data', packet.data); - this.emit('message', packet.data); - var event = { data: packet.data }; - event.toString = function () { - return packet.data; - }; - this.onmessage && this.onmessage.call(this, event); - break; - } - } else { - debug('packet received with socket readyState "%s"', this.readyState); + function complete () { + initIframe(); + fn(); } -}; - -/** - * Called upon handshake completion. - * - * @param {Object} handshake obj - * @api private - */ - -Socket.prototype.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(); - this.setPing(); - - // Prolong liveness of socket on heartbeat - this.removeListener('heartbeat', this.onHeartbeat); - this.on('heartbeat', this.onHeartbeat); -}; -/** - * Resets ping timeout. - * - * @api private - */ - -Socket.prototype.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)); -}; - -/** - * Pings server every `this.pingInterval` and expects response - * within `this.pingTimeout` or closes connection. - * - * @api private - */ - -Socket.prototype.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); -}; - -/** -* Sends a ping packet. -* -* @api public -*/ - -Socket.prototype.ping = function () { - this.sendPacket('ping'); -}; - -/** - * Called on `drain` event - * - * @api private - */ - - Socket.prototype.onDrain = function() { - for (var i = 0; i < this.prevBufferLen; i++) { - if (this.callbackBuffer[i]) { - this.callbackBuffer[i](); + function initIframe () { + if (self.iframe) { + try { + self.form.removeChild(self.iframe); + } catch (e) { + self.onError('jsonp polling iframe removal error', e); + } } - } - - 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 (this.writeBuffer.length == 0) { - this.emit('drain'); - } else { - this.flush(); - } -}; + try { + // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) + var html = '