All files / packages/core/src eventTarget.ts

66.66% Statements 32/48
50% Branches 10/20
63.63% Functions 7/11
65.21% Lines 30/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145                1x 1x       85x 85x         57x   5x     5x       57x       1690x 903x       1690x 116x     1574x                                                                                                                       2741x 1139x     1602x 1602x   1602x 1388x 1146x   1146x           2072x   1416x     656x 656x   656x 938x 938x           656x             1x      
/**
 * EventTarget - Provides the [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) interface
 */
class CornerstoneEventTarget implements EventTarget {
  private listeners;
  private debouncedListeners;
 
  constructor() {
    this.listeners = {};
    this.debouncedListeners = {};
  }
 
  public reset() {
    this.listeners = {};
    this.debouncedListeners = {};
  }
 
  public addEventListenerOnce(type, callback) {
    // Create a wrapper function to encapsulate the original callback
    const onceWrapper = (event) => {
      // Remove the listener after its first invocation
      this.removeEventListener(type, onceWrapper);
 
      // Call the original callback
      callback.call(this, event);
    };
 
    // Add the wrapper as the listener
    this.addEventListener(type, onceWrapper);
  }
 
  public addEventListener(type, callback) {
    if (!this.listeners[type]) {
      this.listeners[type] = [];
    }
 
    // prevent multiple callbacks from firing
    if (this.listeners[type].indexOf(callback) !== -1) {
      return;
    }
 
    this.listeners[type].push(callback);
  }
 
  /**
   * Adds a debounced event listener to the event target.
   *
   * @param type - The type of the event to listen for.
   * @param callback - The callback function to be executed when the event is triggered.
   * @param delay - The delay in milliseconds before the callback is invoked after the last event.
   */
  public addEventListenerDebounced(type, callback, delay) {
    // Ensure the dictionary for the type exists
    this.debouncedListeners[type] = this.debouncedListeners[type] || {};
    const debouncedCallbacks = this.debouncedListeners[type];
 
    // Check if there's already a debounced version of this callback registered
    if (!debouncedCallbacks[callback]) {
      const handle = (event) => {
        // Clear any existing timeout to reset the debounce timer
        if (debouncedCallbacks[callback]) {
          clearTimeout(debouncedCallbacks[callback].timeoutId);
        }
 
        // Set a new timeout
        debouncedCallbacks[callback].timeoutId = setTimeout(() => {
          callback.call(this, event);
        }, delay);
      };
 
      // Store the handle and initial timeoutId (null initially)
      debouncedCallbacks[callback] = {
        original: callback,
        handle,
        timeoutId: null,
      };
 
      // Register the debounced handler
      this.addEventListener(type, handle);
    }
  }
 
  /**
   * Removes a debounced event listener from the event target.
   *
   * @param type - The type of the event.
   * @param callback - The callback function to be removed.
   */
  public removeEventListenerDebounced(type, callback) {
    if (
      this.debouncedListeners[type] &&
      this.debouncedListeners[type][callback]
    ) {
      const debounced = this.debouncedListeners[type][callback];
      this.removeEventListener(type, debounced.handle);
      clearTimeout(debounced.timeoutId);
      delete this.debouncedListeners[type][callback];
    }
  }
 
  public removeEventListener(type, callback) {
    if (!this.listeners[type]) {
      return;
    }
 
    const stack = this.listeners[type];
    const stackLength = stack.length;
 
    for (let i = 0; i < stackLength; i++) {
      if (stack[i] === callback) {
        stack.splice(i, 1);
 
        return;
      }
    }
  }
 
  dispatchEvent(event) {
    if (!this.listeners[event.type]) {
      //console.warn(`Skipping dispatch since there are no listeners for ${event.type}`);
      return;
    }
 
    const stack = this.listeners[event.type].slice();
    const stackLength = stack.length;
 
    for (let i = 0; i < stackLength; i++) {
      try {
        stack[i].call(this, event);
      } catch (error) {
        console.error(`error in event listener of type:  ${event.type}`, error);
      }
    }
 
    return !event.defaultPrevented;
  }
}
 
/**
 * EventTarget - Provides the [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) interface
 */
const eventTarget = new CornerstoneEventTarget();
 
export default eventTarget;