-;(function(){
+!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.eio=e():"undefined"!=typeof global?global.eio=e():"undefined"!=typeof self&&(self.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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-/**
- * Require the given path.
- *
- * @param {String} path
- * @return {Object} exports
- * @api public
- */
+module.exports = require('./lib/');
-function require(path, parent, orig) {
- var resolved = require.resolve(path);
+},{"./lib/":3}],2:[function(require,module,exports){
- // lookup failed
- if (null == resolved) {
- orig = orig || path;
- parent = parent || 'root';
- var err = new Error('Failed to require "' + orig + '" from "' + parent + '"');
- err.path = orig;
- err.parent = parent;
- err.require = true;
- throw err;
- }
+/**
+ * Module dependencies.
+ */
- var module = require.modules[resolved];
+var Emitter = require('emitter');
- // perform real require()
- // by invoking the module's
- // registered function
- if (!module.exports) {
- module.exports = {};
- module.client = module.component = true;
- module.call(this, module.exports, require.relative(resolved), module);
- }
+/**
+ * Module exports.
+ */
- return module.exports;
-}
+module.exports = Emitter;
/**
- * Registered modules.
+ * Compatibility with `WebSocket#addEventListener`.
+ *
+ * @api public
*/
-require.modules = {};
+Emitter.prototype.addEventListener = Emitter.prototype.on;
/**
- * Registered aliases.
+ * Compatibility with `WebSocket#removeEventListener`.
+ *
+ * @api public
*/
-require.aliases = {};
+Emitter.prototype.removeEventListener = Emitter.prototype.off;
/**
- * Resolve `path`.
- *
- * Lookup:
- *
- * - PATH/index.js
- * - PATH.js
- * - PATH
+ * Node-compatible `EventEmitter#removeListener`
*
- * @param {String} path
- * @return {String} path or null
- * @api private
+ * @api public
*/
-require.resolve = function(path) {
- if (path.charAt(0) === '/') path = path.slice(1);
- var index = path + '/index.js';
-
- var paths = [
- path,
- path + '.js',
- path + '.json',
- path + '/index.js',
- path + '/index.json'
- ];
+Emitter.prototype.removeListener = Emitter.prototype.off;
- for (var i = 0; i < paths.length; i++) {
- var path = paths[i];
- if (require.modules.hasOwnProperty(path)) return path;
- }
+},{"emitter":15}],3:[function(require,module,exports){
- if (require.aliases.hasOwnProperty(index)) {
- return require.aliases[index];
- }
-};
+module.exports = require('./socket');
/**
- * Normalize `path` relative to the current path.
+ * Exports parser
+ *
+ * @api public
*
- * @param {String} curr
- * @param {String} path
- * @return {String}
- * @api private
*/
+module.exports.parser = require('engine.io-parser');
-require.normalize = function(curr, path) {
- var segs = [];
-
- if ('.' != path.charAt(0)) return path;
+},{"./socket":4,"engine.io-parser":16}],4:[function(require,module,exports){
+/**
+ * Module dependencies.
+ */
- curr = curr.split('/');
- path = path.split('/');
+var util = require('./util');
+var transports = require('./transports');
+var Emitter = require('./emitter');
+var debug = require('debug')('engine.io-client:socket');
+var index = require('indexof');
+var parser = require('engine.io-parser');
- for (var i = 0; i < path.length; ++i) {
- if ('..' == path[i]) {
- curr.pop();
- } else if ('.' != path[i] && '' != path[i]) {
- segs.push(path[i]);
- }
- }
+/**
+ * Module exports.
+ */
- return curr.concat(segs).join('/');
-};
+module.exports = Socket;
/**
- * Register module at `path` with callback `definition`.
- *
- * @param {String} path
- * @param {Function} definition
- * @api private
+ * Global reference.
*/
-require.register = function(path, definition) {
- require.modules[path] = definition;
-};
+var global = require('global');
/**
- * Alias a module definition.
+ * Noop function.
*
- * @param {String} from
- * @param {String} to
* @api private
*/
-require.alias = function(from, to) {
- if (!require.modules.hasOwnProperty(from)) {
- throw new Error('Failed to alias "' + from + '", it does not exist');
- }
- require.aliases[to] = from;
-};
+function noop(){}
/**
- * Return a require function relative to the `parent` path.
+ * Socket constructor.
*
- * @param {String} parent
- * @return {Function}
- * @api private
+ * @param {String|Object} uri or options
+ * @param {Object} options
+ * @api public
*/
-require.relative = function(parent) {
- var p = require.normalize(parent, '..');
+function Socket(uri, opts){
+ if (!(this instanceof Socket)) return new Socket(uri, opts);
- /**
- * lastIndexOf helper.
- */
+ opts = opts || {};
- function lastIndexOf(arr, obj) {
- var i = arr.length;
- while (i--) {
- if (arr[i] === obj) return i;
- }
- return -1;
+ if (uri && 'object' == typeof uri) {
+ opts = uri;
+ uri = null;
}
- /**
- * The relative require() itself.
- */
-
- function localRequire(path) {
- var resolved = localRequire.resolve(path);
- return require(resolved, parent, path);
+ 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;
}
- /**
- * Resolve relative to the parent.
- */
-
- localRequire.resolve = function(path) {
- var c = path.charAt(0);
- if ('/' == c) return path.slice(1);
- if ('.' == c) return require.normalize(p, path);
-
- // resolve deps by returning
- // the dep in the nearest "deps"
- // directory
- var segs = parent.split('/');
- var i = lastIndexOf(segs, 'deps') + 1;
- if (!i) i = 0;
- path = segs.slice(0, i + 1).join('/') + '/deps/' + path;
- return path;
- };
-
- /**
- * Check if module is defined at `path`.
- */
+ this.secure = null != opts.secure ? opts.secure :
+ (global.location && 'https:' == location.protocol);
- localRequire.exists = function(path) {
- return require.modules.hasOwnProperty(localRequire.resolve(path));
- };
+ if (opts.host) {
+ var pieces = opts.host.split(':');
+ opts.hostname = pieces.shift();
+ if (pieces.length) opts.port = pieces.pop();
+ }
- return localRequire;
-};
-require.register("component-emitter/index.js", function(exports, require, module){
+ this.agent = opts.agent || false;
+ 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();
+}
/**
- * Expose `Emitter`.
+ * Mix in `Emitter`.
*/
-module.exports = Emitter;
+Emitter(Socket.prototype);
/**
- * Initialize a new `Emitter`.
+ * Protocol version.
*
* @api public
*/
-function Emitter(obj) {
- if (obj) return mixin(obj);
-};
+Socket.protocol = parser.protocol; // this is an int
/**
- * Mixin the emitter properties.
+ * Expose deps for legacy compatibility
+ * and standalone browser access.
+ */
+
+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');
+
+/**
+ * Creates transport of the given type.
*
- * @param {Object} obj
- * @return {Object}
+ * @param {String} transport name
+ * @return {Transport}
* @api private
*/
-function mixin(obj) {
- for (var key in Emitter.prototype) {
- obj[key] = Emitter.prototype[key];
+Socket.prototype.createTransport = function (name) {
+ debug('creating transport "%s"', name);
+ var query = clone(this.query);
+
+ // append engine.io protocol identifier
+ query.EIO = parser.protocol;
+
+ // transport name
+ query.transport = name;
+
+ // session id if we already have one
+ 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,
+ timestampRequests: this.timestampRequests,
+ timestampParam: this.timestampParam,
+ flashPath: this.flashPath,
+ policyPort: this.policyPort
+ });
+
+ return transport;
+};
+
+function clone (obj) {
+ var o = {};
+ for (var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ o[i] = obj[i];
+ }
}
- return obj;
+ return o;
}
/**
- * Listen on the given `event` with `fn`.
+ * Initializes transport to use and starts probe.
*
- * @param {String} event
- * @param {Function} fn
- * @return {Emitter}
- * @api public
+ * @api private
*/
-Emitter.prototype.on = function(event, fn){
- this._callbacks = this._callbacks || {};
- (this._callbacks[event] = this._callbacks[event] || [])
- .push(fn);
- return this;
+Socket.prototype.open = function () {
+ var transport = this.transports[0];
+ this.readyState = 'opening';
+ var transport = this.createTransport(transport);
+ transport.open();
+ this.setTransport(transport);
};
/**
- * Adds an `event` listener that will be invoked a single
- * time then automatically removed.
+ * Sets the current transport. Disables the existing one (if any).
*
- * @param {String} event
- * @param {Function} fn
- * @return {Emitter}
- * @api public
+ * @api private
*/
-Emitter.prototype.once = function(event, fn){
+Socket.prototype.setTransport = function(transport){
+ debug('setting transport %s', transport.name);
var self = this;
- this._callbacks = this._callbacks || {};
- function on() {
- self.off(event, on);
- fn.apply(this, arguments);
+ if (this.transport) {
+ debug('clearing existing transport %s', this.transport.name);
+ this.transport.removeAllListeners();
}
- fn._off = on;
- this.on(event, on);
- return this;
+ // set up transport
+ this.transport = transport;
+
+ // 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');
+ });
};
/**
- * Remove the given callback for `event` or all
- * registered callbacks.
+ * Probes a transport.
*
- * @param {String} event
- * @param {Function} fn
- * @return {Emitter}
- * @api public
+ * @param {String} transport name
+ * @api private
*/
-Emitter.prototype.off =
-Emitter.prototype.removeListener =
-Emitter.prototype.removeAllListeners = function(event, fn){
- this._callbacks = this._callbacks || {};
-
- // all
- if (0 == arguments.length) {
- this._callbacks = {};
- return this;
- }
+Socket.prototype.probe = function (name) {
+ debug('probing transport "%s"', name);
+ var transport = this.createTransport(name, { probe: 1 })
+ , failed = false
+ , self = this;
- // specific event
- var callbacks = this._callbacks[event];
- if (!callbacks) return this;
+ transport.once('open', function () {
+ if (failed) return;
- // remove all handlers
- if (1 == arguments.length) {
- delete this._callbacks[event];
- return this;
- }
-
- // remove specific handler
- var i = callbacks.indexOf(fn._off || fn);
- if (~i) callbacks.splice(i, 1);
- return this;
-};
-
-/**
- * Emit `event` with the given args.
- *
- * @param {String} event
- * @param {Mixed} ...
- * @return {Emitter}
- */
-
-Emitter.prototype.emit = function(event){
- this._callbacks = this._callbacks || {};
- var args = [].slice.call(arguments, 1)
- , callbacks = this._callbacks[event];
-
- if (callbacks) {
- callbacks = callbacks.slice(0);
- for (var i = 0, len = callbacks.length; i < len; ++i) {
- callbacks[i].apply(this, args);
- }
- }
-
- return this;
-};
-
-/**
- * Return array of callbacks for `event`.
- *
- * @param {String} event
- * @return {Array}
- * @api public
- */
-
-Emitter.prototype.listeners = function(event){
- this._callbacks = this._callbacks || {};
- return this._callbacks[event] || [];
-};
-
-/**
- * Check if this emitter has `event` handlers.
- *
- * @param {String} event
- * @return {Boolean}
- * @api public
- */
-
-Emitter.prototype.hasListeners = function(event){
- return !! this.listeners(event).length;
-};
-
-});
-require.register("component-indexof/index.js", function(exports, require, module){
-
-var indexOf = [].indexOf;
-
-module.exports = function(arr, obj){
- if (indexOf) return arr.indexOf(obj);
- for (var i = 0; i < arr.length; ++i) {
- if (arr[i] === obj) return i;
- }
- return -1;
-};
-});
-require.register("LearnBoost-engine.io-protocol/lib/index.js", function(exports, require, module){
-/**
- * Module dependencies.
- */
-
-var keys = require('./keys');
-
-/**
- * Current protocol version.
- */
-exports.protocol = 2;
-
-/**
- * Packet types.
- */
-
-var packets = exports.packets = {
- open: 0 // non-ws
- , close: 1 // non-ws
- , ping: 2
- , pong: 3
- , message: 4
- , upgrade: 5
- , noop: 6
-};
-
-var packetslist = keys(packets);
-
-/**
- * Premade error packet.
- */
-
-var err = { type: 'error', data: 'parser error' };
-
-/**
- * Encodes a packet.
- *
- * <packet type id> [ `:` <data> ]
- *
- * Example:
- *
- * 5:hello world
- * 3
- * 4
- *
- * @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;
-};
-
-/**
- * Decodes a packet.
- *
- * @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;
- }
-
- if (data.length > 1) {
- return { type: packetslist[type], data: data.substring(1) };
- } else {
- return { type: packetslist[type] };
- }
-};
-
-/**
- * Encodes multiple messages (payload).
- *
- * <length>: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;
-};
-
-/*
- * Decodes data when a payload is maybe expected.
- *
- * @param {String} data, callback method
- * @api public
- */
-
-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);
-
- 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);
-
- 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);
- }
-
-};
-
-});
-require.register("LearnBoost-engine.io-protocol/lib/keys.js", function(exports, require, module){
-
-/**
- * Gets the keys for an object.
- *
- * @return {Array} keys
- * @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);
- }
- }
- return arr;
-};
-
-});
-require.register("visionmedia-debug/index.js", function(exports, require, module){
-if ('undefined' == typeof window) {
- module.exports = require('./lib/debug');
-} else {
- module.exports = require('./debug');
-}
-
-});
-require.register("visionmedia-debug/debug.js", function(exports, require, module){
-
-/**
- * Expose `debug()` as the module.
- */
+ 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);
-module.exports = debug;
+ 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('upgradeError', err);
+ }
+ });
+ });
-/**
- * Create a debugger with the given `name`.
- *
- * @param {String} name
- * @return {Type}
- * @api public
- */
+ transport.once('error', onerror);
+ function onerror(err) {
+ if (failed) return;
-function debug(name) {
- if (!debug.enabled(name)) return function(){};
+ // Any callback called by transport should be ignored since now
+ failed = true;
- return function(fmt){
- fmt = coerce(fmt);
+ var error = new Error('probe error: ' + err);
+ error.transport = transport.name;
- var curr = new Date;
- var ms = curr - (debug[name] || curr);
- debug[name] = curr;
+ transport.close();
+ transport = null;
- fmt = name
- + ' '
- + fmt
- + ' +' + debug.humanize(ms);
+ debug('probe transport "%s" failed because of error: %s', name, err);
- // This hackery is required for IE8
- // where `console.log` doesn't have 'apply'
- window.console
- && console.log
- && Function.prototype.apply.call(console.log, console, arguments);
+ self.emit('upgradeError', error);
}
-}
-
-/**
- * The currently active debug mode names.
- */
-
-debug.names = [];
-debug.skips = [];
-
-/**
- * Enables a debug mode by name. This can include modes
- * separated by a colon and wildcards.
- *
- * @param {String} name
- * @api public
- */
-
-debug.enable = function(name) {
- try {
- localStorage.debug = name;
- } catch(e){}
- var split = (name || '').split(/[\s,]+/)
- , len = split.length;
+ transport.open();
- for (var i = 0; i < len; i++) {
- name = split[i].replace('*', '.*?');
- if (name[0] === '-') {
- debug.skips.push(new RegExp('^' + name.substr(1) + '$'));
+ this.once('close', function () {
+ if (transport) {
+ debug('socket closed prematurely - aborting probe');
+ failed = true;
+ transport.close();
+ transport = null;
}
- else {
- debug.names.push(new RegExp('^' + name + '$'));
+ });
+
+ this.once('upgrading', function (to) {
+ if (transport && to.name != transport.name) {
+ debug('"%s" works - aborting "%s"', to.name, transport.name);
+ transport.close();
+ transport = null;
}
- }
+ });
};
/**
- * Disable debug output.
+ * Called when connection is deemed open.
*
* @api public
*/
-debug.disable = function(){
- debug.enable('');
-};
-
-/**
- * Humanize the given `ms`.
- *
- * @param {Number} m
- * @return {String}
- * @api private
- */
-
-debug.humanize = function(ms) {
- var sec = 1000
- , min = 60 * 1000
- , hour = 60 * min;
-
- if (ms >= 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';
-};
-
-/**
- * Returns true if the given mode name is enabled, false otherwise.
- *
- * @param {String} name
- * @return {Boolean}
- * @api public
- */
+Socket.prototype.onOpen = function () {
+ debug('socket open');
+ this.readyState = 'open';
+ this.emit('open');
+ this.onopen && this.onopen.call(this);
+ this.flush();
-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;
+ // 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]);
}
}
- return false;
};
/**
- * Coerce `val`.
- */
-
-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');
-
-/**
- * Exports parser
- *
- * @api public
- *
- */
-module.exports.parser = require('engine.io-parser');
-
-});
-require.register("engine.io/lib/socket.js", function(exports, require, module){
-/**
- * Module dependencies.
- */
-
-var util = require('./util')
- , transports = require('./transports')
- , Emitter = require('./emitter')
- , debug = require('debug')('engine-client:socket')
- , index = require('indexof')
- , parser = require('engine.io-parser');
-
-/**
- * Module exports.
- */
-
-module.exports = Socket;
-
-/**
- * Global reference.
- */
-
-var global = util.global();
-
-/**
- * Noop function.
+ * Handles a packet.
*
* @api private
*/
-function noop () {};
-
-/**
- * Socket constructor.
- *
- * @param {String|Object} uri or options
- * @param {Object} options
- * @api public
- */
+Socket.prototype.onPacket = function (packet) {
+ if ('opening' == this.readyState || 'open' == this.readyState) {
+ debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
-function Socket(uri, opts){
- if (!(this instanceof Socket)) return new Socket(uri, opts);
+ this.emit('packet', packet);
- opts = opts || {};
+ // Socket is live - any packet counts
+ this.emit('heartbeat');
- if ('object' == typeof uri) {
- opts = uri;
- uri = null;
- }
+ switch (packet.type) {
+ case 'open':
+ this.onHandshake(util.parseJSON(packet.data));
+ break;
- 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;
- }
+ case 'pong':
+ this.setPing();
+ break;
- this.secure = null != opts.secure ? opts.secure :
- (global.location && 'https:' == location.protocol);
+ case 'error':
+ var err = new Error('server error');
+ err.code = packet.data;
+ this.emit('error', err);
+ break;
- if (opts.host) {
- var pieces = opts.host.split(':');
- opts.hostname = pieces.shift();
- if (pieces.length) opts.port = pieces.pop();
+ 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);
}
-
- 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();
-
- Socket.sockets.push(this);
- Socket.sockets.evs.emit('add', this);
};
/**
- * Mix in `Emitter`.
+ * Called upon handshake completion.
+ *
+ * @param {Object} handshake obj
+ * @api private
*/
-Emitter(Socket.prototype);
+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);
+};
/**
- * Protocol version.
+ * Resets ping timeout.
*
- * @api public
+ * @api private
*/
-Socket.protocol = parser.protocol; // this is an int
+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));
+};
/**
- * Static EventEmitter.
+ * Pings server every `this.pingInterval` and expects response
+ * within `this.pingTimeout` or closes connection.
+ *
+ * @api private
*/
-Socket.sockets = [];
-Socket.sockets.evs = new Emitter;
+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);
+};
/**
- * Expose deps for legacy compatibility
- * and standalone browser access.
- */
+* Sends a ping packet.
+*
+* @api public
+*/
-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');
+Socket.prototype.ping = function () {
+ this.sendPacket('ping');
+};
/**
- * Creates transport of the given type.
+ * Called on `drain` event
*
- * @param {String} transport name
- * @return {Transport}
* @api private
*/
-Socket.prototype.createTransport = function (name) {
- debug('creating transport "%s"', name);
- var query = clone(this.query);
+ Socket.prototype.onDrain = function() {
+ for (var i = 0; i < this.prevBufferLen; i++) {
+ if (this.callbackBuffer[i]) {
+ this.callbackBuffer[i]();
+ }
+ }
- // append engine.io protocol identifier
- query.EIO = parser.protocol;
+ this.writeBuffer.splice(0, this.prevBufferLen);
+ this.callbackBuffer.splice(0, this.prevBufferLen);
- // transport name
- query.transport = name;
+ // 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;
- // session id if we already have one
- if (this.id) query.sid = this.id;
+ if (this.writeBuffer.length == 0) {
+ this.emit('drain');
+ } else {
+ this.flush();
+ }
+};
- 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
- });
+/**
+ * Flush write buffers.
+ *
+ * @api private
+ */
- return transport;
+Socket.prototype.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');
+ }
};
-function clone (obj) {
- var o = {};
- for (var i in obj) {
- if (obj.hasOwnProperty(i)) {
- o[i] = obj[i];
- }
- }
- return o;
-}
+/**
+ * Sends a message.
+ *
+ * @param {String} message.
+ * @param {Function} callback function.
+ * @return {Socket} for chaining.
+ * @api public
+ */
+
+Socket.prototype.write =
+Socket.prototype.send = function (msg, fn) {
+ this.sendPacket('message', msg, fn);
+ return this;
+};
/**
- * Initializes transport to use and starts probe.
+ * Sends a packet.
*
+ * @param {String} packet type.
+ * @param {String} data.
+ * @param {Function} callback function.
* @api private
*/
-Socket.prototype.open = function () {
- this.readyState = 'opening';
- var transport = this.createTransport(this.transports[0]);
- transport.open();
- this.setTransport(transport);
+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();
};
/**
- * Sets the current transport. Disables the existing one (if any).
+ * Closes the connection.
*
* @api private
*/
-Socket.prototype.setTransport = function (transport) {
- var self = this;
-
- if (this.transport) {
- debug('clearing existing transport');
- this.transport.removeAllListeners();
+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();
}
- // set up transport
- this.transport = transport;
+ return this;
+};
- // 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');
- });
+/**
+ * Called upon transport error
+ *
+ * @api private
+ */
+
+Socket.prototype.onError = function (err) {
+ debug('socket error %j', err);
+ this.emit('error', err);
+ this.onerror && this.onerror.call(this, err);
+ this.onClose('transport error', err);
};
/**
- * Probes a transport.
+ * Called upon transport close.
*
- * @param {String} transport name
* @api private
*/
-Socket.prototype.probe = function (name) {
- debug('probing transport "%s"', name);
- var transport = this.createTransport(name, { probe: 1 })
- , failed = false
- , self = this;
+Socket.prototype.onClose = function (reason, desc) {
+ if ('opening' == this.readyState || 'open' == this.readyState) {
+ debug('socket close with reason: "%s"', reason);
+ var self = this;
- transport.once('open', function () {
- if (failed) return;
+ // clear timers
+ clearTimeout(this.pingIntervalTimer);
+ clearTimeout(this.pingTimeoutTimer);
- 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);
+ // 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);
- 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);
- }
- });
- });
+ // 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.onclose && this.onclose.call(this);
+ }
+};
+
+/**
+ * Filters upgrades, returning only those matching client transports.
+ *
+ * @param {Array} server upgrades
+ * @api private
+ *
+ */
- transport.once('error', onerror);
- function onerror(err) {
- if (failed) return;
+Socket.prototype.filterUpgrades = function (upgrades) {
+ var filteredUpgrades = [];
+ for (var i = 0, j = upgrades.length; i<j; i++) {
+ if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]);
+ }
+ return filteredUpgrades;
+};
- // Any callback called by transport should be ignored since now
- failed = true;
+},{"./emitter":2,"./transport":5,"./transports":7,"./util":12,"debug":14,"engine.io-parser":16,"global":19,"indexof":21}],5:[function(require,module,exports){
- var error = new Error('probe error: ' + err);
- error.transport = transport.name;
+/**
+ * Module dependencies.
+ */
- transport.close();
- transport = null;
+var util = require('./util');
+var parser = require('engine.io-parser');
+var Emitter = require('./emitter');
- debug('probe transport "%s" failed because of error: %s', name, err);
+/**
+ * Module exports.
+ */
- self.emit('error', error);
- };
+module.exports = Transport;
- transport.open();
+/**
+ * Transport abstract constructor.
+ *
+ * @param {Object} options.
+ * @api private
+ */
- this.once('close', function () {
- if (transport) {
- debug('socket closed prematurely - aborting probe');
- failed = true;
- transport.close();
- transport = null;
- }
- });
+function Transport (opts) {
+ this.path = opts.path;
+ this.hostname = opts.hostname;
+ this.port = opts.port;
+ this.secure = opts.secure;
+ this.query = opts.query;
+ this.timestampParam = opts.timestampParam;
+ this.timestampRequests = opts.timestampRequests;
+ this.readyState = '';
+ this.agent = opts.agent || false;
+}
- this.once('upgrading', function (to) {
- if (transport && to.name != transport.name) {
- debug('"%s" works - aborting "%s"', to.name, transport.name);
- transport.close();
- transport = null;
- }
- });
-};
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Transport.prototype);
/**
- * Called when connection is deemed open.
+ * Emits an error.
*
+ * @param {String} str
+ * @return {Transport} for chaining
* @api public
*/
-Socket.prototype.onOpen = function () {
- debug('socket open');
- this.readyState = 'open';
- this.emit('open');
- this.onopen && this.onopen.call(this);
- this.flush();
+Transport.prototype.onError = function (msg, desc) {
+ var err = new Error(msg);
+ err.type = 'TransportError';
+ err.description = desc;
+ this.emit('error', err);
+ return this;
+};
- // 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]);
- }
+/**
+ * Opens the transport.
+ *
+ * @api public
+ */
+
+Transport.prototype.open = function () {
+ if ('closed' == this.readyState || '' == this.readyState) {
+ this.readyState = 'opening';
+ this.doOpen();
}
+
+ return this;
};
/**
- * Handles a packet.
+ * Closes the transport.
*
* @api private
*/
-Socket.prototype.onPacket = function (packet) {
+Transport.prototype.close = function () {
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;
+ this.doClose();
+ this.onClose();
+ }
- case 'pong':
- this.setPing();
- break;
+ return this;
+};
- case 'error':
- var err = new Error('server error');
- err.code = packet.data;
- this.emit('error', err);
- break;
+/**
+ * Sends multiple packets.
+ *
+ * @param {Array} packets
+ * @api private
+ */
- 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;
- }
+Transport.prototype.send = function(packets){
+ if ('open' == this.readyState) {
+ this.write(packets);
} else {
- debug('packet received with socket readyState "%s"', this.readyState);
+ throw new Error('Transport not open');
}
};
/**
- * Called upon handshake completion.
+ * Called upon open
*
- * @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);
+Transport.prototype.onOpen = function () {
+ this.readyState = 'open';
+ this.writable = true;
+ this.emit('open');
};
/**
- * Resets ping timeout.
+ * Called with data.
*
+ * @param {String} data
* @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));
+Transport.prototype.onData = function (data) {
+ this.onPacket(parser.decodePacket(data));
};
/**
- * Pings server every `this.pingInterval` and expects response
- * within `this.pingTimeout` or closes connection.
+ * Called with a decoded packet.
+ */
+
+Transport.prototype.onPacket = function (packet) {
+ this.emit('packet', packet);
+};
+
+/**
+ * Called upon close.
*
* @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);
+Transport.prototype.onClose = function () {
+ this.readyState = 'closed';
+ this.emit('close');
};
+},{"./emitter":2,"./util":12,"engine.io-parser":16}],6:[function(require,module,exports){
+
/**
-* Sends a ping packet.
-*
-* @api public
-*/
+ * Module dependencies.
+ */
-Socket.prototype.ping = function () {
- this.sendPacket('ping');
-};
+var WS = require('./websocket');
+var util = require('../util');
+var debug = require('debug')('engine.io-client:flashsocket');
/**
- * Called on `drain` event
- *
- * @api private
+ * Module exports.
*/
- Socket.prototype.onDrain = function() {
- for (var i = 0; i < this.prevBufferLen; i++) {
- if (this.callbackBuffer[i]) {
- this.callbackBuffer[i]();
- }
- }
+module.exports = FlashWS;
- this.writeBuffer.splice(0, this.prevBufferLen);
- this.callbackBuffer.splice(0, this.prevBufferLen);
+/**
+ * Global reference.
+ */
- // 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;
+var global = require('global');
- if (this.writeBuffer.length == 0) {
- this.emit('drain');
- } else {
- this.flush();
- }
-};
+/**
+ * Obfuscated key for Blue Coat.
+ */
+
+var xobject = global[['Active'].concat('Object').join('X')];
+
+/**
+ * FlashWS constructor.
+ *
+ * @api public
+ */
+
+function FlashWS(options){
+ WS.call(this, options);
+ this.flashPath = options.flashPath;
+ this.policyPort = options.policyPort;
+}
/**
- * Flush write buffers.
- *
- * @api private
+ * Inherits from WebSocket.
*/
-Socket.prototype.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');
- }
-};
+util.inherits(FlashWS, WS);
/**
- * Sends a message.
+ * Transport name.
*
- * @param {String} message.
- * @param {Function} callback function.
- * @return {Socket} for chaining.
* @api public
*/
-Socket.prototype.write =
-Socket.prototype.send = function (msg, fn) {
- this.sendPacket('message', msg, fn);
- return this;
-};
+FlashWS.prototype.name = 'flashsocket';
/**
- * Sends a packet.
+ * Opens the transport.
*
- * @param {String} packet type.
- * @param {String} data.
- * @param {Function} callback function.
- * @api private
+ * @api public
*/
-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();
+FlashWS.prototype.doOpen = function(){
+ if (!this.check()) {
+ // let the probe timeout
+ return;
+ }
+
+ // instrument websocketjs logging
+ function log(type){
+ return function(){
+ var str = Array.prototype.join.call(arguments, ' ');
+ debug('[websocketjs %s] %s', type, str);
+ };
+ }
+
+ global.WEB_SOCKET_LOGGER = { log: log('debug'), error: log('error') };
+ global.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;
+ global.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
+
+ if (!global.WEB_SOCKET_SWF_LOCATION) {
+ global.WEB_SOCKET_SWF_LOCATION = this.flashPath + 'WebSocketMainInsecure.swf';
+ }
+
+ // dependencies
+ var deps = [this.flashPath + 'web_socket.js'];
+
+ if (!global.swfobject) {
+ deps.unshift(this.flashPath + 'swfobject.js');
+ }
+
+ var self = this;
+
+ load(deps, function(){
+ self.ready(function(){
+ WebSocket.__addTask(function () {
+ self.socket = new WebSocket(self.uri());
+ self.addEventListeners();
+ });
+ });
+ });
};
/**
- * Closes the connection.
+ * Override to prevent closing uninitialized flashsocket.
*
* @api private
*/
-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();
- }
-
- return this;
+FlashWS.prototype.doClose = function(){
+ if (!this.socket) return;
+ var self = this;
+ WebSocket.__addTask(function(){
+ WS.prototype.doClose.call(self);
+ });
};
/**
- * Called upon transport error
+ * Writes to the Flash socket.
*
* @api private
*/
-Socket.prototype.onError = function (err) {
- debug('socket error %j', err);
- this.emit('error', err);
- this.onerror && this.onerror.call(this, err);
- this.onClose('transport error', err);
+FlashWS.prototype.write = function(){
+ var self = this, args = arguments;
+ WebSocket.__addTask(function(){
+ WS.prototype.write.apply(self, args);
+ });
};
/**
- * Called upon transport close.
+ * Called upon dependencies are loaded.
*
* @api private
*/
-Socket.prototype.onClose = function (reason, desc) {
- if ('opening' == this.readyState || 'open' == this.readyState) {
- debug('socket close with reason: "%s"', reason);
- var self = this;
-
- // clear timers
- clearTimeout(this.pingIntervalTimer);
- clearTimeout(this.pingTimeoutTimer);
+FlashWS.prototype.ready = function(fn){
+ if (typeof WebSocket == 'undefined' ||
+ !('__initialize' in WebSocket) || !global.swfobject) {
+ return;
+ }
- // clean buffers in next tick, so developers can still
- // grab the buffers on `close` event
- setTimeout(function() {
- self.writeBuffer = [];
- self.callbackBuffer = [];
- }, 0);
+ if (global.swfobject.getFlashPlayerVersion().major < 10) {
+ return;
+ }
- // ignore further transport communication
- this.transport.removeAllListeners();
+ function init () {
+ // only start downloading the swf file when
+ // we checked that this browser actually supports it
+ if (!FlashWS.loaded) {
+ if (843 != self.policyPort) {
+ var policy = 'xmlsocket://' + self.hostname + ':' + self.policyPort;
+ WebSocket.loadFlashPolicyFile(policy);
+ }
- // set ready state
- var prev = this.readyState;
- this.readyState = 'closed';
+ WebSocket.__initialize();
+ FlashWS.loaded = true;
+ }
- // clear session id
- this.id = null;
+ fn.call(self);
+ }
- // emit events
- if (prev == 'open') {
- this.emit('close', reason, desc);
- this.onclose && this.onclose.call(this);
- }
+ var self = this;
+ if (document.body) {
+ return init();
}
+
+ util.load(init);
};
/**
- * Filters upgrades, returning only those matching client transports.
- *
- * @param {Array} server upgrades
- * @api private
+ * Feature detection for flashsocket.
*
+ * @return {Boolean} whether this transport is available.
+ * @api public
*/
-Socket.prototype.filterUpgrades = function (upgrades) {
- var filteredUpgrades = [];
- for (var i = 0, j = upgrades.length; i<j; i++) {
- if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]);
+FlashWS.prototype.check = function(){
+ if ('undefined' == typeof window) {
+ return false;
}
- return filteredUpgrades;
-};
-});
-require.register("engine.io/lib/transport.js", function(exports, require, module){
+ if (typeof WebSocket != 'undefined' && !('__initialize' in WebSocket)) {
+ return false;
+ }
-/**
- * Module dependencies.
- */
+ if (xobject) {
+ var control = null;
+ try {
+ control = new xobject('ShockwaveFlash.ShockwaveFlash');
+ } catch (e) { }
+ if (control) {
+ return true;
+ }
+ } else {
+ for (var i = 0, l = navigator.plugins.length; i < l; i++) {
+ for (var j = 0, m = navigator.plugins[i].length; j < m; j++) {
+ if (navigator.plugins[i][j].description == 'Shockwave Flash') {
+ return true;
+ }
+ }
+ }
+ }
-var util = require('./util')
- , parser = require('engine.io-parser')
- , Emitter = require('./emitter');
+ return false;
+};
/**
- * Module exports.
+ * Lazy loading of scripts.
+ * Based on $script by Dustin Diaz - MIT
*/
-module.exports = Transport;
+var scripts = {};
/**
- * Transport abstract constructor.
+ * Injects a script. Keeps tracked of injected ones.
*
- * @param {Object} options.
+ * @param {String} path
+ * @param {Function} callback
* @api private
*/
-function Transport (opts) {
- this.path = opts.path;
- this.hostname = opts.hostname;
- this.port = opts.port;
- this.secure = opts.secure;
- this.query = opts.query;
- this.timestampParam = opts.timestampParam;
- this.timestampRequests = opts.timestampRequests;
- this.readyState = '';
-};
+function create(path, fn){
+ if (scripts[path]) return fn();
-/**
- * Mix in `Emitter`.
- */
+ var el = document.createElement('script');
+ var loaded = false;
-Emitter(Transport.prototype);
+ debug('loading "%s"', path);
+ el.onload = el.onreadystatechange = function(){
+ if (loaded || scripts[path]) return;
+ var rs = el.readyState;
+ if (!rs || 'loaded' == rs || 'complete' == rs) {
+ debug('loaded "%s"', path);
+ el.onload = el.onreadystatechange = null;
+ loaded = true;
+ scripts[path] = true;
+ fn();
+ }
+ };
-/**
- * Emits an error.
- *
- * @param {String} str
- * @return {Transport} for chaining
- * @api public
- */
+ el.async = 1;
+ el.src = path;
-Transport.prototype.onError = function (msg, desc) {
- var err = new Error(msg);
- err.type = 'TransportError';
- err.description = desc;
- this.emit('error', err);
- return this;
-};
+ var head = document.getElementsByTagName('head')[0];
+ head.insertBefore(el, head.firstChild);
+}
/**
- * Opens the transport.
+ * Loads scripts and fires a callback.
*
- * @api public
+ * @param {Array} paths
+ * @param {Function} callback
*/
-Transport.prototype.open = function () {
- if ('closed' == this.readyState || '' == this.readyState) {
- this.readyState = 'opening';
- this.doOpen();
+function load(arr, fn){
+ function process(i){
+ if (!arr[i]) return fn();
+ create(arr[i], function () {
+ process(++i);
+ });
}
- return this;
-};
+ process(0);
+}
+
+},{"../util":12,"./websocket":11,"debug":14,"global":19}],7:[function(require,module,exports){
+
+/**
+ * Module dependencies
+ */
+
+var XMLHttpRequest = require('xmlhttprequest')
+ , XHR = require('./polling-xhr')
+ , JSONP = require('./polling-jsonp')
+ , websocket = require('./websocket')
+ , flashsocket = require('./flashsocket')
/**
- * Closes the transport.
- *
- * @api private
+ * Export transports.
*/
-Transport.prototype.close = function () {
- if ('opening' == this.readyState || 'open' == this.readyState) {
- this.doClose();
- this.onClose();
- }
+exports.polling = polling;
+exports.websocket = websocket;
+exports.flashsocket = flashsocket;
- return this;
-};
+/**
+ * Global reference.
+ */
+
+var global = require('global');
/**
- * Sends multiple packets.
+ * Polling transport polymorphic constructor.
+ * Decides on xhr vs jsonp based on feature detection.
*
- * @param {Array} packets
* @api private
*/
-Transport.prototype.send = function(packets){
- if ('open' == this.readyState) {
- this.write(packets);
+function polling (opts) {
+ var xhr
+ , xd = false;
+
+ if (global.location) {
+ var isSSL = 'https:' == location.protocol;
+ var port = location.port;
+
+ // some user agents have empty `location.port`
+ if (!port) {
+ port = isSSL ? 443 : 80;
+ }
+
+ xd = opts.hostname != location.hostname || port != opts.port;
+ }
+
+ opts.xdomain = xd;
+ xhr = new XMLHttpRequest(opts);
+
+ if (xhr && !opts.forceJSONP) {
+ return new XHR(opts);
} else {
- throw new Error('Transport not open');
+ return new JSONP(opts);
}
};
+},{"./flashsocket":6,"./polling-jsonp":8,"./polling-xhr":9,"./websocket":11,"global":19,"xmlhttprequest":13}],8:[function(require,module,exports){
+
/**
- * Called upon open
- *
- * @api private
+ * Module requirements.
*/
-Transport.prototype.onOpen = function () {
- this.readyState = 'open';
- this.writable = true;
- this.emit('open');
-};
+var Polling = require('./polling');
+var util = require('../util');
/**
- * Called with data.
- *
- * @param {String} data
- * @api private
+ * Module exports.
*/
-Transport.prototype.onData = function (data) {
- this.onPacket(parser.decodePacket(data));
-};
+module.exports = JSONPPolling;
/**
- * Called with a decoded packet.
+ * Global reference.
*/
-Transport.prototype.onPacket = function (packet) {
- this.emit('packet', packet);
-};
+var global = require('global');
/**
- * Called upon close.
- *
- * @api private
+ * Cached regular expressions.
*/
-Transport.prototype.onClose = function () {
- this.readyState = 'closed';
- this.emit('close');
-};
-
-});
-require.register("engine.io/lib/emitter.js", function(exports, require, module){
+var rNewline = /\n/g;
/**
- * Module dependencies.
+ * Global JSONP callbacks.
*/
-var Emitter = require('emitter');
+var callbacks;
/**
- * Module exports.
+ * Callbacks count.
*/
-module.exports = Emitter;
+var index = 0;
/**
- * Compatibility with `WebSocket#addEventListener`.
- *
- * @api public
+ * Noop.
*/
-Emitter.prototype.addEventListener = Emitter.prototype.on;
+function empty () { }
/**
- * Compatibility with `WebSocket#removeEventListener`.
+ * JSONP Polling constructor.
*
+ * @param {Object} opts.
* @api public
*/
-Emitter.prototype.removeEventListener = Emitter.prototype.off;
+function JSONPPolling (opts) {
+ Polling.call(this, opts);
-/**
- * Node-compatible `EventEmitter#removeListener`
- *
- * @api public
- */
+ // 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;
+ }
-Emitter.prototype.removeListener = Emitter.prototype.off;
+ // callback identifier
+ this.index = callbacks.length;
+
+ // add callback to jsonp global
+ var self = this;
+ callbacks.push(function (msg) {
+ self.onData(msg);
+ });
+
+ // append to query string
+ this.query.j = this.index;
+}
-});
-require.register("engine.io/lib/util.js", function(exports, require, module){
/**
- * Status of page load.
+ * Inherits from Polling.
*/
-var pageLoaded = false;
+util.inherits(JSONPPolling, Polling);
/**
- * Returns the global object
+ * Closes the socket
*
* @api private
*/
-exports.global = function () {
- return 'undefined' != typeof window ? window : global;
+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);
};
/**
- * Inheritance.
+ * Starts a poll cycle.
*
- * @param {Function} ctor a
- * @param {Function} ctor b
* @api private
*/
-exports.inherits = function inherits (a, b) {
- function c () { }
- c.prototype = b.prototype;
- a.prototype = new c;
+JSONPPolling.prototype.doPoll = function () {
+ var self = this;
+ var script = document.createElement('script');
+
+ if (this.script) {
+ this.script.parentNode.removeChild(this.script);
+ this.script = null;
+ }
+
+ script.async = true;
+ script.src = this.uri();
+ script.onerror = function(e){
+ self.onError('jsonp poll error',e);
+ };
+
+ var insertAt = document.getElementsByTagName('script')[0];
+ insertAt.parentNode.insertBefore(script, insertAt);
+ this.script = script;
+
+
+ if (util.ua.gecko) {
+ setTimeout(function () {
+ var iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ document.body.removeChild(iframe);
+ }, 100);
+ }
};
/**
- * Object.keys
+ * Writes with a hidden iframe.
+ *
+ * @param {String} data to send
+ * @param {Function} called upon flush.
+ * @api private
*/
-exports.keys = Object.keys || function (obj) {
- var ret = [];
- var has = Object.prototype.hasOwnProperty;
+JSONPPolling.prototype.doWrite = function (data, fn) {
+ var self = this;
- for (var i in obj) {
- if (has.call(obj, i)) {
- ret.push(i);
+ if (!this.form) {
+ var form = document.createElement('form');
+ var area = document.createElement('textarea');
+ var id = this.iframeId = 'eio_iframe_' + this.index;
+ var iframe;
+
+ 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);
+
+ this.form = form;
+ this.area = area;
+ }
+
+ this.form.action = this.uri();
+
+ function complete () {
+ initIframe();
+ fn();
+ }
+
+ function initIframe () {
+ if (self.iframe) {
+ try {
+ self.form.removeChild(self.iframe);
+ } catch (e) {
+ self.onError('jsonp polling iframe removal error', e);
+ }
+ }
+
+ try {
+ // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
+ var html = '<iframe src="javascript:0" name="'+ self.iframeId +'">';
+ iframe = document.createElement(html);
+ } catch (e) {
+ iframe = document.createElement('iframe');
+ iframe.name = self.iframeId;
+ iframe.src = 'javascript:0';
}
+
+ iframe.id = self.iframeId;
+
+ self.form.appendChild(iframe);
+ self.iframe = iframe;
+ }
+
+ initIframe();
+
+ // escape \n to prevent it from being converted into \r\n by some UAs
+ this.area.value = data.replace(rNewline, '\\n');
+
+ try {
+ this.form.submit();
+ } catch(e) {}
+
+ if (this.iframe.attachEvent) {
+ this.iframe.onreadystatechange = function(){
+ if (self.iframe.readyState == 'complete') {
+ complete();
+ }
+ };
+ } else {
+ this.iframe.onload = complete;
}
-
- return ret;
};
+},{"../util":12,"./polling":10,"global":19}],9:[function(require,module,exports){
/**
- * Adds an event.
- *
- * @api private
+ * Module requirements.
*/
-exports.on = function (element, event, fn, capture) {
- if (element.attachEvent) {
- element.attachEvent('on' + event, fn);
- } else if (element.addEventListener) {
- element.addEventListener(event, fn, capture);
- }
-};
+var XMLHttpRequest = require('xmlhttprequest');
+var Polling = require('./polling');
+var util = require('../util');
+var Emitter = require('../emitter');
+var debug = require('debug')('engine.io-client:polling-xhr');
/**
- * Load utility.
- *
- * @api private
+ * Module exports.
*/
-exports.load = function (fn) {
- var global = exports.global();
- if (global.document && document.readyState === 'complete' || pageLoaded) {
- return fn();
- }
-
- exports.on(global, 'load', fn, false);
-};
+module.exports = XHR;
+module.exports.Request = Request;
/**
- * Change the internal pageLoaded value.
+ * Global reference.
*/
-if ('undefined' != typeof window) {
- exports.load(function () {
- pageLoaded = true;
- });
-}
+var global = require('global');
/**
- * Defers a function to ensure a spinner is not displayed by the browser.
- *
- * @param {Function} fn
- * @api private
+ * Obfuscated key for Blue Coat.
*/
-exports.defer = function (fn) {
- if (!exports.ua.webkit || 'undefined' != typeof importScripts) {
- return fn();
- }
-
- exports.load(function () {
- setTimeout(fn, 100);
- });
-};
+var hasAttachEvent = global.document && global.document.attachEvent;
/**
- * JSON parse.
- *
- * @see Based on jQuery#parseJSON (MIT) and JSON2
- * @api private
+ * Empty function
*/
-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+$/;
+function empty(){}
-exports.parseJSON = function (data) {
- var global = exports.global();
+/**
+ * XHR Polling constructor.
+ *
+ * @param {Object} opts
+ * @api public
+ */
- if ('string' != typeof data || !data) {
- return null;
- }
+function XHR(opts){
+ Polling.call(this, opts);
- data = data.replace(rtrimLeft, '').replace(rtrimRight, '');
+ if (global.location) {
+ var isSSL = 'https:' == location.protocol;
+ var port = location.port;
- // Attempt to parse using the native JSON parser first
- if (global.JSON && JSON.parse) {
- return JSON.parse(data);
- }
+ // some user agents have empty `location.port`
+ if (!port) {
+ port = isSSL ? 443 : 80;
+ }
- if (rvalidchars.test(data.replace(rvalidescape, '@')
- .replace(rvalidtokens, ']')
- .replace(rvalidbraces, ''))) {
- return (new Function('return ' + data))();
+ this.xd = opts.hostname != global.location.hostname ||
+ port != opts.port;
}
-};
+}
/**
- * UA / engines detection namespace.
- *
- * @namespace
+ * Inherits from Polling.
*/
-exports.ua = {};
+util.inherits(XHR, Polling);
/**
- * Whether the UA supports CORS for XHR.
+ * Creates a request.
*
+ * @param {String} method
* @api private
*/
-exports.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () {
- var a;
- try {
- a = new XMLHttpRequest();
- } catch (e) {
- return false;
- }
-
- return a.withCredentials != undefined;
-})();
+XHR.prototype.request = function(opts){
+ opts = opts || {};
+ opts.uri = this.uri();
+ opts.xd = this.xd;
+ opts.agent = this.agent || false;
+ return new Request(opts);
+};
/**
- * Detect webkit.
+ * Sends data.
*
+ * @param {String} data to send.
+ * @param {Function} called upon flush.
* @api private
*/
-exports.ua.webkit = 'undefined' != typeof navigator &&
- /webkit/i.test(navigator.userAgent);
+XHR.prototype.doWrite = function(data, fn){
+ var req = this.request({ method: 'POST', data: data });
+ var self = this;
+ req.on('success', fn);
+ req.on('error', function(err){
+ self.onError('xhr post error', err);
+ });
+ this.sendXhr = req;
+};
/**
- * Detect gecko.
+ * Starts a poll cycle.
*
* @api private
*/
-exports.ua.gecko = 'undefined' != typeof navigator &&
- /gecko/i.test(navigator.userAgent);
+XHR.prototype.doPoll = function(){
+ debug('xhr poll');
+ var req = this.request();
+ var self = this;
+ req.on('data', function(data){
+ self.onData(data);
+ });
+ req.on('error', function(err){
+ self.onError('xhr poll error', err);
+ });
+ this.pollXhr = req;
+};
/**
- * Detect android;
+ * Request constructor
+ *
+ * @param {Object} options
+ * @api public
*/
-exports.ua.android = 'undefined' != typeof navigator &&
- /android/i.test(navigator.userAgent);
+function Request(opts){
+ this.method = opts.method || 'GET';
+ this.uri = opts.uri;
+ this.xd = !!opts.xd;
+ this.async = false !== opts.async;
+ this.data = undefined != opts.data ? opts.data : null;
+ this.agent = opts.agent;
+ this.create();
+}
/**
- * Detect iOS.
+ * Mix in `Emitter`.
*/
-exports.ua.ios = 'undefined' != typeof navigator &&
- /^(iPad|iPhone|iPod)$/.test(navigator.platform);
-exports.ua.ios6 = exports.ua.ios && /OS 6_/.test(navigator.userAgent);
+Emitter(Request.prototype);
/**
- * XHR request helper.
+ * Creates the XHR object and sends the request.
*
- * @param {Boolean} whether we need xdomain
* @api private
*/
-exports.request = function request (xdomain) {
- try {
- var _XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
- return new _XMLHttpRequest();
- } catch (e) {}
-
- if (xdomain && 'undefined' != typeof XDomainRequest && !exports.ua.hasCORS) {
- return new XDomainRequest();
- }
+Request.prototype.create = function(){
+ var xhr = this.xhr = new XMLHttpRequest({ agent: this.agent, xdomain: this.xd });
+ var self = this;
- // XMLHttpRequest can be disabled on IE
try {
- if ('undefined' != typeof XMLHttpRequest && (!xdomain || exports.ua.hasCORS)) {
- return new XMLHttpRequest();
- }
- } catch (e) { }
-
- if (!xdomain) {
- try {
- return new ActiveXObject('Microsoft.XMLHTTP');
- } catch(e) { }
- }
-};
-
-/**
- * Parses an URI
- *
- * @author Steven Levithan <stevenlevithan.com> (MIT license)
- * @api private
- */
-
-var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
-
-var parts = [
- 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host'
- , 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'
-];
+ debug('xhr open %s: %s', this.method, this.uri);
+ xhr.open(this.method, this.uri, this.async);
-exports.parseUri = function (str) {
- var m = re.exec(str || '')
- , uri = {}
- , i = 14;
+ if ('POST' == this.method) {
+ try {
+ xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
+ } catch (e) {}
+ }
- while (i--) {
- uri[parts[i]] = m[i] || '';
- }
+ // ie6 check
+ if ('withCredentials' in xhr) {
+ xhr.withCredentials = true;
+ }
- return uri;
-};
+ xhr.onreadystatechange = function(){
+ var data;
-/**
- * Compiles a querystring
- *
- * @param {Object}
- * @api private
- */
+ try {
+ if (4 != xhr.readyState) return;
+ if (200 == xhr.status || 1223 == xhr.status) {
+ data = xhr.responseText;
+ } else {
+ // make sure the `error` event handler that's user-set
+ // does not throw in the same tick and gets caught here
+ setTimeout(function(){
+ self.onError(xhr.status);
+ }, 0);
+ }
+ } catch (e) {
+ self.onError(e);
+ }
-exports.qs = function (obj) {
- var str = '';
+ if (null != data) {
+ self.onData(data);
+ }
+ };
- for (var i in obj) {
- if (obj.hasOwnProperty(i)) {
- if (str.length) str += '&';
- str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);
- }
+ debug('xhr data %s', this.data);
+ xhr.send(this.data);
+ } catch (e) {
+ // Need to defer since .create() is called directly from the constructor
+ // and thus the 'error' event can only be only bound *after* this exception
+ // occurs. Therefore, also, we cannot throw here at all.
+ setTimeout(function() {
+ self.onError(e);
+ }, 0);
+ return;
}
-
- return str;
-};
-
-/**
- * Parses a simple querystring.
- *
- * @param {String} qs
- * @api private
- */
-
-exports.qsParse = 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]);
+
+ if (hasAttachEvent) {
+ this.index = Request.requestsCount++;
+ Request.requests[this.index] = this;
}
- return qry;
};
-});
-require.register("engine.io/lib/transports/index.js", function(exports, require, module){
-
/**
- * Module dependencies
+ * Called upon successful response.
+ *
+ * @api private
*/
-var XHR = require('./polling-xhr')
- , JSONP = require('./polling-jsonp')
- , websocket = require('./websocket')
- , flashsocket = require('./flashsocket')
- , util = require('../util');
+Request.prototype.onSuccess = function(){
+ this.emit('success');
+ this.cleanup();
+};
/**
- * Export transports.
+ * Called if we have data.
+ *
+ * @api private
*/
-exports.polling = polling;
-exports.websocket = websocket;
-exports.flashsocket = flashsocket;
+Request.prototype.onData = function(data){
+ this.emit('data', data);
+ this.onSuccess();
+};
/**
- * Global reference.
+ * Called upon error.
+ *
+ * @api private
*/
-var global = util.global()
+Request.prototype.onError = function(err){
+ this.emit('error', err);
+ this.cleanup();
+};
/**
- * Polling transport polymorphic constructor.
- * Decides on xhr vs jsonp based on feature detection.
+ * Cleans up house.
*
* @api private
*/
-function polling (opts) {
- var xhr
- , xd = false
- , isXProtocol = false;
-
- if (global.location) {
- var isSSL = 'https:' == location.protocol;
- var port = location.port;
+Request.prototype.cleanup = function(){
+ if ('undefined' == typeof this.xhr ) {
+ return;
+ }
+ // xmlhttprequest
+ this.xhr.onreadystatechange = empty;
- // some user agents have empty `location.port`
- if (Number(port) !== port) {
- port = isSSL ? 443 : 80;
- }
+ try {
+ this.xhr.abort();
+ } catch(e) {}
- xd = opts.hostname != location.hostname || port != opts.port;
- isXProtocol = opts.secure != isSSL;
+ if (hasAttachEvent) {
+ delete Request.requests[this.index];
}
- xhr = util.request(xd);
- /* See #7 at http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx */
- if (isXProtocol && global.XDomainRequest && xhr instanceof global.XDomainRequest) {
- return new JSONP(opts);
- }
+ this.xhr = null;
+};
- if (xhr && !opts.forceJSONP) {
- return new XHR(opts);
- } else {
- return new JSONP(opts);
- }
+/**
+ * Aborts the request.
+ *
+ * @api public
+ */
+
+Request.prototype.abort = function(){
+ this.cleanup();
};
-});
-require.register("engine.io/lib/transports/polling.js", function(exports, require, module){
+/**
+ * Cleanup is needed for old versions of IE
+ * that leak memory unless we abort request before unload.
+ */
+
+if (hasAttachEvent) {
+ Request.requestsCount = 0;
+ Request.requests = {};
+
+ global.attachEvent('onunload', function(){
+ for (var i in Request.requests) {
+ if (Request.requests.hasOwnProperty(i)) {
+ Request.requests[i].abort();
+ }
+ }
+ });
+}
+
+},{"../emitter":2,"../util":12,"./polling":10,"debug":14,"global":19,"xmlhttprequest":13}],10:[function(require,module,exports){
/**
* Module dependencies.
*/
-var Transport = require('../transport')
- , util = require('../util')
- , parser = require('engine.io-parser')
- , debug = require('debug')('engine.io-client:polling');
+var Transport = require('../transport');
+var util = require('../util');
+var parser = require('engine.io-parser');
+var debug = require('debug')('engine.io-client:polling');
/**
* Module exports.
* Global reference.
*/
-var global = util.global();
+var global = require('global');
/**
* Polling interface.
self.write([{ type: 'close' }]);
}
- if (this.open) {
+ if ('open' == this.readyState) {
debug('transport open - closing');
close();
} else {
// in case we're trying to close while
// handshaking is in progress (GH-164)
- debug('transport not open - defering close');
+ debug('transport not open - deferring close');
this.once('open', close);
}
};
var port = '';
// cache busting is forced for IE / android / iOS6 ಠ_à²
- if (global.ActiveXObject || util.ua.android || util.ua.ios6 ||
- this.timestampRequests) {
- query[this.timestampParam] = +new Date;
+ if ('ActiveXObject' in global
+ || util.ua.chromeframe
+ || util.ua.android
+ || util.ua.ios6
+ || this.timestampRequests) {
+ if (false !== this.timestampRequests) {
+ query[this.timestampParam] = +new Date;
+ }
}
query = util.qs(query);
return schema + '://' + this.hostname + port + this.path + query;
};
-});
-require.register("engine.io/lib/transports/polling-xhr.js", function(exports, require, module){
-/**
- * Module requirements.
- */
-
-var Polling = require('./polling')
- , util = require('../util')
- , Emitter = require('../emitter')
- , debug = require('debug')('engine.io-client:polling-xhr');
-
+},{"../transport":5,"../util":12,"debug":14,"engine.io-parser":16,"global":19}],11:[function(require,module,exports){
/**
- * Module exports.
+ * Module dependencies.
*/
-module.exports = XHR;
-module.exports.Request = Request;
+var Transport = require('../transport');
+var parser = require('engine.io-parser');
+var util = require('../util');
+var debug = require('debug')('engine.io-client:websocket');
/**
- * Global reference.
+ * `ws` exposes a WebSocket-compatible interface in
+ * Node, or the `WebSocket` or `MozWebSocket` globals
+ * in the browser.
*/
-var global = util.global();
-
+var WebSocket = require('ws');
/**
- * Obfuscated key for Blue Coat.
+ * Module exports.
*/
-var xobject = global[['Active'].concat('Object').join('X')];
+module.exports = WS;
/**
- * Empty function
+ * Global reference.
*/
-function empty(){}
+var global = require('global');
/**
- * XHR Polling constructor.
+ * WebSocket transport constructor.
*
- * @param {Object} opts
+ * @api {Object} connection options
* @api public
*/
-function XHR(opts){
- Polling.call(this, opts);
-
- if (global.location) {
- var isSSL = 'https:' == location.protocol;
- var port = location.port;
-
- // some user agents have empty `location.port`
- if (Number(port) !== port) {
- port = isSSL ? 443 : 80;
- }
-
- this.xd = opts.hostname != global.location.hostname ||
- port != opts.port;
- }
-};
+function WS(opts){
+ Transport.call(this, opts);
+}
/**
- * Inherits from Polling.
+ * Inherits from Transport.
*/
-util.inherits(XHR, Polling);
+util.inherits(WS, Transport);
/**
- * Opens the socket
+ * Transport name.
*
- * @api private
+ * @api public
*/
-XHR.prototype.doOpen = function(){
- var self = this;
- util.defer(function(){
- Polling.prototype.doOpen.call(self);
- });
-};
+WS.prototype.name = 'websocket';
/**
- * Creates a request.
+ * Opens socket.
*
- * @param {String} method
* @api private
*/
-XHR.prototype.request = function(opts){
- opts = opts || {};
- opts.uri = this.uri();
- opts.xd = this.xd;
- return new Request(opts);
-};
-
-/**
- * Sends data.
- *
- * @param {String} data to send.
- * @param {Function} called upon flush.
- * @api private
- */
+WS.prototype.doOpen = function(){
+ if (!this.check()) {
+ // let probe timeout
+ return;
+ }
-XHR.prototype.doWrite = function(data, fn){
- var req = this.request({ method: 'POST', data: data });
var self = this;
- req.on('success', fn);
- req.on('error', function(err){
- self.onError('xhr post error', err);
- });
- this.sendXhr = req;
-};
-
-/**
- * Starts a poll cycle.
- *
- * @api private
- */
+ var uri = this.uri();
+ var protocols = void(0);
+ var opts = { agent: this.agent };
-XHR.prototype.doPoll = function(){
- debug('xhr poll');
- var req = this.request();
- var self = this;
- req.on('data', function(data){
- self.onData(data);
- });
- req.on('error', function(err){
- self.onError('xhr poll error', err);
- });
- this.pollXhr = req;
+ this.socket = new WebSocket(uri, protocols, opts);
+ this.addEventListeners();
};
/**
- * Request constructor
- *
- * @param {Object} options
- * @api public
- */
-
-function Request(opts){
- this.method = opts.method || 'GET';
- this.uri = opts.uri;
- this.xd = !!opts.xd;
- this.async = false !== opts.async;
- this.data = undefined != opts.data ? opts.data : null;
- this.create();
-}
-
-/**
- * Mix in `Emitter`.
- */
-
-Emitter(Request.prototype);
-
-/**
- * Creates the XHR object and sends the request.
+ * Adds event listeners to the socket
*
- * @api private
- */
-
-Request.prototype.create = function(){
- var xhr = this.xhr = util.request(this.xd);
- var self = this;
-
- xhr.open(this.method, this.uri, this.async);
-
- if ('POST' == this.method) {
- try {
- if (xhr.setRequestHeader) {
- // xmlhttprequest
- xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
- } else {
- // xdomainrequest
- xhr.contentType = 'text/plain';
- }
- } catch (e) {}
- }
-
- if (this.xd && global.XDomainRequest && xhr instanceof XDomainRequest) {
- xhr.onerror = function(e){
- self.onError(e);
- };
- xhr.onload = function(){
- self.onData(xhr.responseText);
- };
- xhr.onprogress = empty;
- } else {
- // ie6 check
- if ('withCredentials' in xhr) {
- xhr.withCredentials = true;
- }
-
- xhr.onreadystatechange = function(){
- var data;
-
- try {
- if (4 != xhr.readyState) return;
- if (200 == xhr.status || 1223 == xhr.status) {
- data = xhr.responseText;
- } else {
- self.onError(xhr.status);
- }
- } catch (e) {
- self.onError(e);
- }
-
- if (undefined !== data) {
- self.onData(data);
- }
- };
- }
+ * @api private
+ */
- debug('sending xhr with url %s | data %s', this.uri, this.data);
- xhr.send(this.data);
+WS.prototype.addEventListeners = function(){
+ var self = this;
- if (xobject) {
- this.index = Request.requestsCount++;
- Request.requests[this.index] = this;
- }
+ this.socket.onopen = function(){
+ self.onOpen();
+ };
+ this.socket.onclose = function(){
+ self.onClose();
+ };
+ this.socket.onmessage = function(ev){
+ self.onData(ev.data);
+ };
+ this.socket.onerror = function(e){
+ self.onError('websocket error', e);
+ };
};
/**
- * Called upon successful response.
+ * Override `onData` to use a timer on iOS.
+ * See: https://gist.github.com/mloughran/2052006
*
* @api private
*/
-Request.prototype.onSuccess = function(){
- this.emit('success');
- this.cleanup();
+if ('undefined' != typeof navigator
+ && /iPad|iPhone|iPod/i.test(navigator.userAgent)) {
+ WS.prototype.onData = function(data){
+ var self = this;
+ setTimeout(function(){
+ Transport.prototype.onData.call(self, data);
+ }, 0);
+ };
+}
+
+/**
+ * Writes data to socket.
+ *
+ * @param {Array} array of packets.
+ * @api private
+ */
+
+WS.prototype.write = function(packets){
+ var self = this;
+ this.writable = false;
+ // encodePacket efficient as it uses WS framing
+ // no need for encodePayload
+ for (var i = 0, l = packets.length; i < l; i++) {
+ this.socket.send(parser.encodePacket(packets[i]));
+ }
+ function ondrain() {
+ self.writable = true;
+ self.emit('drain');
+ }
+ // fake drain
+ // defer to next tick to allow Socket to clear writeBuffer
+ setTimeout(ondrain, 0);
};
/**
- * Called if we have data.
+ * Called upon close
*
* @api private
*/
-Request.prototype.onData = function(data){
- this.emit('data', data);
- this.onSuccess();
+WS.prototype.onClose = function(){
+ Transport.prototype.onClose.call(this);
};
/**
- * Called upon error.
+ * Closes socket.
*
* @api private
*/
-Request.prototype.onError = function(err){
- this.emit('error', err);
- this.cleanup();
+WS.prototype.doClose = function(){
+ if (typeof this.socket !== 'undefined') {
+ this.socket.close();
+ }
};
/**
- * Cleans up house.
+ * Generates uri for connection.
*
* @api private
*/
-Request.prototype.cleanup = function(){
- if ('undefined' == typeof this.xhr ) {
- return;
+WS.prototype.uri = function(){
+ var query = this.query || {};
+ var schema = this.secure ? 'wss' : 'ws';
+ var port = '';
+
+ // avoid port if default for schema
+ if (this.port && (('wss' == schema && this.port != 443)
+ || ('ws' == schema && this.port != 80))) {
+ port = ':' + this.port;
}
- // xmlhttprequest
- this.xhr.onreadystatechange = empty;
- // xdomainrequest
- this.xhr.onload = this.xhr.onerror = empty;
+ // append timestamp to URI
+ if (this.timestampRequests) {
+ query[this.timestampParam] = +new Date;
+ }
- try {
- this.xhr.abort();
- } catch(e) {}
+ query = util.qs(query);
- if (xobject) {
- delete Request.requests[this.index];
+ // prepend ? to query
+ if (query.length) {
+ query = '?' + query;
}
- this.xhr = null;
+ return schema + '://' + this.hostname + port + this.path + query;
};
/**
- * Aborts the request.
+ * Feature detection for WebSocket.
*
+ * @return {Boolean} whether this transport is available.
* @api public
*/
-Request.prototype.abort = function(){
- this.cleanup();
+WS.prototype.check = function(){
+ return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name);
};
-if (xobject) {
- Request.requestsCount = 0;
- Request.requests = {};
+},{"../transport":5,"../util":12,"debug":14,"engine.io-parser":16,"global":19,"ws":22}],12:[function(require,module,exports){
- global.attachEvent('onunload', function(){
- for (var i in Request.requests) {
- if (Request.requests.hasOwnProperty(i)) {
- Request.requests[i].abort();
- }
- }
- });
-}
+var global = require('global');
-});
-require.register("engine.io/lib/transports/polling-jsonp.js", function(exports, require, module){
+/**
+ * Status of page load.
+ */
+
+var pageLoaded = false;
/**
- * Module requirements.
+ * Inheritance.
+ *
+ * @param {Function} ctor a
+ * @param {Function} ctor b
+ * @api private
*/
-var Polling = require('./polling')
- , util = require('../util');
+exports.inherits = function inherits (a, b) {
+ function c () { }
+ c.prototype = b.prototype;
+ a.prototype = new c;
+};
/**
- * Module exports.
+ * Object.keys
*/
-module.exports = JSONPPolling;
+exports.keys = Object.keys || function (obj) {
+ var ret = [];
+ var has = Object.prototype.hasOwnProperty;
+
+ for (var i in obj) {
+ if (has.call(obj, i)) {
+ ret.push(i);
+ }
+ }
+
+ return ret;
+};
/**
- * Global reference.
+ * Adds an event.
+ *
+ * @api private
*/
-var global = util.global();
+exports.on = function (element, event, fn, capture) {
+ if (element.attachEvent) {
+ element.attachEvent('on' + event, fn);
+ } else if (element.addEventListener) {
+ element.addEventListener(event, fn, capture);
+ }
+};
/**
- * Cached regular expressions.
+ * Load utility.
+ *
+ * @api private
*/
-var rNewline = /\n/g;
+exports.load = function (fn) {
+ if (global.document && document.readyState === 'complete' || pageLoaded) {
+ return fn();
+ }
+
+ exports.on(global, 'load', fn, false);
+};
/**
- * Global JSONP callbacks.
+ * Change the internal pageLoaded value.
*/
-var callbacks;
+if ('undefined' != typeof window) {
+ exports.load(function () {
+ pageLoaded = true;
+ });
+}
/**
- * Callbacks count.
+ * JSON parse.
+ *
+ * @see Based on jQuery#parseJSON (MIT) and JSON2
+ * @api private
*/
-var index = 0;
+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+$/;
+
+exports.parseJSON = function (data) {
+ if ('string' != typeof data || !data) {
+ return null;
+ }
+
+ data = data.replace(rtrimLeft, '').replace(rtrimRight, '');
+
+ // Attempt to parse using the native JSON parser first
+ if (global.JSON && JSON.parse) {
+ return JSON.parse(data);
+ }
+
+ if (rvalidchars.test(data.replace(rvalidescape, '@')
+ .replace(rvalidtokens, ']')
+ .replace(rvalidbraces, ''))) {
+ return (new Function('return ' + data))();
+ }
+};
/**
- * Noop.
+ * UA / engines detection namespace.
+ *
+ * @namespace
*/
-function empty () { }
+exports.ua = {};
/**
- * JSONP Polling constructor.
+ * Detect webkit.
*
- * @param {Object} opts.
- * @api public
+ * @api private
*/
-function JSONPPolling (opts) {
- Polling.call(this, opts);
+exports.ua.webkit = 'undefined' != typeof navigator &&
+ /webkit/i.test(navigator.userAgent);
- // 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;
- }
+/**
+ * Detect gecko.
+ *
+ * @api private
+ */
- // callback identifier
- this.index = callbacks.length;
+exports.ua.gecko = 'undefined' != typeof navigator &&
+ /gecko/i.test(navigator.userAgent);
- // add callback to jsonp global
- var self = this;
- callbacks.push(function (msg) {
- self.onData(msg);
- });
+/**
+ * Detect android;
+ */
+
+exports.ua.android = 'undefined' != typeof navigator &&
+ /android/i.test(navigator.userAgent);
+
+/**
+ * Detect iOS.
+ */
- // append to query string
- this.query.j = this.index;
-};
+exports.ua.ios = 'undefined' != typeof navigator &&
+ /^(iPad|iPhone|iPod)$/.test(navigator.platform);
+exports.ua.ios6 = exports.ua.ios && /OS 6_/.test(navigator.userAgent);
/**
- * Inherits from Polling.
+ * Detect Chrome Frame.
*/
-util.inherits(JSONPPolling, Polling);
+exports.ua.chromeframe = Boolean(global.externalHost);
/**
- * Opens the socket.
+ * Parses an URI
*
+ * @author Steven Levithan <stevenlevithan.com> (MIT license)
* @api private
*/
-JSONPPolling.prototype.doOpen = function () {
- var self = this;
- util.defer(function () {
- Polling.prototype.doOpen.call(self);
- });
+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'
+];
+
+exports.parseUri = function (str) {
+ var m = re.exec(str || '')
+ , uri = {}
+ , i = 14;
+
+ while (i--) {
+ uri[parts[i]] = m[i] || '';
+ }
+
+ return uri;
};
/**
- * Closes the socket
+ * Compiles a querystring
*
+ * @param {Object}
* @api private
*/
-JSONPPolling.prototype.doClose = function () {
- if (this.script) {
- this.script.parentNode.removeChild(this.script);
- this.script = null;
- }
+exports.qs = function (obj) {
+ var str = '';
- if (this.form) {
- this.form.parentNode.removeChild(this.form);
- this.form = null;
+ for (var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ if (str.length) str += '&';
+ str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);
+ }
}
- Polling.prototype.doClose.call(this);
+ return str;
};
/**
- * Starts a poll cycle.
+ * Parses a simple querystring.
*
+ * @param {String} qs
* @api private
*/
-JSONPPolling.prototype.doPoll = function () {
- var self = this;
- var script = document.createElement('script');
-
- if (this.script) {
- this.script.parentNode.removeChild(this.script);
- this.script = null;
+exports.qsParse = 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;
+};
- script.async = true;
- script.src = this.uri();
- script.onerror = function(e){
- self.onError('jsonp poll error',e);
- }
+},{"global":19}],13:[function(require,module,exports){
+// browser shim for xmlhttprequest module
+var hasCORS = require('has-cors');
- var insertAt = document.getElementsByTagName('script')[0];
- insertAt.parentNode.insertBefore(script, insertAt);
- this.script = script;
+module.exports = function(opts) {
+ var xdomain = opts.xdomain;
+ // XMLHttpRequest can be disabled on IE
+ try {
+ if ('undefined' != typeof XMLHttpRequest && (!xdomain || hasCORS)) {
+ return new XMLHttpRequest();
+ }
+ } catch (e) { }
- if (util.ua.gecko) {
- setTimeout(function () {
- var iframe = document.createElement('iframe');
- document.body.appendChild(iframe);
- document.body.removeChild(iframe);
- }, 100);
+ if (!xdomain) {
+ try {
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ } catch(e) { }
}
-};
+}
+
+},{"has-cors":20}],14:[function(require,module,exports){
/**
- * Writes with a hidden iframe.
+ * Expose `debug()` as the module.
+ */
+
+module.exports = debug;
+
+/**
+ * Create a debugger with the given `name`.
*
- * @param {String} data to send
- * @param {Function} called upon flush.
- * @api private
+ * @param {String} name
+ * @return {Type}
+ * @api public
*/
-JSONPPolling.prototype.doWrite = function (data, fn) {
- var self = this;
+function debug(name) {
+ if (!debug.enabled(name)) return function(){};
- if (!this.form) {
- var form = document.createElement('form');
- var area = document.createElement('textarea');
- var id = this.iframeId = 'eio_iframe_' + this.index;
- var iframe;
+ return function(fmt){
+ fmt = coerce(fmt);
- 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);
+ var curr = new Date;
+ var ms = curr - (debug[name] || curr);
+ debug[name] = curr;
- this.form = form;
- this.area = area;
+ fmt = name
+ + ' '
+ + fmt
+ + ' +' + debug.humanize(ms);
+
+ // This hackery is required for IE8
+ // where `console.log` doesn't have 'apply'
+ window.console
+ && console.log
+ && Function.prototype.apply.call(console.log, console, arguments);
}
+}
- this.form.action = this.uri();
+/**
+ * The currently active debug mode names.
+ */
- function complete () {
- initIframe();
- fn();
- };
+debug.names = [];
+debug.skips = [];
- function initIframe () {
- if (self.iframe) {
- try {
- self.form.removeChild(self.iframe);
- } catch (e) {
- self.onError('jsonp polling iframe removal error', e);
- }
- }
+/**
+ * Enables a debug mode by name. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} name
+ * @api public
+ */
- try {
- // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
- var html = '<iframe src="javascript:0" name="'+ self.iframeId +'">';
- iframe = document.createElement(html);
- } catch (e) {
- iframe = document.createElement('iframe');
- iframe.name = self.iframeId;
- iframe.src = 'javascript:0';
- }
+debug.enable = function(name) {
+ try {
+ localStorage.debug = name;
+ } catch(e){}
- iframe.id = self.iframeId;
+ var split = (name || '').split(/[\s,]+/)
+ , len = split.length;
- self.form.appendChild(iframe);
- self.iframe = iframe;
- };
+ for (var i = 0; i < len; i++) {
+ name = split[i].replace('*', '.*?');
+ if (name[0] === '-') {
+ debug.skips.push(new RegExp('^' + name.substr(1) + '$'));
+ }
+ else {
+ debug.names.push(new RegExp('^' + name + '$'));
+ }
+ }
+};
- initIframe();
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
- // escape \n to prevent it from being converted into \r\n by some UAs
- this.area.value = data.replace(rNewline, '\\n');
+debug.disable = function(){
+ debug.enable('');
+};
- try {
- this.form.submit();
- } catch(e) {}
+/**
+ * Humanize the given `ms`.
+ *
+ * @param {Number} m
+ * @return {String}
+ * @api private
+ */
- if (this.iframe.attachEvent) {
- this.iframe.onreadystatechange = function(){
- if (self.iframe.readyState == 'complete') {
- complete();
- }
- };
- } else {
- this.iframe.onload = complete;
- }
+debug.humanize = function(ms) {
+ var sec = 1000
+ , min = 60 * 1000
+ , hour = 60 * min;
+
+ if (ms >= 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';
};
-});
-require.register("engine.io/lib/transports/websocket.js", function(exports, require, module){
/**
- * Module dependencies.
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
*/
-var Transport = require('../transport')
- , parser = require('engine.io-parser')
- , util = require('../util')
- , debug = require('debug')('engine.io-client:websocket');
+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;
+ }
+ }
+ return false;
+};
/**
- * Module exports.
+ * Coerce `val`.
*/
-module.exports = WS;
+function coerce(val) {
+ if (val instanceof Error) return val.stack || val.message;
+ return val;
+}
+
+// persist
-/**
- * Global reference.
- */
+try {
+ if (window.localStorage) debug.enable(localStorage.debug);
+} catch(e){}
-var global = util.global();
+},{}],15:[function(require,module,exports){
/**
- * WebSocket transport constructor.
- *
- * @api {Object} connection options
- * @api public
+ * Module dependencies.
*/
-function WS(opts){
- Transport.call(this, opts);
-};
+var index = require('indexof');
/**
- * Inherits from Transport.
+ * Expose `Emitter`.
*/
-util.inherits(WS, Transport);
+module.exports = Emitter;
/**
- * Transport name.
+ * Initialize a new `Emitter`.
*
* @api public
*/
-WS.prototype.name = 'websocket';
+function Emitter(obj) {
+ if (obj) return mixin(obj);
+};
/**
- * Opens socket.
+ * Mixin the emitter properties.
*
+ * @param {Object} obj
+ * @return {Object}
* @api private
*/
-WS.prototype.doOpen = function(){
- if (!this.check()) {
- // let probe timeout
- return;
+function mixin(obj) {
+ for (var key in Emitter.prototype) {
+ obj[key] = Emitter.prototype[key];
}
-
- var self = this;
-
- this.socket = new (ws())(this.uri());
- this.socket.onopen = function(){
- self.onOpen();
- };
- this.socket.onclose = function(){
- self.onClose();
- };
- this.socket.onmessage = function(ev){
- self.onData(ev.data);
- };
- this.socket.onerror = function(e){
- self.onError('websocket error', e);
- };
-};
+ return obj;
+}
/**
- * Override `onData` to use a timer on iOS.
- * See: https://gist.github.com/mloughran/2052006
+ * Listen on the given `event` with `fn`.
*
- * @api private
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
*/
-if ('undefined' != typeof navigator
- && /iPad|iPhone|iPod/i.test(navigator.userAgent)) {
- WS.prototype.onData = function(data){
- var self = this;
- setTimeout(function(){
- Transport.prototype.onData.call(self, data);
- }, 0);
- };
-}
+Emitter.prototype.on = function(event, fn){
+ this._callbacks = this._callbacks || {};
+ (this._callbacks[event] = this._callbacks[event] || [])
+ .push(fn);
+ return this;
+};
/**
- * Writes data to socket.
+ * Adds an `event` listener that will be invoked a single
+ * time then automatically removed.
*
- * @param {Array} array of packets.
- * @api private
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
*/
-WS.prototype.write = function(packets){
+Emitter.prototype.once = function(event, fn){
var self = this;
- this.writable = false;
- // encodePacket efficient as it uses WS framing
- // no need for encodePayload
- for (var i = 0, l = packets.length; i < l; i++) {
- this.socket.send(parser.encodePacket(packets[i]));
- }
- function ondrain() {
- self.writable = true;
- self.emit('drain');
- }
- // check periodically if we're done sending
- if ('bufferedAmount' in this.socket) {
- this.bufferedAmountId = setInterval(function() {
- if (self.socket.bufferedAmount == 0) {
- clearInterval(self.bufferedAmountId);
- ondrain();
- }
- }, 50);
- } else {
- // fake drain
- // defer to next tick to allow Socket to clear writeBuffer
- setTimeout(ondrain, 0);
+ this._callbacks = this._callbacks || {};
+
+ function on() {
+ self.off(event, on);
+ fn.apply(this, arguments);
}
+
+ fn._off = on;
+ this.on(event, on);
+ return this;
};
/**
- * Called upon close
+ * Remove the given callback for `event` or all
+ * registered callbacks.
*
- * @api private
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
*/
-WS.prototype.onClose = function(){
- // stop checking to see if websocket is done sending buffer
- clearInterval(this.bufferedAmountId);
- Transport.prototype.onClose.call(this);
-};
+Emitter.prototype.off =
+Emitter.prototype.removeListener =
+Emitter.prototype.removeAllListeners = function(event, fn){
+ this._callbacks = this._callbacks || {};
-/**
- * Closes socket.
- *
- * @api private
- */
+ // all
+ if (0 == arguments.length) {
+ this._callbacks = {};
+ return this;
+ }
-WS.prototype.doClose = function(){
- if (typeof this.socket !== 'undefined') {
- this.socket.close();
+ // specific event
+ var callbacks = this._callbacks[event];
+ if (!callbacks) return this;
+
+ // remove all handlers
+ if (1 == arguments.length) {
+ delete this._callbacks[event];
+ return this;
}
+
+ // remove specific handler
+ var i = index(callbacks, fn._off || fn);
+ if (~i) callbacks.splice(i, 1);
+ return this;
};
/**
- * Generates uri for connection.
+ * Emit `event` with the given args.
*
- * @api private
+ * @param {String} event
+ * @param {Mixed} ...
+ * @return {Emitter}
*/
-WS.prototype.uri = function(){
- var query = this.query || {};
- var schema = this.secure ? 'wss' : 'ws';
- var port = '';
-
- // avoid port if default for schema
- if (this.port && (('wss' == schema && this.port != 443)
- || ('ws' == schema && this.port != 80))) {
- port = ':' + this.port;
- }
-
- // append timestamp to URI
- if (this.timestampRequests) {
- query[this.timestampParam] = +new Date;
- }
-
- query = util.qs(query);
+Emitter.prototype.emit = function(event){
+ this._callbacks = this._callbacks || {};
+ var args = [].slice.call(arguments, 1)
+ , callbacks = this._callbacks[event];
- // prepend ? to query
- if (query.length) {
- query = '?' + query;
+ if (callbacks) {
+ callbacks = callbacks.slice(0);
+ for (var i = 0, len = callbacks.length; i < len; ++i) {
+ callbacks[i].apply(this, args);
+ }
}
- return schema + '://' + this.hostname + port + this.path + query;
+ return this;
};
/**
- * Feature detection for WebSocket.
+ * Return array of callbacks for `event`.
*
- * @return {Boolean} whether this transport is available.
+ * @param {String} event
+ * @return {Array}
* @api public
*/
-WS.prototype.check = function(){
- var websocket = ws();
- return !!websocket && !('__initialize' in websocket && this.name === WS.prototype.name);
+Emitter.prototype.listeners = function(event){
+ this._callbacks = this._callbacks || {};
+ return this._callbacks[event] || [];
};
/**
- * Getter for WS constructor.
+ * Check if this emitter has `event` handlers.
*
- * @api private
+ * @param {String} event
+ * @return {Boolean}
+ * @api public
*/
-function ws(){
- if ('undefined' == typeof window) {
- return require('ws');
- }
+Emitter.prototype.hasListeners = function(event){
+ return !! this.listeners(event).length;
+};
- return global.WebSocket || global.MozWebSocket;
-}
+},{"indexof":21}],16:[function(require,module,exports){
-});
-require.register("engine.io/lib/transports/flashsocket.js", function(exports, require, module){
+module.exports = require('./lib/');
+
+},{"./lib/":17}],17:[function(require,module,exports){
/**
* Module dependencies.
*/
-var WS = require('./websocket')
- , util = require('../util')
- , debug = require('debug')('engine.io-client:flashsocket');
+var keys = require('./keys');
/**
- * Module exports.
+ * Current protocol version.
*/
+exports.protocol = 2;
-module.exports = FlashWS;
+/**
+ * Packet types.
+ */
+
+var packets = exports.packets = {
+ open: 0 // non-ws
+ , close: 1 // non-ws
+ , ping: 2
+ , pong: 3
+ , message: 4
+ , upgrade: 5
+ , noop: 6
+};
+
+var packetslist = keys(packets);
/**
- * Global reference.
+ * Premade error packet.
*/
-var global = util.global()
+var err = { type: 'error', data: 'parser error' };
/**
- * Obfuscated key for Blue Coat.
+ * Encodes a packet.
+ *
+ * <packet type id> [ `:` <data> ]
+ *
+ * Example:
+ *
+ * 5:hello world
+ * 3
+ * 4
+ *
+ * @api private
*/
-var xobject = global[['Active'].concat('Object').join('X')];
+exports.encodePacket = function (packet) {
+ var encoded = packets[packet.type];
+
+ // data fragment is optional
+ if (undefined !== packet.data) {
+ encoded += String(packet.data);
+ }
+
+ return '' + encoded;
+};
/**
- * FlashWS constructor.
+ * Decodes a packet.
*
- * @api public
+ * @return {Object} with `type` and `data` (if any)
+ * @api private
*/
-function FlashWS (options) {
- WS.call(this, options);
- this.flashPath = options.flashPath;
- this.policyPort = options.policyPort;
-};
+exports.decodePacket = function (data) {
+ var type = data.charAt(0);
-/**
- * Inherits from WebSocket.
- */
+ if (Number(type) != type || !packetslist[type]) {
+ return err;
+ }
-util.inherits(FlashWS, WS);
+ if (data.length > 1) {
+ return { type: packetslist[type], data: data.substring(1) };
+ } else {
+ return { type: packetslist[type] };
+ }
+};
/**
- * Transport name.
+ * Encodes multiple messages (payload).
*
- * @api public
+ * <length>:data
+ *
+ * Example:
+ *
+ * 11:hello world2:hi
+ *
+ * @param {Array} packets
+ * @api private
*/
-FlashWS.prototype.name = 'flashsocket';
+exports.encodePayload = function (packets) {
+ if (!packets.length) {
+ return '0:';
+ }
-/**
- * Opens the transport.
+ 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;
+};
+
+/*
+ * Decodes data when a payload is maybe expected.
*
+ * @param {String} data, callback method
* @api public
*/
-FlashWS.prototype.doOpen = function () {
- if (!this.check()) {
- // let the probe timeout
- return;
+exports.decodePayload = function (data, callback) {
+ var packet;
+ if (data == '') {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
}
- // instrument websocketjs logging
- function log (type) {
- return function(){
- var str = Array.prototype.join.call(arguments, ' ');
- debug('[websocketjs %s] %s', type, str);
- };
- };
+ var length = ''
+ , n, msg;
- WEB_SOCKET_LOGGER = { log: log('debug'), error: log('error') };
- WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;
- WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
+ for (var i = 0, l = data.length; i < l; i++) {
+ var chr = data.charAt(i);
- if ('undefined' == typeof WEB_SOCKET_SWF_LOCATION) {
- WEB_SOCKET_SWF_LOCATION = this.flashPath + 'WebSocketMainInsecure.swf';
- }
+ if (':' != chr) {
+ length += chr;
+ } else {
+ if ('' == length || (length != (n = Number(length)))) {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
- // dependencies
- var deps = [this.flashPath + 'web_socket.js'];
+ msg = data.substr(i + 1, n);
- if ('undefined' == typeof swfobject) {
- deps.unshift(this.flashPath + 'swfobject.js');
- }
+ if (length != msg.length) {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
- var self = this;
+ if (msg.length) {
+ packet = exports.decodePacket(msg);
- load(deps, function () {
- self.ready(function () {
- WebSocket.__addTask(function () {
- WS.prototype.doOpen.call(self);
- });
- });
- });
-};
+ if (err.type == packet.type && err.data == packet.data) {
+ // parser error in individual packet - ignoring payload
+ return callback(err, 0, 1);
+ }
-/**
- * Override to prevent closing uninitialized flashsocket.
- *
- * @api private
- */
+ var ret = callback(packet, i + n, l);
+ if (false === ret) return;
+ }
-FlashWS.prototype.doClose = function () {
- if (!this.socket) return;
- var self = this;
- WebSocket.__addTask(function() {
- WS.prototype.doClose.call(self);
- });
-};
+ // advance cursor
+ i += n;
+ length = '';
+ }
+ }
-/**
- * Writes to the Flash socket.
- *
- * @api private
- */
+ if (length != '') {
+ // parser error - ignoring payload
+ return callback(err, 0, 1);
+ }
-FlashWS.prototype.write = function() {
- var self = this, args = arguments;
- WebSocket.__addTask(function () {
- WS.prototype.write.apply(self, args);
- });
};
+},{"./keys":18}],18:[function(require,module,exports){
+
/**
- * Called upon dependencies are loaded.
+ * Gets the keys for an object.
*
+ * @return {Array} keys
* @api private
*/
-FlashWS.prototype.ready = function (fn) {
- if (typeof WebSocket == 'undefined' ||
- !('__initialize' in WebSocket) || !swfobject) {
- return;
- }
+module.exports = Object.keys || function keys (obj){
+ var arr = [];
+ var has = Object.prototype.hasOwnProperty;
- if (swfobject.getFlashPlayerVersion().major < 10) {
- return;
+ for (var i in obj) {
+ if (has.call(obj, i)) {
+ arr.push(i);
+ }
}
+ return arr;
+};
- function init () {
- // Only start downloading the swf file when the checked that this browser
- // actually supports it
- if (!FlashWS.loaded) {
- if (843 != self.policyPort) {
- WebSocket.loadFlashPolicyFile('xmlsocket://' + self.host + ':' + self.policyPort);
- }
+},{}],19:[function(require,module,exports){
- WebSocket.__initialize();
- FlashWS.loaded = true;
- }
+/**
+ * Returns `this`. Execute this without a "context" (i.e. without it being
+ * attached to an object of the left-hand side), and `this` points to the
+ * "global" scope of the current JS execution.
+ */
- fn.call(self);
- }
+module.exports = (function () { return this; })();
- var self = this;
- if (document.body) {
- return init();
- }
+},{}],20:[function(require,module,exports){
- util.load(init);
-};
+/**
+ * Module dependencies.
+ */
+
+var global = require('global');
/**
- * Feature detection for flashsocket.
+ * Module exports.
*
- * @return {Boolean} whether this transport is available.
- * @api public
+ * Logic borrowed from Modernizr:
+ *
+ * - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js
*/
-FlashWS.prototype.check = function () {
- if ('undefined' == typeof window) {
- return false;
- }
+try {
+ module.exports = 'XMLHttpRequest' in global &&
+ 'withCredentials' in new global.XMLHttpRequest();
+} catch (err) {
+ // if XMLHttp support is disabled in IE then it will throw
+ // when trying to create
+ module.exports = false;
+}
- if (typeof WebSocket != 'undefined' && !('__initialize' in WebSocket)) {
- return false;
- }
+},{"global":19}],21:[function(require,module,exports){
- if (xobject) {
- var control = null;
- try {
- control = new xobject('ShockwaveFlash.ShockwaveFlash');
- } catch (e) { }
- if (control) {
- return true;
- }
- } else {
- for (var i = 0, l = navigator.plugins.length; i < l; i++) {
- for (var j = 0, m = navigator.plugins[i].length; j < m; j++) {
- if (navigator.plugins[i][j].description == 'Shockwave Flash') {
- return true;
- }
- }
- }
- }
+var indexOf = [].indexOf;
- return false;
+module.exports = function(arr, obj){
+ if (indexOf) return arr.indexOf(obj);
+ for (var i = 0; i < arr.length; ++i) {
+ if (arr[i] === obj) return i;
+ }
+ return -1;
};
+},{}],22:[function(require,module,exports){
/**
- * Lazy loading of scripts.
- * Based on $script by Dustin Diaz - MIT
+ * Module dependencies.
*/
-var scripts = {};
+var global = (function() { return this; })();
/**
- * Injects a script. Keeps tracked of injected ones.
- *
- * @param {String} path
- * @param {Function} callback
- * @api private
+ * WebSocket constructor.
*/
-function create (path, fn) {
- if (scripts[path]) return fn();
-
- var el = document.createElement('script');
- var loaded = false;
-
- debug('loading "%s"', path);
- el.onload = el.onreadystatechange = function () {
- if (loaded || scripts[path]) return;
- var rs = el.readyState;
- if (!rs || 'loaded' == rs || 'complete' == rs) {
- debug('loaded "%s"', path);
- el.onload = el.onreadystatechange = null;
- loaded = true;
- scripts[path] = true;
- fn();
- }
- };
+var WebSocket = global.WebSocket || global.MozWebSocket;
- el.async = 1;
- el.src = path;
+/**
+ * Module exports.
+ */
- var head = document.getElementsByTagName('head')[0];
- head.insertBefore(el, head.firstChild);
-};
+module.exports = WebSocket ? ws : null;
/**
- * Loads scripts and fires a callback.
+ * WebSocket constructor.
*
- * @param {Array} paths
- * @param {Function} callback
+ * The third `opts` options object gets ignored in web browsers, since it's
+ * non-standard, and throws a TypeError if passed to the constructor.
+ * See: https://github.com/einaros/ws/issues/227
+ *
+ * @param {String} uri
+ * @param {Array} protocols (optional)
+ * @param {Object) opts (optional)
+ * @api public
*/
-function load (arr, fn) {
- function process (i) {
- if (!arr[i]) return fn();
- create(arr[i], function () {
- process(++i);
- });
- };
+function ws(uri, protocols, opts) {
+ var instance;
+ if (protocols) {
+ instance = new WebSocket(uri, protocols);
+ } else {
+ instance = new WebSocket(uri);
+ }
+ return instance;
+}
- process(0);
-};
+if (WebSocket) ws.prototype = WebSocket.prototype;
+},{}]},{},[1])
+(1)
});
-require.alias("component-emitter/index.js", "engine.io/deps/emitter/index.js");
-require.alias("component-emitter/index.js", "emitter/index.js");
-
-require.alias("component-indexof/index.js", "engine.io/deps/indexof/index.js");
-require.alias("component-indexof/index.js", "indexof/index.js");
-
-require.alias("LearnBoost-engine.io-protocol/lib/index.js", "engine.io/deps/engine.io-parser/lib/index.js");
-require.alias("LearnBoost-engine.io-protocol/lib/keys.js", "engine.io/deps/engine.io-parser/lib/keys.js");
-require.alias("LearnBoost-engine.io-protocol/lib/index.js", "engine.io/deps/engine.io-parser/index.js");
-require.alias("LearnBoost-engine.io-protocol/lib/index.js", "engine.io-parser/index.js");
-require.alias("LearnBoost-engine.io-protocol/lib/index.js", "LearnBoost-engine.io-protocol/index.js");
-
-require.alias("visionmedia-debug/index.js", "engine.io/deps/debug/index.js");
-require.alias("visionmedia-debug/debug.js", "engine.io/deps/debug/debug.js");
-require.alias("visionmedia-debug/index.js", "debug/index.js");
-
-require.alias("engine.io/lib/index.js", "engine.io/index.js");
-
-if (typeof exports == "object") {
- module.exports = require("engine.io");
-} else if (typeof define == "function" && define.amd) {
- define(function(){ return require("engine.io"); });
-} else {
- this["eio"] = require("engine.io");
-}})();
\ No newline at end of file
+;