/
fake_event_target.js
166 lines (147 loc) · 4.59 KB
/
fake_event_target.js
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
goog.provide('shaka.util.FakeEventTarget');
goog.require('goog.asserts');
goog.require('shaka.log');
goog.require('shaka.util.FakeEvent');
goog.require('shaka.util.IReleasable');
goog.require('shaka.util.MultiMap');
/**
* @summary A work-alike for EventTarget. Only DOM elements may be true
* EventTargets, but this can be used as a base class to provide event dispatch
* to non-DOM classes. Only FakeEvents should be dispatched.
*
* @implements {EventTarget}
* @implements {shaka.util.IReleasable}
* @exportInterface
*/
shaka.util.FakeEventTarget = class {
/** */
constructor() {
/**
* @private {shaka.util.MultiMap.<shaka.util.FakeEventTarget.ListenerType>}
*/
this.listeners_ = new shaka.util.MultiMap();
/**
* The target of all dispatched events. Defaults to |this|.
* @type {EventTarget}
*/
this.dispatchTarget = this;
}
/**
* Add an event listener to this object.
*
* @param {string} type The event type to listen for.
* @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
* listener object to invoke.
* @param {(!AddEventListenerOptions|boolean)=} options Ignored.
* @override
* @exportInterface
*/
addEventListener(type, listener, options) {
if (!this.listeners_) {
return;
}
this.listeners_.push(type, listener);
}
/**
* Add an event listener to this object that is invoked for all events types
* the object fires.
*
* @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
* listener object to invoke.
* @exportInterface
*/
listenToAllEvents(listener) {
this.addEventListener(shaka.util.FakeEventTarget.ALL_EVENTS_, listener);
}
/**
* Remove an event listener from this object.
*
* @param {string} type The event type for which you wish to remove a
* listener.
* @param {shaka.util.FakeEventTarget.ListenerType} listener The callback or
* listener object to remove.
* @param {(EventListenerOptions|boolean)=} options Ignored.
* @override
* @exportInterface
*/
removeEventListener(type, listener, options) {
if (!this.listeners_) {
return;
}
this.listeners_.remove(type, listener);
}
/**
* Dispatch an event from this object.
*
* @param {!Event} event The event to be dispatched from this object.
* @return {boolean} True if the default action was prevented.
* @override
* @exportInterface
*/
dispatchEvent(event) {
// In many browsers, it is complex to overwrite properties of actual Events.
// Here we expect only to dispatch FakeEvents, which are simpler.
goog.asserts.assert(event instanceof shaka.util.FakeEvent,
'FakeEventTarget can only dispatch FakeEvents!');
if (!this.listeners_) {
return true;
}
let listeners = this.listeners_.get(event.type) || [];
const universalListeners =
this.listeners_.get(shaka.util.FakeEventTarget.ALL_EVENTS_);
if (universalListeners) {
listeners = listeners.concat(universalListeners);
}
// Execute this event on listeners until the event has been stopped or we
// run out of listeners.
for (const listener of listeners) {
// Do this every time, since events can be re-dispatched from handlers.
event.target = this.dispatchTarget;
event.currentTarget = this.dispatchTarget;
try {
// Check for the |handleEvent| member to test if this is a
// |EventListener| instance or a basic function.
if (listener.handleEvent) {
listener.handleEvent(event);
} else {
// eslint-disable-next-line no-restricted-syntax
listener.call(this, event);
}
} catch (exception) {
// Exceptions during event handlers should not affect the caller,
// but should appear on the console as uncaught, according to MDN:
// https://mzl.la/2JXgwRo
shaka.log.error('Uncaught exception in event handler', exception,
exception ? exception.message : null,
exception ? exception.stack : null);
}
if (event.stopped) {
break;
}
}
return event.defaultPrevented;
}
/**
* @override
* @exportInterface
*/
release() {
this.listeners_ = null;
}
};
/**
* These are the listener types defined in the closure extern for EventTarget.
* @typedef {EventListener|function(!Event):*}
* @exportInterface
*/
shaka.util.FakeEventTarget.ListenerType;
/**
* @const {string}
* @private
*/
shaka.util.FakeEventTarget.ALL_EVENTS_ = 'All';