(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.Mock = global.Mock || {}))); }(this, (function (exports) { 'use strict'; var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; /** * Check if we're required to add a port number. * * @see https://url.spec.whatwg.org/#default-port * @param {Number|String} port Port number we need to check * @param {String} protocol Protocol we need to check against. * @returns {Boolean} Is it a default port for the given protocol * @api private */ var requiresPort = function required(port, protocol) { protocol = protocol.split(':')[0]; port = +port; if (!port) { return false; } switch (protocol) { case 'http': case 'ws': return port !== 80; case 'https': case 'wss': return port !== 443; case 'ftp': return port !== 21; case 'gopher': return port !== 70; case 'file': return false; } return port !== 0; }; var has = Object.prototype.hasOwnProperty; var undef; /** * Decode a URI encoded string. * * @param {String} input The URI encoded string. * @returns {String|Null} The decoded string. * @api private */ function decode(input) { try { return decodeURIComponent(input.replace(/\+/g, ' ')); } catch (e) { return null; } } /** * Attempts to encode a given input. * * @param {String} input The string that needs to be encoded. * @returns {String|Null} The encoded string. * @api private */ function encode(input) { try { return encodeURIComponent(input); } catch (e) { return null; } } /** * Simple query string parser. * * @param {String} query The query string that needs to be parsed. * @returns {Object} * @api public */ function querystring(query) { var parser = /([^=?#&]+)=?([^&]*)/g , result = {} , part; while (part = parser.exec(query)) { var key = decode(part[1]) , value = decode(part[2]); // // Prevent overriding of existing properties. This ensures that build-in // methods like `toString` or __proto__ are not overriden by malicious // querystrings. // // In the case if failed decoding, we want to omit the key/value pairs // from the result. // if (key === null || value === null || key in result) { continue; } result[key] = value; } return result; } /** * Transform a query string to an object. * * @param {Object} obj Object that should be transformed. * @param {String} prefix Optional prefix. * @returns {String} * @api public */ function querystringify(obj, prefix) { prefix = prefix || ''; var pairs = [] , value , key; // // Optionally prefix with a '?' if needed // if ('string' !== typeof prefix) { prefix = '?'; } for (key in obj) { if (has.call(obj, key)) { value = obj[key]; // // Edge cases where we actually want to encode the value to an empty // string instead of the stringified value. // if (!value && (value === null || value === undef || isNaN(value))) { value = ''; } key = encode(key); value = encode(value); // // If we failed to encode the strings, we should bail out as we don't // want to add invalid strings to the query. // if (key === null || value === null) { continue; } pairs.push(key +'='+ value); } } return pairs.length ? prefix + pairs.join('&') : ''; } // // Expose the module. // var stringify = querystringify; var parse = querystring; var querystringify_1 = { stringify: stringify, parse: parse }; var CRHTLF = /[\n\r\t]/g; var slashes = /^[A-Za-z][A-Za-z0-9+-.]*:\/\//; var protocolre = /^([a-z][a-z0-9.+-]*:)?(\/\/)?([\\/]+)?([\S\s]*)/i; var windowsDriveLetter = /^[a-zA-Z]:/; var whitespace = /^[\x00-\x20\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+/; /** * Trim a given string. * * @param {String} str String to trim. * @public */ function trimLeft(str) { return (str ? str : '').toString().replace(whitespace, ''); } /** * These are the parse rules for the URL parser, it informs the parser * about: * * 0. The char it Needs to parse, if it's a string it should be done using * indexOf, RegExp using exec and NaN means set as current value. * 1. The property we should set when parsing this value. * 2. Indication if it's backwards or forward parsing, when set as number it's * the value of extra chars that should be split off. * 3. Inherit from location if non existing in the parser. * 4. `toLowerCase` the resulting value. */ var rules = [ ['#', 'hash'], // Extract from the back. ['?', 'query'], // Extract from the back. function sanitize(address, url) { // Sanitize what is left of the address return isSpecial(url.protocol) ? address.replace(/\\/g, '/') : address; }, ['/', 'pathname'], // Extract from the back. ['@', 'auth', 1], // Extract from the front. [NaN, 'host', undefined, 1, 1], // Set left over value. [/:(\d*)$/, 'port', undefined, 1], // RegExp the back. [NaN, 'hostname', undefined, 1, 1] // Set left over. ]; /** * These properties should not be copied or inherited from. This is only needed * for all non blob URL's as a blob URL does not include a hash, only the * origin. * * @type {Object} * @private */ var ignore = { hash: 1, query: 1 }; /** * The location object differs when your code is loaded through a normal page, * Worker or through a worker using a blob. And with the blobble begins the * trouble as the location object will contain the URL of the blob, not the * location of the page where our code is loaded in. The actual origin is * encoded in the `pathname` so we can thankfully generate a good "default" * location from it so we can generate proper relative URL's again. * * @param {Object|String} loc Optional default location object. * @returns {Object} lolcation object. * @public */ function lolcation(loc) { var globalVar; if (typeof window !== 'undefined') { globalVar = window; } else if (typeof commonjsGlobal !== 'undefined') { globalVar = commonjsGlobal; } else if (typeof self !== 'undefined') { globalVar = self; } else { globalVar = {}; } var location = globalVar.location || {}; loc = loc || location; var finaldestination = {} , type = typeof loc , key; if ('blob:' === loc.protocol) { finaldestination = new Url(unescape(loc.pathname), {}); } else if ('string' === type) { finaldestination = new Url(loc, {}); for (key in ignore) { delete finaldestination[key]; } } else if ('object' === type) { for (key in loc) { if (key in ignore) { continue; } finaldestination[key] = loc[key]; } if (finaldestination.slashes === undefined) { finaldestination.slashes = slashes.test(loc.href); } } return finaldestination; } /** * Check whether a protocol scheme is special. * * @param {String} The protocol scheme of the URL * @return {Boolean} `true` if the protocol scheme is special, else `false` * @private */ function isSpecial(scheme) { return ( scheme === 'file:' || scheme === 'ftp:' || scheme === 'http:' || scheme === 'https:' || scheme === 'ws:' || scheme === 'wss:' ); } /** * @typedef ProtocolExtract * @type Object * @property {String} protocol Protocol matched in the URL, in lowercase. * @property {Boolean} slashes `true` if protocol is followed by "//", else `false`. * @property {String} rest Rest of the URL that is not part of the protocol. */ /** * Extract protocol information from a URL with/without double slash ("//"). * * @param {String} address URL we want to extract from. * @param {Object} location * @return {ProtocolExtract} Extracted information. * @private */ function extractProtocol(address, location) { address = trimLeft(address); address = address.replace(CRHTLF, ''); location = location || {}; var match = protocolre.exec(address); var protocol = match[1] ? match[1].toLowerCase() : ''; var forwardSlashes = !!match[2]; var otherSlashes = !!match[3]; var slashesCount = 0; var rest; if (forwardSlashes) { if (otherSlashes) { rest = match[2] + match[3] + match[4]; slashesCount = match[2].length + match[3].length; } else { rest = match[2] + match[4]; slashesCount = match[2].length; } } else { if (otherSlashes) { rest = match[3] + match[4]; slashesCount = match[3].length; } else { rest = match[4]; } } if (protocol === 'file:') { if (slashesCount >= 2) { rest = rest.slice(2); } } else if (isSpecial(protocol)) { rest = match[4]; } else if (protocol) { if (forwardSlashes) { rest = rest.slice(2); } } else if (slashesCount >= 2 && isSpecial(location.protocol)) { rest = match[4]; } return { protocol: protocol, slashes: forwardSlashes || isSpecial(protocol), slashesCount: slashesCount, rest: rest }; } /** * Resolve a relative URL pathname against a base URL pathname. * * @param {String} relative Pathname of the relative URL. * @param {String} base Pathname of the base URL. * @return {String} Resolved pathname. * @private */ function resolve(relative, base) { if (relative === '') { return base; } var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/')) , i = path.length , last = path[i - 1] , unshift = false , up = 0; while (i--) { if (path[i] === '.') { path.splice(i, 1); } else if (path[i] === '..') { path.splice(i, 1); up++; } else if (up) { if (i === 0) { unshift = true; } path.splice(i, 1); up--; } } if (unshift) { path.unshift(''); } if (last === '.' || last === '..') { path.push(''); } return path.join('/'); } /** * The actual URL instance. Instead of returning an object we've opted-in to * create an actual constructor as it's much more memory efficient and * faster and it pleases my OCD. * * It is worth noting that we should not use `URL` as class name to prevent * clashes with the global URL instance that got introduced in browsers. * * @constructor * @param {String} address URL we want to parse. * @param {Object|String} [location] Location defaults for relative paths. * @param {Boolean|Function} [parser] Parser for the query string. * @private */ function Url(address, location, parser) { address = trimLeft(address); address = address.replace(CRHTLF, ''); if (!(this instanceof Url)) { return new Url(address, location, parser); } var relative, extracted, parse, instruction, index, key , instructions = rules.slice() , type = typeof location , url = this , i = 0; // // The following if statements allows this module two have compatibility with // 2 different API: // // 1. Node.js's `url.parse` api which accepts a URL, boolean as arguments // where the boolean indicates that the query string should also be parsed. // // 2. The `URL` interface of the browser which accepts a URL, object as // arguments. The supplied object will be used as default values / fall-back // for relative paths. // if ('object' !== type && 'string' !== type) { parser = location; location = null; } if (parser && 'function' !== typeof parser) { parser = querystringify_1.parse; } location = lolcation(location); // // Extract protocol information before running the instructions. // extracted = extractProtocol(address || '', location); relative = !extracted.protocol && !extracted.slashes; url.slashes = extracted.slashes || relative && location.slashes; url.protocol = extracted.protocol || location.protocol || ''; address = extracted.rest; // // When the authority component is absent the URL starts with a path // component. // if ( extracted.protocol === 'file:' && ( extracted.slashesCount !== 2 || windowsDriveLetter.test(address)) || (!extracted.slashes && (extracted.protocol || extracted.slashesCount < 2 || !isSpecial(url.protocol))) ) { instructions[3] = [/(.*)/, 'pathname']; } for (; i < instructions.length; i++) { instruction = instructions[i]; if (typeof instruction === 'function') { address = instruction(address, url); continue; } parse = instruction[0]; key = instruction[1]; if (parse !== parse) { url[key] = address; } else if ('string' === typeof parse) { index = parse === '@' ? address.lastIndexOf(parse) : address.indexOf(parse); if (~index) { if ('number' === typeof instruction[2]) { url[key] = address.slice(0, index); address = address.slice(index + instruction[2]); } else { url[key] = address.slice(index); address = address.slice(0, index); } } } else if ((index = parse.exec(address))) { url[key] = index[1]; address = address.slice(0, index.index); } url[key] = url[key] || ( relative && instruction[3] ? location[key] || '' : '' ); // // Hostname, host and protocol should be lowercased so they can be used to // create a proper `origin`. // if (instruction[4]) { url[key] = url[key].toLowerCase(); } } // // Also parse the supplied query string in to an object. If we're supplied // with a custom parser as function use that instead of the default build-in // parser. // if (parser) { url.query = parser(url.query); } // // If the URL is relative, resolve the pathname against the base URL. // if ( relative && location.slashes && url.pathname.charAt(0) !== '/' && (url.pathname !== '' || location.pathname !== '') ) { url.pathname = resolve(url.pathname, location.pathname); } // // Default to a / for pathname if none exists. This normalizes the URL // to always have a / // if (url.pathname.charAt(0) !== '/' && isSpecial(url.protocol)) { url.pathname = '/' + url.pathname; } // // We should not add port numbers if they are already the default port number // for a given protocol. As the host also contains the port number we're going // override it with the hostname which contains no port number. // if (!requiresPort(url.port, url.protocol)) { url.host = url.hostname; url.port = ''; } // // Parse down the `auth` for the username and password. // url.username = url.password = ''; if (url.auth) { index = url.auth.indexOf(':'); if (~index) { url.username = url.auth.slice(0, index); url.username = encodeURIComponent(decodeURIComponent(url.username)); url.password = url.auth.slice(index + 1); url.password = encodeURIComponent(decodeURIComponent(url.password)); } else { url.username = encodeURIComponent(decodeURIComponent(url.auth)); } url.auth = url.password ? url.username +':'+ url.password : url.username; } url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host ? url.protocol +'//'+ url.host : 'null'; // // The href is just the compiled result. // url.href = url.toString(); } /** * This is convenience method for changing properties in the URL instance to * insure that they all propagate correctly. * * @param {String} part Property we need to adjust. * @param {Mixed} value The newly assigned value. * @param {Boolean|Function} fn When setting the query, it will be the function * used to parse the query. * When setting the protocol, double slash will be * removed from the final url if it is true. * @returns {URL} URL instance for chaining. * @public */ function set(part, value, fn) { var url = this; switch (part) { case 'query': if ('string' === typeof value && value.length) { value = (fn || querystringify_1.parse)(value); } url[part] = value; break; case 'port': url[part] = value; if (!requiresPort(value, url.protocol)) { url.host = url.hostname; url[part] = ''; } else if (value) { url.host = url.hostname +':'+ value; } break; case 'hostname': url[part] = value; if (url.port) { value += ':'+ url.port; } url.host = value; break; case 'host': url[part] = value; if (/:\d+$/.test(value)) { value = value.split(':'); url.port = value.pop(); url.hostname = value.join(':'); } else { url.hostname = value; url.port = ''; } break; case 'protocol': url.protocol = value.toLowerCase(); url.slashes = !fn; break; case 'pathname': case 'hash': if (value) { var char = part === 'pathname' ? '/' : '#'; url[part] = value.charAt(0) !== char ? char + value : value; } else { url[part] = value; } break; case 'username': case 'password': url[part] = encodeURIComponent(value); break; case 'auth': var index = value.indexOf(':'); if (~index) { url.username = value.slice(0, index); url.username = encodeURIComponent(decodeURIComponent(url.username)); url.password = value.slice(index + 1); url.password = encodeURIComponent(decodeURIComponent(url.password)); } else { url.username = encodeURIComponent(decodeURIComponent(value)); } } for (var i = 0; i < rules.length; i++) { var ins = rules[i]; if (ins[4]) { url[ins[1]] = url[ins[1]].toLowerCase(); } } url.auth = url.password ? url.username +':'+ url.password : url.username; url.origin = url.protocol !== 'file:' && isSpecial(url.protocol) && url.host ? url.protocol +'//'+ url.host : 'null'; url.href = url.toString(); return url; } /** * Transform the properties back in to a valid and full URL string. * * @param {Function} stringify Optional query stringify function. * @returns {String} Compiled version of the URL. * @public */ function toString(stringify) { if (!stringify || 'function' !== typeof stringify) { stringify = querystringify_1.stringify; } var query , url = this , host = url.host , protocol = url.protocol; if (protocol && protocol.charAt(protocol.length - 1) !== ':') { protocol += ':'; } var result = protocol + ((url.protocol && url.slashes) || isSpecial(url.protocol) ? '//' : ''); if (url.username) { result += url.username; if (url.password) { result += ':'+ url.password; } result += '@'; } else if (url.password) { result += ':'+ url.password; result += '@'; } else if ( url.protocol !== 'file:' && isSpecial(url.protocol) && !host && url.pathname !== '/' ) { // // Add back the empty userinfo, otherwise the original invalid URL // might be transformed into a valid one with `url.pathname` as host. // result += '@'; } // // Trailing colon is removed from `url.host` when it is parsed. If it still // ends with a colon, then add back the trailing colon that was removed. This // prevents an invalid URL from being transformed into a valid one. // if (host[host.length - 1] === ':') { host += ':'; } result += host + url.pathname; query = 'object' === typeof url.query ? stringify(url.query) : url.query; if (query) { result += '?' !== query.charAt(0) ? '?'+ query : query; } if (url.hash) { result += url.hash; } return result; } Url.prototype = { set: set, toString: toString }; // // Expose the URL parser and some additional properties that might be useful for // others or testing. // Url.extractProtocol = extractProtocol; Url.location = lolcation; Url.trimLeft = trimLeft; Url.qs = querystringify_1; var urlParse = Url; /* * This delay allows the thread to finish assigning its on* methods * before invoking the delay callback. This is purely a timing hack. * http://geekabyte.blogspot.com/2014/01/javascript-effect-of-setting-settimeout.html * * @param {callback: function} the callback which will be invoked after the timeout * @parma {context: object} the context in which to invoke the function */ function delay(callback, context) { setTimeout(function (timeoutContext) { return callback.call(timeoutContext); }, 4, context); } function log(method, message) { /* eslint-disable no-console */ if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'test') { console[method].call(null, message); } /* eslint-enable no-console */ } function reject(array, callback) { if ( array === void 0 ) array = []; var results = []; array.forEach(function (itemInArray) { if (!callback(itemInArray)) { results.push(itemInArray); } }); return results; } function filter(array, callback) { if ( array === void 0 ) array = []; var results = []; array.forEach(function (itemInArray) { if (callback(itemInArray)) { results.push(itemInArray); } }); return results; } /* * EventTarget is an interface implemented by objects that can * receive events and may have listeners for them. * * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget */ var EventTarget = function EventTarget() { this.listeners = {}; }; /* * Ties a listener function to an event type which can later be invoked via the * dispatchEvent method. * * @param {string} type - the type of event (ie: 'open', 'message', etc.) * @param {function} listener - callback function to invoke when an event is dispatched matching the type * @param {boolean} useCapture - N/A TODO: implement useCapture functionality */ EventTarget.prototype.addEventListener = function addEventListener (type, listener /* , useCapture */) { if (typeof listener === 'function') { if (!Array.isArray(this.listeners[type])) { this.listeners[type] = []; } // Only add the same function once if (filter(this.listeners[type], function (item) { return item === listener; }).length === 0) { this.listeners[type].push(listener); } } }; /* * Removes the listener so it will no longer be invoked via the dispatchEvent method. * * @param {string} type - the type of event (ie: 'open', 'message', etc.) * @param {function} listener - callback function to invoke when an event is dispatched matching the type * @param {boolean} useCapture - N/A TODO: implement useCapture functionality */ EventTarget.prototype.removeEventListener = function removeEventListener (type, removingListener /* , useCapture */) { var arrayOfListeners = this.listeners[type]; this.listeners[type] = reject(arrayOfListeners, function (listener) { return listener === removingListener; }); }; /* * Invokes all listener functions that are listening to the given event.type property. Each * listener will be passed the event as the first argument. * * @param {object} event - event object which will be passed to all listeners of the event.type property */ EventTarget.prototype.dispatchEvent = function dispatchEvent (event) { var this$1 = this; var customArguments = [], len = arguments.length - 1; while ( len-- > 0 ) customArguments[ len ] = arguments[ len + 1 ]; var eventName = event.type; var listeners = this.listeners[eventName]; if (!Array.isArray(listeners)) { return false; } listeners.forEach(function (listener) { if (customArguments.length > 0) { listener.apply(this$1, customArguments); } else { listener.call(this$1, event); } }); return true; }; function trimQueryPartFromURL(url) { var queryIndex = url.indexOf('?'); return queryIndex >= 0 ? url.slice(0, queryIndex) : url; } /* * The network bridge is a way for the mock websocket object to 'communicate' with * all available servers. This is a singleton object so it is important that you * clean up urlMap whenever you are finished. */ var NetworkBridge = function NetworkBridge() { this.urlMap = {}; }; /* * Attaches a websocket object to the urlMap hash so that it can find the server * it is connected to and the server in turn can find it. * * @param {object} websocket - websocket object to add to the urlMap hash * @param {string} url */ NetworkBridge.prototype.attachWebSocket = function attachWebSocket (websocket, url) { var serverURL = trimQueryPartFromURL(url); var connectionLookup = this.urlMap[serverURL]; if (connectionLookup && connectionLookup.server && connectionLookup.websockets.indexOf(websocket) === -1) { connectionLookup.websockets.push(websocket); return connectionLookup.server; } }; /* * Attaches a websocket to a room */ NetworkBridge.prototype.addMembershipToRoom = function addMembershipToRoom (websocket, room) { var connectionLookup = this.urlMap[trimQueryPartFromURL(websocket.url)]; if (connectionLookup && connectionLookup.server && connectionLookup.websockets.indexOf(websocket) !== -1) { if (!connectionLookup.roomMemberships[room]) { connectionLookup.roomMemberships[room] = []; } connectionLookup.roomMemberships[room].push(websocket); } }; /* * Attaches a server object to the urlMap hash so that it can find a websockets * which are connected to it and so that websockets can in turn can find it. * * @param {object} server - server object to add to the urlMap hash * @param {string} url */ NetworkBridge.prototype.attachServer = function attachServer (server, url) { var serverUrl = trimQueryPartFromURL(url); var connectionLookup = this.urlMap[serverUrl]; if (!connectionLookup) { this.urlMap[serverUrl] = { server: server, websockets: [], roomMemberships: {} }; return server; } }; /* * Finds the server which is 'running' on the given url. * * @param {string} url - the url to use to find which server is running on it */ NetworkBridge.prototype.serverLookup = function serverLookup (url) { var serverURL = trimQueryPartFromURL(url); var connectionLookup = this.urlMap[serverURL]; if (connectionLookup) { return connectionLookup.server; } }; /* * Finds all websockets which is 'listening' on the given url. * * @param {string} url - the url to use to find all websockets which are associated with it * @param {string} room - if a room is provided, will only return sockets in this room * @param {class} broadcaster - socket that is broadcasting and is to be excluded from the lookup */ NetworkBridge.prototype.websocketsLookup = function websocketsLookup (url, room, broadcaster) { var serverURL = trimQueryPartFromURL(url); var websockets; var connectionLookup = this.urlMap[serverURL]; websockets = connectionLookup ? connectionLookup.websockets : []; if (room) { var members = connectionLookup.roomMemberships[room]; websockets = members || []; } return broadcaster ? websockets.filter(function (websocket) { return websocket !== broadcaster; }) : websockets; }; /* * Removes the entry associated with the url. * * @param {string} url */ NetworkBridge.prototype.removeServer = function removeServer (url) { delete this.urlMap[trimQueryPartFromURL(url)]; }; /* * Removes the individual websocket from the map of associated websockets. * * @param {object} websocket - websocket object to remove from the url map * @param {string} url */ NetworkBridge.prototype.removeWebSocket = function removeWebSocket (websocket, url) { var serverURL = trimQueryPartFromURL(url); var connectionLookup = this.urlMap[serverURL]; if (connectionLookup) { connectionLookup.websockets = reject(connectionLookup.websockets, function (socket) { return socket === websocket; }); } }; /* * Removes a websocket from a room */ NetworkBridge.prototype.removeMembershipFromRoom = function removeMembershipFromRoom (websocket, room) { var connectionLookup = this.urlMap[trimQueryPartFromURL(websocket.url)]; var memberships = connectionLookup.roomMemberships[room]; if (connectionLookup && memberships !== null) { connectionLookup.roomMemberships[room] = reject(memberships, function (socket) { return socket === websocket; }); } }; var networkBridge = new NetworkBridge(); // Note: this is a singleton /* * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent */ var CLOSE_CODES = { CLOSE_NORMAL: 1000, CLOSE_GOING_AWAY: 1001, CLOSE_PROTOCOL_ERROR: 1002, CLOSE_UNSUPPORTED: 1003, CLOSE_NO_STATUS: 1005, CLOSE_ABNORMAL: 1006, UNSUPPORTED_DATA: 1007, POLICY_VIOLATION: 1008, CLOSE_TOO_LARGE: 1009, MISSING_EXTENSION: 1010, INTERNAL_ERROR: 1011, SERVICE_RESTART: 1012, TRY_AGAIN_LATER: 1013, TLS_HANDSHAKE: 1015 }; var ERROR_PREFIX = { CONSTRUCTOR_ERROR: "Failed to construct 'WebSocket':", CLOSE_ERROR: "Failed to execute 'close' on 'WebSocket':", EVENT: { CONSTRUCT: "Failed to construct 'Event':", MESSAGE: "Failed to construct 'MessageEvent':", CLOSE: "Failed to construct 'CloseEvent':" } }; var EventPrototype = function EventPrototype () {}; EventPrototype.prototype.stopPropagation = function stopPropagation () {}; EventPrototype.prototype.stopImmediatePropagation = function stopImmediatePropagation () {}; // if no arguments are passed then the type is set to "undefined" on // chrome and safari. EventPrototype.prototype.initEvent = function initEvent (type, bubbles, cancelable) { if ( type === void 0 ) type = 'undefined'; if ( bubbles === void 0 ) bubbles = false; if ( cancelable === void 0 ) cancelable = false; this.type = "" + type; this.bubbles = Boolean(bubbles); this.cancelable = Boolean(cancelable); }; var Event = (function (EventPrototype$$1) { function Event(type, eventInitConfig) { if ( eventInitConfig === void 0 ) eventInitConfig = {}; EventPrototype$$1.call(this); if (!type) { throw new TypeError(((ERROR_PREFIX.EVENT_ERROR) + " 1 argument required, but only 0 present.")); } if (typeof eventInitConfig !== 'object') { throw new TypeError(((ERROR_PREFIX.EVENT_ERROR) + " parameter 2 ('eventInitDict') is not an object.")); } var bubbles = eventInitConfig.bubbles; var cancelable = eventInitConfig.cancelable; this.type = "" + type; this.timeStamp = Date.now(); this.target = null; this.srcElement = null; this.returnValue = true; this.isTrusted = false; this.eventPhase = 0; this.defaultPrevented = false; this.currentTarget = null; this.cancelable = cancelable ? Boolean(cancelable) : false; this.cancelBubble = false; this.bubbles = bubbles ? Boolean(bubbles) : false; } if ( EventPrototype$$1 ) Event.__proto__ = EventPrototype$$1; Event.prototype = Object.create( EventPrototype$$1 && EventPrototype$$1.prototype ); Event.prototype.constructor = Event; return Event; }(EventPrototype)); var MessageEvent = (function (EventPrototype$$1) { function MessageEvent(type, eventInitConfig) { if ( eventInitConfig === void 0 ) eventInitConfig = {}; EventPrototype$$1.call(this); if (!type) { throw new TypeError(((ERROR_PREFIX.EVENT.MESSAGE) + " 1 argument required, but only 0 present.")); } if (typeof eventInitConfig !== 'object') { throw new TypeError(((ERROR_PREFIX.EVENT.MESSAGE) + " parameter 2 ('eventInitDict') is not an object")); } var bubbles = eventInitConfig.bubbles; var cancelable = eventInitConfig.cancelable; var data = eventInitConfig.data; var origin = eventInitConfig.origin; var lastEventId = eventInitConfig.lastEventId; var ports = eventInitConfig.ports; this.type = "" + type; this.timeStamp = Date.now(); this.target = null; this.srcElement = null; this.returnValue = true; this.isTrusted = false; this.eventPhase = 0; this.defaultPrevented = false; this.currentTarget = null; this.cancelable = cancelable ? Boolean(cancelable) : false; this.canncelBubble = false; this.bubbles = bubbles ? Boolean(bubbles) : false; this.origin = "" + origin; this.ports = typeof ports === 'undefined' ? null : ports; this.data = typeof data === 'undefined' ? null : data; this.lastEventId = "" + (lastEventId || ''); } if ( EventPrototype$$1 ) MessageEvent.__proto__ = EventPrototype$$1; MessageEvent.prototype = Object.create( EventPrototype$$1 && EventPrototype$$1.prototype ); MessageEvent.prototype.constructor = MessageEvent; return MessageEvent; }(EventPrototype)); var CloseEvent = (function (EventPrototype$$1) { function CloseEvent(type, eventInitConfig) { if ( eventInitConfig === void 0 ) eventInitConfig = {}; EventPrototype$$1.call(this); if (!type) { throw new TypeError(((ERROR_PREFIX.EVENT.CLOSE) + " 1 argument required, but only 0 present.")); } if (typeof eventInitConfig !== 'object') { throw new TypeError(((ERROR_PREFIX.EVENT.CLOSE) + " parameter 2 ('eventInitDict') is not an object")); } var bubbles = eventInitConfig.bubbles; var cancelable = eventInitConfig.cancelable; var code = eventInitConfig.code; var reason = eventInitConfig.reason; var wasClean = eventInitConfig.wasClean; this.type = "" + type; this.timeStamp = Date.now(); this.target = null; this.srcElement = null; this.returnValue = true; this.isTrusted = false; this.eventPhase = 0; this.defaultPrevented = false; this.currentTarget = null; this.cancelable = cancelable ? Boolean(cancelable) : false; this.cancelBubble = false; this.bubbles = bubbles ? Boolean(bubbles) : false; this.code = typeof code === 'number' ? parseInt(code, 10) : 0; this.reason = "" + (reason || ''); this.wasClean = wasClean ? Boolean(wasClean) : false; } if ( EventPrototype$$1 ) CloseEvent.__proto__ = EventPrototype$$1; CloseEvent.prototype = Object.create( EventPrototype$$1 && EventPrototype$$1.prototype ); CloseEvent.prototype.constructor = CloseEvent; return CloseEvent; }(EventPrototype)); /* * Creates an Event object and extends it to allow full modification of * its properties. * * @param {object} config - within config you will need to pass type and optionally target */ function createEvent(config) { var type = config.type; var target = config.target; var eventObject = new Event(type); if (target) { eventObject.target = target; eventObject.srcElement = target; eventObject.currentTarget = target; } return eventObject; } /* * Creates a MessageEvent object and extends it to allow full modification of * its properties. * * @param {object} config - within config: type, origin, data and optionally target */ function createMessageEvent(config) { var type = config.type; var origin = config.origin; var data = config.data; var target = config.target; var messageEvent = new MessageEvent(type, { data: data, origin: origin }); if (target) { messageEvent.target = target; messageEvent.srcElement = target; messageEvent.currentTarget = target; } return messageEvent; } /* * Creates a CloseEvent object and extends it to allow full modification of * its properties. * * @param {object} config - within config: type and optionally target, code, and reason */ function createCloseEvent(config) { var code = config.code; var reason = config.reason; var type = config.type; var target = config.target; var wasClean = config.wasClean; if (!wasClean) { wasClean = code === CLOSE_CODES.CLOSE_NORMAL || code === CLOSE_CODES.CLOSE_NO_STATUS; } var closeEvent = new CloseEvent(type, { code: code, reason: reason, wasClean: wasClean }); if (target) { closeEvent.target = target; closeEvent.srcElement = target; closeEvent.currentTarget = target; } return closeEvent; } function closeWebSocketConnection(context, code, reason) { context.readyState = WebSocket$1.CLOSING; var server = networkBridge.serverLookup(context.url); var closeEvent = createCloseEvent({ type: 'close', target: context.target, code: code, reason: reason }); delay(function () { networkBridge.removeWebSocket(context, context.url); context.readyState = WebSocket$1.CLOSED; context.dispatchEvent(closeEvent); if (server) { server.dispatchEvent(closeEvent, server); } }, context); } function failWebSocketConnection(context, code, reason) { context.readyState = WebSocket$1.CLOSING; var server = networkBridge.serverLookup(context.url); var closeEvent = createCloseEvent({ type: 'close', target: context.target, code: code, reason: reason, wasClean: false }); var errorEvent = createEvent({ type: 'error', target: context.target }); delay(function () { networkBridge.removeWebSocket(context, context.url); context.readyState = WebSocket$1.CLOSED; context.dispatchEvent(errorEvent); context.dispatchEvent(closeEvent); if (server) { server.dispatchEvent(closeEvent, server); } }, context); } function normalizeSendData(data) { if (Object.prototype.toString.call(data) !== '[object Blob]' && !(data instanceof ArrayBuffer)) { data = String(data); } return data; } var proxies = new WeakMap(); function proxyFactory(target) { if (proxies.has(target)) { return proxies.get(target); } var proxy = new Proxy(target, { get: function get(obj, prop) { if (prop === 'close') { return function close(options) { if ( options === void 0 ) options = {}; var code = options.code || CLOSE_CODES.CLOSE_NORMAL; var reason = options.reason || ''; closeWebSocketConnection(proxy, code, reason); }; } if (prop === 'send') { return function send(data) { data = normalizeSendData(data); target.dispatchEvent( createMessageEvent({ type: 'message', data: data, origin: this.url, target: target }) ); }; } var toSocketName = function (type) { return (type === 'message' ? ("server::" + type) : type); }; if (prop === 'on') { return function onWrapper(type, cb) { target.addEventListener(toSocketName(type), cb); }; } if (prop === 'off') { return function offWrapper(type, cb) { target.removeEventListener(toSocketName(type), cb); }; } if (prop === 'target') { return target; } return obj[prop]; } }); proxies.set(target, proxy); return proxy; } function lengthInUtf8Bytes(str) { // Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence. var m = encodeURIComponent(str).match(/%[89ABab]/g); return str.length + (m ? m.length : 0); } function urlVerification(url) { var urlRecord = new urlParse(url); var pathname = urlRecord.pathname; var protocol = urlRecord.protocol; var hash = urlRecord.hash; if (!url) { throw new TypeError(((ERROR_PREFIX.CONSTRUCTOR_ERROR) + " 1 argument required, but only 0 present.")); } if (!pathname) { urlRecord.pathname = '/'; } if (protocol === '') { throw new SyntaxError(((ERROR_PREFIX.CONSTRUCTOR_ERROR) + " The URL '" + (urlRecord.toString()) + "' is invalid.")); } if (protocol !== 'ws:' && protocol !== 'wss:') { throw new SyntaxError( ((ERROR_PREFIX.CONSTRUCTOR_ERROR) + " The URL's scheme must be either 'ws' or 'wss'. '" + protocol + "' is not allowed.") ); } if (hash !== '') { /* eslint-disable max-len */ throw new SyntaxError( ((ERROR_PREFIX.CONSTRUCTOR_ERROR) + " The URL contains a fragment identifier ('" + hash + "'). Fragment identifiers are not allowed in WebSocket URLs.") ); /* eslint-enable max-len */ } return urlRecord.toString(); } function protocolVerification(protocols) { if ( protocols === void 0 ) protocols = []; if (!Array.isArray(protocols) && typeof protocols !== 'string') { throw new SyntaxError(((ERROR_PREFIX.CONSTRUCTOR_ERROR) + " The subprotocol '" + (protocols.toString()) + "' is invalid.")); } if (typeof protocols === 'string') { protocols = [protocols]; } var uniq = protocols .map(function (p) { return ({ count: 1, protocol: p }); }) .reduce(function (a, b) { a[b.protocol] = (a[b.protocol] || 0) + b.count; return a; }, {}); var duplicates = Object.keys(uniq).filter(function (a) { return uniq[a] > 1; }); if (duplicates.length > 0) { throw new SyntaxError(((ERROR_PREFIX.CONSTRUCTOR_ERROR) + " The subprotocol '" + (duplicates[0]) + "' is duplicated.")); } return protocols; } /* * The main websocket class which is designed to mimick the native WebSocket class as close * as possible. * * https://html.spec.whatwg.org/multipage/web-sockets.html */ var WebSocket$1 = (function (EventTarget$$1) { function WebSocket(url, protocols) { EventTarget$$1.call(this); this._onopen = null; this._onmessage = null; this._onerror = null; this._onclose = null; this.url = urlVerification(url); protocols = protocolVerification(protocols); this.protocol = protocols[0] || ''; this.binaryType = 'blob'; this.readyState = WebSocket.CONNECTING; var client = proxyFactory(this); var server = networkBridge.attachWebSocket(client, this.url); /* * This delay is needed so that we dont trigger an event before the callbacks have been * setup. For example: * * var socket = new WebSocket('ws://localhost'); * * If we dont have the delay then the event would be triggered right here and this is * before the onopen had a chance to register itself. * * socket.onopen = () => { // this would never be called }; * * and with the delay the event gets triggered here after all of the callbacks have been * registered :-) */ delay(function delayCallback() { if (server) { if ( server.options.verifyClient && typeof server.options.verifyClient === 'function' && !server.options.verifyClient() ) { this.readyState = WebSocket.CLOSED; log( 'error', ("WebSocket connection to '" + (this.url) + "' failed: HTTP Authentication failed; no valid credentials available") ); networkBridge.removeWebSocket(client, this.url); this.dispatchEvent(createEvent({ type: 'error', target: this })); this.dispatchEvent(createCloseEvent({ type: 'close', target: this, code: CLOSE_CODES.CLOSE_NORMAL })); } else { if (server.options.selectProtocol && typeof server.options.selectProtocol === 'function') { var selectedProtocol = server.options.selectProtocol(protocols); var isFilled = selectedProtocol !== ''; var isRequested = protocols.indexOf(selectedProtocol) !== -1; if (isFilled && !isRequested) { this.readyState = WebSocket.CLOSED; log('error', ("WebSocket connection to '" + (this.url) + "' failed: Invalid Sub-Protocol")); networkBridge.removeWebSocket(client, this.url); this.dispatchEvent(createEvent({ type: 'error', target: this })); this.dispatchEvent(createCloseEvent({ type: 'close', target: this, code: CLOSE_CODES.CLOSE_NORMAL })); return; } this.protocol = selectedProtocol; } this.readyState = WebSocket.OPEN; this.dispatchEvent(createEvent({ type: 'open', target: this })); server.dispatchEvent(createEvent({ type: 'connection' }), client); } } else { this.readyState = WebSocket.CLOSED; this.dispatchEvent(createEvent({ type: 'error', target: this })); this.dispatchEvent(createCloseEvent({ type: 'close', target: this, code: CLOSE_CODES.CLOSE_NORMAL })); log('error', ("WebSocket connection to '" + (this.url) + "' failed")); } }, this); } if ( EventTarget$$1 ) WebSocket.__proto__ = EventTarget$$1; WebSocket.prototype = Object.create( EventTarget$$1 && EventTarget$$1.prototype ); WebSocket.prototype.constructor = WebSocket; var prototypeAccessors = { onopen: {},onmessage: {},onclose: {},onerror: {} }; prototypeAccessors.onopen.get = function () { return this._onopen; }; prototypeAccessors.onmessage.get = function () { return this._onmessage; }; prototypeAccessors.onclose.get = function () { return this._onclose; }; prototypeAccessors.onerror.get = function () { return this._onerror; }; prototypeAccessors.onopen.set = function (listener) { this.removeEventListener('open', this._onopen); this._onopen = listener; this.addEventListener('open', listener); }; prototypeAccessors.onmessage.set = function (listener) { this.removeEventListener('message', this._onmessage); this._onmessage = listener; this.addEventListener('message', listener); }; prototypeAccessors.onclose.set = function (listener) { this.removeEventListener('close', this._onclose); this._onclose = listener; this.addEventListener('close', listener); }; prototypeAccessors.onerror.set = function (listener) { this.removeEventListener('error', this._onerror); this._onerror = listener; this.addEventListener('error', listener); }; WebSocket.prototype.send = function send (data) { var this$1 = this; if (this.readyState === WebSocket.CLOSING || this.readyState === WebSocket.CLOSED) { throw new Error('WebSocket is already in CLOSING or CLOSED state'); } // TODO: handle bufferedAmount var messageEvent = createMessageEvent({ type: 'server::message', origin: this.url, data: normalizeSendData(data) }); var server = networkBridge.serverLookup(this.url); if (server) { delay(function () { this$1.dispatchEvent(messageEvent, data); }, server); } }; WebSocket.prototype.close = function close (code, reason) { if (code !== undefined) { if (typeof code !== 'number' || (code !== 1000 && (code < 3000 || code > 4999))) { throw new TypeError( ((ERROR_PREFIX.CLOSE_ERROR) + " The code must be either 1000, or between 3000 and 4999. " + code + " is neither.") ); } } if (reason !== undefined) { var length = lengthInUtf8Bytes(reason); if (length > 123) { throw new SyntaxError(((ERROR_PREFIX.CLOSE_ERROR) + " The message must not be greater than 123 bytes.")); } } if (this.readyState === WebSocket.CLOSING || this.readyState === WebSocket.CLOSED) { return; } var client = proxyFactory(this); if (this.readyState === WebSocket.CONNECTING) { failWebSocketConnection(client, code || CLOSE_CODES.CLOSE_ABNORMAL, reason); } else { closeWebSocketConnection(client, code || CLOSE_CODES.CLOSE_NO_STATUS, reason); } }; Object.defineProperties( WebSocket.prototype, prototypeAccessors ); return WebSocket; }(EventTarget)); WebSocket$1.CONNECTING = 0; WebSocket$1.prototype.CONNECTING = WebSocket$1.CONNECTING; WebSocket$1.OPEN = 1; WebSocket$1.prototype.OPEN = WebSocket$1.OPEN; WebSocket$1.CLOSING = 2; WebSocket$1.prototype.CLOSING = WebSocket$1.CLOSING; WebSocket$1.CLOSED = 3; WebSocket$1.prototype.CLOSED = WebSocket$1.CLOSED; /* * The socket-io class is designed to mimick the real API as closely as possible. * * http://socket.io/docs/ */ var SocketIO$1 = (function (EventTarget$$1) { function SocketIO(url, protocol) { var this$1 = this; if ( url === void 0 ) url = 'socket.io'; if ( protocol === void 0 ) protocol = ''; EventTarget$$1.call(this); this.binaryType = 'blob'; var urlRecord = new urlParse(url); if (!urlRecord.pathname) { urlRecord.pathname = '/'; } this.url = urlRecord.toString(); this.readyState = SocketIO.CONNECTING; this.protocol = ''; this.target = this; if (typeof protocol === 'string' || (typeof protocol === 'object' && protocol !== null)) { this.protocol = protocol; } else if (Array.isArray(protocol) && protocol.length > 0) { this.protocol = protocol[0]; } var server = networkBridge.attachWebSocket(this, this.url); /* * Delay triggering the connection events so they can be defined in time. */ delay(function delayCallback() { if (server) { this.readyState = SocketIO.OPEN; server.dispatchEvent(createEvent({ type: 'connection' }), server, this); server.dispatchEvent(createEvent({ type: 'connect' }), server, this); // alias this.dispatchEvent(createEvent({ type: 'connect', target: this })); } else { this.readyState = SocketIO.CLOSED; this.dispatchEvent(createEvent({ type: 'error', target: this })); this.dispatchEvent( createCloseEvent({ type: 'close', target: this, code: CLOSE_CODES.CLOSE_NORMAL }) ); log('error', ("Socket.io connection to '" + (this.url) + "' failed")); } }, this); /** Add an aliased event listener for close / disconnect */ this.addEventListener('close', function (event) { this$1.dispatchEvent( createCloseEvent({ type: 'disconnect', target: event.target, code: event.code }) ); }); } if ( EventTarget$$1 ) SocketIO.__proto__ = EventTarget$$1; SocketIO.prototype = Object.create( EventTarget$$1 && EventTarget$$1.prototype ); SocketIO.prototype.constructor = SocketIO; var prototypeAccessors = { broadcast: {} }; /* * Closes the SocketIO connection or connection attempt, if any. * If the connection is already CLOSED, this method does nothing. */ SocketIO.prototype.close = function close () { if (this.readyState !== SocketIO.OPEN) { return undefined; } var server = networkBridge.serverLookup(this.url); networkBridge.removeWebSocket(this, this.url); this.readyState = SocketIO.CLOSED; this.dispatchEvent( createCloseEvent({ type: 'close', target: this, code: CLOSE_CODES.CLOSE_NORMAL }) ); if (server) { server.dispatchEvent( createCloseEvent({ type: 'disconnect', target: this, code: CLOSE_CODES.CLOSE_NORMAL }), server ); } return this; }; /* * Alias for Socket#close * * https://github.com/socketio/socket.io-client/blob/master/lib/socket.js#L383 */ SocketIO.prototype.disconnect = function disconnect () { return this.close(); }; /* * Submits an event to the server with a payload */ SocketIO.prototype.emit = function emit (event) { var data = [], len = arguments.length - 1; while ( len-- > 0 ) data[ len ] = arguments[ len + 1 ]; if (this.readyState !== SocketIO.OPEN) { throw new Error('SocketIO is already in CLOSING or CLOSED state'); } var messageEvent = createMessageEvent({ type: event, origin: this.url, data: data }); var server = networkBridge.serverLookup(this.url); if (server) { server.dispatchEvent.apply(server, [ messageEvent ].concat( data )); } return this; }; /* * Submits a 'message' event to the server. * * Should behave exactly like WebSocket#send * * https://github.com/socketio/socket.io-client/blob/master/lib/socket.js#L113 */ SocketIO.prototype.send = function send (data) { this.emit('message', data); return this; }; /* * For broadcasting events to other connected sockets. * * e.g. socket.broadcast.emit('hi!'); * e.g. socket.broadcast.to('my-room').emit('hi!'); */ prototypeAccessors.broadcast.get = function () { if (this.readyState !== SocketIO.OPEN) { throw new Error('SocketIO is already in CLOSING or CLOSED state'); } var self = this; var server = networkBridge.serverLookup(this.url); if (!server) { throw new Error(("SocketIO can not find a server at the specified URL (" + (this.url) + ")")); } return { emit: function emit(event, data) { server.emit(event, data, { websockets: networkBridge.websocketsLookup(self.url, null, self) }); return self; }, to: function to(room) { return server.to(room, self); }, in: function in$1(room) { return server.in(room, self); } }; }; /* * For registering events to be received from the server */ SocketIO.prototype.on = function on (type, callback) { this.addEventListener(type, callback); return this; }; /* * Remove event listener * * https://github.com/component/emitter#emitteroffevent-fn */ SocketIO.prototype.off = function off (type, callback) { this.removeEventListener(type, callback); }; /* * Check if listeners have already been added for an event * * https://github.com/component/emitter#emitterhaslistenersevent */ SocketIO.prototype.hasListeners = function hasListeners (type) { var listeners = this.listeners[type]; if (!Array.isArray(listeners)) { return false; } return !!listeners.length; }; /* * Join a room on a server * * http://socket.io/docs/rooms-and-namespaces/#joining-and-leaving */ SocketIO.prototype.join = function join (room) { networkBridge.addMembershipToRoom(this, room); }; /* * Get the websocket to leave the room * * http://socket.io/docs/rooms-and-namespaces/#joining-and-leaving */ SocketIO.prototype.leave = function leave (room) { networkBridge.removeMembershipFromRoom(this, room); }; SocketIO.prototype.to = function to (room) { return this.broadcast.to(room); }; SocketIO.prototype.in = function in$1 () { return this.to.apply(null, arguments); }; /* * Invokes all listener functions that are listening to the given event.type property. Each * listener will be passed the event as the first argument. * * @param {object} event - event object which will be passed to all listeners of the event.type property */ SocketIO.prototype.dispatchEvent = function dispatchEvent (event) { var this$1 = this; var customArguments = [], len = arguments.length - 1; while ( len-- > 0 ) customArguments[ len ] = arguments[ len + 1 ]; var eventName = event.type; var listeners = this.listeners[eventName]; if (!Array.isArray(listeners)) { return false; } listeners.forEach(function (listener) { if (customArguments.length > 0) { listener.apply(this$1, customArguments); } else { // Regular WebSockets expect a MessageEvent but Socketio.io just wants raw data // payload instanceof MessageEvent works, but you can't isntance of NodeEvent // for now we detect if the output has data defined on it listener.call(this$1, event.data ? event.data : event); } }); }; Object.defineProperties( SocketIO.prototype, prototypeAccessors ); return SocketIO; }(EventTarget)); SocketIO$1.CONNECTING = 0; SocketIO$1.OPEN = 1; SocketIO$1.CLOSING = 2; SocketIO$1.CLOSED = 3; /* * Static constructor methods for the IO Socket */ var IO = function ioConstructor(url, protocol) { return new SocketIO$1(url, protocol); }; /* * Alias the raw IO() constructor */ IO.connect = function ioConnect(url, protocol) { /* eslint-disable new-cap */ return IO(url, protocol); /* eslint-enable new-cap */ }; var dedupe = function (arr) { return arr.reduce(function (deduped, b) { if (deduped.indexOf(b) > -1) { return deduped; } return deduped.concat(b); }, []); }; function retrieveGlobalObject() { if (typeof window !== 'undefined') { return window; } return typeof process === 'object' && typeof require === 'function' && typeof global === 'object' ? global : this; } var defaultOptions = { mock: true, verifyClient: null, selectProtocol: null }; var Server$1 = (function (EventTarget$$1) { function Server(url, options) { if ( options === void 0 ) options = defaultOptions; EventTarget$$1.call(this); var urlRecord = new urlParse(url); if (!urlRecord.pathname) { urlRecord.pathname = '/'; } this.url = urlRecord.toString(); this.originalWebSocket = null; var server = networkBridge.attachServer(this, this.url); if (!server) { this.dispatchEvent(createEvent({ type: 'error' })); throw new Error('A mock server is already listening on this url'); } this.options = Object.assign({}, defaultOptions, options); if (this.options.mock) { this.mockWebsocket(); } } if ( EventTarget$$1 ) Server.__proto__ = EventTarget$$1; Server.prototype = Object.create( EventTarget$$1 && EventTarget$$1.prototype ); Server.prototype.constructor = Server; /* * Attaches the mock websocket object to the global object */ Server.prototype.mockWebsocket = function mockWebsocket () { var globalObj = retrieveGlobalObject(); this.originalWebSocket = globalObj.WebSocket; globalObj.WebSocket = WebSocket$1; }; /* * Removes the mock websocket object from the global object */ Server.prototype.restoreWebsocket = function restoreWebsocket () { var globalObj = retrieveGlobalObject(); if (this.originalWebSocket !== null) { globalObj.WebSocket = this.originalWebSocket; } this.originalWebSocket = null; }; /** * Removes itself from the urlMap so another server could add itself to the url. * @param {function} callback - The callback is called when the server is stopped */ Server.prototype.stop = function stop (callback) { if ( callback === void 0 ) callback = function () {}; if (this.options.mock) { this.restoreWebsocket(); } networkBridge.removeServer(this.url); if (typeof callback === 'function') { callback(); } }; /* * This is the main function for the mock server to subscribe to the on events. * * ie: mockServer.on('connection', function() { console.log('a mock client connected'); }); * * @param {string} type - The event key to subscribe to. Valid keys are: connection, message, and close. * @param {function} callback - The callback which should be called when a certain event is fired. */ Server.prototype.on = function on (type, callback) { this.addEventListener(type, callback); }; /* * Remove event listener */ Server.prototype.off = function off (type, callback) { this.removeEventListener(type, callback); }; /* * Closes the connection and triggers the onclose method of all listening * websockets. After that it removes itself from the urlMap so another server * could add itself to the url. * * @param {object} options */ Server.prototype.close = function close (options) { if ( options === void 0 ) options = {}; var code = options.code; var reason = options.reason; var wasClean = options.wasClean; var listeners = networkBridge.websocketsLookup(this.url); // Remove server before notifications to prevent immediate reconnects from // socket onclose handlers networkBridge.removeServer(this.url); listeners.forEach(function (socket) { socket.readyState = WebSocket$1.CLOSED; socket.dispatchEvent( createCloseEvent({ type: 'close', target: socket.target, code: code || CLOSE_CODES.CLOSE_NORMAL, reason: reason || '', wasClean: wasClean }) ); }); this.dispatchEvent(createCloseEvent({ type: 'close' }), this); }; /* * Sends a generic message event to all mock clients. */ Server.prototype.emit = function emit (event, data, options) { var this$1 = this; if ( options === void 0 ) options = {}; var websockets = options.websockets; if (!websockets) { websockets = networkBridge.websocketsLookup(this.url); } var normalizedData; if (typeof options !== 'object' || arguments.length > 3) { data = Array.prototype.slice.call(arguments, 1, arguments.length); normalizedData = data.map(function (item) { return normalizeSendData(item); }); } else { normalizedData = normalizeSendData(data); } websockets.forEach(function (socket) { var messageData = socket instanceof SocketIO$1 ? data : normalizedData; if (Array.isArray(messageData)) { socket.dispatchEvent.apply( socket, [ createMessageEvent({ type: event, data: messageData, origin: this$1.url, target: socket.target }) ].concat( messageData ) ); } else { socket.dispatchEvent( createMessageEvent({ type: event, data: messageData, origin: this$1.url, target: socket.target }) ); } }); }; /* * Returns an array of websockets which are listening to this server * TOOD: this should return a set and not be a method */ Server.prototype.clients = function clients () { return networkBridge.websocketsLookup(this.url); }; /* * Prepares a method to submit an event to members of the room * * e.g. server.to('my-room').emit('hi!'); */ Server.prototype.to = function to (room, broadcaster, broadcastList) { var this$1 = this; if ( broadcastList === void 0 ) broadcastList = []; var self = this; var websockets = dedupe(broadcastList.concat(networkBridge.websocketsLookup(this.url, room, broadcaster))); return { to: function (chainedRoom, chainedBroadcaster) { return this$1.to.call(this$1, chainedRoom, chainedBroadcaster, websockets); }, emit: function emit(event, data) { self.emit(event, data, { websockets: websockets }); } }; }; /* * Alias for Server.to */ Server.prototype.in = function in$1 () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return this.to.apply(null, args); }; /* * Simulate an event from the server to the clients. Useful for * simulating errors. */ Server.prototype.simulate = function simulate (event) { var listeners = networkBridge.websocketsLookup(this.url); if (event === 'error') { listeners.forEach(function (socket) { socket.readyState = WebSocket$1.CLOSED; socket.dispatchEvent(createEvent({ type: 'error', target: socket.target })); }); } }; return Server; }(EventTarget)); /* * Alternative constructor to support namespaces in socket.io * * http://socket.io/docs/rooms-and-namespaces/#custom-namespaces */ Server$1.of = function of(url) { return new Server$1(url); }; var Server = Server$1; var WebSocket = WebSocket$1; var SocketIO$$1 = IO; exports.Server = Server; exports.WebSocket = WebSocket; exports.SocketIO = SocketIO$$1; Object.defineProperty(exports, '__esModule', { value: true }); })));