From 68af0d86c3a941e185b34926fdbc57c457466e28 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 31 Aug 2019 11:51:31 -0400 Subject: Improve popup position for vertical text --- ext/fg/js/source.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'ext/fg/js/source.js') diff --git a/ext/fg/js/source.js b/ext/fg/js/source.js index a360b331..a5421934 100644 --- a/ext/fg/js/source.js +++ b/ext/fg/js/source.js @@ -61,6 +61,10 @@ class TextSourceRange { return this.range.getBoundingClientRect(); } + getWritingMode() { + return TextSourceRange.getElementWritingMode(TextSourceRange.getParentElement(this.range.startContainer)); + } + getPaddedRect() { const range = this.range.cloneRange(); const startOffset = range.startOffset; @@ -204,6 +208,23 @@ class TextSourceRange { return state.remainder > 0; } + + static getParentElement(node) { + while (node !== null && node.nodeType !== Node.ELEMENT_NODE) { + node = node.parentNode; + } + return node; + } + + static getElementWritingMode(element) { + if (element === null) { + return 'horizontal-tb'; + } + + const style = window.getComputedStyle(element); + const writingMode = style.writingMode; + return typeof writingMode === 'string' ? writingMode : 'horizontal-tb'; + } } @@ -267,6 +288,10 @@ class TextSourceElement { return this.element.getBoundingClientRect(); } + getWritingMode() { + return 'horizontal-tb'; + } + select() { // NOP } -- 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/fg/js/source.js') 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 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/fg/js/source.js') 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