diff --git a/www/common/cursor.js b/www/common/cursor.js index 1fca5c590..1bb9224c3 100644 --- a/www/common/cursor.js +++ b/www/common/cursor.js @@ -1,39 +1,19 @@ define([ '/common/treesome.js', '/bower_components/rangy/rangy-core.min.js' -], function (Tree, Rangy) { - - var Rangy = window.Rangy = Rangy; - var Tree = window.Tree = Tree; +], function (Tree, Rangy, saveRestore) { + window.Rangy = Rangy; + window.Tree = Tree; // do some function for the start and end of the cursor - var startAndStop = function (f) { ['start', 'end'].forEach(f); }; - - var log = function (x) { - console.log(x); - }; - var error = function (x) { - console.log(x); - }; + var log = function (x) { console.log(x); }; + var error = function (x) { console.log(x); }; + var verbose = function (x) { if (window.verboseMode) { console.log(x); } }; - var verbose = function (x) { - if (window.verboseMode) { console.log(x); } - }; - - /* takes - the CK lib, - an instantiated editor - the document used by the editor - */ - return function (CK, editor, inner) { + /* accepts the document used by the editor */ + return function (inner) { var cursor = {}; - cursor.CK = CK; - cursor.editor = editor; - - // CKE has its own internal node datatype that we'll need - var makeCKElement = cursor.makeCKElement = function (el) { return new CK.dom.node(el); }; - // there ought to only be one cursor at a time, so let's just // keep it internally var Range = cursor.Range = { @@ -53,102 +33,6 @@ define([ Tree.contains(Range.end.el.$, inner)); }; - // used by vdom.onRemote - // used by vdom.editor.on('change' - // used by vdom.document.on('keyup mouseup' - cursor.update = function () { - verbose("Updating cursor position"); - // get ranges - var ranges = editor.getSelection().getRanges(); - // there should be at least one - if (!ranges.length) { - error("No ranges"); - return; - } - - var range = ranges[0]; - - // get cursor start/end elements and offsets - startAndStop(function (pos) { - var C = pos + 'Container', - O = pos + 'Offset'; - - // if the element has changed - if (!Range[pos].el || Range[pos].el.$ !== range[C].$) { - // update it - Range[pos].el = range[C]; - // FIXME WAT - //Range[pos].tags = Tree.tagsUntilElement(range[C].$, inner); - } - // update offsets no matter what - Range[pos].offset = range[O]; - }); - }; - - // used by vdom.onRemote - cursor.find = function () { - var success = true; - startAndStop(function (pos) { - var tags = Range[pos].tags; - var node = Tree.findSameHierarchy(tags, inner); - - if (node) { - var temp = makeCKElement(node); - if (temp.tagName || temp.nodeName) { - Range[pos].el = temp; - } - } else { - success = false; - } - }); - if (success) { - log("Found cursor!"); - } - return success; - }; - - cursor.shift = function (delta) { - Range.start.offset += delta; - Range.end.offset += delta; - }; - - // used by vdom.onRemote - cursor.replace = function () { - log("Attempting to replace cursor"); - - cursor.find(); - -/* startAndStop(function (pos) { - var el = Range[pos].el; - //Range[pos].el = makeCKElement(el); - Range[pos].el.$.nodeName = el.nodeName || el.tagName; - }); */ - -/* if (cursor.lost()) { - console.log("cursor lost"); - if (!cursor.find()) { - console.log("Couldn't find cursor!"); - return false; - } else { - } - } */ - - var sel = editor.getSelection(); // { rev, document, root, isLocked, _ } - - var ranges = sel.getRanges(); - if (!ranges.length) { - log("No cursor range found"); - if (!range) { - return; - } - } - - range = ranges[0]; // {startContainer, startOffset, endContainer, endOffset, collapsed, document, root} - range.setStart(Range.start.el, Range.start.offset); - range.setEnd(Range.end.el, Range.end.offset); - sel.selectRanges([range]); - }; - // assumes a negative index var seekLeft = cursor.seekLeft = function (el, delta, current) { var textLength; @@ -169,7 +53,7 @@ define([ el = Tree.previousNode(el, inner); if (el) { textLength = el.textContent.length; - if (-delta >= textLength) { + if (-delta > textLength) { delta -= textLength; } else { current = textLength + delta; @@ -210,9 +94,15 @@ define([ 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: previous.textContent.length -1, + offset: textLength, error: "out of bounds" }; } @@ -233,66 +123,89 @@ define([ } else { result = { el: el, - offset: offset + offset: current }; } + } else { + error("[seekToDelta] el is undefined"); } return result; }; - cursor.seekTest = function (delta) { - var start = cursor.Range.start; - var last = seekToDelta(start.el.$, delta, start.offset); - - if (last.error) { - return last.error; - } - - var result = seekToDelta(last.el, -delta, last.offset); + /* 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 - if (start.el.$ !== result.el) { - return "didn't find the right element"; - } - if (start.offset !== result.offset) { - return "didn't find the right offset"; - } + 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 () { }; + + /* - /* FIXME - TypeError: a.getLength is not a function */ - cursor.delta = function (d) { - //d = d === 0? 0 : d < 0? -1: 1; - //cursor.Range.start.offset += d; + cursor.recover = function () { }; - //var temp = cursor.Range.start. - /* seekToOffset(cursor.Range.start.el.$, d); // might be null - if seeking backward and result is null, drop them at the start of the document - if seeking forward and result is null, drop them at the end - */ + 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(); - var Selected = ['start', 'end'].map(function (pos) { - var el = cursor.Range[pos].el, - offset = cursor.Range[pos].offset; - return seekToDelta(el, d, cursor.Range[pos].offset); - }); + /* + 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 + */ - /* TODO validate Selected */ - if (!(Selected[0] && Selected[1])) { - // one of the cursor positions is undefined - return; + // 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 { - startAndStop(function (pos, index) { - cursor.Range[pos].el = makeCKElement(Selected[index].el); - cursor.Range[pos].offset = Selected[index].offset; - }); + range.setStart(start.el, start.offset); + range.setEnd(end.el, end.offset); } - var ranges = editor.getSelection().getRanges(); - ranges[0].setStart(cursor.Range.start.el, cursor.Range.start.offset); - var sel = editor.getSelection(); - sel.selectRanges([ranges[0]]); + // 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;