diff --git a/packages/domutils/.gitignore b/packages/domutils/.gitignore
deleted file mode 100644
index 677a6fc263..0000000000
--- a/packages/domutils/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.build*
diff --git a/packages/domutils/domutils.js b/packages/domutils/domutils.js
deleted file mode 100644
index e270d8f622..0000000000
--- a/packages/domutils/domutils.js
+++ /dev/null
@@ -1,573 +0,0 @@
-DomUtils = {};
-
-var qsaFindAllBySelector = function (selector, contextNode) {
- // If IE7 users report the following error message, you
- // can fix it with "meteor add jquery".
- if (! document.querySelectorAll)
- throw new Error("This browser doesn't support querySelectorAll.");
-
- // the search is constrained to descendants of `ancestor`,
- // but it doesn't affect the scope of the query.
- var ancestor = contextNode;
-
- return withElementId(
- contextNode, "DomUtils_findAllBySelector_scope",
- function (idSelector) {
- // scope the entire selector to contextNode by prepending
- // id of contextNode to the selector.
- var doctoredSelector = _.map(selector.split(','), function (selec) {
- return idSelector + " " + selec;
- }).join(',');
- return ancestor.querySelectorAll(doctoredSelector);
- });
-};
-
-// We have our own, querySelectorAll-based implementation of scoped
-// selector matching; it's all you need in IE 8+ and modern browsers.
-//
-// However, we use Sizzle or jQuery if it's present on the client because of:
-// - apps that want jQuery's selector extensions (:visible, :input, etc.)
-// - apps that include jQuery anyway
-// - apps that want IE 7 support
-//
-// XXX others? zepto?
-var findAllBySelector = (window.Sizzle
- || (window.jQuery && window.jQuery.find)
- || qsaFindAllBySelector);
-
-///// Common look-up tables used by htmlToFragment et al.
-
-var testDiv = document.createElement("div");
-testDiv.innerHTML = "
";
-// Need to wrap in a div rather than directly creating SELECT to avoid
-// *another* IE bug.
-var testSelectDiv = document.createElement("div");
-testSelectDiv.innerHTML = "Foo ";
-testSelectDiv.firstChild.setAttribute("name", "myname");
-
-// Tests that, if true, indicate browser quirks present.
-var quirks = {
- // IE loses initial whitespace when setting innerHTML.
- leadingWhitespaceKilled: (testDiv.firstChild.nodeType !== 3),
-
- // IE may insert an empty tbody tag in a table.
- tbodyInsertion: testDiv.getElementsByTagName("tbody").length > 0,
-
- // IE loses some tags in some environments (requiring extra wrapper).
- tagsLost: testDiv.getElementsByTagName("link").length === 0,
-
- // IE <= 9 loses HTML comments in and tags.
- commentsLost: (! testDiv.getElementsByTagName("select")[0].firstChild),
-
- selectValueMustBeFromAttribute: (testSelectDiv.firstChild.value !== "Foo"),
-
- // In IE7, setAttribute('name', foo) doesn't show up in rendered HTML.
- // (In FF3, outerHTML is undefined, but it doesn't have this quirk.)
- mustSetNameInCreateElement: (
- testSelectDiv.firstChild.outerHTML &&
- testSelectDiv.firstChild.outerHTML.indexOf("myname") === -1)
-};
-
-// Set up map of wrappers for different nodes.
-var wrapMap = {
- option: [ 1, "", " " ],
- legend: [ 1, "", " " ],
- thead: [ 1, "" ],
- tr: [ 2, "" ],
- td: [ 3, "" ],
- col: [ 2, "" ],
- area: [ 1, "", " " ],
- _default: [ 0, "", "" ]
-};
-_.extend(wrapMap, {
- optgroup: wrapMap.option,
- tbody: wrapMap.thead,
- tfoot: wrapMap.thead,
- colgroup: wrapMap.thead,
- caption: wrapMap.thead,
- th: wrapMap.td
-});
-if (quirks.tagsLost) {
- // trick from jquery. initial text is ignored when we take lastChild.
- wrapMap._default = [ 1, "div", "
" ];
-}
-
-var rleadingWhitespace = /^\s+/,
- rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
- rtagName = /<([\w:]+)/,
- rtbody = /$2>");
- // Use first tag to determine wrapping needed.
- var firstTagMatch = rtagName.exec(html);
- var firstTag = (firstTagMatch ? firstTagMatch[1].toLowerCase() : "");
- var wrapData = wrapMap[firstTag] || wrapMap._default;
- var fullHtml = wrapData[1] + html + wrapData[2];
- if (quirks.commentsLost) {
- // rewrite and tags into fake tags
- fullHtml = fullHtml.replace(/<\s*(select|option)\b/ig,
- ' tags
- var fakeTags = [];
- // getElementsByTagName returns a "live" collection, so avoid
- // factorings of this code that iterate over it while mutating
- // the DOM.
- // Here we build an array of fake tags and iterate over that.
- _.each(container.getElementsByTagName("ins"), function (ins) {
- if (ins.getAttribute("domutilsrealtagname")) {
- fakeTags.push(ins);
- }
- });
-
- _.each(fakeTags, function (fakeTag) {
- var tagName = fakeTag.getAttribute('domutilsrealtagname');
- if (quirks.mustSetNameInCreateElement &&
- fakeTag.getAttribute('name')) {
- // IE7 can't set 'name' with setAttribute, but it has this
- // crazy syntax for setting it at create time.
- // http://webbugtrack.blogspot.com/2007/10/bug-235-createelement-is-broken-in-ie.html
- // http://msdn.microsoft.com/en-us/library/ms536389.aspx
- tagName = "<" + tagName + " name='" +
- _.escape(fakeTag.getAttribute('name')) + "'/>";
- }
- var realTag = document.createElement(tagName);
- fakeTag.removeAttribute('domutilsrealtagname');
- // copy all attributes. for some reason mergeAttributes doesn't work
- // here: eg, it doesn't copy SELECTED or VALUE. (Probably because
- // these attributes would be expando on INS?)
- var fakeAttrs = fakeTag.attributes;
- for (var i = 0; i < fakeAttrs.length; ++i) {
- var fakeAttr = fakeAttrs.item(i);
- if (fakeAttr.specified) {
- var name = fakeAttr.name.toLowerCase();
- var value = String(fakeAttr.value);
- // IE7 gets confused if you try to setAttribute('selected', ''),
- // so be a little more explicit.
- if (name === 'selected' && value === '')
- value = 'selected';
- realTag.setAttribute(name, value);
- }
- }
-
- // move all children
- while (fakeTag.firstChild)
- realTag.appendChild(fakeTag.firstChild);
- // replace
- fakeTag.parentNode.replaceChild(realTag, fakeTag);
- });
- }
-
- // Reparent children of container to frag.
- while (container.firstChild)
- frag.appendChild(container.firstChild);
- }
-
- return frag;
-};
-
-// Return an HTML string representing the contents of frag,
-// a DocumentFragment. (This is what innerHTML would do if
-// it were defined on DocumentFragments.)
-DomUtils.fragmentToHtml = function (frag) {
- frag = frag.cloneNode(true); // deep copy, don't touch original!
-
- return DomUtils.fragmentToContainer(frag).innerHTML;
-};
-
-// Given a DocumentFragment, return a node whose children are the
-// reparented contents of the DocumentFragment. In most cases this
-// is as simple as creating a DIV, but in the case of a fragment
-// containing TRs, for example, it's necessary to create a TABLE and
-// a TBODY and return the TBODY.
-DomUtils.fragmentToContainer = function (frag) {
- var doc = document; // node factory
-
- var firstElement = frag.firstChild;
- while (firstElement && firstElement.nodeType !== 1) {
- firstElement = firstElement.nextSibling;
- }
-
- var container = doc.createElement("div");
-
- if (! firstElement) {
- // no tags!
- container.appendChild(frag);
- } else {
- var firstTag = firstElement.nodeName;
- var wrapData = wrapMap[firstTag] || wrapMap._default;
-
- container.innerHTML = wrapData[1] + wrapData[2];
- var unwraps = wrapData[0];
- while (unwraps--) {
- container = container.lastChild;
- }
-
- container.appendChild(frag);
- }
-
- return container;
-};
-
-// Returns true if element a contains node b and is not node b.
-DomUtils.elementContains = function (a, b) {
- if (a.nodeType !== 1) /* ELEMENT */
- return false;
- if (a === b)
- return false;
-
- if (a.compareDocumentPosition) {
- return a.compareDocumentPosition(b) & 0x10;
- } else {
- // Should be only old IE and maybe other old browsers here.
- // Modern Safari has both functions but seems to get contains() wrong.
- // IE can't handle b being a text node. We work around this
- // by doing a direct parent test now.
- b = b.parentNode;
- if (! (b && b.nodeType === 1)) /* ELEMENT */
- return false;
- if (a === b)
- return true;
-
- return a.contains(b);
- }
-};
-
-// Returns an array containing the children of contextNode that
-// match `selector`. Unlike querySelectorAll, `selector` is
-// interpreted as if the document were rooted at `contextNode` --
-// the only nodes that can be used to match components of the
-// selector are the descendents of `contextNode`. `contextNode`
-// itself is not included (it can't be used to match a component of
-// the selector, and it can never be included in the returned
-// array.)
-//
-// `contextNode` may be either a node, a document, or a DocumentFragment.
-DomUtils.findAll = function (contextNode, selector) {
- if (contextNode.nodeType === 11 /* DocumentFragment */) {
- // contextNode is a DocumentFragment.
- //
- // We don't expect to be able to run selectors on a DocumentFragment
- // (Sizzle won't work) but we can on a normal elements that aren't
- // in the document. Fortunately we can manipulate offscreen nodes
- // as much as we want as long as we put them back the way they were
- // when we're done.
- var frag = contextNode;
- var container = DomUtils.fragmentToContainer(frag);
- var results = findAllBySelector(selector, container);
- // put nodes back into frag
- while (container.firstChild)
- frag.appendChild(container.firstChild);
- return results;
- }
-
- return findAllBySelector(selector, contextNode);
-};
-
-// Like `findAll` but finds one element (or returns null).
-DomUtils.find = function (contextNode, selector) {
- var results = DomUtils.findAll(contextNode, selector);
- return (results.length ? results[0] : null);
-};
-
-var isElementInClipRange = function (elem, clipStart, clipEnd) {
- // elem is not in clip range if it contains the clip range
- if (DomUtils.elementContains(elem, clipStart))
- return false;
- // elem is in clip range if clipStart <= elem <= clipEnd
- return (DomUtils.compareElementIndex(clipStart, elem) <= 0) &&
- (DomUtils.compareElementIndex(elem, clipEnd) <= 0);
-};
-
-// Like `findAll` but searches the nodes from `start` to `end`
-// inclusive. `start` and `end` must be siblings, and they participate
-// in the search (they can be used to match selector components, and
-// they can appear in the returned results). It's as if the parent of
-// `start` and `end` serves as contextNode, but matches from children
-// that aren't between `start` and `end` (inclusive) are ignored.
-//
-// If `selector` involves sibling selectors, child index selectors, or
-// the like, the results are undefined.
-//
-// precond: clipStart/clipEnd are descendents of contextNode
-// XXX document
-DomUtils.findAllClipped = function (contextNode, selector, clipStart, clipEnd) {
-
- // Ensure the clip range starts and ends on element nodes. This is possible
- // to do without changing the result set because non-element nodes can't
- // be or contain matches.
- while (clipStart !== clipEnd && clipStart.nodeType !== 1)
- clipStart = clipStart.nextSibling;
- while (clipStart !== clipEnd && clipEnd.nodeType !== 1)
- clipEnd = clipEnd.previousSibling;
- if (clipStart.nodeType !== 1)
- return []; // no top-level elements! start === end and it's not an element
-
- // resultsPlus includes matches all matches descended from contextNode,
- // including those that aren't in the clip range.
- var resultsPlus = DomUtils.findAll(contextNode, selector);
-
- // Filter the list of nodes to remove nodes that occur before start
- // or after end.
- return _.reject(resultsPlus, function (n) {
- return ! isElementInClipRange(n, clipStart, clipEnd);
- });
-};
-
-// Like `findAllClipped` but finds one element (or returns null).
-DomUtils.findClipped = function (contextNode, selector, clipStart, clipEnd) {
- var results = DomUtils.findAllClipped(
- contextNode, selector, clipStart, clipEnd);
- return (results.length ? results[0] : null);
-};
-
-// Executes `func` while ensuring that `element` has an ID. If `element`
-// doesn't have an ID, it is assigned `magicId` temporarily.
-// Calls func with a selector of the form "[id='...']" as an argument.
-var withElementId = function (element, magicId, func) {
- var didSetId = false;
- if (! element.getAttribute('id')) {
- element.setAttribute('id', magicId);
- didSetId = true;
- }
- try {
- var escapedNodeId = element.getAttribute('id').replace(/'/g, "\\$&");
- return func("[id='" + escapedNodeId + "']");
- } finally {
- if (didSetId)
- element.removeAttribute('id');
- }
-};
-
-var matchesSelectorMaybeClipped = function (element, contextNode, selector,
- clipStart, clipEnd) {
- var selecs = selector.split(',');
- for(var i = 0, N = selecs.length; i < N; i++) {
- var matches = withElementId(
- element, "DomUtils_matchesSelector_target",
- function (idSelector) {
- var trimmedSelector = selector.match(/\S.*?(?=\s*$)/)[0];
- // appending [id='foo'] to a selector with no whitespace ought to
- // simply restrict the set of possible outputs regardless of the
- // form of the selector.
- var doctoredSelector = trimmedSelector + idSelector;
- var result;
- if (clipStart)
- result = DomUtils.findClipped(contextNode, doctoredSelector,
- clipStart, clipEnd);
- else
- result = DomUtils.find(contextNode, doctoredSelector);
- return (result === element);
- });
-
- if (matches)
- return true;
- }
-
- return false;
-};
-
-// Check if `element` matches `selector`, scoped to `contextNode`.
-DomUtils.matchesSelector = function (element, contextNode, selector) {
- return matchesSelectorMaybeClipped(element, contextNode, selector);
-};
-
-// Check if `element` matches `selector`, scoped to `contextNode`,
-// clipped to ordered siblings `clipStart`..`clipEnd`.
-DomUtils.matchesSelectorClipped = function (element, contextNode, selector,
- clipStart, clipEnd) {
- return matchesSelectorMaybeClipped(element, contextNode, selector,
- clipStart, clipEnd);
-};
-
-// Returns 0 if the nodes are the same or either one contains the other;
-// otherwise, -1 if a comes before b, or else 1 if b comes before a in
-// document order.
-// Requires: `a` and `b` are element nodes in the same document tree.
-DomUtils.compareElementIndex = function (a, b) {
- // See http://ejohn.org/blog/comparing-document-position/
- if (a === b)
- return 0;
- if (a.compareDocumentPosition) {
- var n = a.compareDocumentPosition(b);
- return ((n & 0x18) ? 0 : ((n & 0x4) ? -1 : 1));
- } else {
- // Only old IE is known to not have compareDocumentPosition (though Safari
- // originally lacked it). Thankfully, IE gives us a way of comparing elements
- // via the "sourceIndex" property.
- if (a.contains(b) || b.contains(a))
- return 0;
- return (a.sourceIndex < b.sourceIndex ? -1 : 1);
- }
-};
-
-// Wrap `frag` as necessary to prepare it for insertion in
-// `container`. For example, if `frag` has TR nodes at top level,
-// and `container` is a TABLE, then it's necessary to wrap `frag` in
-// a TBODY to avoid IE quirks.
-//
-// `frag` is a DocumentFragment and will be modified in
-// place. `container` is a DOM element.
-//
-// Returns the number of levels of wrapping applied, which is
-// 0 if no wrapping was performed.
-DomUtils.wrapFragmentForContainer = function (frag, container) {
- if (container && container.nodeName === "TABLE" &&
- _.any(frag.childNodes,
- function (n) { return n.nodeName === "TR"; })) {
- // Avoid putting a TR directly in a TABLE without an
- // intervening TBODY, because it doesn't work in (old?) IE.
- // We do the same thing on all browsers for ease of testing
- // and debugging.
- var tbody = document.createElement("TBODY");
- tbody.appendChild(frag);
- frag.appendChild(tbody);
- return 1;
- }
- return 0;
-};
-
-// Return true if `node` is part of the global DOM document. Like
-// elementContains(document, node), except (1) it works for any node
-// (eg, text nodes), not just elements; (2) it works around browser
-// quirks that would otherwise come up when passing 'document' as
-// the first argument to elementContains.
-//
-// Returns true if node === document.
-DomUtils.isInDocument = function (node) {
- // Deal with all cases where node is not an element
- // node descending from the body first...
- if (node === document)
- return true;
-
- if (node.nodeType !== 1 /* Element */)
- node = node.parentNode;
- if (! (node && node.nodeType === 1))
- return false;
- if (node === document.body)
- return true;
-
- return DomUtils.elementContains(document.body, node);
-};
-
-// Return an HTML string representation of the nodes from
-// firstNode to lastNode, which must be siblings.
-// The tags representing firstNode and lastNode are included,
-// but not their parent or outer siblings.
-DomUtils.rangeToHtml = function (firstNode, lastNode) {
- var frag = document.createDocumentFragment();
- for(var n = firstNode, after = lastNode.nextSibling;
- n && n !== after;
- n = n.nextSibling)
- frag.appendChild(n.cloneNode(true)); // deep copy
- return DomUtils.fragmentToHtml(frag);
-};
-
-// Return an HTML string representation of node, including its
-// own open and close tag.
-DomUtils.outerHtml = function (node) {
- return DomUtils.rangeToHtml(node, node);
-};
-
-// Sets the value of an element, portably across browsers. There's a special
-// case for SELECT elements in IE.
-DomUtils.setElementValue = function (node, value) {
- // Try to assign the value.
- node.value = value;
- if (node.value === value || node.nodeName !== 'SELECT')
- return;
-
- // IE (all versions) appears to only let you assign SELECT values which
- // match valid OPTION values... and moreover, the OPTION value must be
- // explicitly given as an attribute, not just as the text. So we hunt for
- // the OPTION and select it.
- var options = DomUtils.findAll(node, 'option');
- for (var i = 0; i < options.length; ++i) {
- if (DomUtils.getElementValue(options[i]) === value) {
- options[i].selected = true;
- return;
- }
- }
-};
-
-// Gets the value of an element, portably across browsers. There's a special
-// case for SELECT elements in IE.
-DomUtils.getElementValue = function (node) {
- if (!quirks.selectValueMustBeFromAttribute)
- return node.value;
-
- if (node.nodeName === 'OPTION') {
- // Inspired by jQuery.valHooks.option.get.
- var val = node.attributes.value;
- return !val || val.specified ? node.value : node.text;
- } else if (node.nodeName === 'SELECT') {
- if (node.selectedIndex < 0)
- return null;
- return DomUtils.getElementValue(node.options[node.selectedIndex]);
- } else {
- return node.value;
- }
-};
-
-DomUtils.extractRange = function (start, end, optContainer) {
- var parent = start.parentNode;
- var before = start.previousSibling;
- var after = end.nextSibling;
- var n;
- while ((n = (before ? before.nextSibling : parent.firstChild)) &&
- (n !== after)) {
- if (optContainer)
- optContainer.appendChild(n);
- else
- parent.removeChild(n);
- }
-};
diff --git a/packages/domutils/domutils_tests.js b/packages/domutils/domutils_tests.js
deleted file mode 100644
index a0707f308e..0000000000
--- a/packages/domutils/domutils_tests.js
+++ /dev/null
@@ -1,31 +0,0 @@
-Tinytest.add("domutils - setElementValue", function (test) {
- var div = OnscreenDiv();
- div.node().appendChild(DomUtils.htmlToFragment(
- ("Foo Baz " +
- "Quux ")));
-
- var select = DomUtils.find(div.node(), 'select');
- test.equal(DomUtils.getElementValue(select), "Quux");
- _.each(["Foo", "Bar", "Quux"], function (value) {
- DomUtils.setElementValue(select, value);
- test.equal(DomUtils.getElementValue(select), value);
- });
-
- div.kill();
-});
-
-Tinytest.add("domutils - form id expando", function (test) {
- // See https://github.com/meteor/meteor/issues/604
-
- var div = OnscreenDiv();
- div.node().appendChild(DomUtils.htmlToFragment(
- ('')));
- var theInput = DomUtils.find(div.node(), 'input');
- var theForm = theInput.parentNode;
- var theDiv = theForm.parentNode;
-
- // test that this call doesn't throw an exception
- test.equal(DomUtils.matchesSelector(theForm, theDiv, 'form'), true);
-
- div.kill();
-});
diff --git a/packages/domutils/package.js b/packages/domutils/package.js
deleted file mode 100644
index 86323a768a..0000000000
--- a/packages/domutils/package.js
+++ /dev/null
@@ -1,28 +0,0 @@
-Package.describe({
- summary: "Utility functions for DOM manipulation",
- internal: true
-});
-
-Package.on_use(function (api) {
- // XXX
- // Doesn't actually require jQuery (but uses it if available).
- //
- // For now we are going to keep shipping jQuery with all apps
- // so as not to break existing apps, but any time now we will
- // cut this dependency.
- api.use('jquery', 'client');
-
- api.use('underscore', 'client');
-
- api.export('DomUtils', 'client');
- api.add_files('domutils.js', 'client');
-});
-
-Package.on_test(function (api) {
- api.use(['tinytest']);
- api.use(['domutils', 'test-helpers', 'underscore'], 'client');
-
- api.add_files([
- 'domutils_tests.js'
- ], 'client');
-});
diff --git a/packages/liverange/.gitignore b/packages/liverange/.gitignore
deleted file mode 100644
index 677a6fc263..0000000000
--- a/packages/liverange/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.build*
diff --git a/packages/liverange/liverange.js b/packages/liverange/liverange.js
deleted file mode 100644
index 42def4b4b5..0000000000
--- a/packages/liverange/liverange.js
+++ /dev/null
@@ -1,682 +0,0 @@
-// Stand back, I'm going to try SCIENCE.
-
-// Possible optimization: get rid of _startIndex/_endIndex and just search
-// the list. Not clear which strategy will be faster.
-
-// Possible extension: could allow zero-length ranges is some cases,
-// by encoding both 'enter' and 'leave' type events in the same list
-
-var canSetTextProps = (function () {
- // IE8 and earlier don't support expando attributes on text nodes,
- // but fortunately they are allowed on comments.
- var testElem = document.createTextNode("");
- var exception;
- try {
- testElem.test = 123;
- } catch (exception) { }
- if (testElem.test !== 123)
- return false;
-
- // IE9 and 10 have a weird issue with multiple text nodes next to
- // each other losing their expando attributes. Use the same
- // workaround as IE8. Not sure how to test this as a feature, so use
- // browser detection instead.
- // See https://github.com/meteor/meteor/issues/458
- if (document.documentMode)
- return false;
-
- return true;
-})();
-
-var wrapEndpoints = function (start, end) {
- if (canSetTextProps) {
- return [start, end];
- } else {
- // IE8 workaround: insert some empty comments.
- // Comments whose text is "IE" are stripped out
- // in cross-browser testing.
- if (start.nodeType === 3 /* text node */) {
- var placeholder = document.createComment("IE");
- start.parentNode.insertBefore(placeholder, start);
- start = placeholder;
- }
- if (end.nodeType === 3 /* text node */) {
- var placeholder = document.createComment("IE");
- end.parentNode.insertBefore(placeholder, end.nextSibling);
- end = placeholder;
- }
- return [start, end];
- }
-};
-
-
-// This is a constructor (invoke it as 'new LiveRange').
-//
-// Create a range, tagged 'tag', that includes start, end, and all
-// the nodes between them, and the children of all of those nodes,
-// but includes no other nodes. If there are other ranges tagged
-// 'tag' that contain this exact set of nodes, then: if inner is
-// false (the default), the new range will be outside all of them
-// (will contain all of them), or if inner is true, then it will be
-// inside all of them (be contained by all of them.) If there are no
-// other ranges tagged 'tag' that contain this exact set of nodes,
-// then 'inner' is ignored because the nesting of the new range with
-// respect to other ranges is uniquely determined. (Nesting of
-// ranges with different tags is undefined.)
-//
-// To track the range as it's relocated, some of the DOM nodes that
-// are part of the range will have an expando attribute set on
-// them. The name of the expando attribute will be the value of
-// 'tag', so pick something that won't collide.
-//
-// Instead of start and end, you can pass a document or
-// documentfragment for start and leave end undefined. Or you can
-// pass a node for start and leave end undefined, in which case end
-// === start. If start and end are distinct nodes, they must be
-// siblings.
-//
-// You can set any attributes you like on the returned LiveRange
-// object, with two exceptions. First, attribute names that start
-// with '_' are reserved. Second, the attribute 'tag' contains the
-// tag name of this range and mustn't be changed.
-//
-// It would be possible to add a fast path through this function
-// when caller can promise that there is no range that starts on
-// start that does not end by end, and vice versa. eg: when start
-// and end are the first and last child of their parent respectively
-// or when caller is building up the range tree from the inside
-// out. Let's wait for the profiler to tell us to add this.
-//
-// XXX Should eventually support LiveRanges where start === end
-// and start.parentNode is null.
-LiveRange = function (tag, start, end, inner) {
- if (start.nodeType === 11 /* DocumentFragment */) {
- end = start.lastChild;
- start = start.firstChild;
- } else {
- if (! start.parentNode)
- throw new Error("LiveRange start and end must have a parent");
- }
- end = end || start;
-
- this.tag = tag; // must be set before calling _ensureTag
-
- var endpoints = wrapEndpoints(start, end);
- start = this._ensureTag(endpoints[0]);
- end = this._ensureTag(endpoints[1]);
-
- // Decide at what indices in start[tag][0] and end[tag][1] we
- // should insert the new range.
- //
- // The start[tag][0] array lists the other ranges that start at
- // `start`, and we must choose an insertion index that puts us
- // inside the ones that end at later siblings, and outside the ones
- // that end at earlier siblings. The ones that end at the same
- // sibling (i.e. share both our start and end) we must be inside
- // or outside of depending on `inner`. The array lists ranges
- // from the outside in.
- //
- // The same logic applies to end[tag][1], which lists the other ranges
- // that happen to end at `end` from in the inside out.
- //
- // Liveranges technically start just before, and end just after, their
- // start and end nodes to which the liverange data is attached.
-
- var startIndex = findPosition(start[tag][0], true, end, start, inner);
- var endIndex = findPosition(end[tag][1], false, start, end, inner);
-
- // this._start is the node N such that we begin before N, but not
- // before the node before N in the preorder traversal of the
- // document (if there is such a node.) this._start[this.tag][0]
- // will be the list of all LiveRanges for which this._start is N,
- // including us, sorted in the order that the ranges start. and
- // finally, this._startIndex is the value such that
- // this._start[this.tag][0][this._startIndex] === this.
- //
- // Similarly for this._end, except it's the node N such that we end
- // after N, but not after the node after N in the postorder
- // traversal; and the data is stored in this._end[this.tag][1], and
- // it's sorted in the order that the ranges end.
-
- // Set this._start, this._end, this._startIndex, this._endIndex
- this._insertEntries(start, 0, startIndex, [this]);
- this._insertEntries(end, 1, endIndex, [this]);
-};
-
-var findPosition = function(ranges, findEndNotStart, edge, otherEdge, inner) {
- var index;
- // For purpose of finding where we belong in start[tag][0],
- // walk the array and determine where we start to see ranges
- // end at `end` (==edge) or earlier. For the purpose of finding
- // where we belong in end[tag][1], walk the array and determine
- // where we start to see ranges start at `start` (==edge) or
- // earlier. In both cases, we slide a sibling pointer backwards
- // looking for `edge`, though the details are slightly different.
- //
- // Use `inner` to take first or last candidate index for insertion.
- // Candidate indices are: Right before a range whose edge is `edge`
- // (i.e., a range with same start and end as we are creating),
- // or the index where ranges start to have edges earlier than `edge`
- // (treating the end of the list as such an index). We detect the
- // latter case when `n` hits `edge` without hitting the edge of the
- // current range; that is, it is about to move past `edge`. This is
- // always an appropriate time to stop.
- //
- // Joint traversal of the array and DOM should be fast. The most
- // expensive thing to happen would be a single walk from lastChild
- // to end looking for range ends, or from end to start looking for
- // range starts.
- //
- // invariant: n >= edge ("n is after, or is, edge")
- var initialN = (findEndNotStart ? edge.parentNode.lastChild : otherEdge);
- var takeFirst = (findEndNotStart ? ! inner : inner);
- for(var i=0, n=initialN; i<=ranges.length; i++) {
- var r = ranges[i];
- var curEdge = r && (findEndNotStart ? r._end : r._start);
- while (n !== curEdge && n !== edge) {
- n = n.previousSibling;
- }
- if (curEdge === edge) {
- index = i;
- if (takeFirst) break;
- } else if (n === edge) {
- index = i;
- break;
- }
- }
- return index;
-};
-
-LiveRange.prototype._ensureTag = function (node) {
- if (!(this.tag in node))
- node[this.tag] = [[], []];
- return node;
-};
-
-var canDeleteExpandos = (function() {
- // IE7 can't remove expando attributes from DOM nodes with
- // delete. Instead you must remove them with node.removeAttribute.
- var node = document.createElement("DIV");
- var exception;
- var result = false;
- try {
- node.test = 12;
- delete node.test;
- result = true;
- } catch (exception) { }
- return result;
-})();
-
-LiveRange._cleanNode = function (tag, node, force) {
- var data = node[tag];
- if (data && (!(data[0].length + data[1].length) || force)) {
- if (canDeleteExpandos)
- delete node[tag];
- else
- node.removeAttribute(tag);
- }
-};
-
-// Delete a LiveRange. This is analogous to removing a DOM node from
-// its parent -- it will no longer appear when traversing the tree
-// with visit().
-//
-// On modern browsers there is no requirement to delete LiveRanges on
-// defunct nodes. They will be garbage collected just like any other
-// object. However, on old versions of IE, you probably do need to
-// manually remove all ranges because IE can't GC reference cycles
-// through the DOM.
-//
-// Pass true for `recursive` to also destroy all descendent ranges.
-LiveRange.prototype.destroy = function (recursive) {
- var self = this;
-
- if (recursive) {
- // recursive case: destroy all descendent ranges too
- // (more efficient than actually recursing)
-
- this.visit(function(isStart, range) {
- if (isStart) {
- range._start = null;
- range._end = null;
- }
- }, function(isStart, node) {
- if (! isStart) {
- // when leaving a node, force-clean its children
- for(var n = node.firstChild; n; n = n.nextSibling) {
- LiveRange._cleanNode(self.tag, n, true);
- }
- }
- });
-
- this._removeEntries(this._start, 0, this._startIndex);
- this._removeEntries(this._end, 1, 0, this._endIndex + 1);
-
- if (this._start !== this._end) {
- // force-clean the top-level nodes in this, besides _start and _end
- for(var n = this._start.nextSibling;
- n !== this._end;
- n = n.nextSibling) {
- LiveRange._cleanNode(self.tag, n, true);
- }
-
- // clean ends on this._start and starts on this._end
- if (this._start[self.tag])
- this._removeEntries(this._start, 1);
- if (this._end[self.tag])
- this._removeEntries(this._end, 0);
- }
-
- this._start = this._end = null;
-
- } else {
- this._removeEntries(this._start, 0, this._startIndex, this._startIndex + 1);
- this._removeEntries(this._end, 1, this._endIndex, this._endIndex + 1);
- this._start = this._end = null;
- }
-};
-
-// Return the first node in the range (in preorder traversal)
-LiveRange.prototype.firstNode = function () {
- return this._start;
-};
-
-// Return the last node in the range (in postorder traversal)
-LiveRange.prototype.lastNode = function () {
- return this._end;
-};
-
-// Return the node that immediately contains this LiveRange, that is,
-// the parentNode of firstNode and lastNode.
-LiveRange.prototype.containerNode = function() {
- return this._start.parentNode;
-};
-
-// Walk through the current contents of a LiveRange, enumerating
-// either the contained ranges (with the same tag as this range),
-// the contained elements, or both.
-//
-// visitRange(isStart, range) is invoked for each range
-// start-point or end-point that we encounter as we walk the range
-// stored in 'this' (not counting the endpoints of 'this' itself.)
-// visitNode(isStart, node) is similar but for nodes. Both
-// functions are optional.
-//
-// If you return false (i.e. a value === false) from visitRange
-// or visitNode when isStart is true, the children of that range
-// or node are skipped, and the next callback will be the same
-// range or node with isStart false.
-//
-// If you create or destroy ranges with this tag from a visitation
-// function, results are undefined!
-LiveRange.prototype.visit = function(visitRange, visitNode) {
- visitRange = visitRange || function() {};
- visitNode = visitNode || function() {};
-
- var tag = this.tag;
-
- var recurse = function(start, end, startRangeSkip) {
- var startIndex = startRangeSkip || 0;
- var after = end.nextSibling;
- for(var n = start; n && n !== after; n = n.nextSibling) {
- var startData = n[tag] && n[tag][0];
- if (startData && startIndex < startData.length) {
- // immediate child range that starts with n
- var range = startData[startIndex];
- // be robust if visitRange mutates _start or _end;
- // useful in destroy(true)
- var rangeStart = range._start;
- var rangeEnd = range._end;
- if (visitRange(true, range) !== false)
- recurse(rangeStart, rangeEnd, startIndex+1);
- visitRange(false, range);
- n = rangeEnd;
- } else {
- // bare node
- if (visitNode(true, n) !== false && n.firstChild)
- recurse(n.firstChild, n.lastChild);
- visitNode(false, n);
- }
- startIndex = 0;
- }
- };
-
- recurse(this._start, this._end, this._startIndex + 1);
-};
-
-// startEnd === 0 for starts, 1 for ends
-LiveRange.prototype._removeEntries =
- function(node, startEnd, i, j)
-{
- var entries = node[this.tag][startEnd];
- i = i || 0;
- j = (j || j === 0) ? j : entries.length;
- var removed = entries.splice(i, j-i);
- // fix up remaining ranges (not removed ones)
- for(var a = i; a < entries.length; a++) {
- if (startEnd) entries[a]._endIndex = a;
- else entries[a]._startIndex = a;
- }
-
- // potentially remove empty liverange data
- if (! entries.length) {
- LiveRange._cleanNode(this.tag, node);
- }
-
- return removed;
-};
-
-LiveRange.prototype._insertEntries =
- function(node, startEnd, i, newRanges)
-{
- // insert the new ranges and "adopt" them by setting node pointers
- var entries = node[this.tag][startEnd];
- Array.prototype.splice.apply(entries, [i, 0].concat(newRanges));
- for(var a=i; a < entries.length; a++) {
- if (startEnd) {
- entries[a]._end = node;
- entries[a]._endIndex = a;
- } else {
- entries[a]._start = node;
- entries[a]._startIndex = a;
- }
- }
-};
-
-// Replace the contents of this range with the provided
-// DocumentFragment. Returns the previous contents as a
-// DocumentFragment.
-//
-// "The right thing happens" with child LiveRanges:
-// - If there were child LiveRanges inside us, they will end up in
-// the returned DocumentFragment.
-// - If the input DocumentFragment has LiveRanges, they will become
-// our children.
-//
-// It is illegal for newFrag to be empty.
-LiveRange.prototype.replaceContents = function (newFrag) {
- if (! newFrag.firstChild)
- throw new Error("replaceContents requires non-empty fragment");
-
- return this.operate(function(oldStart, oldEnd) {
- // Insert new fragment
- oldStart.parentNode.insertBefore(newFrag, oldStart);
-
- // Pull out departing fragment
- // Possible optimization: use W3C Ranges on browsers that support them
- var retFrag = oldStart.ownerDocument.createDocumentFragment();
- var walk = oldStart;
- while (true) {
- var next = walk.nextSibling;
- retFrag.appendChild(walk);
- if (walk === oldEnd)
- break;
- walk = next;
- if (!walk)
- throw new Error("LiveRanges must begin and end on siblings in order");
- }
-
- return retFrag;
- });
-};
-
-
-// Perform a user-specified DOM mutation on the contents of this range.
-//
-// `func` is called with two parameters, `oldStart` and `oldEnd`, equal
-// to the original firstNode() and lastNode() of this range. `func` is allowed
-// to perform arbitrary operations on the sequence of nodes from `oldStart`
-// to `oldEnd` and on child ranges of this range. `func` may NOT call methods
-// on this range itself or otherwise rely on the existence of this range and
-// enclosing ranges. `func` must leave at least one node to become the new
-// contents of this range.
-//
-// The return value of `func` is returned.
-//
-// This method is a generalization of replaceContents that works by
-// temporarily removing this LiveRange from the DOM and restoring it after
-// `func` has been called.
-LiveRange.prototype.operate = function (func) {
- // boundary nodes of departing fragment
- var oldStart = this._start;
- var oldEnd = this._end;
-
- // pull off outer liverange data
- var outerStarts =
- this._removeEntries(oldStart, 0, 0, this._startIndex + 1);
- var outerEnds =
- this._removeEntries(oldEnd, 1, this._endIndex);
-
- var containerNode = oldStart.parentNode;
- var beforeNode = oldStart.previousSibling;
- var afterNode = oldEnd.nextSibling;
-
- var ret = null;
-
- // perform user-specifiedDOM manipulation
- ret = func(oldStart, oldEnd);
-
- // see what we've got...
-
- var newStart =
- beforeNode ? beforeNode.nextSibling : containerNode.firstChild;
- var newEnd =
- afterNode ? afterNode.previousSibling : containerNode.lastChild;
-
- if (! newStart || newStart === afterNode) {
- throw new Error("Ranges must contain at least one element");
- }
-
- // wrap endpoints if necessary
- var newEndpoints = wrapEndpoints(newStart, newEnd);
- newStart = this._ensureTag(newEndpoints[0]);
- newEnd = this._ensureTag(newEndpoints[1]);
-
- // put the outer liveranges back
-
- this._insertEntries(newStart, 0, 0, outerStarts);
- this._insertEntries(newEnd, 1, newEnd[this.tag][1].length, outerEnds);
-
- return ret;
-};
-
-// Move all liverange data represented in the DOM from sourceNode to
-// targetNode. targetNode must be capable of receiving liverange tags
-// (for example, a node that has been the first or last node of a liverange
-// before; not a text node in IE).
-//
-// This is a low-level operation suitable for moving liveranges en masse
-// from one DOM tree to another, where transplantTag is called on every
-// pair of nodes such that targetNode takes the place of sourceNode.
-LiveRange.transplantTag = function(tag, targetNode, sourceNode) {
-
- if (! sourceNode[tag])
- return;
-
- // copy data pointer
- targetNode[tag] = sourceNode[tag];
- sourceNode[tag] = null;
-
- var starts = targetNode[tag][0];
- var ends = targetNode[tag][1];
-
- // fix _start and _end pointers
- for(var i=0;i 0 &&
- this._start[this.tag][0][this._startIndex - 1]._end === this._end) {
- // immediately enclosing range wraps same nodes, so can't extract because
- // it would empty it.
- return null;
- }
-
- var before = this._start.previousSibling;
- var after = this._end.nextSibling;
- var parent = this._start.parentNode;
-
- if (this._startIndex > 0) {
- // must be a later node where outer ranges that start here end;
- // move their starts to after
- this._ensureTag(after);
- this._insertEntries(after, 0, 0,
- this._removeEntries(this._start, 0, 0,
- this._startIndex));
- }
-
- if (this._endIndex < this._end[this.tag][1].length - 1) {
- // must be an earlier node where outer ranges that end here
- // start; move their ends to before
- this._ensureTag(before);
- this._insertEntries(before, 1, before[this.tag][1].length,
- this._removeEntries(this._end, 1,
- this._endIndex + 1));
- }
-
- var result = document.createDocumentFragment();
-
- for(var n;
- n = before ? before.nextSibling : parent.firstChild,
- n && n !== after;)
- result.appendChild(n);
-
- return result;
-};
-
-// Find the immediately enclosing parent range of this range, or
-// null if this range has no enclosing ranges.
-//
-// If `withSameContainer` is true, we stop looking when we reach
-// this range's container node (the parent of its endpoints) and
-// only return liveranges whose first and last nodes are siblings
-// of this one's.
-LiveRange.prototype.findParent = function(withSameContainer) {
- var result = enclosingRangeSearch(this.tag, this._end, this._endIndex);
- if (result)
- return result;
-
- if (withSameContainer)
- return null;
-
- return LiveRange.findRange(this.tag, this.containerNode());
-};
-
-// Find the nearest enclosing range containing `node`, if any.
-LiveRange.findRange = function(tag, node) {
- var result = enclosingRangeSearch(tag, node);
- if (result)
- return result;
-
- if (! node.parentNode)
- return null;
-
- return LiveRange.findRange(tag, node.parentNode);
-};
-
-var enclosingRangeSearch = function(tag, end, endIndex) {
- // Search for an enclosing range, at the same level,
- // starting at node `end` or after the range whose
- // position in the end array of `end` is `endIndex`.
- // The search works by scanning forwards for range ends
- // while skipping over ranges whose starts we encounter.
-
- if (typeof endIndex === "undefined")
- endIndex = -1;
-
- if (end[tag] && endIndex + 1 < end[tag][1].length) {
- // immediately enclosing range ends at same node as this one
- return end[tag][1][endIndex + 1];
- }
-
- var node = 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._endIndex + 1;
- }
- if (node[tag] && endIndex < node[tag][1].length)
- return node[tag][1][endIndex];
- node = node.nextSibling;
- }
-
- return null;
-};
diff --git a/packages/liverange/liverange_test_helpers.js b/packages/liverange/liverange_test_helpers.js
deleted file mode 100644
index f9c436e961..0000000000
--- a/packages/liverange/liverange_test_helpers.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// checks that ranges balance and that node and index pointers are
-// correct. if both of these things are true, then everything
-// contained by 'range' must be a valid subtree. (assuming that
-// visit() is actually working.)
-check_liverange_integrity = function (range) {
- var stack = [];
-
- var check_node = function (node) {
- var data = node[range.tag] || [[], []];
- for (var i = 0; i < data[0].length; i++) {
- if (data[0][i]._start !== node)
- throw new Error("integrity check failed - incorrect _start");
- if (data[0][i]._startIndex !== i)
- throw new Error("integrity check failed - incorrect _startIndex");
- }
- for (var i = 0; i < data[1].length; i++) {
- if (data[1][i]._end !== node)
- throw new Error("integrity check failed - incorrect _end");
- if (data[1][i]._endIndex !== i)
- throw new Error("integrity check failed - incorrect _endIndex");
- }
- };
-
- range.visit(function (isStart, range) {
- if (isStart)
- stack.push(range);
- else
- if (range !== stack.pop())
- throw new Error("integrity check failed - unbalanced range");
- }, function (isStart, node) {
- if (isStart) {
- check_node(node);
- stack.push(node);
- }
- else
- if (node !== stack.pop())
- throw new Error("integrity check failed - unbalanced node");
- });
-
- if (stack.length)
- throw new Error("integrity check failed - missing close tags");
-};
diff --git a/packages/liverange/liverange_tests.js b/packages/liverange/liverange_tests.js
deleted file mode 100644
index 10088aa832..0000000000
--- a/packages/liverange/liverange_tests.js
+++ /dev/null
@@ -1,621 +0,0 @@
-/******************************************************************************/
-
-var create = function (id, start, end, inner, tag) {
- var ret = new LiveRange(tag || 'a', start, end, inner);
- ret.id = id;
- return ret;
-};
-
-var frag = function (html) {
- var ret = document.createDocumentFragment();
- var q = $(html);
- for (var i = 0; i < q.length; i++)
- ret.appendChild(q[i]);
- return ret;
-};
-
-// takes ranges or fragments. tag is used only for fragments.
-var dump = function (what, tag) {
- var ret = "";
-
- var emit = function (isStart, obj) {
- ret += (isStart ? "<": "") + obj.id + ">";
- };
-
- if (typeof what === 'object' && what.nodeType === 11 /* DocumentFragment */) {
- if (what.firstChild) {
- var range = new LiveRange(tag || 'a', what);
- range.visit(emit, emit);
- range.destroy();
- }
- } else {
- emit(true, what);
- what.visit(emit, emit);
- emit(false, what);
- }
-
- return ret;
-};
-
-// actual can be a range or a fragment
-var assert_dump = function (test, expected, actual, tag) {
- test.equal(dump(actual), expected, "Tree doesn't match");
- if (actual instanceof LiveRange)
- check_liverange_integrity(actual);
- else {
- if (actual.firstChild) {
- var range = new LiveRange(tag || 'a', actual);
- check_liverange_integrity(range);
- range.destroy();
- }
- }
-};
-
-var contained_ranges = function (range) {
- var result = {range: range, children: []};
- var stack = [result];
-
- range.visit(function (isStart, range) {
- if (isStart) {
- var record = {range: range, children: []};
- stack[stack.length - 1].children.push(record);
- stack.push(record);
- } else
- if (stack.pop().range !== range)
- throw new Error("Overlapping ranges detected");
- });
-
- return result;
-};
-
-var assert_contained = function (r, expected) {
- // one day, fold in the above function (use visit() directly)
- var actual = contained_ranges(r);
-
- var traverse = function (exp, act) {
- if (exp.range !== act.range)
- throw new Error("contained(): range doesn't match");
- if (exp.children.length !== act.children.length)
- throw new Error("contained(): different tree shape");
- for (var i = 0; i < exp.children.length; i++)
- traverse(exp.children[i], act.children[i]);
- };
-
- traverse(expected, actual);
-};
-
-/******************************************************************************/
-
-Tinytest.add("liverange - single node", function (test) {
- var f = frag("
");
- var r_a = create("a", f);
- test.instanceOf(r_a, LiveRange);
- assert_dump(test, "<1>1> ", r_a);
- assert_dump(test, "<1>1> ", f);
- assert_contained(r_a, {range: r_a, children: []});
-
- var r_b = create("b", f);
- assert_dump(test, "<1>1> ", r_a);
- assert_dump(test, "<1>1> ", r_b);
- assert_dump(test, "<1>1> ", f);
- assert_contained(r_a, {range: r_a, children: []});
- assert_contained(r_b, {range: r_b, children: [{range: r_a, children: []}]});
- test.equal(r_a.firstNode(), f.firstChild);
- test.equal(r_a.lastNode(), f.lastChild);
- test.equal(r_b.firstNode(), f.firstChild);
- test.equal(r_b.lastNode(), f.lastChild);
-
- var ret1 = r_a.replaceContents(frag("
"), true);
- test.equal(ret1.nodeType, 11 /* DocumentFragment */);
- assert_dump(test, "<1>1>", ret1);
- assert_dump(test, "<2>2> ", r_a);
- assert_dump(test, "<2>2> ", r_b);
- assert_dump(test, "<2>2> ", f);
-
- var ret2 = r_b.replaceContents(frag("
"), true);
- assert_dump(test, "<2>2> ", ret2);
- assert_dump(test, "<2>2> ", r_a);
- assert_dump(test, "<3>3> ", r_b);
- assert_dump(test, "<3>3> ", f);
-
- r_a.destroy();
- assert_dump(test, "<2>2>", ret2);
-
- var r_c = create("c", f);
- var r_d = create("d", f);
- var r_e = create("e", f);
- assert_dump(test, "<3>3> ", r_c);
- assert_dump(test, "<3>3> ", r_d);
- assert_dump(test, "<3>3> ", r_e);
- assert_dump(test, "<1>1>", ret1);
- assert_dump(test, "<3>3> ", r_b);
-
- r_d.destroy();
- assert_dump(test, "<3>3> ", r_b);
- assert_dump(test, "<3>3> ", r_c);
- assert_dump(test, "<3>3> ", r_e);
- assert_dump(test, "<1>1>", ret1);
-
- assert_contained(r_e,
- {range: r_e,
- children: [{range: r_c,
- children: [{range: r_b, children: []}]}]});
-
- test.equal(r_b.firstNode(), f.firstChild);
- test.equal(r_b.lastNode(), f.lastChild);
- test.equal(r_c.firstNode(), f.firstChild);
- test.equal(r_c.lastNode(), f.lastChild);
- test.equal(r_e.firstNode(), f.firstChild);
- test.equal(r_e.lastNode(), f.lastChild);
-
- r_b.destroy();
- assert_dump(test, "<3>3> ", r_c);
- assert_dump(test, "<3>3> ", r_e);
-
- r_e.destroy();
- assert_dump(test, "<3>3> ", r_c);
-
-});
-
-Tinytest.add("liverange - empty replace", function (test) {
- var f,r;
-
- f = frag("
");
- r = create("z", f);
- test.throws(function() {
- r.replaceContents(frag(""));
- });
-
- f = frag("
");
- r = create("z", f.childNodes[1]);
- assert_dump(test, "<1>1><2>2> <3>3>", f);
- test.throws(function() {
- r.replaceContents(frag(""));
- });
-});
-
-Tinytest.add("liverange - multiple nodes", function (test) {
- var f = frag("
");
- assert_dump(test, "<1>1><2>2><3>3><4>4><5>5>", f);
-
- var r_a = create("a", f.childNodes[2], f.childNodes[3]);
- assert_dump(test, "<1>1><2>2><3>3><4>4> <5>5>", f);
- assert_dump(test, "<3>3><4>4> ", r_a);
-
- var r_b = create("b", f.childNodes[3], f.childNodes[3]);
- assert_dump(test, "<1>1><2>2><3>3><4>4> <5>5>", f);
- assert_dump(test, "<3>3><4>4> ", r_a);
- assert_dump(test, "<4>4> ", r_b);
-
- var r_c = create("c", f.childNodes[2], f.childNodes[3]);
- assert_dump(test, "<1>1><2>2><3>3><4>4> <5>5>", f);
- assert_dump(test, "<3>3><4>4> ", r_a);
- assert_dump(test, "<4>4> ", r_b);
- assert_dump(test, "<3>3><4>4> ", r_c);
-
- var r_d = create("d", f.childNodes[3], f.childNodes[3]);
- assert_dump(test, "<1>1><2>2><3>3><4>4> <5>5>", f);
- assert_dump(test, "<3>3><4>4> ", r_a);
- assert_dump(test, "<4>4> ", r_b);
- assert_dump(test, "<3>3><4>4> ", r_c);
- assert_dump(test, "<4>4> ", r_d);
-
- var r_e = create("e", f.childNodes[2], f.childNodes[2]);
- assert_dump(test, "<1>1><2>2><3>3> <4>4> <5>5>", f);
- assert_dump(test, "<3>3> <4>4> ", r_a);
- assert_dump(test, "<4>4> ", r_b);
- assert_dump(test, "<3>3> <4>4> ", r_c);
- assert_dump(test, "<4>4> ", r_d);
- assert_dump(test, "<3>3> ", r_e);
-
- var r_f = create("f", f.childNodes[2], f.childNodes[3]);
- assert_dump(test, "<1>1><2>2><3>3> <4>4> <5>5>", f);
- assert_dump(test, "<3>3> <4>4> ", r_a);
- assert_dump(test, "<4>4> ", r_b);
- assert_dump(test, "<3>3> <4>4> ", r_c);
- assert_dump(test, "<4>4> ", r_d);
- assert_dump(test, "<3>3> ", r_e);
- assert_dump(test, "<3>3> <4>4> ", r_f);
-
- assert_contained(r_f, {range: r_f, children: [{range: r_c, children: [{range: r_a, children: [{range: r_e, children: []},{range: r_d, children: [{range: r_b, children: []}]}]}]}]});
-
- var r_g = create("g", f.childNodes[0], f.childNodes[3]);
- var r_h = create("h", f.childNodes[0], f.childNodes[3]);
- var r_i = create("i", f.childNodes[1], f.childNodes[3]);
- assert_dump(test, "<1>1><2>2><3>3> <4>4> <5>5>", f);
- assert_dump(test, "<3>3> <4>4> ", r_a);
- assert_dump(test, "<4>4> ", r_b);
- assert_dump(test, "<3>3> <4>4> ", r_c);
- assert_dump(test, "<4>4> ", r_d);
- assert_dump(test, "<3>3> ", r_e);
- assert_dump(test, "<3>3> <4>4> ", r_f);
- assert_dump(test, "<1>1><2>2><3>3> <4>4> ", r_g);
- assert_dump(test, "<1>1><2>2><3>3> <4>4> ", r_h);
- assert_dump(test, "<2>2><3>3> <4>4> ", r_i);
-
- var f2 = frag("
");
- f2.childNodes[1].appendChild(f);
- assert_dump(test, "", f);
- assert_dump(test, "<6>6><7><1>1><2>2><3>3> <4>4> <5>5>7><8>8>", f2);
- assert_dump(test, "<3>3> <4>4> ", r_a);
- assert_dump(test, "<4>4> ", r_b);
- assert_dump(test, "<3>3> <4>4> ", r_c);
- assert_dump(test, "<4>4> ", r_d);
- assert_dump(test, "<3>3> ", r_e);
- assert_dump(test, "<3>3> <4>4> ", r_f);
- assert_dump(test, "<1>1><2>2><3>3> <4>4> ", r_g);
- assert_dump(test, "<1>1><2>2><3>3> <4>4> ", r_h);
- assert_dump(test, "<2>2><3>3> <4>4> ", r_i);
-
- var r_j = create("j", f2.childNodes[1], f2.childNodes[2]);
- var r_k = create("k", f2.childNodes[0], f2.childNodes[2]);
- var r_l = create("l", f2.childNodes[0], f2.childNodes[2]);
- assert_dump(test, "<6>6><7><1>1><2>2><3>3> <4>4> <5>5>7><8>8> ", f2);
-
- var f3 = frag("
");
- var r_m = create("m", f3.childNodes[0], f3.childNodes[2]);
- var r_n = create("n", f3.childNodes[0], f3.childNodes[0]);
- var r_o = create("o", f3.childNodes[0], f3.childNodes[0]);
- assert_dump(test, "<9>9> <10>10><11>11> ", f3);
-
- var ret1 = r_i.replaceContents(f3, true);
- assert_dump(test, "", f3);
- assert_dump(test, "<2>2><3>3> <4>4> ", ret1);
- assert_dump(test, "<6>6><7><1>1><9>9> <10>10><11>11> <5>5>7><8>8> ", f2);
- assert_dump(test, "<3>3> <4>4> ", r_a);
- assert_dump(test, "<4>4> ", r_b);
- assert_dump(test, "<3>3> <4>4> ", r_c);
- assert_dump(test, "<4>4> ", r_d);
- assert_dump(test, "<3>3> ", r_e);
- assert_dump(test, "<3>3> <4>4> ", r_f);
- assert_dump(test, "<1>1><9>9> <10>10><11>11> ", r_g);
- assert_dump(test, "<1>1><9>9> <10>10><11>11> ", r_h);
- assert_dump(test, "<9>9> <10>10><11>11> ",r_i);
- assert_dump(test, "<7><1>1><9>9> <10>10><11>11> <5>5>7><8>8> ", r_j);
- assert_dump(test, "<6>6><7><1>1><9>9> <10>10><11>11> <5>5>7><8>8> ", r_k);
- assert_dump(test, "<6>6><7><1>1><9>9> <10>10><11>11> <5>5>7><8>8> ", r_l);
-
- r_h.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9> <10>10><11>11> <5>5>7><8>8> ", f2);
- r_m.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9> <10>10><11>11> <5>5>7><8>8> ", f2);
- r_n.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9> <10>10><11>11> <5>5>7><8>8> ", f2);
- r_j.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9> <10>10><11>11> <5>5>7><8>8> ", f2);
- r_o.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9><10>10><11>11> <5>5>7><8>8> ", f2);
- r_g.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9><10>10><11>11> <5>5>7><8>8> ", f2);
- r_l.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9><10>10><11>11> <5>5>7><8>8> ", f2);
- r_i.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9><10>10><11>11><5>5>7><8>8> ", f2);
- r_k.destroy();
- assert_dump(test, "<6>6><7><1>1><9>9><10>10><11>11><5>5>7><8>8>", f2);
-});
-
-Tinytest.add("liverange - deep visit", function (test) {
-
- var f = frag("");
-
- var dive = function (f, count) {
- for (var i = 0; i < count; i ++)
- f = f.firstChild;
- return f;
- };
-
- var r_a = create("a", dive(f, 5), dive(f, 5));
- var r_b = create("b", dive(f, 3), dive(f, 3));
- var r_c = create("c", dive(f, 2), dive(f, 2));
- var r_d = create("d", f);
-
- assert_dump(test, "<1><2><3><4><5>5> 4>3> 2> 1> ",
- f);
-
- assert_contained(r_d,
- {range: r_d, children: [{range: r_c, children: [{range: r_b, children: [{range: r_a, children: []}]}]}]});
-});
-
-Tinytest.add("liverange - create inner", function (test) {
- // Basics
-
- var f = frag("
");
- assert_dump(test, "<1>1><2>2><3>3><4>4><5>5>", f);
-
- var r_a = create("a", f.childNodes[2], f.childNodes[4], true);
- assert_dump(test, "<1>1><2>2><3>3><4>4><5>5> ", f);
-
- var r_b = create("b", f.childNodes[2], f.childNodes[4], true);
- assert_dump(test, "<1>1><2>2><3>3><4>4><5>5> ", f);
-
- var r_c = create("c", f.childNodes[2], f.childNodes[4]);
- assert_dump(test, "<1>1><2>2><3>3><4>4><5>5> ", f);
-
- // [{[a] [b]}]
-
- var r_d = create("d", f.childNodes[0], f.childNodes[0]);
- var r_e = create("e", f.childNodes[1], f.childNodes[1]);
- var r_f = create("f", f.childNodes[0], f.childNodes[1]);
- assert_dump(test, "<1>1> <2>2> <3>3><4>4><5>5> ", f);
-
- var r_g = create("g", f.childNodes[0], f.childNodes[1], true);
- assert_dump(test, "<1>1> <2>2> <3>3><4>4><5>5> ", f);
-
- var r_h = create("h", f.childNodes[0], f.childNodes[1]);
- assert_dump(test, "<1>1> <2>2> <3>3><4>4><5>5> ", f);
-
- var r_i = create("i", f.childNodes[0], f.childNodes[1], true);
- assert_dump(test, "<1>1> <2>2> <3>3><4>4><5>5> ", f);
-
- var r_j = create("j", f.childNodes[0], f.childNodes[0], true);
- assert_dump(test, "<1>1> <2>2> <3>3><4>4><5>5> ", f);
-
- var r_k = create("k", f.childNodes[0], f.childNodes[0]);
- assert_dump(test, "<1>1> <2>2> <3>3><4>4><5>5> ", f);
-
- var r_l = create("l", f.childNodes[0], f.childNodes[1], true);
- assert_dump(test, "<1>1> <2>2> <3>3><4>4><5>5> ", f);
- assert_dump(test, "<3>3><4>4><5>5> ", r_c);
- assert_dump(test, "<3>3><4>4><5>5> ", r_b);
- assert_dump(test, "<3>3><4>4><5>5> ", r_a);
- assert_dump(test, "<1>1> ", r_d);
- assert_dump(test, "<2>2> ", r_e);
- assert_dump(test, "<1>1> <2>2> ", r_f);
- assert_dump(test, "<1>1> <2>2> ", r_g);
- assert_dump(test, "<1>1> <2>2> ", r_h);
- assert_dump(test, "<1>1> <2>2> ", r_i);
- assert_dump(test, "<1>1> ", r_j);
- assert_dump(test, "<1>1> ", r_k);
- assert_dump(test, "<1>1> <2>2> ", r_l);
-
- // [{a b [c]}]
- f = frag("
");
- r_a = create("a", f.childNodes[2], f.childNodes[2]);
- r_b = create("b", f.childNodes[0], f.childNodes[2]);
- r_c = create("c", f.childNodes[0], f.childNodes[2], true);
- assert_dump(test, "<1>1><2>2><3>3> ", f);
-
- // [{[a] b c}]
-
- f = frag("
");
- r_a = create("a", f.childNodes[0], f.childNodes[0]);
- r_b = create("b", f.childNodes[0], f.childNodes[2]);
- r_c = create("c", f.childNodes[0], f.childNodes[2], true);
- assert_dump(test, "<1>1> <2>2><3>3> ", f);
-
- // [{[a b] c}]
-
- f = frag("
");
- r_a = create("a", f.childNodes[0], f.childNodes[1]);
- r_b = create("b", f.childNodes[0], f.childNodes[2]);
- r_c = create("c", f.childNodes[0], f.childNodes[2], true);
- assert_dump(test, "<1>1><2>2> <3>3> ", f);
-
- // Cases where start and end have no common ranges, and so the
- // balance counter will have to run
-
- f = frag("
");
- r_a = create("a", f.childNodes[0], f.childNodes[0]);
- r_b = create("b", f.childNodes[0], f.childNodes[2]);
- assert_dump(test, "<1>1> <2>2><3>3> ", f);
-
- f = frag("
");
- r_a = create("a", f.childNodes[0], f.childNodes[2]);
- r_b = create("b", f.childNodes[0], f.childNodes[0]);
- assert_dump(test, "<1>1> <2>2><3>3> ", f);
-
- f = frag("
");
- r_a = create("a", f.childNodes[2], f.childNodes[2]);
- r_b = create("b", f.childNodes[0], f.childNodes[2]);
- assert_dump(test, "<1>1><2>2><3>3> ", f);
-
- f = frag("
");
- r_a = create("a", f.childNodes[0], f.childNodes[2]);
- r_b = create("b", f.childNodes[2], f.childNodes[2]);
- assert_dump(test, "<1>1><2>2><3>3> ", f);
-
- f = frag("
");
- r_a = create("a", f.childNodes[0], f.childNodes[0]);
- r_b = create("b", f.childNodes[0], f.childNodes[0]);
- r_c = create("c", f.childNodes[2], f.childNodes[2]);
- r_d = create("d", f.childNodes[2], f.childNodes[2]);
- r_e = create("e", f.childNodes[0], f.childNodes[2]);
- assert_dump(test, "<1>1> <2>2><3>3> ", f);
-
- f = frag("
");
- r_a = create("a", f.childNodes[0], f.childNodes[0]);
- r_b = create("b", f.childNodes[0], f.childNodes[0]);
- r_c = create("c", f.childNodes[2], f.childNodes[2]);
- r_e = create("e", f.childNodes[0], f.childNodes[2]);
- assert_dump(test, "<1>1> <2>2><3>3> ", f);
-
- try_all_permutations(
- function () {
- f = frag("
");
- },
- [
- function () { create("a", f.childNodes[1], f.childNodes[2]); },
- function () { create("b", f.childNodes[2], f.childNodes[2]); },
- function () { create("c", f.childNodes[0], f.childNodes[2]); }
- ],
- function () {
- assert_dump(test, "<1>1><2>2><3>3> ", f);
- }
- );
-
- try_all_permutations(
- function () {
- f = frag("
");
- },
- [
- function () { create("a", f.childNodes[0], f.childNodes[0]); },
- function () { create("b", f.childNodes[0], f.childNodes[1]); },
- function () { create("c", f.childNodes[0], f.childNodes[2]); }
- ],
- function () {
- assert_dump(test, "<1>1> <2>2> <3>3> ", f);
- }
- );
-});
-
-var makeTestPattern = function(codedStr) {
- codedStr = codedStr.replace(/\*/g, '[]');
-
- var self = {};
- self.tag = '_foo';
- self.ranges = {};
-
- // set up self.ranges
- var curNode = document.createDocumentFragment();
- var starts = [];
- for(var i=0; i