diff options
author | Alex Yatskov <FooSoft@users.noreply.github.com> | 2019-09-02 08:30:19 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-02 08:30:19 -0700 |
commit | 037363e3a70675a580431d2ee891e3d88fae511c (patch) | |
tree | d42dab6f73fdcc52b9192a29cc1e1219608ca035 | |
parent | eee89fa1853b80d563bc9ef5ba05d9a9e8ee2d0d (diff) | |
parent | e3d7ec8db7a86d475fba00d48f9cbe150feb36ff (diff) |
Merge pull request #197 from toasted-nutbread/imposter-improvements
Imposter element improvements
-rw-r--r-- | ext/fg/js/document.js | 114 | ||||
-rw-r--r-- | ext/fg/js/frontend.js | 6 | ||||
-rw-r--r-- | ext/fg/js/source.js | 15 | ||||
-rw-r--r-- | ext/mixed/js/display.js | 20 |
4 files changed, 102 insertions, 53 deletions
diff --git a/ext/fg/js/document.js b/ext/fg/js/document.js index 8a412f96..dc2a9b87 100644 --- a/ext/fg/js/document.js +++ b/ext/fg/js/document.js @@ -17,75 +17,107 @@ */ -function docOffsetCalc(element) { - 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; +function docSetImposterStyle(style, propertyName, value) { + style.setProperty(propertyName, value, 'important'); +} - const rect = element.getBoundingClientRect(); - const top = Math.round(rect.top + scrollTop - clientTop); - const left = Math.round(rect.left + scrollLeft - clientLeft); +function docImposterCreate(element, isTextarea) { + const elementStyle = window.getComputedStyle(element); + const elementRect = element.getBoundingClientRect(); + 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; - return {top, left}; -} + imposter.innerText = element.value; -function docImposterCreate(element) { - const styleProps = window.getComputedStyle(element); - const stylePairs = []; - for (const key of styleProps) { - stylePairs.push(`${key}: ${styleProps[key]};`); + 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', `${top}px`); + docSetImposterStyle(imposterStyle, 'left', `${left}px`); + docSetImposterStyle(imposterStyle, 'margin', '0'); + docSetImposterStyle(imposterStyle, 'pointer-events', 'auto'); + + if (isTextarea) { + if (elementStyle.overflow === 'visible') { + docSetImposterStyle(imposterStyle, 'overflow', 'auto'); + } + } else { + docSetImposterStyle(imposterStyle, 'overflow', 'hidden'); + docSetImposterStyle(imposterStyle, 'white-space', 'nowrap'); + docSetImposterStyle(imposterStyle, 'line-height', elementStyle.height); } - const offset = docOffsetCalc(element); - const imposter = document.createElement('div'); - 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; - if (element.nodeName === 'TEXTAREA' && styleProps.overflow === 'visible') { - imposter.style.overflow = 'auto'; + container.appendChild(imposter); + document.body.appendChild(container); + + // Adjust size + const imposterRect = imposter.getBoundingClientRect(); + if (imposterRect.width !== elementRect.width || imposterRect.height !== elementRect.height) { + 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`); } - document.body.appendChild(imposter); imposter.scrollTop = element.scrollTop; imposter.scrollLeft = element.scrollLeft; - return imposter; -} - -function docImposterDestroy() { - for (const element of document.getElementsByClassName('yomichan-imposter')) { - element.parentNode.removeChild(element); - } + 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, imposterContainer] = docImposterCreate(element, false); + break; case 'TEXTAREA': - imposter = docImposterCreate(element); + [imposter, imposterContainer] = docImposterCreate(element, true); break; } } const range = document.caretRangeFromPoint(point.x, point.y); - if (imposter !== null) { - imposter.style.zIndex = -2147483646; + if (range !== null && isPointInRange(point, range)) { + if (imposter !== null) { + docSetImposterStyle(imposterContainer.style, 'z-index', '-2147483646'); + docSetImposterStyle(imposter.style, 'pointer-events', 'none'); + } + return new TextSourceRange(range, '', imposterContainer); + } else { + if (imposterContainer !== null) { + imposterContainer.parentNode.removeChild(imposterContainer); + } + 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..69d8197d 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, imposterContainer) { this.range = range; this.content = content; + this.imposterContainer = imposterContainer; } clone() { - return new TextSourceRange(this.range.cloneRange(), this.content); + return new TextSourceRange(this.range.cloneRange(), this.content, this.imposterContainer); + } + + cleanup() { + if (this.imposterContainer !== null && this.imposterContainer.parentNode !== null) { + this.imposterContainer.parentNode.removeChild(this.imposterContainer); + } } 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: { |