| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 | '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|Object)} handler 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, handler, options = {}) {    for (const listener of this.listeners(type)) {      if (        !options[kForOnEventAttribute] &&        listener[kListener] === handler &&        !listener[kForOnEventAttribute]      ) {        return;      }    }    let wrapper;    if (type === 'message') {      wrapper = function onMessage(data, isBinary) {        const event = new MessageEvent('message', {          data: isBinary ? data : data.toString()        });        event[kTarget] = this;        callListener(handler, 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;        callListener(handler, this, event);      };    } else if (type === 'error') {      wrapper = function onError(error) {        const event = new ErrorEvent('error', {          error,          message: error.message        });        event[kTarget] = this;        callListener(handler, this, event);      };    } else if (type === 'open') {      wrapper = function onOpen() {        const event = new Event('open');        event[kTarget] = this;        callListener(handler, this, event);      };    } else {      return;    }    wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];    wrapper[kListener] = handler;    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|Object)} 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};/** * Call an event listener * * @param {(Function|Object)} listener The listener to call * @param {*} thisArg The value to use as `this`` when calling the listener * @param {Event} event The event to pass to the listener * @private */function callListener(listener, thisArg, event) {  if (typeof listener === 'object' && listener.handleEvent) {    listener.handleEvent.call(listener, event);  } else {    listener.call(thisArg, event);  }}
 |