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
 | 
						|
};
 |