1 | // Licensed to the Software Freedom Conservancy (SFC) under one
|
2 | // or more contributor license agreements. See the NOTICE file
|
3 | // distributed with this work for additional information
|
4 | // regarding copyright ownership. The SFC licenses this file
|
5 | // to you under the Apache License, Version 2.0 (the
|
6 | // "License"); you may not use this file except in compliance
|
7 | // with the License. You may obtain a copy of the License at
|
8 | //
|
9 | // http://www.apache.org/licenses/LICENSE-2.0
|
10 | //
|
11 | // Unless required by applicable law or agreed to in writing,
|
12 | // software distributed under the License is distributed on an
|
13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14 | // KIND, either express or implied. See the License for the
|
15 | // specific language governing permissions and limitations
|
16 | // under the License.
|
17 |
|
18 | ;
|
19 |
|
20 | /**
|
21 | * Describes an event listener registered on an {@linkplain EventEmitter}.
|
22 | */
|
23 | class Listener {
|
24 | /**
|
25 | * @param {!Function} fn The acutal listener function.
|
26 | * @param {(Object|undefined)} scope The object in whose scope to invoke the
|
27 | * listener.
|
28 | * @param {boolean} oneshot Whether this listener should only be used once.
|
29 | */
|
30 | constructor(fn, scope, oneshot) {
|
31 | this.fn = fn;
|
32 | this.scope = scope;
|
33 | this.oneshot = oneshot;
|
34 | }
|
35 | }
|
36 |
|
37 |
|
38 | /** @type {!WeakMap<!EventEmitter, !Map<string, !Set<!Listener>>>} */
|
39 | const EVENTS = new WeakMap;
|
40 |
|
41 |
|
42 | /**
|
43 | * Object that can emit events for others to listen for.
|
44 | */
|
45 | class EventEmitter {
|
46 | /**
|
47 | * Fires an event and calls all listeners.
|
48 | * @param {string} type The type of event to emit.
|
49 | * @param {...*} var_args Any arguments to pass to each listener.
|
50 | */
|
51 | emit(type, var_args) {
|
52 | let events = EVENTS.get(this);
|
53 | if (!events) {
|
54 | return;
|
55 | }
|
56 |
|
57 | let args = Array.prototype.slice.call(arguments, 1);
|
58 |
|
59 | let listeners = events.get(type);
|
60 | if (listeners) {
|
61 | for (let listener of listeners) {
|
62 | listener.fn.apply(listener.scope, args);
|
63 | if (listener.oneshot) {
|
64 | listeners.delete(listener);
|
65 | }
|
66 | }
|
67 | }
|
68 | }
|
69 |
|
70 | /**
|
71 | * Returns a mutable list of listeners for a specific type of event.
|
72 | * @param {string} type The type of event to retrieve the listeners for.
|
73 | * @return {!Set<!Listener>} The registered listeners for the given event
|
74 | * type.
|
75 | */
|
76 | listeners(type) {
|
77 | let events = EVENTS.get(this);
|
78 | if (!events) {
|
79 | events = new Map;
|
80 | EVENTS.set(this, events);
|
81 | }
|
82 |
|
83 | let listeners = events.get(type);
|
84 | if (!listeners) {
|
85 | listeners = new Set;
|
86 | events.set(type, listeners);
|
87 | }
|
88 | return listeners;
|
89 | }
|
90 |
|
91 | /**
|
92 | * Registers a listener.
|
93 | * @param {string} type The type of event to listen for.
|
94 | * @param {!Function} fn The function to invoke when the event is fired.
|
95 | * @param {Object=} opt_self The object in whose scope to invoke the listener.
|
96 | * @param {boolean=} opt_oneshot Whether the listener should b (e removed after
|
97 | * the first event is fired.
|
98 | * @return {!EventEmitter} A self reference.
|
99 | * @private
|
100 | */
|
101 | addListener_(type, fn, opt_self, opt_oneshot) {
|
102 | let listeners = this.listeners(type);
|
103 | for (let listener of listeners) {
|
104 | if (listener.fn === fn) {
|
105 | return this;
|
106 | }
|
107 | }
|
108 | listeners.add(new Listener(fn, opt_self || undefined, !!opt_oneshot));
|
109 | return this;
|
110 | }
|
111 |
|
112 | /**
|
113 | * Registers a listener.
|
114 | * @param {string} type The type of event to listen for.
|
115 | * @param {!Function} fn The function to invoke when the event is fired.
|
116 | * @param {Object=} opt_self The object in whose scope to invoke the listener.
|
117 | * @return {!EventEmitter} A self reference.
|
118 | */
|
119 | addListener(type, fn, opt_self) {
|
120 | return this.addListener_(type, fn, opt_self, false);
|
121 | }
|
122 |
|
123 | /**
|
124 | * Registers a one-time listener which will be called only the first time an
|
125 | * event is emitted, after which it will be removed.
|
126 | * @param {string} type The type of event to listen for.
|
127 | * @param {!Function} fn The function to invoke when the event is fired.
|
128 | * @param {Object=} opt_self The object in whose scope to invoke the listener.
|
129 | * @return {!EventEmitter} A self reference.
|
130 | */
|
131 | once(type, fn, opt_self) {
|
132 | return this.addListener_(type, fn, opt_self, true);
|
133 | }
|
134 |
|
135 | /**
|
136 | * An alias for {@link #addListener() addListener()}.
|
137 | * @param {string} type The type of event to listen for.
|
138 | * @param {!Function} fn The function to invoke when the event is fired.
|
139 | * @param {Object=} opt_self The object in whose scope to invoke the listener.
|
140 | * @return {!EventEmitter} A self reference.
|
141 | */
|
142 | on(type, fn, opt_self) {
|
143 | return this.addListener(type, fn, opt_self);
|
144 | }
|
145 |
|
146 | /**
|
147 | * Removes a previously registered event listener.
|
148 | * @param {string} type The type of event to unregister.
|
149 | * @param {!Function} listenerFn The handler function to remove.
|
150 | * @return {!EventEmitter} A self reference.
|
151 | */
|
152 | removeListener(type, listenerFn) {
|
153 | if (typeof type !== 'string' || typeof listenerFn !== 'function') {
|
154 | throw TypeError('invalid args: expected (string, function), got ('
|
155 | + (typeof type) + ', ' + (typeof listenerFn) + ')');
|
156 | }
|
157 |
|
158 | let events = EVENTS.get(this);
|
159 | if (!events) {
|
160 | return this;
|
161 | }
|
162 |
|
163 | let listeners = events.get(type);
|
164 | if (!listeners) {
|
165 | return this;
|
166 | }
|
167 |
|
168 | let match;
|
169 | for (let listener of listeners) {
|
170 | if (listener.fn === listenerFn) {
|
171 | match = listener;
|
172 | break;
|
173 | }
|
174 | }
|
175 | if (match) {
|
176 | listeners.delete(match);
|
177 | if (!listeners.size) {
|
178 | events.delete(type);
|
179 | }
|
180 | }
|
181 | return this;
|
182 | }
|
183 |
|
184 | /**
|
185 | * Removes all listeners for a specific type of event. If no event is
|
186 | * specified, all listeners across all types will be removed.
|
187 | * @param {string=} opt_type The type of event to remove listeners from.
|
188 | * @return {!EventEmitter} A self reference.
|
189 | */
|
190 | removeAllListeners(opt_type) {
|
191 | let events = EVENTS.get(this);
|
192 | if (events) {
|
193 | if (typeof opt_type === 'string') {
|
194 | events.delete(opt_type);
|
195 | } else {
|
196 | EVENTS.delete(this);
|
197 | }
|
198 | }
|
199 | return this;
|
200 | }
|
201 | }
|
202 |
|
203 |
|
204 | // PUBLIC API
|
205 |
|
206 |
|
207 | module.exports = {
|
208 | EventEmitter: EventEmitter,
|
209 | Listener: Listener
|
210 | };
|