mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Meteor.ui._LiveRange -> LiveRange
This commit is contained in:
@@ -1,12 +1,6 @@
|
||||
// Stand back, I'm going to try SCIENCE.
|
||||
|
||||
Meteor.ui = Meteor.ui || {};
|
||||
|
||||
(function () {
|
||||
// XXX we should eventually move LiveRange off into its own
|
||||
// package. but it would be also be nice to keep it as a single,
|
||||
// self-contained file to make it easier to use outside of Meteor.
|
||||
|
||||
// Possible optimization: get rid of start_idx/end_idx and just search
|
||||
// the list. Not clear which strategy will be faster.
|
||||
|
||||
@@ -25,7 +19,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
return (test_elt.test === 123);
|
||||
})();
|
||||
|
||||
Meteor.ui._wrap_endpoints = function (start, end) {
|
||||
var wrapEndpoints = function (start, end) {
|
||||
if (canSetTextProps) {
|
||||
return [start, end];
|
||||
} else {
|
||||
@@ -47,7 +41,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
};
|
||||
|
||||
|
||||
// This is a constructor (invoke it as 'new Meteor.ui._LiveRange').
|
||||
// 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,
|
||||
@@ -61,7 +55,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// other ranges is uniquely determined.
|
||||
//
|
||||
// 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
|
||||
// are part of the range will have an expando attribute 0Aset on
|
||||
// them. The name of the expando attribute will be 'tag', so pick
|
||||
// something that won't collide.
|
||||
//
|
||||
@@ -81,7 +75,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// 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.
|
||||
Meteor.ui._LiveRange = function (tag, start, end, inner) {
|
||||
LiveRange = function (tag, start, end, inner) {
|
||||
if (start.nodeType === 11 /* DocumentFragment */) {
|
||||
end = start.lastChild;
|
||||
start = start.firstChild;
|
||||
@@ -93,7 +87,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
|
||||
this.tag = tag; // must be set before calling _ensure_tag
|
||||
|
||||
var endpoints = Meteor.ui._wrap_endpoints(start, end);
|
||||
var endpoints = wrapEndpoints(start, end);
|
||||
start = this._ensure_tag(endpoints[0]);
|
||||
end = this._ensure_tag(endpoints[1]);
|
||||
|
||||
@@ -179,7 +173,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
return index;
|
||||
};
|
||||
|
||||
Meteor.ui._LiveRange.prototype._ensure_tag = function (node) {
|
||||
LiveRange.prototype._ensure_tag = function (node) {
|
||||
if (!(this.tag in node))
|
||||
node[this.tag] = [[], []];
|
||||
return node;
|
||||
@@ -199,7 +193,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
return result;
|
||||
})();
|
||||
|
||||
Meteor.ui._LiveRange._clean_node = function (tag, node, force) {
|
||||
LiveRange._clean_node = function (tag, node, force) {
|
||||
var data = node[tag];
|
||||
if (data && (!(data[0].length + data[1].length) || force)) {
|
||||
if (can_delete_expandos)
|
||||
@@ -220,7 +214,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// through the DOM.
|
||||
//
|
||||
// Pass true for `recursive` to also destroy all descendent ranges.
|
||||
Meteor.ui._LiveRange.prototype.destroy = function (recursive) {
|
||||
LiveRange.prototype.destroy = function (recursive) {
|
||||
var self = this;
|
||||
|
||||
if (recursive) {
|
||||
@@ -236,7 +230,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
if (! is_start) {
|
||||
// when leaving a node, force-clean its children
|
||||
for(var n = node.firstChild; n; n = n.nextSibling) {
|
||||
Meteor.ui._LiveRange._clean_node(self.tag, n, true);
|
||||
LiveRange._clean_node(self.tag, n, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -249,7 +243,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
for(var n = this._start.nextSibling;
|
||||
n !== this._end;
|
||||
n = n.nextSibling) {
|
||||
Meteor.ui._LiveRange._clean_node(self.tag, n, true);
|
||||
LiveRange._clean_node(self.tag, n, true);
|
||||
}
|
||||
|
||||
// clean ends on this._start and starts on this._end
|
||||
@@ -269,18 +263,18 @@ Meteor.ui = Meteor.ui || {};
|
||||
};
|
||||
|
||||
// Return the first node in the range (in preorder traversal)
|
||||
Meteor.ui._LiveRange.prototype.firstNode = function () {
|
||||
LiveRange.prototype.firstNode = function () {
|
||||
return this._start;
|
||||
};
|
||||
|
||||
// Return the last node in the range (in postorder traversal)
|
||||
Meteor.ui._LiveRange.prototype.lastNode = function () {
|
||||
LiveRange.prototype.lastNode = function () {
|
||||
return this._end;
|
||||
};
|
||||
|
||||
// Return the node that immediately contains this LiveRange, that is,
|
||||
// the parentNode of firstNode and lastNode.
|
||||
Meteor.ui._LiveRange.prototype.containerNode = function() {
|
||||
LiveRange.prototype.containerNode = function() {
|
||||
return this._start.parentNode;
|
||||
};
|
||||
|
||||
@@ -301,7 +295,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
//
|
||||
// If you create or destroy ranges with this tag from a visitation
|
||||
// function, results are undefined!
|
||||
Meteor.ui._LiveRange.prototype.visit = function(visit_range, visit_node) {
|
||||
LiveRange.prototype.visit = function(visit_range, visit_node) {
|
||||
visit_range = visit_range || function() {};
|
||||
visit_node = visit_node || function() {};
|
||||
|
||||
@@ -338,7 +332,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
};
|
||||
|
||||
// startEnd === 0 for starts, 1 for ends
|
||||
Meteor.ui._LiveRange.prototype._remove_entries =
|
||||
LiveRange.prototype._remove_entries =
|
||||
function(node, startEnd, i, j)
|
||||
{
|
||||
var entries = node[this.tag][startEnd];
|
||||
@@ -353,13 +347,13 @@ Meteor.ui = Meteor.ui || {};
|
||||
|
||||
// potentially remove empty liverange data
|
||||
if (! entries.length) {
|
||||
Meteor.ui._LiveRange._clean_node(this.tag, node);
|
||||
LiveRange._clean_node(this.tag, node);
|
||||
}
|
||||
|
||||
return removed;
|
||||
};
|
||||
|
||||
Meteor.ui._LiveRange.prototype._insert_entries =
|
||||
LiveRange.prototype._insert_entries =
|
||||
function(node, startEnd, i, newRanges)
|
||||
{
|
||||
// insert the new ranges and "adopt" them by setting node pointers
|
||||
@@ -387,7 +381,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// our children.
|
||||
//
|
||||
// It is illegal for new_frag to be empty.
|
||||
Meteor.ui._LiveRange.prototype.replace_contents = function (new_frag) {
|
||||
LiveRange.prototype.replace_contents = function (new_frag) {
|
||||
if (! new_frag.firstChild)
|
||||
throw new Error("replace_contents requires non-empty fragment");
|
||||
|
||||
@@ -429,7 +423,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// This method is a generalization of replace_contents that works by
|
||||
// temporarily removing this LiveRange from the DOM and restoring it after
|
||||
// `func` has been called.
|
||||
Meteor.ui._LiveRange.prototype.operate = function (func) {
|
||||
LiveRange.prototype.operate = function (func) {
|
||||
// boundary nodes of departing fragment
|
||||
var old_start = this._start;
|
||||
var old_end = this._end;
|
||||
@@ -461,7 +455,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
}
|
||||
|
||||
// wrap endpoints if necessary
|
||||
var new_endpoints = Meteor.ui._wrap_endpoints(new_start, new_end);
|
||||
var new_endpoints = wrapEndpoints(new_start, new_end);
|
||||
new_start = this._ensure_tag(new_endpoints[0]);
|
||||
new_end = this._ensure_tag(new_endpoints[1]);
|
||||
|
||||
@@ -481,7 +475,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// This is a low-level operation suitable for moving liveranges en masse
|
||||
// from one DOM tree to another, where transplant_tag is called on every
|
||||
// pair of nodes such that targetNode takes the place of sourceNode.
|
||||
Meteor.ui._LiveRange.transplant_tag = function(tag, targetNode, sourceNode) {
|
||||
LiveRange.transplant_tag = function(tag, targetNode, sourceNode) {
|
||||
|
||||
if (! sourceNode[tag])
|
||||
return;
|
||||
@@ -509,7 +503,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
//
|
||||
// This is a low-level operation suitable for moving liveranges en masse
|
||||
// from one DOM tree to another.
|
||||
Meteor.ui._LiveRange.transplant_range = function(tgtStart, tgtEnd, srcRange) {
|
||||
LiveRange.transplant_range = function(tgtStart, tgtEnd, srcRange) {
|
||||
srcRange._ensure_tag(tgtStart);
|
||||
if (tgtEnd !== tgtStart)
|
||||
srcRange._ensure_tag(tgtEnd);
|
||||
@@ -525,7 +519,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// Inserts a DocumentFragment immediately before this range.
|
||||
// The new nodes are outside this range but inside all
|
||||
// enclosing ranges.
|
||||
Meteor.ui._LiveRange.prototype.insert_before = function(frag) {
|
||||
LiveRange.prototype.insert_before = function(frag) {
|
||||
var frag_start = frag.firstChild;
|
||||
|
||||
if (! frag_start) // empty frag
|
||||
@@ -545,7 +539,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// Inserts a DocumentFragment immediately after this range.
|
||||
// The new nodes are outside this range but inside all
|
||||
// enclosing ranges.
|
||||
Meteor.ui._LiveRange.prototype.insert_after = function(frag) {
|
||||
LiveRange.prototype.insert_after = function(frag) {
|
||||
var frag_end = frag.lastChild;
|
||||
|
||||
if (! frag_end) // empty frag
|
||||
@@ -571,7 +565,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// 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.
|
||||
Meteor.ui._LiveRange.prototype.extract = function() {
|
||||
LiveRange.prototype.extract = function() {
|
||||
if (this._start_idx > 0 &&
|
||||
this._start[this.tag][0][this._start_idx - 1]._end === this._end) {
|
||||
// immediately enclosing range wraps same nodes, so can't extract because
|
||||
@@ -618,30 +612,30 @@ Meteor.ui = Meteor.ui || {};
|
||||
// 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.
|
||||
Meteor.ui._LiveRange.prototype.findParent = function(withSameContainer) {
|
||||
var result = _enclosing_range_search(this.tag, this._end, this._end_idx);
|
||||
LiveRange.prototype.findParent = function(withSameContainer) {
|
||||
var result = enclosingRangeSearch(this.tag, this._end, this._end_idx);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (withSameContainer)
|
||||
return null;
|
||||
|
||||
return Meteor.ui._LiveRange.findRange(this.tag, this.containerNode());
|
||||
return LiveRange.findRange(this.tag, this.containerNode());
|
||||
};
|
||||
|
||||
// Find the nearest enclosing range containing `node`, if any.
|
||||
Meteor.ui._LiveRange.findRange = function(tag, node) {
|
||||
var result = _enclosing_range_search(tag, node);
|
||||
LiveRange.findRange = function(tag, node) {
|
||||
var result = enclosingRangeSearch(tag, node);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (! node.parentNode)
|
||||
return null;
|
||||
|
||||
return Meteor.ui._LiveRange.findRange(tag, node.parentNode);
|
||||
return LiveRange.findRange(tag, node.parentNode);
|
||||
};
|
||||
|
||||
var _enclosing_range_search = function(tag, end, end_idx) {
|
||||
var enclosingRangeSearch = function(tag, end, end_idx) {
|
||||
// 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 `end_idx`.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/******************************************************************************/
|
||||
|
||||
var create = function (id, start, end, inner, tag) {
|
||||
var ret = new Meteor.ui._LiveRange(tag || 'a', start, end, inner);
|
||||
var ret = new LiveRange(tag || 'a', start, end, inner);
|
||||
ret.id = id;
|
||||
return ret;
|
||||
};
|
||||
@@ -27,7 +27,7 @@ var dump = function (what, tag) {
|
||||
|
||||
if (typeof what === 'object' && what.nodeType === 11 /* DocumentFragment */) {
|
||||
if (what.firstChild) {
|
||||
var range = new Meteor.ui._LiveRange(tag || 'a', what);
|
||||
var range = new LiveRange(tag || 'a', what);
|
||||
range.visit(emit, emit);
|
||||
range.destroy();
|
||||
}
|
||||
@@ -43,11 +43,11 @@ var dump = function (what, tag) {
|
||||
// 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 Meteor.ui._LiveRange)
|
||||
if (actual instanceof LiveRange)
|
||||
check_liverange_integrity(actual);
|
||||
else {
|
||||
if (actual.firstChild) {
|
||||
var range = new Meteor.ui._LiveRange(tag || 'a', actual);
|
||||
var range = new LiveRange(tag || 'a', actual);
|
||||
check_liverange_integrity(range);
|
||||
range.destroy();
|
||||
}
|
||||
@@ -92,7 +92,7 @@ var assert_contained = function (r, expected) {
|
||||
Tinytest.add("liverange - single node", function (test) {
|
||||
var f = frag("<div id=1></div>");
|
||||
var r_a = create("a", f);
|
||||
test.instanceOf(r_a, Meteor.ui._LiveRange);
|
||||
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: []});
|
||||
@@ -481,7 +481,7 @@ var makeTestPattern = function(codedStr) {
|
||||
// close range
|
||||
var start = starts.pop();
|
||||
var range =
|
||||
new Meteor.ui._LiveRange(
|
||||
new LiveRange(
|
||||
self.tag, start[0].childNodes[start[1]],
|
||||
start[0].lastChild);
|
||||
range.letter = c.toUpperCase();
|
||||
@@ -506,12 +506,12 @@ var makeTestPattern = function(codedStr) {
|
||||
};
|
||||
|
||||
self.findRange = function(node) {
|
||||
return Meteor.ui._LiveRange.findRange(self.tag, node);
|
||||
return LiveRange.findRange(self.tag, node);
|
||||
};
|
||||
|
||||
self.currentString = function() {
|
||||
var buf = [];
|
||||
var tempRange = new Meteor.ui._LiveRange(self.tag, self.frag);
|
||||
var tempRange = new LiveRange(self.tag, self.frag);
|
||||
tempRange.visit(function(is_start, range) {
|
||||
buf.push(is_start ?
|
||||
range.letter.toUpperCase() :
|
||||
@@ -603,11 +603,11 @@ Tinytest.add("liverange - destroy", function(test) {
|
||||
var frag = document.createDocumentFragment();
|
||||
var txt = document.createComment("pudding");
|
||||
frag.appendChild(txt);
|
||||
var rng5 = new Meteor.ui._LiveRange('_pudding', txt);
|
||||
var rng4 = new Meteor.ui._LiveRange('_pudding', txt);
|
||||
var rng3 = new Meteor.ui._LiveRange('_pudding', txt);
|
||||
var rng2 = new Meteor.ui._LiveRange('_pudding', txt);
|
||||
var rng1 = new Meteor.ui._LiveRange('_pudding', 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;
|
||||
|
||||
@@ -118,7 +118,7 @@ Meteor.ui._doc = Meteor.ui._doc || {};
|
||||
Meteor.ui._doc._newAnnotations[id] = null;
|
||||
|
||||
var subFrag = makeFrag(html);
|
||||
var range = new Meteor.ui._LiveRange(Meteor.ui._TAG, subFrag);
|
||||
var range = new LiveRange(Meteor.ui._TAG, subFrag);
|
||||
// assign options to the LiveRange, including `id`
|
||||
_.extend(range, options);
|
||||
// enqueue the new range for callback processing
|
||||
@@ -200,7 +200,7 @@ Meteor.ui._doc = Meteor.ui._doc || {};
|
||||
// properly killing them and cleaning the
|
||||
// nodes. May be called as cleanNodes(fragment) or cleanNodes(node) as well.
|
||||
Meteor.ui._doc.cleanNodes = function(start, end) {
|
||||
var wrapper = new Meteor.ui._LiveRange(Meteor.ui._TAG, start, end);
|
||||
var wrapper = new LiveRange(Meteor.ui._TAG, start, end);
|
||||
wrapper.visit(function (isStart, range) {
|
||||
if (isStart && ! range.dead) {
|
||||
range.dead = true;
|
||||
|
||||
@@ -23,7 +23,7 @@ Tinytest.add("livedocument - assembly", function(test) {
|
||||
test.equal(f.html(), html);
|
||||
|
||||
var actualGroups = [];
|
||||
var tempRange = new Meteor.ui._LiveRange(Meteor.ui._TAG, frag);
|
||||
var tempRange = new LiveRange(Meteor.ui._TAG, frag);
|
||||
tempRange.visit(function(isStart, rng) {
|
||||
if (! isStart)
|
||||
actualGroups.push(Meteor.ui._rangeToHtml(rng));
|
||||
|
||||
@@ -324,7 +324,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
};
|
||||
|
||||
if (! range)
|
||||
range = new Meteor.ui._LiveRange(Meteor.ui._tag, frag);
|
||||
range = new LiveRange(Meteor.ui._tag, frag);
|
||||
|
||||
// Table-body fix: if container is a table and frag
|
||||
// contains a TR, wrap fragment in a TBODY on all browsers,
|
||||
@@ -655,7 +655,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
if (! curNode)
|
||||
return;
|
||||
|
||||
var innerRange = Meteor.ui._LiveRange.findRange(Meteor.ui._tag, curNode);
|
||||
var innerRange = LiveRange.findRange(Meteor.ui._tag, curNode);
|
||||
|
||||
var type = event.type;
|
||||
|
||||
@@ -702,7 +702,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
|
||||
// find the innermost enclosing liverange that has event data
|
||||
var findEventData = function(node) {
|
||||
var innerRange = Meteor.ui._LiveRange.findRange(Meteor.ui._tag, node);
|
||||
var innerRange = LiveRange.findRange(Meteor.ui._tag, node);
|
||||
|
||||
for(var range = innerRange; range; range = range.findParent())
|
||||
if (range.data)
|
||||
@@ -781,7 +781,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
|
||||
// create a temporary range around newFrag in order
|
||||
// to visit it.
|
||||
var tempRange = new Meteor.ui._LiveRange(Meteor.ui._tag, newFrag);
|
||||
var tempRange = new LiveRange(Meteor.ui._tag, newFrag);
|
||||
// visit new frag
|
||||
eachKeyedChunk(tempRange, function(r, path) {
|
||||
var oldRange = oldChunks[path];
|
||||
@@ -828,7 +828,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
var patch = function(tgtParent, srcParent, tgtBefore, tgtAfter, nodeMatches) {
|
||||
|
||||
var copyFunc = function(t, s) {
|
||||
Meteor.ui._LiveRange.transplant_tag(Meteor.ui._tag, t, s);
|
||||
LiveRange.transplant_tag(Meteor.ui._tag, t, s);
|
||||
};
|
||||
|
||||
var patcher = new Meteor.ui._Patcher(
|
||||
@@ -862,7 +862,7 @@ Meteor.ui = Meteor.ui || {};
|
||||
// range match! for constant chunk
|
||||
if (patcher.match(pair[0], pair[1], null, true)) {
|
||||
patcher.skipToSiblings(pair[2], pair[3]);
|
||||
Meteor.ui._LiveRange.transplant_range(
|
||||
LiveRange.transplant_range(
|
||||
pair[0], pair[2], pair[4]);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -91,7 +91,7 @@ Tinytest.add("patcher - basic", function(test) {
|
||||
test.isTrue(ret);
|
||||
assert_html(x, "<i><u>bar</u><s>baz</s></i>");
|
||||
|
||||
var LiveRange = Meteor.ui._LiveRange;
|
||||
var LiveRange = LiveRange;
|
||||
var t = "_foo";
|
||||
var liverange = function(start, end, inner) {
|
||||
return new LiveRange(t, start, end, inner);
|
||||
|
||||
@@ -28,7 +28,7 @@ _.extend(Spark._Renderer.prototype, {
|
||||
annotate: function (html, tag, what) {
|
||||
var id = tag + "-" + this.createId();
|
||||
this.annotations[id] = function (start, end) {
|
||||
var range = new Meteor.ui._LiveRange(tag, start, end);
|
||||
var range = new LiveRange(tag, start, end);
|
||||
if (what instanceof Function)
|
||||
what(range);
|
||||
else
|
||||
@@ -67,7 +67,7 @@ Spark.setContext = function (html, context) {
|
||||
};
|
||||
|
||||
Spark.getContext = function (node) {
|
||||
var range = Meteor.ui._LiveRange.findRange("_context", node);
|
||||
var range = LiveRange.findRange("_context", node);
|
||||
return range && range.context;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user