• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 14804036802

02 May 2025 09:58PM UTC coverage: 5.927% (-83.4%) from 89.28%
14804036802

Pull #3404

github

web-flow
Merge 5c103d7f8 into 0f2ccaeb2
Pull Request #3404: feat: added Graph module to Math

234 of 8383 branches covered (2.79%)

229 of 246 new or added lines in 1 file covered. (93.09%)

13145 existing lines in 208 files now uncovered.

934 of 15759 relevant lines covered (5.93%)

4.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.43
/src/engine/EventEmitter.ts
1
export type EventMap = Record<string, any>;
2
export type EventKey<T extends EventMap> = string & keyof T;
3
export type Handler<EventType> = (event: EventType) => void;
4

5
/**
6
 * Interface that represents a handle to a subscription that can be closed
7
 */
8
export interface Subscription {
9
  /**
10
   * Removes the associated event handler, synonymous with events.off(...);
11
   */
12
  close(): void;
13
}
14

15
/**
16
 * Excalibur's typed event emitter, this allows events to be sent with any string to Type mapping
17
 */
18
export class EventEmitter<TEventMap extends EventMap = any> {
19
  private _paused = false;
1✔
20
  private _empty = true;
1✔
21
  private _listeners: Record<string, Handler<any>[]> = {};
1✔
22
  private _listenersOnce: Record<string, Handler<any>[]> = {};
1✔
23
  private _pipes: EventEmitter<any>[] = [];
1✔
24

25
  /**
26
   * Removes all listeners and pipes
27
   */
28
  clear() {
UNCOV
29
    this._listeners = {};
×
UNCOV
30
    this._listenersOnce = {};
×
UNCOV
31
    this._pipes.length = 0;
×
UNCOV
32
    this._empty = true;
×
33
  }
34

35
  on<TEventName extends EventKey<TEventMap>>(eventName: TEventName, handler: Handler<TEventMap[TEventName]>): Subscription;
36
  on(eventName: string, handler: Handler<unknown>): Subscription;
37
  on<TEventName extends EventKey<TEventMap> | string>(eventName: TEventName, handler: Handler<TEventMap[TEventName]>): Subscription {
UNCOV
38
    this._empty = false;
×
UNCOV
39
    this._listeners[eventName] = this._listeners[eventName] ?? [];
×
UNCOV
40
    this._listeners[eventName].push(handler);
×
UNCOV
41
    return {
×
UNCOV
42
      close: () => this.off(eventName, handler)
×
43
    };
44
  }
45

46
  once<TEventName extends EventKey<TEventMap>>(eventName: TEventName, handler: Handler<TEventMap[TEventName]>): Subscription;
47
  once(eventName: string, handler: Handler<unknown>): Subscription;
48
  once<TEventName extends EventKey<TEventMap> | string>(eventName: TEventName, handler: Handler<TEventMap[TEventName]>): Subscription {
UNCOV
49
    this._empty = false;
×
UNCOV
50
    this._listenersOnce[eventName] = this._listenersOnce[eventName] ?? [];
×
UNCOV
51
    this._listenersOnce[eventName].push(handler);
×
UNCOV
52
    return {
×
53
      close: () => this.off(eventName, handler)
×
54
    };
55
  }
56

57
  off<TEventName extends EventKey<TEventMap>>(eventName: TEventName, handler: Handler<TEventMap[TEventName]>): void;
58
  off(eventName: string, handler: Handler<unknown>): void;
59
  off(eventName: string): void;
60
  off<TEventName extends EventKey<TEventMap> | string>(eventName: TEventName, handler?: Handler<TEventMap[TEventName]>): void {
UNCOV
61
    if (handler) {
×
UNCOV
62
      const newListeners = this._listeners[eventName]?.filter((h) => h !== handler);
×
UNCOV
63
      this._listeners[eventName] = newListeners;
×
64

UNCOV
65
      const newOnceListeners = this._listenersOnce[eventName]?.filter((h) => h !== handler);
×
UNCOV
66
      this._listenersOnce[eventName] = newOnceListeners;
×
67
    } else {
UNCOV
68
      delete this._listeners[eventName];
×
69
    }
70
  }
71

72
  emit<TEventName extends EventKey<TEventMap>>(eventName: TEventName, event: TEventMap[TEventName]): void;
73
  emit(eventName: string, event?: any): void;
74
  emit<TEventName extends EventKey<TEventMap> | string>(eventName: TEventName, event?: TEventMap[TEventName]): void {
UNCOV
75
    if (this._empty) {
×
UNCOV
76
      return;
×
77
    }
UNCOV
78
    if (this._paused) {
×
79
      return;
×
80
    }
UNCOV
81
    const listeners = this._listeners[eventName];
×
UNCOV
82
    if (listeners) {
×
UNCOV
83
      for (let i = 0; i < listeners.length; i++) {
×
UNCOV
84
        listeners[i](event);
×
85
      }
86
    }
UNCOV
87
    const onces = this._listenersOnce[eventName];
×
UNCOV
88
    this._listenersOnce[eventName] = [];
×
UNCOV
89
    if (onces) {
×
UNCOV
90
      for (let i = 0; i < onces.length; i++) {
×
UNCOV
91
        onces[i](event);
×
92
      }
93
    }
UNCOV
94
    for (let i = 0; i < this._pipes.length; i++) {
×
UNCOV
95
      this._pipes[i].emit(eventName, event);
×
96
    }
97
  }
98

99
  /**
100
   * Replay events from this emitter to another
101
   * @param emitter
102
   */
103
  pipe(emitter: EventEmitter<any>): Subscription {
UNCOV
104
    if (this === emitter) {
×
UNCOV
105
      throw Error('Cannot pipe to self');
×
106
    }
UNCOV
107
    this._empty = false;
×
UNCOV
108
    this._pipes.push(emitter);
×
UNCOV
109
    return {
×
110
      close: () => {
UNCOV
111
        const i = this._pipes.indexOf(emitter);
×
UNCOV
112
        if (i > -1) {
×
UNCOV
113
          this._pipes.splice(i, 1);
×
114
        }
115
      }
116
    };
117
  }
118

119
  /**
120
   * Remove any piped emitters
121
   * @param emitter
122
   */
123
  unpipe(emitter: EventEmitter<any>): void {
UNCOV
124
    const i = this._pipes.indexOf(emitter);
×
UNCOV
125
    if (i > -1) {
×
UNCOV
126
      this._pipes.splice(i, 1);
×
127
    }
128
  }
129

130
  /**
131
   * Paused event emitters do not emit events
132
   */
133
  pause(): void {
134
    this._paused = true;
×
135
  }
136

137
  /**
138
   * Unpaused event emitter do emit events
139
   */
140
  unpause(): void {
141
    this._paused = false;
×
142
  }
143
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc