AtteanX-Endpoint
view release on metacpan or search on metacpan
share/endpoint/www/js/editor.js view on Meta::CPAN
// parts is an interface to make it possible to 'delay' fetching
// the next DOM node until we are completely done with the one
// before it. This is necessary because often the next node is
// not yet available when we want to proceed past the current
// one.
var parts = {
current: null,
// Fetch current node.
get: function(){
if (!this.current)
this.current = traversal.nodes.shift();
return this.current;
},
// Advance to the next part (do not fetch it yet).
next: function(){
this.current = null;
},
// Remove the current part from the DOM tree, and move to the
// next.
remove: function(){
container.removeChild(this.get());
this.current = null;
},
// Advance to the next part that is not empty, discarding empty
// parts.
getNonEmpty: function(){
var part = this.get();
// Allow empty nodes when they are alone on a line, needed
// for the FF cursor bug workaround (see select.js,
// insertNewlineAtCursor).
while (part && isSpan(part) && part.currentText == "") {
// Leave empty nodes that are alone on a line alone in
// Opera, since that browsers doesn't deal well with
// having 2 BRs in a row.
if (window.opera && surroundedByBRs(part)) {
this.next();
part = this.get();
}
else {
var old = part;
this.remove();
part = this.get();
// Adjust selection information, if any. See select.js for details.
select.snapshotMove(old.firstChild, part && (part.firstChild || part), 0);
}
}
return part;
}
};
var lineDirty = false, prevLineDirty = true, lineNodes = 0;
// This forEach loops over the tokens from the parsed stream, and
// at the same time uses the parts object to proceed through the
// corresponding DOM nodes.
forEach(parsed, function(token){
var part = parts.getNonEmpty();
if (token.value == "\n"){
// The idea of the two streams actually staying synchronized
// is such a long shot that we explicitly check.
if (!isBR(part))
throw "Parser out of sync. Expected BR.";
if (part.dirty || !part.indentation) lineDirty = true;
maybeTouch(from);
from = part;
// Every <br> gets a copy of the parser state and a lexical
// context assigned to it. The first is used to be able to
// later resume parsing from this point, the second is used
// for indentation.
part.parserFromHere = parsed.copy();
part.indentation = token.indentation || alwaysZero;
part.dirty = false;
// If the target argument wasn't an integer, go at least
// until that node.
if (endTime == null && part == target) throw StopIteration;
// A clean line with more than one node means we are done.
// Throwing a StopIteration is the way to break out of a
// MochiKit forEach loop.
if ((endTime != null && time() >= endTime) || (!lineDirty && !prevLineDirty && lineNodes > 1 && !cleanLines))
throw StopIteration;
prevLineDirty = lineDirty; lineDirty = false; lineNodes = 0;
parts.next();
}
else {
if (!isSpan(part))
throw "Parser out of sync. Expected SPAN.";
if (part.dirty)
lineDirty = true;
lineNodes++;
// If the part matches the token, we can leave it alone.
if (correctPart(token, part)){
part.dirty = false;
parts.next();
}
// Otherwise, we have to fix it.
else {
lineDirty = true;
// Insert the correct part.
var newPart = tokenPart(token);
container.insertBefore(newPart, part);
if (active) active(newPart, token, self);
var tokensize = token.value.length;
var offset = 0;
// Eat up parts until the text for this token has been
// removed, adjusting the stored selection info (see
// select.js) in the process.
while (tokensize > 0) {
part = parts.get();
var partsize = part.currentText.length;
select.snapshotReplaceNode(part.firstChild, newPart.firstChild, tokensize, offset);
if (partsize > tokensize){
shortenPart(part, tokensize);
tokensize = 0;
}
( run in 0.706 second using v1.01-cache-2.11-cpan-f56aa216473 )