From 4da4827bcbcdd1ef163f635d9b29416ff272b0bb Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 27 Nov 2023 12:48:14 -0500 Subject: Add JSDoc type annotations to project (rebased) --- ext/js/app/frontend.js | 308 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 246 insertions(+), 62 deletions(-) (limited to 'ext/js/app/frontend.js') diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index 4c13eac2..fec933f8 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -17,7 +17,7 @@ */ import {GoogleDocsUtil} from '../accessibility/google-docs-util.js'; -import {EventListenerCollection, invokeMessageHandler, isObject, log, promiseAnimationFrame} from '../core.js'; +import {EventListenerCollection, invokeMessageHandler, log, promiseAnimationFrame} from '../core.js'; import {DocumentUtil} from '../dom/document-util.js'; import {TextSourceElement} from '../dom/text-source-element.js'; import {TextSourceRange} from '../dom/text-source-range.js'; @@ -25,7 +25,6 @@ import {HotkeyHandler} from '../input/hotkey-handler.js'; import {TextScanner} from '../language/text-scanner.js'; import {yomitan} from '../yomitan.js'; import {PopupFactory} from './popup-factory.js'; -import {Popup} from './popup.js'; /** * This is the main class responsible for scanning and handling webpage content. @@ -33,19 +32,7 @@ import {Popup} from './popup.js'; export class Frontend { /** * Creates a new instance. - * @param {object} details Details about how to set up the instance. - * @param {string} details.pageType The type of page, one of 'web', 'popup', or 'search'. - * @param {PopupFactory} details.popupFactory A PopupFactory instance to use for generating popups. - * @param {number} details.depth The nesting depth value of the popup. - * @param {number} details.tabId The tab ID of the host tab. - * @param {number} details.frameId The frame ID of the host frame. - * @param {?string} details.parentPopupId The popup ID of the parent popup if one exists, otherwise null. - * @param {?number} details.parentFrameId The frame ID of the parent popup if one exists, otherwise null. - * @param {boolean} details.useProxyPopup Whether or not proxy popups should be used. - * @param {boolean} details.canUseWindowPopup Whether or not window popups can be used. - * @param {boolean} details.allowRootFramePopupProxy Whether or not popups can be hosted in the root frame. - * @param {boolean} details.childrenSupported Whether popups can create child popups or not. - * @param {HotkeyHandler} details.hotkeyHandler A HotkeyHandler instance. + * @param {import('frontend').ConstructorDetails} details Details about how to set up the instance. */ constructor({ pageType, @@ -61,24 +48,43 @@ export class Frontend { childrenSupported=true, hotkeyHandler }) { + /** @type {import('frontend').PageType} */ this._pageType = pageType; + /** @type {PopupFactory} */ this._popupFactory = popupFactory; + /** @type {number} */ this._depth = depth; + /** @type {number|undefined} */ this._tabId = tabId; + /** @type {number} */ this._frameId = frameId; + /** @type {?string} */ this._parentPopupId = parentPopupId; + /** @type {?number} */ this._parentFrameId = parentFrameId; + /** @type {boolean} */ this._useProxyPopup = useProxyPopup; + /** @type {boolean} */ this._canUseWindowPopup = canUseWindowPopup; + /** @type {boolean} */ this._allowRootFramePopupProxy = allowRootFramePopupProxy; + /** @type {boolean} */ this._childrenSupported = childrenSupported; + /** @type {HotkeyHandler} */ this._hotkeyHandler = hotkeyHandler; + /** @type {?import('popup').PopupAny} */ this._popup = null; + /** @type {boolean} */ this._disabledOverride = false; + /** @type {?import('settings').ProfileOptions} */ this._options = null; + /** @type {number} */ this._pageZoomFactor = 1.0; + /** @type {number} */ this._contentScale = 1.0; + /** @type {Promise} */ this._lastShowPromise = Promise.resolve(); + /** @type {TextScanner} */ this._textScanner = new TextScanner({ node: window, ignoreElements: this._ignoreElements.bind(this), @@ -87,19 +93,27 @@ export class Frontend { searchTerms: true, searchKanji: true }); + /** @type {boolean} */ this._textScannerHasBeenEnabled = false; + /** @type {Map<'default'|'window'|'iframe'|'proxy', Promise>} */ this._popupCache = new Map(); + /** @type {EventListenerCollection} */ this._popupEventListeners = new EventListenerCollection(); + /** @type {?import('core').TokenObject} */ this._updatePopupToken = null; + /** @type {?number} */ this._clearSelectionTimer = null; + /** @type {boolean} */ this._isPointerOverPopup = false; + /** @type {?import('settings').OptionsContext} */ this._optionsContextOverride = null; - this._runtimeMessageHandlers = new Map([ + /** @type {import('core').MessageHandlerMap} */ + this._runtimeMessageHandlers = new Map(/** @type {import('core').MessageHandlerArray} */ ([ ['Frontend.requestReadyBroadcast', {async: false, handler: this._onMessageRequestFrontendReadyBroadcast.bind(this)}], ['Frontend.setAllVisibleOverride', {async: true, handler: this._onApiSetAllVisibleOverride.bind(this)}], ['Frontend.clearAllVisibleOverride', {async: true, handler: this._onApiClearAllVisibleOverride.bind(this)}] - ]); + ])); this._hotkeyHandler.registerActions([ ['scanSelectedText', this._onActionScanSelectedText.bind(this)], @@ -125,7 +139,7 @@ export class Frontend { /** * Gets the popup instance. - * @type {Popup} + * @type {?import('popup').PopupAny} */ get popup() { return this._popup; @@ -148,8 +162,8 @@ export class Frontend { window.addEventListener('resize', this._onResize.bind(this), false); DocumentUtil.addFullscreenChangeEventListener(this._updatePopup.bind(this)); - const visualViewport = window.visualViewport; - if (visualViewport !== null && typeof visualViewport === 'object') { + const {visualViewport} = window; + if (typeof visualViewport !== 'undefined' && visualViewport !== null) { visualViewport.addEventListener('scroll', this._onVisualViewportScroll.bind(this)); visualViewport.addEventListener('resize', this._onVisualViewportResize.bind(this)); } @@ -172,7 +186,7 @@ export class Frontend { this._prepareSiteSpecific(); this._updateContentScale(); - this._signalFrontendReady(); + this._signalFrontendReady(null); } /** @@ -186,7 +200,7 @@ export class Frontend { /** * Set or clear an override options context object. - * @param {?object} optionsContext An options context object to use as the override, or `null` to clear the override. + * @param {?import('settings').OptionsContext} optionsContext An options context object to use as the override, or `null` to clear the override. */ setOptionsContextOverride(optionsContext) { this._optionsContextOverride = optionsContext; @@ -194,7 +208,7 @@ export class Frontend { /** * Performs a new search on a specific source. - * @param {TextSourceRange|TextSourceElement} textSource The text source to search. + * @param {import('text-source').TextSource} textSource The text source to search. */ async setTextSource(textSource) { this._textScanner.setCurrentTextSource(null); @@ -216,7 +230,7 @@ export class Frontend { /** * Waits for the previous `showContent` call to be completed. - * @returns {Promise} A promise which is resolved when the previous `showContent` call has completed. + * @returns {Promise} A promise which is resolved when the previous `showContent` call has completed. */ showContentCompleted() { return this._lastShowPromise; @@ -224,45 +238,73 @@ export class Frontend { // Message handlers + /** + * @param {import('frontend').FrontendRequestReadyBroadcastParams} params + */ _onMessageRequestFrontendReadyBroadcast({frameId}) { this._signalFrontendReady(frameId); } // Action handlers + /** + * @returns {void} + */ _onActionScanSelectedText() { this._scanSelectedText(false); } + /** + * @returns {void} + */ _onActionScanTextAtCaret() { this._scanSelectedText(true); } // API message handlers + /** + * @returns {string} + */ _onApiGetUrl() { return window.location.href; } + /** + * @returns {void} + */ _onApiClosePopup() { this._clearSelection(false); } + /** + * @returns {void} + */ _onApiCopySelection() { // This will not work on Firefox if a popup has focus, which is usually the case when this function is called. document.execCommand('copy'); } + /** + * @returns {string} + */ _onApiGetSelectionText() { - return document.getSelection().toString(); + const selection = document.getSelection(); + return selection !== null ? selection.toString() : ''; } + /** + * @returns {import('frontend').GetPopupInfoResult} + */ _onApiGetPopupInfo() { return { popupId: (this._popup !== null ? this._popup.id : null) }; } + /** + * @returns {{url: string, documentTitle: string}} + */ _onApiGetPageInfo() { return { url: window.location.href, @@ -270,6 +312,10 @@ export class Frontend { }; } + /** + * @param {{value: boolean, priority: number, awaitFrame: boolean}} params + * @returns {Promise} + */ async _onApiSetAllVisibleOverride({value, priority, awaitFrame}) { const result = await this._popupFactory.setAllVisibleOverride(value, priority); if (awaitFrame) { @@ -278,45 +324,71 @@ export class Frontend { return result; } + /** + * @param {{token: import('core').TokenString}} params + * @returns {Promise} + */ async _onApiClearAllVisibleOverride({token}) { return await this._popupFactory.clearAllVisibleOverride(token); } // Private + /** + * @returns {void} + */ _onResize() { this._updatePopupPosition(); } + /** @type {import('extension').ChromeRuntimeOnMessageCallback} */ _onRuntimeMessage({action, params}, sender, callback) { const messageHandler = this._runtimeMessageHandlers.get(action); if (typeof messageHandler === 'undefined') { return false; } return invokeMessageHandler(messageHandler, params, callback, sender); } + /** + * @param {{newZoomFactor: number}} params + */ _onZoomChanged({newZoomFactor}) { this._pageZoomFactor = newZoomFactor; this._updateContentScale(); } + /** + * @returns {void} + */ _onClosePopups() { this._clearSelection(true); } + /** + * @returns {void} + */ _onVisualViewportScroll() { this._updatePopupPosition(); } + /** + * @returns {void} + */ _onVisualViewportResize() { this._updateContentScale(); } + /** + * @returns {void} + */ _onTextScannerClear() { this._clearSelection(false); } - _onSearched({type, dictionaryEntries, sentence, inputInfo: {eventType, passive, detail}, textSource, optionsContext, detail: {documentTitle}, error}) { - const scanningOptions = this._options.scanning; + /** + * @param {import('text-scanner').SearchedEventDetails} details + */ + _onSearched({type, dictionaryEntries, sentence, inputInfo: {eventType, passive, detail: inputInfoDetail}, textSource, optionsContext, detail, error}) { + const scanningOptions = /** @type {import('settings').ProfileOptions} */ (this._options).scanning; if (error !== null) { if (yomitan.isExtensionUnloaded) { @@ -326,34 +398,43 @@ export class Frontend { } else { log.error(error); } - } if (type !== null) { + } if (type !== null && optionsContext !== null) { this._stopClearSelectionDelayed(); let focus = (eventType === 'mouseMove'); - if (isObject(detail)) { - const focus2 = detail.focus; + if (typeof inputInfoDetail === 'object' && inputInfoDetail !== null) { + const focus2 = inputInfoDetail.focus; if (typeof focus2 === 'boolean') { focus = focus2; } } - this._showContent(textSource, focus, dictionaryEntries, type, sentence, documentTitle, optionsContext); + this._showContent(textSource, focus, dictionaryEntries, type, sentence, detail !== null ? detail.documentTitle : null, optionsContext); } else { if (scanningOptions.autoHideResults) { - this._clearSelectionDelayed(scanningOptions.hideDelay, false); + this._clearSelectionDelayed(scanningOptions.hideDelay, false, false); } } } + /** + * @returns {void} + */ _onPopupFramePointerOver() { this._isPointerOverPopup = true; this._stopClearSelectionDelayed(); } + /** + * @returns {void} + */ _onPopupFramePointerOut() { this._isPointerOverPopup = false; - const scanningOptions = this._options.scanning; + const scanningOptions = /** @type {import('settings').ProfileOptions} */ (this._options).scanning; if (scanningOptions.hidePopupOnCursorExit) { - this._clearSelectionDelayed(scanningOptions.hidePopupOnCursorExitDelay, false); + this._clearSelectionDelayed(scanningOptions.hidePopupOnCursorExitDelay, false, false); } } + /** + * @param {boolean} passive + */ _clearSelection(passive) { this._stopClearSelectionDelayed(); if (this._popup !== null) { @@ -364,6 +445,11 @@ export class Frontend { this._textScanner.clearSelection(); } + /** + * @param {number} delay + * @param {boolean} restart + * @param {boolean} passive + */ _clearSelectionDelayed(delay, restart, passive) { if (!this._textScanner.hasSelection()) { return; } if (delay > 0) { @@ -379,6 +465,9 @@ export class Frontend { } } + /** + * @returns {void} + */ _stopClearSelectionDelayed() { if (this._clearSelectionTimer !== null) { clearTimeout(this._clearSelectionTimer); @@ -386,6 +475,9 @@ export class Frontend { } } + /** + * @returns {Promise} + */ async _updateOptionsInternal() { const optionsContext = await this._getOptionsContext(); const options = await yomitan.api.optionsGet(optionsContext); @@ -426,12 +518,16 @@ export class Frontend { await this._textScanner.searchLast(); } + /** + * @returns {Promise} + */ async _updatePopup() { - const {usePopupWindow, showIframePopupsInRootFrame} = this._options.general; + const {usePopupWindow, showIframePopupsInRootFrame} = /** @type {import('settings').ProfileOptions} */ (this._options).general; const isIframe = !this._useProxyPopup && (window !== window.parent); const currentPopup = this._popup; + /** @type {Promise|undefined} */ let popupPromise; if (usePopupWindow && this._canUseWindowPopup) { popupPromise = this._popupCache.get('window'); @@ -466,6 +562,7 @@ export class Frontend { // The token below is used as a unique identifier to ensure that a new _updatePopup call // hasn't been started during the await. + /** @type {?import('core').TokenObject} */ const token = {}; this._updatePopupToken = token; const popup = await popupPromise; @@ -489,6 +586,9 @@ export class Frontend { this._isPointerOverPopup = false; } + /** + * @returns {Promise} + */ async _getDefaultPopup() { const isXmlDocument = (typeof XMLDocument !== 'undefined' && document instanceof XMLDocument); if (isXmlDocument) { @@ -502,6 +602,9 @@ export class Frontend { }); } + /** + * @returns {Promise} + */ async _getProxyPopup() { return await this._popupFactory.getOrCreatePopup({ frameId: this._parentFrameId, @@ -511,6 +614,9 @@ export class Frontend { }); } + /** + * @returns {Promise} + */ async _getIframeProxyPopup() { const targetFrameId = 0; // Root frameId try { @@ -520,7 +626,8 @@ export class Frontend { return await this._getDefaultPopup(); } - const {popupId} = await yomitan.crossFrame.invoke(targetFrameId, 'Frontend.getPopupInfo'); + /** @type {import('frontend').GetPopupInfoResult} */ + const {popupId} = await yomitan.crossFrame.invoke(targetFrameId, 'Frontend.getPopupInfo', {}); if (popupId === null) { return null; } @@ -537,6 +644,9 @@ export class Frontend { return popup; } + /** + * @returns {Promise} + */ async _getPopupWindow() { return await this._popupFactory.getOrCreatePopup({ depth: this._depth, @@ -545,6 +655,9 @@ export class Frontend { }); } + /** + * @returns {Element[]} + */ _ignoreElements() { if (this._popup !== null) { const container = this._popup.container; @@ -555,6 +668,11 @@ export class Frontend { return []; } + /** + * @param {number} x + * @param {number} y + * @returns {Promise} + */ async _ignorePoint(x, y) { try { return this._popup !== null && await this._popup.containsPoint(x, y); @@ -566,17 +684,44 @@ export class Frontend { } } + /** + * @param {import('text-source').TextSource} textSource + */ _showExtensionUnloaded(textSource) { - if (textSource === null) { - textSource = this._textScanner.getCurrentTextSource(); - if (textSource === null) { return; } - } this._showPopupContent(textSource, null, null); } + /** + * @param {import('text-source').TextSource} textSource + * @param {boolean} focus + * @param {?import('dictionary').DictionaryEntry[]} dictionaryEntries + * @param {import('display').PageType} type + * @param {?import('display').HistoryStateSentence} sentence + * @param {?string} documentTitle + * @param {import('settings').OptionsContext} optionsContext + */ _showContent(textSource, focus, dictionaryEntries, type, sentence, documentTitle, optionsContext) { const query = textSource.text(); const {url} = optionsContext; + /** @type {import('display').HistoryState} */ + const detailsState = { + focusEntry: 0, + optionsContext, + url + }; + if (sentence !== null) { detailsState.sentence = sentence; } + if (documentTitle !== null) { detailsState.documentTitle = documentTitle; } + /** @type {import('display').HistoryContent} */ + const detailsContent = { + contentOrigin: { + tabId: this._tabId, + frameId: this._frameId + } + }; + if (dictionaryEntries !== null) { + detailsContent.dictionaryEntries = dictionaryEntries; + } + /** @type {import('display').ContentDetails} */ const details = { focus, historyMode: 'clear', @@ -585,28 +730,22 @@ export class Frontend { query, wildcards: 'off' }, - state: { - focusEntry: 0, - optionsContext, - url, - sentence, - documentTitle - }, - content: { - dictionaryEntries, - contentOrigin: { - tabId: this._tabId, - frameId: this._frameId - } - } + state: detailsState, + content: detailsContent }; - if (textSource.type === 'element' && textSource.fullContent !== query) { + if (textSource instanceof TextSourceElement && textSource.fullContent !== query) { details.params.full = textSource.fullContent; details.params['full-visible'] = 'true'; } this._showPopupContent(textSource, optionsContext, details); } + /** + * @param {import('text-source').TextSource} textSource + * @param {?import('settings').OptionsContext} optionsContext + * @param {?import('display').ContentDetails} details + * @returns {Promise} + */ _showPopupContent(textSource, optionsContext, details) { const sourceRects = []; for (const {left, top, right, bottom} of textSource.getRects()) { @@ -631,6 +770,9 @@ export class Frontend { return this._lastShowPromise; } + /** + * @returns {void} + */ _updateTextScannerEnabled() { const enabled = (this._options !== null && this._options.general.enable && !this._disabledOverride); if (enabled === this._textScanner.isEnabled()) { return; } @@ -643,15 +785,18 @@ export class Frontend { } } + /** + * @returns {void} + */ _updateContentScale() { - const {popupScalingFactor, popupScaleRelativeToPageZoom, popupScaleRelativeToVisualViewport} = this._options.general; + const {popupScalingFactor, popupScaleRelativeToPageZoom, popupScaleRelativeToVisualViewport} = /** @type {import('settings').ProfileOptions} */ (this._options).general; let contentScale = popupScalingFactor; if (popupScaleRelativeToPageZoom) { contentScale /= this._pageZoomFactor; } if (popupScaleRelativeToVisualViewport) { - const visualViewport = window.visualViewport; - const visualViewportScale = (visualViewport !== null && typeof visualViewport === 'object' ? visualViewport.scale : 1.0); + const {visualViewport} = window; + const visualViewportScale = (typeof visualViewport !== 'undefined' && visualViewport !== null ? visualViewport.scale : 1.0); contentScale /= visualViewportScale; } if (contentScale === this._contentScale) { return; } @@ -663,6 +808,9 @@ export class Frontend { this._updatePopupPosition(); } + /** + * @returns {Promise} + */ async _updatePopupPosition() { const textSource = this._textScanner.getCurrentTextSource(); if ( @@ -674,7 +822,11 @@ export class Frontend { } } - _signalFrontendReady(targetFrameId=null) { + /** + * @param {?number} targetFrameId + */ + _signalFrontendReady(targetFrameId) { + /** @type {import('frontend').FrontendReadyDetails} */ const params = {frameId: this._frameId}; if (targetFrameId === null) { yomitan.api.broadcastTab('frontendReady', params); @@ -683,8 +835,14 @@ export class Frontend { } } + /** + * @param {number} frameId + * @param {?number} timeout + * @returns {Promise} + */ async _waitForFrontendReady(frameId, timeout) { return new Promise((resolve, reject) => { + /** @type {?number} */ let timeoutId = null; const cleanup = () => { @@ -694,10 +852,11 @@ export class Frontend { } chrome.runtime.onMessage.removeListener(onMessage); }; - const onMessage = (message, sender, sendResponse) => { + /** @type {import('extension').ChromeRuntimeOnMessageCallback} */ + const onMessage = (message, _sender, sendResponse) => { try { const {action, params} = message; - if (action === 'frontendReady' && params.frameId === frameId) { + if (action === 'frontendReady' && /** @type {import('frontend').FrontendReadyDetails} */ (params).frameId === frameId) { cleanup(); resolve(); sendResponse(); @@ -720,6 +879,10 @@ export class Frontend { }); } + /** + * @param {import('settings').PreventMiddleMouseOptions} preventMiddleMouseOptions + * @returns {boolean} + */ _getPreventMiddleMouseValueForPageType(preventMiddleMouseOptions) { switch (this._pageType) { case 'web': return preventMiddleMouseOptions.onWebPages; @@ -729,6 +892,9 @@ export class Frontend { } } + /** + * @returns {Promise} + */ async _getOptionsContext() { let optionsContext = this._optionsContextOverride; if (optionsContext === null) { @@ -737,10 +903,13 @@ export class Frontend { return optionsContext; } + /** + * @returns {Promise<{optionsContext: import('settings').OptionsContext, detail?: import('text-scanner').SearchResultDetail}>} + */ async _getSearchContext() { let url = window.location.href; let documentTitle = document.title; - if (this._useProxyPopup) { + if (this._useProxyPopup && this._parentFrameId !== null) { try { ({url, documentTitle} = await yomitan.crossFrame.invoke(this._parentFrameId, 'Frontend.getPageInfo', {})); } catch (e) { @@ -759,6 +928,10 @@ export class Frontend { }; } + /** + * @param {boolean} allowEmptyRange + * @returns {Promise} + */ async _scanSelectedText(allowEmptyRange) { const range = this._getFirstSelectionRange(allowEmptyRange); if (range === null) { return false; } @@ -767,8 +940,13 @@ export class Frontend { return true; } + /** + * @param {boolean} allowEmptyRange + * @returns {?Range} + */ _getFirstSelectionRange(allowEmptyRange) { const selection = window.getSelection(); + if (selection === null) { return null; } for (let i = 0, ii = selection.rangeCount; i < ii; ++i) { const range = selection.getRangeAt(i); if (range.toString().length > 0 || allowEmptyRange) { @@ -778,6 +956,9 @@ export class Frontend { return null; } + /** + * @returns {void} + */ _prepareSiteSpecific() { switch (location.hostname.toLowerCase()) { case 'docs.google.com': @@ -786,6 +967,9 @@ export class Frontend { } } + /** + * @returns {Promise} + */ async _prepareGoogleDocs() { if (typeof GoogleDocsUtil !== 'undefined') { return; } await yomitan.api.loadExtensionScripts([ -- cgit v1.2.3 From aabd761ee9064f6a46703f234e016f31f6441fa0 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 27 Nov 2023 13:36:04 -0500 Subject: Remove unneeded references --- ext/js/accessibility/accessibility-controller.js | 5 ++--- ext/js/accessibility/google-docs-util.js | 1 - ext/js/app/frontend.js | 6 ++---- ext/js/app/popup-proxy.js | 14 ++++++-------- ext/js/app/popup-window.js | 9 ++++----- ext/js/background/backend.js | 1 - ext/js/comm/anki-connect.js | 1 - ext/js/dom/document-util.js | 1 - ext/js/dom/text-source-element.js | 1 - ext/js/dom/text-source-range.js | 1 - ext/js/language/dictionary-worker-handler.js | 1 - ext/js/language/dictionary-worker-media-loader.js | 2 +- ext/js/language/translator.js | 1 - ext/js/templates/template-renderer-proxy.js | 2 +- 14 files changed, 16 insertions(+), 30 deletions(-) (limited to 'ext/js/app/frontend.js') diff --git a/ext/js/accessibility/accessibility-controller.js b/ext/js/accessibility/accessibility-controller.js index a4239947..8250b369 100644 --- a/ext/js/accessibility/accessibility-controller.js +++ b/ext/js/accessibility/accessibility-controller.js @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import {ScriptManager} from '../background/script-manager.js'; import {log} from '../core.js'; /** @@ -25,10 +24,10 @@ import {log} from '../core.js'; export class AccessibilityController { /** * Creates a new instance. - * @param {ScriptManager} scriptManager An instance of the `ScriptManager` class. + * @param {import('../background/script-manager.js').ScriptManager} scriptManager An instance of the `ScriptManager` class. */ constructor(scriptManager) { - /** @type {ScriptManager} */ + /** @type {import('../background/script-manager.js').ScriptManager} */ this._scriptManager = scriptManager; /** @type {?import('core').TokenObject} */ this._updateGoogleDocsAccessibilityToken = null; diff --git a/ext/js/accessibility/google-docs-util.js b/ext/js/accessibility/google-docs-util.js index 9db45cc1..4321f082 100644 --- a/ext/js/accessibility/google-docs-util.js +++ b/ext/js/accessibility/google-docs-util.js @@ -17,7 +17,6 @@ */ import {DocumentUtil} from '../dom/document-util.js'; -import {TextSourceElement} from '../dom/text-source-element.js'; import {TextSourceRange} from '../dom/text-source-range.js'; /** diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index fec933f8..628c504e 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -21,10 +21,8 @@ import {EventListenerCollection, invokeMessageHandler, log, promiseAnimationFram import {DocumentUtil} from '../dom/document-util.js'; import {TextSourceElement} from '../dom/text-source-element.js'; import {TextSourceRange} from '../dom/text-source-range.js'; -import {HotkeyHandler} from '../input/hotkey-handler.js'; import {TextScanner} from '../language/text-scanner.js'; import {yomitan} from '../yomitan.js'; -import {PopupFactory} from './popup-factory.js'; /** * This is the main class responsible for scanning and handling webpage content. @@ -50,7 +48,7 @@ export class Frontend { }) { /** @type {import('frontend').PageType} */ this._pageType = pageType; - /** @type {PopupFactory} */ + /** @type {import('./popup-factory.js').PopupFactory} */ this._popupFactory = popupFactory; /** @type {number} */ this._depth = depth; @@ -70,7 +68,7 @@ export class Frontend { this._allowRootFramePopupProxy = allowRootFramePopupProxy; /** @type {boolean} */ this._childrenSupported = childrenSupported; - /** @type {HotkeyHandler} */ + /** @type {import('../input/hotkey-handler.js').HotkeyHandler} */ this._hotkeyHandler = hotkeyHandler; /** @type {?import('popup').PopupAny} */ this._popup = null; diff --git a/ext/js/app/popup-proxy.js b/ext/js/app/popup-proxy.js index 9b5b0214..924175e2 100644 --- a/ext/js/app/popup-proxy.js +++ b/ext/js/app/popup-proxy.js @@ -16,10 +16,8 @@ * along with this program. If not, see . */ -import {FrameOffsetForwarder} from '../comm/frame-offset-forwarder.js'; import {EventDispatcher, log} from '../core.js'; import {yomitan} from '../yomitan.js'; -import {Popup} from './popup.js'; /** * This class is a proxy for a Popup that is hosted in a different frame. @@ -44,7 +42,7 @@ export class PopupProxy extends EventDispatcher { this._depth = depth; /** @type {number} */ this._frameId = frameId; - /** @type {?FrameOffsetForwarder} */ + /** @type {?import('../comm/frame-offset-forwarder.js').FrameOffsetForwarder} */ this._frameOffsetForwarder = frameOffsetForwarder; /** @type {number} */ @@ -70,7 +68,7 @@ export class PopupProxy extends EventDispatcher { /** * The parent of the popup, which is always `null` for `PopupProxy` instances, * since any potential parent popups are in a different frame. - * @type {?Popup} + * @type {?import('./popup.js').Popup} */ get parent() { return null; @@ -78,7 +76,7 @@ export class PopupProxy extends EventDispatcher { /** * Attempts to set the parent popup. - * @param {Popup} _value The parent to assign. + * @param {import('./popup.js').Popup} _value The parent to assign. * @throws {Error} Throws an error, since this class doesn't support a direct parent. */ set parent(_value) { @@ -88,7 +86,7 @@ export class PopupProxy extends EventDispatcher { /** * The popup child popup, which is always null for `PopupProxy` instances, * since any potential child popups are in a different frame. - * @type {?Popup} + * @type {?import('./popup.js').Popup} */ get child() { return null; @@ -96,7 +94,7 @@ export class PopupProxy extends EventDispatcher { /** * Attempts to set the child popup. - * @param {Popup} _child The child to assign. + * @param {import('./popup.js').Popup} _child The child to assign. * @throws {Error} Throws an error, since this class doesn't support children. */ set child(_child) { @@ -354,7 +352,7 @@ export class PopupProxy extends EventDispatcher { * @param {number} now */ async _updateFrameOffsetInner(now) { - this._frameOffsetPromise = /** @type {FrameOffsetForwarder} */ (this._frameOffsetForwarder).getOffset(); + this._frameOffsetPromise = /** @type {import('../comm/frame-offset-forwarder.js').FrameOffsetForwarder} */ (this._frameOffsetForwarder).getOffset(); try { const offset = await this._frameOffsetPromise; if (offset !== null) { diff --git a/ext/js/app/popup-window.js b/ext/js/app/popup-window.js index af1ac1e4..9a0f8011 100644 --- a/ext/js/app/popup-window.js +++ b/ext/js/app/popup-window.js @@ -18,7 +18,6 @@ import {EventDispatcher} from '../core.js'; import {yomitan} from '../yomitan.js'; -import {Popup} from './popup.js'; /** * This class represents a popup that is hosted in a new native window. @@ -54,7 +53,7 @@ export class PopupWindow extends EventDispatcher { } /** - * @type {?Popup} + * @type {?import('./popup.js').Popup} */ get parent() { return null; @@ -63,7 +62,7 @@ export class PopupWindow extends EventDispatcher { /** * The parent of the popup, which is always `null` for `PopupWindow` instances, * since any potential parent popups are in a different frame. - * @param {Popup} _value The parent to assign. + * @param {import('./popup.js').Popup} _value The parent to assign. * @throws {Error} Throws an error, since this class doesn't support children. */ set parent(_value) { @@ -73,7 +72,7 @@ export class PopupWindow extends EventDispatcher { /** * The popup child popup, which is always null for `PopupWindow` instances, * since any potential child popups are in a different frame. - * @type {?Popup} + * @type {?import('./popup.js').Popup} */ get child() { return null; @@ -81,7 +80,7 @@ export class PopupWindow extends EventDispatcher { /** * Attempts to set the child popup. - * @param {Popup} _value The child to assign. + * @param {import('./popup.js').Popup} _value The child to assign. * @throws Throws an error, since this class doesn't support children. */ set child(_value) { diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index a8683b3f..14877cf1 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -25,7 +25,6 @@ import {Mecab} from '../comm/mecab.js'; import {clone, deferPromise, generateId, invokeMessageHandler, isObject, log, promiseTimeout} from '../core.js'; import {ExtensionError} from '../core/extension-error.js'; import {AnkiUtil} from '../data/anki-util.js'; -import {JsonSchema} from '../data/json-schema.js'; import {OptionsUtil} from '../data/options-util.js'; import {PermissionsUtil} from '../data/permissions-util.js'; import {ArrayBufferUtil} from '../data/sandbox/array-buffer-util.js'; diff --git a/ext/js/comm/anki-connect.js b/ext/js/comm/anki-connect.js index 3262af41..b876703f 100644 --- a/ext/js/comm/anki-connect.js +++ b/ext/js/comm/anki-connect.js @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import {isObject} from '../core.js'; import {AnkiUtil} from '../data/anki-util.js'; /** diff --git a/ext/js/dom/document-util.js b/ext/js/dom/document-util.js index f53d55fd..cf58d39f 100644 --- a/ext/js/dom/document-util.js +++ b/ext/js/dom/document-util.js @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import {EventListenerCollection} from '../core.js'; import {DOMTextScanner} from './dom-text-scanner.js'; import {TextSourceElement} from './text-source-element.js'; import {TextSourceRange} from './text-source-range.js'; diff --git a/ext/js/dom/text-source-element.js b/ext/js/dom/text-source-element.js index fbe89a61..47c18e30 100644 --- a/ext/js/dom/text-source-element.js +++ b/ext/js/dom/text-source-element.js @@ -18,7 +18,6 @@ import {StringUtil} from '../data/sandbox/string-util.js'; import {DocumentUtil} from './document-util.js'; -import {TextSourceRange} from './text-source-range.js'; /** * This class represents a text source that is attached to a HTML element, such as an diff --git a/ext/js/dom/text-source-range.js b/ext/js/dom/text-source-range.js index 5c3d4184..5dbbd636 100644 --- a/ext/js/dom/text-source-range.js +++ b/ext/js/dom/text-source-range.js @@ -18,7 +18,6 @@ import {DocumentUtil} from './document-util.js'; import {DOMTextScanner} from './dom-text-scanner.js'; -import {TextSourceElement} from './text-source-element.js'; /** * This class represents a text source that comes from text nodes in the document. diff --git a/ext/js/language/dictionary-worker-handler.js b/ext/js/language/dictionary-worker-handler.js index 8ac342b2..3a85cb71 100644 --- a/ext/js/language/dictionary-worker-handler.js +++ b/ext/js/language/dictionary-worker-handler.js @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import {serializeError} from '../core.js'; import {DictionaryDatabase} from './dictionary-database.js'; import {DictionaryImporter} from './dictionary-importer.js'; import {DictionaryWorkerMediaLoader} from './dictionary-worker-media-loader.js'; diff --git a/ext/js/language/dictionary-worker-media-loader.js b/ext/js/language/dictionary-worker-media-loader.js index 9e3fd67e..2701389e 100644 --- a/ext/js/language/dictionary-worker-media-loader.js +++ b/ext/js/language/dictionary-worker-media-loader.js @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import {deserializeError, generateId} from '../core.js'; +import {generateId} from '../core.js'; /** * Class used for loading and validating media from a worker thread diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js index 9b01c1ff..67cc53c6 100644 --- a/ext/js/language/translator.js +++ b/ext/js/language/translator.js @@ -19,7 +19,6 @@ import {RegexUtil} from '../general/regex-util.js'; import {TextSourceMap} from '../general/text-source-map.js'; import {Deinflector} from './deinflector.js'; -import {DictionaryDatabase} from './dictionary-database.js'; /** * Class which finds term and kanji dictionary entries for text. diff --git a/ext/js/templates/template-renderer-proxy.js b/ext/js/templates/template-renderer-proxy.js index 6d019d14..e67b715a 100644 --- a/ext/js/templates/template-renderer-proxy.js +++ b/ext/js/templates/template-renderer-proxy.js @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import {deserializeError, generateId} from '../core.js'; +import {generateId} from '../core.js'; export class TemplateRendererProxy { constructor() { -- cgit v1.2.3 From 14d12f6ba20b837a04c638b935773f3120e194ff Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 27 Nov 2023 19:33:01 -0500 Subject: Update timer types and such --- dev/jsconfig.json | 2 +- ext/js/app/frontend.js | 11 +++++++++-- ext/js/background/backend.js | 4 ++-- ext/js/background/offscreen.js | 4 +++- ext/js/comm/api.js | 2 +- ext/js/comm/clipboard-monitor.js | 2 +- ext/js/comm/cross-frame-api.js | 21 +++++++++++++-------- ext/js/comm/frame-ancestry-handler.js | 2 +- ext/js/comm/frame-client.js | 2 +- ext/js/comm/mecab.js | 2 +- ext/js/core.js | 2 +- ext/js/display/display-audio.js | 2 +- ext/js/display/display-notification.js | 2 +- ext/js/display/display.js | 2 +- ext/js/display/element-overflow-controller.js | 8 ++++---- ext/js/display/option-toggle-hotkey-handler.js | 2 +- ext/js/display/search-display-controller.js | 2 +- ext/js/dom/document-util.js | 2 +- ext/js/dom/panel-element.js | 2 +- ext/js/language/text-scanner.js | 6 +++--- ext/js/media/audio-downloader.js | 2 +- ext/js/pages/settings/popup-preview-frame.js | 2 +- test/jsconfig.json | 2 +- types/ext/core.d.ts | 2 ++ types/ext/cross-frame-api.d.ts | 2 +- types/ext/offscreen.d.ts | 2 +- types/other/globals.d.ts | 22 ++++++++++++++++++++++ types/other/web-set-timeout.d.ts | 24 ------------------------ 28 files changed, 77 insertions(+), 63 deletions(-) create mode 100644 types/other/globals.d.ts delete mode 100644 types/other/web-set-timeout.d.ts (limited to 'ext/js/app/frontend.js') diff --git a/dev/jsconfig.json b/dev/jsconfig.json index 518f97ad..d4efe694 100644 --- a/dev/jsconfig.json +++ b/dev/jsconfig.json @@ -71,7 +71,7 @@ "../ext/js/language/translator.js", "../ext/js/media/media-util.js", "../types/dev/**/*.ts", - "../types/other/web-set-timeout.d.ts" + "../types/other/globals.d.ts" ], "exclude": [ "../node_modules" diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index 628c504e..e1f8d8c9 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -99,7 +99,7 @@ export class Frontend { this._popupEventListeners = new EventListenerCollection(); /** @type {?import('core').TokenObject} */ this._updatePopupToken = null; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._clearSelectionTimer = null; /** @type {boolean} */ this._isPointerOverPopup = false; @@ -840,7 +840,7 @@ export class Frontend { */ async _waitForFrontendReady(frameId, timeout) { return new Promise((resolve, reject) => { - /** @type {?number} */ + /** @type {?import('core').Timeout} */ let timeoutId = null; const cleanup = () => { @@ -973,6 +973,13 @@ export class Frontend { await yomitan.api.loadExtensionScripts([ '/js/accessibility/google-docs-util.js' ]); + this._prepareGoogleDocs2(); + } + + /** + * @returns {Promise} + */ + async _prepareGoogleDocs2() { if (typeof GoogleDocsUtil === 'undefined') { return; } DocumentUtil.registerGetRangeFromPointHandler(GoogleDocsUtil.getRangeFromPoint.bind(GoogleDocsUtil)); } diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 44f5a42d..f1a47e7f 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -138,7 +138,7 @@ export class Backend { /** @type {?string} */ this._defaultBrowserActionTitle = null; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._badgePrepareDelayTimer = null; /** @type {?import('log').LogLevel} */ this._logErrorLevel = null; @@ -1981,7 +1981,7 @@ export class Backend { */ _waitUntilTabFrameIsReady(tabId, frameId, timeout=null) { return new Promise((resolve, reject) => { - /** @type {?number} */ + /** @type {?import('core').Timeout} */ let timer = null; /** @type {?import('extension').ChromeRuntimeOnMessageCallback} */ let onMessage = (message, sender) => { diff --git a/ext/js/background/offscreen.js b/ext/js/background/offscreen.js index 45345c01..8da661ad 100644 --- a/ext/js/background/offscreen.js +++ b/ext/js/background/offscreen.js @@ -51,7 +51,7 @@ export class Offscreen { }); /** @type {import('offscreen').MessageHandlerMap} */ - this._messageHandlers = new Map([ + const messageHandlers = new Map([ ['clipboardGetTextOffscreen', {async: true, handler: this._getTextHandler.bind(this)}], ['clipboardGetImageOffscreen', {async: true, handler: this._getImageHandler.bind(this)}], ['clipboardSetBrowserOffscreen', {async: false, handler: this._setClipboardBrowser.bind(this)}], @@ -65,6 +65,8 @@ export class Offscreen { ['getTermFrequenciesOffscreen', {async: true, handler: this._getTermFrequenciesHandler.bind(this)}], ['clearDatabaseCachesOffscreen', {async: false, handler: this._clearDatabaseCachesHandler.bind(this)}] ]); + /** @type {import('offscreen').MessageHandlerMap} */ + this._messageHandlers = messageHandlers; const onMessage = this._onMessage.bind(this); chrome.runtime.onMessage.addListener(onMessage); diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index 0cfdba59..26218595 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -431,7 +431,7 @@ export class API { */ _createActionPort(timeout) { return new Promise((resolve, reject) => { - /** @type {?number} */ + /** @type {?import('core').Timeout} */ let timer = null; const portDetails = deferPromise(); diff --git a/ext/js/comm/clipboard-monitor.js b/ext/js/comm/clipboard-monitor.js index 06e95438..3b3a56a9 100644 --- a/ext/js/comm/clipboard-monitor.js +++ b/ext/js/comm/clipboard-monitor.js @@ -31,7 +31,7 @@ export class ClipboardMonitor extends EventDispatcher { this._japaneseUtil = japaneseUtil; /** @type {import('clipboard-monitor').ClipboardReaderLike} */ this._clipboardReader = clipboardReader; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._timerId = null; /** @type {?import('core').TokenObject} */ this._timerToken = null; diff --git a/ext/js/comm/cross-frame-api.js b/ext/js/comm/cross-frame-api.js index 34f3f36a..3ac38cf2 100644 --- a/ext/js/comm/cross-frame-api.js +++ b/ext/js/comm/cross-frame-api.js @@ -25,14 +25,14 @@ import {yomitan} from '../yomitan.js'; */ class CrossFrameAPIPort extends EventDispatcher { /** - * @param {?number} otherTabId + * @param {number} otherTabId * @param {number} otherFrameId * @param {chrome.runtime.Port} port * @param {import('core').MessageHandlerMap} messageHandlers */ constructor(otherTabId, otherFrameId, port, messageHandlers) { super(); - /** @type {?number} */ + /** @type {number} */ this._otherTabId = otherTabId; /** @type {number} */ this._otherFrameId = otherFrameId; @@ -48,7 +48,7 @@ class CrossFrameAPIPort extends EventDispatcher { this._eventListeners = new EventListenerCollection(); } - /** @type {?number} */ + /** @type {number} */ get otherTabId() { return this._otherTabId; } @@ -299,7 +299,7 @@ export class CrossFrameAPI { this._ackTimeout = 3000; // 3 seconds /** @type {number} */ this._responseTimeout = 10000; // 10 seconds - /** @type {Map>} */ + /** @type {Map>} */ this._commPorts = new Map(); /** @type {import('core').MessageHandlerMap} */ this._messageHandlers = new Map(); @@ -339,7 +339,12 @@ export class CrossFrameAPI { * @returns {Promise} */ async invokeTab(targetTabId, targetFrameId, action, params) { - if (typeof targetTabId !== 'number') { targetTabId = this._tabId; } + if (typeof targetTabId !== 'number') { + targetTabId = this._tabId; + if (typeof targetTabId !== 'number') { + throw new Error('Unknown target tab id for invocation'); + } + } const commPort = await this._getOrCreateCommPort(targetTabId, targetFrameId); return await commPort.invoke(action, params, this._ackTimeout, this._responseTimeout); } @@ -405,7 +410,7 @@ export class CrossFrameAPI { } /** - * @param {?number} otherTabId + * @param {number} otherTabId * @param {number} otherFrameId * @returns {Promise} */ @@ -420,7 +425,7 @@ export class CrossFrameAPI { return await this._createCommPort(otherTabId, otherFrameId); } /** - * @param {?number} otherTabId + * @param {number} otherTabId * @param {number} otherFrameId * @returns {Promise} */ @@ -438,7 +443,7 @@ export class CrossFrameAPI { } /** - * @param {?number} otherTabId + * @param {number} otherTabId * @param {number} otherFrameId * @param {chrome.runtime.Port} port * @returns {CrossFrameAPIPort} diff --git a/ext/js/comm/frame-ancestry-handler.js b/ext/js/comm/frame-ancestry-handler.js index 49c96c22..687ec368 100644 --- a/ext/js/comm/frame-ancestry-handler.js +++ b/ext/js/comm/frame-ancestry-handler.js @@ -122,7 +122,7 @@ export class FrameAncestryHandler { const responseMessageId = `${this._responseMessageIdBase}${uniqueId}`; /** @type {number[]} */ const results = []; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ let timer = null; const cleanup = () => { diff --git a/ext/js/comm/frame-client.js b/ext/js/comm/frame-client.js index 1519cf7f..8aa8c6d6 100644 --- a/ext/js/comm/frame-client.js +++ b/ext/js/comm/frame-client.js @@ -83,7 +83,7 @@ export class FrameClient { _connectInternal(frame, targetOrigin, hostFrameId, setupFrame, timeout) { return new Promise((resolve, reject) => { const tokenMap = new Map(); - /** @type {?number} */ + /** @type {?import('core').Timeout} */ let timer = null; const deferPromiseDetails = /** @type {import('core').DeferredPromiseDetails} */ (deferPromise()); const frameLoadedPromise = deferPromiseDetails.promise; diff --git a/ext/js/comm/mecab.js b/ext/js/comm/mecab.js index 072b42f3..0a87463b 100644 --- a/ext/js/comm/mecab.js +++ b/ext/js/comm/mecab.js @@ -31,7 +31,7 @@ export class Mecab { this._port = null; /** @type {number} */ this._sequence = 0; - /** @type {Map void, reject: (reason?: unknown) => void, timer: number}>} */ + /** @type {Map void, reject: (reason?: unknown) => void, timer: import('core').Timeout}>} */ this._invocations = new Map(); /** @type {EventListenerCollection} */ this._eventListeners = new EventListenerCollection(); diff --git a/ext/js/core.js b/ext/js/core.js index cdac2020..a53d5572 100644 --- a/ext/js/core.js +++ b/ext/js/core.js @@ -307,7 +307,7 @@ export function promiseAnimationFrame(timeout) { return; } - /** @type {?number} */ + /** @type {?import('core').Timeout} */ let timer = null; /** @type {?number} */ let frameRequest = null; diff --git a/ext/js/display/display-audio.js b/ext/js/display/display-audio.js index 3fbfc3c8..3576decb 100644 --- a/ext/js/display/display-audio.js +++ b/ext/js/display/display-audio.js @@ -36,7 +36,7 @@ export class DisplayAudio { this._playbackVolume = 1.0; /** @type {boolean} */ this._autoPlay = false; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._autoPlayAudioTimer = null; /** @type {number} */ this._autoPlayAudioDelay = 400; diff --git a/ext/js/display/display-notification.js b/ext/js/display/display-notification.js index b3f20700..d1cfa537 100644 --- a/ext/js/display/display-notification.js +++ b/ext/js/display/display-notification.js @@ -34,7 +34,7 @@ export class DisplayNotification { this._closeButton = /** @type {HTMLElement} */ (node.querySelector('.footer-notification-close-button')); /** @type {EventListenerCollection} */ this._eventListeners = new EventListenerCollection(); - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._closeTimer = null; } diff --git a/ext/js/display/display.js b/ext/js/display/display.js index f14291d1..6e1450c3 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -117,7 +117,7 @@ export class Display extends EventDispatcher { this._queryOffset = 0; /** @type {HTMLElement} */ this._progressIndicator = /** @type {HTMLElement} */ (document.querySelector('#progress-indicator')); - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._progressIndicatorTimer = null; /** @type {DynamicProperty} */ this._progressIndicatorVisible = new DynamicProperty(false); diff --git a/ext/js/display/element-overflow-controller.js b/ext/js/display/element-overflow-controller.js index 1d2c808f..35277fa5 100644 --- a/ext/js/display/element-overflow-controller.js +++ b/ext/js/display/element-overflow-controller.js @@ -22,7 +22,7 @@ export class ElementOverflowController { constructor() { /** @type {Element[]} */ this._elements = []; - /** @type {?number} */ + /** @type {?(number|import('core').Timeout)} */ this._checkTimer = null; /** @type {EventListenerCollection} */ this._eventListeners = new EventListenerCollection(); @@ -154,7 +154,7 @@ export class ElementOverflowController { /** * @param {() => void} callback * @param {number} timeout - * @returns {number} + * @returns {number|import('core').Timeout} */ _requestIdleCallback(callback, timeout) { if (typeof requestIdleCallback === 'function') { @@ -165,11 +165,11 @@ export class ElementOverflowController { } /** - * @param {number} handle + * @param {number|import('core').Timeout} handle */ _cancelIdleCallback(handle) { if (typeof cancelIdleCallback === 'function') { - cancelIdleCallback(handle); + cancelIdleCallback(/** @type {number} */ (handle)); } else { clearTimeout(handle); } diff --git a/ext/js/display/option-toggle-hotkey-handler.js b/ext/js/display/option-toggle-hotkey-handler.js index 531e208d..edd7de5b 100644 --- a/ext/js/display/option-toggle-hotkey-handler.js +++ b/ext/js/display/option-toggle-hotkey-handler.js @@ -29,7 +29,7 @@ export class OptionToggleHotkeyHandler { this._display = display; /** @type {?import('./display-notification.js').DisplayNotification} */ this._notification = null; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._notificationHideTimer = null; /** @type {number} */ this._notificationHideTimeout = 5000; diff --git a/ext/js/display/search-display-controller.js b/ext/js/display/search-display-controller.js index b93757c2..98a4666b 100644 --- a/ext/js/display/search-display-controller.js +++ b/ext/js/display/search-display-controller.js @@ -63,7 +63,7 @@ export class SearchDisplayController { this._wanakanaBound = false; /** @type {boolean} */ this._introVisible = true; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._introAnimationTimer = null; /** @type {boolean} */ this._clipboardMonitorEnabled = false; diff --git a/ext/js/dom/document-util.js b/ext/js/dom/document-util.js index cf58d39f..549a8195 100644 --- a/ext/js/dom/document-util.js +++ b/ext/js/dom/document-util.js @@ -360,7 +360,7 @@ export class DocumentUtil { /** * Adds a fullscreen change event listener. This function handles all of the browser-specific variants. * @param {EventListener} onFullscreenChanged The event callback. - * @param {?EventListenerCollection} eventListenerCollection An optional `EventListenerCollection` to add the registration to. + * @param {?import('../core.js').EventListenerCollection} eventListenerCollection An optional `EventListenerCollection` to add the registration to. */ static addFullscreenChangeEventListener(onFullscreenChanged, eventListenerCollection=null) { const target = document; diff --git a/ext/js/dom/panel-element.js b/ext/js/dom/panel-element.js index 314eb2fd..748c3a36 100644 --- a/ext/js/dom/panel-element.js +++ b/ext/js/dom/panel-element.js @@ -37,7 +37,7 @@ export class PanelElement extends EventDispatcher { this._mutationObserver = null; /** @type {boolean} */ this._visible = false; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._closeTimer = null; } diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index f6bcde8d..d1b033e6 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -120,7 +120,7 @@ export class TextScanner extends EventDispatcher { /** @type {boolean} */ this._preventNextClickScan = false; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._preventNextClickScanTimer = null; /** @type {number} */ this._preventNextClickScanTimerDuration = 50; @@ -145,7 +145,7 @@ export class TextScanner extends EventDispatcher { /** @type {boolean} */ this._canClearSelection = true; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._textSelectionTimer = null; /** @type {boolean} */ this._yomitanIsChangingTextSelectionNow = false; @@ -995,7 +995,7 @@ export class TextScanner extends EventDispatcher { async _scanTimerWait() { const delay = this._delay; const promise = /** @type {Promise} */ (new Promise((resolve) => { - /** @type {?number} */ + /** @type {?import('core').Timeout} */ let timeout = setTimeout(() => { timeout = null; resolve(true); diff --git a/ext/js/media/audio-downloader.js b/ext/js/media/audio-downloader.js index 0847d479..e041cc67 100644 --- a/ext/js/media/audio-downloader.js +++ b/ext/js/media/audio-downloader.js @@ -316,7 +316,7 @@ export class AudioDownloader { let signal; /** @type {?import('request-builder.js').ProgressCallback} */ let onProgress = null; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ let idleTimer = null; if (typeof idleTimeout === 'number') { const abortController = new AbortController(); diff --git a/ext/js/pages/settings/popup-preview-frame.js b/ext/js/pages/settings/popup-preview-frame.js index bb00829f..dab21f4d 100644 --- a/ext/js/pages/settings/popup-preview-frame.js +++ b/ext/js/pages/settings/popup-preview-frame.js @@ -43,7 +43,7 @@ export class PopupPreviewFrame { this._apiOptionsGetOld = null; /** @type {boolean} */ this._popupShown = false; - /** @type {?number} */ + /** @type {?import('core').Timeout} */ this._themeChangeTimeout = null; /** @type {?import('text-source').TextSource} */ this._textSource = null; diff --git a/test/jsconfig.json b/test/jsconfig.json index 261ec345..934aab81 100644 --- a/test/jsconfig.json +++ b/test/jsconfig.json @@ -31,7 +31,7 @@ "../ext/**/*.js", "../types/ext/**/*.ts", "../types/dev/**/*.ts", - "../types/other/web-set-timeout.d.ts" + "../types/other/globals.d.ts" ], "exclude": [ "../node_modules", diff --git a/types/ext/core.d.ts b/types/ext/core.d.ts index ce3a09f0..b83e6a74 100644 --- a/types/ext/core.d.ts +++ b/types/ext/core.d.ts @@ -100,3 +100,5 @@ export type MessageHandlerDetails = { export type MessageHandlerMap = Map; export type MessageHandlerArray = [key: string, handlerDetails: MessageHandlerDetails][]; + +export type Timeout = number | NodeJS.Timeout; diff --git a/types/ext/cross-frame-api.d.ts b/types/ext/cross-frame-api.d.ts index 5cedbec9..88ce59a7 100644 --- a/types/ext/cross-frame-api.d.ts +++ b/types/ext/cross-frame-api.d.ts @@ -50,5 +50,5 @@ export type Invocation = { responseTimeout: number; action: string; ack: boolean; - timer: number | null; + timer: Core.Timeout | null; }; diff --git a/types/ext/offscreen.d.ts b/types/ext/offscreen.d.ts index 7dd64d1e..ddf7eadc 100644 --- a/types/ext/offscreen.d.ts +++ b/types/ext/offscreen.d.ts @@ -112,4 +112,4 @@ export type MessageHandler< details: MessageDetailsMap[TMessage], ) => (TIsAsync extends true ? Promise> : MessageReturn); -export type MessageHandlerMap = Map; +export type MessageHandlerMap = Map; diff --git a/types/other/globals.d.ts b/types/other/globals.d.ts new file mode 100644 index 00000000..330f16c2 --- /dev/null +++ b/types/other/globals.d.ts @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 Yomitan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +declare global { + function clearTimeout(timeoutId: NodeJS.Timeout | string | number | undefined): void; +} + +export {}; diff --git a/types/other/web-set-timeout.d.ts b/types/other/web-set-timeout.d.ts deleted file mode 100644 index 98e40dab..00000000 --- a/types/other/web-set-timeout.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2023 Yomitan Authors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -declare module 'web-set-timeout' { - global { - // These types are used to ensure that setTimeout returns a number - function setTimeout(callback: (...args: TArgs) => void, ms?: number, ...args: TArgs): number; - function setTimeout(callback: (args: void) => void, ms?: number): number; - } -} -- cgit v1.2.3