diff options
Diffstat (limited to 'ext/js/pages/settings/popup-preview-frame.js')
-rw-r--r-- | ext/js/pages/settings/popup-preview-frame.js | 102 |
1 files changed, 86 insertions, 16 deletions
diff --git a/ext/js/pages/settings/popup-preview-frame.js b/ext/js/pages/settings/popup-preview-frame.js index 7a1a0b3a..60d264fa 100644 --- a/ext/js/pages/settings/popup-preview-frame.js +++ b/ext/js/pages/settings/popup-preview-frame.js @@ -22,32 +22,53 @@ import {TextSourceRange} from '../../dom/text-source-range.js'; import {yomitan} from '../../yomitan.js'; export class PopupPreviewFrame { + /** + * @param {number} tabId + * @param {number} frameId + * @param {import('../../app/popup-factory.js').PopupFactory} popupFactory + * @param {import('../../input/hotkey-handler.js').HotkeyHandler} hotkeyHandler + */ constructor(tabId, frameId, popupFactory, hotkeyHandler) { + /** @type {number} */ this._tabId = tabId; + /** @type {number} */ this._frameId = frameId; + /** @type {import('../../app/popup-factory.js').PopupFactory} */ this._popupFactory = popupFactory; + /** @type {import('../../input/hotkey-handler.js').HotkeyHandler} */ this._hotkeyHandler = hotkeyHandler; + /** @type {?Frontend} */ this._frontend = null; + /** @type {?(optionsContext: import('settings').OptionsContext) => Promise<import('settings').ProfileOptions>} */ this._apiOptionsGetOld = null; + /** @type {boolean} */ this._popupShown = false; + /** @type {?import('core').Timeout} */ this._themeChangeTimeout = null; + /** @type {?import('text-source').TextSource} */ this._textSource = null; + /** @type {?import('settings').OptionsContext} */ this._optionsContext = null; + /** @type {?HTMLElement} */ this._exampleText = null; + /** @type {?HTMLInputElement} */ this._exampleTextInput = null; + /** @type {string} */ this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); - this._windowMessageHandlers = new Map([ + /** @type {Map<string, (params: import('core').SerializableObjectAny) => void>} */ + this._windowMessageHandlers = new Map(/** @type {[key: string, handler: (params: import('core').SerializableObjectAny) => void][]} */ ([ ['PopupPreviewFrame.setText', this._onSetText.bind(this)], ['PopupPreviewFrame.setCustomCss', this._setCustomCss.bind(this)], ['PopupPreviewFrame.setCustomOuterCss', this._setCustomOuterCss.bind(this)], ['PopupPreviewFrame.updateOptionsContext', this._updateOptionsContext.bind(this)] - ]); + ])); } + /** */ async prepare() { - this._exampleText = document.querySelector('#example-text'); - this._exampleTextInput = document.querySelector('#example-text-input'); + this._exampleText = /** @type {HTMLElement} */ (document.querySelector('#example-text')); + this._exampleTextInput = /** @type {HTMLInputElement} */ (document.querySelector('#example-text-input')); if (this._exampleTextInput !== null && typeof wanakana !== 'undefined') { wanakana.bind(this._exampleTextInput); @@ -56,12 +77,14 @@ export class PopupPreviewFrame { window.addEventListener('message', this._onMessage.bind(this), false); // Setup events - document.querySelector('#theme-dark-checkbox').addEventListener('change', this._onThemeDarkCheckboxChanged.bind(this), false); + const darkThemeCheckbox = /** @type {HTMLInputElement} */ (document.querySelector('#theme-dark-checkbox')); + darkThemeCheckbox.addEventListener('change', this._onThemeDarkCheckboxChanged.bind(this), false); this._exampleText.addEventListener('click', this._onExampleTextClick.bind(this), false); this._exampleTextInput.addEventListener('blur', this._onExampleTextInputBlur.bind(this), false); this._exampleTextInput.addEventListener('input', this._onExampleTextInputInput.bind(this), false); // Overwrite API functions + /** @type {?(optionsContext: import('settings').OptionsContext) => Promise<import('settings').ProfileOptions>} */ this._apiOptionsGetOld = yomitan.api.optionsGet.bind(yomitan.api); yomitan.api.optionsGet = this._apiOptionsGet.bind(this); @@ -84,7 +107,10 @@ export class PopupPreviewFrame { await this._frontend.prepare(); this._frontend.setDisabledOverride(true); this._frontend.canClearSelection = false; - this._frontend.popup.on('customOuterCssChanged', this._onCustomOuterCssChanged.bind(this)); + const {popup} = this._frontend; + if (popup !== null) { + popup.on('customOuterCssChanged', this._onCustomOuterCssChanged.bind(this)); + } // Update search this._updateSearch(); @@ -92,8 +118,12 @@ export class PopupPreviewFrame { // Private - async _apiOptionsGet(...args) { - const options = await this._apiOptionsGetOld(...args); + /** + * @param {import('settings').OptionsContext} optionsContext + * @returns {Promise<import('settings').ProfileOptions>} + */ + async _apiOptionsGet(optionsContext) { + const options = await /** @type {(optionsContext: import('settings').OptionsContext) => Promise<import('settings').ProfileOptions>} */ (this._apiOptionsGetOld)(optionsContext); options.general.enable = true; options.general.debugInfo = false; options.general.popupWidth = 400; @@ -108,16 +138,24 @@ export class PopupPreviewFrame { return options; } + /** + * @param {import('popup').CustomOuterCssChangedEvent} details + */ _onCustomOuterCssChanged({node, inShadow}) { if (node === null || inShadow) { return; } const node2 = document.querySelector('#popup-outer-css'); if (node2 === null) { return; } + const {parentNode} = node2; + if (parentNode === null) { return; } // This simulates the stylesheet priorities when injecting using the web extension API. - node2.parentNode.insertBefore(node, node2); + parentNode.insertBefore(node, node2); } + /** + * @param {MessageEvent<{action: string, params: import('core').SerializableObject}>} e + */ _onMessage(e) { if (e.origin !== this._targetOrigin) { return; } @@ -128,19 +166,24 @@ export class PopupPreviewFrame { handler(params); } + /** + * @param {Event} e + */ _onThemeDarkCheckboxChanged(e) { - document.documentElement.classList.toggle('dark', e.target.checked); + const element = /** @type {HTMLInputElement} */ (e.currentTarget); + document.documentElement.classList.toggle('dark', element.checked); if (this._themeChangeTimeout !== null) { clearTimeout(this._themeChangeTimeout); } this._themeChangeTimeout = setTimeout(() => { this._themeChangeTimeout = null; - const popup = this._frontend.popup; + const popup = /** @type {Frontend} */ (this._frontend).popup; if (popup === null) { return; } popup.updateTheme(); }, 300); } + /** */ _onExampleTextClick() { if (this._exampleTextInput === null) { return; } const visible = this._exampleTextInput.hidden; @@ -150,19 +193,31 @@ export class PopupPreviewFrame { this._exampleTextInput.select(); } + /** */ _onExampleTextInputBlur() { if (this._exampleTextInput === null) { return; } this._exampleTextInput.hidden = true; } + /** + * @param {Event} e + */ _onExampleTextInputInput(e) { - this._setText(e.currentTarget.value); + const element = /** @type {HTMLInputElement} */ (e.currentTarget); + this._setText(element.value, false); } + /** + * @param {{text: string}} details + */ _onSetText({text}) { this._setText(text, true); } + /** + * @param {string} text + * @param {boolean} setInput + */ _setText(text, setInput) { if (setInput && this._exampleTextInput !== null) { this._exampleTextInput.value = text; @@ -175,6 +230,9 @@ export class PopupPreviewFrame { this._updateSearch(); } + /** + * @param {boolean} visible + */ _setInfoVisible(visible) { const node = document.querySelector('.placeholder-info'); if (node === null) { return; } @@ -182,6 +240,9 @@ export class PopupPreviewFrame { node.classList.toggle('placeholder-info-visible', visible); } + /** + * @param {{css: string}} details + */ _setCustomCss({css}) { if (this._frontend === null) { return; } const popup = this._frontend.popup; @@ -189,6 +250,9 @@ export class PopupPreviewFrame { popup.setCustomCss(css); } + /** + * @param {{css: string}} details + */ _setCustomOuterCss({css}) { if (this._frontend === null) { return; } const popup = this._frontend.popup; @@ -196,7 +260,11 @@ export class PopupPreviewFrame { popup.setCustomOuterCss(css, false); } - async _updateOptionsContext({optionsContext}) { + /** + * @param {{optionsContext: import('settings').OptionsContext}} details + */ + async _updateOptionsContext(details) { + const {optionsContext} = details; this._optionsContext = optionsContext; if (this._frontend === null) { return; } this._frontend.setOptionsContextOverride(optionsContext); @@ -204,6 +272,7 @@ export class PopupPreviewFrame { await this._updateSearch(); } + /** */ async _updateSearch() { if (this._exampleText === null) { return; } @@ -213,16 +282,17 @@ export class PopupPreviewFrame { const range = document.createRange(); range.selectNodeContents(textNode); const source = TextSourceRange.create(range); + const frontend = /** @type {Frontend} */ (this._frontend); try { - await this._frontend.setTextSource(source); + await frontend.setTextSource(source); } finally { source.cleanup(); } this._textSource = source; - await this._frontend.showContentCompleted(); + await frontend.showContentCompleted(); - const popup = this._frontend.popup; + const popup = frontend.popup; if (popup !== null && popup.isVisibleSync()) { this._popupShown = true; } |