Source: modules/Communicator.js

/** @module modules/Communicator */

/**
 * Manages sending and receiving events to and from the message proxy. Delegates document click
 * event handling to methods. Event delegatee methods should be defined using arrow function syntax
 * to ensure that `this` will not be affected by execution context.
  */
export class Communicator {
  /**
   * The key is a DOM element `id` attribute. The value is an instance of an `Element` for that DOM
   * element.
   * @type {Object.<string, Element>}
   */
  domElements = {};
  /**
   * The name of the class for use in representing the object as a string.
   * @summary `static`
   * @type {string}
   * @variation 0
   */
  static name;
  /**
   * The name of the class for use in representing an instance of the object as a string.
   * @summary `instance`
   * @type {string}
   * @variation 1
   */
  name;
  /**
   * Various class properties necessary for the behavior of reactive components.
   * @type {Object}
   */
  state = {};

  /**
   * Sets the class message proxy, then adds a document click event listener that checks for a
   * callable property of `self` matching the delegatee method naming format. If found, the method
   * is called and provided with the event.
   * @param {Proxy} messageProxy - The application message proxy.
   */
  constructor(messageProxy) {
    this.messageProxy = messageProxy;

    document.addEventListener("click", (event) => {
      const targetId = event.target.getAttribute("id");

      if (this.domElements.hasOwnProperty(targetId)) {
        event.stopPropagation();
        const element = this.domElements[targetId];
        const eventMethodName = this.constructEventMethodName(element);

        if (this.hasOwnProperty(eventMethodName)) {
          if (typeof(this[eventMethodName]) === "function") {
            this[eventMethodName](event);
          }
        }
      }
    });
  }

  /**
   * Prints the class name when the object is presented as a string.
   * @variation 0
   */
  static toString() {
    return this.name;
  }

  /**
   * Prints the class name when an instance of the object is presented as a string.
   * @variation 1
   */
  toString() {
    return this.name;
  }

  /**
   * Sets the state for the provided object and property to the provided value when the message
   * proxy send setter is trapped, then, if an appropriately named method exists, calls that method.
   * @param {Object} object - The sender.
   * @param {string} property - The name of the property of the seconder sender which to store the
   * state.
   * @param {number|string|boolean|Object} value - The value to set for the provided property name.
   */
  receive(object, property, value) {
    // Create a key and object for the sender if it does not exist
    if (!this.state.hasOwnProperty(object.toString())) {
      this.state[object] = {};
    }

    // Set the property state and construct the receive method name
    this.state[object][property] = value;
    const receiveMethodName = this.constructReceiveMethodName(property);

    // Call the receive method if it exists
    if (this.hasOwnProperty(receiveMethodName)) {
      this[receiveMethodName](object, property, value);
    }
  }

  /**
   * Formats a method name from the provided property name.
   * @param {string} property - The name of the property
   * @returns {string} A formatted string representing a method name.
   */
  constructReceiveMethodName(property) {
    return property + "Receive";
  }

  /**
   * Invokes the message proxy send setter.
   * @param {string} property - The name of the property.
   * @param {number|string|boolean|Object} value - The value to set for the provided property name.
   */
  send(property, value) {
    this.messageProxy.send = [this, property, value];
  }

  /**
   * Formats a method name from the provided DOM element.
   * @param {Element} element - An DOM element instance.
   * @returns {string} A formatted string represented a method name.
   */
  constructEventMethodName(element) {
    return element.getAttribute("id") + "ElementClick";
  }
}