start of Meteor UI Events

This commit is contained in:
David Greenspan
2013-08-29 19:52:03 -07:00
parent 8af99767c2
commit b557c65460
2 changed files with 117 additions and 0 deletions

View File

@@ -650,4 +650,69 @@ var moveWithOwnersIntoTbody = function (range) {
return tbody;
};
///// EVENTS
// XXX could write the form of arguments for this function
// in several different ways, including simply as an event map.
DomRange.prototype.on = function (events, selector, handler) {
var parentNode = this.parentNode();
if (! parentNode)
// if we're not in the DOM, silently fail.
return;
var eventTypes = [];
events.replace(/[^ /]+/g, function (e) {
eventTypes.push(e);
});
if (! handler && (typeof selector === 'function')) {
// omitted `selector`
handler = selector;
selector = null;
} else if (! selector) {
// take `""` to `null`
selector = null;
}
for (var i = 0, N = eventTypes.length; i < N; i++) {
var type = eventTypes[i];
var eventDict = parentNode.$_uievents;
if (! eventDict)
eventDict = (parentNode.$_uievents = {});
var info = eventDict[type];
if (! info) {
info = eventDict[type] = {};
info.handlers = [];
}
var handlerRecord = {
elem: parentNode,
type: type,
selector: selector,
$ui: this.component,
handler: handler
};
// It's important that lowLevelHandler be a different
// instance for each handlerRecord, because its identity
// is used to remove it. Capture handlerRecord in a
// closure so that we have access to it, even when
// the var changes, and so we don't pull in the rest of
// the stack frame.
handlerRecord.lowLevelHandler = (function (h) {
return function (evt) {
if ((! selector) && evt.currentTarget !== evt.target)
// no selector means only fire on target
return;
return h.handler.call(h.$ui, evt);
};
})(handlerRecord);
info.handlers.push(handlerRecord);
$(parentNode).on(type, selector || '*',
handlerRecord.lowLevelHandler);
}
};
UI.DomRange = DomRange;

View File

@@ -1,5 +1,6 @@
var DomRange = UI.DomRange;
var parseHTML = UI.DomBackend.parseHTML;
// fake component; DomRange host
var Comp = function (which) {
@@ -16,6 +17,18 @@ var isEndMarker = function (n) {
return (n.$ui && n === n.$ui.dom.end);
};
var inDocument = function (range, func) {
var onscreen = document.createElement("DIV");
onscreen.style.display = 'none';
document.body.appendChild(onscreen);
DomRange.insert(range.component, onscreen);
try {
func(range);
} finally {
document.body.removeChild(onscreen);
}
};
Tinytest.add("ui - DomRange - basic", function (test) {
var r = new DomRange;
r.which = 'R';
@@ -593,6 +606,45 @@ Tinytest.add("ui - DomRange - tables", function (test) {
test.equal(tbody.childNodes[4].nodeName, 'TR');
});
Tinytest.add("ui - DomRange - basic events", function (test) {
// test.equal doesn't work on arrays of DOM nodes, so
// we need this. It's `===` that descends recursively
// into any arrays.
var arrayEqual = function (a, b) {
test.equal(_.isArray(a), _.isArray(b));
if (_.isArray(a)) {
test.equal(a.length, b.length);
for (var i = 0; i < a.length; i++) {
arrayEqual(a[i], b[i]);
}
} else {
test.isTrue(a[i] === b[i]);
}
};
var htmlRange = function (html) {
var r = new DomRange;
_.each(parseHTML(html), function (node) {
r.add(node);
});
return r;
};
inDocument(
htmlRange("<span>Foo</span>"),
function (r) {
var buf = [];
r.on('click', 'span', function (evt) {
buf.push([evt.type, evt.target, evt.currentTarget]);
});
arrayEqual(buf, []);
var span = r.elements()[0];
span.click();
arrayEqual(buf, [['click', span, span]]);
});
});
// TO TEST STILL:
// - external remove element