Skip to content
This repository has been archived by the owner on Jan 22, 2024. It is now read-only.

Commit

Permalink
Some cleanups before introducing Listenable handling to goog.events:
Browse files Browse the repository at this point in the history
* Add goog.events.cleanUp that cleans up goog.events internal
data structure given a ListenableKey.

* Move key number generation to goog.events.ListenableKey.

* Rejigger code in goog.events to refer to
goog.events.listeners_ in as little places as possible
(subsequent logic to unify handling for DOM events and
Listenable will be cleaner, as listen_ will just return a
ListenableKey and listen/listenOnce will handle putting the
object into listeners_ array).

R=nicksantos,gboyer
DELTA=137 (104 added, 16 deleted, 17 changed)


Revision created by MOE tool push_codebase.
MOE_MIGRATION=6169


git-svn-id: http://closure-library.googlecode.com/svn/trunk@2452 0b95b8e8-c90f-11de-9d4f-f947ee5921c8
  • Loading branch information
chrishenry@google.com committed Jan 20, 2013
1 parent 7c2f0de commit 60f36ab
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 33 deletions.
38 changes: 25 additions & 13 deletions closure/goog/events/events.js
Expand Up @@ -245,16 +245,14 @@ goog.events.listen_ = function(
} }


var proxy = goog.events.getProxy(); var proxy = goog.events.getProxy();
proxy.src = src;
listenerObj = new goog.events.Listener(); listenerObj = new goog.events.Listener();
listenerObj.init(listener, proxy, src, type, capture, opt_handler); listenerObj.init(listener, proxy, src, type, capture, opt_handler);
listenerObj.callOnce = callOnce; listenerObj.callOnce = callOnce;


var key = listenerObj.key; proxy.src = src;
proxy.key = key; proxy.listener = listenerObj;


listenerArray.push(listenerObj); listenerArray.push(listenerObj);
goog.events.listeners_[key] = listenerObj;


if (!goog.events.sources_[srcUid]) { if (!goog.events.sources_[srcUid]) {
goog.events.sources_[srcUid] = []; goog.events.sources_[srcUid] = [];
Expand All @@ -275,6 +273,8 @@ goog.events.listen_ = function(
src.attachEvent(goog.events.getOnString_(type), proxy); src.attachEvent(goog.events.getOnString_(type), proxy);
} }


var key = listenerObj.key;
goog.events.listeners_[key] = listenerObj;
return key; return key;
}; };


