SPB Git forge

spb/admin-ka

Public
41commits 1branches 0releases
172.9 MBsize
maindefault branch
19 days agolast push
JavaScript 65.5% Python 17.8% CSS 13% HTML 3.7%
7.1 KB · 293 lines javascript
Raw Blame History
1'use strict';23const { kForOnEventAttribute, kListener } = require('./constants');45const kCode = Symbol('kCode');6const kData = Symbol('kData');7const kError = Symbol('kError');8const kMessage = Symbol('kMessage');9const kReason = Symbol('kReason');10const kTarget = Symbol('kTarget');11const kType = Symbol('kType');12const kWasClean = Symbol('kWasClean');1314/**15 * Class representing an event.16 */17class Event {18  /**19   * Create a new `Event`.20   *21   * @param {String} type The name of the event22   * @throws {TypeError} If the `type` argument is not specified23   */24  constructor(type) {25    this[kTarget] = null;26    this[kType] = type;27  }2829  /**30   * @type {*}31   */32  get target() {33    return this[kTarget];34  }3536  /**37   * @type {String}38   */39  get type() {40    return this[kType];41  }42}4344Object.defineProperty(Event.prototype, 'target', { enumerable: true });45Object.defineProperty(Event.prototype, 'type', { enumerable: true });4647/**48 * Class representing a close event.49 *50 * @extends Event51 */52class CloseEvent extends Event {53  /**54   * Create a new `CloseEvent`.55   *56   * @param {String} type The name of the event57   * @param {Object} [options] A dictionary object that allows for setting58   *     attributes via object members of the same name59   * @param {Number} [options.code=0] The status code explaining why the60   *     connection was closed61   * @param {String} [options.reason=''] A human-readable string explaining why62   *     the connection was closed63   * @param {Boolean} [options.wasClean=false] Indicates whether or not the64   *     connection was cleanly closed65   */66  constructor(type, options = {}) {67    super(type);6869    this[kCode] = options.code === undefined ? 0 : options.code;70    this[kReason] = options.reason === undefined ? '' : options.reason;71    this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;72  }7374  /**75   * @type {Number}76   */77  get code() {78    return this[kCode];79  }8081  /**82   * @type {String}83   */84  get reason() {85    return this[kReason];86  }8788  /**89   * @type {Boolean}90   */91  get wasClean() {92    return this[kWasClean];93  }94}9596Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });97Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });98Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });99100/**101 * Class representing an error event.102 *103 * @extends Event104 */105class ErrorEvent extends Event {106  /**107   * Create a new `ErrorEvent`.108   *109   * @param {String} type The name of the event110   * @param {Object} [options] A dictionary object that allows for setting111   *     attributes via object members of the same name112   * @param {*} [options.error=null] The error that generated this event113   * @param {String} [options.message=''] The error message114   */115  constructor(type, options = {}) {116    super(type);117118    this[kError] = options.error === undefined ? null : options.error;119    this[kMessage] = options.message === undefined ? '' : options.message;120  }121122  /**123   * @type {*}124   */125  get error() {126    return this[kError];127  }128129  /**130   * @type {String}131   */132  get message() {133    return this[kMessage];134  }135}136137Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });138Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });139140/**141 * Class representing a message event.142 *143 * @extends Event144 */145class MessageEvent extends Event {146  /**147   * Create a new `MessageEvent`.148   *149   * @param {String} type The name of the event150   * @param {Object} [options] A dictionary object that allows for setting151   *     attributes via object members of the same name152   * @param {*} [options.data=null] The message content153   */154  constructor(type, options = {}) {155    super(type);156157    this[kData] = options.data === undefined ? null : options.data;158  }159160  /**161   * @type {*}162   */163  get data() {164    return this[kData];165  }166}167168Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });169170/**171 * This provides methods for emulating the `EventTarget` interface. It's not172 * meant to be used directly.173 *174 * @mixin175 */176const EventTarget = {177  /**178   * Register an event listener.179   *180   * @param {String} type A string representing the event type to listen for181   * @param {(Function|Object)} handler The listener to add182   * @param {Object} [options] An options object specifies characteristics about183   *     the event listener184   * @param {Boolean} [options.once=false] A `Boolean` indicating that the185   *     listener should be invoked at most once after being added. If `true`,186   *     the listener would be automatically removed when invoked.187   * @public188   */189  addEventListener(type, handler, options = {}) {190    for (const listener of this.listeners(type)) {191      if (192        !options[kForOnEventAttribute] &&193        listener[kListener] === handler &&194        !listener[kForOnEventAttribute]195      ) {196        return;197      }198    }199200    let wrapper;201202    if (type === 'message') {203      wrapper = function onMessage(data, isBinary) {204        const event = new MessageEvent('message', {205          data: isBinary ? data : data.toString()206        });207208        event[kTarget] = this;209        callListener(handler, this, event);210      };211    } else if (type === 'close') {212      wrapper = function onClose(code, message) {213        const event = new CloseEvent('close', {214          code,215          reason: message.toString(),216          wasClean: this._closeFrameReceived && this._closeFrameSent217        });218219        event[kTarget] = this;220        callListener(handler, this, event);221      };222    } else if (type === 'error') {223      wrapper = function onError(error) {224        const event = new ErrorEvent('error', {225          error,226          message: error.message227        });228229        event[kTarget] = this;230        callListener(handler, this, event);231      };232    } else if (type === 'open') {233      wrapper = function onOpen() {234        const event = new Event('open');235236        event[kTarget] = this;237        callListener(handler, this, event);238      };239    } else {240      return;241    }242243    wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];244    wrapper[kListener] = handler;245246    if (options.once) {247      this.once(type, wrapper);248    } else {249      this.on(type, wrapper);250    }251  },252253  /**254   * Remove an event listener.255   *256   * @param {String} type A string representing the event type to remove257   * @param {(Function|Object)} handler The listener to remove258   * @public259   */260  removeEventListener(type, handler) {261    for (const listener of this.listeners(type)) {262      if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {263        this.removeListener(type, listener);264        break;265      }266    }267  }268};269270module.exports = {271  CloseEvent,272  ErrorEvent,273  Event,274  EventTarget,275  MessageEvent276};277278/**279 * Call an event listener280 *281 * @param {(Function|Object)} listener The listener to call282 * @param {*} thisArg The value to use as `this`` when calling the listener283 * @param {Event} event The event to pass to the listener284 * @private285 */286function callListener(listener, thisArg, event) {287  if (typeof listener === 'object' && listener.handleEvent) {288    listener.handleEvent.call(listener, event);289  } else {290    listener.call(thisArg, event);291  }292}293