text: 'use strict';
  
  /**
   * Class representing an event.
   *
   * @private
   */
  class Event {
    /**
     * Create a new `Event`.
     *
     * @param {String} type The name of the event
     * @param {Object} target A reference to the target to which the event was dispatched
     */
    constructor (type, target) {
      this.target = target;
      this.type = type;
    }
  }
  
  /**
   * Class representing a message event.
   *
   * @extends Event
   * @private
   */
  class MessageEvent extends Event {
    /**
     * Create a new `MessageEvent`.
     *
     * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data
     * @param {WebSocket} target A reference to the target to which the event was dispatched
     */
    constructor (data, target) {
      super('message', target);
  
      this.data = data;
    }
  }
  
  /**
   * Class representing a close event.
   *
   * @extends Event
   * @private
   */
  class CloseEvent extends Event {
    /**
     * Create a new `CloseEvent`.
     *
     * @param {Number} code The status code explaining why the connection is being closed
     * @param {String} reason A human-readable string explaining why the connection is closing
     * @param {WebSocket} target A reference to the target to which the event was dispatched
     */
    constructor (code, reason, target) {
      super('close', target);
  
      this.wasClean = target._closeFrameReceived && target._closeFrameSent;
      this.reason = reason;
      this.code = code;
    }
  }
  
  /**
   * Class representing an open event.
   *
   * @extends Event
   * @private
   */
  class OpenEvent extends Event {
    /**
     * Create a new `OpenEvent`.
     *
     * @param {WebSocket} target A reference to the target to which the event was dispatched
     */
    constructor (target) {
      super('open', target);
    }
  }
  
  /**
   * 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} method A string representing the event type to listen for
     * @param {Function} listener The listener to add
     * @public
     */
    addEventListener (method, listener) {
      if (typeof listener !== 'function') return;
  
      function onMessage (data) {
        listener.call(this, new MessageEvent(data, this));
      }
  
      function onClose (code, message) {
        listener.call(this, new CloseEvent(code, message, this));
      }
  
      function onError (event) {
        event.type = 'error';
        event.target = this;
        listener.call(this, event);
      }
  
      function onOpen () {
        listener.call(this, new OpenEvent(this));
      }
  
      if (method === 'message') {
        onMessage._listener = listener;
        this.on(method, onMessage);
      } else if (method === 'close') {
        onClose._listener = listener;
        this.on(method, onClose);
      } else if (method === 'error') {
        onError._listener = listener;
        this.on(method, onError);
      } else if (method === 'open') {
        onOpen._listener = listener;
        this.on(method, onOpen);
      } else {
        this.on(method, listener);
      }
    },
  
    /**
     * Remove an event listener.
     *
     * @param {String} method A string representing the event type to remove
     * @param {Function} listener The listener to remove
     * @public
     */
    removeEventListener (method, listener) {
      const listeners = this.listeners(method);
  
      for (var i = 0; i < listeners.length; i++) {
        if (listeners[i] === listener || listeners[i]._listener === listener) {
          this.removeListener(method, listeners[i]);
        }
      }
    }
  };
  
  module.exports = EventTarget;
  