mbed connector webapp quickstart
Diff: static/js/socket.io.js
- Revision:
- 0:4233b7b10255
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/static/js/socket.io.js Fri Feb 10 08:36:15 2017 +0000 @@ -0,0 +1,7006 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.io=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(_dereq_,module,exports){ + +module.exports = _dereq_('./lib/'); + +},{"./lib/":2}],2:[function(_dereq_,module,exports){ + +/** + * Module dependencies. + */ + +var url = _dereq_('./url'); +var parser = _dereq_('socket.io-parser'); +var Manager = _dereq_('./manager'); +var debug = _dereq_('debug')('socket.io-client'); + +/** + * Module exports. + */ + +module.exports = exports = lookup; + +/** + * Managers cache. + */ + +var cache = exports.managers = {}; + +/** + * Looks up an existing `Manager` for multiplexing. + * If the user summons: + * + * `io('http://localhost/a');` + * `io('http://localhost/b');` + * + * We reuse the existing instance based on same scheme/port/host, + * and we initialize sockets for each namespace. + * + * @api public + */ + +function lookup(uri, opts) { + if (typeof uri == 'object') { + opts = uri; + uri = undefined; + } + + opts = opts || {}; + + var parsed = url(uri); + var source = parsed.source; + var id = parsed.id; + var path = parsed.path; + var sameNamespace = (cache[id] && cache[id].nsps[path] && + path == cache[id].nsps[path].nsp); + var newConnection = opts.forceNew || opts['force new connection'] || + false === opts.multiplex || sameNamespace; + + var io; + + if (newConnection) { + debug('ignoring socket cache for %s', source); + io = Manager(source, opts); + } else { + if (!cache[id]) { + debug('new io instance for %s', source); + cache[id] = Manager(source, opts); + } + io = cache[id]; + } + + return io.socket(parsed.path); +} + +/** + * Protocol version. + * + * @api public + */ + +exports.protocol = parser.protocol; + +/** + * `connect`. + * + * @param {String} uri + * @api public + */ + +exports.connect = lookup; + +/** + * Expose constructors for standalone build. + * + * @api public + */ + +exports.Manager = _dereq_('./manager'); +exports.Socket = _dereq_('./socket'); + +},{"./manager":3,"./socket":5,"./url":6,"debug":10,"socket.io-parser":46}],3:[function(_dereq_,module,exports){ + +/** + * Module dependencies. + */ + +var url = _dereq_('./url'); +var eio = _dereq_('engine.io-client'); +var Socket = _dereq_('./socket'); +var Emitter = _dereq_('component-emitter'); +var parser = _dereq_('socket.io-parser'); +var on = _dereq_('./on'); +var bind = _dereq_('component-bind'); +var object = _dereq_('object-component'); +var debug = _dereq_('debug')('socket.io-client:manager'); +var indexOf = _dereq_('indexof'); +var Backoff = _dereq_('backo2'); + +/** + * Module exports + */ + +module.exports = Manager; + +/** + * `Manager` constructor. + * + * @param {String} engine instance or engine uri/opts + * @param {Object} options + * @api public + */ + +function Manager(uri, opts){ + if (!(this instanceof Manager)) return new Manager(uri, opts); + if (uri && ('object' == typeof uri)) { + opts = uri; + uri = undefined; + } + opts = opts || {}; + + opts.path = opts.path || '/socket.io'; + this.nsps = {}; + this.subs = []; + this.opts = opts; + this.reconnection(opts.reconnection !== false); + this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); + this.reconnectionDelay(opts.reconnectionDelay || 1000); + this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); + this.randomizationFactor(opts.randomizationFactor || 0.5); + this.backoff = new Backoff({ + min: this.reconnectionDelay(), + max: this.reconnectionDelayMax(), + jitter: this.randomizationFactor() + }); + this.timeout(null == opts.timeout ? 20000 : opts.timeout); + this.readyState = 'closed'; + this.uri = uri; + this.connected = []; + this.encoding = false; + this.packetBuffer = []; + this.encoder = new parser.Encoder(); + this.decoder = new parser.Decoder(); + this.autoConnect = opts.autoConnect !== false; + if (this.autoConnect) this.open(); +} + +/** + * Propagate given event to sockets and emit on `this` + * + * @api private + */ + +Manager.prototype.emitAll = function() { + this.emit.apply(this, arguments); + for (var nsp in this.nsps) { + this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); + } +}; + +/** + * Update `socket.id` of all sockets + * + * @api private + */ + +Manager.prototype.updateSocketIds = function(){ + for (var nsp in this.nsps) { + this.nsps[nsp].id = this.engine.id; + } +}; + +/** + * Mix in `Emitter`. + */ + +Emitter(Manager.prototype); + +/** + * Sets the `reconnection` config. + * + * @param {Boolean} true/false if it should automatically reconnect + * @return {Manager} self or value + * @api public + */ + +Manager.prototype.reconnection = function(v){ + if (!arguments.length) return this._reconnection; + this._reconnection = !!v; + return this; +}; + +/** + * Sets the reconnection attempts config. + * + * @param {Number} max reconnection attempts before giving up + * @return {Manager} self or value + * @api public + */ + +Manager.prototype.reconnectionAttempts = function(v){ + if (!arguments.length) return this._reconnectionAttempts; + this._reconnectionAttempts = v; + return this; +}; + +/** + * Sets the delay between reconnections. + * + * @param {Number} delay + * @return {Manager} self or value + * @api public + */ + +Manager.prototype.reconnectionDelay = function(v){ + if (!arguments.length) return this._reconnectionDelay; + this._reconnectionDelay = v; + this.backoff && this.backoff.setMin(v); + return this; +}; + +Manager.prototype.randomizationFactor = function(v){ + if (!arguments.length) return this._randomizationFactor; + this._randomizationFactor = v; + this.backoff && this.backoff.setJitter(v); + return this; +}; + +/** + * Sets the maximum delay between reconnections. + * + * @param {Number} delay + * @return {Manager} self or value + * @api public + */ + +Manager.prototype.reconnectionDelayMax = function(v){ + if (!arguments.length) return this._reconnectionDelayMax; + this._reconnectionDelayMax = v; + this.backoff && this.backoff.setMax(v); + return this; +}; + +/** + * Sets the connection timeout. `false` to disable + * + * @return {Manager} self or value + * @api public + */ + +Manager.prototype.timeout = function(v){ + if (!arguments.length) return this._timeout; + this._timeout = v; + return this; +}; + +/** + * Starts trying to reconnect if reconnection is enabled and we have not + * started reconnecting yet + * + * @api private + */ + +Manager.prototype.maybeReconnectOnOpen = function() { + // Only try to reconnect if it's the first time we're connecting + if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) { + // keeps reconnection from firing twice for the same reconnection loop + this.reconnect(); + } +}; + + +/** + * Sets the current transport `socket`. + * + * @param {Function} optional, callback + * @return {Manager} self + * @api public + */ + +Manager.prototype.open = +Manager.prototype.connect = function(fn){ + debug('readyState %s', this.readyState); + if (~this.readyState.indexOf('open')) return this; + + debug('opening %s', this.uri); + this.engine = eio(this.uri, this.opts); + var socket = this.engine; + var self = this; + this.readyState = 'opening'; + this.skipReconnect = false; + + // emit `open` + var openSub = on(socket, 'open', function() { + self.onopen(); + fn && fn(); + }); + + // emit `connect_error` + var errorSub = on(socket, 'error', function(data){ + debug('connect_error'); + self.cleanup(); + self.readyState = 'closed'; + self.emitAll('connect_error', data); + if (fn) { + var err = new Error('Connection error'); + err.data = data; + fn(err); + } else { + // Only do this if there is no fn to handle the error + self.maybeReconnectOnOpen(); + } + }); + + // emit `connect_timeout` + if (false !== this._timeout) { + var timeout = this._timeout; + debug('connect attempt will timeout after %d', timeout); + + // set timer + var timer = setTimeout(function(){ + debug('connect attempt timed out after %d', timeout); + openSub.destroy(); + socket.close(); + socket.emit('error', 'timeout'); + self.emitAll('connect_timeout', timeout); + }, timeout); + + this.subs.push({ + destroy: function(){ + clearTimeout(timer); + } + }); + } + + this.subs.push(openSub); + this.subs.push(errorSub); + + return this; +}; + +/** + * Called upon transport open. + * + * @api private + */ + +Manager.prototype.onopen = function(){ + debug('open'); + + // clear old subs + this.cleanup(); + + // mark as open + this.readyState = 'open'; + this.emit('open'); + + // add new subs + var socket = this.engine; + this.subs.push(on(socket, 'data', bind(this, 'ondata'))); + this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); + this.subs.push(on(socket, 'error', bind(this, 'onerror'))); + this.subs.push(on(socket, 'close', bind(this, 'onclose'))); +}; + +/** + * Called with data. + * + * @api private + */ + +Manager.prototype.ondata = function(data){ + this.decoder.add(data); +}; + +/** + * Called when parser fully decodes a packet. + * + * @api private + */ + +Manager.prototype.ondecoded = function(packet) { + this.emit('packet', packet); +}; + +/** + * Called upon socket error. + * + * @api private + */ + +Manager.prototype.onerror = function(err){ + debug('error', err); + this.emitAll('error', err); +}; + +/** + * Creates a new socket for the given `nsp`. + * + * @return {Socket} + * @api public + */ + +Manager.prototype.socket = function(nsp){ + var socket = this.nsps[nsp]; + if (!socket) { + socket = new Socket(this, nsp); + this.nsps[nsp] = socket; + var self = this; + socket.on('connect', function(){ + socket.id = self.engine.id; + if (!~indexOf(self.connected, socket)) { + self.connected.push(socket); + } + }); + } + return socket; +}; + +/** + * Called upon a socket close. + * + * @param {Socket} socket + */ + +Manager.prototype.destroy = function(socket){ + var index = indexOf(this.connected, socket); + if (~index) this.connected.splice(index, 1); + if (this.connected.length) return; + + this.close(); +}; + +/** + * Writes a packet. + * + * @param {Object} packet + * @api private + */ + +Manager.prototype.packet = function(packet){ + debug('writing packet %j', packet); + var self = this; + + if (!self.encoding) { + // encode, then write to engine with result + self.encoding = true; + this.encoder.encode(packet, function(encodedPackets) { + for (var i = 0; i < encodedPackets.length; i++) { + self.engine.write(encodedPackets[i]); + } + self.encoding = false; + self.processPacketQueue(); + }); + } else { // add packet to the queue + self.packetBuffer.push(packet); + } +}; + +/** + * If packet buffer is non-empty, begins encoding the + * next packet in line. + * + * @api private + */ + +Manager.prototype.processPacketQueue = function() { + if (this.packetBuffer.length > 0 && !this.encoding) { + var pack = this.packetBuffer.shift(); + this.packet(pack); + } +}; + +/** + * Clean up transport subscriptions and packet buffer. + * + * @api private + */ + +Manager.prototype.cleanup = function(){ + var sub; + while (sub = this.subs.shift()) sub.destroy(); + + this.packetBuffer = []; + this.encoding = false; + + this.decoder.destroy(); +}; + +/** + * Close the current socket. + * + * @api private + */ + +Manager.prototype.close = +Manager.prototype.disconnect = function(){ + this.skipReconnect = true; + this.backoff.reset(); + this.readyState = 'closed'; + this.engine && this.engine.close(); +}; + +/** + * Called upon engine close. + * + * @api private + */ + +Manager.prototype.onclose = function(reason){ + debug('close'); + 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":4,"./socket":5,"./url":6,"backo2":7,"component-bind":8,"component-emitter":9,"debug":10,"engine.io-client":11,"indexof":42,"object-component":43,"socket.io-parser":46}],4:[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); + } + }; +} + +},{}],5:[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, + disconnect: 1, + error: 1, + reconnect: 1, + reconnect_attempt: 1, + reconnect_failed: 1, + reconnect_error: 1, + reconnecting: 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 = {}; + if (this.io.autoConnect) this.open(); + this.receiveBuffer = []; + this.sendBuffer = []; + this.connected = false; + this.disconnected = true; +} + +/** + * 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(); + 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 }; + + // 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); + } + + 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){ + debug('calling ack %s with %j', packet.id, packet.data); + var fn = this.acks[packet.id]; + fn.apply(this, packet.data); + delete this.acks[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; +}; + +},{"./on":4,"component-bind":8,"component-emitter":9,"debug":10,"has-binary":38,"socket.io-parser":46,"to-array":50}],6:[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.hostname + 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 || '/'; + + // define unique id + obj.id = obj.protocol + '://' + obj.host + ':' + obj.port; + // define href + obj.href = obj.protocol + '://' + obj.host + (loc && loc.port == obj.port ? '' : (':' + obj.port)); + + return obj; +} + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"debug":10,"parseuri":44}],7:[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; +}; + + +},{}],8:[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))); + } +}; + +},{}],9:[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){ + var self = this; + this._callbacks = this._callbacks || {}; + + function on() { + self.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; +}; + +},{}],10:[function(_dereq_,module,exports){ + +/** + * Expose `debug()` as the module. + */ + +module.exports = debug; + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + fmt = coerce(fmt); + + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + 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); + } +} + +/** + * 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; + + 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 + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @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 + */ + +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; +}; + +/** + * Coerce `val`. + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} + +// persist + +try { + if (window.localStorage) debug.enable(localStorage.debug); +} catch(e){} + +},{}],11:[function(_dereq_,module,exports){ + +module.exports = _dereq_('./lib/'); + +},{"./lib/":12}],12:[function(_dereq_,module,exports){ + +module.exports = _dereq_('./socket'); + +/** + * Exports parser + * + * @api public + * + */ +module.exports.parser = _dereq_('engine.io-parser'); + +},{"./socket":13,"engine.io-parser":25}],13:[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.host = uri.host; + opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; + opts.port = uri.port; + if (uri.query) opts.query = uri.query; + } + + this.secure = null != opts.secure ? opts.secure : + (global.location && 'https:' == location.protocol); + + if (opts.host) { + var pieces = opts.host.split(':'); + opts.hostname = pieces.shift(); + if (pieces.length) { + opts.port = pieces.pop(); + } else if (!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.callbackBuffer = []; + this.policyPort = opts.policyPort || 843; + this.rememberUpgrade = opts.rememberUpgrade || false; + this.binaryType = null; + this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; + + // SSL options for Node.js client + this.pfx = opts.pfx || null; + this.key = opts.key || null; + this.passphrase = opts.passphrase || null; + this.cert = opts.cert || null; + this.ca = opts.ca || null; + this.ciphers = opts.ciphers || null; + this.rejectUnauthorized = opts.rejectUnauthorized || null; + + 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 + }); + + 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) + var transport; + 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(); + break; + + case 'error': + var err = new Error('server error'); + err.code = packet.data; + this.emit('error', 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 public +*/ + +Socket.prototype.ping = function () { + this.sendPacket('ping'); +}; + +/** + * Called on `drain` event + * + * @api private + */ + +Socket.prototype.onDrain = function() { + for (var i = 0; i < this.prevBufferLen; i++) { + if (this.callbackBuffer[i]) { + this.callbackBuffer[i](); + } + } + + this.writeBuffer.splice(0, this.prevBufferLen); + this.callbackBuffer.splice(0, this.prevBufferLen); + + // setting prevBufferLen = 0 is very important + // for example, when upgrading, upgrade packet is sent over, + // and a nonzero prevBufferLen could cause problems on `drain` + this.prevBufferLen = 0; + + if (this.writeBuffer.length == 0) { + this.emit('drain'); + } else { + this.flush(); + } +}; + +/** + * 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. + * @return {Socket} for chaining. + * @api public + */ + +Socket.prototype.write = +Socket.prototype.send = function (msg, fn) { + this.sendPacket('message', msg, fn); + return this; +}; + +/** + * Sends a packet. + * + * @param {String} packet type. + * @param {String} data. + * @param {Function} callback function. + * @api private + */ + +Socket.prototype.sendPacket = function (type, data, fn) { + if ('closing' == this.readyState || 'closed' == this.readyState) { + return; + } + + var packet = { type: type, data: data }; + this.emit('packetCreate', packet); + this.writeBuffer.push(packet); + this.callbackBuffer.push(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; + + 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); + } + + if (this.writeBuffer.length) { + this.once('drain', function() { + if (this.upgrading) { + waitForUpgrade(); + } else { + close(); + } + }); + } else if (this.upgrading) { + waitForUpgrade(); + } else { + close(); + } + } + + 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); + + // clean buffers in next tick, so developers can still + // grab the buffers on `close` event + setTimeout(function() { + self.writeBuffer = []; + self.callbackBuffer = []; + self.prevBufferLen = 0; + }, 0); + + // stop event from firing again for transport + this.transport.removeAllListeners('close'); + + // ensure transport won't stay open + this.transport.close(); + + // ignore further transport communication + this.transport.removeAllListeners(); + + // set ready state + this.readyState = 'closed'; + + // clear session id + this.id = null; + + // emit close event + this.emit('close', reason, desc); + } +}; + +/** + * 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<j; i++) { + if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]); + } + return filteredUpgrades; +}; + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./transport":14,"./transports":15,"component-emitter":9,"debug":22,"engine.io-parser":25,"indexof":42,"parsejson":34,"parseqs":35,"parseuri":36}],14:[function(_dereq_,module,exports){ +/** + * Module dependencies. + */ + +var parser = _dereq_('engine.io-parser'); +var Emitter = _dereq_('component-emitter'); + +/** + * Module exports. + */ + +module.exports = Transport; + +/** + * Transport abstract constructor. + * + * @param {Object} options. + * @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 = ''; + this.agent = opts.agent || false; + this.socket = opts.socket; + this.enablesXDR = opts.enablesXDR; + + // SSL options for Node.js client + this.pfx = opts.pfx; + this.key = opts.key; + this.passphrase = opts.passphrase; + this.cert = opts.cert; + this.ca = opts.ca; + this.ciphers = opts.ciphers; + this.rejectUnauthorized = opts.rejectUnauthorized; +} + +/** + * Mix in `Emitter`. + */ + +Emitter(Transport.prototype); + +/** + * A counter used to prevent collisions in the timestamps used + * for cache busting. + */ + +Transport.timestamps = 0; + +/** + * Emits an error. + * + * @param {String} str + * @return {Transport} for chaining + * @api public + */ + +Transport.prototype.onError = function (msg, desc) { + var err = new Error(msg); + err.type = 'TransportError'; + err.description = desc; + this.emit('error', err); + return this; +}; + +/** + * Opens the transport. + * + * @api public + */ + +Transport.prototype.open = function () { + if ('closed' == this.readyState || '' == this.readyState) { + this.readyState = 'opening'; + this.doOpen(); + } + + return this; +}; + +/** + * Closes the transport. + * + * @api private + */ + +Transport.prototype.close = function () { + if ('opening' == this.readyState || 'open' == this.readyState) { + this.doClose(); + this.onClose(); + } + + return this; +}; + +/** + * Sends multiple packets. + * + * @param {Array} packets + * @api private + */ + +Transport.prototype.send = function(packets){ + if ('open' == this.readyState) { + this.write(packets); + } else { + throw new Error('Transport not open'); + } +}; + +/** + * Called upon open + * + * @api private + */ + +Transport.prototype.onOpen = function () { + this.readyState = 'open'; + this.writable = true; + this.emit('open'); +}; + +/** + * Called with data. + * + * @param {String} data + * @api private + */ + +Transport.prototype.onData = function(data){ + var packet = parser.decodePacket(data, this.socket.binaryType); + this.onPacket(packet); +}; + +/** + * Called with a decoded packet. + */ + +Transport.prototype.onPacket = function (packet) { + this.emit('packet', packet); +}; + +/** + * Called upon close. + * + * @api private + */ + +Transport.prototype.onClose = function () { + this.readyState = 'closed'; + this.emit('close'); +}; + +},{"component-emitter":9,"engine.io-parser":25}],15:[function(_dereq_,module,exports){ +(function (global){ +/** + * Module dependencies + */ + +var XMLHttpRequest = _dereq_('xmlhttprequest'); +var XHR = _dereq_('./polling-xhr'); +var JSONP = _dereq_('./polling-jsonp'); +var websocket = _dereq_('./websocket'); + +/** + * Export transports. + */ + +exports.polling = polling; +exports.websocket = websocket; + +/** + * Polling transport polymorphic constructor. + * Decides on xhr vs jsonp based on feature detection. + * + * @api private + */ + +function polling(opts){ + var xhr; + var xd = false; + var xs = false; + var jsonp = false !== opts.jsonp; + + 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; + xs = opts.secure != isSSL; + } + + opts.xdomain = xd; + opts.xscheme = xs; + xhr = new XMLHttpRequest(opts); + + if ('open' in xhr && !opts.forceJSONP) { + return new XHR(opts); + } else { + if (!jsonp) throw new Error('JSONP disabled'); + return new JSONP(opts); + } +} + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./polling-jsonp":16,"./polling-xhr":17,"./websocket":19,"xmlhttprequest":20}],16:[function(_dereq_,module,exports){ +(function (global){ + +/** + * Module requirements. + */ + +var Polling = _dereq_('./polling'); +var inherit = _dereq_('component-inherit'); + +/** + * Module exports. + */ + +module.exports = JSONPPolling; + +/** + * Cached regular expressions. + */ + +var rNewline = /\n/g; +var rEscapedNewline = /\\n/g; + +/** + * Global JSONP callbacks. + */ + +var callbacks; + +/** + * Callbacks count. + */ + +var index = 0; + +/** + * Noop. + */ + +function empty () { } + +/** + * JSONP Polling constructor. + * + * @param {Object} opts. + * @api public + */ + +function JSONPPolling (opts) { + Polling.call(this, opts); + + this.query = this.query || {}; + + // 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; + } + + // 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; + + // prevent spurious errors from being emitted when the window is unloaded + if (global.document && global.addEventListener) { + global.addEventListener('beforeunload', function () { + if (self.script) self.script.onerror = empty; + }, false); + } +} + +/** + * Inherits from Polling. + */ + +inherit(JSONPPolling, Polling); + +/* + * JSONP only supports binary as base64 encoded strings + */ + +JSONPPolling.prototype.supportsBinary = false; + +/** + * Closes the socket. + * + * @api private + */ + +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; + this.iframe = null; + } + + Polling.prototype.doClose.call(this); +}; + +/** + * Starts a poll cycle. + * + * @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; + } + + 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; + + var isUAgecko = 'undefined' != typeof navigator && /gecko/i.test(navigator.userAgent); + + if (isUAgecko) { + setTimeout(function () { + var iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + document.body.removeChild(iframe); + }, 100); + } +}; + +/** + * Writes with a hidden iframe. + * + * @param {String} data to send + * @param {Function} called upon flush. + * @api private + */ + +JSONPPolling.prototype.doWrite = function (data, fn) { + var self = this; + + 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 + // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side + data = data.replace(rEscapedNewline, '\\\n'); + 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; + } +}; + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./polling":18,"component-inherit":21}],17:[function(_dereq_,module,exports){ +(function (global){ +/** + * Module requirements. + */ + +var XMLHttpRequest = _dereq_('xmlhttprequest'); +var Polling = _dereq_('./polling'); +var Emitter = _dereq_('component-emitter'); +var inherit = _dereq_('component-inherit'); +var debug = _dereq_('debug')('engine.io-client:polling-xhr'); + +/** + * Module exports. + */ + +module.exports = XHR; +module.exports.Request = Request; + +/** + * Empty function + */ + +function empty(){} + +/** + * XHR Polling constructor. + * + * @param {Object} opts + * @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 (!port) { + port = isSSL ? 443 : 80; + } + + this.xd = opts.hostname != global.location.hostname || + port != opts.port; + this.xs = opts.secure != isSSL; + } +} + +/** + * Inherits from Polling. + */ + +inherit(XHR, Polling); + +/** + * XHR supports binary + */ + +XHR.prototype.supportsBinary = true; + +/** + * Creates a request. + * + * @param {String} method + * @api private + */ + +XHR.prototype.request = function(opts){ + opts = opts || {}; + opts.uri = this.uri(); + opts.xd = this.xd; + opts.xs = this.xs; + opts.agent = this.agent || false; + opts.supportsBinary = this.supportsBinary; + opts.enablesXDR = this.enablesXDR; + + // SSL options for Node.js client + opts.pfx = this.pfx; + opts.key = this.key; + opts.passphrase = this.passphrase; + opts.cert = this.cert; + opts.ca = this.ca; + opts.ciphers = this.ciphers; + opts.rejectUnauthorized = this.rejectUnauthorized; + + return new Request(opts); +}; + +/** + * Sends data. + * + * @param {String} data to send. + * @param {Function} called upon flush. + * @api private + */ + +XHR.prototype.doWrite = function(data, fn){ + var isBinary = typeof data !== 'string' && data !== undefined; + var req = this.request({ method: 'POST', data: data, isBinary: isBinary }); + 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 + */ + +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; +}; + +/** + * Request constructor + * + * @param {Object} options + * @api public + */ + +function Request(opts){ + this.method = opts.method || 'GET'; + this.uri = opts.uri; + this.xd = !!opts.xd; + this.xs = !!opts.xs; + this.async = false !== opts.async; + this.data = undefined != opts.data ? opts.data : null; + this.agent = opts.agent; + this.isBinary = opts.isBinary; + this.supportsBinary = opts.supportsBinary; + this.enablesXDR = opts.enablesXDR; + + // SSL options for Node.js client + this.pfx = opts.pfx; + this.key = opts.key; + this.passphrase = opts.passphrase; + this.cert = opts.cert; + this.ca = opts.ca; + this.ciphers = opts.ciphers; + this.rejectUnauthorized = opts.rejectUnauthorized; + + this.create(); +} + +/** + * Mix in `Emitter`. + */ + +Emitter(Request.prototype); + +/** + * Creates the XHR object and sends the request. + * + * @api private + */ + +Request.prototype.create = function(){ + var opts = { agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR }; + + // SSL options for Node.js client + opts.pfx = this.pfx; + opts.key = this.key; + opts.passphrase = this.passphrase; + opts.cert = this.cert; + opts.ca = this.ca; + opts.ciphers = this.ciphers; + opts.rejectUnauthorized = this.rejectUnauthorized; + + var xhr = this.xhr = new XMLHttpRequest(opts); + var self = this; + + try { + debug('xhr open %s: %s', this.method, this.uri); + xhr.open(this.method, this.uri, this.async); + if (this.supportsBinary) { + // This has to be done after open because Firefox is stupid + // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension + xhr.responseType = 'arraybuffer'; + } + + if ('POST' == this.method) { + try { + if (this.isBinary) { + xhr.setRequestHeader('Content-type', 'application/octet-stream'); + } else { + xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); + } + } catch (e) {} + } + + // ie6 check + if ('withCredentials' in xhr) { + xhr.withCredentials = true; + } + + if (this.hasXDR()) { + xhr.onload = function(){ + self.onLoad(); + }; + xhr.onerror = function(){ + self.onError(xhr.responseText); + }; + } else { + xhr.onreadystatechange = function(){ + if (4 != xhr.readyState) return; + if (200 == xhr.status || 1223 == xhr.status) { + self.onLoad(); + } 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); + } + }; + } + + debug('xhr data %s', this.data); + xhr.send(this.data); + } catch (e) { + // Need to defer since .create() is called directly fhrom 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; + } + + if (global.document) { + this.index = Request.requestsCount++; + Request.requests[this.index] = this; + } +}; + +/** + * Called upon successful response. + * + * @api private + */ + +Request.prototype.onSuccess = function(){ + this.emit('success'); + this.cleanup(); +}; + +/** + * Called if we have data. + * + * @api private + */ + +Request.prototype.onData = function(data){ + this.emit('data', data); + this.onSuccess(); +}; + +/** + * Called upon error. + * + * @api private + */ + +Request.prototype.onError = function(err){ + this.emit('error', err); + this.cleanup(true); +}; + +/** + * Cleans up house. + * + * @api private + */ + +Request.prototype.cleanup = function(fromError){ + if ('undefined' == typeof this.xhr || null === this.xhr) { + return; + } + // xmlhttprequest + if (this.hasXDR()) { + this.xhr.onload = this.xhr.onerror = empty; + } else { + this.xhr.onreadystatechange = empty; + } + + if (fromError) { + try { + this.xhr.abort(); + } catch(e) {} + } + + if (global.document) { + delete Request.requests[this.index]; + } + + this.xhr = null; +}; + +/** + * Called upon load. + * + * @api private + */ + +Request.prototype.onLoad = function(){ + var data; + try { + var contentType; + try { + contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0]; + } catch (e) {} + if (contentType === 'application/octet-stream') { + data = this.xhr.response; + } else { + if (!this.supportsBinary) { + data = this.xhr.responseText; + } else { + data = 'ok'; + } + } + } catch (e) { + this.onError(e); + } + if (null != data) { + this.onData(data); + } +}; + +/** + * Check if it has XDomainRequest. + * + * @api private + */ + +Request.prototype.hasXDR = function(){ + return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR; +}; + +/** + * Aborts the request. + * + * @api public + */ + +Request.prototype.abort = function(){ + this.cleanup(); +}; + +/** + * Aborts pending requests when unloading the window. This is needed to prevent + * memory leaks (e.g. when using IE) and to ensure that no spurious error is + * emitted. + */ + +if (global.document) { + Request.requestsCount = 0; + Request.requests = {}; + if (global.attachEvent) { + global.attachEvent('onunload', unloadHandler); + } else if (global.addEventListener) { + global.addEventListener('beforeunload', unloadHandler, false); + } +} + +function unloadHandler() { + for (var i in Request.requests) { + if (Request.requests.hasOwnProperty(i)) { + Request.requests[i].abort(); + } + } +} + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./polling":18,"component-emitter":9,"component-inherit":21,"debug":22,"xmlhttprequest":20}],18:[function(_dereq_,module,exports){ +/** + * Module dependencies. + */ + +var Transport = _dereq_('../transport'); +var parseqs = _dereq_('parseqs'); +var parser = _dereq_('engine.io-parser'); +var inherit = _dereq_('component-inherit'); +var debug = _dereq_('debug')('engine.io-client:polling'); + +/** + * Module exports. + */ + +module.exports = Polling; + +/** + * Is XHR2 supported? + */ + +var hasXHR2 = (function() { + var XMLHttpRequest = _dereq_('xmlhttprequest'); + var xhr = new XMLHttpRequest({ xdomain: false }); + return null != xhr.responseType; +})(); + +/** + * Polling interface. + * + * @param {Object} opts + * @api private + */ + +function Polling(opts){ + var forceBase64 = (opts && opts.forceBase64); + if (!hasXHR2 || forceBase64) { + this.supportsBinary = false; + } + Transport.call(this, opts); +} + +/** + * Inherits from Transport. + */ + +inherit(Polling, Transport); + +/** + * Transport name. + */ + +Polling.prototype.name = 'polling'; + +/** + * Opens the socket (triggers polling). We write a PING message to determine + * when the transport is open. + * + * @api private + */ + +Polling.prototype.doOpen = function(){ + this.poll(); +}; + +/** + * Pauses polling. + * + * @param {Function} callback upon buffers are flushed and transport is paused + * @api private + */ + +Polling.prototype.pause = function(onPause){ + var pending = 0; + var self = this; + + this.readyState = 'pausing'; + + function pause(){ + debug('paused'); + self.readyState = 'paused'; + onPause(); + } + + if (this.polling || !this.writable) { + var total = 0; + + if (this.polling) { + debug('we are currently polling - waiting to pause'); + total++; + this.once('pollComplete', function(){ + debug('pre-pause polling complete'); + --total || pause(); + }); + } + + if (!this.writable) { + debug('we are currently writing - waiting to pause'); + total++; + this.once('drain', function(){ + debug('pre-pause writing complete'); + --total || pause(); + }); + } + } else { + pause(); + } +}; + +/** + * Starts polling cycle. + * + * @api public + */ + +Polling.prototype.poll = function(){ + debug('polling'); + this.polling = true; + this.doPoll(); + this.emit('poll'); +}; + +/** + * Overloads onData to detect payloads. + * + * @api private + */ + +Polling.prototype.onData = function(data){ + var self = this; + debug('polling got data %s', data); + var callback = function(packet, index, total) { + // if its the first message we consider the transport open + if ('opening' == self.readyState) { + self.onOpen(); + } + + // if its a close packet, we close the ongoing requests + if ('close' == packet.type) { + self.onClose(); + return false; + } + + // otherwise bypass onData and handle the message + self.onPacket(packet); + }; + + // decode payload + parser.decodePayload(data, this.socket.binaryType, callback); + + // if an event did not trigger closing + if ('closed' != this.readyState) { + // if we got data we're not polling + this.polling = false; + this.emit('pollComplete'); + + if ('open' == this.readyState) { + this.poll(); + } else { + debug('ignoring poll - transport state "%s"', this.readyState); + } + } +}; + +/** + * For polling, send a close packet. + * + * @api private + */ + +Polling.prototype.doClose = function(){ + var self = this; + + function close(){ + debug('writing close packet'); + self.write([{ type: 'close' }]); + } + + 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 - deferring close'); + this.once('open', close); + } +}; + +/** + * Writes a packets payload. + * + * @param {Array} data packets + * @param {Function} drain callback + * @api private + */ + +Polling.prototype.write = function(packets){ + var self = this; + this.writable = false; + var callbackfn = function() { + self.writable = true; + self.emit('drain'); + }; + + var self = this; + parser.encodePayload(packets, this.supportsBinary, function(data) { + self.doWrite(data, callbackfn); + }); +}; + +/** + * Generates uri for connection. + * + * @api private + */ + +Polling.prototype.uri = function(){ + var query = this.query || {}; + var schema = this.secure ? 'https' : 'http'; + var port = ''; + + // cache busting is forced + if (false !== this.timestampRequests) { + query[this.timestampParam] = +new Date + '-' + Transport.timestamps++; + } + + if (!this.supportsBinary && !query.sid) { + query.b64 = 1; + } + + query = parseqs.encode(query); + + // avoid port if default for schema + if (this.port && (('https' == schema && this.port != 443) || + ('http' == schema && this.port != 80))) { + port = ':' + this.port; + } + + // prepend ? to query + if (query.length) { + query = '?' + query; + } + + return schema + '://' + this.hostname + port + this.path + query; +}; + +},{"../transport":14,"component-inherit":21,"debug":22,"engine.io-parser":25,"parseqs":35,"xmlhttprequest":20}],19:[function(_dereq_,module,exports){ +/** + * Module dependencies. + */ + +var Transport = _dereq_('../transport'); +var parser = _dereq_('engine.io-parser'); +var parseqs = _dereq_('parseqs'); +var inherit = _dereq_('component-inherit'); +var debug = _dereq_('debug')('engine.io-client:websocket'); + +/** + * `ws` exposes a WebSocket-compatible interface in + * Node, or the `WebSocket` or `MozWebSocket` globals + * in the browser. + */ + +var WebSocket = _dereq_('ws'); + +/** + * Module exports. + */ + +module.exports = WS; + +/** + * WebSocket transport constructor. + * + * @api {Object} connection options + * @api public + */ + +function WS(opts){ + var forceBase64 = (opts && opts.forceBase64); + if (forceBase64) { + this.supportsBinary = false; + } + Transport.call(this, opts); +} + +/** + * Inherits from Transport. + */ + +inherit(WS, Transport); + +/** + * Transport name. + * + * @api public + */ + +WS.prototype.name = 'websocket'; + +/* + * WebSockets support binary + */ + +WS.prototype.supportsBinary = true; + +/** + * Opens socket. + * + * @api private + */ + +WS.prototype.doOpen = function(){ + if (!this.check()) { + // let probe timeout + return; + } + + var self = this; + var uri = this.uri(); + var protocols = void(0); + var opts = { agent: this.agent }; + + // SSL options for Node.js client + opts.pfx = this.pfx; + opts.key = this.key; + opts.passphrase = this.passphrase; + opts.cert = this.cert; + opts.ca = this.ca; + opts.ciphers = this.ciphers; + opts.rejectUnauthorized = this.rejectUnauthorized; + + this.ws = new WebSocket(uri, protocols, opts); + + if (this.ws.binaryType === undefined) { + this.supportsBinary = false; + } + + this.ws.binaryType = 'arraybuffer'; + this.addEventListeners(); +}; + +/** + * Adds event listeners to the socket + * + * @api private + */ + +WS.prototype.addEventListeners = function(){ + var self = this; + + this.ws.onopen = function(){ + self.onOpen(); + }; + this.ws.onclose = function(){ + self.onClose(); + }; + this.ws.onmessage = function(ev){ + self.onData(ev.data); + }; + this.ws.onerror = function(e){ + self.onError('websocket error', e); + }; +}; + +/** + * Override `onData` to use a timer on iOS. + * See: https://gist.github.com/mloughran/2052006 + * + * @api private + */ + +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++) { + parser.encodePacket(packets[i], this.supportsBinary, function(data) { + //Sometimes the websocket has already been closed but the browser didn't + //have a chance of informing us about it yet, in that case send will + //throw an error + try { + self.ws.send(data); + } catch (e){ + debug('websocket closed before onclose event'); + } + }); + } + + function ondrain() { + self.writable = true; + self.emit('drain'); + } + // fake drain + // defer to next tick to allow Socket to clear writeBuffer + setTimeout(ondrain, 0); +}; + +/** + * Called upon close + * + * @api private + */ + +WS.prototype.onClose = function(){ + Transport.prototype.onClose.call(this); +}; + +/** + * Closes socket. + * + * @api private + */ + +WS.prototype.doClose = function(){ + if (typeof this.ws !== 'undefined') { + this.ws.close(); + } +}; + +/** + * Generates uri for connection. + * + * @api private + */ + +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; + } + + // communicate binary support capabilities + if (!this.supportsBinary) { + query.b64 = 1; + } + + query = parseqs.encode(query); + + // prepend ? to query + if (query.length) { + query = '?' + query; + } + + return schema + '://' + this.hostname + port + this.path + query; +}; + +/** + * Feature detection for WebSocket. + * + * @return {Boolean} whether this transport is available. + * @api public + */ + +WS.prototype.check = function(){ + return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); +}; + +},{"../transport":14,"component-inherit":21,"debug":22,"engine.io-parser":25,"parseqs":35,"ws":37}],20:[function(_dereq_,module,exports){ +// browser shim for xmlhttprequest module +var hasCORS = _dereq_('has-cors'); + +module.exports = function(opts) { + var xdomain = opts.xdomain; + + // scheme must be same when usign XDomainRequest + // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx + var xscheme = opts.xscheme; + + // XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default. + // https://github.com/Automattic/engine.io-client/pull/217 + var enablesXDR = opts.enablesXDR; + + // XMLHttpRequest can be disabled on IE + try { + if ('undefined' != typeof XMLHttpRequest && (!xdomain || hasCORS)) { + return new XMLHttpRequest(); + } + } catch (e) { } + + // Use XDomainRequest for IE8 if enablesXDR is true + // because loading bar keeps flashing when using jsonp-polling + // https://github.com/yujiosaka/socke.io-ie8-loading-example + try { + if ('undefined' != typeof XDomainRequest && !xscheme && enablesXDR) { + return new XDomainRequest(); + } + } catch (e) { } + + if (!xdomain) { + try { + return new ActiveXObject('Microsoft.XMLHTTP'); + } catch(e) { } + } +} + +},{"has-cors":40}],21:[function(_dereq_,module,exports){ + +module.exports = function(a, b){ + var fn = function(){}; + fn.prototype = b.prototype; + a.prototype = new fn; + a.prototype.constructor = a; +}; +},{}],22:[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; + +/** + * 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, + // where the `console.log` function doesn't have 'apply' + return 'object' == typeof console + && 'function' == typeof console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + localStorage.removeItem('debug'); + } else { + localStorage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = localStorage.debug; + } catch(e) {} + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +},{"./debug":23}],23:[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":24}],24:[function(_dereq_,module,exports){ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 's': + return n * s; + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} + +},{}],25:[function(_dereq_,module,exports){ +(function (global){ +/** + * Module dependencies. + */ + +var keys = _dereq_('./keys'); +var hasBinary = _dereq_('has-binary'); +var sliceBuffer = _dereq_('arraybuffer.slice'); +var base64encoder = _dereq_('base64-arraybuffer'); +var after = _dereq_('after'); +var utf8 = _dereq_('utf8'); + +/** + * Check if we are running an android browser. That requires us to use + * ArrayBuffer with polling transports... + * + * http://ghinda.net/jpeg-blob-ajax-android/ + */ + +var isAndroid = navigator.userAgent.match(/Android/i); + +/** + * Check if we are running in PhantomJS. + * Uploading a Blob with PhantomJS does not work correctly, as reported here: + * https://github.com/ariya/phantomjs/issues/11395 + * @type boolean + */ +var isPhantomJS = /PhantomJS/i.test(navigator.userAgent); + +/** + * When true, avoids using Blobs to encode payloads. + * @type boolean + */ +var dontSendBlobs = isAndroid || isPhantomJS; + +/** + * Current protocol version. + */ + +exports.protocol = 3; + +/** + * 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' }; + +/** + * Create a blob api even for blob builder when vendor prefixes exist + */ + +var Blob = _dereq_('blob'); + +/** + * Encodes a packet. + * + * <packet type id> [ <data> ] + * + * Example: + * + * 5hello world + * 3 + * 4 + * + * Binary is encoded in an identical principle + * + * @api private + */ + +exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) { + if ('function' == typeof supportsBinary) { + callback = supportsBinary; + supportsBinary = false; + } + + if ('function' == typeof utf8encode) { + callback = utf8encode; + utf8encode = null; + } + + var data = (packet.data === undefined) + ? undefined + : packet.data.buffer || packet.data; + + if (global.ArrayBuffer && data instanceof ArrayBuffer) { + return encodeArrayBuffer(packet, supportsBinary, callback); + } else if (Blob && data instanceof global.Blob) { + return encodeBlob(packet, supportsBinary, callback); + } + + // might be an object with { base64: true, data: dataAsBase64String } + if (data && data.base64) { + return encodeBase64Object(packet, callback); + } + + // Sending data as a utf-8 string + var encoded = packets[packet.type]; + + // data fragment is optional + if (undefined !== packet.data) { + encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data); + } + + return callback('' + encoded); + +}; + +function encodeBase64Object(packet, callback) { + // packet data is an object { base64: true, data: dataAsBase64String } + var message = 'b' + exports.packets[packet.type] + packet.data.data; + return callback(message); +} + +/** + * Encode packet helpers for binary types + */ + +function encodeArrayBuffer(packet, supportsBinary, callback) { + if (!supportsBinary) { + return exports.encodeBase64Packet(packet, callback); + } + + var data = packet.data; + var contentArray = new Uint8Array(data); + var resultBuffer = new Uint8Array(1 + data.byteLength); + + resultBuffer[0] = packets[packet.type]; + for (var i = 0; i < contentArray.length; i++) { + resultBuffer[i+1] = contentArray[i]; + } + + return callback(resultBuffer.buffer); +} + +function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) { + if (!supportsBinary) { + return exports.encodeBase64Packet(packet, callback); + } + + var fr = new FileReader(); + fr.onload = function() { + packet.data = fr.result; + exports.encodePacket(packet, supportsBinary, true, callback); + }; + return fr.readAsArrayBuffer(packet.data); +} + +function encodeBlob(packet, supportsBinary, callback) { + if (!supportsBinary) { + return exports.encodeBase64Packet(packet, callback); + } + + if (dontSendBlobs) { + return encodeBlobAsArrayBuffer(packet, supportsBinary, callback); + } + + var length = new Uint8Array(1); + length[0] = packets[packet.type]; + var blob = new Blob([length.buffer, packet.data]); + + return callback(blob); +} + +/** + * Encodes a packet with binary data in a base64 string + * + * @param {Object} packet, has `type` and `data` + * @return {String} base64 encoded message + */ + +exports.encodeBase64Packet = function(packet, callback) { + var message = 'b' + exports.packets[packet.type]; + if (Blob && packet.data instanceof Blob) { + var fr = new FileReader(); + fr.onload = function() { + var b64 = fr.result.split(',')[1]; + callback(message + b64); + }; + return fr.readAsDataURL(packet.data); + } + + var b64data; + try { + b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data)); + } catch (e) { + // iPhone Safari doesn't let you apply with typed arrays + var typed = new Uint8Array(packet.data); + var basic = new Array(typed.length); + for (var i = 0; i < typed.length; i++) { + basic[i] = typed[i]; + } + b64data = String.fromCharCode.apply(null, basic); + } + message += global.btoa(b64data); + return callback(message); +}; + +/** + * Decodes a packet. Changes format to Blob if requested. + * + * @return {Object} with `type` and `data` (if any) + * @api private + */ + +exports.decodePacket = function (data, binaryType, utf8decode) { + // String data + if (typeof data == 'string' || data === undefined) { + if (data.charAt(0) == 'b') { + return exports.decodeBase64Packet(data.substr(1), binaryType); + } + + if (utf8decode) { + try { + data = utf8.decode(data); + } catch (e) { + return err; + } + } + var type = data.charAt(0); + + if (Number(type) != type || !packetslist[type]) { + return err; + } + + if (data.length > 1) { + return { type: packetslist[type], data: data.substring(1) }; + } else { + return { type: packetslist[type] }; + } + } + + var asArray = new Uint8Array(data); + var type = asArray[0]; + var rest = sliceBuffer(data, 1); + if (Blob && binaryType === 'blob') { + rest = new Blob([rest]); + } + return { type: packetslist[type], data: rest }; +}; + +/** + * Decodes a packet encoded in a base64 string + * + * @param {String} base64 encoded message + * @return {Object} with `type` and `data` (if any) + */ + +exports.decodeBase64Packet = function(msg, binaryType) { + var type = packetslist[msg.charAt(0)]; + if (!global.ArrayBuffer) { + return { type: type, data: { base64: true, data: msg.substr(1) } }; + } + + var data = base64encoder.decode(msg.substr(1)); + + if (binaryType === 'blob' && Blob) { + data = new Blob([data]); + } + + return { type: type, data: data }; +}; + +/** + * Encodes multiple messages (payload). + * + * <length>:data + * + * Example: + * + * 11:hello world2:hi + * + * If any contents are binary, they will be encoded as base64 strings. Base64 + * encoded strings are marked with a b before the length specifier + * + * @param {Array} packets + * @api private + */ + +exports.encodePayload = function (packets, supportsBinary, callback) { + if (typeof supportsBinary == 'function') { + callback = supportsBinary; + supportsBinary = null; + } + + var isBinary = hasBinary(packets); + + if (supportsBinary && isBinary) { + if (Blob && !dontSendBlobs) { + return exports.encodePayloadAsBlob(packets, callback); + } + + return exports.encodePayloadAsArrayBuffer(packets, callback); + } + + if (!packets.length) { + return callback('0:'); + } + + function setLengthHeader(message) { + return message.length + ':' + message; + } + + function encodeOne(packet, doneCallback) { + exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) { + doneCallback(null, setLengthHeader(message)); + }); + } + + map(packets, encodeOne, function(err, results) { + return callback(results.join('')); + }); +}; + +/** + * Async array map using after + */ + +function map(ary, each, done) { + var result = new Array(ary.length); + var next = after(ary.length, done); + + var eachWithIndex = function(i, el, cb) { + each(el, function(error, msg) { + result[i] = msg; + cb(error, result); + }); + }; + + for (var i = 0; i < ary.length; i++) { + eachWithIndex(i, ary[i], next); + } +} + +/* + * Decodes data when a payload is maybe expected. Possible binary contents are + * decoded from their base64 representation + * + * @param {String} data, callback method + * @api public + */ + +exports.decodePayload = function (data, binaryType, callback) { + if (typeof data != 'string') { + return exports.decodePayloadAsBinary(data, binaryType, callback); + } + + if (typeof binaryType === 'function') { + callback = binaryType; + binaryType = null; + } + + var packet; + if (data == '') { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + var length = '' + , n, msg; + + for (var i = 0, l = data.length; i < l; i++) { + var chr = data.charAt(i); + + if (':' != chr) { + length += chr; + } else { + if ('' == length || (length != (n = Number(length)))) { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + msg = data.substr(i + 1, n); + + if (length != msg.length) { + // parser error - ignoring payload + return callback(err, 0, 1); + } + + if (msg.length) { + packet = exports.decodePacket(msg, binaryType, true); + + if (err.type == packet.type && err.data == packet.data) { + // parser error in individual packet - ignoring payload + return callback(err, 0, 1); + } + + var ret = callback(packet, i + n, l); + if (false === ret) return; + } + + // advance cursor + i += n; + length = ''; + } + } + + if (length != '') { + // parser error - ignoring payload + return callback(err, 0, 1); + } + +}; + +/** + * Encodes multiple messages (payload) as binary. + * + * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number + * 255><data> + * + * Example: + * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers + * + * @param {Array} packets + * @return {ArrayBuffer} encoded payload + * @api private + */ + +exports.encodePayloadAsArrayBuffer = function(packets, callback) { + if (!packets.length) { + return callback(new ArrayBuffer(0)); + } + + function encodeOne(packet, doneCallback) { + exports.encodePacket(packet, true, true, function(data) { + return doneCallback(null, data); + }); + } + + map(packets, encodeOne, function(err, encodedPackets) { + var totalLength = encodedPackets.reduce(function(acc, p) { + var len; + if (typeof p === 'string'){ + len = p.length; + } else { + len = p.byteLength; + } + return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2 + }, 0); + + var resultArray = new Uint8Array(totalLength); + + var bufferIndex = 0; + encodedPackets.forEach(function(p) { + var isString = typeof p === 'string'; + var ab = p; + if (isString) { + var view = new Uint8Array(p.length); + for (var i = 0; i < p.length; i++) { + view[i] = p.charCodeAt(i); + } + ab = view.buffer; + } + + if (isString) { // not true binary + resultArray[bufferIndex++] = 0; + } else { // true binary + resultArray[bufferIndex++] = 1; + } + + var lenStr = ab.byteLength.toString(); + for (var i = 0; i < lenStr.length; i++) { + resultArray[bufferIndex++] = parseInt(lenStr[i]); + } + resultArray[bufferIndex++] = 255; + + var view = new Uint8Array(ab); + for (var i = 0; i < view.length; i++) { + resultArray[bufferIndex++] = view[i]; + } + }); + + return callback(resultArray.buffer); + }); +}; + +/** + * Encode as Blob + */ + +exports.encodePayloadAsBlob = function(packets, callback) { + function encodeOne(packet, doneCallback) { + exports.encodePacket(packet, true, true, function(encoded) { + var binaryIdentifier = new Uint8Array(1); + binaryIdentifier[0] = 1; + if (typeof encoded === 'string') { + var view = new Uint8Array(encoded.length); + for (var i = 0; i < encoded.length; i++) { + view[i] = encoded.charCodeAt(i); + } + encoded = view.buffer; + binaryIdentifier[0] = 0; + } + + var len = (encoded instanceof ArrayBuffer) + ? encoded.byteLength + : encoded.size; + + var lenStr = len.toString(); + var lengthAry = new Uint8Array(lenStr.length + 1); + for (var i = 0; i < lenStr.length; i++) { + lengthAry[i] = parseInt(lenStr[i]); + } + lengthAry[lenStr.length] = 255; + + if (Blob) { + var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]); + doneCallback(null, blob); + } + }); + } + + map(packets, encodeOne, function(err, results) { + return callback(new Blob(results)); + }); +}; + +/* + * Decodes data when a payload is maybe expected. Strings are decoded by + * interpreting each byte as a key code for entries marked to start with 0. See + * description of encodePayloadAsBinary + * + * @param {ArrayBuffer} data, callback method + * @api public + */ + +exports.decodePayloadAsBinary = function (data, binaryType, callback) { + if (typeof binaryType === 'function') { + callback = binaryType; + binaryType = null; + } + + var bufferTail = data; + var buffers = []; + + var numberTooLong = false; + while (bufferTail.byteLength > 0) { + var tailArray = new Uint8Array(bufferTail); + var isString = tailArray[0] === 0; + var msgLength = ''; + + for (var i = 1; ; i++) { + if (tailArray[i] == 255) break; + + if (msgLength.length > 310) { + numberTooLong = true; + break; + } + + msgLength += tailArray[i]; + } + + if(numberTooLong) return callback(err, 0, 1); + + bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length); + msgLength = parseInt(msgLength); + + var msg = sliceBuffer(bufferTail, 0, msgLength); + if (isString) { + try { + msg = String.fromCharCode.apply(null, new Uint8Array(msg)); + } catch (e) { + // iPhone Safari doesn't let you apply to typed arrays + var typed = new Uint8Array(msg); + msg = ''; + for (var i = 0; i < typed.length; i++) { + msg += String.fromCharCode(typed[i]); + } + } + } + + buffers.push(msg); + bufferTail = sliceBuffer(bufferTail, msgLength); + } + + var total = buffers.length; + buffers.forEach(function(buffer, i) { + callback(exports.decodePacket(buffer, binaryType, true), i, total); + }); +}; + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./keys":26,"after":27,"arraybuffer.slice":28,"base64-arraybuffer":29,"blob":30,"has-binary":31,"utf8":33}],26:[function(_dereq_,module,exports){ + +/** + * 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; +}; + +},{}],27:[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() {} + +},{}],28:[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; +}; + +},{}],29:[function(_dereq_,module,exports){ +/* + * base64-arraybuffer + * https://github.com/niklasvh/base64-arraybuffer + * + * Copyright (c) 2012 Niklas von Hertzen + * Licensed under the MIT license. + */ +(function(chars){ + "use strict"; + + 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 = chars.indexOf(base64[i]); + encoded2 = chars.indexOf(base64[i+1]); + encoded3 = chars.indexOf(base64[i+2]); + encoded4 = chars.indexOf(base64[i+3]); + + bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); + bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); + bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); + } + + return arraybuffer; + }; +})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + +},{}],30:[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 b = new Blob(['hi']); + return b.size == 2; + } catch(e) { + return false; + } +})(); + +/** + * Check if BlobBuilder is supported + */ + +var blobBuilderSupported = BlobBuilder + && BlobBuilder.prototype.append + && BlobBuilder.prototype.getBlob; + +function BlobBuilderConstructor(ary, options) { + options = options || {}; + + var bb = new BlobBuilder(); + for (var i = 0; i < ary.length; i++) { + bb.append(ary[i]); + } + return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); +}; + +module.exports = (function() { + if (blobSupported) { + return global.Blob; + } else if (blobBuilderSupported) { + return BlobBuilderConstructor; + } else { + return undefined; + } +})(); + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],31:[function(_dereq_,module,exports){ +(function (global){ + +/* + * Module requirements. + */ + +var isArray = _dereq_('isarray'); + +/** + * Module exports. + */ + +module.exports = hasBinary; + +/** + * Checks for binary data. + * + * Right now only Buffer and ArrayBuffer are supported.. + * + * @param {Object} anything + * @api public + */ + +function hasBinary(data) { + + function _hasBinary(obj) { + if (!obj) return false; + + if ( (global.Buffer && global.Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer) || + (global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File) + ) { + return true; + } + + if (isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + if (_hasBinary(obj[i])) { + return true; + } + } + } else if (obj && 'object' == typeof obj) { + if (obj.toJSON) { + obj = obj.toJSON(); + } + + for (var key in obj) { + if (obj.hasOwnProperty(key) && _hasBinary(obj[key])) { + return true; + } + } + } + + return false; + } + + return _hasBinary(data); +} + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"isarray":32}],32:[function(_dereq_,module,exports){ +module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; +}; + +},{}],33:[function(_dereq_,module,exports){ +(function (global){ +/*! http://mths.be/utf8js v2.0.0 by @mathias */ +;(function(root) { + + // Detect free variables `exports` + var freeExports = typeof exports == 'object' && exports; + + // Detect free variable `module` + var freeModule = typeof module == 'object' && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, + // and use it as `root` + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + var stringFromCharCode = String.fromCharCode; + + // Taken from http://mths.be/punycode + function ucs2decode(string) { + var output = []; + var counter = 0; + var length = string.length; + var value; + var extra; + while (counter < length) { + value = string.charCodeAt(counter++); + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // high surrogate, and there is a next character + extra = string.charCodeAt(counter++); + if ((extra & 0xFC00) == 0xDC00) { // low surrogate + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // unmatched surrogate; only append this code unit, in case the next + // code unit is the high surrogate of a surrogate pair + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + return output; + } + + // Taken from http://mths.be/punycode + function ucs2encode(array) { + var length = array.length; + var index = -1; + var value; + var output = ''; + while (++index < length) { + value = array[index]; + if (value > 0xFFFF) { + value -= 0x10000; + output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); + value = 0xDC00 | value & 0x3FF; + } + output += stringFromCharCode(value); + } + return output; + } + + /*--------------------------------------------------------------------------*/ + + function createByte(codePoint, shift) { + return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); + } + + function encodeCodePoint(codePoint) { + if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence + return stringFromCharCode(codePoint); + } + var symbol = ''; + if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence + symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); + } + else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence + symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); + symbol += createByte(codePoint, 6); + } + else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence + symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); + symbol += createByte(codePoint, 12); + symbol += createByte(codePoint, 6); + } + symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); + return symbol; + } + + function utf8encode(string) { + var codePoints = ucs2decode(string); + + // console.log(JSON.stringify(codePoints.map(function(x) { + // return 'U+' + x.toString(16).toUpperCase(); + // }))); + + var length = codePoints.length; + var index = -1; + var codePoint; + var byteString = ''; + while (++index < length) { + codePoint = codePoints[index]; + byteString += encodeCodePoint(codePoint); + } + return byteString; + } + + /*--------------------------------------------------------------------------*/ + + function readContinuationByte() { + if (byteIndex >= byteCount) { + throw Error('Invalid byte index'); + } + + var continuationByte = byteArray[byteIndex] & 0xFF; + byteIndex++; + + if ((continuationByte & 0xC0) == 0x80) { + return continuationByte & 0x3F; + } + + // If we end up here, it’s not a continuation byte + throw Error('Invalid continuation byte'); + } + + function decodeSymbol() { + var byte1; + var byte2; + var byte3; + var byte4; + var codePoint; + + if (byteIndex > byteCount) { + throw Error('Invalid byte index'); + } + + if (byteIndex == byteCount) { + return false; + } + + // Read first byte + byte1 = byteArray[byteIndex] & 0xFF; + byteIndex++; + + // 1-byte sequence (no continuation bytes) + if ((byte1 & 0x80) == 0) { + return byte1; + } + + // 2-byte sequence + if ((byte1 & 0xE0) == 0xC0) { + var byte2 = readContinuationByte(); + codePoint = ((byte1 & 0x1F) << 6) | byte2; + if (codePoint >= 0x80) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 3-byte sequence (may include unpaired surrogates) + if ((byte1 & 0xF0) == 0xE0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; + if (codePoint >= 0x0800) { + return codePoint; + } else { + throw Error('Invalid continuation byte'); + } + } + + // 4-byte sequence + if ((byte1 & 0xF8) == 0xF0) { + byte2 = readContinuationByte(); + byte3 = readContinuationByte(); + byte4 = readContinuationByte(); + codePoint = ((byte1 & 0x0F) << 0x12) | (byte2 << 0x0C) | + (byte3 << 0x06) | byte4; + if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { + return codePoint; + } + } + + throw Error('Invalid UTF-8 detected'); + } + + var byteArray; + var byteCount; + var byteIndex; + function utf8decode(byteString) { + byteArray = ucs2decode(byteString); + byteCount = byteArray.length; + byteIndex = 0; + var codePoints = []; + var tmp; + while ((tmp = decodeSymbol()) !== false) { + codePoints.push(tmp); + } + return ucs2encode(codePoints); + } + + /*--------------------------------------------------------------------------*/ + + var utf8 = { + 'version': '2.0.0', + 'encode': utf8encode, + 'decode': utf8decode + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define(function() { + return utf8; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = utf8; + } else { // in Narwhal or RingoJS v0.7.0- + var object = {}; + var hasOwnProperty = object.hasOwnProperty; + for (var key in utf8) { + hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]); + } + } + } else { // in Rhino or a web browser + root.utf8 = utf8; + } + +}(this)); + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],34:[function(_dereq_,module,exports){ +(function (global){ +/** + * JSON parse. + * + * @see Based on jQuery#parseJSON (MIT) and JSON2 + * @api private + */ + +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+$/; + +module.exports = function parsejson(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))(); + } +}; +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],35:[function(_dereq_,module,exports){ +/** + * Compiles a querystring + * Returns string representation of the object + * + * @param {Object} + * @api private + */ + +exports.encode = function (obj) { + var str = ''; + + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + if (str.length) str += '&'; + str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]); + } + } + + return str; +}; + +/** + * Parses a simple querystring into an object + * + * @param {String} qs + * @api private + */ + +exports.decode = function(qs){ + var qry = {}; + var pairs = qs.split('&'); + for (var i = 0, l = pairs.length; i < l; i++) { + var pair = pairs[i].split('='); + qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); + } + return qry; +}; + +},{}],36:[function(_dereq_,module,exports){ +/** + * Parses an URI + * + * @author Steven Levithan <stevenlevithan.com> (MIT license) + * @api private + */ + +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' +]; + +module.exports = function parseuri(str) { + var src = str, + b = str.indexOf('['), + e = str.indexOf(']'); + + if (b != -1 && e != -1) { + str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length); + } + + var m = re.exec(str || ''), + uri = {}, + i = 14; + + while (i--) { + uri[parts[i]] = m[i] || ''; + } + + if (b != -1 && e != -1) { + uri.source = src; + uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':'); + uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':'); + uri.ipv6uri = true; + } + + return uri; +}; + +},{}],37:[function(_dereq_,module,exports){ + +/** + * Module dependencies. + */ + +var global = (function() { return this; })(); + +/** + * WebSocket constructor. + */ + +var WebSocket = global.WebSocket || global.MozWebSocket; + +/** + * Module exports. + */ + +module.exports = WebSocket ? ws : null; + +/** + * WebSocket constructor. + * + * 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 ws(uri, protocols, opts) { + var instance; + if (protocols) { + instance = new WebSocket(uri, protocols); + } else { + instance = new WebSocket(uri); + } + return instance; +} + +if (WebSocket) ws.prototype = WebSocket.prototype; + +},{}],38:[function(_dereq_,module,exports){ +(function (global){ + +/* + * Module requirements. + */ + +var isArray = _dereq_('isarray'); + +/** + * Module exports. + */ + +module.exports = hasBinary; + +/** + * Checks for binary data. + * + * Right now only Buffer and ArrayBuffer are supported.. + * + * @param {Object} anything + * @api public + */ + +function hasBinary(data) { + + function _hasBinary(obj) { + if (!obj) return false; + + if ( (global.Buffer && global.Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer) || + (global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File) + ) { + return true; + } + + if (isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + if (_hasBinary(obj[i])) { + return true; + } + } + } else if (obj && 'object' == typeof obj) { + if (obj.toJSON) { + obj = obj.toJSON(); + } + + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) { + return true; + } + } + } + + return false; + } + + return _hasBinary(data); +} + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"isarray":39}],39:[function(_dereq_,module,exports){ +module.exports=_dereq_(32) +},{}],40:[function(_dereq_,module,exports){ + +/** + * Module dependencies. + */ + +var global = _dereq_('global'); + +/** + * Module exports. + * + * Logic borrowed from Modernizr: + * + * - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js + */ + +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; +} + +},{"global":41}],41:[function(_dereq_,module,exports){ + +/** + * 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. + */ + +module.exports = (function () { return this; })(); + +},{}],42:[function(_dereq_,module,exports){ + +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; +}; +},{}],43:[function(_dereq_,module,exports){ + +/** + * HOP ref. + */ + +var has = Object.prototype.hasOwnProperty; + +/** + * Return own keys in `obj`. + * + * @param {Object} obj + * @return {Array} + * @api public + */ + +exports.keys = Object.keys || function(obj){ + var keys = []; + for (var key in obj) { + if (has.call(obj, key)) { + keys.push(key); + } + } + return keys; +}; + +/** + * Return own values in `obj`. + * + * @param {Object} obj + * @return {Array} + * @api public + */ + +exports.values = function(obj){ + var vals = []; + for (var key in obj) { + if (has.call(obj, key)) { + vals.push(obj[key]); + } + } + return vals; +}; + +/** + * Merge `b` into `a`. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api public + */ + +exports.merge = function(a, b){ + for (var key in b) { + if (has.call(b, key)) { + a[key] = b[key]; + } + } + return a; +}; + +/** + * Return length of `obj`. + * + * @param {Object} obj + * @return {Number} + * @api public + */ + +exports.length = function(obj){ + return exports.keys(obj).length; +}; + +/** + * Check if `obj` is empty. + * + * @param {Object} obj + * @return {Boolean} + * @api public + */ + +exports.isEmpty = function(obj){ + return 0 == exports.length(obj); +}; +},{}],44:[function(_dereq_,module,exports){ +/** + * Parses an URI + * + * @author Steven Levithan <stevenlevithan.com> (MIT license) + * @api private + */ + +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' +]; + +module.exports = function parseuri(str) { + var m = re.exec(str || '') + , uri = {} + , i = 14; + + while (i--) { + uri[parts[i]] = m[i] || ''; + } + + return uri; +}; + +},{}],45:[function(_dereq_,module,exports){ +(function (global){ +/*global Blob,File*/ + +/** + * Module requirements + */ + +var isArray = _dereq_('isarray'); +var isBuf = _dereq_('./is-buffer'); + +/** + * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder. + * Anything with blobs or files should be fed through removeBlobs before coming + * here. + * + * @param {Object} packet - socket.io event packet + * @return {Object} with deconstructed packet and list of buffers + * @api public + */ + +exports.deconstructPacket = function(packet){ + var buffers = []; + var packetData = packet.data; + + function _deconstructPacket(data) { + if (!data) return data; + + if (isBuf(data)) { + var placeholder = { _placeholder: true, num: buffers.length }; + buffers.push(data); + return placeholder; + } else if (isArray(data)) { + var newData = new Array(data.length); + for (var i = 0; i < data.length; i++) { + newData[i] = _deconstructPacket(data[i]); + } + return newData; + } else if ('object' == typeof data && !(data instanceof Date)) { + var newData = {}; + for (var key in data) { + newData[key] = _deconstructPacket(data[key]); + } + return newData; + } + return data; + } + + var pack = packet; + pack.data = _deconstructPacket(packetData); + pack.attachments = buffers.length; // number of binary 'attachments' + return {packet: pack, buffers: buffers}; +}; + +/** + * Reconstructs a binary packet from its placeholder packet and buffers + * + * @param {Object} packet - event packet with placeholders + * @param {Array} buffers - binary buffers to put in placeholder positions + * @return {Object} reconstructed packet + * @api public + */ + +exports.reconstructPacket = function(packet, buffers) { + var curPlaceHolder = 0; + + function _reconstructPacket(data) { + if (data && data._placeholder) { + var buf = buffers[data.num]; // appropriate buffer (should be natural order anyway) + return buf; + } else if (isArray(data)) { + for (var i = 0; i < data.length; i++) { + data[i] = _reconstructPacket(data[i]); + } + return data; + } else if (data && 'object' == typeof data) { + for (var key in data) { + data[key] = _reconstructPacket(data[key]); + } + return data; + } + return data; + } + + packet.data = _reconstructPacket(packet.data); + packet.attachments = undefined; // no longer useful + return packet; +}; + +/** + * Asynchronously removes Blobs or Files from data via + * FileReader's readAsArrayBuffer method. Used before encoding + * data as msgpack. Calls callback with the blobless data. + * + * @param {Object} data + * @param {Function} callback + * @api private + */ + +exports.removeBlobs = function(data, callback) { + function _removeBlobs(obj, curKey, containingObject) { + if (!obj) return obj; + + // convert any blob + if ((global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File)) { + pendingBlobs++; + + // async filereader + var fileReader = new FileReader(); + fileReader.onload = function() { // this.result == arraybuffer + if (containingObject) { + containingObject[curKey] = this.result; + } + else { + bloblessData = this.result; + } + + // if nothing pending its callback time + if(! --pendingBlobs) { + callback(bloblessData); + } + }; + + fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer + } else if (isArray(obj)) { // handle array + for (var i = 0; i < obj.length; i++) { + _removeBlobs(obj[i], i, obj); + } + } else if (obj && 'object' == typeof obj && !isBuf(obj)) { // and object + for (var key in obj) { + _removeBlobs(obj[key], key, obj); + } + } + } + + var pendingBlobs = 0; + var bloblessData = data; + _removeBlobs(bloblessData); + if (!pendingBlobs) { + callback(bloblessData); + } +}; + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./is-buffer":47,"isarray":48}],46:[function(_dereq_,module,exports){ + +/** + * Module dependencies. + */ + +var debug = _dereq_('debug')('socket.io-parser'); +var json = _dereq_('json3'); +var isArray = _dereq_('isarray'); +var Emitter = _dereq_('component-emitter'); +var binary = _dereq_('./binary'); +var isBuf = _dereq_('./is-buffer'); + +/** + * Protocol version. + * + * @api public + */ + +exports.protocol = 4; + +/** + * Packet types. + * + * @api public + */ + +exports.types = [ + 'CONNECT', + 'DISCONNECT', + 'EVENT', + 'BINARY_EVENT', + 'ACK', + 'BINARY_ACK', + 'ERROR' +]; + +/** + * Packet type `connect`. + * + * @api public + */ + +exports.CONNECT = 0; + +/** + * Packet type `disconnect`. + * + * @api public + */ + +exports.DISCONNECT = 1; + +/** + * Packet type `event`. + * + * @api public + */ + +exports.EVENT = 2; + +/** + * Packet type `ack`. + * + * @api public + */ + +exports.ACK = 3; + +/** + * Packet type `error`. + * + * @api public + */ + +exports.ERROR = 4; + +/** + * Packet type 'binary event' + * + * @api public + */ + +exports.BINARY_EVENT = 5; + +/** + * Packet type `binary ack`. For acks with binary arguments. + * + * @api public + */ + +exports.BINARY_ACK = 6; + +/** + * Encoder constructor. + * + * @api public + */ + +exports.Encoder = Encoder; + +/** + * Decoder constructor. + * + * @api public + */ + +exports.Decoder = Decoder; + +/** + * A socket.io Encoder instance + * + * @api public + */ + +function Encoder() {} + +/** + * Encode a packet as a single string if non-binary, or as a + * buffer sequence, depending on packet type. + * + * @param {Object} obj - packet object + * @param {Function} callback - function to handle encodings (likely engine.write) + * @return Calls callback with Array of encodings + * @api public + */ + +Encoder.prototype.encode = function(obj, callback){ + debug('encoding packet %j', obj); + + if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) { + encodeAsBinary(obj, callback); + } + else { + var encoding = encodeAsString(obj); + callback([encoding]); + } +}; + +/** + * Encode packet as string. + * + * @param {Object} packet + * @return {String} encoded + * @api private + */ + +function encodeAsString(obj) { + var str = ''; + var nsp = false; + + // first is type + str += obj.type; + + // attachments if we have them + if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) { + str += obj.attachments; + str += '-'; + } + + // if we have a namespace other than `/` + // we append it followed by a comma `,` + if (obj.nsp && '/' != obj.nsp) { + nsp = true; + str += obj.nsp; + } + + // immediately followed by the id + if (null != obj.id) { + if (nsp) { + str += ','; + nsp = false; + } + str += obj.id; + } + + // json data + if (null != obj.data) { + if (nsp) str += ','; + str += json.stringify(obj.data); + } + + debug('encoded %j as %s', obj, str); + return str; +} + +/** + * Encode packet as 'buffer sequence' by removing blobs, and + * deconstructing packet into object with placeholders and + * a list of buffers. + * + * @param {Object} packet + * @return {Buffer} encoded + * @api private + */ + +function encodeAsBinary(obj, callback) { + + function writeEncoding(bloblessData) { + var deconstruction = binary.deconstructPacket(bloblessData); + var pack = encodeAsString(deconstruction.packet); + var buffers = deconstruction.buffers; + + buffers.unshift(pack); // add packet info to beginning of data list + callback(buffers); // write all the buffers + } + + binary.removeBlobs(obj, writeEncoding); +} + +/** + * A socket.io Decoder instance + * + * @return {Object} decoder + * @api public + */ + +function Decoder() { + this.reconstructor = null; +} + +/** + * Mix in `Emitter` with Decoder. + */ + +Emitter(Decoder.prototype); + +/** + * Decodes an ecoded packet string into packet JSON. + * + * @param {String} obj - encoded packet + * @return {Object} packet + * @api public + */ + +Decoder.prototype.add = function(obj) { + var packet; + if ('string' == typeof obj) { + packet = decodeString(obj); + if (exports.BINARY_EVENT == packet.type || exports.BINARY_ACK == packet.type) { // binary packet's json + this.reconstructor = new BinaryReconstructor(packet); + + // no attachments, labeled binary but no binary data to follow + if (this.reconstructor.reconPack.attachments === 0) { + this.emit('decoded', packet); + } + } else { // non-binary full packet + this.emit('decoded', packet); + } + } + else if (isBuf(obj) || obj.base64) { // raw binary data + if (!this.reconstructor) { + throw new Error('got binary data when not reconstructing a packet'); + } else { + packet = this.reconstructor.takeBinaryData(obj); + if (packet) { // received final buffer + this.reconstructor = null; + this.emit('decoded', packet); + } + } + } + else { + throw new Error('Unknown type: ' + obj); + } +}; + +/** + * Decode a packet String (JSON data) + * + * @param {String} str + * @return {Object} packet + * @api private + */ + +function decodeString(str) { + var p = {}; + var i = 0; + + // look up type + p.type = Number(str.charAt(0)); + if (null == exports.types[p.type]) return error(); + + // look up attachments if type binary + if (exports.BINARY_EVENT == p.type || exports.BINARY_ACK == p.type) { + var buf = ''; + while (str.charAt(++i) != '-') { + buf += str.charAt(i); + if (i == str.length) break; + } + if (buf != Number(buf) || str.charAt(i) != '-') { + throw new Error('Illegal attachments'); + } + p.attachments = Number(buf); + } + + // look up namespace (if any) + if ('/' == str.charAt(i + 1)) { + p.nsp = ''; + while (++i) { + var c = str.charAt(i); + if (',' == c) break; + p.nsp += c; + if (i == str.length) break; + } + } else { + p.nsp = '/'; + } + + // look up id + var next = str.charAt(i + 1); + if ('' !== next && Number(next) == next) { + p.id = ''; + while (++i) { + var c = str.charAt(i); + if (null == c || Number(c) != c) { + --i; + break; + } + p.id += str.charAt(i); + if (i == str.length) break; + } + p.id = Number(p.id); + } + + // look up json data + if (str.charAt(++i)) { + try { + p.data = json.parse(str.substr(i)); + } catch(e){ + return error(); + } + } + + debug('decoded %s as %j', str, p); + return p; +} + +/** + * Deallocates a parser's resources + * + * @api public + */ + +Decoder.prototype.destroy = function() { + if (this.reconstructor) { + this.reconstructor.finishedReconstruction(); + } +}; + +/** + * A manager of a binary event's 'buffer sequence'. Should + * be constructed whenever a packet of type BINARY_EVENT is + * decoded. + * + * @param {Object} packet + * @return {BinaryReconstructor} initialized reconstructor + * @api private + */ + +function BinaryReconstructor(packet) { + this.reconPack = packet; + this.buffers = []; +} + +/** + * Method to be called when binary data received from connection + * after a BINARY_EVENT packet. + * + * @param {Buffer | ArrayBuffer} binData - the raw binary data received + * @return {null | Object} returns null if more binary data is expected or + * a reconstructed packet object if all buffers have been received. + * @api private + */ + +BinaryReconstructor.prototype.takeBinaryData = function(binData) { + this.buffers.push(binData); + if (this.buffers.length == this.reconPack.attachments) { // done with buffer list + var packet = binary.reconstructPacket(this.reconPack, this.buffers); + this.finishedReconstruction(); + return packet; + } + return null; +}; + +/** + * Cleans up binary packet reconstruction variables. + * + * @api private + */ + +BinaryReconstructor.prototype.finishedReconstruction = function() { + this.reconPack = null; + this.buffers = []; +}; + +function error(data){ + return { + type: exports.ERROR, + data: 'parser error' + }; +} + +},{"./binary":45,"./is-buffer":47,"component-emitter":9,"debug":10,"isarray":48,"json3":49}],47:[function(_dereq_,module,exports){ +(function (global){ + +module.exports = isBuf; + +/** + * Returns true if obj is a buffer or an arraybuffer. + * + * @api private + */ + +function isBuf(obj) { + return (global.Buffer && global.Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer); +} + +}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],48:[function(_dereq_,module,exports){ +module.exports=_dereq_(32) +},{}],49:[function(_dereq_,module,exports){ +/*! JSON v3.2.6 | http://bestiejs.github.io/json3 | Copyright 2012-2013, Kit Cambridge | http://kit.mit-license.org */ +;(function (window) { + // Convenience aliases. + var getClass = {}.toString, isProperty, forEach, undef; + + // Detect the `define` function exposed by asynchronous module loaders. The + // strict `define` check is necessary for compatibility with `r.js`. + var isLoader = typeof define === "function" && define.amd; + + // Detect native implementations. + var nativeJSON = typeof JSON == "object" && JSON; + + // Set up the JSON 3 namespace, preferring the CommonJS `exports` object if + // available. + var JSON3 = typeof exports == "object" && exports && !exports.nodeType && exports; + + if (JSON3 && nativeJSON) { + // Explicitly delegate to the native `stringify` and `parse` + // implementations in CommonJS environments. + JSON3.stringify = nativeJSON.stringify; + JSON3.parse = nativeJSON.parse; + } else { + // Export for web browsers, JavaScript engines, and asynchronous module + // loaders, using the global `JSON` object if available. + JSON3 = window.JSON = nativeJSON || {}; + } + + // Test the `Date#getUTC*` methods. Based on work by @Yaffle. + var isExtended = new Date(-3509827334573292); + try { + // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical + // results for certain dates in Opera >= 10.53. + isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && + // Safari < 2.0.2 stores the internal millisecond time value correctly, + // but clips the values returned by the date methods to the range of + // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). + isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; + } catch (exception) {} + + // Internal: Determines whether the native `JSON.stringify` and `parse` + // implementations are spec-compliant. Based on work by Ken Snyder. + function has(name) { + if (has[name] !== undef) { + // Return cached feature test result. + return has[name]; + } + + var isSupported; + if (name == "bug-string-char-index") { + // IE <= 7 doesn't support accessing string characters using square + // bracket notation. IE 8 only supports this for primitives. + isSupported = "a"[0] != "a"; + } else if (name == "json") { + // Indicates whether both `JSON.stringify` and `JSON.parse` are + // supported. + isSupported = has("json-stringify") && has("json-parse"); + } else { + var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; + // Test `JSON.stringify`. + if (name == "json-stringify") { + var stringify = JSON3.stringify, stringifySupported = typeof stringify == "function" && isExtended; + if (stringifySupported) { + // A test function object with a custom `toJSON` method. + (value = function () { + return 1; + }).toJSON = value; + try { + stringifySupported = + // Firefox 3.1b1 and b2 serialize string, number, and boolean + // primitives as object literals. + stringify(0) === "0" && + // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object + // literals. + stringify(new Number()) === "0" && + stringify(new String()) == '""' && + // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or + // does not define a canonical JSON representation (this applies to + // objects with `toJSON` properties as well, *unless* they are nested + // within an object or array). + stringify(getClass) === undef && + // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and + // FF 3.1b3 pass this test. + stringify(undef) === undef && + // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, + // respectively, if the value is omitted entirely. + stringify() === undef && + // FF 3.1b1, 2 throw an error if the given value is not a number, + // string, array, object, Boolean, or `null` literal. This applies to + // objects with custom `toJSON` methods as well, unless they are nested + // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` + // methods entirely. + stringify(value) === "1" && + stringify([value]) == "[1]" && + // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of + // `"[null]"`. + stringify([undef]) == "[null]" && + // YUI 3.0.0b1 fails to serialize `null` literals. + stringify(null) == "null" && + // FF 3.1b1, 2 halts serialization if an array contains a function: + // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 + // elides non-JSON values from objects and arrays, unless they + // define custom `toJSON` methods. + stringify([undef, getClass, null]) == "[null,null,null]" && + // Simple serialization test. FF 3.1b1 uses Unicode escape sequences + // where character escape codes are expected (e.g., `\b` => `\u0008`). + stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && + // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. + stringify(null, value) === "1" && + stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && + // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly + // serialize extended years. + stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && + // The milliseconds are optional in ES 5, but required in 5.1. + stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && + // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative + // four-digit years instead of six-digit years. Credits: @Yaffle. + stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && + // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond + // values less than 1000. Credits: @Yaffle. + stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; + } catch (exception) { + stringifySupported = false; + } + } + isSupported = stringifySupported; + } + // Test `JSON.parse`. + if (name == "json-parse") { + var parse = JSON3.parse; + if (typeof parse == "function") { + try { + // FF 3.1b1, b2 will throw an exception if a bare literal is provided. + // Conforming implementations should also coerce the initial argument to + // a string prior to parsing. + if (parse("0") === 0 && !parse(false)) { + // Simple parsing test. + value = parse(serialized); + var parseSupported = value["a"].length == 5 && value["a"][0] === 1; + if (parseSupported) { + try { + // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. + parseSupported = !parse('"\t"'); + } catch (exception) {} + if (parseSupported) { + try { + // FF 4.0 and 4.0.1 allow leading `+` signs and leading + // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow + // certain octal literals. + parseSupported = parse("01") !== 1; + } catch (exception) {} + } + if (parseSupported) { + try { + // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal + // points. These environments, along with FF 3.1b1 and 2, + // also allow trailing commas in JSON objects and arrays. + parseSupported = parse("1.") !== 1; + } catch (exception) {} + } + } + } + } catch (exception) { + parseSupported = false; + } + } + isSupported = parseSupported; + } + } + return has[name] = !!isSupported; + } + + if (!has("json")) { + // Common `[[Class]]` name aliases. + var functionClass = "[object Function]"; + var dateClass = "[object Date]"; + var numberClass = "[object Number]"; + var stringClass = "[object String]"; + var arrayClass = "[object Array]"; + var booleanClass = "[object Boolean]"; + + // Detect incomplete support for accessing string characters by index. + var charIndexBuggy = has("bug-string-char-index"); + + // Define additional utility methods if the `Date` methods are buggy. + if (!isExtended) { + var floor = Math.floor; + // A mapping between the months of the year and the number of days between + // January 1st and the first of the respective month. + var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + // Internal: Calculates the number of days between the Unix epoch and the + // first day of the given month. + var getDay = function (year, month) { + return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); + }; + } + + // Internal: Determines if a property is a direct property of the given + // object. Delegates to the native `Object#hasOwnProperty` method. + if (!(isProperty = {}.hasOwnProperty)) { + isProperty = function (property) { + var members = {}, constructor; + if ((members.__proto__ = null, members.__proto__ = { + // The *proto* property cannot be set multiple times in recent + // versions of Firefox and SeaMonkey. + "toString": 1 + }, members).toString != getClass) { + // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but + // supports the mutable *proto* property. + isProperty = function (property) { + // Capture and break the object's prototype chain (see section 8.6.2 + // of the ES 5.1 spec). The parenthesized expression prevents an + // unsafe transformation by the Closure Compiler. + var original = this.__proto__, result = property in (this.__proto__ = null, this); + // Restore the original prototype chain. + this.__proto__ = original; + return result; + }; + } else { + // Capture a reference to the top-level `Object` constructor. + constructor = members.constructor; + // Use the `constructor` property to simulate `Object#hasOwnProperty` in + // other environments. + isProperty = function (property) { + var parent = (this.constructor || constructor).prototype; + return property in this && !(property in parent && this[property] === parent[property]); + }; + } + members = null; + return isProperty.call(this, property); + }; + } + + // Internal: A set of primitive types used by `isHostType`. + var PrimitiveTypes = { + 'boolean': 1, + 'number': 1, + 'string': 1, + 'undefined': 1 + }; + + // Internal: Determines if the given object `property` value is a + // non-primitive. + var isHostType = function (object, property) { + var type = typeof object[property]; + return type == 'object' ? !!object[property] : !PrimitiveTypes[type]; + }; + + // Internal: Normalizes the `for...in` iteration algorithm across + // environments. Each enumerated key is yielded to a `callback` function. + forEach = function (object, callback) { + var size = 0, Properties, members, property; + + // Tests for bugs in the current environment's `for...in` algorithm. The + // `valueOf` property inherits the non-enumerable flag from + // `Object.prototype` in older versions of IE, Netscape, and Mozilla. + (Properties = function () { + this.valueOf = 0; + }).prototype.valueOf = 0; + + // Iterate over a new instance of the `Properties` class. + members = new Properties(); + for (property in members) { + // Ignore all properties inherited from `Object.prototype`. + if (isProperty.call(members, property)) { + size++; + } + } + Properties = members = null; + + // Normalize the iteration algorithm. + if (!size) { + // A list of non-enumerable properties inherited from `Object.prototype`. + members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; + // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable + // properties. + forEach = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, length; + var hasProperty = !isFunction && typeof object.constructor != 'function' && isHostType(object, 'hasOwnProperty') ? object.hasOwnProperty : isProperty; + for (property in object) { + // Gecko <= 1.0 enumerates the `prototype` property of functions under + // certain conditions; IE does not. + if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { + callback(property); + } + } + // Manually invoke the callback for each non-enumerable property. + for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); + }; + } else if (size == 2) { + // Safari <= 2.0.4 enumerates shadowed properties twice. + forEach = function (object, callback) { + // Create a set of iterated properties. + var members = {}, isFunction = getClass.call(object) == functionClass, property; + for (property in object) { + // Store each property name to prevent double enumeration. The + // `prototype` property of functions is not enumerated due to cross- + // environment inconsistencies. + if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { + callback(property); + } + } + }; + } else { + // No bugs detected; use the standard `for...in` algorithm. + forEach = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, isConstructor; + for (property in object) { + if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { + callback(property); + } + } + // Manually invoke the callback for the `constructor` property due to + // cross-environment inconsistencies. + if (isConstructor || isProperty.call(object, (property = "constructor"))) { + callback(property); + } + }; + } + return forEach(object, callback); + }; + + // Public: Serializes a JavaScript `value` as a JSON string. The optional + // `filter` argument may specify either a function that alters how object and + // array members are serialized, or an array of strings and numbers that + // indicates which properties should be serialized. The optional `width` + // argument may be either a string or number that specifies the indentation + // level of the output. + if (!has("json-stringify")) { + // Internal: A map of control characters and their escaped equivalents. + var Escapes = { + 92: "\\\\", + 34: '\\"', + 8: "\\b", + 12: "\\f", + 10: "\\n", + 13: "\\r", + 9: "\\t" + }; + + // Internal: Converts `value` into a zero-padded string such that its + // length is at least equal to `width`. The `width` must be <= 6. + var leadingZeroes = "000000"; + var toPaddedString = function (width, value) { + // The `|| 0` expression is necessary to work around a bug in + // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. + return (leadingZeroes + (value || 0)).slice(-width); + }; + + // Internal: Double-quotes a string `value`, replacing all ASCII control + // characters (characters with code unit values between 0 and 31) with + // their escaped equivalents. This is an implementation of the + // `Quote(value)` operation defined in ES 5.1 section 15.12.3. + var unicodePrefix = "\\u00"; + var quote = function (value) { + var result = '"', index = 0, length = value.length, isLarge = length > 10 && charIndexBuggy, symbols; + if (isLarge) { + symbols = value.split(""); + } + for (; index < length; index++) { + var charCode = value.charCodeAt(index); + // If the character is a control character, append its Unicode or + // shorthand escape sequence; otherwise, append the character as-is. + switch (charCode) { + case 8: case 9: case 10: case 12: case 13: case 34: case 92: + result += Escapes[charCode]; + break; + default: + if (charCode < 32) { + result += unicodePrefix + toPaddedString(2, charCode.toString(16)); + break; + } + result += isLarge ? symbols[index] : charIndexBuggy ? value.charAt(index) : value[index]; + } + } + return result + '"'; + }; + + // Internal: Recursively serializes an object. Implements the + // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. + var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { + var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; + try { + // Necessary for host object support. + value = object[property]; + } catch (exception) {} + if (typeof value == "object" && value) { + className = getClass.call(value); + if (className == dateClass && !isProperty.call(value, "toJSON")) { + if (value > -1 / 0 && value < 1 / 0) { + // Dates are serialized according to the `Date#toJSON` method + // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 + // for the ISO 8601 date time string format. + if (getDay) { + // Manually compute the year, month, date, hours, minutes, + // seconds, and milliseconds if the `getUTC*` methods are + // buggy. Adapted from @Yaffle's `date-shim` project. + date = floor(value / 864e5); + for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); + for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); + date = 1 + date - getDay(year, month); + // The `time` value specifies the time within the day (see ES + // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used + // to compute `A modulo B`, as the `%` operator does not + // correspond to the `modulo` operation for negative numbers. + time = (value % 864e5 + 864e5) % 864e5; + // The hours, minutes, seconds, and milliseconds are obtained by + // decomposing the time within the day. See section 15.9.1.10. + hours = floor(time / 36e5) % 24; + minutes = floor(time / 6e4) % 60; + seconds = floor(time / 1e3) % 60; + milliseconds = time % 1e3; + } else { + year = value.getUTCFullYear(); + month = value.getUTCMonth(); + date = value.getUTCDate(); + hours = value.getUTCHours(); + minutes = value.getUTCMinutes(); + seconds = value.getUTCSeconds(); + milliseconds = value.getUTCMilliseconds(); + } + // Serialize extended years correctly. + value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + + "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + + // Months, dates, hours, minutes, and seconds should have two + // digits; milliseconds should have three. + "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + + // Milliseconds are optional in ES 5.0, but required in 5.1. + "." + toPaddedString(3, milliseconds) + "Z"; + } else { + value = null; + } + } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { + // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the + // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3 + // ignores all `toJSON` methods on these objects unless they are + // defined directly on an instance. + value = value.toJSON(property); + } + } + if (callback) { + // If a replacement function was provided, call it to obtain the value + // for serialization. + value = callback.call(object, property, value); + } + if (value === null) { + return "null"; + } + className = getClass.call(value); + if (className == booleanClass) { + // Booleans are represented literally. + return "" + value; + } else if (className == numberClass) { + // JSON numbers must be finite. `Infinity` and `NaN` are serialized as + // `"null"`. + return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; + } else if (className == stringClass) { + // Strings are double-quoted and escaped. + return quote("" + value); + } + // Recursively serialize objects and arrays. + if (typeof value == "object") { + // Check for cyclic structures. This is a linear search; performance + // is inversely proportional to the number of unique nested objects. + for (length = stack.length; length--;) { + if (stack[length] === value) { + // Cyclic structures cannot be serialized by `JSON.stringify`. + throw TypeError(); + } + } + // Add the object to the stack of traversed objects. + stack.push(value); + results = []; + // Save the current indentation level and indent one additional level. + prefix = indentation; + indentation += whitespace; + if (className == arrayClass) { + // Recursively serialize array elements. + for (index = 0, length = value.length; index < length; index++) { + element = serialize(index, value, callback, properties, whitespace, indentation, stack); + results.push(element === undef ? "null" : element); + } + result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; + } else { + // Recursively serialize object members. Members are selected from + // either a user-specified list of property names, or the object + // itself. + forEach(properties || value, function (property) { + var element = serialize(property, value, callback, properties, whitespace, indentation, stack); + if (element !== undef) { + // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} + // is not the empty string, let `member` {quote(property) + ":"} + // be the concatenation of `member` and the `space` character." + // The "`space` character" refers to the literal space + // character, not the `space` {width} argument provided to + // `JSON.stringify`. + results.push(quote(property) + ":" + (whitespace ? " " : "") + element); + } + }); + result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; + } + // Remove the object from the traversed object stack. + stack.pop(); + return result; + } + }; + + // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. + JSON3.stringify = function (source, filter, width) { + var whitespace, callback, properties, className; + if (typeof filter == "function" || typeof filter == "object" && filter) { + if ((className = getClass.call(filter)) == functionClass) { + callback = filter; + } else if (className == arrayClass) { + // Convert the property names array into a makeshift set. + properties = {}; + for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); + } + } + if (width) { + if ((className = getClass.call(width)) == numberClass) { + // Convert the `width` to an integer and create a string containing + // `width` number of space characters. + if ((width -= width % 1) > 0) { + for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); + } + } else if (className == stringClass) { + whitespace = width.length <= 10 ? width : width.slice(0, 10); + } + } + // Opera <= 7.54u2 discards the values associated with empty string keys + // (`""`) only if they are used directly within an object member list + // (e.g., `!("" in { "": 1})`). + return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); + }; + } + + // Public: Parses a JSON source string. + if (!has("json-parse")) { + var fromCharCode = String.fromCharCode; + + // Internal: A map of escaped control characters and their unescaped + // equivalents. + var Unescapes = { + 92: "\\", + 34: '"', + 47: "/", + 98: "\b", + 116: "\t", + 110: "\n", + 102: "\f", + 114: "\r" + }; + + // Internal: Stores the parser state. + var Index, Source; + + // Internal: Resets the parser state and throws a `SyntaxError`. + var abort = function() { + Index = Source = null; + throw SyntaxError(); + }; + + // Internal: Returns the next token, or `"$"` if the parser has reached + // the end of the source string. A token may be a string, number, `null` + // literal, or Boolean literal. + var lex = function () { + var source = Source, length = source.length, value, begin, position, isSigned, charCode; + while (Index < length) { + charCode = source.charCodeAt(Index); + switch (charCode) { + case 9: case 10: case 13: case 32: + // Skip whitespace tokens, including tabs, carriage returns, line + // feeds, and space characters. + Index++; + break; + case 123: case 125: case 91: case 93: case 58: case 44: + // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at + // the current position. + value = charIndexBuggy ? source.charAt(Index) : source[Index]; + Index++; + return value; + case 34: + // `"` delimits a JSON string; advance to the next character and + // begin parsing the string. String tokens are prefixed with the + // sentinel `@` character to distinguish them from punctuators and + // end-of-string tokens. + for (value = "@", Index++; Index < length;) { + charCode = source.charCodeAt(Index); + if (charCode < 32) { + // Unescaped ASCII control characters (those with a code unit + // less than the space character) are not permitted. + abort(); + } else if (charCode == 92) { + // A reverse solidus (`\`) marks the beginning of an escaped + // control character (including `"`, `\`, and `/`) or Unicode + // escape sequence. + charCode = source.charCodeAt(++Index); + switch (charCode) { + case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: + // Revive escaped control characters. + value += Unescapes[charCode]; + Index++; + break; + case 117: + // `\u` marks the beginning of a Unicode escape sequence. + // Advance to the first character and validate the + // four-digit code point. + begin = ++Index; + for (position = Index + 4; Index < position; Index++) { + charCode = source.charCodeAt(Index); + // A valid sequence comprises four hexdigits (case- + // insensitive) that form a single hexadecimal value. + if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { + // Invalid Unicode escape sequence. + abort(); + } + } + // Revive the escaped character. + value += fromCharCode("0x" + source.slice(begin, Index)); + break; + default: + // Invalid escape sequence. + abort(); + } + } else { + if (charCode == 34) { + // An unescaped double-quote character marks the end of the + // string. + break; + } + charCode = source.charCodeAt(Index); + begin = Index; + // Optimize for the common case where a string is valid. + while (charCode >= 32 && charCode != 92 && charCode != 34) { + charCode = source.charCodeAt(++Index); + } + // Append the string as-is. + value += source.slice(begin, Index); + } + } + if (source.charCodeAt(Index) == 34) { + // Advance to the next character and return the revived string. + Index++; + return value; + } + // Unterminated string. + abort(); + default: + // Parse numbers and literals. + begin = Index; + // Advance past the negative sign, if one is specified. + if (charCode == 45) { + isSigned = true; + charCode = source.charCodeAt(++Index); + } + // Parse an integer or floating-point value. + if (charCode >= 48 && charCode <= 57) { + // Leading zeroes are interpreted as octal literals. + if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { + // Illegal octal literal. + abort(); + } + isSigned = false; + // Parse the integer component. + for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); + // Floats cannot contain a leading decimal point; however, this + // case is already accounted for by the parser. + if (source.charCodeAt(Index) == 46) { + position = ++Index; + // Parse the decimal component. + for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); + if (position == Index) { + // Illegal trailing decimal. + abort(); + } + Index = position; + } + // Parse exponents. The `e` denoting the exponent is + // case-insensitive. + charCode = source.charCodeAt(Index); + if (charCode == 101 || charCode == 69) { + charCode = source.charCodeAt(++Index); + // Skip past the sign following the exponent, if one is + // specified. + if (charCode == 43 || charCode == 45) { + Index++; + } + // Parse the exponential component. + for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); + if (position == Index) { + // Illegal empty exponent. + abort(); + } + Index = position; + } + // Coerce the parsed value to a JavaScript number. + return +source.slice(begin, Index); + } + // A negative sign may only precede numbers. + if (isSigned) { + abort(); + } + // `true`, `false`, and `null` literals. + if (source.slice(Index, Index + 4) == "true") { + Index += 4; + return true; + } else if (source.slice(Index, Index + 5) == "false") { + Index += 5; + return false; + } else if (source.slice(Index, Index + 4) == "null") { + Index += 4; + return null; + } + // Unrecognized token. + abort(); + } + } + // Return the sentinel `$` character if the parser has reached the end + // of the source string. + return "$"; + }; + + // Internal: Parses a JSON `value` token. + var get = function (value) { + var results, hasMembers; + if (value == "$") { + // Unexpected end of input. + abort(); + } + if (typeof value == "string") { + if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { + // Remove the sentinel `@` character. + return value.slice(1); + } + // Parse object and array literals. + if (value == "[") { + // Parses a JSON array, returning a new JavaScript array. + results = []; + for (;; hasMembers || (hasMembers = true)) { + value = lex(); + // A closing square bracket marks the end of the array literal. + if (value == "]") { + break; + } + // If the array literal contains elements, the current token + // should be a comma separating the previous element from the + // next. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "]") { + // Unexpected trailing `,` in array literal. + abort(); + } + } else { + // A `,` must separate each array element. + abort(); + } + } + // Elisions and leading commas are not permitted. + if (value == ",") { + abort(); + } + results.push(get(value)); + } + return results; + } else if (value == "{") { + // Parses a JSON object, returning a new JavaScript object. + results = {}; + for (;; hasMembers || (hasMembers = true)) { + value = lex(); + // A closing curly brace marks the end of the object literal. + if (value == "}") { + break; + } + // If the object literal contains members, the current token + // should be a comma separator. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "}") { + // Unexpected trailing `,` in object literal. + abort(); + } + } else { + // A `,` must separate each object member. + abort(); + } + } + // Leading commas are not permitted, object property names must be + // double-quoted strings, and a `:` must separate each property + // name and value. + if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { + abort(); + } + results[value.slice(1)] = get(lex()); + } + return results; + } + // Unexpected token encountered. + abort(); + } + return value; + }; + + // Internal: Updates a traversed object member. + var update = function(source, property, callback) { + var element = walk(source, property, callback); + if (element === undef) { + delete source[property]; + } else { + source[property] = element; + } + }; + + // Internal: Recursively traverses a parsed JSON object, invoking the + // `callback` function for each value. This is an implementation of the + // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. + var walk = function (source, property, callback) { + var value = source[property], length; + if (typeof value == "object" && value) { + // `forEach` can't be used to traverse an array in Opera <= 8.54 + // because its `Object#hasOwnProperty` implementation returns `false` + // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). + if (getClass.call(value) == arrayClass) { + for (length = value.length; length--;) { + update(value, length, callback); + } + } else { + forEach(value, function (property) { + update(value, property, callback); + }); + } + } + return callback.call(source, property, value); + }; + + // Public: `JSON.parse`. See ES 5.1 section 15.12.2. + JSON3.parse = function (source, callback) { + var result, value; + Index = 0; + Source = "" + source; + result = get(lex()); + // If a JSON string contains multiple tokens, it is invalid. + if (lex() != "$") { + abort(); + } + // Reset the parser state. + Index = Source = null; + return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; + }; + } + } + + // Export for asynchronous module loaders. + if (isLoader) { + define(function () { + return JSON3; + }); + } +}(this)); + +},{}],50:[function(_dereq_,module,exports){ +module.exports = toArray + +function toArray(list, index) { + var array = [] + + index = index || 0 + + for (var i = index || 0; i < list.length; i++) { + array[i - index] = list[i] + } + + return array +} + +},{}]},{},[1]) +(1) +});