From 129ad9ab02bfc8c6a7d5a7714f74d61cb3a4f415 Mon Sep 17 00:00:00 2001 From: Anton Wilhelm Date: Wed, 31 Aug 2016 17:58:54 +0200 Subject: [PATCH] update dist --- dist/deepstream.js | 5266 ++++++++++++++++++++-------------------- dist/deepstream.min.js | 10 +- 2 files changed, 2702 insertions(+), 2574 deletions(-) diff --git a/dist/deepstream.js b/dist/deepstream.js index 9f30e98af..76768a742 100644 --- a/dist/deepstream.js +++ b/dist/deepstream.js @@ -1,4 +1,226 @@ (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.deepstream = 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 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; +}; + +},{}],3:[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+/"); + +},{}],4:[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 global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],5:[function(_dereq_,module,exports){ /** * Expose `Emitter`. @@ -164,2710 +386,2583 @@ Emitter.prototype.hasListeners = function(event){ return !! this.listeners(event).length; }; -},{}],2:[function(_dereq_,module,exports){ - -module.exports = _dereq_('./lib/'); - -},{"./lib/":3}],3:[function(_dereq_,module,exports){ +},{}],6:[function(_dereq_,module,exports){ -module.exports = _dereq_('./socket'); +module.exports = function(a, b){ + var fn = function(){}; + fn.prototype = b.prototype; + a.prototype = new fn; + a.prototype.constructor = a; +}; +},{}],7:[function(_dereq_,module,exports){ /** - * Exports parser - * - * @api public + * This is the web browser implementation of `debug()`. * + * Expose `debug()` as the module. */ -module.exports.parser = _dereq_('engine.io-parser'); -},{"./socket":4,"engine.io-parser":16}],4:[function(_dereq_,module,exports){ -(function (global){ +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(); + /** - * Module dependencies. + * Colors. */ -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'); +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; /** - * Module exports. + * 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 */ -module.exports = Socket; +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); +} /** - * Noop function. - * - * @api private + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ -function noop(){} +exports.formatters.j = function(v) { + return JSON.stringify(v); +}; + /** - * Socket constructor. + * Colorize log arguments if enabled. * - * @param {String|Object} uri or options - * @param {Object} options * @api public */ -function Socket(uri, opts){ - if (!(this instanceof Socket)) return new Socket(uri, opts); +function formatArgs() { + var args = arguments; + var useColors = this.useColors; - opts = opts || {}; + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); - if (uri && 'object' == typeof uri) { - opts = uri; - uri = null; - } + if (!useColors) return args; - 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; - } + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); - this.secure = null != opts.secure ? opts.secure : - (global.location && 'https:' == location.protocol); + // 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; + } + }); - if (opts.hostname && !opts.port) { - // if no port is specified manually, use the protocol default - opts.port = this.secure ? '443' : '80'; - } + args.splice(lastC, 0, c); + return args; +} - 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; +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ - if (true === this.perMessageDeflate) this.perMessageDeflate = {}; - if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { - this.perMessageDeflate.threshold = 1024; - } +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); +} - // 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; +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ - // 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; +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; } - } + } catch(e) {} +} - this.open(); +/** + * 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; } -Socket.priorWebsocketSuccess = false; +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); /** - * Mix in `Emitter`. + * 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 */ -Emitter(Socket.prototype); +function localstorage(){ + try { + return window.localStorage; + } catch (e) {} +} + +},{"./debug":8}],8:[function(_dereq_,module,exports){ /** - * Protocol version. + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. * - * @api public + * Expose `debug()` as the module. */ -Socket.protocol = parser.protocol; // this is an int +exports = module.exports = debug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = _dereq_('ms'); /** - * Expose deps for legacy compatibility - * and standalone browser access. + * The currently active debug mode names, and names to skip. */ -Socket.Socket = Socket; -Socket.Transport = _dereq_('./transport'); -Socket.transports = _dereq_('./transports'); -Socket.parser = _dereq_('engine.io-parser'); +exports.names = []; +exports.skips = []; /** - * Creates transport of the given type. + * Map of special "%n" handling functions, for the debug "format" argument. * - * @param {String} transport name - * @return {Transport} - * @api private + * Valid key names are a single, lowercased letter, i.e. "n". */ -Socket.prototype.createTransport = function (name) { - debug('creating transport "%s"', name); - var query = clone(this.query); +exports.formatters = {}; - // append engine.io protocol identifier - query.EIO = parser.protocol; +/** + * Previously assigned color. + */ - // transport name - query.transport = name; +var prevColor = 0; - // session id if we already have one - if (this.id) query.sid = this.id; +/** + * Previous log timestamp. + */ - 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; -} +var prevTime; /** - * Initializes transport to use and starts probe. + * Select a color. * + * @return {Number} * @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); -}; +function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; +} /** - * Sets the current transport. Disables the existing one (if any). + * Create a debugger with the given `namespace`. * - * @api private + * @param {String} namespace + * @return {Function} + * @api public */ -Socket.prototype.setTransport = function(transport){ - debug('setting transport %s', transport.name); - var self = this; +function debug(namespace) { - if (this.transport) { - debug('clearing existing transport %s', this.transport.name); - this.transport.removeAllListeners(); + // define the `disabled` version + function disabled() { } + disabled.enabled = false; - // 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'); - }); -}; + // define the `enabled` version + function enabled() { -/** - * Probes a transport. - * - * @param {String} transport name - * @api private - */ + var self = enabled; -Socket.prototype.probe = function (name) { - debug('probing transport "%s"', name); - var transport = this.createTransport(name, { probe: 1 }) - , failed = false - , self = this; + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; - Socket.priorWebsocketSuccess = false; + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); - function onTransportOpen(){ - if (self.onlyBinaryUpgrades) { - var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; - failed = failed || upgradeLosesBinary; - } - if (failed) return; + var args = Array.prototype.slice.call(arguments); - 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; + args[0] = exports.coerce(args[0]); - 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'); + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } - cleanup(); + // 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); - 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); + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; } + return match; }); - } - - function freezeTransport() { - if (failed) return; - - // Any callback called by transport should be ignored since now - failed = true; - - cleanup(); - transport.close(); - transport = null; + 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; - //Handle any error that happens while probing - function onerror(err) { - var error = new Error('probe error: ' + err); - error.transport = transport.name; + var fn = exports.enabled(namespace) ? enabled : disabled; - freezeTransport(); + fn.namespace = namespace; - debug('probe transport "%s" failed because of error: %s', name, err); + return fn; +} - self.emit('upgradeError', error); - } +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ - function onTransportClose(){ - onerror("transport closed"); - } +function enable(namespaces) { + exports.save(namespaces); - //When the socket is closed while we're probing - function onclose(){ - onerror("socket closed"); - } + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; - //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(); + 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 + '$')); } } - - //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. + * Disable debug output. * * @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]); - } - } -}; +function disable() { + exports.enable(''); +} /** - * Handles a packet. + * Returns true if the given mode name is enabled, false otherwise. * - * @api private + * @param {String} name + * @return {Boolean} + * @api public */ -Socket.prototype.onPacket = function (packet) { - if ('opening' == this.readyState || 'open' == this.readyState) { - debug('socket receive: type "%s", data "%s"', packet.type, packet.data); - - this.emit('packet', packet); - - // Socket is live - any packet counts - this.emit('heartbeat'); - - switch (packet.type) { - case 'open': - this.onHandshake(parsejson(packet.data)); - break; - - case 'pong': - this.setPing(); - this.emit('pong'); - break; - - case 'error': - var err = new Error('server error'); - err.code = packet.data; - this.onError(err); - break; - - case 'message': - this.emit('data', packet.data); - this.emit('message', packet.data); - break; +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; } - } else { - debug('packet received with socket readyState "%s"', this.readyState); } -}; + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} /** - * Called upon handshake completion. + * Coerce `val`. * - * @param {Object} handshake obj + * @param {Mixed} val + * @return {Mixed} * @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(); +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} - // Prolong liveness of socket on heartbeat - this.removeListener('heartbeat', this.onHeartbeat); - this.on('heartbeat', this.onHeartbeat); -}; +},{"ms":28}],9:[function(_dereq_,module,exports){ -/** - * Resets ping timeout. - * - * @api private - */ +module.exports = _dereq_('./lib/'); -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)); -}; +},{"./lib/":10}],10:[function(_dereq_,module,exports){ + +module.exports = _dereq_('./socket'); /** - * Pings server every `this.pingInterval` and expects response - * within `this.pingTimeout` or closes connection. + * Exports parser + * + * @api public * - * @api private */ +module.exports.parser = _dereq_('engine.io-parser'); -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); -}; - +},{"./socket":11,"engine.io-parser":19}],11:[function(_dereq_,module,exports){ +(function (global){ /** -* Sends a ping packet. -* -* @api private -*/ + * Module dependencies. + */ -Socket.prototype.ping = function () { - var self = this; - this.sendPacket('ping', function(){ - self.emit('ping'); - }); -}; +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'); /** - * Called on `drain` event - * - * @api private + * Module exports. */ -Socket.prototype.onDrain = function() { - this.writeBuffer.splice(0, this.prevBufferLen); - - // setting prevBufferLen = 0 is very important - // for example, when upgrading, upgrade packet is sent over, - // and a nonzero prevBufferLen could cause problems on `drain` - this.prevBufferLen = 0; - - if (0 === this.writeBuffer.length) { - this.emit('drain'); - } else { - this.flush(); - } -}; +module.exports = Socket; /** - * Flush write buffers. + * Noop function. * * @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'); - } -}; +function noop(){} /** - * Sends a message. + * Socket constructor. * - * @param {String} message. - * @param {Function} callback function. - * @param {Object} options. - * @return {Socket} for chaining. + * @param {String|Object} uri or options + * @param {Object} options * @api public */ -Socket.prototype.write = -Socket.prototype.send = function (msg, options, fn) { - this.sendPacket('message', msg, options, fn); - return this; -}; +function Socket(uri, opts){ + if (!(this instanceof Socket)) return new Socket(uri, opts); -/** - * Sends a packet. - * - * @param {String} packet type. - * @param {String} data. - * @param {Object} options. - * @param {Function} callback function. - * @api private - */ + opts = opts || {}; -Socket.prototype.sendPacket = function (type, data, options, fn) { - if('function' == typeof data) { - fn = data; - data = undefined; + if (uri && 'object' == typeof uri) { + opts = uri; + uri = null; } - if ('function' == typeof options) { - fn = options; - options = 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; } - if ('closing' == this.readyState || 'closed' == this.readyState) { - return; + 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'; } - options = options || {}; - options.compress = false !== options.compress; + 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; - var packet = { - type: type, - data: data, - options: options - }; - this.emit('packetCreate', packet); - this.writeBuffer.push(packet); - if (fn) this.once('flush', fn); - this.flush(); -}; - -/** - * Closes the connection. - * - * @api private - */ - -Socket.prototype.close = function () { - if ('opening' == this.readyState || 'open' == this.readyState) { - this.readyState = 'closing'; + if (true === this.perMessageDeflate) this.perMessageDeflate = {}; + if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { + this.perMessageDeflate.threshold = 1024; + } - var self = this; + // 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; - if (this.writeBuffer.length) { - this.once('drain', function() { - if (this.upgrading) { - waitForUpgrade(); - } else { - close(); - } - }); - } else if (this.upgrading) { - waitForUpgrade(); - } else { - close(); + // 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; } } - function close() { - self.onClose('forced close'); - debug('socket closing - telling transport to close'); - self.transport.close(); - } + this.open(); +} - function cleanupAndClose() { - self.removeListener('upgrade', cleanupAndClose); - self.removeListener('upgradeError', cleanupAndClose); - close(); - } +Socket.priorWebsocketSuccess = false; - 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); - } +/** + * Mix in `Emitter`. + */ - return this; -}; +Emitter(Socket.prototype); /** - * Called upon transport error + * Protocol version. * - * @api private + * @api public */ -Socket.prototype.onError = function (err) { - debug('socket error %j', err); - Socket.priorWebsocketSuccess = false; - this.emit('error', err); - this.onClose('transport error', err); -}; +Socket.protocol = parser.protocol; // this is an int /** - * Called upon transport close. - * - * @api private + * Expose deps for legacy compatibility + * and standalone browser access. */ -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; +Socket.Socket = Socket; +Socket.Transport = _dereq_('./transport'); +Socket.transports = _dereq_('./transports'); +Socket.parser = _dereq_('engine.io-parser'); - // clear timers - clearTimeout(this.pingIntervalTimer); - clearTimeout(this.pingTimeoutTimer); +/** + * Creates transport of the given type. + * + * @param {String} transport name + * @return {Transport} + * @api private + */ - // stop event from firing again for transport - this.transport.removeAllListeners('close'); +Socket.prototype.createTransport = function (name) { + debug('creating transport "%s"', name); + var query = clone(this.query); - // ensure transport won't stay open - this.transport.close(); + // append engine.io protocol identifier + query.EIO = parser.protocol; - // ignore further transport communication - this.transport.removeAllListeners(); + // transport name + query.transport = name; - // set ready state - this.readyState = 'closed'; + // session id if we already have one + if (this.id) query.sid = this.id; - // clear session id - this.id = null; + 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 + }); - // emit close event - this.emit('close', reason, desc); + return transport; +}; - // clean buffers after, so users can still - // grab the buffers on `close` event - self.writeBuffer = []; - self.prevBufferLen = 0; +function clone (obj) { + var o = {}; + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + o[i] = obj[i]; + } } -}; + return o; +} /** - * Filters upgrades, returning only those matching client transports. + * Initializes transport to use and starts probe. * - * @param {Array} server upgrades * @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'; -Socket.prototype.filterUpgrades = function (upgrades) { - var filteredUpgrades = []; - for (var i = 0, j = upgrades.length; i