From 9e9040178d1c498f4c7298a241137d2e7af59114 Mon Sep 17 00:00:00 2001
From: toasted-nutbread <toasted-nutbread@users.noreply.github.com>
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 <toasted-nutbread@users.noreply.github.com>
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 <toasted-nutbread@users.noreply.github.com>
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 <toasted-nutbread@users.noreply.github.com>
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 <toasted-nutbread@users.noreply.github.com>
Date: Sat, 31 Aug 2019 15:10:46 -0400
Subject: Fix imposter issues with <input type="text">

---
 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 <toasted-nutbread@users.noreply.github.com>
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 <toasted-nutbread@users.noreply.github.com>
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