From e104d43021bac4cbaf0681ff6cad743063e2e97e Mon Sep 17 00:00:00 2001 From: David Greenspan Date: Mon, 2 Sep 2013 14:45:19 -0700 Subject: [PATCH] comments; work in IE 8; failing test for IE 9 --- packages/ui/domrange.js | 39 +++++++++++++++++++++++++++++------ packages/ui/domrange_tests.js | 37 +++++++++++++++++++++++++++++++-- packages/ui/package.js | 2 +- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/packages/ui/domrange.js b/packages/ui/domrange.js index bd59759432..a1157f864f 100644 --- a/packages/ui/domrange.js +++ b/packages/ui/domrange.js @@ -65,6 +65,22 @@ var checkId = function (id) { throw new Error("id may not be empty"); }; +var textExpandosOk = (function () { + var tn = document.createTextNode(''); + try { + tn.blahblah = true; + return true; + } catch (e) { + // IE 8 + return false; + } +})(); + +var createMarkerNode = ( + textExpandosOk ? + function () { return document.createTextNode(""); } : + function () { return document.createComment(""); }); + var DomRange = function (component) { // This code supports IE 8 if `createTextNode` is changed // to `createComment`. What we really should do is: @@ -73,9 +89,10 @@ var DomRange = function (component) { // - keep a list of all DomRanges to avoid IE 9+ GC of // TextNodes; this will probably help DomRange removal // detection too. - var start = document.createTextNode(""); - var end = document.createTextNode(""); + var start = createMarkerNode(); + var end = createMarkerNode(); var fragment = DomBackend.newFragment([start, end]); + fragment.$_uiIsOffscreen = true; if (component) { this.component = component; @@ -730,8 +747,9 @@ DomRange.prototype.$ = function (selector) { // Since jQuery can't run selectors on a DocumentFragment, // we don't expect findBySelector to work. - if (parentNode.nodeType === 11) // DocumentFragment - throw new Error("Can't use $ on a detached component"); + if (parentNode.nodeType === 11 /* DocumentFragment */ || + parentNode.$_uiIsOffscreen) + throw new Error("Can't use $ on an offscreen component"); var results = DomBackend.findBySelector(selector, parentNode); @@ -817,8 +835,17 @@ var HandlerRec = function (elem, type, selector, handler, $ui) { }; })(this); - var tryCapturing = (! eventsToDelegate.hasOwnProperty( - DomBackend.parseEventType(type))); + // WHY CAPTURE AND DELEGATE: jQuery can't delegate + // non-bubbling events, because + // event capture doesn't work in IE 8. However, there + // are all sorts of new-fangled non-bubbling events + // like "play" and "touchenter". We delegate these + // events using capture in all browsers except IE 8. + // IE 8 doesn't support these events anyway. + + var tryCapturing = elem.addEventListener && + (! eventsToDelegate.hasOwnProperty( + DomBackend.parseEventType(type))); if (tryCapturing) { this.capturingHandler = (function (h) { diff --git a/packages/ui/domrange_tests.js b/packages/ui/domrange_tests.js index cb879918ee..c268fb6cec 100644 --- a/packages/ui/domrange_tests.js +++ b/packages/ui/domrange_tests.js @@ -731,7 +731,7 @@ Tinytest.add("ui - DomRange - contains", function (test) { test.isTrue(r.contains(span)); test.isTrue(s.contains(span)); test.isTrue(t.contains(span)); - test.isFalse(r.contains(r.parentNode)); + test.isFalse(r.contains(r.parentNode())); test.isFalse(r.contains(document.createElement("DIV"))); }); }); @@ -808,6 +808,37 @@ Tinytest.add("ui - DomRange - get", function (test) { test.isTrue(r.get('x') === x); }); +// This test targets IE 9 and 10, which allow properties +// to be attached to TextNodes but may lose them over time. +// Specifically, the JavaScript view of a TextNode seems to +// be only weakly retained by the TextNode itself, so if you +// hang an object graph off a TextNode, you need some other +// pointer to the TextNode or an object in the graph to +// retain it. +Tinytest.addAsync("ui - DomRange - IE TextNode GC", function (test, onComplete) { + var r = new DomRange; + var B = document.createElement('B'); + B.id = 'ie_textnode_gc_test'; + document.body.appendChild(B); + DomRange.insert(r, B); + r = null; + B = null; + + // trigger GC... + if (typeof CollectGarbage === 'function') + CollectGarbage(); + + // come back later... + window.setTimeout(function () { + var B = document.getElementById("ie_textnode_gc_test"); + test.isTrue(B.firstChild.$ui); + test.isTrue(B.lastChild.$ui); + window.BBB = B; + document.body.removeChild(B); + onComplete(); + }, 500); +}); + // TO TEST STILL: // - external remove element // - double-add, double-remove @@ -818,4 +849,6 @@ Tinytest.add("ui - DomRange - get", function (test) { // - can't add multiple members with id, but can add array of 1. // can add 0 with no id. // - add a node or range with the same id as an old member -// works if that member is gone. \ No newline at end of file +// works if that member is gone. +// - events get wired if declared before adding; get moved +// when wrapping in TBODY \ No newline at end of file diff --git a/packages/ui/package.js b/packages/ui/package.js index 0caa7f794c..d1a49d8e37 100644 --- a/packages/ui/package.js +++ b/packages/ui/package.js @@ -12,7 +12,7 @@ Package.on_use(function (api) { api.use('minimongo'); // for idStringify api.add_files(['backend.js', - 'domrange.js']); + 'domrange.js'], 'client'); }); Package.on_test(function (api) {