mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
No more domutils and liverange packages
This commit is contained in:
1
packages/domutils/.gitignore
vendored
1
packages/domutils/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
.build*
|
||||
@@ -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 = " <link/><table></table><select><!----></select>";
|
||||
// Need to wrap in a div rather than directly creating SELECT to avoid
|
||||
// *another* IE bug.
|
||||
var testSelectDiv = document.createElement("div");
|
||||
testSelectDiv.innerHTML = "<select><option selected>Foo</option></select>";
|
||||
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 <select> and <option> 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, "<select multiple='multiple'>", "</select>" ],
|
||||
legend: [ 1, "<fieldset>", "</fieldset>" ],
|
||||
thead: [ 1, "<table>", "</table>" ],
|
||||
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
|
||||
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
|
||||
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
|
||||
area: [ 1, "<map>", "</map>" ],
|
||||
_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<div>", "</div>" ];
|
||||
}
|
||||
|
||||
var rleadingWhitespace = /^\s+/,
|
||||
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
|
||||
rtagName = /<([\w:]+)/,
|
||||
rtbody = /<tbody/i,
|
||||
rhtml = /<|&#?\w+;/,
|
||||
rnoInnerhtml = /<(?:script|style)/i;
|
||||
|
||||
|
||||
// Parse an HTML string, which may contain multiple top-level tags,
|
||||
// and return a DocumentFragment.
|
||||
DomUtils.htmlToFragment = function (html) {
|
||||
var doc = document; // node factory
|
||||
var frag = doc.createDocumentFragment();
|
||||
|
||||
if (! html.length) {
|
||||
// empty, do nothing
|
||||
} else if (! rhtml.test(html)) {
|
||||
// Just text.
|
||||
frag.appendChild(doc.createTextNode(html));
|
||||
} else {
|
||||
// General case.
|
||||
// Replace self-closing tags
|
||||
html = html.replace(rxhtmlTag, "<$1></$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 <select> and <option> tags into fake tags
|
||||
fullHtml = fullHtml.replace(/<\s*(select|option)\b/ig,
|
||||
'<ins domutilsrealtagname="$1"');
|
||||
fullHtml = fullHtml.replace(/<\/\s*(select|option)\b/ig,
|
||||
'</ins');
|
||||
}
|
||||
|
||||
var container = doc.createElement("div");
|
||||
// insert wrapped HTML into a DIV
|
||||
container.innerHTML = fullHtml;
|
||||
// set "container" to inner node of wrapper
|
||||
var unwraps = wrapData[0];
|
||||
while (unwraps--) {
|
||||
container = container.lastChild;
|
||||
}
|
||||
|
||||
if (quirks.tbodyInsertion && ! rtbody.test(html)) {
|
||||
// Any tbody we find was created by the browser.
|
||||
var tbodies = container.getElementsByTagName("tbody");
|
||||
_.each(tbodies, function (n) {
|
||||
if (! n.firstChild) {
|
||||
// spurious empty tbody
|
||||
n.parentNode.removeChild(n);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (quirks.leadingWhitespaceKilled) {
|
||||
var wsMatch = rleadingWhitespace.exec(html);
|
||||
if (wsMatch) {
|
||||
container.insertBefore(doc.createTextNode(wsMatch[0]),
|
||||
container.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
if (quirks.commentsLost) {
|
||||
// replace fake select tags with real <select> 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);
|
||||
}
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
Tinytest.add("domutils - setElementValue", function (test) {
|
||||
var div = OnscreenDiv();
|
||||
div.node().appendChild(DomUtils.htmlToFragment(
|
||||
("<select><option>Foo</option><option value='Bar'>Baz</option>" +
|
||||
"<option selected value='Quux'>Quux</option></select>")));
|
||||
|
||||
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(
|
||||
('<form><input name="id"></form>')));
|
||||
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();
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
1
packages/liverange/.gitignore
vendored
1
packages/liverange/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
.build*
|
||||
@@ -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<starts.length;i++)
|
||||
starts[i]._start = targetNode;
|
||||
for(var i=0;i<ends.length;i++)
|
||||
ends[i]._end = targetNode;
|
||||
};
|
||||
|
||||
// Takes two sibling nodes tgtStart and tgtEnd with no LiveRange data on them
|
||||
// and a LiveRange srcRange in a separate DOM tree. Transplants srcRange
|
||||
// to span from tgtStart to tgtEnd, and also copies info about enclosing ranges
|
||||
// starting on srcRange._start or ending on srcRange._end. tgtStart and tgtEnd
|
||||
// must be capable of receiving liverange tags (for example, nodes that have
|
||||
// held liverange data in the past; not text nodes in IE).
|
||||
//
|
||||
// This is a low-level operation suitable for moving liveranges en masse
|
||||
// from one DOM tree to another.
|
||||
LiveRange.transplantRange = function(tgtStart, tgtEnd, srcRange) {
|
||||
srcRange._ensureTag(tgtStart);
|
||||
if (tgtEnd !== tgtStart)
|
||||
srcRange._ensureTag(tgtEnd);
|
||||
|
||||
srcRange._insertEntries(
|
||||
tgtStart, 0, 0,
|
||||
srcRange._start[srcRange.tag][0].slice(0, srcRange._startIndex + 1));
|
||||
srcRange._insertEntries(
|
||||
tgtEnd, 1, 0,
|
||||
srcRange._end[srcRange.tag][1].slice(srcRange._endIndex));
|
||||
};
|
||||
|
||||
// Inserts a DocumentFragment immediately before this range.
|
||||
// The new nodes are outside this range but inside all
|
||||
// enclosing ranges.
|
||||
LiveRange.prototype.insertBefore = function(frag) {
|
||||
var fragStart = frag.firstChild;
|
||||
|
||||
if (! fragStart) // empty frag
|
||||
return;
|
||||
|
||||
// insert into DOM
|
||||
this._start.parentNode.insertBefore(frag, this._start);
|
||||
|
||||
// move starts of ranges that begin on this._start, but are
|
||||
// outside this, to beginning of fragStart
|
||||
this._ensureTag(fragStart);
|
||||
this._insertEntries(fragStart, 0, 0,
|
||||
this._removeEntries(this._start, 0, 0,
|
||||
this._startIndex));
|
||||
};
|
||||
|
||||
// Inserts a DocumentFragment immediately after this range.
|
||||
// The new nodes are outside this range but inside all
|
||||
// enclosing ranges.
|
||||
LiveRange.prototype.insertAfter = function(frag) {
|
||||
var fragEnd = frag.lastChild;
|
||||
|
||||
if (! fragEnd) // empty frag
|
||||
return;
|
||||
|
||||
// insert into DOM
|
||||
this._end.parentNode.insertBefore(frag, this._end.nextSibling);
|
||||
|
||||
// move ends of ranges that end on this._end, but are
|
||||
// outside this, to end of fragEnd
|
||||
this._ensureTag(fragEnd);
|
||||
this._insertEntries(fragEnd, 1, fragEnd[this.tag][1].length,
|
||||
this._removeEntries(this._end, 1,
|
||||
this._endIndex + 1));
|
||||
};
|
||||
|
||||
// Extracts this range and its contents from the DOM and
|
||||
// puts it into a DocumentFragment, which is returned.
|
||||
// All nodes and ranges outside this range are properly
|
||||
// preserved.
|
||||
//
|
||||
// Because liveranges must contain at least one node,
|
||||
// it is illegal to perform `extract` if the immediately
|
||||
// enclosing range would become empty. If this precondition
|
||||
// is violated, no action is taken and null is returned.
|
||||
LiveRange.prototype.extract = function() {
|
||||
if (this._startIndex > 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;
|
||||
};
|
||||
@@ -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");
|
||||
};
|
||||
@@ -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("<div id=1></div>");
|
||||
var r_a = create("a", f);
|
||||
test.instanceOf(r_a, LiveRange);
|
||||
assert_dump(test, "<a><1></1></a>", r_a);
|
||||
assert_dump(test, "<a><1></1></a>", f);
|
||||
assert_contained(r_a, {range: r_a, children: []});
|
||||
|
||||
var r_b = create("b", f);
|
||||
assert_dump(test, "<a><1></1></a>", r_a);
|
||||
assert_dump(test, "<b><a><1></1></a></b>", r_b);
|
||||
assert_dump(test, "<b><a><1></1></a></b>", 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("<div id=2></div>"), true);
|
||||
test.equal(ret1.nodeType, 11 /* DocumentFragment */);
|
||||
assert_dump(test, "<1></1>", ret1);
|
||||
assert_dump(test, "<a><2></2></a>", r_a);
|
||||
assert_dump(test, "<b><a><2></2></a></b>", r_b);
|
||||
assert_dump(test, "<b><a><2></2></a></b>", f);
|
||||
|
||||
var ret2 = r_b.replaceContents(frag("<div id=3></div>"), true);
|
||||
assert_dump(test, "<a><2></2></a>", ret2);
|
||||
assert_dump(test, "<a><2></2></a>", r_a);
|
||||
assert_dump(test, "<b><3></3></b>", r_b);
|
||||
assert_dump(test, "<b><3></3></b>", 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, "<c><b><3></3></b></c>", r_c);
|
||||
assert_dump(test, "<d><c><b><3></3></b></c></d>", r_d);
|
||||
assert_dump(test, "<e><d><c><b><3></3></b></c></d></e>", r_e);
|
||||
assert_dump(test, "<1></1>", ret1);
|
||||
assert_dump(test, "<b><3></3></b>", r_b);
|
||||
|
||||
r_d.destroy();
|
||||
assert_dump(test, "<b><3></3></b>", r_b);
|
||||
assert_dump(test, "<c><b><3></3></b></c>", r_c);
|
||||
assert_dump(test, "<e><c><b><3></3></b></c></e>", 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, "<c><3></3></c>", r_c);
|
||||
assert_dump(test, "<e><c><3></3></c></e>", r_e);
|
||||
|
||||
r_e.destroy();
|
||||
assert_dump(test, "<c><3></3></c>", r_c);
|
||||
|
||||
});
|
||||
|
||||
Tinytest.add("liverange - empty replace", function (test) {
|
||||
var f,r;
|
||||
|
||||
f = frag("<div id=1></div>");
|
||||
r = create("z", f);
|
||||
test.throws(function() {
|
||||
r.replaceContents(frag(""));
|
||||
});
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
r = create("z", f.childNodes[1]);
|
||||
assert_dump(test, "<1></1><z><2></2></z><3></3>", f);
|
||||
test.throws(function() {
|
||||
r.replaceContents(frag(""));
|
||||
});
|
||||
});
|
||||
|
||||
Tinytest.add("liverange - multiple nodes", function (test) {
|
||||
var f = frag("<div id=1></div><div id=2></div><div id=3></div><div id=4></div><div id=5></div>");
|
||||
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><a><3></3><4></4></a><5></5>", f);
|
||||
assert_dump(test, "<a><3></3><4></4></a>", r_a);
|
||||
|
||||
var r_b = create("b", f.childNodes[3], f.childNodes[3]);
|
||||
assert_dump(test, "<1></1><2></2><a><3></3><b><4></4></b></a><5></5>", f);
|
||||
assert_dump(test, "<a><3></3><b><4></4></b></a>", r_a);
|
||||
assert_dump(test, "<b><4></4></b>", r_b);
|
||||
|
||||
var r_c = create("c", f.childNodes[2], f.childNodes[3]);
|
||||
assert_dump(test, "<1></1><2></2><c><a><3></3><b><4></4></b></a></c><5></5>", f);
|
||||
assert_dump(test, "<a><3></3><b><4></4></b></a>", r_a);
|
||||
assert_dump(test, "<b><4></4></b>", r_b);
|
||||
assert_dump(test, "<c><a><3></3><b><4></4></b></a></c>", r_c);
|
||||
|
||||
var r_d = create("d", f.childNodes[3], f.childNodes[3]);
|
||||
assert_dump(test, "<1></1><2></2><c><a><3></3><d><b><4></4></b></d></a></c><5></5>", f);
|
||||
assert_dump(test, "<a><3></3><d><b><4></4></b></d></a>", r_a);
|
||||
assert_dump(test, "<b><4></4></b>", r_b);
|
||||
assert_dump(test, "<c><a><3></3><d><b><4></4></b></d></a></c>", r_c);
|
||||
assert_dump(test, "<d><b><4></4></b></d>", r_d);
|
||||
|
||||
var r_e = create("e", f.childNodes[2], f.childNodes[2]);
|
||||
assert_dump(test, "<1></1><2></2><c><a><e><3></3></e><d><b><4></4></b></d></a></c><5></5>", f);
|
||||
assert_dump(test, "<a><e><3></3></e><d><b><4></4></b></d></a>", r_a);
|
||||
assert_dump(test, "<b><4></4></b>", r_b);
|
||||
assert_dump(test, "<c><a><e><3></3></e><d><b><4></4></b></d></a></c>", r_c);
|
||||
assert_dump(test, "<d><b><4></4></b></d>", r_d);
|
||||
assert_dump(test, "<e><3></3></e>", r_e);
|
||||
|
||||
var r_f = create("f", f.childNodes[2], f.childNodes[3]);
|
||||
assert_dump(test, "<1></1><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f><5></5>", f);
|
||||
assert_dump(test, "<a><e><3></3></e><d><b><4></4></b></d></a>", r_a);
|
||||
assert_dump(test, "<b><4></4></b>", r_b);
|
||||
assert_dump(test, "<c><a><e><3></3></e><d><b><4></4></b></d></a></c>", r_c);
|
||||
assert_dump(test, "<d><b><4></4></b></d>", r_d);
|
||||
assert_dump(test, "<e><3></3></e>", r_e);
|
||||
assert_dump(test, "<f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f>", 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, "<h><g><1></1><i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i></g></h><5></5>", f);
|
||||
assert_dump(test, "<a><e><3></3></e><d><b><4></4></b></d></a>", r_a);
|
||||
assert_dump(test, "<b><4></4></b>", r_b);
|
||||
assert_dump(test, "<c><a><e><3></3></e><d><b><4></4></b></d></a></c>", r_c);
|
||||
assert_dump(test, "<d><b><4></4></b></d>", r_d);
|
||||
assert_dump(test, "<e><3></3></e>", r_e);
|
||||
assert_dump(test, "<f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f>", r_f);
|
||||
assert_dump(test, "<g><1></1><i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i></g>", r_g);
|
||||
assert_dump(test, "<h><g><1></1><i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i></g></h>", r_h);
|
||||
assert_dump(test, "<i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i>", r_i);
|
||||
|
||||
var f2 = frag("<div id=6></div><div id=7></div><div id=8></div>");
|
||||
f2.childNodes[1].appendChild(f);
|
||||
assert_dump(test, "", f);
|
||||
assert_dump(test, "<6></6><7><h><g><1></1><i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i></g></h><5></5></7><8></8>", f2);
|
||||
assert_dump(test, "<a><e><3></3></e><d><b><4></4></b></d></a>", r_a);
|
||||
assert_dump(test, "<b><4></4></b>", r_b);
|
||||
assert_dump(test, "<c><a><e><3></3></e><d><b><4></4></b></d></a></c>", r_c);
|
||||
assert_dump(test, "<d><b><4></4></b></d>", r_d);
|
||||
assert_dump(test, "<e><3></3></e>", r_e);
|
||||
assert_dump(test, "<f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f>", r_f);
|
||||
assert_dump(test, "<g><1></1><i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i></g>", r_g);
|
||||
assert_dump(test, "<h><g><1></1><i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i></g></h>", r_h);
|
||||
assert_dump(test, "<i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i>", 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, "<l><k><6></6><j><7><h><g><1></1><i><2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f></i></g></h><5></5></7><8></8></j></k></l>", f2);
|
||||
|
||||
var f3 = frag("<div id=9></div><div id=10></div><div id=11></div>");
|
||||
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, "<m><o><n><9></9></n></o><10></10><11></11></m>", f3);
|
||||
|
||||
var ret1 = r_i.replaceContents(f3, true);
|
||||
assert_dump(test, "", f3);
|
||||
assert_dump(test, "<2></2><f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f>", ret1);
|
||||
assert_dump(test, "<l><k><6></6><j><7><h><g><1></1><i><m><o><n><9></9></n></o><10></10><11></11></m></i></g></h><5></5></7><8></8></j></k></l>", f2);
|
||||
assert_dump(test, "<a><e><3></3></e><d><b><4></4></b></d></a>", r_a);
|
||||
assert_dump(test, "<b><4></4></b>", r_b);
|
||||
assert_dump(test, "<c><a><e><3></3></e><d><b><4></4></b></d></a></c>", r_c);
|
||||
assert_dump(test, "<d><b><4></4></b></d>", r_d);
|
||||
assert_dump(test, "<e><3></3></e>", r_e);
|
||||
assert_dump(test, "<f><c><a><e><3></3></e><d><b><4></4></b></d></a></c></f>", r_f);
|
||||
assert_dump(test, "<g><1></1><i><m><o><n><9></9></n></o><10></10><11></11></m></i></g>", r_g);
|
||||
assert_dump(test, "<h><g><1></1><i><m><o><n><9></9></n></o><10></10><11></11></m></i></g></h>", r_h);
|
||||
assert_dump(test, "<i><m><o><n><9></9></n></o><10></10><11></11></m></i>",r_i);
|
||||
assert_dump(test, "<j><7><h><g><1></1><i><m><o><n><9></9></n></o><10></10><11></11></m></i></g></h><5></5></7><8></8></j>", r_j);
|
||||
assert_dump(test, "<k><6></6><j><7><h><g><1></1><i><m><o><n><9></9></n></o><10></10><11></11></m></i></g></h><5></5></7><8></8></j></k>", r_k);
|
||||
assert_dump(test, "<l><k><6></6><j><7><h><g><1></1><i><m><o><n><9></9></n></o><10></10><11></11></m></i></g></h><5></5></7><8></8></j></k></l>", r_l);
|
||||
|
||||
r_h.destroy();
|
||||
assert_dump(test, "<l><k><6></6><j><7><g><1></1><i><m><o><n><9></9></n></o><10></10><11></11></m></i></g><5></5></7><8></8></j></k></l>", f2);
|
||||
r_m.destroy();
|
||||
assert_dump(test, "<l><k><6></6><j><7><g><1></1><i><o><n><9></9></n></o><10></10><11></11></i></g><5></5></7><8></8></j></k></l>", f2);
|
||||
r_n.destroy();
|
||||
assert_dump(test, "<l><k><6></6><j><7><g><1></1><i><o><9></9></o><10></10><11></11></i></g><5></5></7><8></8></j></k></l>", f2);
|
||||
r_j.destroy();
|
||||
assert_dump(test, "<l><k><6></6><7><g><1></1><i><o><9></9></o><10></10><11></11></i></g><5></5></7><8></8></k></l>", f2);
|
||||
r_o.destroy();
|
||||
assert_dump(test, "<l><k><6></6><7><g><1></1><i><9></9><10></10><11></11></i></g><5></5></7><8></8></k></l>", f2);
|
||||
r_g.destroy();
|
||||
assert_dump(test, "<l><k><6></6><7><1></1><i><9></9><10></10><11></11></i><5></5></7><8></8></k></l>", f2);
|
||||
r_l.destroy();
|
||||
assert_dump(test, "<k><6></6><7><1></1><i><9></9><10></10><11></11></i><5></5></7><8></8></k>", f2);
|
||||
r_i.destroy();
|
||||
assert_dump(test, "<k><6></6><7><1></1><9></9><10></10><11></11><5></5></7><8></8></k>", 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("<div id=1><div id=2><div id=3><div id=4><div id=5></div></div></div></div></div>");
|
||||
|
||||
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, "<d><1><c><2><b><3><4><a><5></5></a></4></3></b></2></c></1></d>",
|
||||
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("<div id=1></div><div id=2></div><div id=3></div><div id=4></div><div id=5></div>");
|
||||
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><a><3></3><4></4><5></5></a>", f);
|
||||
|
||||
var r_b = create("b", f.childNodes[2], f.childNodes[4], true);
|
||||
assert_dump(test, "<1></1><2></2><a><b><3></3><4></4><5></5></b></a>", f);
|
||||
|
||||
var r_c = create("c", f.childNodes[2], f.childNodes[4]);
|
||||
assert_dump(test, "<1></1><2></2><c><a><b><3></3><4></4><5></5></b></a></c>", 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, "<f><d><1></1></d><e><2></2></e></f><c><a><b><3></3><4></4><5></5></b></a></c>", f);
|
||||
|
||||
var r_g = create("g", f.childNodes[0], f.childNodes[1], true);
|
||||
assert_dump(test, "<f><g><d><1></1></d><e><2></2></e></g></f><c><a><b><3></3><4></4><5></5></b></a></c>", f);
|
||||
|
||||
var r_h = create("h", f.childNodes[0], f.childNodes[1]);
|
||||
assert_dump(test, "<h><f><g><d><1></1></d><e><2></2></e></g></f></h><c><a><b><3></3><4></4><5></5></b></a></c>", f);
|
||||
|
||||
var r_i = create("i", f.childNodes[0], f.childNodes[1], true);
|
||||
assert_dump(test, "<h><f><g><i><d><1></1></d><e><2></2></e></i></g></f></h><c><a><b><3></3><4></4><5></5></b></a></c>", f);
|
||||
|
||||
var r_j = create("j", f.childNodes[0], f.childNodes[0], true);
|
||||
assert_dump(test, "<h><f><g><i><d><j><1></1></j></d><e><2></2></e></i></g></f></h><c><a><b><3></3><4></4><5></5></b></a></c>", f);
|
||||
|
||||
var r_k = create("k", f.childNodes[0], f.childNodes[0]);
|
||||
assert_dump(test, "<h><f><g><i><k><d><j><1></1></j></d></k><e><2></2></e></i></g></f></h><c><a><b><3></3><4></4><5></5></b></a></c>", f);
|
||||
|
||||
var r_l = create("l", f.childNodes[0], f.childNodes[1], true);
|
||||
assert_dump(test, "<h><f><g><i><l><k><d><j><1></1></j></d></k><e><2></2></e></l></i></g></f></h><c><a><b><3></3><4></4><5></5></b></a></c>", f);
|
||||
assert_dump(test, "<c><a><b><3></3><4></4><5></5></b></a></c>", r_c);
|
||||
assert_dump(test, "<b><3></3><4></4><5></5></b>", r_b);
|
||||
assert_dump(test, "<a><b><3></3><4></4><5></5></b></a>", r_a);
|
||||
assert_dump(test, "<d><j><1></1></j></d>", r_d);
|
||||
assert_dump(test, "<e><2></2></e>", r_e);
|
||||
assert_dump(test, "<f><g><i><l><k><d><j><1></1></j></d></k><e><2></2></e></l></i></g></f>", r_f);
|
||||
assert_dump(test, "<g><i><l><k><d><j><1></1></j></d></k><e><2></2></e></l></i></g>", r_g);
|
||||
assert_dump(test, "<h><f><g><i><l><k><d><j><1></1></j></d></k><e><2></2></e></l></i></g></f></h>", r_h);
|
||||
assert_dump(test, "<i><l><k><d><j><1></1></j></d></k><e><2></2></e></l></i>", r_i);
|
||||
assert_dump(test, "<j><1></1></j>", r_j);
|
||||
assert_dump(test, "<k><d><j><1></1></j></d></k>", r_k);
|
||||
assert_dump(test, "<l><k><d><j><1></1></j></d></k><e><2></2></e></l>", r_l);
|
||||
|
||||
// [{a b [c]}]
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
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, "<b><c><1></1><2></2><a><3></3></a></c></b>", f);
|
||||
|
||||
// [{[a] b c}]
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
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, "<b><c><a><1></1></a><2></2><3></3></c></b>", f);
|
||||
|
||||
// [{[a b] c}]
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
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, "<b><c><a><1></1><2></2></a><3></3></c></b>", f);
|
||||
|
||||
// Cases where start and end have no common ranges, and so the
|
||||
// balance counter will have to run
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
r_a = create("a", f.childNodes[0], f.childNodes[0]);
|
||||
r_b = create("b", f.childNodes[0], f.childNodes[2]);
|
||||
assert_dump(test, "<b><a><1></1></a><2></2><3></3></b>", f);
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
r_a = create("a", f.childNodes[0], f.childNodes[2]);
|
||||
r_b = create("b", f.childNodes[0], f.childNodes[0]);
|
||||
assert_dump(test, "<a><b><1></1></b><2></2><3></3></a>", f);
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
r_a = create("a", f.childNodes[2], f.childNodes[2]);
|
||||
r_b = create("b", f.childNodes[0], f.childNodes[2]);
|
||||
assert_dump(test, "<b><1></1><2></2><a><3></3></a></b>", f);
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
r_a = create("a", f.childNodes[0], f.childNodes[2]);
|
||||
r_b = create("b", f.childNodes[2], f.childNodes[2]);
|
||||
assert_dump(test, "<a><1></1><2></2><b><3></3></b></a>", f);
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
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, "<e><b><a><1></1></a></b><2></2><d><c><3></3></c></d></e>", f);
|
||||
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
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, "<e><b><a><1></1></a></b><2></2><c><3></3></c></e>", f);
|
||||
|
||||
try_all_permutations(
|
||||
function () {
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
},
|
||||
[
|
||||
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, "<c><1></1><a><2></2><b><3></3></b></a></c>", f);
|
||||
}
|
||||
);
|
||||
|
||||
try_all_permutations(
|
||||
function () {
|
||||
f = frag("<div id=1></div><div id=2></div><div id=3></div>");
|
||||
},
|
||||
[
|
||||
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, "<c><b><a><1></1></a><2></2></b><3></3></c>", 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<codedStr.length; i++) {
|
||||
var c = codedStr.charAt(i);
|
||||
if (/[A-Z]/.test(c)) {
|
||||
// open range
|
||||
starts.push([curNode, curNode.childNodes.length]);
|
||||
} else if (/[a-z]/.test(c)) {
|
||||
// close range
|
||||
var start = starts.pop();
|
||||
var range =
|
||||
new LiveRange(
|
||||
self.tag, start[0].childNodes[start[1]],
|
||||
start[0].lastChild);
|
||||
range.letter = c.toUpperCase();
|
||||
self.ranges[range.letter] = range;
|
||||
} else if (c === '[') {
|
||||
curNode.appendChild(document.createElement("DIV"));
|
||||
curNode = curNode.lastChild;
|
||||
} else if (c === ']') {
|
||||
// close node
|
||||
curNode = curNode.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
self.frag = curNode;
|
||||
|
||||
self.path = function(/*args*/) {
|
||||
var node = self.frag;
|
||||
_.each(arguments, function(i) {
|
||||
node = node.childNodes[i];
|
||||
});
|
||||
return node;
|
||||
};
|
||||
|
||||
self.findRange = function(node) {
|
||||
return LiveRange.findRange(self.tag, node);
|
||||
};
|
||||
|
||||
self.currentString = function() {
|
||||
var buf = [];
|
||||
var tempRange = new LiveRange(self.tag, self.frag);
|
||||
tempRange.visit(function(isStart, range) {
|
||||
buf.push(isStart ?
|
||||
range.letter.toUpperCase() :
|
||||
range.letter.toLowerCase());
|
||||
}, function(isStart, node) {
|
||||
buf.push(isStart ? '[' : ']');
|
||||
});
|
||||
tempRange.destroy();
|
||||
|
||||
return buf.join('').replace(/\[\]/g, '*');
|
||||
};
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
Tinytest.add("liverange - findParent", function(test) {
|
||||
var str = "I*[[AB[H***FDE*ed*fG*gh]*baC*c*]]i*";
|
||||
var pat = makeTestPattern(str);
|
||||
test.equal(pat.currentString(), str);
|
||||
|
||||
var ranges = pat.ranges;
|
||||
|
||||
test.equal(ranges.E.findParent().letter, 'D');
|
||||
test.equal(ranges.D.findParent().letter, 'F');
|
||||
test.equal(ranges.F.findParent().letter, 'H');
|
||||
test.equal(ranges.H.findParent().letter, 'B');
|
||||
test.equal(ranges.B.findParent().letter, 'A');
|
||||
test.equal(ranges.A.findParent().letter, 'I');
|
||||
test.equal(ranges.I.findParent(), null);
|
||||
|
||||
test.equal(ranges.E.findParent(true).letter, 'D');
|
||||
test.equal(ranges.D.findParent(true).letter, 'F');
|
||||
test.equal(ranges.F.findParent(true).letter, 'H');
|
||||
test.equal(ranges.H.findParent(true), null);
|
||||
test.equal(ranges.B.findParent(true).letter, 'A');
|
||||
test.equal(ranges.A.findParent(true), null);
|
||||
test.equal(ranges.I.findParent(true), null);
|
||||
|
||||
|
||||
test.equal(pat.findRange(pat.path(0)).letter, 'I');
|
||||
test.equal(pat.findRange(pat.path(1)).letter, 'I');
|
||||
test.equal(pat.findRange(pat.path(2)), null);
|
||||
|
||||
test.equal(pat.findRange(pat.path(1, 0)).letter, 'I');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 0)).letter, 'B');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 1)).letter, 'B');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 2)).letter, 'C');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 3)).letter, 'I');
|
||||
|
||||
test.equal(pat.findRange(pat.path(1, 0, 0, 0)).letter, 'H');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 0, 1)).letter, 'H');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 0, 2)).letter, 'H');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 0, 3)).letter, 'E');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 0, 4)).letter, 'F');
|
||||
test.equal(pat.findRange(pat.path(1, 0, 0, 5)).letter, 'G');
|
||||
|
||||
});
|
||||
|
||||
Tinytest.add("liverange - destroy", function(test) {
|
||||
var str = "I*[[AB[H***FDE*ed*fG*gh]*baC*c*]]J*ji*";
|
||||
var pat = makeTestPattern(str);
|
||||
|
||||
pat.ranges.D.destroy();
|
||||
test.equal(pat.currentString(), str.replace(/[Dd]/g, ''));
|
||||
pat.ranges.B.destroy();
|
||||
test.equal(pat.currentString(), str.replace(/[DdBb]/g, ''));
|
||||
pat.ranges.A.destroy();
|
||||
test.equal(pat.currentString(), str.replace(/[DdBbAa]/g, ''));
|
||||
|
||||
// recursive destroy
|
||||
pat.ranges.F.destroy(true);
|
||||
test.equal(pat.currentString(),
|
||||
"I*[[[H*****G*gh]*C*c*]]J*ji*");
|
||||
pat.ranges.I.destroy(true);
|
||||
test.equal(pat.currentString(),
|
||||
"*[[[******]***]]**");
|
||||
|
||||
var childrenHaveNoTags = function(node) {
|
||||
for(var n = node.firstChild; n; n = n.nextSibling) {
|
||||
test.isFalse(node[pat.tag]);
|
||||
if (n.firstChild)
|
||||
childrenHaveNoTags(n); // recurse
|
||||
}
|
||||
};
|
||||
|
||||
childrenHaveNoTags(pat.frag);
|
||||
|
||||
// test recursive on single node
|
||||
var frag = document.createDocumentFragment();
|
||||
var txt = document.createComment("pudding");
|
||||
frag.appendChild(txt);
|
||||
var rng5 = new LiveRange('_pudding', txt);
|
||||
var rng4 = new LiveRange('_pudding', txt);
|
||||
var rng3 = new LiveRange('_pudding', txt);
|
||||
var rng2 = new LiveRange('_pudding', txt);
|
||||
var rng1 = new LiveRange('_pudding', txt);
|
||||
rng1.num = 1;
|
||||
rng2.num = 2;
|
||||
rng3.num = 3;
|
||||
rng4.num = 4;
|
||||
rng5.num = 5;
|
||||
// kill an inner range
|
||||
rng4.destroy(true);
|
||||
// check that outer ranges are still there
|
||||
var buf = [];
|
||||
rng1.visit(function(isStart, r) {
|
||||
buf.push([isStart, r.num]);
|
||||
});
|
||||
test.equal(buf, [[true, 2], [true, 3], [false, 3], [false, 2]]);
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
Package.describe({
|
||||
summary: "Mark, track, and update an arbitrary region in the DOM",
|
||||
internal: true
|
||||
});
|
||||
|
||||
Package.on_use(function (api) {
|
||||
api.export('LiveRange', 'client');
|
||||
api.add_files('liverange.js', 'client');
|
||||
});
|
||||
|
||||
Package.on_test(function (api) {
|
||||
api.use(['tinytest']);
|
||||
api.use(['liverange', 'test-helpers', 'domutils', 'underscore', 'jquery'],
|
||||
'client');
|
||||
|
||||
api.add_files([
|
||||
'liverange_test_helpers.js',
|
||||
'liverange_tests.js'
|
||||
], 'client');
|
||||
});
|
||||
@@ -11,7 +11,6 @@ Package.on_test(function (api) {
|
||||
api.use('jquery');
|
||||
api.use('test-helpers');
|
||||
api.use('showdown');
|
||||
api.use('domutils');
|
||||
|
||||
api.use('templating', 'client');
|
||||
api.add_files([
|
||||
|
||||
@@ -4,8 +4,7 @@ Package.describe({
|
||||
});
|
||||
|
||||
Package.on_use(function (api) {
|
||||
api.use(['underscore', 'deps', 'ejson', 'tinytest', 'random',
|
||||
'domutils']);
|
||||
api.use(['underscore', 'deps', 'ejson', 'tinytest', 'random']);
|
||||
api.use(['jquery'], 'client');
|
||||
|
||||
// XXX for connection.js. Not sure this really belongs in
|
||||
@@ -22,7 +21,7 @@ Package.on_use(function (api) {
|
||||
'SeededRandom', 'ReactiveVar', 'clickElement', 'blurElement',
|
||||
'focusElement', 'simulateEvent', 'getStyleProperty', 'canonicalizeHtml',
|
||||
'withCallbackLogger', 'testAsyncMulti', 'simplePoll',
|
||||
'makeTestConnection'], {testOnly: true});
|
||||
'makeTestConnection', 'DomUtils'], {testOnly: true});
|
||||
|
||||
api.add_files('try_all_permutations.js');
|
||||
api.add_files('async_multi.js');
|
||||
@@ -33,6 +32,7 @@ Package.on_use(function (api) {
|
||||
api.add_files('current_style.js');
|
||||
api.add_files('reactivevar.js');
|
||||
api.add_files('callback_logger.js');
|
||||
api.add_files('domutils.js', 'client');
|
||||
api.add_files('connection.js', 'server');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user