diff --git a/src/static/js/scroll.js b/src/static/js/scroll.js index 064831fe7..063e55513 100644 --- a/src/static/js/scroll.js +++ b/src/static/js/scroll.js @@ -1,3 +1,5 @@ +'use strict'; + /* This file handles scroll on edition or when user presses arrow keys. In this file we have two representations of line (browser and rep line). @@ -17,23 +19,26 @@ function Scroll(outerWin) { this.rootDocument = parent.parent.document; } -Scroll.prototype.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary = function (rep, isScrollableEvent, innerHeight) { +Scroll.prototype.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary = + function (rep, isScrollableEvent, innerHeight) { // 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 = this.scrollSettings.scrollWhenCaretIsInTheLastLineOfViewport; - if (shouldScrollWhenCaretIsAtBottomOfViewport) { - // avoid scrolling when selection includes multiple lines -- user can potentially be selecting more lines - // than it fits on viewport - const multipleLinesSelected = rep.selStart[0] !== rep.selEnd[0]; + const shouldScrollWhenCaretIsAtBottomOfViewport = + this.scrollSettings.scrollWhenCaretIsInTheLastLineOfViewport; + if (shouldScrollWhenCaretIsAtBottomOfViewport) { + // avoid scrolling when selection includes multiple lines -- + // user can potentially be selecting more lines + // than it fits on viewport + const multipleLinesSelected = rep.selStart[0] !== rep.selEnd[0]; - // avoid scrolling when pad loads - if (isScrollableEvent && !multipleLinesSelected && this._isCaretAtTheBottomOfViewport(rep)) { - // when scrollWhenFocusLineIsOutOfViewport.percentage is 0, pixelsToScroll is 0 - const pixelsToScroll = this._getPixelsRelativeToPercentageOfViewport(innerHeight); - this._scrollYPage(pixelsToScroll); + // avoid scrolling when pad loads + if (isScrollableEvent && !multipleLinesSelected && this._isCaretAtTheBottomOfViewport(rep)) { + // when scrollWhenFocusLineIsOutOfViewport.percentage is 0, pixelsToScroll is 0 + const pixelsToScroll = this._getPixelsRelativeToPercentageOfViewport(innerHeight); + this._scrollYPage(pixelsToScroll); + } } - } -}; + }; Scroll.prototype.scrollWhenPressArrowKeys = function (arrowUp, rep, innerHeight) { // if percentageScrollArrowUp is 0, let the scroll to be handled as default, put the previous @@ -60,8 +65,10 @@ Scroll.prototype._isCaretAtTheBottomOfViewport = function (rep) { const caretLine = rep.selStart[0]; const lineAfterCaretLine = caretLine + 1; const firstLineVisibleAfterCaretLine = caretPosition.getNextVisibleLine(lineAfterCaretLine, rep); - const caretLineIsPartiallyVisibleOnViewport = this._isLinePartiallyVisibleOnViewport(caretLine, rep); - const lineAfterCaretLineIsPartiallyVisibleOnViewport = this._isLinePartiallyVisibleOnViewport(firstLineVisibleAfterCaretLine, rep); + const caretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(caretLine, rep); + const lineAfterCaretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(firstLineVisibleAfterCaretLine, rep); if (caretLineIsPartiallyVisibleOnViewport || lineAfterCaretLineIsPartiallyVisibleOnViewport) { // check if the caret is in the bottom of the viewport const caretLinePosition = caretPosition.getPosition(); @@ -99,9 +106,11 @@ Scroll.prototype._getViewPortTopBottom = function () { const doc = this.doc; const height = doc.documentElement.clientHeight; // includes padding - // we have to get the exactly height of the viewport. So it has to subtract all the values which changes + // we have to get the exactly height of the viewport. + // So it has to subtract all the values which changes // the viewport height (E.g. padding, position top) - const viewportExtraSpacesAndPosition = this._getEditorPositionTop() + this._getPaddingTopAddedWhenPageViewIsEnable(); + const viewportExtraSpacesAndPosition = + this._getEditorPositionTop() + this._getPaddingTopAddedWhenPageViewIsEnable(); return { top: theTop, bottom: (theTop + height - viewportExtraSpacesAndPosition), @@ -162,9 +171,12 @@ Scroll.prototype.setScrollXY = function (x, y) { Scroll.prototype._isCaretAtTheTopOfViewport = function (rep) { const caretLine = rep.selStart[0]; const linePrevCaretLine = caretLine - 1; - const firstLineVisibleBeforeCaretLine = caretPosition.getPreviousVisibleLine(linePrevCaretLine, rep); - const caretLineIsPartiallyVisibleOnViewport = this._isLinePartiallyVisibleOnViewport(caretLine, rep); - const lineBeforeCaretLineIsPartiallyVisibleOnViewport = this._isLinePartiallyVisibleOnViewport(firstLineVisibleBeforeCaretLine, rep); + const firstLineVisibleBeforeCaretLine = + caretPosition.getPreviousVisibleLine(linePrevCaretLine, rep); + const caretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(caretLine, rep); + const lineBeforeCaretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(firstLineVisibleBeforeCaretLine, rep); if (caretLineIsPartiallyVisibleOnViewport || lineBeforeCaretLineIsPartiallyVisibleOnViewport) { const caretLinePosition = caretPosition.getPosition(); // get the position of the browser line const viewportPosition = this._getViewPortTopBottom(); @@ -172,7 +184,8 @@ Scroll.prototype._isCaretAtTheTopOfViewport = function (rep) { const viewportBottom = viewportPosition.bottom; const caretLineIsBelowViewportTop = caretLinePosition.bottom >= viewportTop; const caretLineIsAboveViewportBottom = caretLinePosition.top < viewportBottom; - const caretLineIsInsideOfViewport = caretLineIsBelowViewportTop && caretLineIsAboveViewportBottom; + const caretLineIsInsideOfViewport = + caretLineIsBelowViewportTop && caretLineIsAboveViewportBottom; if (caretLineIsInsideOfViewport) { const prevLineTop = caretPosition.getPositionTopOfPreviousBrowserLine(caretLinePosition, rep); const previousLineIsAboveViewportTop = prevLineTop < viewportTop; @@ -185,14 +198,15 @@ Scroll.prototype._isCaretAtTheTopOfViewport = function (rep) { // 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. -Scroll.prototype._getPixelsRelativeToPercentageOfViewport = function (innerHeight, aboveOfViewport) { - let pixels = 0; - const scrollPercentageRelativeToViewport = this._getPercentageToScroll(aboveOfViewport); - if (scrollPercentageRelativeToViewport > 0 && scrollPercentageRelativeToViewport <= 1) { - pixels = parseInt(innerHeight * scrollPercentageRelativeToViewport); - } - return pixels; -}; +Scroll.prototype._getPixelsRelativeToPercentageOfViewport = + function (innerHeight, aboveOfViewport) { + let pixels = 0; + const scrollPercentageRelativeToViewport = this._getPercentageToScroll(aboveOfViewport); + if (scrollPercentageRelativeToViewport > 0 && scrollPercentageRelativeToViewport <= 1) { + pixels = parseInt(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 @@ -226,30 +240,34 @@ Scroll.prototype._scrollYPageWithoutAnimation = function (pixelsToScroll) { this.outerWin.scrollBy(0, pixelsToScroll); }; -Scroll.prototype._scrollYPageWithAnimation = function (pixelsToScroll, durationOfAnimationToShowFocusline) { - const outerDocBody = this.doc.getElementById('outerdocbody'); +Scroll.prototype._scrollYPageWithAnimation = + function (pixelsToScroll, durationOfAnimationToShowFocusline) { + const outerDocBody = this.doc.getElementById('outerdocbody'); - // it works on later versions of Chrome - const $outerDocBody = $(outerDocBody); - this._triggerScrollWithAnimation($outerDocBody, pixelsToScroll, durationOfAnimationToShowFocusline); + // it works on later versions of Chrome + const $outerDocBody = $(outerDocBody); + this._triggerScrollWithAnimation( + $outerDocBody, pixelsToScroll, durationOfAnimationToShowFocusline); - // it works on Firefox and earlier versions of Chrome - const $outerDocBodyParent = $outerDocBody.parent(); - this._triggerScrollWithAnimation($outerDocBodyParent, pixelsToScroll, durationOfAnimationToShowFocusline); -}; + // it works on Firefox and earlier versions of Chrome + const $outerDocBodyParent = $outerDocBody.parent(); + this._triggerScrollWithAnimation( + $outerDocBodyParent, pixelsToScroll, durationOfAnimationToShowFocusline); + }; -// using a custom queue and clearing it, we avoid creating a queue of scroll animations. So if this function -// is called twice quickly, only the last one runs. -Scroll.prototype._triggerScrollWithAnimation = function ($elem, pixelsToScroll, durationOfAnimationToShowFocusline) { - // clear the queue of animation - $elem.stop('scrollanimation'); - $elem.animate({ - scrollTop: `+=${pixelsToScroll}`, - }, { - duration: durationOfAnimationToShowFocusline, - queue: 'scrollanimation', - }).dequeue('scrollanimation'); -}; +// using a custom queue and clearing it, we avoid creating a queue of scroll animations. +// So if this function is called twice quickly, only the last one runs. +Scroll.prototype._triggerScrollWithAnimation = + function ($elem, pixelsToScroll, durationOfAnimationToShowFocusline) { + // clear the queue of animation + $elem.stop('scrollanimation'); + $elem.animate({ + scrollTop: `+=${pixelsToScroll}`, + }, { + duration: durationOfAnimationToShowFocusline, + queue: 'scrollanimation', + }).dequeue('scrollanimation'); + }; // scrollAmountWhenFocusLineIsOutOfViewport is set to 0 (default), scroll it the minimum distance // needed to be completely in view. If the value is greater than 0 and less than or equal to 1, @@ -257,7 +275,6 @@ Scroll.prototype._triggerScrollWithAnimation = function ($elem, pixelsToScroll, // (viewport height * scrollAmountWhenFocusLineIsOutOfViewport) pixels Scroll.prototype.scrollNodeVerticallyIntoView = function (rep, innerHeight) { const viewport = this._getViewPortTopBottom(); - const isPartOfRepLineOutOfViewport = this._partOfRepLineIsOutOfViewport(viewport, rep); // when the selection changes outside of the viewport the browser automatically scrolls the line // to inside of the viewport. Tested on IE, Firefox, Chrome in releases from 2015 until now @@ -269,10 +286,12 @@ Scroll.prototype.scrollNodeVerticallyIntoView = function (rep, innerHeight) { const caretIsAboveOfViewport = distanceOfTopOfViewport < 0; const caretIsBelowOfViewport = distanceOfBottomOfViewport < 0; if (caretIsAboveOfViewport) { - var pixelsToScroll = distanceOfTopOfViewport - this._getPixelsRelativeToPercentageOfViewport(innerHeight, true); + const pixelsToScroll = + distanceOfTopOfViewport - this._getPixelsRelativeToPercentageOfViewport(innerHeight, true); this._scrollYPage(pixelsToScroll); } else if (caretIsBelowOfViewport) { - var pixelsToScroll = -distanceOfBottomOfViewport + this._getPixelsRelativeToPercentageOfViewport(innerHeight); + const pixelsToScroll = -distanceOfBottomOfViewport + + this._getPixelsRelativeToPercentageOfViewport(innerHeight); this._scrollYPage(pixelsToScroll); } else { this.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep, true, innerHeight); @@ -311,12 +330,10 @@ Scroll.prototype.getVisibleLineRange = function (rep) { const obj = {}; const self = this; const start = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).bottom > viewport.top); - let end = rep.lines.search((e) => - // return the first line that the top position is greater or equal than - // the viewport. That is the first line that is below the viewport bottom. - // So the line that is in the bottom of the viewport is the very previous one. - self._getLineEntryTopBottom(e, obj).top >= viewport.bottom - ); + // return the first line that the top position is greater or equal than + // the viewport. That is the first line that is below the viewport bottom. + // So the line that is in the bottom of the viewport is the very previous one. + let end = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).top >= viewport.bottom); if (end < start) end = start; // unlikely // top.console.log(start+","+(end -1)); return [start, end - 1]; @@ -327,6 +344,4 @@ Scroll.prototype.getVisibleCharRange = function (rep) { return [rep.lines.offsetOfIndex(lineRange[0]), rep.lines.offsetOfIndex(lineRange[1])]; }; -exports.init = function (outerWin) { - return new Scroll(outerWin); -}; +exports.init = (outerWin) => new Scroll(outerWin);