# lib/goog/events/listenable.js

1 | // Copyright 2012 The Closure Library Authors. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS-IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /**
16 | * @fileoverview An interface for a listenable JavaScript object.
17 | * @author chrishenry@google.com (Chris Henry)
18 | */
19 |
20 | goog.provide('goog.events.Listenable');
21 | goog.provide('goog.events.ListenableKey');
22 |
23 | /** @suppress {extraRequire} */
24 | goog.require('goog.events.EventId');
25 |
26 |
27 |
28 | /**
29 | * A listenable interface. A listenable is an object with the ability
30 | * to dispatch/broadcast events to "event listeners" registered via
31 | * listen/listenOnce.
32 | *
33 | * The interface allows for an event propagation mechanism similar
34 | * to one offered by native browser event targets, such as
35 | * capture/bubble mechanism, stopping propagation, and preventing
36 | * default actions. Capture/bubble mechanism depends on the ancestor
37 | * tree constructed via {@code #getParentEventTarget}; this tree
38 | * must be directed acyclic graph. The meaning of default action(s)
39 | * in preventDefault is specific to a particular use case.
40 | *
41 | * Implementations that do not support capture/bubble or can not have
42 | * a parent listenable can simply not implement any ability to set the
43 | * parent listenable (and have {@code #getParentEventTarget} return
44 | * null).
45 | *
46 | * Implementation of this class can be used with or independently from
47 | * goog.events.
48 | *
49 | * Implementation must call {@code #addImplementation(implClass)}.
50 | *
51 | * @interface
52 | * @see goog.events
53 | * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html
54 | */
55 | goog.events.Listenable = function() {};
56 |
57 |
58 | /**
59 | * An expando property to indicate that an object implements
60 | * goog.events.Listenable.
61 | *
62 | * See addImplementation/isImplementedBy.
63 | *
64 | * @type {string}
65 | * @const
66 | */
67 | goog.events.Listenable.IMPLEMENTED_BY_PROP =
68 | 'closure_listenable_' + ((Math.random() * 1e6) | 0);
69 |
70 |
71 | /**
72 | * Marks a given class (constructor) as an implementation of
73 | * Listenable, do that we can query that fact at runtime. The class
74 | * must have already implemented the interface.
75 | * @param {!Function} cls The class constructor. The corresponding
76 | * class must have already implemented the interface.
77 | */
78 | goog.events.Listenable.addImplementation = function(cls) {
79 | cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;
80 | };
81 |
82 |
83 | /**
84 | * @param {Object} obj The object to check.
85 | * @return {boolean} Whether a given instance implements Listenable. The
86 | * class/superclass of the instance must call addImplementation.
87 | */
88 | goog.events.Listenable.isImplementedBy = function(obj) {
89 | return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);
90 | };
91 |
92 |
93 | /**
94 | * Adds an event listener. A listener can only be added once to an
95 | * object and if it is added again the key for the listener is
96 | * returned. Note that if the existing listener is a one-off listener
97 | * (registered via listenOnce), it will no longer be a one-off
98 | * listener after a call to listen().
99 | *
100 | * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
101 | * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
102 | * method.
103 | * @param {boolean=} opt_useCapture Whether to fire in capture phase
104 | * (defaults to false).
105 | * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
106 | * listener.
107 | * @return {goog.events.ListenableKey} Unique key for the listener.
108 | * @template SCOPE,EVENTOBJ
109 | */
110 | goog.events.Listenable.prototype.listen;
111 |
112 |
113 | /**
114 | * Adds an event listener that is removed automatically after the
115 | * listener fired once.
116 | *
117 | * If an existing listener already exists, listenOnce will do
118 | * nothing. In particular, if the listener was previously registered
119 | * via listen(), listenOnce() will not turn the listener into a
120 | * one-off listener. Similarly, if there is already an existing
121 | * one-off listener, listenOnce does not modify the listeners (it is
122 | * still a once listener).
123 | *
124 | * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
125 | * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
126 | * method.
127 | * @param {boolean=} opt_useCapture Whether to fire in capture phase
128 | * (defaults to false).
129 | * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
130 | * listener.
131 | * @return {goog.events.ListenableKey} Unique key for the listener.
132 | * @template SCOPE,EVENTOBJ
133 | */
134 | goog.events.Listenable.prototype.listenOnce;
135 |
136 |
137 | /**
138 | * Removes an event listener which was added with listen() or listenOnce().
139 | *
140 | * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
141 | * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
142 | * method.
143 | * @param {boolean=} opt_useCapture Whether to fire in capture phase
144 | * (defaults to false).
145 | * @param {SCOPE=} opt_listenerScope Object in whose scope to call
146 | * the listener.
147 | * @return {boolean} Whether any listener was removed.
148 | * @template SCOPE,EVENTOBJ
149 | */
150 | goog.events.Listenable.prototype.unlisten;
151 |
152 |
153 | /**
154 | * Removes an event listener which was added with listen() by the key
155 | * returned by listen().
156 | *
157 | * @param {goog.events.ListenableKey} key The key returned by
158 | * listen() or listenOnce().
159 | * @return {boolean} Whether any listener was removed.
160 | */
161 | goog.events.Listenable.prototype.unlistenByKey;
162 |
163 |
164 | /**
165 | * Dispatches an event (or event like object) and calls all listeners
166 | * listening for events of this type. The type of the event is decided by the
167 | * type property on the event object.
168 | *
169 | * If any of the listeners returns false OR calls preventDefault then this
170 | * function will return false. If one of the capture listeners calls
171 | * stopPropagation, then the bubble listeners won't fire.
172 | *
173 | * @param {goog.events.EventLike} e Event object.
174 | * @return {boolean} If anyone called preventDefault on the event object (or
175 | * if any of the listeners returns false) this will also return false.
176 | */
177 | goog.events.Listenable.prototype.dispatchEvent;
178 |
179 |
180 | /**
181 | * Removes all listeners from this listenable. If type is specified,
182 | * it will only remove listeners of the particular type. otherwise all
183 | * registered listeners will be removed.
184 | *
185 | * @param {string=} opt_type Type of event to remove, default is to
186 | * remove all types.
187 | * @return {number} Number of listeners removed.
188 | */
189 | goog.events.Listenable.prototype.removeAllListeners;
190 |
191 |
192 | /**
193 | * Returns the parent of this event target to use for capture/bubble
194 | * mechanism.
195 | *
196 | * NOTE(chrishenry): The name reflects the original implementation of
197 | * custom event target ({@code goog.events.EventTarget}). We decided
198 | * that changing the name is not worth it.
199 | *
200 | * @return {goog.events.Listenable} The parent EventTarget or null if
201 | * there is no parent.
202 | */
203 | goog.events.Listenable.prototype.getParentEventTarget;
204 |
205 |
206 | /**
207 | * Fires all registered listeners in this listenable for the given
208 | * type and capture mode, passing them the given eventObject. This
209 | * does not perform actual capture/bubble. Only implementors of the
210 | * interface should be using this.
211 | *
212 | * @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the
213 | * listeners to fire.
214 | * @param {boolean} capture The capture mode of the listeners to fire.
215 | * @param {EVENTOBJ} eventObject The event object to fire.
216 | * @return {boolean} Whether all listeners succeeded without
217 | * attempting to prevent default behavior. If any listener returns
218 | * false or called goog.events.Event#preventDefault, this returns
219 | * false.
220 | * @template EVENTOBJ
221 | */
222 | goog.events.Listenable.prototype.fireListeners;
223 |
224 |
225 | /**
226 | * Gets all listeners in this listenable for the given type and
227 | * capture mode.
228 | *
229 | * @param {string|!goog.events.EventId} type The type of the listeners to fire.
230 | * @param {boolean} capture The capture mode of the listeners to fire.
231 | * @return {!Array<goog.events.ListenableKey>} An array of registered
232 | * listeners.
233 | * @template EVENTOBJ
234 | */
235 | goog.events.Listenable.prototype.getListeners;
236 |
237 |
238 | /**
239 | * Gets the goog.events.ListenableKey for the event or null if no such
240 | * listener is in use.
241 | *
242 | * @param {string|!goog.events.EventId<EVENTOBJ>} type The name of the event
243 | * without the 'on' prefix.
244 | * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The
245 | * listener function to get.
246 | * @param {boolean} capture Whether the listener is a capturing listener.
247 | * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
248 | * listener.
249 | * @return {goog.events.ListenableKey} the found listener or null if not found.
250 | * @template SCOPE,EVENTOBJ
251 | */
252 | goog.events.Listenable.prototype.getListener;
253 |
254 |
255 | /**
256 | * Whether there is any active listeners matching the specified
257 | * signature. If either the type or capture parameters are
258 | * unspecified, the function will match on the remaining criteria.
259 | *
260 | * @param {string|!goog.events.EventId<EVENTOBJ>=} opt_type Event type.
261 | * @param {boolean=} opt_capture Whether to check for capture or bubble
262 | * listeners.
263 | * @return {boolean} Whether there is any active listeners matching
264 | * the requested type and/or capture phase.
265 | * @template EVENTOBJ
266 | */
267 | goog.events.Listenable.prototype.hasListener;
268 |
269 |
270 |
271 | /**
272 | * An interface that describes a single registered listener.
273 | * @interface
274 | */
275 | goog.events.ListenableKey = function() {};
276 |
277 |
278 | /**
279 | * Counter used to create a unique key
280 | * @type {number}
281 | * @private
282 | */
283 | goog.events.ListenableKey.counter_ = 0;
284 |
285 |
286 | /**
287 | * Reserves a key to be used for ListenableKey#key field.
288 | * @return {number} A number to be used to fill ListenableKey#key
289 | * field.
290 | */
291 | goog.events.ListenableKey.reserveKey = function() {
292 | return ++goog.events.ListenableKey.counter_;
293 | };
294 |
295 |
296 | /**
297 | * The source event target.
298 | * @type {!(Object|goog.events.Listenable|goog.events.EventTarget)}
299 | */
300 | goog.events.ListenableKey.prototype.src;
301 |
302 |
303 | /**
304 | * The event type the listener is listening to.
305 | * @type {string}
306 | */
307 | goog.events.ListenableKey.prototype.type;
308 |
309 |
310 | /**
311 | * The listener function.
312 | * @type {function(?):?|{handleEvent:function(?):?}|null}
313 | */
314 | goog.events.ListenableKey.prototype.listener;
315 |
316 |
317 | /**
318 | * Whether the listener works on capture phase.
319 | * @type {boolean}
320 | */
321 | goog.events.ListenableKey.prototype.capture;
322 |
323 |
324 | /**
325 | * The 'this' object for the listener function's scope.
326 | * @type {Object}
327 | */
328 | goog.events.ListenableKey.prototype.handler;
329 |
330 |
331 | /**
332 | * A globally unique number to identify the key.
333 | * @type {number}
334 | */
335 | goog.events.ListenableKey.prototype.key;