diff options
Diffstat (limited to 'ext/fg/js/popup.js')
-rw-r--r-- | ext/fg/js/popup.js | 144 |
1 files changed, 120 insertions, 24 deletions
diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 9ca91afa..b5eb9fe2 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -25,8 +25,9 @@ class Popup { this.frameId = null; this.parent = null; this.child = null; + this.childrenSupported = true; this.container = document.createElement('iframe'); - this.container.id = 'yomichan-float'; + this.container.className = 'yomichan-float'; this.container.addEventListener('mousedown', e => e.stopPropagation()); this.container.addEventListener('scroll', e => e.stopPropagation()); this.container.setAttribute('src', chrome.extension.getURL('/fg/float.html')); @@ -36,17 +37,19 @@ class Popup { this.isInjected = false; this.visible = false; this.visibleOverride = null; + this.options = null; + this.stylesheetInjectedViaApi = false; this.updateVisibility(); } - inject(options) { + inject() { if (this.injectPromise === null) { - this.injectPromise = this.createInjectPromise(options); + this.injectPromise = this.createInjectPromise(); } return this.injectPromise; } - async createInjectPromise(options) { + async createInjectPromise() { try { const {frameId} = await this.frameIdPromise; if (typeof frameId === 'number') { @@ -60,30 +63,44 @@ class Popup { const parentFrameId = (typeof this.frameId === 'number' ? this.frameId : null); this.container.addEventListener('load', () => { this.invokeApi('initialize', { - options: { - general: { - customPopupCss: options.general.customPopupCss - } - }, + options: this.options, popupInfo: { id: this.id, depth: this.depth, parentFrameId }, - url: this.url + url: this.url, + childrenSupported: this.childrenSupported }); resolve(); }); this.observeFullscreen(); this.onFullscreenChanged(); + this.setCustomOuterCss(this.options.general.customPopupOuterCss, false); this.isInjected = true; }); } - async show(elementRect, writingMode, options) { - await this.inject(options); + isInitialized() { + return this.options !== null; + } + + async setOptions(options) { + this.options = options; + this.updateTheme(); + } + + async showContent(elementRect, writingMode, type=null, details=null) { + if (!this.isInitialized()) { return; } + await this.show(elementRect, writingMode); + if (type === null) { return; } + this.invokeApi('setContent', {type, details}); + } - const optionsGeneral = options.general; + async show(elementRect, writingMode) { + await this.inject(); + + const optionsGeneral = this.options.general; const container = this.container; const containerRect = container.getBoundingClientRect(); const getPosition = ( @@ -208,11 +225,6 @@ class Popup { return [position, size, after]; } - async showOrphaned(elementRect, writingMode, options) { - await this.show(elementRect, writingMode, options); - this.invokeApi('orphaned'); - } - hide(changeFocus) { if (!this.isVisible()) { return; @@ -227,6 +239,10 @@ class Popup { } } + async isVisibleAsync() { + return this.isVisible(); + } + isVisible() { return this.isInjected && (this.visibleOverride !== null ? this.visibleOverride : this.visible); } @@ -261,6 +277,44 @@ class Popup { } } + updateTheme() { + this.container.dataset.yomichanTheme = this.options.general.popupOuterTheme; + this.container.dataset.yomichanSiteColor = this.getSiteColor(); + } + + getSiteColor() { + const color = [255, 255, 255]; + Popup.addColor(color, Popup.getColorInfo(window.getComputedStyle(document.documentElement).backgroundColor)); + Popup.addColor(color, Popup.getColorInfo(window.getComputedStyle(document.body).backgroundColor)); + const dark = (color[0] < 128 && color[1] < 128 && color[2] < 128); + return dark ? 'dark' : 'light'; + } + + static addColor(target, color) { + if (color === null) { return; } + + const a = color[3]; + if (a <= 0.0) { return; } + + const aInv = 1.0 - a; + for (let i = 0; i < 3; ++i) { + target[i] = target[i] * aInv + color[i] * a; + } + } + + static getColorInfo(cssColor) { + const m = /^\s*rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d\.]+)\s*)?\)\s*$/.exec(cssColor); + if (m === null) { return null; } + + const m4 = m[4]; + return [ + Number.parseInt(m[1], 10), + Number.parseInt(m[2], 10), + Number.parseInt(m[3], 10), + m4 ? Math.max(0.0, Math.min(1.0, Number.parseFloat(m4))) : 1.0 + ]; + } + async containsPoint(x, y) { for (let popup = this; popup !== null && popup.isVisible(); popup = popup.child) { const rect = popup.container.getBoundingClientRect(); @@ -271,14 +325,25 @@ class Popup { return false; } - async termsShow(elementRect, writingMode, definitions, options, context) { - await this.show(elementRect, writingMode, options); - this.invokeApi('termsShow', {definitions, options, context}); + async setCustomCss(css) { + this.invokeApi('setCustomCss', {css}); } - async kanjiShow(elementRect, writingMode, definitions, options, context) { - await this.show(elementRect, writingMode, options); - this.invokeApi('kanjiShow', {definitions, options, context}); + async setCustomOuterCss(css, injectDirectly) { + // Cannot repeatedly inject stylesheets using web extension APIs since there is no way to remove them. + if (this.stylesheetInjectedViaApi) { return; } + + if (injectDirectly || Popup.isOnExtensionPage()) { + Popup.injectOuterStylesheet(css); + } else { + if (!css) { return; } + try { + await apiInjectStylesheet(css); + this.stylesheetInjectedViaApi = true; + } catch (e) { + // NOP + } + } } clearAutoPlayTimer() { @@ -322,4 +387,35 @@ class Popup { get url() { return window.location.href; } + + static isOnExtensionPage() { + try { + const url = chrome.runtime.getURL('/'); + return window.location.href.substr(0, url.length) === url; + } catch (e) { + // NOP + } + } + + static injectOuterStylesheet(css) { + if (Popup.outerStylesheet === null) { + if (!css) { return; } + Popup.outerStylesheet = document.createElement('style'); + Popup.outerStylesheet.id = "yomichan-popup-outer-stylesheet"; + } + + const outerStylesheet = Popup.outerStylesheet; + if (css) { + outerStylesheet.textContent = css; + + const par = document.head; + if (par && outerStylesheet.parentNode !== par) { + par.appendChild(outerStylesheet); + } + } else { + outerStylesheet.textContent = ''; + } + } } + +Popup.outerStylesheet = null; |