267 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			267 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								'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
							 | 
						||
| 
								 | 
							
								};
							 |