Expand All @@ -288,10 +288,10 @@ goog.events.getProxy = function() {
// Use a local var f to prevent one allocation. // Use a local var f to prevent one allocation.
var f = goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ? var f = goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ?
function(eventObject) { function(eventObject) {
return proxyCallbackFunction.call(f.src, f.key, eventObject); return proxyCallbackFunction.call(f.src, f.listener, eventObject);
} : } :
function(eventObject) { function(eventObject) {
var v = proxyCallbackFunction.call(f.src, f.key, eventObject); var v = proxyCallbackFunction.call(f.src, f.listener, eventObject);
// NOTE(user): In IE, we hack in a capture phase. However, if // NOTE(user): In IE, we hack in a capture phase. However, if
// there is inline event handler which tries to prevent default (for // there is inline event handler which tries to prevent default (for
// example <a href="..." onclick="return false">...</a>) in a // example <a href="..." onclick="return false">...</a>) in a
Expand Down Expand Up @@ -491,6 +491,22 @@ goog.events.unlistenWithWrapper = function(src, wrapper, listener, opt_capt,
}; };




/**
* Cleans up goog.events internal data structure. This should be
* called by all implementations of goog.events.Listenable when
* removing listeners.
*
* TODO(user): Once we remove numeric key support from
* goog.events.listen and friend, we will be able to remove this
* requirement.
*
* @param {goog.events.ListenableKey} listenableKey The key to clean up.
*/
goog.events.cleanUp = function(listenableKey) {
delete goog.events.listeners_[listenableKey.key];
};


/** /**
* Cleans up the listener array as well as the listener tree * Cleans up the listener array as well as the listener tree
* @param {string} type The type of the event. * @param {string} type The type of the event.
Expand Down Expand Up @@ -953,23 +969,19 @@ goog.events.protectBrowserEventEntryPoint = function(errorHandler) {
* Handles an event and dispatches it to the correct listeners. This * Handles an event and dispatches it to the correct listeners. This
* function is a proxy for the real listener the user specified. * function is a proxy for the real listener the user specified.
* *
* @param {number} key Unique key for the listener. * @param {goog.events.Listener} listener The listener object.
* @param {Event=} opt_evt Optional event object that gets passed in via the * @param {Event=} opt_evt Optional event object that gets passed in via the
* native event handlers. * native event handlers.
* @return {boolean} Result of the event handler. * @return {boolean} Result of the event handler.
* @this {goog.events.EventTarget|Object} The object or Element that * @this {goog.events.EventTarget|Object} The object or Element that
* fired the event. * fired the event.
* @private * @private
*/ */
goog.events.handleBrowserEvent_ = function(key, opt_evt) { goog.events.handleBrowserEvent_ = function(listener, opt_evt) {
// If the listener isn't there it was probably removed when processing if (listener.removed) {
// another listener on the same event (e.g. the later listener is
// not managed by closure so that they are both fired under IE)
if (!goog.events.listeners_[key]) {
return true; return true;
} }


var listener = goog.events.listeners_[key];
var type = listener.type; var type = listener.type;
var map = goog.events.listenerTree_; var map = goog.events.listenerTree_;


Expand Down
17 changes: 12 additions & 5 deletions closure/goog/events/eventtarget.js
Expand Up @@ -312,9 +312,7 @@ goog.events.EventTarget.prototype.unlisten = function(
listenerArray, listener, opt_useCapture, opt_listenerScope); listenerArray, listener, opt_useCapture, opt_listenerScope);
if (index > -1) { if (index > -1) {
var listenerObj = listenerArray[index]; var listenerObj = listenerArray[index];
// We still need to mark this as removed as unlisten may be called goog.events.cleanUp(listenerObj);
// when a listener fired (the listener may already be queued to be
// fired in the same dispatch sequence).
listenerObj.removed = true; listenerObj.removed = true;
return goog.array.removeAt(listenerArray, index); return goog.array.removeAt(listenerArray, index);
} }
Expand All @@ -331,7 +329,12 @@ goog.events.EventTarget.prototype.unlistenByKey = function(key) {
return false; return false;
} }


return goog.array.remove(this.eventTargetListeners_[type], key); var removed = goog.array.remove(this.eventTargetListeners_[type], key);
if (removed) {
goog.events.cleanUp(key);
key.removed = true;
}
return removed;
}; };




Expand All @@ -342,7 +345,11 @@ goog.events.EventTarget.prototype.removeAllListeners = function(
for (var type in this.eventTargetListeners_) { for (var type in this.eventTargetListeners_) {
if (!opt_type || type == opt_type) { if (!opt_type || type == opt_type) {
var listenerArray = this.eventTargetListeners_[type]; var listenerArray = this.eventTargetListeners_[type];
count += listenerArray.length; for (var i = 0; i < listenerArray.length; i++) {
++count;
goog.events.cleanUp(listenerArray[i]);
listenerArray[i].removed = true;
}
listenerArray.length = 0; listenerArray.length = 0;
} }
} }
Expand Down
5 changes: 4 additions & 1 deletion closure/goog/events/eventtarget_test.html
Expand Up @@ -32,6 +32,9 @@
var unlistenFn = function(src, type, listener, opt_capt, opt_handler) { var unlistenFn = function(src, type, listener, opt_capt, opt_handler) {
return src.unlisten(type, listener, opt_capt, opt_handler); return src.unlisten(type, listener, opt_capt, opt_handler);
}; };
var unlistenByKeyFn = function(src, key) {
return src.unlistenByKey(key);
};
var listenOnceFn = function(src, type, listener, opt_capt, opt_handler) { var listenOnceFn = function(src, type, listener, opt_capt, opt_handler) {
return src.listenOnce(type, listener, opt_capt, opt_handler); return src.listenOnce(type, listener, opt_capt, opt_handler);
}; };
Expand All @@ -46,7 +49,7 @@
}; };


