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
20goog.provide('goog.events.Listenable');
21goog.provide('goog.events.ListenableKey');
22
23/** @suppress {extraRequire} */
24goog.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 */
55goog.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 */
67goog.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 */
78goog.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 */
88goog.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 */
110goog.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 */
134goog.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 */
150goog.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 */
161goog.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 */
177goog.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 */
189goog.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 */
203goog.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 */
222goog.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 */
235goog.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 */
252goog.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 */
267goog.events.Listenable.prototype.hasListener;
268
269
270
271/**
272 * An interface that describes a single registered listener.
273 * @interface
274 */
275goog.events.ListenableKey = function() {};
276
277
278/**
279 * Counter used to create a unique key
280 * @type {number}
281 * @private
282 */
283goog.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 */
291goog.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 */
300goog.events.ListenableKey.prototype.src;
301
302
303/**
304 * The event type the listener is listening to.
305 * @type {string}
306 */
307goog.events.ListenableKey.prototype.type;
308
309
310/**
311 * The listener function.
312 * @type {function(?):?|{handleEvent:function(?):?}|null}
313 */
314goog.events.ListenableKey.prototype.listener;
315
316
317/**
318 * Whether the listener works on capture phase.
319 * @type {boolean}
320 */
321goog.events.ListenableKey.prototype.capture;
322
323
324/**
325 * The 'this' object for the listener function's scope.
326 * @type {Object}
327 */
328goog.events.ListenableKey.prototype.handler;
329
330
331/**
332 * A globally unique number to identify the key.
333 * @type {number}
334 */
335goog.events.ListenableKey.prototype.key;