lint: caretPosition linting

This commit is contained in:
John McLear 2020-12-22 15:01:20 +00:00
parent aeab9cc0ad
commit 1d57d4ee3f

View file

@ -1,18 +1,20 @@
'use strict';
// One rep.line(div) can be broken in more than one line in the browser. // One rep.line(div) can be broken in more than one line in the browser.
// This function is useful to get the caret position of the line as // This function is useful to get the caret position of the line as
// is represented by the browser // is represented by the browser
exports.getPosition = function () { exports.getPosition = () => {
let rect, line; let rect, line;
const editor = $('#innerdocbody')[0];
const range = getSelectionRange(); const range = getSelectionRange();
const isSelectionInsideTheEditor = range && $(range.endContainer).closest('body')[0].id === 'innerdocbody'; const isSelectionInsideTheEditor = range &&
$(range.endContainer).closest('body')[0].id === 'innerdocbody';
if (isSelectionInsideTheEditor) { if (isSelectionInsideTheEditor) {
// when we have the caret in an empty line, e.g. a line with only a <br>, // when we have the caret in an empty line, e.g. a line with only a <br>,
// getBoundingClientRect() returns all dimensions value as 0 // getBoundingClientRect() returns all dimensions value as 0
const selectionIsInTheBeginningOfLine = range.endOffset > 0; const selectionIsInTheBeginningOfLine = range.endOffset > 0;
if (selectionIsInTheBeginningOfLine) { if (selectionIsInTheBeginningOfLine) {
var clonedRange = createSelectionRange(range); const clonedRange = createSelectionRange(range);
line = getPositionOfElementOrSelection(clonedRange); line = getPositionOfElementOrSelection(clonedRange);
clonedRange.detach(); clonedRange.detach();
} }
@ -20,7 +22,7 @@ exports.getPosition = function () {
// when there's a <br> or any element that has no height, we can't get // when there's a <br> or any element that has no height, we can't get
// the dimension of the element where the caret is // the dimension of the element where the caret is
if (!rect || rect.height === 0) { if (!rect || rect.height === 0) {
var clonedRange = createSelectionRange(range); const clonedRange = createSelectionRange(range);
// as we can't get the element height, we create a text node to get the dimensions // as we can't get the element height, we create a text node to get the dimensions
// on the position // on the position
@ -36,8 +38,8 @@ exports.getPosition = function () {
return line; return line;
}; };
var createSelectionRange = function (range) { const createSelectionRange = (range) => {
clonedRange = range.cloneRange(); const clonedRange = range.cloneRange();
// we set the selection start and end to avoid error when user selects a text bigger than // we set the selection start and end to avoid error when user selects a text bigger than
// the viewport height and uses the arrow keys to expand the selection. In this particular // the viewport height and uses the arrow keys to expand the selection. In this particular
@ -48,7 +50,7 @@ var createSelectionRange = function (range) {
return clonedRange; return clonedRange;
}; };
const getPositionOfRepLineAtOffset = function (node, offset) { const getPositionOfRepLineAtOffset = (node, offset) => {
// it is not a text node, so we cannot make a selection // it is not a text node, so we cannot make a selection
if (node.tagName === 'BR' || node.tagName === 'EMPTY') { if (node.tagName === 'BR' || node.tagName === 'EMPTY') {
return getPositionOfElementOrSelection(node); return getPositionOfElementOrSelection(node);
@ -66,7 +68,7 @@ const getPositionOfRepLineAtOffset = function (node, offset) {
return linePosition; return linePosition;
}; };
function getPositionOfElementOrSelection(element) { const getPositionOfElementOrSelection = (element) => {
const rect = element.getBoundingClientRect(); const rect = element.getBoundingClientRect();
const linePosition = { const linePosition = {
bottom: rect.bottom, bottom: rect.bottom,
@ -74,15 +76,15 @@ function getPositionOfElementOrSelection(element) {
top: rect.top, top: rect.top,
}; };
return linePosition; return linePosition;
} };
// here we have two possibilities: // here we have two possibilities:
// [1] the line before the caret line has the same type, so both of them has the same margin, padding // [1] the line before the caret line has the same type, so both of them has the same margin,
// height, etc. So, we can use the caret line to make calculation necessary to know where is the top // padding height, etc. So, we can use the caret line to make calculation necessary to know
// of the previous line // where is the top of the previous line
// [2] the line before is part of another rep line. It's possible this line has different margins // [2] the line before is part of another rep line. It's possible this line has different margins
// height. So we have to get the exactly position of the line // height. So we have to get the exactly position of the line
exports.getPositionTopOfPreviousBrowserLine = function (caretLinePosition, rep) { exports.getPositionTopOfPreviousBrowserLine = (caretLinePosition, rep) => {
let previousLineTop = caretLinePosition.top - caretLinePosition.height; // [1] let previousLineTop = caretLinePosition.top - caretLinePosition.height; // [1]
const isCaretLineFirstBrowserLine = caretLineIsFirstBrowserLine(caretLinePosition.top, rep); const isCaretLineFirstBrowserLine = caretLineIsFirstBrowserLine(caretLinePosition.top, rep);
@ -91,13 +93,14 @@ exports.getPositionTopOfPreviousBrowserLine = function (caretLinePosition, rep)
if (isCaretLineFirstBrowserLine) { // [2] if (isCaretLineFirstBrowserLine) { // [2]
const lineBeforeCaretLine = rep.selStart[0] - 1; const lineBeforeCaretLine = rep.selStart[0] - 1;
const firstLineVisibleBeforeCaretLine = getPreviousVisibleLine(lineBeforeCaretLine, rep); const firstLineVisibleBeforeCaretLine = getPreviousVisibleLine(lineBeforeCaretLine, rep);
const linePosition = getDimensionOfLastBrowserLineOfRepLine(firstLineVisibleBeforeCaretLine, rep); const linePosition =
getDimensionOfLastBrowserLineOfRepLine(firstLineVisibleBeforeCaretLine, rep);
previousLineTop = linePosition.top; previousLineTop = linePosition.top;
} }
return previousLineTop; return previousLineTop;
}; };
function caretLineIsFirstBrowserLine(caretLineTop, rep) { const caretLineIsFirstBrowserLine = (caretLineTop, rep) => {
const caretRepLine = rep.selStart[0]; const caretRepLine = rep.selStart[0];
const lineNode = rep.lines.atIndex(caretRepLine).lineNode; const lineNode = rep.lines.atIndex(caretRepLine).lineNode;
const firstRootNode = getFirstRootChildNode(lineNode); const firstRootNode = getFirstRootChildNode(lineNode);
@ -105,37 +108,28 @@ function caretLineIsFirstBrowserLine(caretLineTop, rep) {
// to get the position of the node we get the position of the first char // to get the position of the node we get the position of the first char
const positionOfFirstRootNode = getPositionOfRepLineAtOffset(firstRootNode, 1); const positionOfFirstRootNode = getPositionOfRepLineAtOffset(firstRootNode, 1);
return positionOfFirstRootNode.top === caretLineTop; return positionOfFirstRootNode.top === caretLineTop;
} };
// find the first root node, usually it is a text node // find the first root node, usually it is a text node
function getFirstRootChildNode(node) { const getFirstRootChildNode = (node) => {
if (!node.firstChild) { if (!node.firstChild) {
return node; return node;
} else { } else {
return getFirstRootChildNode(node.firstChild); return getFirstRootChildNode(node.firstChild);
} }
} };
function getPreviousVisibleLine(line, rep) { const getDimensionOfLastBrowserLineOfRepLine = (line, rep) => {
if (line < 0) {
return 0;
} else if (isLineVisible(line, rep)) {
return line;
} else {
return getPreviousVisibleLine(line - 1, rep);
}
}
function getDimensionOfLastBrowserLineOfRepLine(line, rep) {
const lineNode = rep.lines.atIndex(line).lineNode; const lineNode = rep.lines.atIndex(line).lineNode;
const lastRootChildNode = getLastRootChildNode(lineNode); const lastRootChildNode = getLastRootChildNode(lineNode);
// we get the position of the line in the last char of it // we get the position of the line in the last char of it
const lastRootChildNodePosition = getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length); const lastRootChildNodePosition =
getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length);
return lastRootChildNodePosition; return lastRootChildNodePosition;
} };
function getLastRootChildNode(node) { const getLastRootChildNode = (node) => {
if (!node.lastChild) { if (!node.lastChild) {
return { return {
node, node,
@ -144,39 +138,42 @@ function getLastRootChildNode(node) {
} else { } else {
return getLastRootChildNode(node.lastChild); return getLastRootChildNode(node.lastChild);
} }
} };
// here we have two possibilities: // here we have two possibilities:
// [1] The next line is part of the same rep line of the caret line, so we have the same dimensions. // [1] The next line is part of the same rep line of the caret line, so we have the same dimensions.
// So, we can use the caret line to calculate the bottom of the line. // So, we can use the caret line to calculate the bottom of the line.
// [2] the next line is part of another rep line. It's possible this line has different dimensions, so we // [2] the next line is part of another rep line.
// have to get the exactly dimension of it // It's possible this line has different dimensions, so we have to get the exactly dimension of it
exports.getBottomOfNextBrowserLine = function (caretLinePosition, rep) { exports.getBottomOfNextBrowserLine = (caretLinePosition, rep) => {
let nextLineBottom = caretLinePosition.bottom + caretLinePosition.height; // [1] let nextLineBottom = caretLinePosition.bottom + caretLinePosition.height; // [1]
const isCaretLineLastBrowserLine = caretLineIsLastBrowserLineOfRepLine(caretLinePosition.top, rep); const isCaretLineLastBrowserLine =
caretLineIsLastBrowserLineOfRepLine(caretLinePosition.top, rep);
// the caret is at the end of a rep line, so we can get the next browser line dimension // the caret is at the end of a rep line, so we can get the next browser line dimension
// using the position of the first char of the next rep line // using the position of the first char of the next rep line
if (isCaretLineLastBrowserLine) { // [2] if (isCaretLineLastBrowserLine) { // [2]
const nextLineAfterCaretLine = rep.selStart[0] + 1; const nextLineAfterCaretLine = rep.selStart[0] + 1;
const firstNextLineVisibleAfterCaretLine = getNextVisibleLine(nextLineAfterCaretLine, rep); const firstNextLineVisibleAfterCaretLine = getNextVisibleLine(nextLineAfterCaretLine, rep);
const linePosition = getDimensionOfFirstBrowserLineOfRepLine(firstNextLineVisibleAfterCaretLine, rep); const linePosition =
getDimensionOfFirstBrowserLineOfRepLine(firstNextLineVisibleAfterCaretLine, rep);
nextLineBottom = linePosition.bottom; nextLineBottom = linePosition.bottom;
} }
return nextLineBottom; return nextLineBottom;
}; };
function caretLineIsLastBrowserLineOfRepLine(caretLineTop, rep) { const caretLineIsLastBrowserLineOfRepLine = (caretLineTop, rep) => {
const caretRepLine = rep.selStart[0]; const caretRepLine = rep.selStart[0];
const lineNode = rep.lines.atIndex(caretRepLine).lineNode; const lineNode = rep.lines.atIndex(caretRepLine).lineNode;
const lastRootChildNode = getLastRootChildNode(lineNode); const lastRootChildNode = getLastRootChildNode(lineNode);
// we take a rep line and get the position of the last char of it // we take a rep line and get the position of the last char of it
const lastRootChildNodePosition = getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length); const lastRootChildNodePosition =
getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length);
return lastRootChildNodePosition.top === caretLineTop; return lastRootChildNodePosition.top === caretLineTop;
} };
function getPreviousVisibleLine(line, rep) { const getPreviousVisibleLine = (line, rep) => {
const firstLineOfPad = 0; const firstLineOfPad = 0;
if (line <= firstLineOfPad) { if (line <= firstLineOfPad) {
return firstLineOfPad; return firstLineOfPad;
@ -185,10 +182,12 @@ function getPreviousVisibleLine(line, rep) {
} else { } else {
return getPreviousVisibleLine(line - 1, rep); return getPreviousVisibleLine(line - 1, rep);
} }
} };
exports.getPreviousVisibleLine = getPreviousVisibleLine; exports.getPreviousVisibleLine = getPreviousVisibleLine;
function getNextVisibleLine(line, rep) { const getNextVisibleLine = (line, rep) => {
const lastLineOfThePad = rep.lines.length() - 1; const lastLineOfThePad = rep.lines.length() - 1;
if (line >= lastLineOfThePad) { if (line >= lastLineOfThePad) {
return lastLineOfThePad; return lastLineOfThePad;
@ -197,31 +196,28 @@ function getNextVisibleLine(line, rep) {
} else { } else {
return getNextVisibleLine(line + 1, rep); return getNextVisibleLine(line + 1, rep);
} }
} };
exports.getNextVisibleLine = getNextVisibleLine; exports.getNextVisibleLine = getNextVisibleLine;
function isLineVisible(line, rep) { const isLineVisible = (line, rep) => rep.lines.atIndex(line).lineNode.offsetHeight > 0;
return rep.lines.atIndex(line).lineNode.offsetHeight > 0;
}
function getDimensionOfFirstBrowserLineOfRepLine(line, rep) { const getDimensionOfFirstBrowserLineOfRepLine = (line, rep) => {
const lineNode = rep.lines.atIndex(line).lineNode; const lineNode = rep.lines.atIndex(line).lineNode;
const firstRootChildNode = getFirstRootChildNode(lineNode); const firstRootChildNode = getFirstRootChildNode(lineNode);
// we can get the position of the line, getting the position of the first char of the rep line // we can get the position of the line, getting the position of the first char of the rep line
const firstRootChildNodePosition = getPositionOfRepLineAtOffset(firstRootChildNode, 1); const firstRootChildNodePosition = getPositionOfRepLineAtOffset(firstRootChildNode, 1);
return firstRootChildNodePosition; return firstRootChildNodePosition;
} };
function getSelectionRange() { const getSelectionRange = () => {
let selection;
if (!window.getSelection) { if (!window.getSelection) {
return; return;
} }
selection = window.getSelection(); const selection = window.getSelection();
if (selection.rangeCount > 0) { if (selection.rangeCount > 0) {
return selection.getRangeAt(0); return selection.getRangeAt(0);
} else { } else {
return null; return null;
} }
} };