goog.events.eventTargetTester.setUp( goog.events.eventTargetTester.setUp(
listenFn, unlistenFn, listenFn, unlistenFn, unlistenByKeyFn,
listenOnceFn, dispatchEventFn, listenOnceFn, dispatchEventFn,
removeAllFn, getListenersFn, removeAllFn, getListenersFn,
goog.events.eventTargetTester.KeyType.NUMBER, goog.events.eventTargetTester.KeyType.NUMBER,
Expand Down
5 changes: 4 additions & 1 deletion closure/goog/events/eventtarget_via_googevents_test.html
Expand Up @@ -22,8 +22,11 @@
<script> <script>


function setUp() { function setUp() {
var unlistenByKeyFn = function(src, key) {
return goog.events.unlistenByKey(key);
};
goog.events.eventTargetTester.setUp( goog.events.eventTargetTester.setUp(
goog.events.listen, goog.events.unlisten, goog.events.listen, goog.events.unlisten, unlistenByKeyFn,
goog.events.listenOnce, goog.events.dispatchEvent, goog.events.listenOnce, goog.events.dispatchEvent,
goog.events.removeAll, goog.events.getListeners, goog.events.removeAll, goog.events.getListeners,
goog.events.eventTargetTester.KeyType.NUMBER, goog.events.eventTargetTester.KeyType.NUMBER,
Expand Down
2 changes: 1 addition & 1 deletion closure/goog/events/eventtarget_via_w3cinterface_test.html
Expand Up @@ -32,7 +32,7 @@
}; };


goog.events.eventTargetTester.setUp( goog.events.eventTargetTester.setUp(
listenFn, unlistenFn, listenFn, unlistenFn, null /* unlistenByKeyFn */,
null /* listenOnceFn */, dispatchEventFn, null /* removeAllFn */, null /* listenOnceFn */, dispatchEventFn, null /* removeAllFn */,
null /* getListenersFn */, null /* getListenersFn */,
goog.events.eventTargetTester.KeyType.UNDEFINED, goog.events.eventTargetTester.KeyType.UNDEFINED,
Expand Down
44 changes: 41 additions & 3 deletions closure/goog/events/eventtargettester.js
Expand Up @@ -38,6 +38,9 @@ goog.require('goog.testing.recordFunction');
* @param {Function} unlistenFn Function that, given the same * @param {Function} unlistenFn Function that, given the same
* signature as goog.events.unlisten, will remove listener from * signature as goog.events.unlisten, will remove listener from
* the given event target. * the given event target.
* @param {Function} unlistenByKeyFn Function that, given 2
* parameters: src and key, will remove the corresponding
* listener.
* @param {Function} listenOnceFn Function that, given the same * @param {Function} listenOnceFn Function that, given the same
* signature as goog.events.listenOnce, will add a one-time * signature as goog.events.listenOnce, will add a one-time
* listener to the given event target. * listener to the given event target.
Expand All @@ -58,11 +61,12 @@ goog.require('goog.testing.recordFunction');
* be set to false. * be set to false.
*/ */
goog.events.eventTargetTester.setUp = function( goog.events.eventTargetTester.setUp = function(
listenFn, unlistenFn, listenOnceFn, dispatchEventFn, listenFn, unlistenFn, unlistenByKeyFn, listenOnceFn,
removeAllFn, getListenersFn, dispatchEventFn, removeAllFn, getListenersFn,
listenKeyType, unlistenFnReturnType) { listenKeyType, unlistenFnReturnType) {
listen = listenFn; listen = listenFn;
unlisten = unlistenFn; unlisten = unlistenFn;
unlistenByKey = unlistenByKeyFn;
listenOnce = listenOnceFn; listenOnce = listenOnceFn;
dispatchEvent = dispatchEventFn; dispatchEvent = dispatchEventFn;
removeAll = removeAllFn; removeAll = removeAllFn;
Expand Down Expand Up @@ -164,7 +168,7 @@ var EventType = {
}; };




var listen, unlisten, listenOnce, dispatchEvent; var listen, unlisten, unlistenByKey, listenOnce, dispatchEvent;
var removeAll, getListeners; var removeAll, getListeners;
var keyType, unlistenReturnType; var keyType, unlistenReturnType;
var eventTargets, listeners; var eventTargets, listeners;
Expand Down Expand Up @@ -711,6 +715,40 @@ function testUnlistenInListen() {
} }




function testUnlistenByKeyInListen() {
if (!unlistenByKey) {
return;
}

var key1, key2;
listeners[1] = createListener(
function(e) {
unlistenByKey(eventTargets[0], key1);
unlistenByKey(eventTargets[0], key2);
});
listen(eventTargets[0], EventType.A, listeners[0]);
key1 = listen(eventTargets[0], EventType.A, listeners[1]);
key2 = listen(eventTargets[0], EventType.A, listeners[2]);
listen(eventTargets[0], EventType.A, listeners[3]);

dispatchEvent(eventTargets[0], EventType.A);

assertListenerIsCalled(listeners[0], times(1));
assertListenerIsCalled(listeners[1], times(1));
assertListenerIsCalled(listeners[2], times(0));
assertListenerIsCalled(listeners[3], times(1));
assertNoOtherListenerIsCalled();
resetListeners();

dispatchEvent(eventTargets[0], EventType.A);
assertListenerIsCalled(listeners[0], times(1));
assertListenerIsCalled(listeners[1], times(0));
assertListenerIsCalled(listeners[2], times(0));
assertListenerIsCalled(listeners[3], times(1));
assertNoOtherListenerIsCalled();
}


function testSetParentEventTarget() { function testSetParentEventTarget() {
assertNull(eventTargets[0].getParentEventTarget()); assertNull(eventTargets[0].getParentEventTarget());


Expand Down
32 changes: 32 additions & 0 deletions closure/goog/events/listenable.js
Expand Up @@ -124,6 +124,8 @@ goog.events.Listenable.prototype.listenOnce;
/** /**
* Removes an event listener which was added with listen() or listenOnce(). * Removes an event listener which was added with listen() or listenOnce().
* *
* Implementation needs to call goog.events.cleanUp.
*
* @param {string} type Event type or array of event types. * @param {string} type Event type or array of event types.
* @param {!Function} listener Callback method, or an object * @param {!Function} listener Callback method, or an object
* with a handleEvent function. TODO(user): Consider whether * with a handleEvent function. TODO(user): Consider whether
Expand All @@ -141,6 +143,8 @@ goog.events.Listenable.prototype.unlisten;
* Removes an event listener which was added with listen() by the key * Removes an event listener which was added with listen() by the key
* returned by listen(). * returned by listen().
* *
* Implementation needs to call goog.events.cleanUp.
*
* @param {goog.events.ListenableKey} key The key returned by * @param {goog.events.ListenableKey} key The key returned by
* listen() or listenOnce(). * listen() or listenOnce().
* @return {boolean} Whether any listener was removed. * @return {boolean} Whether any listener was removed.
Expand Down Expand Up @@ -169,6 +173,9 @@ goog.events.Listenable.prototype.dispatchEvent;
* it will only remove listeners of the particular type. otherwise all * it will only remove listeners of the particular type. otherwise all
* registered listeners will be removed. * registered listeners will be removed.
* *
* Implementation needs to call goog.events.cleanUp for each removed
* listener.
*
* @param {string=} opt_type Type of event to remove, default is to * @param {string=} opt_type Type of event to remove, default is to
* remove all types. * remove all types.
* @return {number} Number of listeners removed. * @return {number} Number of listeners removed.
Expand Down Expand Up @@ -213,6 +220,24 @@ goog.events.Listenable.prototype.getListeners;
goog.events.ListenableKey = function() {}; goog.events.ListenableKey = function() {};




/**
* Counter used to create a unique key
* @type {number}
* @private
*/
goog.events.ListenableKey.counter_ = 0;


/**
* Reserves a key to be used for ListenableKey#key field.
* @return {number} A number to be used to fill ListenableKey#key
* field.
*/
goog.events.ListenableKey.reserveKey = function() {
return ++goog.events.ListenableKey.counter_;
};


/** /**
* The source event target. * The source event target.
* @type {!Object} * @type {!Object}
Expand Down Expand Up @@ -247,3 +272,10 @@ goog.events.ListenableKey.prototype.capture;
* @type {Object} * @type {Object}
*/ */
goog.events.ListenableKey.prototype.handler; goog.events.ListenableKey.prototype.handler;


/**
* A globally unique number to identify the key.
* @type {number}
*/
goog.events.ListenableKey.prototype.key;
11 changes: 2 additions & 9 deletions closure/goog/events/listener.js
Expand Up @@ -35,14 +35,6 @@ goog.events.Listener = function() {
}; };




/**
* Counter used to create a unique key
* @type {number}
* @private
*/
goog.events.Listener.counter_ = 0;


/** /**
* @define {boolean} Whether to enable the monitoring of the * @define {boolean} Whether to enable the monitoring of the
* goog.events.Listener instances. Switching on the monitoring is only * goog.events.Listener instances. Switching on the monitoring is only
Expand Down Expand Up @@ -106,6 +98,7 @@ goog.events.Listener.prototype.handler;
/** /**
* The key of the listener. * The key of the listener.
* @type {number} * @type {number}
* @override
*/ */
goog.events.Listener.prototype.key = 0; goog.events.Listener.prototype.key = 0;


Expand Down Expand Up @@ -162,7 +155,7 @@ goog.events.Listener.prototype.init = function(listener, proxy, src, type,
this.capture = !!capture; this.capture = !!capture;
this.handler = opt_handler; this.handler = opt_handler;
this.callOnce = false; this.callOnce = false;
this.key = ++goog.events.Listener.counter_; this.key = goog.events.ListenableKey.reserveKey();
this.removed = false; this.removed = false;
}; };


Expand Down

0 comments on commit 60f36ab

Please sign in to comment.