AtteanX-Endpoint
view release on metacpan or search on metacpan
share/endpoint/www/js/undo.js view on Meta::CPAN
// Note that this makes the loop skip line objects that have
// been pulled into chains by lines before them.
if (!temp(line.from)) return;
var chain = [], curNode = line.from, safe = true;
// Put any line objects (referred to by temp info) before this
// one on the front of the array.
while (true) {
var curLine = temp(curNode);
if (!curLine) {
if (safe) break;
else curLine = buildLine(curNode);
}
chain.unshift(curLine);
setTemp(curNode, null);
if (!curNode) break;
safe = self.after(curNode);
curNode = nextBR(curNode, "previous");
}
curNode = line.to; safe = self.before(line.from);
// Add lines after this one at end of array.
while (true) {
if (!curNode) break;
var curLine = temp(curNode);
if (!curLine) {
if (safe) break;
else curLine = buildLine(curNode);
}
chain.push(curLine);
setTemp(curNode, null);
safe = self.before(curNode);
curNode = nextBR(curNode, "next");
}
chains.push(chain);
});
return chains;
},
// Find the 'shadow' of a given chain by following the links in the
// DOM nodes at its start and end.
shadowChain: function(chain) {
var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to;
while (true) {
shadows.push(next);
var nextNode = next.to;
if (!nextNode || nextNode == end)
break;
else
next = nextNode.historyAfter || this.before(end);
// (The this.before(end) is a hack -- FF sometimes removes
// properties from BR nodes, in which case the best we can hope
// for is to not break.)
}
return shadows;
},
// Update the DOM tree to contain the lines specified in a given
// chain, link this chain into the DOM nodes.
applyChain: function(chain) {
// Some attempt is made to prevent the cursor from jumping
// randomly when an undo or redo happens. It still behaves a bit
// strange sometimes.
var cursor = select.cursorPos(this.container, false), self = this;
// Remove all nodes in the DOM tree between from and to (null for
// start/end of container).
function removeRange(from, to) {
var pos = from ? from.nextSibling : self.container.firstChild;
while (pos != to) {
var temp = pos.nextSibling;
removeElement(pos);
pos = temp;
}
}
var start = chain[0].from, end = chain[chain.length - 1].to;
// Clear the space where this change has to be made.
removeRange(start, end);
// Insert the content specified by the chain into the DOM tree.
for (var i = 0; i < chain.length; i++) {
var line = chain[i];
// The start and end of the space are already correct, but BR
// tags inside it have to be put back.
if (i > 0)
self.container.insertBefore(line.from, end);
// Add the text.
var node = makePartSpan(fixSpaces(line.text));
self.container.insertBefore(node, end);
// See if the cursor was on this line. Put it back, adjusting
// for changed line length, if it was.
if (cursor && cursor.node == line.from) {
var cursordiff = 0;
var prev = this.after(line.from);
if (prev && i == chain.length - 1) {
// Only adjust if the cursor is after the unchanged part of
// the line.
for (var match = 0; match < cursor.offset &&
line.text.charAt(match) == prev.text.charAt(match); match++){}
if (cursor.offset > match)
cursordiff = line.text.length - prev.text.length;
}
select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)});
}
// Cursor was in removed line, this is last new line.
else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) {
select.setCursorPos(this.container, {node: line.from, offset: line.text.length});
}
}
// Anchor the chain in the DOM tree.
this.linkChain(chain);
return start;
}
};
( run in 1.590 second using v1.01-cache-2.11-cpan-d8267643d1d )