mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-02-01 11:22:41 +01:00
oof struggling with char offset
This commit is contained in:
parent
8e5c124f5e
commit
81e50061dc
2 changed files with 118 additions and 63 deletions
|
@ -3071,50 +3071,14 @@ function Ace2Inner() {
|
||||||
// boolean - reflects if the user is attempting to highlight content
|
// 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]);
|
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) {
|
if (isPageUp) {
|
||||||
// Approach #99991248928174 to solve this problem....
|
// Approach #99991248928174 to solve this problem....
|
||||||
scroll.movePage('up');
|
scroll.movePage('up');
|
||||||
const firstVisible = scroll.getFirstVisibleCharacter('up', rep);
|
const modifiedRep = scroll.getFirstVisibleCharacter('up', rep);
|
||||||
rep.selStart[0] = firstVisible;
|
rep.selStart[0] = modifiedRep.selStart[0];
|
||||||
rep.selEnd[0] = firstVisible;
|
rep.selEnd[0] = modifiedRep.selEnd[0];
|
||||||
|
rep.selStart[1] = modifiedRep.selStart[1];
|
||||||
|
rep.selEnd[1] = modifiedRep.selEnd[1];
|
||||||
}
|
}
|
||||||
if (isPageDown) {
|
if (isPageDown) {
|
||||||
/** *
|
/** *
|
||||||
|
@ -3124,33 +3088,39 @@ function Ace2Inner() {
|
||||||
const lengthOfLastLine = rep.lines.atIndex(rep.selEnd[0]).width - 1;
|
const lengthOfLastLine = rep.lines.atIndex(rep.selEnd[0]).width - 1;
|
||||||
const endOfLine = lengthOfLastLine === rep.selEnd[1];
|
const endOfLine = lengthOfLastLine === rep.selEnd[1];
|
||||||
const atBottom = (rep.lines.length() - 1) === rep.selEnd[0];
|
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 we are right at the bottom of the document, no need to continue
|
||||||
if (atBottom && endOfLine) return;
|
if (atBottom && endOfLine) return;
|
||||||
|
|
||||||
|
|
||||||
/** *
|
/** *
|
||||||
* Move the actual view
|
* Move the actual view
|
||||||
*/
|
*/
|
||||||
|
|
||||||
scroll.movePage('down');
|
scroll.movePage('down');
|
||||||
|
const hasMoved = originalPosition.top !== scroll._getViewPortTopBottom().top;
|
||||||
|
|
||||||
/** *
|
/** *
|
||||||
* Move the caret
|
* Move the caret
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const firstVisible = scroll.getFirstVisibleCharacter('down', rep);
|
const modifiedRep = scroll.getFirstVisibleCharacter('down', rep);
|
||||||
top.console.log('fB', firstVisible);
|
rep.selStart[0] = modifiedRep.selStart[0];
|
||||||
top.console.log(rep.lines.length());
|
rep.selEnd[0] = modifiedRep.selEnd[0];
|
||||||
|
rep.selStart[1] = modifiedRep.selStart[1];
|
||||||
|
rep.selEnd[1] = modifiedRep.selEnd[1];
|
||||||
|
|
||||||
if (rep.selStart[0] === firstVisible) {
|
top.console.log(rep.lines.length());
|
||||||
// we're at the bottom
|
if (!hasMoved) {
|
||||||
|
// we're at the bottom so select the last bit of content.
|
||||||
rep.selStart[0] = rep.lines.length() - 1;
|
rep.selStart[0] = rep.lines.length() - 1;
|
||||||
rep.selEnd[0] = rep.lines.length() - 1;
|
rep.selEnd[0] = rep.lines.length() - 1;
|
||||||
rep.selStart[1] = rep.lines.atIndex(rep.selStart[0]).length;
|
rep.selStart[1] = rep.lines.atIndex(rep.selStart[0]).length;
|
||||||
rep.selEnd[1] = rep.lines.atIndex(rep.selStart[0]).length;
|
rep.selEnd[1] = rep.lines.atIndex(rep.selStart[0]).length;
|
||||||
} else {
|
} else {
|
||||||
rep.selStart[0] = firstVisible;
|
// we moved, this will need modifying to support remembered x offset
|
||||||
rep.selEnd[0] = firstVisible;
|
rep.selStart[0] = modifiedRep.selStart[0];
|
||||||
|
rep.selEnd[0] = modifiedRep.selEnd[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -369,30 +369,115 @@ Scroll.prototype.movePage = function (direction) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
Scroll.prototype.getFirstVisibleCharacter = function (direction) {
|
Scroll.prototype.getFirstVisibleCharacter = function (direction, rep) {
|
||||||
const viewport = this._getViewPortTopBottom();
|
const viewport = this._getViewPortTopBottom();
|
||||||
console.log('viewport', viewport);
|
console.log('viewport', viewport);
|
||||||
const editor = parent.document.getElementsByTagName('iframe');
|
const editor = parent.document.getElementsByTagName('iframe');
|
||||||
const lines = $(editor).contents().find('div');
|
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) => {
|
$.each(lines, (index, line) => {
|
||||||
const lineTopOffset = $(line).offset().top;
|
// Line height important for supporting long lines that fill viewport.
|
||||||
// console.log(index, line);
|
const lineBase = $(line).offset().top + $(line).height();
|
||||||
// console.log($(line).offset().top);
|
|
||||||
// console.log(index, 'lineTopOffset', lineTopOffset);
|
|
||||||
// console.log('viewport.top', viewport.top);
|
|
||||||
|
|
||||||
// is each line in the viewport?
|
// 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...
|
// Important for supporting long lines.
|
||||||
if (lineTopOffset > viewport.top) {
|
if (modifiedRep.selEnd[0] !== rep.selEnd[0]) willGoToNextLine = true;
|
||||||
// top.console.log('returning', index);
|
return false; // exit $.each because we found a lovely line :)
|
||||||
goToLine = index;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue