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