Fixed types

This commit is contained in:
SamTv12345 2024-07-17 15:56:45 +02:00
parent f7bd2185a8
commit 71c222c72f
3 changed files with 87 additions and 53 deletions

View file

@ -3,8 +3,11 @@
// 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
// is represented by the browser
import {Position, RepModel, RepNode} from "./types/RepModel";
export const getPosition = () => {
const range = getSelectionRange();
// @ts-ignore
if (!range || $(range.endContainer).closest('body')[0].id !== 'innerdocbody') return null;
// 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. As we can't get the element height, we create a text node to get
@ -18,7 +21,7 @@ export const getPosition = () => {
return line;
};
const createSelectionRange = (range) => {
const createSelectionRange = (range: Range) => {
const clonedRange = range.cloneRange();
// we set the selection start and end to avoid error when user selects a text bigger than
@ -30,14 +33,14 @@ const createSelectionRange = (range) => {
return clonedRange;
};
const getPositionOfRepLineAtOffset = (node, offset) => {
const getPositionOfRepLineAtOffset = (node: any, offset: number) => {
// it is not a text node, so we cannot make a selection
if (node.tagName === 'BR' || node.tagName === 'EMPTY') {
return getPositionOfElementOrSelection(node);
}
while (node.length === 0 && node.nextSibling) {
node = node.nextSibling;
node = node.nextSibling as any;
}
const newRange = new Range();
@ -48,14 +51,13 @@ const getPositionOfRepLineAtOffset = (node, offset) => {
return linePosition;
};
const getPositionOfElementOrSelection = (element) => {
const getPositionOfElementOrSelection = (element: Range):Position => {
const rect = element.getBoundingClientRect();
const linePosition = {
return {
bottom: rect.bottom,
height: rect.height,
top: rect.top,
};
return linePosition;
} satisfies Position;
};
// here we have two possibilities:
@ -64,7 +66,7 @@ const getPositionOfElementOrSelection = (element) => {
// 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
// height. So we have to get the exactly position of the line
export const getPositionTopOfPreviousBrowserLine = (caretLinePosition, rep) => {
export const getPositionTopOfPreviousBrowserLine = (caretLinePosition: Position, rep: RepModel) => {
let previousLineTop = caretLinePosition.top - caretLinePosition.height; // [1]
const isCaretLineFirstBrowserLine = caretLineIsFirstBrowserLine(caretLinePosition.top, rep);
@ -80,7 +82,7 @@ export const getPositionTopOfPreviousBrowserLine = (caretLinePosition, rep) => {
return previousLineTop;
};
const caretLineIsFirstBrowserLine = (caretLineTop, rep) => {
const caretLineIsFirstBrowserLine = (caretLineTop: number, rep: RepModel) => {
const caretRepLine = rep.selStart[0];
const lineNode = rep.lines.atIndex(caretRepLine).lineNode;
const firstRootNode = getFirstRootChildNode(lineNode);
@ -91,7 +93,7 @@ const caretLineIsFirstBrowserLine = (caretLineTop, rep) => {
};
// find the first root node, usually it is a text node
const getFirstRootChildNode = (node) => {
const getFirstRootChildNode = (node: RepNode) => {
if (!node.firstChild) {
return node;
} else {
@ -99,7 +101,7 @@ const getFirstRootChildNode = (node) => {
}
};
const getDimensionOfLastBrowserLineOfRepLine = (line, rep) => {
const getDimensionOfLastBrowserLineOfRepLine = (line: number, rep: RepModel) => {
const lineNode = rep.lines.atIndex(line).lineNode;
const lastRootChildNode = getLastRootChildNode(lineNode);
@ -109,7 +111,7 @@ const getDimensionOfLastBrowserLineOfRepLine = (line, rep) => {
return lastRootChildNodePosition;
};
const getLastRootChildNode = (node) => {
const getLastRootChildNode = (node: RepNode) => {
if (!node.lastChild) {
return {
node,
@ -125,7 +127,7 @@ const getLastRootChildNode = (node) => {
// 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 have to get the exactly dimension of it
export const getBottomOfNextBrowserLine = (caretLinePosition, rep) => {
export const getBottomOfNextBrowserLine = (caretLinePosition: Position, rep: RepModel) => {
let nextLineBottom = caretLinePosition.bottom + caretLinePosition.height; // [1]
const isCaretLineLastBrowserLine =
caretLineIsLastBrowserLineOfRepLine(caretLinePosition.top, rep);
@ -142,7 +144,7 @@ export const getBottomOfNextBrowserLine = (caretLinePosition, rep) => {
return nextLineBottom;
};
const caretLineIsLastBrowserLineOfRepLine = (caretLineTop, rep) => {
const caretLineIsLastBrowserLineOfRepLine = (caretLineTop: number, rep: RepModel) => {
const caretRepLine = rep.selStart[0];
const lineNode = rep.lines.atIndex(caretRepLine).lineNode;
const lastRootChildNode = getLastRootChildNode(lineNode);
@ -153,7 +155,7 @@ const caretLineIsLastBrowserLineOfRepLine = (caretLineTop, rep) => {
return lastRootChildNodePosition.top === caretLineTop;
};
export const getPreviousVisibleLine = (line, rep) => {
export const getPreviousVisibleLine = (line: number, rep: RepModel): number => {
const firstLineOfPad = 0;
if (line <= firstLineOfPad) {
return firstLineOfPad;
@ -166,7 +168,7 @@ export const getPreviousVisibleLine = (line, rep) => {
export const getNextVisibleLine = (line, rep) => {
export const getNextVisibleLine = (line: number, rep: RepModel): number => {
const lastLineOfThePad = rep.lines.length() - 1;
if (line >= lastLineOfThePad) {
return lastLineOfThePad;
@ -177,9 +179,9 @@ export const getNextVisibleLine = (line, rep) => {
}
};
const isLineVisible = (line, rep) => rep.lines.atIndex(line).lineNode.offsetHeight > 0;
const isLineVisible = (line: number, rep: RepModel) => rep.lines.atIndex(line).lineNode.offsetHeight > 0;
const getDimensionOfFirstBrowserLineOfRepLine = (line, rep) => {
const getDimensionOfFirstBrowserLineOfRepLine = (line: number, rep: RepModel) => {
const lineNode = rep.lines.atIndex(line).lineNode;
const firstRootChildNode = getFirstRootChildNode(lineNode);

View file

@ -1,4 +1,5 @@
import {getBottomOfNextBrowserLine, getNextVisibleLine, getPosition, getPositionTopOfPreviousBrowserLine, getPreviousVisibleLine} from './caretPosition';
import {Position, RepModel, RepNode, WindowElementWithScrolling} from "./types/RepModel";
class Scroll {
@ -8,6 +9,7 @@ class Scroll {
private scrollSettings: any;
constructor(outerWin: HTMLIFrameElement) {
// @ts-ignore
this.scrollSettings = window.clientVars.scrollWhenFocusLineIsOutOfViewport;
// DOM reference
@ -16,7 +18,7 @@ class Scroll {
this.rootDocument = parent.parent.document;
}
scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep, isScrollableEvent, innerHeight) {
scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep: RepModel, isScrollableEvent: boolean, innerHeight: number) {
// are we placing the caret on the line at the bottom of viewport?
// And if so, do we need to scroll the editor, as defined on the settings.json?
const shouldScrollWhenCaretIsAtBottomOfViewport =
@ -36,7 +38,7 @@ class Scroll {
}
}
scrollWhenPressArrowKeys(arrowUp, rep, innerHeight) {
scrollWhenPressArrowKeys(arrowUp: boolean, rep: RepModel, innerHeight: number) {
// if percentageScrollArrowUp is 0, let the scroll to be handled as default, put the previous
// rep line on the top of the viewport
if (this._arrowUpWasPressedInTheFirstLineOfTheViewport(arrowUp, rep)) {
@ -50,7 +52,7 @@ class Scroll {
}
}
_isCaretAtTheBottomOfViewport(rep) {
_isCaretAtTheBottomOfViewport(rep: RepModel) {
// computing a line position using getBoundingClientRect() is expensive.
// (obs: getBoundingClientRect() is called on caretPosition.getPosition())
// To avoid that, we only call this function when it is possible that the
@ -64,16 +66,15 @@ class Scroll {
this._isLinePartiallyVisibleOnViewport(firstLineVisibleAfterCaretLine, rep);
if (caretLineIsPartiallyVisibleOnViewport || lineAfterCaretLineIsPartiallyVisibleOnViewport) {
// check if the caret is in the bottom of the viewport
const caretLinePosition = getPosition();
const caretLinePosition = getPosition()!;
const viewportBottom = this._getViewPortTopBottom().bottom;
const nextLineBottom = getBottomOfNextBrowserLine(caretLinePosition, rep);
const nextLineIsBelowViewportBottom = nextLineBottom > viewportBottom;
return nextLineIsBelowViewportBottom;
return nextLineBottom > viewportBottom;
}
return false;
};
_isLinePartiallyVisibleOnViewport(lineNumber, rep){
_isLinePartiallyVisibleOnViewport(lineNumber: number, rep: RepModel){
const lineNode = rep.lines.atIndex(lineNumber);
const linePosition = this._getLineEntryTopBottom(lineNode);
const lineTop = linePosition.top;
@ -123,7 +124,7 @@ class Scroll {
};
_getScrollXY() {
const win = this.outerWin;
const win = this.outerWin as WindowElementWithScrolling;
const odoc = this.doc;
if (typeof (win.pageYOffset) === 'number') {
return {
@ -141,26 +142,26 @@ class Scroll {
};
getScrollX() {
return this._getScrollXY().x;
return this._getScrollXY()!.x;
};
getScrollY () {
return this._getScrollXY().y;
return this._getScrollXY()!.y;
};
setScrollX(x) {
setScrollX(x: number) {
this.outerWin.scrollTo(x, this.getScrollY());
};
setScrollY(y) {
setScrollY(y: number) {
this.outerWin.scrollTo(this.getScrollX(), y);
};
setScrollXY(x, y) {
setScrollXY(x: number, y: number) {
this.outerWin.scrollTo(x, y);
};
_isCaretAtTheTopOfViewport(rep) {
_isCaretAtTheTopOfViewport(rep: RepModel) {
const caretLine = rep.selStart[0];
const linePrevCaretLine = caretLine - 1;
const firstLineVisibleBeforeCaretLine =
@ -174,12 +175,12 @@ class Scroll {
const viewportPosition = this._getViewPortTopBottom();
const viewportTop = viewportPosition.top;
const viewportBottom = viewportPosition.bottom;
const caretLineIsBelowViewportTop = caretLinePosition.bottom >= viewportTop;
const caretLineIsAboveViewportBottom = caretLinePosition.top < viewportBottom;
const caretLineIsBelowViewportTop = caretLinePosition!.bottom >= viewportTop;
const caretLineIsAboveViewportBottom = caretLinePosition!.top < viewportBottom;
const caretLineIsInsideOfViewport =
caretLineIsBelowViewportTop && caretLineIsAboveViewportBottom;
if (caretLineIsInsideOfViewport) {
const prevLineTop = getPositionTopOfPreviousBrowserLine(caretLinePosition, rep);
const prevLineTop = getPositionTopOfPreviousBrowserLine(caretLinePosition!, rep);
const previousLineIsAboveViewportTop = prevLineTop < viewportTop;
return previousLineIsAboveViewportTop;
}
@ -190,18 +191,18 @@ class Scroll {
// By default, when user makes an edition in a line out of viewport, this line goes
// to the edge of viewport. This function gets the extra pixels necessary to get the
// caret line in a position X relative to Y% viewport.
_getPixelsRelativeToPercentageOfViewport(innerHeight, aboveOfViewport) {
_getPixelsRelativeToPercentageOfViewport(innerHeight: number, aboveOfViewport?: boolean) {
let pixels = 0;
const scrollPercentageRelativeToViewport = this._getPercentageToScroll(aboveOfViewport);
if (scrollPercentageRelativeToViewport > 0 && scrollPercentageRelativeToViewport <= 1) {
pixels = parseInt(innerHeight * scrollPercentageRelativeToViewport);
pixels = parseInt(String(innerHeight * scrollPercentageRelativeToViewport));
}
return pixels;
};
// we use different percentages when change selection. It depends on if it is
// either above the top or below the bottom of the page
_getPercentageToScroll(aboveOfViewport: boolean) {
_getPercentageToScroll(aboveOfViewport: boolean|undefined) {
let percentageToScroll = this.scrollSettings.percentage.editionBelowViewport;
if (aboveOfViewport) {
percentageToScroll = this.scrollSettings.percentage.editionAboveViewport;
@ -209,16 +210,16 @@ class Scroll {
return percentageToScroll;
};
_getPixelsToScrollWhenUserPressesArrowUp(innerHeight) {
_getPixelsToScrollWhenUserPressesArrowUp(innerHeight: number) {
let pixels = 0;
const percentageToScrollUp = this.scrollSettings.percentageToScrollWhenUserPressesArrowUp;
if (percentageToScrollUp > 0 && percentageToScrollUp <= 1) {
pixels = parseInt(innerHeight * percentageToScrollUp);
pixels = parseInt(String(innerHeight * percentageToScrollUp));
}
return pixels;
};
_scrollYPage(pixelsToScroll) {
_scrollYPage(pixelsToScroll: number) {
const durationOfAnimationToShowFocusline = this.scrollSettings.duration;
if (durationOfAnimationToShowFocusline) {
this._scrollYPageWithAnimation(pixelsToScroll, durationOfAnimationToShowFocusline);
@ -227,15 +228,15 @@ class Scroll {
}
};
_scrollYPageWithoutAnimation(pixelsToScroll) {
_scrollYPageWithoutAnimation(pixelsToScroll: number) {
this.outerWin.scrollBy(0, pixelsToScroll);
};
_scrollYPageWithAnimation(pixelsToScroll, durationOfAnimationToShowFocusline) {
_scrollYPageWithAnimation(pixelsToScroll: number, durationOfAnimationToShowFocusline: number) {
const outerDocBody = this.doc.getElementById('outerdocbody');
// it works on later versions of Chrome
const $outerDocBody = $(outerDocBody);
const $outerDocBody = $(outerDocBody!);
this._triggerScrollWithAnimation(
$outerDocBody, pixelsToScroll, durationOfAnimationToShowFocusline);
@ -245,7 +246,7 @@ class Scroll {
$outerDocBodyParent, pixelsToScroll, durationOfAnimationToShowFocusline);
};
_triggerScrollWithAnimation($elem, pixelsToScroll, durationOfAnimationToShowFocusline) {
_triggerScrollWithAnimation($elem:any, pixelsToScroll: number, durationOfAnimationToShowFocusline: number) {
// clear the queue of animation
$elem.stop('scrollanimation');
$elem.animate({
@ -258,7 +259,7 @@ class Scroll {
scrollNodeVerticallyIntoView(rep, innerHeight) {
scrollNodeVerticallyIntoView(rep: RepModel, innerHeight: number) {
const viewport = this._getViewPortTopBottom();
// when the selection changes outside of the viewport the browser automatically scrolls the line
@ -288,7 +289,7 @@ class Scroll {
}
};
_partOfRepLineIsOutOfViewport(viewportPosition, rep) {
_partOfRepLineIsOutOfViewport(viewportPosition: Position, rep: RepModel) {
const focusLine = (rep.selFocusAtStart ? rep.selStart[0] : rep.selEnd[0]);
const line = rep.lines.atIndex(focusLine);
const linePosition = this._getLineEntryTopBottom(line);
@ -298,25 +299,25 @@ class Scroll {
return lineIsBelowOfViewport || lineIsAboveOfViewport;
};
_getLineEntryTopBottom(entry, destObj) {
_getLineEntryTopBottom(entry: RepNode, destObj?: Position) {
const dom = entry.lineNode;
const top = dom.offsetTop;
const height = dom.offsetHeight;
const obj = (destObj || {});
const obj = (destObj || {}) as Position;
obj.top = top;
obj.bottom = (top + height);
return obj;
};
_arrowUpWasPressedInTheFirstLineOfTheViewport(arrowUp, rep) {
_arrowUpWasPressedInTheFirstLineOfTheViewport(arrowUp: boolean, rep: RepModel) {
const percentageScrollArrowUp = this.scrollSettings.percentageToScrollWhenUserPressesArrowUp;
return percentageScrollArrowUp && arrowUp && this._isCaretAtTheTopOfViewport(rep);
};
getVisibleLineRange(rep) {
getVisibleLineRange(rep: RepModel) {
const viewport = this._getViewPortTopBottom();
// console.log("viewport top/bottom: %o", viewport);
const obj = {};
const obj = {} as Position;
const self = this;
const start = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).bottom > viewport.top);
// return the first line that the top position is greater or equal than
@ -328,7 +329,7 @@ class Scroll {
return [start, end - 1];
};
getVisibleCharRange(rep) {
getVisibleCharRange(rep: RepModel) {
const lineRange = this.getVisibleLineRange(rep);
return [rep.lines.offsetOfIndex(lineRange[0]), rep.lines.offsetOfIndex(lineRange[1])];
};

View file

@ -0,0 +1,31 @@
export type RepModel = {
lines: {
atIndex: (num: number)=>RepNode,
offsetOfIndex: (range: number)=>number,
search: (filter: (e: RepNode)=>boolean)=>number,
length: ()=>number
}
selStart: number[],
selEnd: number[],
selFocusAtStart: boolean
}
export type Position = {
bottom: number,
height: number,
top: number
}
export type RepNode = {
firstChild: RepNode,
lineNode: RepNode
length: number,
lastChild: RepNode,
offsetHeight: number,
offsetTop: number
}
export type WindowElementWithScrolling = HTMLIFrameElement & {
pageYOffset: number|string,
pageXOffset: number
}