UNPKG

nodulator

Version:

Complete NodeJS Framework for Restfull APIs

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