From 81e50061dc689f15129ac87efa40d63913645f24 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 2 Jan 2021 16:30:32 +0000 Subject: [PATCH] oof struggling with char offset --- src/static/js/ace2_inner.js | 68 ++++++---------------- src/static/js/scroll.js | 113 +++++++++++++++++++++++++++++++----- 2 files changed, 118 insertions(+), 63 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index bfdc2203d..16ea146fb 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3071,50 +3071,14 @@ function Ace2Inner() { // boolean - reflects if the user is attempting to highlight content const highlighting = shiftKey && (rep.selStart[0] !== rep.selEnd[0] || rep.selStart[1] !== rep.selEnd[1]); - // input is a line - // returned is the number of characters in that index that are currently - // visible in the viewport - // TODO CAKE JM - const getVisibleCharRangeOfLineInViewport = (line) => { - const range = document.createRange(); - const chars = line.text.split(''); // split "abc" into ["a","b","c"] - const parentElement = document.getElementById(line.domInfo.node.id).childNodes; - // top.console.log(parentElement); - const nodeArray = Array.from(document.body.childNodes).filter; - // top.console.log(nodeArray); - /* - const characterTopOffset = []; - for (const node of parentElement) { - // each span.. - top.console.log('span', node); // shows all nodes from the collection - top.console.log('span length', node.offsetTop); // shows all nodes from the collection - - // now do each character - let i = 0; - while (i < node.textContent.length) { - top.console.log(i, node.textContent[i]); - const range = document.createRange(); - range.setStart(node, i); - range.setEnd(node, i + 1); - const char = range.getClientRects(); - if (char.length) { - for (const element in chars) { - top.console.log(element.top); - } - } - // above is broken.. - i++; - } - } - */ - return 1000; - }; if (isPageUp) { // Approach #99991248928174 to solve this problem.... scroll.movePage('up'); - const firstVisible = scroll.getFirstVisibleCharacter('up', rep); - rep.selStart[0] = firstVisible; - rep.selEnd[0] = firstVisible; + const modifiedRep = scroll.getFirstVisibleCharacter('up', rep); + rep.selStart[0] = modifiedRep.selStart[0]; + rep.selEnd[0] = modifiedRep.selEnd[0]; + rep.selStart[1] = modifiedRep.selStart[1]; + rep.selEnd[1] = modifiedRep.selEnd[1]; } if (isPageDown) { /** * @@ -3124,33 +3088,39 @@ function Ace2Inner() { const lengthOfLastLine = rep.lines.atIndex(rep.selEnd[0]).width - 1; const endOfLine = lengthOfLastLine === rep.selEnd[1]; const atBottom = (rep.lines.length() - 1) === rep.selEnd[0]; + const originalPosition = scroll._getViewPortTopBottom(); + // If we are right at the bottom of the document, no need to continue if (atBottom && endOfLine) return; - /** * * Move the actual view */ scroll.movePage('down'); + const hasMoved = originalPosition.top !== scroll._getViewPortTopBottom().top; /** * * Move the caret */ - const firstVisible = scroll.getFirstVisibleCharacter('down', rep); - top.console.log('fB', firstVisible); - top.console.log(rep.lines.length()); + const modifiedRep = scroll.getFirstVisibleCharacter('down', rep); + rep.selStart[0] = modifiedRep.selStart[0]; + rep.selEnd[0] = modifiedRep.selEnd[0]; + rep.selStart[1] = modifiedRep.selStart[1]; + rep.selEnd[1] = modifiedRep.selEnd[1]; - if (rep.selStart[0] === firstVisible) { - // we're at the bottom + top.console.log(rep.lines.length()); + if (!hasMoved) { + // we're at the bottom so select the last bit of content. rep.selStart[0] = rep.lines.length() - 1; rep.selEnd[0] = rep.lines.length() - 1; rep.selStart[1] = rep.lines.atIndex(rep.selStart[0]).length; rep.selEnd[1] = rep.lines.atIndex(rep.selStart[0]).length; } else { - rep.selStart[0] = firstVisible; - rep.selEnd[0] = firstVisible; + // we moved, this will need modifying to support remembered x offset + rep.selStart[0] = modifiedRep.selStart[0]; + rep.selEnd[0] = modifiedRep.selEnd[0]; } } diff --git a/src/static/js/scroll.js b/src/static/js/scroll.js index b31623f78..136eb4f27 100644 --- a/src/static/js/scroll.js +++ b/src/static/js/scroll.js @@ -369,30 +369,115 @@ Scroll.prototype.movePage = function (direction) { return; }; -Scroll.prototype.getFirstVisibleCharacter = function (direction) { +Scroll.prototype.getFirstVisibleCharacter = function (direction, rep) { const viewport = this._getViewPortTopBottom(); console.log('viewport', viewport); const editor = parent.document.getElementsByTagName('iframe'); const lines = $(editor).contents().find('div'); - let goToLine = 0; + // const currentLine = $(editor).contents().find('#innerdocbody'); + const currentLine = rep.lines.atIndex(rep.selEnd[0]); + console.log('currentLine', currentLine); + const modifiedRep = {}; + modifiedRep.selStart = []; + modifiedRep.selEnd = []; + let willGoToNextLine = false; + // we have moved the viewport at this point, we want to know which + // line is visible? $.each(lines, (index, line) => { - const lineTopOffset = $(line).offset().top; - // console.log(index, line); - // console.log($(line).offset().top); - // console.log(index, 'lineTopOffset', lineTopOffset); - // console.log('viewport.top', viewport.top); + // Line height important for supporting long lines that fill viewport. + const lineBase = $(line).offset().top + $(line).height(); // is each line in the viewport? + if (lineBase > viewport.top) { + top.console.log('returning', index); + modifiedRep.selEnd[0] = index; + modifiedRep.selStart[0] = index; + modifiedRep.selEnd[1] = 0; + modifiedRep.selStart[1] = 0; - // JM TODO Long lines... - if (lineTopOffset > viewport.top) { - // top.console.log('returning', index); - goToLine = index; - return false; + // Important for supporting long lines. + if (modifiedRep.selEnd[0] !== rep.selEnd[0]) willGoToNextLine = true; + return false; // exit $.each because we found a lovely line :) } }); - // go to this rep. - return goToLine; + + if (willGoToNextLine) return modifiedRep; + + // oh dear, looks like the original line is still the first in the viewport.. + // we will need to move the rep X chars within that original position. + console.log('CANT SEE NEXT LiNE!'); + modifiedRep.selStart[0] = rep.selStart[0]; + modifiedRep.selEnd[0] = rep.selEnd[0]; + + const numberOfVisibleChars = this.getCountOfVisibleCharsInViewport(currentLine, viewport); + + // TODO, figure out how many chars are visible in line. + modifiedRep.selStart[1] = rep.selStart[1] + numberOfVisibleChars; + modifiedRep.selEnd[1] = rep.selEnd[1] + numberOfVisibleChars; + return modifiedRep; +}; + +// line is a DOM Line +// returned is the number of characters in that index that are currently visible +// IE 120,240 +Scroll.prototype.getCountOfVisibleCharsInViewport = (line, viewport) => { + const range = document.createRange(); + const chars = line.text.split(''); // split "abc" into ["a","b","c"] + const parentElement = document.getElementById(line.domInfo.node.id).childNodes; + const charNumber = []; + // top.console.log(parentElement); + for (let node of parentElement) { + // each span.. + // top.console.log('span', node); // shows all nodes from the collection + // top.console.log('span length', node.offsetTop); // shows all nodes from the collection + + // each character + let i = 0; + console.log(node); + node = node.childNodes[0]; + if (node.childNodes && node.childNodes[1].length === 0) return; + console.log(node); + console.log(node.wholeText.length); + while (i < node.wholeText.length) { + // top.console.log(i, node.textContent[i]); + const range = document.createRange(); + let failed = false; + try { + range.setStart(node, i); + } catch (e) { + failed = true; + console.log('fail', e); + // console.log('node', node); + } + try { + range.setEnd(node, i + 1); + } catch (e) { + failed = true; + console.log('fail', e); + console.log('node', node); + } + // console.log('range', range); + let char; + if (!failed) char = range.getClientRects(); + console.log(node); + console.log('charr????', char); + if (char) return; + if (char && char.length && char[0]) { + const topOffset = char[0].y; + charNumber.push(topOffset); + // is this element in view? + console.log('topOffset', topOffset, 'viewport', viewport); + if (topOffset > viewport.top) { + console.log('can put rep here!', i); + return; + } + } + i++; + } + top.console.log('charNumber', charNumber); + return; // TEMPJM CAKE remove once stable + } + return 1000; };