diff options
Diffstat (limited to 'ext/fg/js/popup.js')
| -rw-r--r-- | ext/fg/js/popup.js | 112 | 
1 files changed, 82 insertions, 30 deletions
| diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 86ce575d..1b15977b 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -18,7 +18,13 @@  class Popup { -    constructor() { +    constructor(id, depth, frameIdPromise) { +        this.id = id; +        this.depth = depth; +        this.frameIdPromise = frameIdPromise; +        this.frameId = null; +        this.parent = null; +        this.child = null;          this.container = document.createElement('iframe');          this.container.id = 'yomichan-float';          this.container.addEventListener('mousedown', e => e.stopPropagation()); @@ -26,26 +32,46 @@ class Popup {          this.container.setAttribute('src', chrome.extension.getURL('/fg/float.html'));          this.container.style.width = '0px';          this.container.style.height = '0px'; -        this.injected = null; +        this.injectPromise = null; +        this.isInjected = false;      }      inject(options) { -        if (!this.injected) { -            this.injected = new Promise((resolve, reject) => { -                this.container.addEventListener('load', () => { -                    this.invokeApi('setOptions', { -                        general: { -                            customPopupCss: options.general.customPopupCss -                        } -                    }); -                    resolve(); -                }); -                this.observeFullscreen(); -                this.onFullscreenChanged(); -            }); +        if (this.injectPromise === null) { +            this.injectPromise = this.createInjectPromise(options); +        } +        return this.injectPromise; +    } + +    async createInjectPromise(options) { +        try { +            const {frameId} = await this.frameIdPromise; +            if (typeof frameId === 'number') { +                this.frameId = frameId; +            } +        } catch (e) { +            // NOP          } -        return this.injected; +        return new Promise((resolve) => { +            const parentFrameId = (typeof this.frameId === 'number' ? this.frameId : null); +            this.container.addEventListener('load', () => { +                this.invokeApi('popupNestedInitialize', { +                    id: this.id, +                    depth: this.depth, +                    parentFrameId +                }); +                this.invokeApi('setOptions', { +                    general: { +                        customPopupCss: options.general.customPopupCss +                    } +                }); +                resolve(); +            }); +            this.observeFullscreen(); +            this.onFullscreenChanged(); +            this.isInjected = true; +        });      }      async show(elementRect, writingMode, options) { @@ -77,6 +103,8 @@ class Popup {          container.style.width = `${width}px`;          container.style.height = `${height}px`;          container.style.visibility = 'visible'; + +        this.hideChildren();      }      static getPositionForHorizontalText(elementRect, width, height, maxWidth, maxHeight, optionsGeneral) { @@ -178,12 +206,28 @@ class Popup {      }      hide() { +        this.hideChildren(); +        this.hideContainer(); +        this.focusParent(); +    } + +    hideChildren() { +        // recursively hides all children +        if (this.child && !this.child.isContainerHidden()) { +            this.child.hide(); +        } +    } + +    hideContainer() {          this.container.style.visibility = 'hidden'; -        this.container.blur(); +    } + +    isContainerHidden() { +        return (this.container.style.visibility === 'hidden');      }      isVisible() { -        return this.injected && this.container.style.visibility !== 'hidden'; +        return this.isInjected && this.container.style.visibility !== 'hidden';      }      setVisible(visible) { @@ -194,19 +238,27 @@ class Popup {          }      } -    containsPoint(point) { -        if (!this.isVisible()) { -            return false; +    focusParent() { +        if (this.parent && this.parent.container) { +            // Chrome doesn't like focusing iframe without contentWindow. +            this.parent.container.contentWindow.focus(); +        } else { +            // Firefox doesn't like focusing window without first blurring the iframe. +            // this.container.contentWindow.blur() doesn't work on Firefox for some reason. +            this.container.blur(); +            // This is needed for Chrome. +            window.focus();          } +    } -        const rect = this.container.getBoundingClientRect(); -        const contained = -            point.x >= rect.left && -            point.y >= rect.top && -            point.x < rect.right && -            point.y < rect.bottom; - -        return contained; +    async containsPoint({x, y}) { +        for (let popup = this; popup !== null && popup.isVisible(); popup = popup.child) { +            const rect = popup.container.getBoundingClientRect(); +            if (x >= rect.left && y >= rect.top && x < rect.right && y < rect.bottom) { +                return true; +            } +        } +        return false;      }      async termsShow(elementRect, writingMode, definitions, options, context) { @@ -220,7 +272,7 @@ class Popup {      }      clearAutoPlayTimer() { -        if (this.injected) { +        if (this.isInjected) {              this.invokeApi('clearAutoPlayTimer');          }      } |