| 'use strict'; |
| |
| const { kForOnEventAttribute, kListener } = require('./constants'); |
| |
| const kCode = Symbol('kCode'); |
| const kData = Symbol('kData'); |
| const kError = Symbol('kError'); |
| const kMessage = Symbol('kMessage'); |
| const kReason = Symbol('kReason'); |
| const kTarget = Symbol('kTarget'); |
| const kType = Symbol('kType'); |
| const kWasClean = Symbol('kWasClean'); |
| |
| /** |
| * Class representing an event. |
| */ |
| class Event { |
| /** |
| * Create a new `Event`. |
| * |
| * @param {String} type The name of the event |
| * @throws {TypeError} If the `type` argument is not specified |
| */ |
| constructor(type) { |
| this[kTarget] = null; |
| this[kType] = type; |
| } |
| |
| /** |
| * @type {*} |
| */ |
| get target() { |
| return this[kTarget]; |
| } |
| |
| /** |
| * @type {String} |
| */ |
| get type() { |
| return this[kType]; |
| } |
| } |
| |
| Object.defineProperty(Event.prototype, 'target', { enumerable: true }); |
| Object.defineProperty(Event.prototype, 'type', { enumerable: true }); |
| |
| /** |
| * Class representing a close event. |
| * |
| * @extends Event |
| */ |
| class CloseEvent extends Event { |
| /** |
| * Create a new `CloseEvent`. |
| * |
| * @param {String} type The name of the event |
| * @param {Object} [options] A dictionary object that allows for setting |
| * attributes via object members of the same name |
| * @param {Number} [options.code=0] The status code explaining why the |
| * connection was closed |
| * @param {String} [options.reason=''] A human-readable string explaining why |
| * the connection was closed |
| * @param {Boolean} [options.wasClean=false] Indicates whether or not the |
| * connection was cleanly closed |
| */ |
| constructor(type, options = {}) { |
| super(type); |
| |
| this[kCode] = options.code === undefined ? 0 : options.code; |
| this[kReason] = options.reason === undefined ? '' : options.reason; |
| this[kWasClean] = options.wasClean === undefined ? false : options.wasClean; |
| } |
| |
| /** |
| * @type {Number} |
| */ |
| get code() { |
| return this[kCode]; |
| } |
| |
| /** |
| * @type {String} |
| */ |
| get reason() { |
| return this[kReason]; |
| } |
| |
| /** |
| * @type {Boolean} |
| */ |
| get wasClean() { |
| return this[kWasClean]; |
| } |
| } |
| |
| Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true }); |
| Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true }); |
| Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true }); |
| |
| /** |
| * Class representing an error event. |
| * |
| * @extends Event |
| */ |
| class ErrorEvent extends Event { |
| /** |
| * Create a new `ErrorEvent`. |
| * |
| * @param {String} type The name of the event |
| * @param {Object} [options] A dictionary object that allows for setting |
| * attributes via object members of the same name |
| * @param {*} [options.error=null] The error that generated this event |
| * @param {String} [options.message=''] The error message |
| */ |
| constructor(type, options = {}) { |
| super(type); |
| |
| this[kError] = options.error === undefined ? null : options.error; |
| this[kMessage] = options.message === undefined ? '' : options.message; |
| } |
| |
| /** |
| * @type {*} |
| */ |
| get error() { |
| return this[kError]; |
| } |
| |
| /** |
| * @type {String} |
| */ |
| get message() { |
| return this[kMessage]; |
| } |
| } |
| |
| Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true }); |
| Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true }); |
| |
| /** |
| * Class representing a message event. |
| * |
| * @extends Event |
| */ |
| class MessageEvent extends Event { |
| /** |
| * Create a new `MessageEvent`. |
| * |
| * @param {String} type The name of the event |
| * @param {Object} [options] A dictionary object that allows for setting |
| * attributes via object members of the same name |
| * @param {*} [options.data=null] The message content |
| */ |
| constructor(type, options = {}) { |
| super(type); |
| |
| this[kData] = options.data === undefined ? null : options.data; |
| } |
| |
| /** |
| * @type {*} |
| */ |
| get data() { |
| return this[kData]; |
| } |
| } |
| |
| Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true }); |
| |
| /** |
| * This provides methods for emulating the `EventTarget` interface. It's not |
| * meant to be used directly. |
| * |
| * @mixin |
| */ |
| const EventTarget = { |
| /** |
| * Register an event listener. |
| * |
| * @param {String} type A string representing the event type to listen for |
| * @param {Function} listener The listener to add |
| * @param {Object} [options] An options object specifies characteristics about |
| * the event listener |
| * @param {Boolean} [options.once=false] A `Boolean` indicating that the |
| * listener should be invoked at most once after being added. If `true`, |
| * the listener would be automatically removed when invoked. |
| * @public |
| */ |
| addEventListener(type, listener, options = {}) { |
| let wrapper; |
| |
| if (type === 'message') { |
| wrapper = function onMessage(data, isBinary) { |
| const event = new MessageEvent('message', { |
| data: isBinary ? data : data.toString() |
| }); |
| |
| event[kTarget] = this; |
| listener.call(this, event); |
| }; |
| } else if (type === 'close') { |
| wrapper = function onClose(code, message) { |
| const event = new CloseEvent('close', { |
| code, |
| reason: message.toString(), |
| wasClean: this._closeFrameReceived && this._closeFrameSent |
| }); |
| |
| event[kTarget] = this; |
| listener.call(this, event); |
| }; |
| } else if (type === 'error') { |
| wrapper = function onError(error) { |
| const event = new ErrorEvent('error', { |
| error, |
| message: error.message |
| }); |
| |
| event[kTarget] = this; |
| listener.call(this, event); |
| }; |
| } else if (type === 'open') { |
| wrapper = function onOpen() { |
| const event = new Event('open'); |
| |
| event[kTarget] = this; |
| listener.call(this, event); |
| }; |
| } else { |
| return; |
| } |
| |
| wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute]; |
| wrapper[kListener] = listener; |
| |
| if (options.once) { |
| this.once(type, wrapper); |
| } else { |
| this.on(type, wrapper); |
| } |
| }, |
| |
| /** |
| * Remove an event listener. |
| * |
| * @param {String} type A string representing the event type to remove |
| * @param {Function} handler The listener to remove |
| * @public |
| */ |
| removeEventListener(type, handler) { |
| for (const listener of this.listeners(type)) { |
| if (listener[kListener] === handler && !listener[kForOnEventAttribute]) { |
| this.removeListener(type, listener); |
| break; |
| } |
| } |
| } |
| }; |
| |
| module.exports = { |
| CloseEvent, |
| ErrorEvent, |
| Event, |
| EventTarget, |
| MessageEvent |
| }; |