From e533a1867e2d8537f4d397ce5ea42a8bb4eee711 Mon Sep 17 00:00:00 2001 From: David Greenspan Date: Tue, 24 Apr 2012 19:36:41 -0700 Subject: [PATCH] first working version of fixed event bugs --- packages/liveui/liverange.js | 45 ++++++++++++++++++++++++++++- packages/liveui/liveui.js | 51 +++++++++++++++++++++++++++------ packages/liveui/liveui_tests.js | 5 ---- 3 files changed, 86 insertions(+), 15 deletions(-) diff --git a/packages/liveui/liverange.js b/packages/liveui/liverange.js index 211d3fa8e25..1ee3197ba4d 100644 --- a/packages/liveui/liverange.js +++ b/packages/liveui/liverange.js @@ -294,7 +294,7 @@ Meteor.ui = Meteor.ui || {}; var startIndex = start_range_skip || 0; var after = end.nextSibling; for(var n = start; n && n !== after; n = n.nextSibling) { - var startData = tag && n[tag] && n[tag][0]; + var startData = n[tag] && n[tag][0]; if (startData && startIndex < startData.length) { // immediate child range that starts with n var range = startData[startIndex]; @@ -550,4 +550,47 @@ Meteor.ui = Meteor.ui || {}; return result; }; + Meteor.ui._LiveRange.prototype.findParent = function(withSameContainer) { + var tag = this.tag; + + if (this._end_idx + 1 < this._end[tag][1].length) { + // immediately enclosing range ends at same node as this one + return this._end[tag][1][this._end_idx + 1]; + } + + var node = this._end.nextSibling; + while (node) { + var endIndex = 0; + var startData = node[tag] && node[tag][0]; + if (startData && startData.length) { + // skip over sibling of this range + var r = startData[0]; + node = r._end; + endIndex = r._end_idx + 1; + } + if (endIndex < node[tag][1].length) + return node[tag][1][endIndex]; + node = node.nextSibling; + } + + if (withSameContainer) + return null; + + return Meteor.ui._LiveRange.findRange(tag, this.containerNode()); + }; + + Meteor.ui._LiveRange.findRange = function(tag, node) { + while (node) { + var endData = node[tag] && node[tag][1]; + if (endData.length) + return endData[0]; + node = node.nextSibling; + } + + if (! node.parentNode) + return null; + + return Meteor.ui._LiveRange.findRange(tag, node.parentNode); + }; + })(); diff --git a/packages/liveui/liveui.js b/packages/liveui/liveui.js index 5b0097e8f6d..227f1be694b 100644 --- a/packages/liveui/liveui.js +++ b/packages/liveui/liveui.js @@ -419,19 +419,19 @@ Meteor.ui = Meteor.ui || {}; patcher.diffpatch(copyFunc); }); + attach_secondary_events(tgtRange); }; Meteor.ui._wire_up = function(cx, range, html_func, react_data) { // wire events var data = react_data || {}; if (data.events) { - for(var n = range.firstNode(); - n && n.previousSibling !== range.lastNode(); - n = n.nextSibling) { - Meteor.ui._setupEvents(n, data.events, data.event_data); - } + range.events = data.events; + range.event_data = data.event_data; } + attach_primary_events(range); + // record that if we see this range offscreen during a flush, // we are to kill the context (mark it killed and invalidate it). // Kill old context from previous update. @@ -480,6 +480,10 @@ Meteor.ui = Meteor.ui || {}; in_range); }; + var renderElse = function() { + return Meteor.ui.render(else_func, react_data); + }; + var callbacks = { added: function(doc, before_idx) { var frag = renderItem(doc); @@ -491,15 +495,18 @@ Meteor.ui = Meteor.ui || {}; else range_list[before_idx].insert_before(frag); + attach_secondary_events(range); + range_list.splice(before_idx, 0, range); }, removed: function(doc, at_idx) { - if (range_list.length === 1) + if (range_list.length === 1) { cleanup_frag( - outer_range.replace_contents(Meteor.ui.render( - else_func, react_data))); - else + outer_range.replace_contents(renderElse())); + attach_secondary_events(outer_range); + } else { cleanup_frag(range_list[at_idx].extract()); + } range_list.splice(at_idx, 1); }, @@ -561,4 +568,30 @@ Meteor.ui = Meteor.ui || {}; range.destroy(true); }; + // Attach events specified by `range` to top-level nodes in `range`. + var attach_primary_events = function(range) { + if (range.events) { + for(var n = range.firstNode(); + n && n.previousSibling !== range.lastNode(); + n = n.nextSibling) { + Meteor.ui._setupEvents(n, range.events, range.event_data); + } + } + }; + + // Attach events specified by enclosing ranges of `range`, at the + // same DOM level, to nodes in `range`. + var attach_secondary_events = function(range) { + for(var r = range; r; r = r.findParent(true)) { + if (r === range) + continue; + + for(var n = range.firstNode(); + n && n.previousSibling !== range.lastNode(); + n = n.nextSibling) { + Meteor.ui._setupEvents(n, r.events, r.event_data); + } + + } + }; })(); diff --git a/packages/liveui/liveui_tests.js b/packages/liveui/liveui_tests.js index ad339c108d3..af5d13d1470 100644 --- a/packages/liveui/liveui_tests.js +++ b/packages/liveui/liveui_tests.js @@ -1225,13 +1225,11 @@ Tinytest.add("liveui - events", function(test) { Meteor.flush(); // selector that specifies a top-level div - // FAILS in 0.3.3 event_buf.length = 0; div = OnscreenDiv(Meteor.ui.render(function() { return '
Foo
'; }, {events: eventmap("click div")})); simulateEvent(getid("foozy"), 'click'); - test.expect_fail(); test.equal(event_buf, ['click div']); div.kill(); Meteor.flush(); @@ -1248,7 +1246,6 @@ Tinytest.add("liveui - events", function(test) { // replaced top-level elements still have event handlers // even if not replaced by the chunk wih the handlers - // FAILS in 0.3.3 var R = ReactiveVar("p"); event_buf.length = 0; div = OnscreenDiv(Meteor.ui.render(function() { @@ -1262,13 +1259,11 @@ Tinytest.add("liveui - events", function(test) { R.set("div"); // change tag, which is sure to replace element Meteor.flush(); simulateEvent(getid("foozy"), 'click'); // still clickable? - test.expect_fail(); test.equal(event_buf, ['click']); event_buf.length = 0; R.set("p"); Meteor.flush(); simulateEvent(getid("foozy"), 'click'); - test.expect_fail(); test.equal(event_buf, ['click']); event_buf.length = 0;