(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.io = f()}})(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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0 && !this.encoding) { var pack = this.packetBuffer.shift(); this.packet(pack); } }; /** * Clean up transport subscriptions and packet buffer. * * @api private */ Manager.prototype.cleanup = function(){ debug('cleanup'); var sub; while (sub = this.subs.shift()) sub.destroy(); this.packetBuffer = []; this.encoding = false; this.lastPing = null; this.decoder.destroy(); }; /** * Close the current socket. * * @api private */ Manager.prototype.close = Manager.prototype.disconnect = function(){ debug('disconnect'); this.skipReconnect = true; this.reconnecting = false; if ('opening' == this.readyState) { // `onclose` will not fire because // an open event never happened this.cleanup(); } this.backoff.reset(); this.readyState = 'closed'; if (this.engine) this.engine.close(); }; /** * Called upon engine close. * * @api private */ Manager.prototype.onclose = function(reason){ debug('onclose'); this.cleanup(); this.backoff.reset(); this.readyState = 'closed'; this.emit('close', reason); if (this._reconnection && !this.skipReconnect) { this.reconnect(); } }; /** * Attempt a reconnection. * * @api private */ Manager.prototype.reconnect = function(){ if (this.reconnecting || this.skipReconnect) return this; var self = this; if (this.backoff.attempts >= this._reconnectionAttempts) { debug('reconnect failed'); this.backoff.reset(); this.emitAll('reconnect_failed'); this.reconnecting = false; } else { var delay = this.backoff.duration(); debug('will wait %dms before reconnect attempt', delay); this.reconnecting = true; var timer = setTimeout(function(){ if (self.skipReconnect) return; debug('attempting reconnect'); self.emitAll('reconnect_attempt', self.backoff.attempts); self.emitAll('reconnecting', self.backoff.attempts); // check again for the case socket closed in above events if (self.skipReconnect) return; self.open(function(err){ if (err) { debug('reconnect attempt error'); self.reconnecting = false; self.reconnect(); self.emitAll('reconnect_error', err.data); } else { debug('reconnect success'); self.onreconnect(); } }); }, delay); this.subs.push({ destroy: function(){ clearTimeout(timer); } }); } }; /** * Called upon successful reconnect. * * @api private */ Manager.prototype.onreconnect = function(){ var attempt = this.backoff.attempts; this.reconnecting = false; this.backoff.reset(); this.updateSocketIds(); this.emitAll('reconnect', attempt); }; },{"./on":3,"./socket":4,"backo2":8,"component-bind":11,"component-emitter":12,"debug":14,"engine.io-client":16,"indexof":32,"socket.io-parser":40}],3:[function(_dereq_,module,exports){ /** * Module exports. */ module.exports = on; /** * Helper for subscriptions. * * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter` * @param {String} event name * @param {Function} callback * @api public */ function on(obj, ev, fn) { obj.on(ev, fn); return { destroy: function(){ obj.removeListener(ev, fn); } }; } },{}],4:[function(_dereq_,module,exports){ /** * Module dependencies. */ var parser = _dereq_('socket.io-parser'); var Emitter = _dereq_('component-emitter'); var toArray = _dereq_('to-array'); var on = _dereq_('./on'); var bind = _dereq_('component-bind'); var debug = _dereq_('debug')('socket.io-client:socket'); var hasBin = _dereq_('has-binary'); /** * Module exports. */ module.exports = exports = Socket; /** * Internal events (blacklisted). * These events can't be emitted by the user. * * @api private */ var events = { connect: 1, connect_error: 1, connect_timeout: 1, connecting: 1, disconnect: 1, error: 1, reconnect: 1, reconnect_attempt: 1, reconnect_failed: 1, reconnect_error: 1, reconnecting: 1, ping: 1, pong: 1 }; /** * Shortcut to `Emitter#emit`. */ var emit = Emitter.prototype.emit; /** * `Socket` constructor. * * @api public */ function Socket(io, nsp){ this.io = io; this.nsp = nsp; this.json = this; // compat this.ids = 0; this.acks = {}; this.receiveBuffer = []; this.sendBuffer = []; this.connected = false; this.disconnected = true; if (this.io.autoConnect) this.open(); } /** * Mix in `Emitter`. */ Emitter(Socket.prototype); /** * Subscribe to open, close and packet events * * @api private */ Socket.prototype.subEvents = function() { if (this.subs) return; var io = this.io; this.subs = [ on(io, 'open', bind(this, 'onopen')), on(io, 'packet', bind(this, 'onpacket')), on(io, 'close', bind(this, 'onclose')) ]; }; /** * "Opens" the socket. * * @api public */ Socket.prototype.open = Socket.prototype.connect = function(){ if (this.connected) return this; this.subEvents(); this.io.open(); // ensure open if ('open' == this.io.readyState) this.onopen(); this.emit('connecting'); return this; }; /** * Sends a `message` event. * * @return {Socket} self * @api public */ Socket.prototype.send = function(){ var args = toArray(arguments); args.unshift('message'); this.emit.apply(this, args); return this; }; /** * Override `emit`. * If the event is in `events`, it's emitted normally. * * @param {String} event name * @return {Socket} self * @api public */ Socket.prototype.emit = function(ev){ if (events.hasOwnProperty(ev)) { emit.apply(this, arguments); return this; } var args = toArray(arguments); var parserType = parser.EVENT; // default if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary var packet = { type: parserType, data: args }; packet.options = {}; packet.options.compress = !this.flags || false !== this.flags.compress; // event ack callback if ('function' == typeof args[args.length - 1]) { debug('emitting packet with ack id %d', this.ids); this.acks[this.ids] = args.pop(); packet.id = this.ids++; } if (this.connected) { this.packet(packet); } else { this.sendBuffer.push(packet); } delete this.flags; return this; }; /** * Sends a packet. * * @param {Object} packet * @api private */ Socket.prototype.packet = function(packet){ packet.nsp = this.nsp; this.io.packet(packet); }; /** * Called upon engine `open`. * * @api private */ Socket.prototype.onopen = function(){ debug('transport is open - connecting'); // write connect packet if necessary if ('/' != this.nsp) { this.packet({ type: parser.CONNECT }); } }; /** * Called upon engine `close`. * * @param {String} reason * @api private */ Socket.prototype.onclose = function(reason){ debug('close (%s)', reason); this.connected = false; this.disconnected = true; delete this.id; this.emit('disconnect', reason); }; /** * Called with socket packet. * * @param {Object} packet * @api private */ Socket.prototype.onpacket = function(packet){ if (packet.nsp != this.nsp) return; switch (packet.type) { case parser.CONNECT: this.onconnect(); break; case parser.EVENT: this.onevent(packet); break; case parser.BINARY_EVENT: this.onevent(packet); break; case parser.ACK: this.onack(packet); break; case parser.BINARY_ACK: this.onack(packet); break; case parser.DISCONNECT: this.ondisconnect(); break; case parser.ERROR: this.emit('error', packet.data); break; } }; /** * Called upon a server event. * * @param {Object} packet * @api private */ Socket.prototype.onevent = function(packet){ var args = packet.data || []; debug('emitting event %j', args); if (null != packet.id) { debug('attaching ack callback to event'); args.push(this.ack(packet.id)); } if (this.connected) { emit.apply(this, args); } else { this.receiveBuffer.push(args); } }; /** * Produces an ack callback to emit with an event. * * @api private */ Socket.prototype.ack = function(id){ var self = this; var sent = false; return function(){ // prevent double callbacks if (sent) return; sent = true; var args = toArray(arguments); debug('sending ack %j', args); var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK; self.packet({ type: type, id: id, data: args }); }; }; /** * Called upon a server acknowlegement. * * @param {Object} packet * @api private */ Socket.prototype.onack = function(packet){ var ack = this.acks[packet.id]; if ('function' == typeof ack) { debug('calling ack %s with %j', packet.id, packet.data); ack.apply(this, packet.data); delete this.acks[packet.id]; } else { debug('bad ack %s', packet.id); } }; /** * Called upon server connect. * * @api private */ Socket.prototype.onconnect = function(){ this.connected = true; this.disconnected = false; this.emit('connect'); this.emitBuffered(); }; /** * Emit buffered events (received and emitted). * * @api private */ Socket.prototype.emitBuffered = function(){ var i; for (i = 0; i < this.receiveBuffer.length; i++) { emit.apply(this, this.receiveBuffer[i]); } this.receiveBuffer = []; for (i = 0; i < this.sendBuffer.length; i++) { this.packet(this.sendBuffer[i]); } this.sendBuffer = []; }; /** * Called upon server disconnect. * * @api private */ Socket.prototype.ondisconnect = function(){ debug('server disconnect (%s)', this.nsp); this.destroy(); this.onclose('io server disconnect'); }; /** * Called upon forced client/server side disconnections, * this method ensures the manager stops tracking us and * that reconnections don't get triggered for this. * * @api private. */ Socket.prototype.destroy = function(){ if (this.subs) { // clean subscriptions to avoid reconnections for (var i = 0; i < this.subs.length; i++) { this.subs[i].destroy(); } this.subs = null; } this.io.destroy(this); }; /** * Disconnects the socket manually. * * @return {Socket} self * @api public */ Socket.prototype.close = Socket.prototype.disconnect = function(){ if (this.connected) { debug('performing disconnect (%s)', this.nsp); this.packet({ type: parser.DISCONNECT }); } // remove socket from pool this.destroy(); if (this.connected) { // fire events this.onclose('io client disconnect'); } return this; }; /** * Sets the compress flag. * * @param {Boolean} if `true`, compresses the sending data * @return {Socket} self * @api public */ Socket.prototype.compress = function(compress){ this.flags = this.flags || {}; this.flags.compress = compress; return this; }; },{"./on":3,"component-bind":11,"component-emitter":12,"debug":14,"has-binary":30,"socket.io-parser":40,"to-array":43}],5:[function(_dereq_,module,exports){ (function (global){ /** * Module dependencies. */ var parseuri = _dereq_('parseuri'); var debug = _dereq_('debug')('socket.io-client:url'); /** * Module exports. */ module.exports = url; /** * URL parser. * * @param {String} url * @param {Object} An object meant to mimic window.location. * Defaults to window.location. * @api public */ function url(uri, loc){ var obj = uri; // default to window.location var loc = loc || global.location; if (null == uri) uri = loc.protocol + '//' + loc.host; // relative path support if ('string' == typeof uri) { if ('/' == uri.charAt(0)) { if ('/' == uri.charAt(1)) { uri = loc.protocol + uri; } else { uri = loc.host + uri; } } if (!/^(https?|wss?):\/\//.test(uri)) { debug('protocol-less url %s', uri); if ('undefined' != typeof loc) { uri = loc.protocol + '//' + uri; } else { uri = 'https://' + uri; } } // parse debug('parse %s', uri); obj = parseuri(uri); } // make sure we treat `localhost:80` and `localhost` equally if (!obj.port) { if (/^(http|ws)$/.test(obj.protocol)) { obj.port = '80'; } else if (/^(http|ws)s$/.test(obj.protocol)) { obj.port = '443'; } } obj.path = obj.path || '/'; var ipv6 = obj.host.indexOf(':') !== -1; var host = ipv6 ? '[' + obj.host + ']' : obj.host; // define unique id obj.id = obj.protocol + '://' + host + ':' + obj.port; // define href obj.href = obj.protocol + '://' + host + (loc && loc.port == obj.port ? '' : (':' + obj.port)); return obj; } }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}) },{"debug":14,"parseuri":38}],6:[function(_dereq_,module,exports){ module.exports = after function after(count, callback, err_cb) { var bail = false err_cb = err_cb || noop proxy.count = count return (count === 0) ? callback() : proxy function proxy(err, result) { if (proxy.count <= 0) { throw new Error('after called too many times') } --proxy.count // after first error, rest are passed to err_cb if (err) { bail = true callback(err) // future error callbacks will go to error handler callback = err_cb } else if (proxy.count === 0 && !bail) { callback(null, result) } } } function noop() {} },{}],7:[function(_dereq_,module,exports){ /** * An abstraction for slicing an arraybuffer even when * ArrayBuffer.prototype.slice is not supported * * @api public */ module.exports = function(arraybuffer, start, end) { var bytes = arraybuffer.byteLength; start = start || 0; end = end || bytes; if (arraybuffer.slice) { return arraybuffer.slice(start, end); } if (start < 0) { start += bytes; } if (end < 0) { end += bytes; } if (end > bytes) { end = bytes; } if (start >= bytes || start >= end || bytes === 0) { return new ArrayBuffer(0); } var abv = new Uint8Array(arraybuffer); var result = new Uint8Array(end - start); for (var i = start, ii = 0; i < end; i++, ii++) { result[ii] = abv[i]; } return result.buffer; }; },{}],8:[function(_dereq_,module,exports){ /** * Expose `Backoff`. */ module.exports = Backoff; /** * Initialize backoff timer with `opts`. * * - `min` initial timeout in milliseconds [100] * - `max` max timeout [10000] * - `jitter` [0] * - `factor` [2] * * @param {Object} opts * @api public */ function Backoff(opts) { opts = opts || {}; this.ms = opts.min || 100; this.max = opts.max || 10000; this.factor = opts.factor || 2; this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0; this.attempts = 0; } /** * Return the backoff duration. * * @return {Number} * @api public */ Backoff.prototype.duration = function(){ var ms = this.ms * Math.pow(this.factor, this.attempts++); if (this.jitter) { var rand = Math.random(); var deviation = Math.floor(rand * this.jitter * ms); ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation; } return Math.min(ms, this.max) | 0; }; /** * Reset the number of attempts. * * @api public */ Backoff.prototype.reset = function(){ this.attempts = 0; }; /** * Set the minimum duration * * @api public */ Backoff.prototype.setMin = function(min){ this.ms = min; }; /** * Set the maximum duration * * @api public */ Backoff.prototype.setMax = function(max){ this.max = max; }; /** * Set the jitter * * @api public */ Backoff.prototype.setJitter = function(jitter){ this.jitter = jitter; }; },{}],9:[function(_dereq_,module,exports){ /* * base64-arraybuffer * https://github.com/niklasvh/base64-arraybuffer * * Copyright (c) 2012 Niklas von Hertzen * Licensed under the MIT license. */ (function(){ "use strict"; var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Use a lookup table to find the index. var lookup = new Uint8Array(256); for (var i = 0; i < chars.length; i++) { lookup[chars.charCodeAt(i)] = i; } exports.encode = function(arraybuffer) { var bytes = new Uint8Array(arraybuffer), i, len = bytes.length, base64 = ""; for (i = 0; i < len; i+=3) { base64 += chars[bytes[i] >> 2]; base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; base64 += chars[bytes[i + 2] & 63]; } if ((len % 3) === 2) { base64 = base64.substring(0, base64.length - 1) + "="; } else if (len % 3 === 1) { base64 = base64.substring(0, base64.length - 2) + "=="; } return base64; }; exports.decode = function(base64) { var bufferLength = base64.length * 0.75, len = base64.length, i, p = 0, encoded1, encoded2, encoded3, encoded4; if (base64[base64.length - 1] === "=") { bufferLength--; if (base64[base64.length - 2] === "=") { bufferLength--; } } var arraybuffer = new ArrayBuffer(bufferLength), bytes = new Uint8Array(arraybuffer); for (i = 0; i < len; i+=4) { encoded1 = lookup[base64.charCodeAt(i)]; encoded2 = lookup[base64.charCodeAt(i+1)]; encoded3 = lookup[base64.charCodeAt(i+2)]; encoded4 = lookup[base64.charCodeAt(i+3)]; bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); } return arraybuffer; }; })(); },{}],10:[function(_dereq_,module,exports){ (function (global){ /** * Create a blob builder even when vendor prefixes exist */ var BlobBuilder = global.BlobBuilder || global.WebKitBlobBuilder || global.MSBlobBuilder || global.MozBlobBuilder; /** * Check if Blob constructor is supported */ var blobSupported = (function() { try { var a = new Blob(['hi']); return a.size === 2; } catch(e) { return false; } })(); /** * Check if Blob constructor supports ArrayBufferViews * Fails in Safari 6, so we need to map to ArrayBuffers there. */ var blobSupportsArrayBufferView = blobSupported && (function() { try { var b = new Blob([new Uint8Array([1,2])]); return b.size === 2; } catch(e) { return false; } })(); /** * Check if BlobBuilder is supported */ var blobBuilderSupported = BlobBuilder && BlobBuilder.prototype.append && BlobBuilder.prototype.getBlob; /** * Helper function that maps ArrayBufferViews to ArrayBuffers * Used by BlobBuilder constructor and old browsers that didn't * support it in the Blob constructor. */ function mapArrayBufferViews(ary) { for (var i = 0; i < ary.length; i++) { var chunk = ary[i]; if (chunk.buffer instanceof ArrayBuffer) { var buf = chunk.buffer; // if this is a subarray, make a copy so we only // include the subarray region from the underlying buffer if (chunk.byteLength !== buf.byteLength) { var copy = new Uint8Array(chunk.byteLength); copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)); buf = copy.buffer; } ary[i] = buf; } } } function BlobBuilderConstructor(ary, options) { options = options || {}; var bb = new BlobBuilder(); mapArrayBufferViews(ary); for (var i = 0; i < ary.length; i++) { bb.append(ary[i]); } return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); }; function BlobConstructor(ary, options) { mapArrayBufferViews(ary); return new Blob(ary, options || {}); }; module.exports = (function() { if (blobSupported) { return blobSupportsArrayBufferView ? global.Blob : BlobConstructor; } else if (blobBuilderSupported) { return BlobBuilderConstructor; } else { return undefined; } })(); }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}) },{}],11:[function(_dereq_,module,exports){ /** * Slice reference. */ var slice = [].slice; /** * Bind `obj` to `fn`. * * @param {Object} obj * @param {Function|String} fn or string * @return {Function} * @api public */ module.exports = function(obj, fn){ if ('string' == typeof fn) fn = obj[fn]; if ('function' != typeof fn) throw new Error('bind() requires a function'); var args = slice.call(arguments, 2); return function(){ return fn.apply(obj, args.concat(slice.call(arguments))); } }; },{}],12:[function(_dereq_,module,exports){ /** * Expose `Emitter`. */ module.exports = Emitter; /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); }; /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; (this._callbacks['$' + event] = this._callbacks['$' + event] || []) .push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function(event, fn){ function on() { this.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // 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 cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } 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; }; },{}],13:[function(_dereq_,module,exports){ module.exports = function(a, b){ var fn = function(){}; fn.prototype = b.prototype; a.prototype = new fn; a.prototype.constructor = a; }; },{}],14:[function(_dereq_,module,exports){ /** * This is the web browser implementation of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = _dereq_('./debug'); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; exports.load = load; exports.useColors = useColors; exports.storage = 'undefined' != typeof chrome && 'undefined' != typeof chrome.storage ? chrome.storage.local : localstorage(); /** * Colors. */ exports.colors = [ 'lightseagreen', 'forestgreen', 'goldenrod', 'dodgerblue', 'darkorchid', 'crimson' ]; /** * Currently only WebKit-based Web Inspectors, Firefox >= v31, * and the Firebug extension (any Firefox version) are known * to support "%c" CSS customizations. * * TODO: add a `localStorage` variable to explicitly enable/disable colors */ function useColors() { // is webkit? http://stackoverflow.com/a/16459606/376773 return ('WebkitAppearance' in document.documentElement.style) || // is firebug? http://stackoverflow.com/a/398120/376773 (window.console && (console.firebug || (console.exception && console.table))) || // is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); } /** * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ exports.formatters.j = function(v) { return JSON.stringify(v); }; /** * Colorize log arguments if enabled. * * @api public */ function formatArgs() { var args = arguments; var useColors = this.useColors; args[0] = (useColors ? '%c' : '') + this.namespace + (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') + '+' + exports.humanize(this.diff); if (!useColors) return args; var c = 'color: ' + this.color; args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); // the final "%c" is somewhat tricky, because there could be other // arguments passed either before or after the %c, so we need to // figure out the correct index to insert the CSS into var index = 0; var lastC = 0; args[0].replace(/%[a-z%]/g, function(match) { if ('%%' === match) return; index++; if ('%c' === match) { // we only are interested in the *last* %c // (the user may have provided their own) lastC = index; } }); args.splice(lastC, 0, c); return args; } /** * Invokes `console.log()` when available. * No-op when `console.log` is not a "function". * * @api public */ function log() { // this hackery is required for IE8/9, where // the `console.log` function doesn't have 'apply' return 'object' === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments); } /** * Save `namespaces`. * * @param {String} namespaces * @api private */ function save(namespaces) { try { if (null == namespaces) { exports.storage.removeItem('debug'); } else { exports.storage.debug = namespaces; } } catch(e) {} } /** * Load `namespaces`. * * @return {String} returns the previously persisted debug modes * @api private */ function load() { var r; try { r = exports.storage.debug; } catch(e) {} return r; } /** * Enable namespaces listed in `localStorage.debug` initially. */ exports.enable(load()); /** * Localstorage attempts to return the localstorage. * * This is necessary because safari throws * when a user disables cookies/localstorage * and you attempt to access it. * * @return {LocalStorage} * @api private */ function localstorage(){ try { return window.localStorage; } catch (e) {} } },{"./debug":15}],15:[function(_dereq_,module,exports){ /** * This is the common logic for both the Node.js and web browser * implementations of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = debug; exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; exports.humanize = _dereq_('ms'); /** * The currently active debug mode names, and names to skip. */ exports.names = []; exports.skips = []; /** * Map of special "%n" handling functions, for the debug "format" argument. * * Valid key names are a single, lowercased letter, i.e. "n". */ exports.formatters = {}; /** * Previously assigned color. */ var prevColor = 0; /** * Previous log timestamp. */ var prevTime; /** * Select a color. * * @return {Number} * @api private */ function selectColor() { return exports.colors[prevColor++ % exports.colors.length]; } /** * Create a debugger with the given `namespace`. * * @param {String} namespace * @return {Function} * @api public */ function debug(namespace) { // define the `disabled` version function disabled() { } disabled.enabled = false; // define the `enabled` version function enabled() { var self = enabled; // set `diff` timestamp var curr = +new Date(); var ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; // add the `color` if not set if (null == self.useColors) self.useColors = exports.useColors(); if (null == self.color && self.useColors) self.color = selectColor(); var args = Array.prototype.slice.call(arguments); args[0] = exports.coerce(args[0]); if ('string' !== typeof args[0]) { // anything else let's inspect with %o args = ['%o'].concat(args); } // apply any `formatters` transformations var index = 0; args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { // if we encounter an escaped % then don't increase the array index if (match === '%%') return match; index++; var formatter = exports.formatters[format]; if ('function' === typeof formatter) { var val = args[index]; match = formatter.call(self, val); // now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); index--; } return match; }); if ('function' === typeof exports.formatArgs) { args = exports.formatArgs.apply(self, args); } var logFn = enabled.log || exports.log || console.log.bind(console); logFn.apply(self, args); } enabled.enabled = true; var fn = exports.enabled(namespace) ? enabled : disabled; fn.namespace = namespace; return fn; } /** * Enables a debug mode by namespaces. This can include modes * separated by a colon and wildcards. * * @param {String} namespaces * @api public */ function enable(namespaces) { exports.save(namespaces); var split = (namespaces || '').split(/[\s,]+/); var len = split.length; for (var i = 0; i < len; i++) { if (!split[i]) continue; // ignore empty strings namespaces = split[i].replace(/\*/g, '.*?'); if (namespaces[0] === '-') { exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); } else { exports.names.push(new RegExp('^' + namespaces + '$')); } } } /** * Disable debug output. * * @api public */ function disable() { exports.enable(''); } /** * Returns true if the given mode name is enabled, false otherwise. * * @param {String} name * @return {Boolean} * @api public */ function enabled(name) { var i, len; for (i = 0, len = exports.skips.length; i < len; i++) { if (exports.skips[i].test(name)) { return false; } } for (i = 0, len = exports.names.length; i < len; i++) { if (exports.names[i].test(name)) { return true; } } return false; } /** * Coerce `val`. * * @param {Mixed} val * @return {Mixed} * @api private */ function coerce(val) { if (val instanceof Error) return val.stack || val.message; return val; } },{"ms":35}],16:[function(_dereq_,module,exports){ module.exports = _dereq_('./lib/'); },{"./lib/":17}],17:[function(_dereq_,module,exports){ module.exports = _dereq_('./socket'); /** * Exports parser * * @api public * */ module.exports.parser = _dereq_('engine.io-parser'); },{"./socket":18,"engine.io-parser":27}],18:[function(_dereq_,module,exports){ (function (global){ /** * Module dependencies. */ var transports = _dereq_('./transports'); var Emitter = _dereq_('component-emitter'); var debug = _dereq_('debug')('engine.io-client:socket'); var index = _dereq_('indexof'); var parser = _dereq_('engine.io-parser'); var parseuri = _dereq_('parseuri'); var parsejson = _dereq_('parsejson'); var parseqs = _dereq_('parseqs'); /** * Module exports. */ module.exports = Socket; /** * Noop function. * * @api private */ function noop(){} /** * Socket constructor. * * @param {String|Object} uri or options * @param {Object} options * @api public */ function Socket(uri, opts){ if (!(this instanceof Socket)) return new Socket(uri, opts); opts = opts || {}; if (uri && 'object' == typeof uri) { opts = uri; uri = null; } if (uri) { uri = parseuri(uri); opts.hostname = uri.host; opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; opts.port = uri.port; if (uri.query) opts.query = uri.query; } else if (opts.host) { opts.hostname = parseuri(opts.host).host; } this.secure = null != opts.secure ? opts.secure : (global.location && 'https:' == location.protocol); if (opts.hostname && !opts.port) { // if no port is specified manually, use the protocol default opts.port = this.secure ? '443' : '80'; } 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 = parseqs.decode(this.query); this.upgrade = false !== opts.upgrade; this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; this.forceJSONP = !!opts.forceJSONP; this.jsonp = false !== opts.jsonp; this.forceBase64 = !!opts.forceBase64; this.enablesXDR = !!opts.enablesXDR; this.timestampParam = opts.timestampParam || 't'; this.timestampRequests = opts.timestampRequests; this.transports = opts.transports || ['polling', 'websocket']; this.readyState = ''; this.writeBuffer = []; this.policyPort = opts.policyPort || 843; this.rememberUpgrade = opts.rememberUpgrade || false; this.binaryType = null; this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false; if (true === this.perMessageDeflate) this.perMessageDeflate = {}; if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { this.perMessageDeflate.threshold = 1024; } // SSL options for Node.js client this.pfx = opts.pfx || null; this.key = opts.key || null; this.passphrase = opts.passphrase || null; this.cert = opts.cert || null; this.ca = opts.ca || null; this.ciphers = opts.ciphers || null; this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? true : opts.rejectUnauthorized; // other options for Node.js client var freeGlobal = typeof global == 'object' && global; if (freeGlobal.global === freeGlobal) { if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) { this.extraHeaders = opts.extraHeaders; } } this.open(); } Socket.priorWebsocketSuccess = false; /** * Mix in `Emitter`. */ Emitter(Socket.prototype); /** * Protocol version. * * @api public */ Socket.protocol = parser.protocol; // this is an int /** * Expose deps for legacy compatibility * and standalone browser access. */ Socket.Socket = Socket; Socket.Transport = _dereq_('./transport'); Socket.transports = _dereq_('./transports'); Socket.parser = _dereq_('engine.io-parser'); /** * Creates transport of the given type. * * @param {String} transport name * @return {Transport} * @api private */ 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, jsonp: this.jsonp, forceBase64: this.forceBase64, enablesXDR: this.enablesXDR, timestampRequests: this.timestampRequests, timestampParam: this.timestampParam, policyPort: this.policyPort, socket: this, pfx: this.pfx, key: this.key, passphrase: this.passphrase, cert: this.cert, ca: this.ca, ciphers: this.ciphers, rejectUnauthorized: this.rejectUnauthorized, perMessageDeflate: this.perMessageDeflate, extraHeaders: this.extraHeaders }); return transport; }; function clone (obj) { var o = {}; for (var i in obj) { if (obj.hasOwnProperty(i)) { o[i] = obj[i]; } } return o; } /** * Initializes transport to use and starts probe. * * @api private */ Socket.prototype.open = function () { var transport; if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) { transport = 'websocket'; } else if (0 === this.transports.length) { // Emit error on next tick so it can be listened to var self = this; setTimeout(function() { self.emit('error', 'No transports available'); }, 0); return; } else { transport = this.transports[0]; } this.readyState = 'opening'; // Retry with the next transport if the transport is disabled (jsonp: false) try { transport = this.createTransport(transport); } catch (e) { this.transports.shift(); this.open(); return; } transport.open(); this.setTransport(transport); }; /** * Sets the current transport. Disables the existing one (if any). * * @api private */ Socket.prototype.setTransport = function(transport){ debug('setting transport %s', transport.name); var self = this; if (this.transport) { debug('clearing existing transport %s', this.transport.name); this.transport.removeAllListeners(); } // 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'); }); }; /** * Probes a transport. * * @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.priorWebsocketSuccess = false; function onTransportOpen(){ if (self.onlyBinaryUpgrades) { var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; failed = failed || upgradeLosesBinary; } if (failed) return; debug('probe transport "%s" opened', name); transport.send([{ type: 'ping', data: 'probe' }]); transport.once('packet', function (msg) { if (failed) return; if ('pong' == msg.type && 'probe' == msg.data) { debug('probe transport "%s" pong', name); self.upgrading = true; self.emit('upgrading', transport); if (!transport) return; Socket.priorWebsocketSuccess = 'websocket' == transport.name; debug('pausing current transport "%s"', self.transport.name); self.transport.pause(function () { if (failed) return; if ('closed' == self.readyState) return; debug('changing transport and sending upgrade packet'); cleanup(); self.setTransport(transport); transport.send([{ type: 'upgrade' }]); self.emit('upgrade', transport); transport = null; self.upgrading = false; self.flush(); }); } else { debug('probe transport "%s" failed', name); var err = new Error('probe error'); err.transport = transport.name; self.emit('upgradeError', err); } }); } function freezeTransport() { if (failed) return; // Any callback called by transport should be ignored since now failed = true; cleanup(); transport.close(); transport = null; } //Handle any error that happens while probing function onerror(err) { var error = new Error('probe error: ' + err); error.transport = transport.name; freezeTransport(); debug('probe transport "%s" failed because of error: %s', name, err); self.emit('upgradeError', error); } function onTransportClose(){ onerror("transport closed"); } //When the socket is closed while we're probing function onclose(){ onerror("socket closed"); } //When the socket is upgraded while we're probing function onupgrade(to){ if (transport && to.name != transport.name) { debug('"%s" works - aborting "%s"', to.name, transport.name); freezeTransport(); } } //Remove all listeners on the transport and on self function cleanup(){ transport.removeListener('open', onTransportOpen); transport.removeListener('error', onerror); transport.removeListener('close', onTransportClose); self.removeListener('close', onclose); self.removeListener('upgrading', onupgrade); } transport.once('open', onTransportOpen); transport.once('error', onerror); transport.once('close', onTransportClose); this.once('close', onclose); this.once('upgrading', onupgrade); transport.open(); }; /** * Called when connection is deemed open. * * @api public */ Socket.prototype.onOpen = function () { debug('socket open'); this.readyState = 'open'; Socket.priorWebsocketSuccess = 'websocket' == this.transport.name; this.emit('open'); this.flush(); // we check for `readyState` in case an `open` // listener already closed the socket if ('open' == this.readyState && this.upgrade && this.transport.pause) { debug('starting upgrade probes'); for (var i = 0, l = this.upgrades.length; i < l; i++) { this.probe(this.upgrades[i]); } } }; /** * Handles a packet. * * @api private */ Socket.prototype.onPacket = function (packet) { if ('opening' == this.readyState || 'open' == this.readyState) { debug('socket receive: type "%s", data "%s"', packet.type, packet.data); this.emit('packet', packet); // Socket is live - any packet counts this.emit('heartbeat'); switch (packet.type) { case 'open': this.onHandshake(parsejson(packet.data)); break; case 'pong': this.setPing(); this.emit('pong'); break; case 'error': var err = new Error('server error'); err.code = packet.data; this.onError(err); break; case 'message': this.emit('data', packet.data); this.emit('message', packet.data); break; } } else { debug('packet received with socket readyState "%s"', this.readyState); } }; /** * Called upon handshake completion. * * @param {Object} handshake obj * @api private */ Socket.prototype.onHandshake = function (data) { this.emit('handshake', data); this.id = data.sid; this.transport.query.sid = data.sid; this.upgrades = this.filterUpgrades(data.upgrades); this.pingInterval = data.pingInterval; this.pingTimeout = data.pingTimeout; this.onOpen(); // In case open handler closes socket if ('closed' == this.readyState) return; this.setPing(); // Prolong liveness of socket on heartbeat this.removeListener('heartbeat', this.onHeartbeat); this.on('heartbeat', this.onHeartbeat); }; /** * Resets ping timeout. * * @api private */ Socket.prototype.onHeartbeat = function (timeout) { clearTimeout(this.pingTimeoutTimer); var self = this; self.pingTimeoutTimer = setTimeout(function () { if ('closed' == self.readyState) return; self.onClose('ping timeout'); }, timeout || (self.pingInterval + self.pingTimeout)); }; /** * Pings server every `this.pingInterval` and expects response * within `this.pingTimeout` or closes connection. * * @api private */ Socket.prototype.setPing = function () { var self = this; clearTimeout(self.pingIntervalTimer); self.pingIntervalTimer = setTimeout(function () { debug('writing ping packet - expecting pong within %sms', self.pingTimeout); self.ping(); self.onHeartbeat(self.pingTimeout); }, self.pingInterval); }; /** * Sends a ping packet. * * @api private */ Socket.prototype.ping = function () { var self = this; this.sendPacket('ping', function(){ self.emit('ping'); }); }; /** * Called on `drain` event * * @api private */ Socket.prototype.onDrain = function() { this.writeBuffer.splice(0, this.prevBufferLen); // setting prevBufferLen = 0 is very important // for example, when upgrading, upgrade packet is sent over, // and a nonzero prevBufferLen could cause problems on `drain` this.prevBufferLen = 0; if (0 === this.writeBuffer.length) { this.emit('drain'); } else { this.flush(); } }; /** * Flush write buffers. * * @api private */ 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'); } }; /** * Sends a message. * * @param {String} message. * @param {Function} callback function. * @param {Object} options. * @return {Socket} for chaining. * @api public */ Socket.prototype.write = Socket.prototype.send = function (msg, options, fn) { this.sendPacket('message', msg, options, fn); return this; }; /** * Sends a packet. * * @param {String} packet type. * @param {String} data. * @param {Object} options. * @param {Function} callback function. * @api private */ Socket.prototype.sendPacket = function (type, data, options, fn) { if('function' == typeof data) { fn = data; data = undefined; } if ('function' == typeof options) { fn = options; options = null; } if ('closing' == this.readyState || 'closed' == this.readyState) { return; } options = options || {}; options.compress = false !== options.compress; var packet = { type: type, data: data, options: options }; this.emit('packetCreate', packet); this.writeBuffer.push(packet); if (fn) this.once('flush', fn); this.flush(); }; /** * Closes the connection. * * @api private */ Socket.prototype.close = function () { if ('opening' == this.readyState || 'open' == this.readyState) { this.readyState = 'closing'; var self = this; if (this.writeBuffer.length) { this.once('drain', function() { if (this.upgrading) { waitForUpgrade(); } else { close(); } }); } else if (this.upgrading) { waitForUpgrade(); } else { close(); } } function close() { self.onClose('forced close'); debug('socket closing - telling transport to close'); self.transport.close(); } function cleanupAndClose() { self.removeListener('upgrade', cleanupAndClose); self.removeListener('upgradeError', cleanupAndClose); close(); } function waitForUpgrade() { // wait for upgrade to finish since we can't send packets while pausing a transport self.once('upgrade', cleanupAndClose); self.once('upgradeError', cleanupAndClose); } return this; }; /** * Called upon transport error * * @api private */ Socket.prototype.onError = function (err) { debug('socket error %j', err); Socket.priorWebsocketSuccess = false; this.emit('error', err); this.onClose('transport error', err); }; /** * Called upon transport close. * * @api private */ Socket.prototype.onClose = function (reason, desc) { if ('opening' == this.readyState || 'open' == this.readyState || 'closing' == this.readyState) { debug('socket close with reason: "%s"', reason); var self = this; // clear timers clearTimeout(this.pingIntervalTimer); clearTimeout(this.pingTimeoutTimer); // stop event from firing again for transport this.transport.removeAllListeners('close'); // ensure transport won't stay open this.transport.close(); // ignore further transport communication this.transport.removeAllListeners(); // set ready state this.readyState = 'closed'; // clear session id this.id = null; // emit close event this.emit('close', reason, desc); // clean buffers after, so users can still // grab the buffers on `close` event self.writeBuffer = []; self.prevBufferLen = 0; } }; /** * Filters upgrades, returning only those matching client transports. * * @param {Array} server upgrades * @api private * */ Socket.prototype.filterUpgrades = function (upgrades) { var filteredUpgrades = []; for (var i = 0, j = upgrades.length; i