Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / third_party / google_input_tools / third_party / closure_library / closure / goog / events / listenermap.js
1 // Copyright 2013 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 A map of listeners that provides utility functions to
17  * deal with listeners on an event target. Used by
18  * {@code goog.events.EventTarget}.
19  *
20  * WARNING: Do not use this class from outside goog.events package.
21  *
22  * @visibility {//closure/goog/bin/sizetests:__pkg__}
23  * @visibility {//closure/goog/events:__pkg__}
24  * @visibility {//closure/goog/labs/events:__pkg__}
25  */
26
27 goog.provide('goog.events.ListenerMap');
28
29 goog.require('goog.array');
30 goog.require('goog.events.Listener');
31 goog.require('goog.object');
32
33
34
35 /**
36  * Creates a new listener map.
37  * @param {EventTarget|goog.events.Listenable} src The src object.
38  * @constructor
39  * @final
40  */
41 goog.events.ListenerMap = function(src) {
42   /** @type {EventTarget|goog.events.Listenable} */
43   this.src = src;
44
45   /**
46    * Maps of event type to an array of listeners.
47    * @type {Object.<string, !Array.<!goog.events.Listener>>}
48    */
49   this.listeners = {};
50
51   /**
52    * The count of types in this map that have registered listeners.
53    * @private {number}
54    */
55   this.typeCount_ = 0;
56 };
57
58
59 /**
60  * @return {number} The count of event types in this map that actually
61  *     have registered listeners.
62  */
63 goog.events.ListenerMap.prototype.getTypeCount = function() {
64   return this.typeCount_;
65 };
66
67
68 /**
69  * @return {number} Total number of registered listeners.
70  */
71 goog.events.ListenerMap.prototype.getListenerCount = function() {
72   var count = 0;
73   for (var type in this.listeners) {
74     count += this.listeners[type].length;
75   }
76   return count;
77 };
78
79
80 /**
81  * Adds an event listener. A listener can only be added once to an
82  * object and if it is added again the key for the listener is
83  * returned.
84  *
85  * Note that a one-off listener will not change an existing listener,
86  * if any. On the other hand a normal listener will change existing
87  * one-off listener to become a normal listener.
88  *
89  * @param {string|!goog.events.EventId} type The listener event type.
90  * @param {!Function} listener This listener callback method.
91  * @param {boolean} callOnce Whether the listener is a one-off
92  *     listener.
93  * @param {boolean=} opt_useCapture The capture mode of the listener.
94  * @param {Object=} opt_listenerScope Object in whose scope to call the
95  *     listener.
96  * @return {goog.events.ListenableKey} Unique key for the listener.
97  */
98 goog.events.ListenerMap.prototype.add = function(
99     type, listener, callOnce, opt_useCapture, opt_listenerScope) {
100   var typeStr = type.toString();
101   var listenerArray = this.listeners[typeStr];
102   if (!listenerArray) {
103     listenerArray = this.listeners[typeStr] = [];
104     this.typeCount_++;
105   }
106
107   var listenerObj;
108   var index = goog.events.ListenerMap.findListenerIndex_(
109       listenerArray, listener, opt_useCapture, opt_listenerScope);
110   if (index > -1) {
111     listenerObj = listenerArray[index];
112     if (!callOnce) {
113       // Ensure that, if there is an existing callOnce listener, it is no
114       // longer a callOnce listener.
115       listenerObj.callOnce = false;
116     }
117   } else {
118     listenerObj = new goog.events.Listener(
119         listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope);
120     listenerObj.callOnce = callOnce;
121     listenerArray.push(listenerObj);
122   }
123   return listenerObj;
124 };
125
126
127 /**
128  * Removes a matching listener.
129  * @param {string|!goog.events.EventId} type The listener event type.
130  * @param {!Function} listener This listener callback method.
131  * @param {boolean=} opt_useCapture The capture mode of the listener.
132  * @param {Object=} opt_listenerScope Object in whose scope to call the
133  *     listener.
134  * @return {boolean} Whether any listener was removed.
135  */
136 goog.events.ListenerMap.prototype.remove = function(
137     type, listener, opt_useCapture, opt_listenerScope) {
138   var typeStr = type.toString();
139   if (!(typeStr in this.listeners)) {
140     return false;
141   }
142
143   var listenerArray = this.listeners[typeStr];
144   var index = goog.events.ListenerMap.findListenerIndex_(
145       listenerArray, listener, opt_useCapture, opt_listenerScope);
146   if (index > -1) {
147     var listenerObj = listenerArray[index];
148     listenerObj.markAsRemoved();
149     goog.array.removeAt(listenerArray, index);
150     if (listenerArray.length == 0) {
151       delete this.listeners[typeStr];
152       this.typeCount_--;
153     }
154     return true;
155   }
156   return false;
157 };
158
159
160 /**
161  * Removes the given listener object.
162  * @param {goog.events.ListenableKey} listener The listener to remove.
163  * @return {boolean} Whether the listener is removed.
164  */
165 goog.events.ListenerMap.prototype.removeByKey = function(listener) {
166   var type = listener.type;
167   if (!(type in this.listeners)) {
168     return false;
169   }
170
171   var removed = goog.array.remove(this.listeners[type], listener);
172   if (removed) {
173     listener.markAsRemoved();
174     if (this.listeners[type].length == 0) {
175       delete this.listeners[type];
176       this.typeCount_--;
177     }
178   }
179   return removed;
180 };
181
182
183 /**
184  * Removes all listeners from this map. If opt_type is provided, only
185  * listeners that match the given type are removed.
186  * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
187  * @return {number} Number of listeners removed.
188  */
189 goog.events.ListenerMap.prototype.removeAll = function(opt_type) {
190   var typeStr = opt_type && opt_type.toString();
191   var count = 0;
192   for (var type in this.listeners) {
193     if (!typeStr || type == typeStr) {
194       var listenerArray = this.listeners[type];
195       for (var i = 0; i < listenerArray.length; i++) {
196         ++count;
197         listenerArray[i].markAsRemoved();
198       }
199       delete this.listeners[type];
200       this.typeCount_--;
201     }
202   }
203   return count;
204 };
205
206
207 /**
208  * Gets all listeners that match the given type and capture mode. The
209  * returned array is a copy (but the listener objects are not).
210  * @param {string|!goog.events.EventId} type The type of the listeners
211  *     to retrieve.
212  * @param {boolean} capture The capture mode of the listeners to retrieve.
213  * @return {!Array.<goog.events.ListenableKey>} An array of matching
214  *     listeners.
215  */
216 goog.events.ListenerMap.prototype.getListeners = function(type, capture) {
217   var listenerArray = this.listeners[type.toString()];
218   var rv = [];
219   if (listenerArray) {
220     for (var i = 0; i < listenerArray.length; ++i) {
221       var listenerObj = listenerArray[i];
222       if (listenerObj.capture == capture) {
223         rv.push(listenerObj);
224       }
225     }
226   }
227   return rv;
228 };
229
230
231 /**
232  * Gets the goog.events.ListenableKey for the event or null if no such
233  * listener is in use.
234  *
235  * @param {string|!goog.events.EventId} type The type of the listener
236  *     to retrieve.
237  * @param {!Function} listener The listener function to get.
238  * @param {boolean} capture Whether the listener is a capturing listener.
239  * @param {Object=} opt_listenerScope Object in whose scope to call the
240  *     listener.
241  * @return {goog.events.ListenableKey} the found listener or null if not found.
242  */
243 goog.events.ListenerMap.prototype.getListener = function(
244     type, listener, capture, opt_listenerScope) {
245   var listenerArray = this.listeners[type.toString()];
246   var i = -1;
247   if (listenerArray) {
248     i = goog.events.ListenerMap.findListenerIndex_(
249         listenerArray, listener, capture, opt_listenerScope);
250   }
251   return i > -1 ? listenerArray[i] : null;
252 };
253
254
255 /**
256  * Whether there is a matching listener. If either the type or capture
257  * parameters are unspecified, the function will match on the
258  * remaining criteria.
259  *
260  * @param {string|!goog.events.EventId=} opt_type The type of the listener.
261  * @param {boolean=} opt_capture The capture mode of the listener.
262  * @return {boolean} Whether there is an active listener matching
263  *     the requested type and/or capture phase.
264  */
265 goog.events.ListenerMap.prototype.hasListener = function(
266     opt_type, opt_capture) {
267   var hasType = goog.isDef(opt_type);
268   var typeStr = hasType ? opt_type.toString() : '';
269   var hasCapture = goog.isDef(opt_capture);
270
271   return goog.object.some(
272       this.listeners, function(listenerArray, type) {
273         for (var i = 0; i < listenerArray.length; ++i) {
274           if ((!hasType || listenerArray[i].type == typeStr) &&
275               (!hasCapture || listenerArray[i].capture == opt_capture)) {
276             return true;
277           }
278         }
279
280         return false;
281       });
282 };
283
284
285 /**
286  * Finds the index of a matching goog.events.Listener in the given
287  * listenerArray.
288  * @param {!Array.<!goog.events.Listener>} listenerArray Array of listener.
289  * @param {!Function} listener The listener function.
290  * @param {boolean=} opt_useCapture The capture flag for the listener.
291  * @param {Object=} opt_listenerScope The listener scope.
292  * @return {number} The index of the matching listener within the
293  *     listenerArray.
294  * @private
295  */
296 goog.events.ListenerMap.findListenerIndex_ = function(
297     listenerArray, listener, opt_useCapture, opt_listenerScope) {
298   for (var i = 0; i < listenerArray.length; ++i) {
299     var listenerObj = listenerArray[i];
300     if (!listenerObj.removed &&
301         listenerObj.listener == listener &&
302         listenerObj.capture == !!opt_useCapture &&
303         listenerObj.handler == opt_listenerScope) {
304       return i;
305     }
306   }
307   return -1;
308 };