aboutsummaryrefslogtreecommitdiff
path: root/ext/fg/js/popup.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/fg/js/popup.js')
-rw-r--r--ext/fg/js/popup.js112
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');
}
}