|
|
|
@ -127,12 +127,90 @@ define([
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
error("[seekToDelta] el is undefined");
|
|
|
|
|
error("[cursor.seekToDelta] el is undefined");
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* cursor.update takes notes about wherever the cursor was last seen
|
|
|
|
|
var checkFromEnd = cursor.checkFromEnd = function (root, predicate) {
|
|
|
|
|
var last = Tree.rightmostNode(root);
|
|
|
|
|
var success = false;
|
|
|
|
|
var i = 0;
|
|
|
|
|
while (last && !success) {
|
|
|
|
|
success = predicate(last, i++);
|
|
|
|
|
last = Tree.previousNode(last, root);
|
|
|
|
|
}
|
|
|
|
|
return success;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* uses closure to capture the root node because I'm lazy
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
var getTrailingNodes = cursor.getTrailingNodes = function (el) {
|
|
|
|
|
var trailing = [];
|
|
|
|
|
var success = cursor.checkFromEnd(inner, function (cur) {
|
|
|
|
|
if (cur === el) {
|
|
|
|
|
trailing.push(cur);
|
|
|
|
|
return true;
|
|
|
|
|
} else { trailing.push(cur); }
|
|
|
|
|
});
|
|
|
|
|
return success && trailing;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var recoverNodeByTrailing = cursor.recoverNodeByTrailing = function (trailing) {
|
|
|
|
|
// clone the array
|
|
|
|
|
var T = trailing.slice(0);
|
|
|
|
|
var L = T.length;
|
|
|
|
|
var el = null;
|
|
|
|
|
cursor.checkFromEnd(inner, function (cur, i) {
|
|
|
|
|
if (i >= L) {
|
|
|
|
|
//console.log("[cursor.recoverNodeByTrailing] out of bounds");
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
if (cur.nodeName !== T[i].nodeName) {
|
|
|
|
|
console.log("[cursor.recoverNodeByTrailing] false name");
|
|
|
|
|
console.log(cur);
|
|
|
|
|
return true;
|
|
|
|
|
} else if (cur.nodeName === T[i].nodeName && i === L - 1) {
|
|
|
|
|
el = cur;
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return el;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* cursor.find uses information produced by side effects of 'update'
|
|
|
|
|
to recover the cursor
|
|
|
|
|
*/
|
|
|
|
|
cursor.find = function () {
|
|
|
|
|
['start', 'end'].forEach(function (pos) {
|
|
|
|
|
// is this metric even reliable?
|
|
|
|
|
var node;
|
|
|
|
|
var trailing = Range[pos].trailing;
|
|
|
|
|
if (Range[pos].lost) {
|
|
|
|
|
if (trailing.length) {
|
|
|
|
|
// use the trailing nodes to find the node..
|
|
|
|
|
node = recoverNodeByTrailing(trailing);
|
|
|
|
|
}
|
|
|
|
|
if (node) {
|
|
|
|
|
// if that worked....great!
|
|
|
|
|
Range[pos].el = node;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// if not, try falling back to the nearest parent?
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// it wasn't lost, you don't need to do anything.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* cursor.update takes notes about wherever the cursor was last seen
|
|
|
|
|
in the event of a cursor loss, the information produced by side
|
|
|
|
|
effects of this function should be used to recover the cursor
|
|
|
|
|
|
|
|
|
@ -142,31 +220,97 @@ define([
|
|
|
|
|
verbose("cursor.update");
|
|
|
|
|
root = root || inner;
|
|
|
|
|
sel = sel || Rangy.getSelection(root);
|
|
|
|
|
//if (!sel.rangeCount) { return 'no ranges found'; }
|
|
|
|
|
// FIXME under what circumstances are no ranges found?
|
|
|
|
|
if (!sel.rangeCount) {
|
|
|
|
|
error('[cursor.update] no ranges found');
|
|
|
|
|
//return 'no ranges found';
|
|
|
|
|
}
|
|
|
|
|
var range = sel.getRangeAt(0);
|
|
|
|
|
|
|
|
|
|
// Big R Range is caught in closure, and maintains persistent state
|
|
|
|
|
Range.start.el = range.startContainer;
|
|
|
|
|
Range.start.offset = range.startOffset;
|
|
|
|
|
Range.start.parents = Tree.parentsOf(Range.start.el, root);
|
|
|
|
|
|
|
|
|
|
Range.end.el = range.endContainer;
|
|
|
|
|
Range.end.offset = range.endOffset;
|
|
|
|
|
Range.end.parents = Tree.parentsOf(Range.end.el, root);
|
|
|
|
|
['start', 'end'].forEach(function (pos) {
|
|
|
|
|
Range[pos].el = range[pos+'Container'];
|
|
|
|
|
Range[pos].offset = range[pos+'Offset'];
|
|
|
|
|
Range[pos].parents = Tree.parentsOf(Range[pos].el, root);
|
|
|
|
|
|
|
|
|
|
// trailing is either an array or false
|
|
|
|
|
var trailing = getTrailingNodes(Range[pos].el);
|
|
|
|
|
|
|
|
|
|
// if it's false, then the cursor has been lost
|
|
|
|
|
// TODO do a more careful check to see if the cursor has been destroyed.
|
|
|
|
|
Range[pos].lost = !trailing;
|
|
|
|
|
|
|
|
|
|
if (Range[pos].lost) {
|
|
|
|
|
// don't overwrite the previous trailing nodes
|
|
|
|
|
// that array will be used to recover the cursor
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// if you haven't lost the cursor, update the node list
|
|
|
|
|
Range[pos].trailing = trailing;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* cursor.find uses information produced by side effects of 'update'
|
|
|
|
|
to recover the cursor
|
|
|
|
|
*/
|
|
|
|
|
cursor.find = function () { };
|
|
|
|
|
/* 0 -> neither is lost
|
|
|
|
|
1 -> start is lost
|
|
|
|
|
2 -> end is lost
|
|
|
|
|
3 -> both are lost */
|
|
|
|
|
var isLost = cursor.isLost = function () {
|
|
|
|
|
var state = ['start', 'end'].map(function (pos, i) {
|
|
|
|
|
return Tree.contains(Range[pos].el, inner)? 0 : i + 1;
|
|
|
|
|
});
|
|
|
|
|
return state[0] | state[1];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
var recover = cursor.recover = function (lost) {
|
|
|
|
|
var sel = Rangy.getSelection(inner);
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
cursor.recover = function () { };
|
|
|
|
|
// create a range to modify
|
|
|
|
|
var range = Rangy.createRange();
|
|
|
|
|
|
|
|
|
|
/* if range is backwards, cursor.delta fails
|
|
|
|
|
so check if they're in the expected order
|
|
|
|
|
before setting the new range */
|
|
|
|
|
|
|
|
|
|
if (lost & 1) {
|
|
|
|
|
Range.start.el = recoverNodeByTrailing(Range.start.trailing);
|
|
|
|
|
}
|
|
|
|
|
if (lost & 2) {
|
|
|
|
|
Range.end.el = recoverNodeByTrailing(Range.end.trailing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// el.parentNode is null
|
|
|
|
|
var order = Tree.orderOfNodes(Range.start.el, Range.end.el, inner);
|
|
|
|
|
var backward;
|
|
|
|
|
|
|
|
|
|
// this could all be one line but nobody would be able to read it
|
|
|
|
|
if (order === -1) {
|
|
|
|
|
// definitely backward
|
|
|
|
|
backward = true;
|
|
|
|
|
} else if (order === 0) {
|
|
|
|
|
// might be backward, check offsets to know for sure
|
|
|
|
|
backward = (Range.start.offset > Range.end.offset);
|
|
|
|
|
} else {
|
|
|
|
|
// definitely not backward
|
|
|
|
|
backward = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor.delta = function (delta, collapse) {
|
|
|
|
|
if (backward) {
|
|
|
|
|
range.setStart(Range.end.el, Range.end.offset);
|
|
|
|
|
range.setEnd(Range.start.el, Range.start.offset);
|
|
|
|
|
} else {
|
|
|
|
|
range.setStart(Range.start.el, Range.start.offset);
|
|
|
|
|
range.setEnd(Range.end.el, Range.end.offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// actually set the cursor to the new range
|
|
|
|
|
sel.setSingleRange(range);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
cursor.delta = function (delta1, delta2) {
|
|
|
|
|
var sel = Rangy.getSelection(inner);
|
|
|
|
|
delta2 = (typeof delta2 !== 'undefined') ? delta2 : delta1;
|
|
|
|
|
|
|
|
|
|
// update returns errors if there are problems
|
|
|
|
|
// and updates the persistent Range object
|
|
|
|
@ -184,13 +328,29 @@ define([
|
|
|
|
|
|
|
|
|
|
// using infromation about wherever you were last...
|
|
|
|
|
// move both parts by some delta
|
|
|
|
|
var start = seekToDelta(Range.start.el, delta, Range.start.offset);
|
|
|
|
|
var end = seekToDelta(Range.end.el, delta, Range.end.offset);
|
|
|
|
|
var start = seekToDelta(Range.start.el, delta1, Range.start.offset);
|
|
|
|
|
var end = seekToDelta(Range.end.el, delta2, Range.end.offset);
|
|
|
|
|
|
|
|
|
|
/* if range is backwards, cursor.delta fails
|
|
|
|
|
so check if they're in the expected order
|
|
|
|
|
before setting the new range */
|
|
|
|
|
if (Tree.orderOfNodes(start.el, end.el, inner) === -1) {
|
|
|
|
|
|
|
|
|
|
var order = Tree.orderOfNodes(start.el, end.el, inner);
|
|
|
|
|
var backward;
|
|
|
|
|
|
|
|
|
|
// this could all be one line but nobody would be able to read it
|
|
|
|
|
if (order === -1) {
|
|
|
|
|
// definitely backward
|
|
|
|
|
backward = true;
|
|
|
|
|
} else if (order === 0) {
|
|
|
|
|
// might be backward, check offsets to know for sure
|
|
|
|
|
backward = (start.offset > end.offset);
|
|
|
|
|
} else {
|
|
|
|
|
// definitely not backward
|
|
|
|
|
backward = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (backward) {
|
|
|
|
|
range.setStart(end.el, end.offset);
|
|
|
|
|
range.setEnd(start.el, start.offset);
|
|
|
|
|
} else {
|
|
|
|
@ -200,12 +360,10 @@ define([
|
|
|
|
|
|
|
|
|
|
// actually set the cursor to the new range
|
|
|
|
|
sel.setSingleRange(range);
|
|
|
|
|
if (delta < 0) {
|
|
|
|
|
// seeking left, so start might have an error
|
|
|
|
|
return start.error;
|
|
|
|
|
} else {
|
|
|
|
|
return end.error;
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
startError: start.error,
|
|
|
|
|
endError: end.error
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return cursor;
|
|
|
|
|