aboutsummaryrefslogtreecommitdiff
path: root/ext/js
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2023-11-27 14:55:27 -0500
committertoasted-nutbread <toasted-nutbread@users.noreply.github.com>2023-11-27 14:55:27 -0500
commit7aed9a371b0d74c0d75179a08068e8935b76d780 (patch)
tree2d895b20e31cb03c8200e4429d987c6d253b1da7 /ext/js
parent5dc16745468c229e7c31f6cddaad83fb9c36b98f (diff)
Update types
Diffstat (limited to 'ext/js')
-rw-r--r--ext/js/background/backend.js22
-rw-r--r--ext/js/background/offscreen-proxy.js27
-rw-r--r--ext/js/background/offscreen.js9
-rw-r--r--ext/js/background/request-builder.js13
-rw-r--r--ext/js/comm/api.js6
-rw-r--r--ext/js/comm/clipboard-reader.js4
-rw-r--r--ext/js/display/search-action-popup-controller.js4
-rw-r--r--ext/js/dom/sandbox/css-style-applier.js2
-rw-r--r--ext/js/dom/text-source-element.js2
-rw-r--r--ext/js/dom/text-source-range.js2
-rw-r--r--ext/js/general/regex-util.js2
-rw-r--r--ext/js/language/__mocks__/dictionary-importer-media-loader.js1
-rw-r--r--ext/js/language/dictionary-importer.js2
-rw-r--r--ext/js/language/dictionary-worker.js2
-rw-r--r--ext/js/language/sandbox/japanese-util.js8
-rw-r--r--ext/js/language/text-scanner.js1
-rw-r--r--ext/js/language/translator.js4
-rw-r--r--ext/js/media/audio-downloader.js6
-rw-r--r--ext/js/pages/settings/backup-controller.js54
-rw-r--r--ext/js/pages/settings/recommended-permissions-controller.js36
20 files changed, 125 insertions, 82 deletions
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js
index 14877cf1..be68ecf4 100644
--- a/ext/js/background/backend.js
+++ b/ext/js/background/backend.js
@@ -96,7 +96,7 @@ export class Backend {
});
/** @type {?import('settings').Options} */
this._options = null;
- /** @type {JsonSchema[]} */
+ /** @type {import('../data/json-schema.js').JsonSchema[]} */
this._profileConditionsSchemaCache = [];
/** @type {ProfileConditionsUtil} */
this._profileConditionsUtil = new ProfileConditionsUtil();
@@ -665,7 +665,7 @@ export class Backend {
async _onApiInjectStylesheet({type, value}, sender) {
const {frameId, tab} = sender;
if (typeof tab !== 'object' || tab === null || typeof tab.id !== 'number') { throw new Error('Invalid tab'); }
- return await this._scriptManager.injectStylesheet(type, value, tab.id, frameId, false, true, 'document_start');
+ return await this._scriptManager.injectStylesheet(type, value, tab.id, frameId, false);
}
/** @type {import('api').Handler<import('api').GetStylesheetContentDetails, import('api').GetStylesheetContentResult>} */
@@ -895,13 +895,7 @@ export class Backend {
}
}
- /**
- *
- * @param root0
- * @param root0.targetTabId
- * @param root0.targetFrameId
- * @param sender
- */
+ /** @type {import('api').Handler<import('api').OpenCrossFramePortDetails, import('api').OpenCrossFramePortResult, true>} */
_onApiOpenCrossFramePort({targetTabId, targetFrameId}, sender) {
const sourceTabId = (sender && sender.tab ? sender.tab.id : null);
if (typeof sourceTabId !== 'number') {
@@ -922,7 +916,9 @@ export class Backend {
otherTabId: sourceTabId,
otherFrameId: sourceFrameId
};
+ /** @type {?chrome.runtime.Port} */
let sourcePort = chrome.tabs.connect(sourceTabId, {frameId: sourceFrameId, name: JSON.stringify(sourceDetails)});
+ /** @type {?chrome.runtime.Port} */
let targetPort = chrome.tabs.connect(targetTabId, {frameId: targetFrameId, name: JSON.stringify(targetDetails)});
const cleanup = () => {
@@ -937,8 +933,12 @@ export class Backend {
}
};
- sourcePort.onMessage.addListener((message) => { targetPort.postMessage(message); });
- targetPort.onMessage.addListener((message) => { sourcePort.postMessage(message); });
+ sourcePort.onMessage.addListener((message) => {
+ if (targetPort !== null) { targetPort.postMessage(message); }
+ });
+ targetPort.onMessage.addListener((message) => {
+ if (sourcePort !== null) { sourcePort.postMessage(message); }
+ });
sourcePort.onDisconnect.addListener(cleanup);
targetPort.onDisconnect.addListener(cleanup);
diff --git a/ext/js/background/offscreen-proxy.js b/ext/js/background/offscreen-proxy.js
index c01f523d..0fb2f269 100644
--- a/ext/js/background/offscreen-proxy.js
+++ b/ext/js/background/offscreen-proxy.js
@@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-import {deserializeError, isObject} from '../core.js';
+import {isObject} from '../core.js';
import {ArrayBufferUtil} from '../data/sandbox/array-buffer-util.js';
export class OffscreenProxy {
@@ -158,15 +158,36 @@ export class TranslatorProxy {
}
export class ClipboardReaderProxy {
+ /**
+ * @param {OffscreenProxy} offscreen
+ */
constructor(offscreen) {
+ /** @type {?import('environment').Browser} */
+ this._browser = null;
+ /** @type {OffscreenProxy} */
this._offscreen = offscreen;
}
+ /** @type {?import('environment').Browser} */
+ get browser() { return this._browser; }
+ set browser(value) {
+ if (this._browser === value) { return; }
+ this._browser = value;
+ this._offscreen.sendMessagePromise({action: 'clipboardSetBrowserOffsecreen', params: {value}});
+ }
+
+ /**
+ * @param {boolean} useRichText
+ * @returns {Promise<string>}
+ */
async getText(useRichText) {
- return this._offscreen.sendMessagePromise({action: 'clipboardGetTextOffscreen', params: {useRichText}});
+ return await this._offscreen.sendMessagePromise({action: 'clipboardGetTextOffscreen', params: {useRichText}});
}
+ /**
+ * @returns {Promise<?string>}
+ */
async getImage() {
- return this._offscreen.sendMessagePromise({action: 'clipboardGetImageOffscreen'});
+ return await this._offscreen.sendMessagePromise({action: 'clipboardGetImageOffscreen'});
}
}
diff --git a/ext/js/background/offscreen.js b/ext/js/background/offscreen.js
index 27cee8c4..6302aa84 100644
--- a/ext/js/background/offscreen.js
+++ b/ext/js/background/offscreen.js
@@ -50,6 +50,7 @@ export class Offscreen {
this._messageHandlers = new Map([
['clipboardGetTextOffscreen', {async: true, contentScript: true, handler: this._getTextHandler.bind(this)}],
['clipboardGetImageOffscreen', {async: true, contentScript: true, handler: this._getImageHandler.bind(this)}],
+ ['clipboardSetBrowserOffsecreen', {async: false, contentScript: true, handler: this._setClipboardBrowser.bind(this)}],
['databasePrepareOffscreen', {async: true, contentScript: true, handler: this._prepareDatabaseHandler.bind(this)}],
['getDictionaryInfoOffscreen', {async: true, contentScript: true, handler: this._getDictionaryInfoHandler.bind(this)}],
['databasePurgeOffscreen', {async: true, contentScript: true, handler: this._purgeDatabaseHandler.bind(this)}],
@@ -59,7 +60,6 @@ export class Offscreen {
['findTermsOffscreen', {async: true, contentScript: true, handler: this._findTermsHandler.bind(this)}],
['getTermFrequenciesOffscreen', {async: true, contentScript: true, handler: this._getTermFrequenciesHandler.bind(this)}],
['clearDatabaseCachesOffscreen', {async: false, contentScript: true, handler: this._clearDatabaseCachesHandler.bind(this)}]
-
]);
const onMessage = this._onMessage.bind(this);
@@ -76,6 +76,13 @@ export class Offscreen {
return this._clipboardReader.getImage();
}
+ /**
+ * @param {{value: import('environment').Browser}} details
+ */
+ _setClipboardBrowser({value}) {
+ this._clipboardReader.browser = value;
+ }
+
_prepareDatabaseHandler() {
if (this._prepareDatabasePromise !== null) {
return this._prepareDatabasePromise;
diff --git a/ext/js/background/request-builder.js b/ext/js/background/request-builder.js
index 48fe2dd9..5ae7fbf5 100644
--- a/ext/js/background/request-builder.js
+++ b/ext/js/background/request-builder.js
@@ -22,12 +22,6 @@
*/
export class RequestBuilder {
/**
- * A progress callback for a fetch read.
- * @callback ProgressCallback
- * @param {boolean} complete Whether or not the data has been completely read.
- */
-
- /**
* Creates a new instance.
*/
constructor() {
@@ -109,14 +103,17 @@ export class RequestBuilder {
/**
* Reads the array buffer body of a fetch response, with an optional `onProgress` callback.
* @param {Response} response The response of a `fetch` call.
- * @param {ProgressCallback} onProgress The progress callback
+ * @param {?import('request-builder.js').ProgressCallback} onProgress The progress callback
* @returns {Promise<Uint8Array>} The resulting binary data.
*/
static async readFetchResponseArrayBuffer(response, onProgress) {
let reader;
try {
if (typeof onProgress === 'function') {
- reader = response.body.getReader();
+ const {body} = response;
+ if (body !== null) {
+ reader = body.getReader();
+ }
}
} catch (e) {
// Not supported
diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js
index 62dc98b1..0cfdba59 100644
--- a/ext/js/comm/api.js
+++ b/ext/js/comm/api.js
@@ -415,9 +415,9 @@ export class API {
}
/**
- *
- * @param targetTabId
- * @param targetFrameId
+ * @param {import('api').OpenCrossFramePortDetails['targetTabId']} targetTabId
+ * @param {import('api').OpenCrossFramePortDetails['targetFrameId']} targetFrameId
+ * @returns {Promise<import('api').OpenCrossFramePortResult>}
*/
openCrossFramePort(targetTabId, targetFrameId) {
return this._invoke('openCrossFramePort', {targetTabId, targetFrameId});
diff --git a/ext/js/comm/clipboard-reader.js b/ext/js/comm/clipboard-reader.js
index c7b45a7c..364e31a3 100644
--- a/ext/js/comm/clipboard-reader.js
+++ b/ext/js/comm/clipboard-reader.js
@@ -29,7 +29,7 @@ export class ClipboardReader {
constructor({document=null, pasteTargetSelector=null, richContentPasteTargetSelector=null}) {
/** @type {?Document} */
this._document = document;
- /** @type {?string} */
+ /** @type {?import('environment').Browser} */
this._browser = null;
/** @type {?HTMLTextAreaElement} */
this._pasteTarget = null;
@@ -43,7 +43,7 @@ export class ClipboardReader {
/**
* Gets the browser being used.
- * @type {?string}
+ * @type {?import('environment').Browser}
*/
get browser() {
return this._browser;
diff --git a/ext/js/display/search-action-popup-controller.js b/ext/js/display/search-action-popup-controller.js
index 733fd70a..3a2057a1 100644
--- a/ext/js/display/search-action-popup-controller.js
+++ b/ext/js/display/search-action-popup-controller.js
@@ -18,10 +18,10 @@
export class SearchActionPopupController {
/**
- * @param {SearchPersistentStateController} searchPersistentStateController
+ * @param {import('./search-persistent-state-controller.js').SearchPersistentStateController} searchPersistentStateController
*/
constructor(searchPersistentStateController) {
- /** @type {SearchPersistentStateController} */
+ /** @type {import('./search-persistent-state-controller.js').SearchPersistentStateController} */
this._searchPersistentStateController = searchPersistentStateController;
}
diff --git a/ext/js/dom/sandbox/css-style-applier.js b/ext/js/dom/sandbox/css-style-applier.js
index 332ca4f2..ea36a02d 100644
--- a/ext/js/dom/sandbox/css-style-applier.js
+++ b/ext/js/dom/sandbox/css-style-applier.js
@@ -24,7 +24,7 @@ export class CssStyleApplier {
/**
* Creates a new instance of the class.
* @param {string} styleDataUrl The local URL to the JSON file continaing the style rules.
- * The style rules should follow the format of {@link CssStyleApplierRawStyleData}.
+ * The style rules should follow the format of `CssStyleApplierRawStyleData`.
*/
constructor(styleDataUrl) {
/** @type {string} */
diff --git a/ext/js/dom/text-source-element.js b/ext/js/dom/text-source-element.js
index 47c18e30..40ff5cc9 100644
--- a/ext/js/dom/text-source-element.js
+++ b/ext/js/dom/text-source-element.js
@@ -173,7 +173,7 @@ export class TextSourceElement {
/**
* Checks whether another text source has the same starting point.
- * @param {TextSourceElement|TextSourceRange} other The other source to test.
+ * @param {import('text-source').TextSource} other The other source to test.
* @returns {boolean} `true` if the starting points are equivalent, `false` otherwise.
*/
hasSameStart(other) {
diff --git a/ext/js/dom/text-source-range.js b/ext/js/dom/text-source-range.js
index 5dbbd636..fd09fdda 100644
--- a/ext/js/dom/text-source-range.js
+++ b/ext/js/dom/text-source-range.js
@@ -206,7 +206,7 @@ export class TextSourceRange {
/**
* Checks whether another text source has the same starting point.
- * @param {TextSourceElement|TextSourceRange} other The other source to test.
+ * @param {import('text-source').TextSource} other The other source to test.
* @returns {boolean} `true` if the starting points are equivalent, `false` otherwise.
* @throws {Error} An exception can be thrown if `Range.compareBoundaryPoints` fails,
* which shouldn't happen, but the handler is kept in case of unexpected errors.
diff --git a/ext/js/general/regex-util.js b/ext/js/general/regex-util.js
index 726ce9f2..62248968 100644
--- a/ext/js/general/regex-util.js
+++ b/ext/js/general/regex-util.js
@@ -25,7 +25,7 @@ export class RegexUtil {
* Applies string.replace using a regular expression and replacement string as arguments.
* A source map of the changes is also maintained.
* @param {string} text A string of the text to replace.
- * @param {TextSourceMap} sourceMap An instance of `TextSourceMap` which corresponds to `text`.
+ * @param {import('./text-source-map.js').TextSourceMap} sourceMap An instance of `TextSourceMap` which corresponds to `text`.
* @param {RegExp} pattern A regular expression to use as the replacement.
* @param {string} replacement A replacement string that follows the format of the standard
* JavaScript regular expression replacement string.
diff --git a/ext/js/language/__mocks__/dictionary-importer-media-loader.js b/ext/js/language/__mocks__/dictionary-importer-media-loader.js
index 96f0f6dd..ffda29b3 100644
--- a/ext/js/language/__mocks__/dictionary-importer-media-loader.js
+++ b/ext/js/language/__mocks__/dictionary-importer-media-loader.js
@@ -17,6 +17,7 @@
*/
export class DictionaryImporterMediaLoader {
+ /** @type {import('dictionary-importer-media-loader').GetImageDetailsFunction} */
async getImageDetails(content) {
// Placeholder values
return {content, width: 100, height: 100};
diff --git a/ext/js/language/dictionary-importer.js b/ext/js/language/dictionary-importer.js
index aa6d7ae6..2a2f4063 100644
--- a/ext/js/language/dictionary-importer.js
+++ b/ext/js/language/dictionary-importer.js
@@ -36,7 +36,7 @@ export class DictionaryImporter {
}
/**
- * @param {DictionaryDatabase} dictionaryDatabase
+ * @param {import('./dictionary-database.js').DictionaryDatabase} dictionaryDatabase
* @param {ArrayBuffer} archiveContent
* @param {import('dictionary-importer').ImportDetails} details
* @returns {Promise<import('dictionary-importer').ImportResult>}
diff --git a/ext/js/language/dictionary-worker.js b/ext/js/language/dictionary-worker.js
index 3e78a6ff..3119dd7b 100644
--- a/ext/js/language/dictionary-worker.js
+++ b/ext/js/language/dictionary-worker.js
@@ -157,6 +157,8 @@ export class DictionaryWorker {
resolve(result2);
} else {
// If formatResult is not provided, the response is assumed to be the same type
+ // For some reason, eslint thinks the TResponse type is undefined
+ // eslint-disable-next-line jsdoc/no-undefined-types
resolve(/** @type {TResponse} */ (/** @type {unknown} */ (result)));
}
}
diff --git a/ext/js/language/sandbox/japanese-util.js b/ext/js/language/sandbox/japanese-util.js
index f7f20b3b..4c9c46bd 100644
--- a/ext/js/language/sandbox/japanese-util.js
+++ b/ext/js/language/sandbox/japanese-util.js
@@ -466,7 +466,7 @@ export class JapaneseUtil {
/**
* @param {string} text
- * @param {?TextSourceMap} [sourceMap]
+ * @param {?import('../../general/text-source-map.js').TextSourceMap} [sourceMap]
* @returns {string}
*/
convertHalfWidthKanaToFullWidth(text, sourceMap=null) {
@@ -513,7 +513,7 @@ export class JapaneseUtil {
/**
* @param {string} text
- * @param {?TextSourceMap} sourceMap
+ * @param {?import('../../general/text-source-map.js').TextSourceMap} sourceMap
* @returns {string}
*/
convertAlphabeticToKana(text, sourceMap=null) {
@@ -676,7 +676,7 @@ export class JapaneseUtil {
/**
* @param {string} text
* @param {boolean} fullCollapse
- * @param {?TextSourceMap} [sourceMap]
+ * @param {?import('../../general/text-source-map.js').TextSourceMap} [sourceMap]
* @returns {string}
*/
collapseEmphaticSequences(text, fullCollapse, sourceMap=null) {
@@ -816,7 +816,7 @@ export class JapaneseUtil {
/**
* @param {string} text
- * @param {?TextSourceMap} sourceMap
+ * @param {?import('../../general/text-source-map.js').TextSourceMap} sourceMap
* @param {number} sourceMapStart
* @returns {string}
*/
diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js
index b4d9a642..f6bcde8d 100644
--- a/ext/js/language/text-scanner.js
+++ b/ext/js/language/text-scanner.js
@@ -18,6 +18,7 @@
import {EventDispatcher, EventListenerCollection, clone, log} from '../core.js';
import {DocumentUtil} from '../dom/document-util.js';
+import {TextSourceElement} from '../dom/text-source-element.js';
import {yomitan} from '../yomitan.js';
/**
diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js
index 67cc53c6..c21b16b1 100644
--- a/ext/js/language/translator.js
+++ b/ext/js/language/translator.js
@@ -29,9 +29,9 @@ export class Translator {
* @param {import('translator').ConstructorDetails} details The details for the class.
*/
constructor({japaneseUtil, database}) {
- /** @type {JapaneseUtil} */
+ /** @type {import('./sandbox/japanese-util.js').JapaneseUtil} */
this._japaneseUtil = japaneseUtil;
- /** @type {DictionaryDatabase} */
+ /** @type {import('./dictionary-database.js').DictionaryDatabase} */
this._database = database;
/** @type {?Deinflector} */
this._deinflector = null;
diff --git a/ext/js/media/audio-downloader.js b/ext/js/media/audio-downloader.js
index 7b236790..0847d479 100644
--- a/ext/js/media/audio-downloader.js
+++ b/ext/js/media/audio-downloader.js
@@ -25,10 +25,10 @@ import {SimpleDOMParser} from '../dom/simple-dom-parser.js';
export class AudioDownloader {
/**
- * @param {{japaneseUtil: JapaneseUtil, requestBuilder: RequestBuilder}} details
+ * @param {{japaneseUtil: import('../language/sandbox/japanese-util.js').JapaneseUtil, requestBuilder: RequestBuilder}} details
*/
constructor({japaneseUtil, requestBuilder}) {
- /** @type {JapaneseUtil} */
+ /** @type {import('../language/sandbox/japanese-util.js').JapaneseUtil} */
this._japaneseUtil = japaneseUtil;
/** @type {RequestBuilder} */
this._requestBuilder = requestBuilder;
@@ -314,7 +314,7 @@ export class AudioDownloader {
*/
async _downloadAudioFromUrl(url, sourceType, idleTimeout) {
let signal;
- /** @type {?(done: boolean) => void} */
+ /** @type {?import('request-builder.js').ProgressCallback} */
let onProgress = null;
/** @type {?number} */
let idleTimer = null;
diff --git a/ext/js/pages/settings/backup-controller.js b/ext/js/pages/settings/backup-controller.js
index 50a50b1a..52c5f418 100644
--- a/ext/js/pages/settings/backup-controller.js
+++ b/ext/js/pages/settings/backup-controller.js
@@ -534,12 +534,11 @@ export class BackupController {
// Exporting Dictionaries Database
/**
- *
- * @param message
- * @param isWarning
+ * @param {string} message
+ * @param {boolean} [isWarning]
*/
_databaseExportImportErrorMessage(message, isWarning=false) {
- const errorMessageContainer = document.querySelector('#db-ops-error-report');
+ const errorMessageContainer = /** @type {HTMLElement} */ (document.querySelector('#db-ops-error-report'));
errorMessageContainer.style.display = 'block';
errorMessageContainer.textContent = message;
@@ -553,15 +552,11 @@ export class BackupController {
}
/**
- *
- * @param root0
- * @param root0.totalRows
- * @param root0.completedRows
- * @param root0.done
+ * @param {{totalRows: number, completedRows: number, done: boolean}} details
*/
_databaseExportProgressCallback({totalRows, completedRows, done}) {
console.log(`Progress: ${completedRows} of ${totalRows} rows completed`);
- const messageContainer = document.querySelector('#db-ops-progress-report');
+ const messageContainer = /** @type {HTMLElement} */ (document.querySelector('#db-ops-progress-report'));
messageContainer.style.display = 'block';
messageContainer.textContent = `Export Progress: ${completedRows} of ${totalRows} rows completed`;
@@ -572,8 +567,8 @@ export class BackupController {
}
/**
- *
- * @param databaseName
+ * @param {string} databaseName
+ * @returns {Promise<Blob>}
*/
async _exportDatabase(databaseName) {
const db = await new Dexie(databaseName).open();
@@ -592,7 +587,7 @@ export class BackupController {
return;
}
- const errorMessageContainer = document.querySelector('#db-ops-error-report');
+ const errorMessageContainer = /** @type {HTMLElement} */ (document.querySelector('#db-ops-error-report'));
errorMessageContainer.style.display = 'none';
const date = new Date(Date.now());
@@ -616,15 +611,11 @@ export class BackupController {
// Importing Dictionaries Database
/**
- *
- * @param root0
- * @param root0.totalRows
- * @param root0.completedRows
- * @param root0.done
+ * @param {{totalRows: number, completedRows: number, done: boolean}} details
*/
_databaseImportProgressCallback({totalRows, completedRows, done}) {
console.log(`Progress: ${completedRows} of ${totalRows} rows completed`);
- const messageContainer = document.querySelector('#db-ops-progress-report');
+ const messageContainer = /** @type {HTMLElement} */ (document.querySelector('#db-ops-progress-report'));
messageContainer.style.display = 'block';
messageContainer.style.color = '#4169e1';
messageContainer.textContent = `Import Progress: ${completedRows} of ${totalRows} rows completed`;
@@ -637,9 +628,8 @@ export class BackupController {
}
/**
- *
- * @param databaseName
- * @param file
+ * @param {string} databaseName
+ * @param {File} file
*/
async _importDatabase(databaseName, file) {
await yomitan.api.purgeDatabase();
@@ -648,16 +638,13 @@ export class BackupController {
yomitan.trigger('storageChanged');
}
- /**
- *
- */
+ /** */
_onSettingsImportDatabaseClick() {
- document.querySelector('#settings-import-db').click();
+ /** @type {HTMLElement} */ (document.querySelector('#settings-import-db')).click();
}
/**
- *
- * @param e
+ * @param {Event} e
*/
async _onSettingsImportDatabaseChange(e) {
if (this._settingsExportDatabaseToken !== null) {
@@ -666,22 +653,23 @@ export class BackupController {
return;
}
- const errorMessageContainer = document.querySelector('#db-ops-error-report');
+ const errorMessageContainer = /** @type {HTMLElement} */ (document.querySelector('#db-ops-error-report'));
errorMessageContainer.style.display = 'none';
- const files = e.target.files;
- if (files.length === 0) { return; }
+ const element = /** @type {HTMLInputElement} */ (e.currentTarget);
+ const files = element.files;
+ if (files === null || files.length === 0) { return; }
const pageExitPrevention = this._settingsController.preventPageExit();
const file = files[0];
- e.target.value = null;
+ element.value = '';
try {
const token = {};
this._settingsExportDatabaseToken = token;
await this._importDatabase(this._dictionariesDatabaseName, file);
} catch (error) {
console.log(error);
- const messageContainer = document.querySelector('#db-ops-progress-report');
+ const messageContainer = /** @type {HTMLElement} */ (document.querySelector('#db-ops-progress-report'));
messageContainer.style.color = 'red';
this._databaseExportImportErrorMessage('Encountered errors when importing. Please restart the browser and try again. If it continues to fail, reinstall Yomitan and import dictionaries one-by-one.');
} finally {
diff --git a/ext/js/pages/settings/recommended-permissions-controller.js b/ext/js/pages/settings/recommended-permissions-controller.js
index e04dbdf7..b19311aa 100644
--- a/ext/js/pages/settings/recommended-permissions-controller.js
+++ b/ext/js/pages/settings/recommended-permissions-controller.js
@@ -19,13 +19,21 @@
import {EventListenerCollection} from '../../core.js';
export class RecommendedPermissionsController {
+ /**
+ * @param {import('./settings-controller.js').SettingsController} settingsController
+ */
constructor(settingsController) {
+ /** @type {import('./settings-controller.js').SettingsController} */
this._settingsController = settingsController;
+ /** @type {?NodeListOf<HTMLInputElement>} */
this._originToggleNodes = null;
+ /** @type {EventListenerCollection} */
this._eventListeners = new EventListenerCollection();
+ /** @type {?HTMLElement} */
this._errorContainer = null;
}
+ /** */
async prepare() {
this._originToggleNodes = document.querySelectorAll('.recommended-permissions-toggle');
this._errorContainer = document.querySelector('#recommended-permissions-error');
@@ -39,35 +47,53 @@ export class RecommendedPermissionsController {
// Private
+ /**
+ * @param {import('settings-controller').PermissionsChangedEvent} details
+ */
_onPermissionsChanged({permissions}) {
this._eventListeners.removeAllEventListeners();
const originsSet = new Set(permissions.origins);
- for (const node of this._originToggleNodes) {
- node.checked = originsSet.has(node.dataset.origin);
+ if (this._originToggleNodes !== null) {
+ for (const node of this._originToggleNodes) {
+ const {origin} = node.dataset;
+ node.checked = typeof origin === 'string' && originsSet.has(origin);
+ }
}
}
+ /**
+ * @param {Event} e
+ */
_onOriginToggleChange(e) {
- const node = e.currentTarget;
+ const node = /** @type {HTMLInputElement} */ (e.currentTarget);
const value = node.checked;
node.checked = !value;
const {origin} = node.dataset;
+ if (typeof origin !== 'string') { return; }
this._setOriginPermissionEnabled(origin, value);
}
+ /** */
async _updatePermissions() {
const permissions = await this._settingsController.permissionsUtil.getAllPermissions();
this._onPermissionsChanged({permissions});
}
+ /**
+ * @param {string} origin
+ * @param {boolean} enabled
+ * @returns {Promise<boolean>}
+ */
async _setOriginPermissionEnabled(origin, enabled) {
let added = false;
try {
added = await this._settingsController.permissionsUtil.setPermissionsGranted({origins: [origin]}, enabled);
} catch (e) {
- this._errorContainer.hidden = false;
- this._errorContainer.textContent = e.message;
+ if (this._errorContainer !== null) {
+ this._errorContainer.hidden = false;
+ this._errorContainer.textContent = e instanceof Error ? e.message : `${e}`;
+ }
}
if (!added) { return false; }
await this._updatePermissions();