From 316fcbb7a130fa04147495f28d2e907b18362e61 Mon Sep 17 00:00:00 2001
From: David Greenspan
Date: Wed, 9 May 2012 19:13:22 -0700
Subject: [PATCH] fix bubbling and more comments
---
packages/liveui/liveevents_w3c.js | 2 ++
packages/liveui/liveui.js | 13 ++++++++++++-
packages/liveui/liveui_tests.js | 28 +++++++++++++++++++++++++---
3 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/packages/liveui/liveevents_w3c.js b/packages/liveui/liveevents_w3c.js
index 45a8269d16..58a2d0e970 100644
--- a/packages/liveui/liveevents_w3c.js
+++ b/packages/liveui/liveevents_w3c.js
@@ -130,6 +130,8 @@ Meteor.ui._event._loadW3CImpl = function() {
var eventsCaptured = {};
Meteor.ui._event.registerEventType = function(eventType, subtreeRoot) {
+ // We capture on the entire document, so don't actually care
+ // about subtreeRoot!
installCapturer(eventType);
};
diff --git a/packages/liveui/liveui.js b/packages/liveui/liveui.js
index d5cc53a191..11170784e5 100644
--- a/packages/liveui/liveui.js
+++ b/packages/liveui/liveui.js
@@ -582,6 +582,9 @@ Meteor.ui = Meteor.ui || {};
// without taking enclosing ranges into account, so additional event
// handlers need to be attached.
var attach_secondary_events = function(range) {
+ // Implementations of LiveEvents that use whole-document event capture
+ // (all except old IE) don't actually need any of this; this function
+ // could be a no-op.
for(var r = range; r; r = r.findParent()) {
if (r === range)
continue;
@@ -598,6 +601,12 @@ Meteor.ui = Meteor.ui || {};
}
};
+ // Handle a currently-propagating event on a particular node.
+ // We walk all enclosing liveranges of the node, from the inside out,
+ // looking for matching handlers. If the app calls stopPropagation(),
+ // we don't stop immediately (within an event map), but we DO stop
+ // between ranges (i.e. templates), in keeping with the idea that
+ // ranges are like invisible container nodes.
Meteor.ui._handleEvent = function(event) {
var curNode = event.currentTarget;
if (! curNode)
@@ -609,7 +618,7 @@ Meteor.ui = Meteor.ui || {};
var type = event.type;
- for(var range = innerRange; range; range = range.findParent(true)) {
+ for(var range = innerRange; range; range = range.findParent()) {
if (! range.event_handlers)
continue;
@@ -626,6 +635,8 @@ Meteor.ui = Meteor.ui || {};
}
var returnValue = h.callback.call(range.event_data, event);
+ // allow app to `return false` from event handler, just like
+ // you can in a jquery event handler
if (returnValue === false) {
event.stopPropagation();
event.preventDefault();
diff --git a/packages/liveui/liveui_tests.js b/packages/liveui/liveui_tests.js
index faa09f649d..db30488398 100644
--- a/packages/liveui/liveui_tests.js
+++ b/packages/liveui/liveui_tests.js
@@ -1314,7 +1314,8 @@ Tinytest.add("liveui - basic events", function(test) {
// "deep reach" from high node down to replaced low node.
// Tests that attach_secondary_events actually does the
- // right thing in IE. Also tests change event bubbling.
+ // right thing in IE. Also tests change event bubbling
+ // and proper interpretation of event maps.
event_buf.length = 0;
R = ReactiveVar('foo');
div = OnscreenDiv(Meteor.ui.render(function() {
@@ -1323,13 +1324,34 @@ Tinytest.add("liveui - basic events", function(test) {
return ''+R.get();
}, {events: eventmap('click input'), event_data:event_buf}) +
'
';
- }, { events: eventmap('change b'), event_data:event_buf }));
+ }, { events: eventmap('change b', 'change input'), event_data:event_buf }));
R.set('bar');
Meteor.flush();
// click on input
clickElement(div.node().getElementsByTagName('input')[0]);
event_buf.sort(); // don't care about order
- test.equal(event_buf, ['change b', 'click input']);
+ test.equal(event_buf, ['change b', 'change input', 'click input']);
+ event_buf.length = 0;
+ div.kill();
+ Meteor.flush();
+
+ // test that 'click *' fires on bubble
+ event_buf.length = 0;
+ R = ReactiveVar('foo');
+ div = OnscreenDiv(Meteor.ui.render(function() {
+ return '