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       1692x 897x       1692x 117x     1575x                                                                                                                       2745x 1129x     1616x 1616x   1616x 1395x 1147x   1147x           2079x   1435x     644x 644x   644x 941x 941x           644x             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;