diff --git a/service.js b/service.js index 07e5a68..bd5a12e 100644 --- a/service.js +++ b/service.js @@ -73,6 +73,9 @@ class BesService { }) besServices.push(this) + + // Initial sync the scroll as hostElement may be scrolled by non-(0, 0) at the time of BesService registration. + this.onScroll() } /** @@ -360,11 +363,13 @@ class BesService { */ drawMistakeMarkup(match) { const range = match.range - const canvasPanelRect = this.canvasPanel.getBoundingClientRect() - const scrollX = canvasPanelRect.left - const scrollY = canvasPanelRect.top match.highlights = Array.from(range.getClientRects()) if (match.highlights.length === 0) return + const canvasPanelRect = this.canvasPanel.getBoundingClientRect() + for (let rect of match.highlights) { + rect.x -= canvasPanelRect.x + rect.y -= canvasPanelRect.y + } const dpr = window.devicePixelRatio this.ctx.lineWidth = 2 * dpr // Use 2 for clearer visibility const ruleId = match.match.rule.id @@ -386,7 +391,7 @@ class BesService { const scale = (markerY2 - markerY1) / 18 const x = match.highlights[0].left const y = match.highlights[0].bottom - this.drawMissingComma(x - scrollX, y - scrollY, scale, '?') + this.drawMissingComma(x, y, scale, '?') break } @@ -413,25 +418,20 @@ class BesService { // in another line, making a confusing UX. const x = match.highlights[0].left const y = match.highlights[0].bottom - this.drawMissingComma(x - scrollX, y - scrollY, scale) + this.drawMissingComma(x, y, scale) } else if (/^\s+$/.test(toInsert)) { const x = match.highlights[0].left const y1 = match.highlights[0].bottom - 2 * scale const y2 = match.highlights[0].top + 2 * scale - this.drawWrongSpacing( - x - scrollX, - y1 - scrollY, - y2 - scrollY, - scale - ) + this.drawWrongSpacing(x, y1, y2, scale) } else { const x = match.highlights[0].left - 1 * scale const y1 = match.highlights[0].bottom const y2 = match.highlights[0].top this.drawMissingText( - x - scrollX, - y1 - scrollY, - y2 - scrollY, + x, + y1, + y2, scale, replacement.substr(lengthDiff).trim() ) @@ -446,25 +446,20 @@ class BesService { if (toInsert === ',') { const x = match.highlights.at(-1).right const y = match.highlights.at(-1).bottom - this.drawMissingComma(x - scrollX, y - scrollY, scale) + this.drawMissingComma(x, y, scale) } else if (/^\s+$/.test(toInsert)) { const x = match.highlights.at(-1).right const y1 = match.highlights.at(-1).bottom - 2 * scale const y2 = match.highlights.at(-1).top + 2 * scale - this.drawWrongSpacing( - x - scrollX, - y1 - scrollY, - y2 - scrollY, - scale - ) + this.drawWrongSpacing(x, y1, y2, scale) } else { const x = match.highlights.at(-1).right + 1 * scale const y1 = match.highlights.at(-1).bottom const y2 = match.highlights.at(-1).top this.drawMissingText( - x - scrollX, - y1 - scrollY, - y2 - scrollY, + x, + y1, + y2, scale, replacement.substr(-lengthDiff).trim() ) @@ -488,12 +483,7 @@ class BesService { const x = (rect.left + rect.right) / 2 const y1 = rect.top const y2 = rect.bottom - this.drawWrongSpacing( - x - scrollX, - y1 - scrollY, - y2 - scrollY, - scale - ) + this.drawWrongSpacing(x, y1, y2, scale) } else { for (let rect of this.makeRange( match.data, @@ -501,10 +491,10 @@ class BesService { match.match.offset - lengthDiff )?.getClientRects()) this.drawExcessiveText( - rect.left - scrollX, - rect.bottom - scrollY, - rect.right - scrollX, - rect.top - scrollY + rect.left, + rect.bottom, + rect.right, + rect.top ) } } else if (context.substr(0, replacement.length) === replacement) { @@ -523,12 +513,7 @@ class BesService { const x = (rect.left + rect.right) / 2 const y1 = rect.top const y2 = rect.bottom - this.drawWrongSpacing( - x - scrollX, - y1 - scrollY, - y2 - scrollY, - scale - ) + this.drawWrongSpacing(x, y1, y2, scale) } else { for (let rect of this.makeRange( match.data, @@ -536,10 +521,10 @@ class BesService { match.match.offset + match.match.length )?.getClientRects()) this.drawExcessiveText( - rect.left - scrollX, - rect.bottom - scrollY, - rect.right - scrollX, - rect.top - scrollY + rect.left, + rect.bottom, + rect.right, + rect.top ) } } else { @@ -559,20 +544,20 @@ class BesService { for (let rect of match.highlights) { if (first) { this.drawWrongText( - rect.left - scrollX, - rect.bottom - scrollY, - rect.right - scrollX, - rect.top - scrollY, + rect.left, + rect.bottom, + rect.right, + rect.top, scale, replacement ) first = false } else { this.drawExcessiveText( - rect.left - scrollX, - rect.bottom - scrollY, - rect.right - scrollX, - rect.top - scrollY + rect.left, + rect.bottom, + rect.right, + rect.top ) } } @@ -600,22 +585,11 @@ class BesService { if (/^\s+$/.test(toInsert)) { const y1 = rects[0].bottom - 2 * scale const y2 = rects[0].top + 2 * scale - this.drawWrongSpacing( - x - scrollX, - y1 - scrollY, - y2 - scrollY, - scale - ) + this.drawWrongSpacing(x, y1, y2, scale) } else { const y1 = rects[0].bottom const y2 = rects[0].top - this.drawMissingText( - x - scrollX, - y1 - scrollY, - y2 - scrollY, - scale, - toInsert.trim() - ) + this.drawMissingText(x, y1, y2, scale, toInsert.trim()) } } else if (lengthL + lengthR === replacement.length) { // Something to remove @@ -629,19 +603,14 @@ class BesService { const x = (rects[0].left + rects[0].right) / 2 const y1 = rects[0].top const y2 = rects[0].bottom - this.drawWrongSpacing( - x - scrollX, - y1 - scrollY, - y2 - scrollY, - scale - ) + this.drawWrongSpacing(x, y1, y2, scale) } else { for (let rect of rects) this.drawExcessiveText( - rect.left - scrollX, - rect.bottom - scrollY, - rect.right - scrollX, - rect.top - scrollY + rect.left, + rect.bottom, + rect.right, + rect.top ) } } else { @@ -655,20 +624,20 @@ class BesService { for (let rect of rects) { if (first) { this.drawWrongText( - rect.left - scrollX, - rect.bottom - scrollY, - rect.right - scrollX, - rect.top - scrollY, + rect.left, + rect.bottom, + rect.right, + rect.top, scale, toReplace ) first = false } else { this.drawExcessiveText( - rect.left - scrollX, - rect.bottom - scrollY, - rect.right - scrollX, - rect.top - scrollY + rect.left, + rect.bottom, + rect.right, + rect.top ) } } @@ -684,19 +653,14 @@ class BesService { const x2 = rect.right const y = rect.bottom const scale = (rect.bottom - rect.top) / 18 - this.drawAttentionRequired( - x1 - scrollX, - x2 - scrollX, - y - scrollY, - scale - ) + this.drawAttentionRequired(x1, x2, y, scale) } markerY1 = Math.min(...match.highlights.map(rect => rect.top)) markerY2 = Math.max(...match.highlights.map(rect => rect.bottom)) } - this.drawSideMarker(markerY1 - scrollY, markerY2 - scrollY) + this.drawSideMarker(markerY1, markerY2) } static commonPrefixLength(s1, s2) { @@ -872,15 +836,11 @@ class BesService { */ setCorrectionPanelSize() { this.disableMutationObserver() + const styles = window.getComputedStyle(this.hostElement) - const hostRect = this.hostElement.getBoundingClientRect() - // Sync margins one by one. Firefox is not happy when syncing all at once. - this.scrollPanel.style.marginLeft = styles.marginLeft - this.scrollPanel.style.marginTop = styles.marginTop - this.scrollPanel.style.marginRight = styles.marginRight - this.scrollPanel.style.marginBottom = styles.marginBottom - this.scrollPanel.style.boxSizing = styles.boxSizing - this.scrollPanel.style.scrollBehavior = styles.scrollBehavior + this.textFont = styles.fontFamily + + // Resize canvas if needed. this.canvasPanel.style.width = `${this.hostElement.scrollWidth}px` this.canvasPanel.style.height = `${this.hostElement.scrollHeight}px` const dpr = window.devicePixelRatio @@ -895,6 +855,14 @@ class BesService { this.canvasPanel.height = newCanvasHeight this.redrawAllMistakeMarkup() } + + // Note: Firefox is not happy when syncing all margins at once. + this.scrollPanel.style.marginLeft = styles.marginLeft + this.scrollPanel.style.marginTop = styles.marginTop + this.scrollPanel.style.marginRight = styles.marginRight + this.scrollPanel.style.marginBottom = styles.marginBottom + this.scrollPanel.style.boxSizing = styles.boxSizing + this.scrollPanel.style.scrollBehavior = styles.scrollBehavior if (this.isHostElementInline()) { const totalWidth = parseFloat(styles.paddingLeft) + @@ -905,9 +873,11 @@ class BesService { this.scrollPanel.style.width = `${totalWidth}px` this.scrollPanel.style.height = styles.height } else { + const hostRect = this.hostElement.getBoundingClientRect() this.scrollPanel.style.width = `${hostRect.width}px` this.scrollPanel.style.height = `${hostRect.height}px` } + this.enableMutationObserver() } @@ -934,6 +904,7 @@ class BesService { * @param {*} match Grammar checking rule match */ highlightMistake(match) { + const canvasPanelRect = this.canvasPanel.getBoundingClientRect() match.highlights.forEach(rect => { const el = document.createElement('div') el.classList.add('bes-highlight-rect') @@ -942,8 +913,8 @@ class BesService { ? 'bes-highlight-spelling-rect' : 'bes-highlight-grammar-rect' ) - el.style.left = `${rect.x + window.scrollX}px` - el.style.top = `${rect.y + window.scrollY}px` + el.style.left = `${rect.x + canvasPanelRect.x + window.scrollX}px` + el.style.top = `${rect.y + canvasPanelRect.y + window.scrollY}px` el.style.width = `${rect.width}px` el.style.height = `${rect.height}px` document.body.appendChild(el) @@ -1357,12 +1328,14 @@ class BesTreeService extends BesService { const source = event?.detail !== 1 ? event?.detail : event const el = this.getBlockParent(source.targetElement || source.target) if (!el) return - // TODO: Adjust source.clientX and source.clientY for scrolling offset. + const canvasPanelRect = this.canvasPanel.getBoundingClientRect() + let x = source.clientX - canvasPanelRect.x + let y = source.clientY - canvasPanelRect.y const pointsInRect = [] for (let result of this.results) { for (let m of result.matches) { for (let rect of m.highlights) { - if (BesService.isPointInRect(source.clientX, source.clientY, rect)) { + if (BesService.isPointInRect(x, y, rect)) { pointsInRect.push({ el, match: m }) } } @@ -2022,12 +1995,14 @@ class BesPlainTextService extends BesService { const source = event?.detail !== 1 ? event?.detail : event const el = source.targetElement || source.target || this.hostElement if (!el) return - // TODO: Adjust source.clientX and source.clientY for scrolling offset. + const canvasPanelRect = this.canvasPanel.getBoundingClientRect() + let x = source.clientX - canvasPanelRect.x + let y = source.clientY - canvasPanelRect.y const pointsInRect = [] for (let result of this.results) { for (let m of result.matches) { for (let rect of m.highlights) { - if (BesService.isPointInRect(source.clientX, source.clientY, rect)) { + if (BesService.isPointInRect(x, y, rect)) { pointsInRect.push({ el: result.range, match: m }) } }