diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/fg/js/document.js | 74 | ||||
| -rw-r--r-- | ext/fg/js/frontend.js | 2 | ||||
| -rw-r--r-- | ext/fg/js/source.js | 57 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 3 | 
4 files changed, 94 insertions, 42 deletions
| diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index f58a64fc..86396a8a 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -17,6 +17,8 @@   */ +const IS_FIREFOX = /Firefox/.test(navigator.userAgent); +  function docOffsetCalc(element) {      const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;      const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; @@ -69,43 +71,23 @@ function docRangeFromPoint(point) {      const element = document.elementFromPoint(point.x, point.y);      let imposter = null;      if (element) { -        if (element.nodeName === 'IMG' || element.nodeName === 'BUTTON') { -            return new TextSourceElement(element); -        } else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { -            imposter = docImposterCreate(element); +        switch (element.nodeName) { +            case 'IMG': +            case 'BUTTON': +                return new TextSourceElement(element); +            case 'INPUT': +            case 'TEXTAREA': +                imposter = docImposterCreate(element); +                break;          }      } -    if (!document.caretRangeFromPoint) { -        document.caretRangeFromPoint = (x, y) => { -            const position = document.caretPositionFromPoint(x,y); -            if (position && position.offsetNode && position.offsetNode.nodeType === Node.TEXT_NODE) { -                const range = document.createRange(); -                range.setStart(position.offsetNode, position.offset); -                range.setEnd(position.offsetNode, position.offset); -                return range; -            } -            return null; -        }; -    } -      const range = document.caretRangeFromPoint(point.x, point.y); -    if (range === null) { -        return; +    if (imposter !== null) { +        imposter.style.zIndex = -2147483646;      } -    if(imposter !== null) imposter.style.zIndex = -2147483646; - -    const rects = range.getClientRects(); -    for (const rect of rects) { -        if (point.y <= rect.bottom + 2) { -            return new TextSourceRange(range); -        } -    } - -    if (navigator.userAgent.match(/Firefox/)) { -        return new TextSourceRange(range); -    } +    return range !== null && isPointInRange(point, range) ? new TextSourceRange(range) : null;  }  function docSentenceExtract(source, extent) { @@ -178,3 +160,33 @@ function docSentenceExtract(source, extent) {          offset: position - startPos - padding      };  } + +function isPointInRange(point, range) { +    if (IS_FIREFOX) { +        // Always return true on Firefox due to an issue where range.getClientRects() +        // does not return a correct set of rects for characters at the beginning of a line. +        return true; +    } + +    const y = point.y - 2; +    for (const rect of range.getClientRects()) { +        if (y <= rect.bottom) { +            return true; +        } +    } + +    return false; +} + +if (typeof document.caretRangeFromPoint !== 'function') { +    document.caretRangeFromPoint = (x, y) => { +        const position = document.caretPositionFromPoint(x, y); +        if (position && position.offsetNode && position.offsetNode.nodeType === Node.TEXT_NODE) { +            const range = document.createRange(); +            range.setStart(position.offsetNode, position.offset); +            range.setEnd(position.offsetNode, position.offset); +            return range; +        } +        return null; +    }; +} diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index bafd06bd..25dd38e1 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -192,7 +192,7 @@ class Frontend {      }      onTouchMove(e) { -        if (!this.scrollPrevent || this.primaryTouchIdentifier === null) { +        if (!this.scrollPrevent || !e.cancelable || this.primaryTouchIdentifier === null) {              return;          } diff --git a/ext/fg/js/source.js b/ext/fg/js/source.js index 664dbec7..bbf00e30 100644 --- a/ext/fg/js/source.js +++ b/ext/fg/js/source.js @@ -16,6 +16,9 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +// \u200c (Zero-width non-joiner) appears on Google Docs from Chrome 76 onwards +const IGNORE_TEXT_PATTERN = /\u200c/; +  /*   * TextSourceRange @@ -124,11 +127,23 @@ class TextSourceRange {      static seekForwardHelper(node, state) {          if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) {              const offset = state.node === node ? state.offset : 0; -            const remaining = node.length - offset; -            const consumed = Math.min(remaining, state.remainder); -            state.content = state.content + node.nodeValue.substring(offset, offset + consumed); + +            let consumed = 0; +            let stripped = 0; +            while (state.remainder - consumed > 0) { +                const currentChar = node.nodeValue[offset + consumed + stripped]; +                if (!currentChar) { +                    break; +                } else if (currentChar.match(IGNORE_TEXT_PATTERN)) { +                    stripped++; +                } else { +                    consumed++; +                    state.content += currentChar; +                } +            } +              state.node = node; -            state.offset = offset + consumed; +            state.offset = offset + consumed + stripped;              state.remainder -= consumed;          } else if (TextSourceRange.shouldEnter(node)) {              for (let i = 0; i < node.childNodes.length; ++i) { @@ -161,11 +176,23 @@ class TextSourceRange {      static seekBackwardHelper(node, state) {          if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) {              const offset = state.node === node ? state.offset : node.length; -            const remaining = offset; -            const consumed = Math.min(remaining, state.remainder); -            state.content = node.nodeValue.substring(offset - consumed, offset) + state.content; + +            let consumed = 0; +            let stripped = 0; +            while (state.remainder - consumed > 0) { +                const currentChar = node.nodeValue[offset - consumed - stripped]; // negative indices are undefined in JS +                if (!currentChar) { +                    break; +                } else if (currentChar.match(IGNORE_TEXT_PATTERN)) { +                    stripped++; +                } else { +                    consumed++; +                    state.content = currentChar + state.content; +                } +            } +              state.node = node; -            state.offset = offset - consumed; +            state.offset = offset - consumed - stripped;              state.remainder -= consumed;          } else if (TextSourceRange.shouldEnter(node)) {              for (let i = node.childNodes.length - 1; i >= 0; --i) { @@ -211,8 +238,18 @@ class TextSourceElement {                  break;          } -        this.content = this.content || ''; -        this.content = this.content.substring(0, length); +        let consumed = 0; +        let content = ''; +        for (let currentChar of this.content || '') { +            if (consumed >= length) { +                break; +            } else if (!currentChar.match(IGNORE_TEXT_PATTERN)) { +                consumed++; +                content += currentChar; +            } +        } + +        this.content = content;          return this.content.length;      } diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 0aa19aec..01cb406e 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -81,6 +81,9 @@ class Display {              const clickedElement = $(e.target);              const textSource = docRangeFromPoint({x: e.clientX, y: e.clientY}); +            if (textSource === null) { +                return false; +            }              textSource.setEndOffset(this.options.scanning.length);              const {definitions, length} = await apiTermsFind(textSource.text()); |