define([ '/common/treesome.js', '/bower_components/rangy/rangy-core.min.js' ], function (Tree, Rangy, saveRestore) { window.Rangy = Rangy; window.Tree = Tree; // do some function for the start and end of the cursor var log = function (x) { console.log(x); }; var error = function (x) { console.log(x); }; var verbose = function (x) { if (window.verboseMode) { console.log(x); } }; /* accepts the document used by the editor */ return function (inner) { var cursor = {}; // there ought to only be one cursor at a time, so let's just // keep it internally var Range = cursor.Range = { start: { el: null, offset: 0 }, end: { el: null, offset:0 } }; /* FIXME we shouldn't use this, as only one might have been lost */ cursor.lost = function () { return !(Tree.contains(Range.start.el.$, inner) && Tree.contains(Range.end.el.$, inner)); }; // assumes a negative index var seekLeft = cursor.seekLeft = function (el, delta, current) { var textLength; var previous; // normalize if (-delta >= current) { delta += current; current = 0; } else { current += delta; delta = 0; } while (delta) { previous = el; el = Tree.previousNode(el, inner); if (el) { textLength = el.textContent.length; if (-delta > textLength) { delta -= textLength; } else { current = textLength + delta; delta = 0; } } else { return { el: previous, offset: 0, error: "out of bounds" }; } } return { el: el, offset: current }; }; // seekRight assumes a positive delta var seekRight = cursor.seekRight = function (el, delta, current) { var textLength; var previous; // normalize delta += current; current = 0; while (delta) { if (el) { textLength = el.textContent.length; if (delta >= textLength) { delta -= textLength; previous = el; el = Tree.nextNode(el, inner); } else { current = delta; delta = 0; } } else { // don't ever return a negative index if (previous.textContent.length) { textLength = previous.textContent.length - 1; } else { textLength = 0; } return { el: previous, offset: textLength, error: "out of bounds" }; } } return { el: el, offset: current }; }; var seekToDelta = cursor.seekToDelta = function (el, delta, current) { var result = null; if (el) { if (delta < 0) { return seekLeft(el, delta, current); } else if (delta > 0) { return seekRight(el, delta, current); } else { result = { el: el, offset: current }; } } else { error("[seekToDelta] el is undefined"); } return result; }; /* 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 returns an error string if no range is found */ cursor.update = function (sel, root) { verbose("cursor.update"); root = root || inner; sel = sel || Rangy.getSelection(root); //if (!sel.rangeCount) { 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); }; /* cursor.find uses information produced by side effects of 'update' to recover the cursor */ cursor.find = function () { }; /* */ cursor.recover = function () { }; cursor.delta = function (delta, collapse) { var sel = Rangy.getSelection(inner); // update returns errors if there are problems // and updates the persistent Range object var err = cursor.update(sel, inner); if (err) { return err; } // create a range to modify var range = Rangy.createRange(); /* The assumption below is that Range.(start|end).el actually exists. This might not be the case. TODO check if start and end elements are defined */ // 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); /* 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) { range.setStart(end.el, end.offset); range.setEnd(start.el, start.offset); } else { range.setStart(start.el, start.offset); range.setEnd(end.el, end.offset); } // 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 cursor; }; });