From 9e9040178d1c498f4c7298a241137d2e7af59114 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 31 Aug 2019 13:58:45 -0400 Subject: Fix some size and positioning issues related to imposter element --- ext/fg/js/document.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'ext') diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index 8a412f96..962592ec 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -17,16 +17,15 @@ */ -function docOffsetCalc(element) { +function docOffsetCalc(elementRect) { const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; const clientTop = document.documentElement.clientTop || document.body.clientTop || 0; const clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0; - const rect = element.getBoundingClientRect(); - const top = Math.round(rect.top + scrollTop - clientTop); - const left = Math.round(rect.left + scrollLeft - clientLeft); + const top = Math.round(elementRect.top + scrollTop - clientTop); + const left = Math.round(elementRect.left + scrollLeft - clientLeft); return {top, left}; } @@ -38,7 +37,8 @@ function docImposterCreate(element) { stylePairs.push(`${key}: ${styleProps[key]};`); } - const offset = docOffsetCalc(element); + const elementRect = element.getBoundingClientRect(); + const offset = docOffsetCalc(elementRect); const imposter = document.createElement('div'); imposter.className = 'yomichan-imposter'; imposter.innerText = element.value; @@ -48,11 +48,22 @@ function docImposterCreate(element) { imposter.style.left = `${offset.left}px`; imposter.style.opacity = 0; imposter.style.zIndex = 2147483646; + imposter.style.margin = '0'; if (element.nodeName === 'TEXTAREA' && styleProps.overflow === 'visible') { imposter.style.overflow = 'auto'; } document.body.appendChild(imposter); + + // Adjust size + const imposterRect = imposter.getBoundingClientRect(); + if (imposterRect.width !== elementRect.width || imposterRect.height !== elementRect.height) { + const width = parseFloat(styleProps.width) + (elementRect.width - imposterRect.width); + const height = parseFloat(styleProps.height) + (elementRect.height - imposterRect.height); + imposter.style.width = `${width}px`; + imposter.style.height = `${height}px`; + } + imposter.scrollTop = element.scrollTop; imposter.scrollLeft = element.scrollLeft; -- cgit v1.2.3 From 62d66d93f7e7f666184aa215c876691c5433043f Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 31 Aug 2019 14:08:30 -0400 Subject: Remove rounding --- ext/fg/js/document.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ext') diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index 962592ec..514f8f2e 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -24,8 +24,8 @@ function docOffsetCalc(elementRect) { const clientTop = document.documentElement.clientTop || document.body.clientTop || 0; const clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0; - const top = Math.round(elementRect.top + scrollTop - clientTop); - const left = Math.round(elementRect.left + scrollLeft - clientLeft); + const top = elementRect.top + scrollTop - clientTop; + const left = elementRect.left + scrollLeft - clientLeft; return {top, left}; } -- cgit v1.2.3 From e47e041217708c49032dd3d215eacb23276a1f59 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 31 Aug 2019 14:13:47 -0400 Subject: Disable pointer events on hidden imposter --- ext/fg/js/document.js | 1 + 1 file changed, 1 insertion(+) (limited to 'ext') diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index 514f8f2e..a017a0a6 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -94,6 +94,7 @@ function docRangeFromPoint(point) { const range = document.caretRangeFromPoint(point.x, point.y); if (imposter !== null) { imposter.style.zIndex = -2147483646; + imposter.style.pointerEvents = 'none'; } return range !== null && isPointInRange(point, range) ? new TextSourceRange(range) : null; -- cgit v1.2.3 From ad0dca7bb12d13545b559e9c738fcc0767ba20d5 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 31 Aug 2019 14:57:24 -0400 Subject: Make the imposter element tracked using TextSourceRange --- ext/fg/js/document.js | 22 +++++++++++----------- ext/fg/js/frontend.js | 6 +++--- ext/fg/js/source.js | 15 +++++++++++++-- ext/mixed/js/display.js | 20 +++++++++++++------- 4 files changed, 40 insertions(+), 23 deletions(-) (limited to 'ext') diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index a017a0a6..e6a16bd5 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -70,12 +70,6 @@ function docImposterCreate(element) { return imposter; } -function docImposterDestroy() { - for (const element of document.getElementsByClassName('yomichan-imposter')) { - element.parentNode.removeChild(element); - } -} - function docRangeFromPoint(point) { const element = document.elementFromPoint(point.x, point.y); let imposter = null; @@ -92,12 +86,18 @@ function docRangeFromPoint(point) { } const range = document.caretRangeFromPoint(point.x, point.y); - if (imposter !== null) { - imposter.style.zIndex = -2147483646; - imposter.style.pointerEvents = 'none'; + if (range !== null && isPointInRange(point, range)) { + if (imposter !== null) { + imposter.style.zIndex = -2147483646; + imposter.style.pointerEvents = 'none'; + } + return new TextSourceRange(range, '', imposter); + } else { + if (imposter !== null) { + imposter.parentNode.removeChild(imposter); + } + return null; } - - return range !== null && isPointInRange(point, range) ? new TextSourceRange(range) : null; } function docSentenceExtract(source, extent) { diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 3c5f2ac8..ebff768e 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -307,10 +307,11 @@ class Frontend { this.onError(e); } } finally { + if (textSource !== null) { + textSource.cleanup(); + } if (hideResults && this.options.scanning.autoHideResults) { this.searchClear(); - } else { - docImposterDestroy(); } this.pendingLookup = false; @@ -371,7 +372,6 @@ class Frontend { } searchClear() { - docImposterDestroy(); this.popup.hide(); this.popup.clearAutoPlayTimer(); diff --git a/ext/fg/js/source.js b/ext/fg/js/source.js index a360b331..409e81aa 100644 --- a/ext/fg/js/source.js +++ b/ext/fg/js/source.js @@ -25,13 +25,20 @@ const IGNORE_TEXT_PATTERN = /\u200c/; */ class TextSourceRange { - constructor(range, content='') { + constructor(range, content, imposter) { this.range = range; this.content = content; + this.imposter = imposter; } clone() { - return new TextSourceRange(this.range.cloneRange(), this.content); + return new TextSourceRange(this.range.cloneRange(), this.content, this.imposter); + } + + cleanup() { + if (this.imposter !== null && this.imposter.parentNode !== null) { + this.imposter.parentNode.removeChild(this.imposter); + } } text() { @@ -221,6 +228,10 @@ class TextSourceElement { return new TextSourceElement(this.element, this.content); } + cleanup() { + // NOP + } + text() { return this.content; } diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index a2707bd0..4620e198 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -84,16 +84,22 @@ class Display { if (textSource === null) { return false; } - textSource.setEndOffset(this.options.scanning.length); - const {definitions, length} = await apiTermsFind(textSource.text()); - if (definitions.length === 0) { - return false; - } + let definitions, length, sentence; + try { + textSource.setEndOffset(this.options.scanning.length); - textSource.setEndOffset(length); + ({definitions, length} = await apiTermsFind(textSource.text())); + if (definitions.length === 0) { + return false; + } - const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + textSource.setEndOffset(length); + + sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + } finally { + textSource.cleanup(); + } const context = { source: { -- cgit v1.2.3 From 9b46fe70de9d0e7b625649fcdde53f5b91bbb114 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 31 Aug 2019 15:10:46 -0400 Subject: Fix imposter issues with --- ext/fg/js/document.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'ext') diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index e6a16bd5..1a49521b 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -30,7 +30,7 @@ function docOffsetCalc(elementRect) { return {top, left}; } -function docImposterCreate(element) { +function docImposterCreate(element, isTextarea) { const styleProps = window.getComputedStyle(element); const stylePairs = []; for (const key of styleProps) { @@ -49,8 +49,14 @@ function docImposterCreate(element) { imposter.style.opacity = 0; imposter.style.zIndex = 2147483646; imposter.style.margin = '0'; - if (element.nodeName === 'TEXTAREA' && styleProps.overflow === 'visible') { - imposter.style.overflow = 'auto'; + if (isTextarea) { + if (styleProps.overflow === 'visible') { + imposter.style.overflow = 'auto'; + } + } else { + imposter.style.overflow = 'hidden'; + imposter.style.whiteSpace = 'nowrap'; + imposter.style.lineHeight = styleProps.height; } document.body.appendChild(imposter); @@ -79,8 +85,10 @@ function docRangeFromPoint(point) { case 'BUTTON': return new TextSourceElement(element); case 'INPUT': + imposter = docImposterCreate(element, false); + break; case 'TEXTAREA': - imposter = docImposterCreate(element); + imposter = docImposterCreate(element, true); break; } } -- cgit v1.2.3 From e3e7dad2cc896a84c5c332f4c95a8984386be844 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 31 Aug 2019 15:30:32 -0400 Subject: Use important CSS priority for imposter element styles --- ext/fg/js/document.js | 55 +++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 24 deletions(-) (limited to 'ext') diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index 1a49521b..247ff0ee 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -17,6 +17,10 @@ */ +function docSetImposterStyle(style, propertyName, value) { + style.setProperty(propertyName, value, 'important'); +} + function docOffsetCalc(elementRect) { const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; @@ -31,32 +35,34 @@ function docOffsetCalc(elementRect) { } function docImposterCreate(element, isTextarea) { - const styleProps = window.getComputedStyle(element); - const stylePairs = []; - for (const key of styleProps) { - stylePairs.push(`${key}: ${styleProps[key]};`); - } - + const elementStyle = window.getComputedStyle(element); const elementRect = element.getBoundingClientRect(); const offset = docOffsetCalc(elementRect); const imposter = document.createElement('div'); + const imposterStyle = imposter.style; + imposter.className = 'yomichan-imposter'; imposter.innerText = element.value; - imposter.style.cssText = stylePairs.join('\n'); - imposter.style.position = 'absolute'; - imposter.style.top = `${offset.top}px`; - imposter.style.left = `${offset.left}px`; - imposter.style.opacity = 0; - imposter.style.zIndex = 2147483646; - imposter.style.margin = '0'; + + for (let i = 0, ii = elementStyle.length; i < ii; ++i) { + const property = elementStyle[i]; + docSetImposterStyle(imposterStyle, property, elementStyle.getPropertyValue(property)); + } + docSetImposterStyle(imposterStyle, 'position', 'absolute'); + docSetImposterStyle(imposterStyle, 'top', `${offset.top}px`); + docSetImposterStyle(imposterStyle, 'left', `${offset.left}px`); + docSetImposterStyle(imposterStyle, 'opacity', '0'); + docSetImposterStyle(imposterStyle, 'z-index', '2147483646'); + docSetImposterStyle(imposterStyle, 'margin', '0'); + if (isTextarea) { - if (styleProps.overflow === 'visible') { - imposter.style.overflow = 'auto'; + if (elementStyle.overflow === 'visible') { + docSetImposterStyle(imposterStyle, 'overflow', 'auto'); } } else { - imposter.style.overflow = 'hidden'; - imposter.style.whiteSpace = 'nowrap'; - imposter.style.lineHeight = styleProps.height; + docSetImposterStyle(imposterStyle, 'overflow', 'hidden'); + docSetImposterStyle(imposterStyle, 'white-space', 'nowrap'); + docSetImposterStyle(imposterStyle, 'line-height', elementStyle.height); } document.body.appendChild(imposter); @@ -64,10 +70,10 @@ function docImposterCreate(element, isTextarea) { // Adjust size const imposterRect = imposter.getBoundingClientRect(); if (imposterRect.width !== elementRect.width || imposterRect.height !== elementRect.height) { - const width = parseFloat(styleProps.width) + (elementRect.width - imposterRect.width); - const height = parseFloat(styleProps.height) + (elementRect.height - imposterRect.height); - imposter.style.width = `${width}px`; - imposter.style.height = `${height}px`; + const width = parseFloat(elementStyle.width) + (elementRect.width - imposterRect.width); + const height = parseFloat(elementStyle.height) + (elementRect.height - imposterRect.height); + docSetImposterStyle(imposterStyle, 'width', `${width}px`); + docSetImposterStyle(imposterStyle, 'height', `${height}px`); } imposter.scrollTop = element.scrollTop; @@ -96,8 +102,9 @@ function docRangeFromPoint(point) { const range = document.caretRangeFromPoint(point.x, point.y); if (range !== null && isPointInRange(point, range)) { if (imposter !== null) { - imposter.style.zIndex = -2147483646; - imposter.style.pointerEvents = 'none'; + const imposterStyle = imposter.style; + docSetImposterStyle(imposterStyle, 'z-index', '-2147483646'); + docSetImposterStyle(imposterStyle, 'pointer-events', 'none'); } return new TextSourceRange(range, '', imposter); } else { -- cgit v1.2.3 From e3d7ec8db7a86d475fba00d48f9cbe150feb36ff Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 1 Sep 2019 16:06:22 -0400 Subject: Create container for imposter element The container will prevent the imposter element's size from affecting the document's primary scrollbars. --- ext/fg/js/document.js | 63 +++++++++++++++++++++++++++------------------------ ext/fg/js/source.js | 10 ++++---- 2 files changed, 39 insertions(+), 34 deletions(-) (limited to 'ext') diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index 247ff0ee..dc2a9b87 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -21,27 +21,32 @@ function docSetImposterStyle(style, propertyName, value) { style.setProperty(propertyName, value, 'important'); } -function docOffsetCalc(elementRect) { - const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; - const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; - - const clientTop = document.documentElement.clientTop || document.body.clientTop || 0; - const clientLeft = document.documentElement.clientLeft || document.body.clientLeft || 0; - - const top = elementRect.top + scrollTop - clientTop; - const left = elementRect.left + scrollLeft - clientLeft; - - return {top, left}; -} - function docImposterCreate(element, isTextarea) { const elementStyle = window.getComputedStyle(element); const elementRect = element.getBoundingClientRect(); - const offset = docOffsetCalc(elementRect); + const documentRect = document.documentElement.getBoundingClientRect(); + const left = elementRect.left - documentRect.left; + const top = elementRect.top - documentRect.top; + + // Container + const container = document.createElement('div'); + const containerStyle = container.style; + docSetImposterStyle(containerStyle, 'all', 'initial'); + docSetImposterStyle(containerStyle, 'position', 'absolute'); + docSetImposterStyle(containerStyle, 'left', '0'); + docSetImposterStyle(containerStyle, 'top', '0'); + docSetImposterStyle(containerStyle, 'width', `${documentRect.width}px`); + docSetImposterStyle(containerStyle, 'height', `${documentRect.height}px`); + docSetImposterStyle(containerStyle, 'overflow', 'hidden'); + docSetImposterStyle(containerStyle, 'opacity', '0'); + + docSetImposterStyle(containerStyle, 'pointer-events', 'none'); + docSetImposterStyle(containerStyle, 'z-index', '2147483646'); + + // Imposter const imposter = document.createElement('div'); const imposterStyle = imposter.style; - imposter.className = 'yomichan-imposter'; imposter.innerText = element.value; for (let i = 0, ii = elementStyle.length; i < ii; ++i) { @@ -49,11 +54,10 @@ function docImposterCreate(element, isTextarea) { docSetImposterStyle(imposterStyle, property, elementStyle.getPropertyValue(property)); } docSetImposterStyle(imposterStyle, 'position', 'absolute'); - docSetImposterStyle(imposterStyle, 'top', `${offset.top}px`); - docSetImposterStyle(imposterStyle, 'left', `${offset.left}px`); - docSetImposterStyle(imposterStyle, 'opacity', '0'); - docSetImposterStyle(imposterStyle, 'z-index', '2147483646'); + docSetImposterStyle(imposterStyle, 'top', `${top}px`); + docSetImposterStyle(imposterStyle, 'left', `${left}px`); docSetImposterStyle(imposterStyle, 'margin', '0'); + docSetImposterStyle(imposterStyle, 'pointer-events', 'auto'); if (isTextarea) { if (elementStyle.overflow === 'visible') { @@ -65,7 +69,8 @@ function docImposterCreate(element, isTextarea) { docSetImposterStyle(imposterStyle, 'line-height', elementStyle.height); } - document.body.appendChild(imposter); + container.appendChild(imposter); + document.body.appendChild(container); // Adjust size const imposterRect = imposter.getBoundingClientRect(); @@ -79,22 +84,23 @@ function docImposterCreate(element, isTextarea) { imposter.scrollTop = element.scrollTop; imposter.scrollLeft = element.scrollLeft; - return imposter; + return [imposter, container]; } function docRangeFromPoint(point) { const element = document.elementFromPoint(point.x, point.y); let imposter = null; + let imposterContainer = null; if (element) { switch (element.nodeName) { case 'IMG': case 'BUTTON': return new TextSourceElement(element); case 'INPUT': - imposter = docImposterCreate(element, false); + [imposter, imposterContainer] = docImposterCreate(element, false); break; case 'TEXTAREA': - imposter = docImposterCreate(element, true); + [imposter, imposterContainer] = docImposterCreate(element, true); break; } } @@ -102,14 +108,13 @@ function docRangeFromPoint(point) { const range = document.caretRangeFromPoint(point.x, point.y); if (range !== null && isPointInRange(point, range)) { if (imposter !== null) { - const imposterStyle = imposter.style; - docSetImposterStyle(imposterStyle, 'z-index', '-2147483646'); - docSetImposterStyle(imposterStyle, 'pointer-events', 'none'); + docSetImposterStyle(imposterContainer.style, 'z-index', '-2147483646'); + docSetImposterStyle(imposter.style, 'pointer-events', 'none'); } - return new TextSourceRange(range, '', imposter); + return new TextSourceRange(range, '', imposterContainer); } else { - if (imposter !== null) { - imposter.parentNode.removeChild(imposter); + if (imposterContainer !== null) { + imposterContainer.parentNode.removeChild(imposterContainer); } return null; } diff --git a/ext/fg/js/source.js b/ext/fg/js/source.js index 409e81aa..69d8197d 100644 --- a/ext/fg/js/source.js +++ b/ext/fg/js/source.js @@ -25,19 +25,19 @@ const IGNORE_TEXT_PATTERN = /\u200c/; */ class TextSourceRange { - constructor(range, content, imposter) { + constructor(range, content, imposterContainer) { this.range = range; this.content = content; - this.imposter = imposter; + this.imposterContainer = imposterContainer; } clone() { - return new TextSourceRange(this.range.cloneRange(), this.content, this.imposter); + return new TextSourceRange(this.range.cloneRange(), this.content, this.imposterContainer); } cleanup() { - if (this.imposter !== null && this.imposter.parentNode !== null) { - this.imposter.parentNode.removeChild(this.imposter); + if (this.imposterContainer !== null && this.imposterContainer.parentNode !== null) { + this.imposterContainer.parentNode.removeChild(this.imposterContainer); } } -- cgit v1.2.3