/*
* Copyright (C) 2023 Yomitan Authors
* Copyright (C) 2016-2022 Yomichan 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 .
*/
import {ExtensionError} from '../core/extension-error.js';
export class API {
/**
* @param {import('../yomitan.js').Yomitan} yomitan
*/
constructor(yomitan) {
/** @type {import('../yomitan.js').Yomitan} */
this._yomitan = yomitan;
}
/**
* @param {import('api').OptionsGetDetails['optionsContext']} optionsContext
* @returns {Promise}
*/
optionsGet(optionsContext) {
/** @type {import('api').OptionsGetDetails} */
const details = {optionsContext};
return this._invoke('optionsGet', details);
}
/**
* @returns {Promise}
*/
optionsGetFull() {
return this._invoke('optionsGetFull');
}
/**
* @param {import('api').TermsFindDetails['text']} text
* @param {import('api').TermsFindDetails['details']} details
* @param {import('api').TermsFindDetails['optionsContext']} optionsContext
* @returns {Promise}
*/
termsFind(text, details, optionsContext) {
/** @type {import('api').TermsFindDetails} */
const details2 = {text, details, optionsContext};
return this._invoke('termsFind', details2);
}
/**
* @param {import('api').ParseTextDetails['text']} text
* @param {import('api').ParseTextDetails['optionsContext']} optionsContext
* @param {import('api').ParseTextDetails['scanLength']} scanLength
* @param {import('api').ParseTextDetails['useInternalParser']} useInternalParser
* @param {import('api').ParseTextDetails['useMecabParser']} useMecabParser
* @returns {Promise}
*/
parseText(text, optionsContext, scanLength, useInternalParser, useMecabParser) {
/** @type {import('api').ParseTextDetails} */
const details = {text, optionsContext, scanLength, useInternalParser, useMecabParser};
return this._invoke('parseText', details);
}
/**
* @param {import('api').KanjiFindDetails['text']} text
* @param {import('api').KanjiFindDetails['optionsContext']} optionsContext
* @returns {Promise}
*/
kanjiFind(text, optionsContext) {
/** @type {import('api').KanjiFindDetails} */
const details = {text, optionsContext};
return this._invoke('kanjiFind', details);
}
/**
* @returns {Promise}
*/
isAnkiConnected() {
return this._invoke('isAnkiConnected');
}
/**
* @returns {Promise}
*/
getAnkiConnectVersion() {
return this._invoke('getAnkiConnectVersion');
}
/**
* @param {import('api').AddAnkiNoteDetails['note']} note
* @returns {Promise}
*/
addAnkiNote(note) {
/** @type {import('api').AddAnkiNoteDetails} */
const details = {note};
return this._invoke('addAnkiNote', details);
}
/**
* @param {import('api').GetAnkiNoteInfoDetails['notes']} notes
* @param {import('api').GetAnkiNoteInfoDetails['fetchAdditionalInfo']} fetchAdditionalInfo
* @returns {Promise}
*/
getAnkiNoteInfo(notes, fetchAdditionalInfo) {
/** @type {import('api').GetAnkiNoteInfoDetails} */
const details = {notes, fetchAdditionalInfo};
return this._invoke('getAnkiNoteInfo', details);
}
/**
* @param {import('api').InjectAnkiNoteMediaDetails['timestamp']} timestamp
* @param {import('api').InjectAnkiNoteMediaDetails['definitionDetails']} definitionDetails
* @param {import('api').InjectAnkiNoteMediaDetails['audioDetails']} audioDetails
* @param {import('api').InjectAnkiNoteMediaDetails['screenshotDetails']} screenshotDetails
* @param {import('api').InjectAnkiNoteMediaDetails['clipboardDetails']} clipboardDetails
* @param {import('api').InjectAnkiNoteMediaDetails['dictionaryMediaDetails']} dictionaryMediaDetails
* @returns {Promise}
*/
injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails) {
/** @type {import('api').InjectAnkiNoteMediaDetails} */
const details = {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails};
return this._invoke('injectAnkiNoteMedia', details);
}
/**
* @param {import('api').NoteViewDetails['noteId']} noteId
* @param {import('api').NoteViewDetails['mode']} mode
* @param {import('api').NoteViewDetails['allowFallback']} allowFallback
* @returns {Promise}
*/
noteView(noteId, mode, allowFallback) {
/** @type {import('api').NoteViewDetails} */
const details = {noteId, mode, allowFallback};
return this._invoke('noteView', details);
}
/**
* @param {import('api').SuspendAnkiCardsForNoteDetails['noteId']} noteId
* @returns {Promise}
*/
suspendAnkiCardsForNote(noteId) {
/** @type {import('api').SuspendAnkiCardsForNoteDetails} */
const details = {noteId};
return this._invoke('suspendAnkiCardsForNote', details);
}
/**
* @param {import('api').GetTermAudioInfoListDetails['source']} source
* @param {import('api').GetTermAudioInfoListDetails['term']} term
* @param {import('api').GetTermAudioInfoListDetails['reading']} reading
* @returns {Promise}
*/
getTermAudioInfoList(source, term, reading) {
/** @type {import('api').GetTermAudioInfoListDetails} */
const details = {source, term, reading};
return this._invoke('getTermAudioInfoList', details);
}
/**
* @param {import('api').CommandExecDetails['command']} command
* @param {import('api').CommandExecDetails['params']} [params]
* @returns {Promise}
*/
commandExec(command, params) {
/** @type {import('api').CommandExecDetails} */
const details = {command, params};
return this._invoke('commandExec', details);
}
/**
* @param {import('api').SendMessageToFrameDetails['frameId']} frameId
* @param {import('api').SendMessageToFrameDetails['action']} action
* @param {import('api').SendMessageToFrameDetails['params']} [params]
* @returns {Promise}
*/
sendMessageToFrame(frameId, action, params) {
/** @type {import('api').SendMessageToFrameDetails} */
const details = {frameId, action, params};
return this._invoke('sendMessageToFrame', details);
}
/**
* @param {import('api').BroadcastTabDetails['action']} action
* @param {import('api').BroadcastTabDetails['params']} params
* @returns {Promise}
*/
broadcastTab(action, params) {
/** @type {import('api').BroadcastTabDetails} */
const details = {action, params};
return this._invoke('broadcastTab', details);
}
/**
* @returns {Promise}
*/
frameInformationGet() {
return this._invoke('frameInformationGet');
}
/**
* @param {import('api').InjectStylesheetDetails['type']} type
* @param {import('api').InjectStylesheetDetails['value']} value
* @returns {Promise}
*/
injectStylesheet(type, value) {
/** @type {import('api').InjectStylesheetDetails} */
const details = {type, value};
return this._invoke('injectStylesheet', details);
}
/**
* @param {import('api').GetStylesheetContentDetails['url']} url
* @returns {Promise}
*/
getStylesheetContent(url) {
/** @type {import('api').GetStylesheetContentDetails} */
const details = {url};
return this._invoke('getStylesheetContent', details);
}
/**
* @returns {Promise}
*/
getEnvironmentInfo() {
return this._invoke('getEnvironmentInfo');
}
/**
* @returns {Promise}
*/
clipboardGet() {
return this._invoke('clipboardGet');
}
/**
* @returns {Promise}
*/
getDisplayTemplatesHtml() {
return this._invoke('getDisplayTemplatesHtml');
}
/**
* @returns {Promise}
*/
getZoom() {
return this._invoke('getZoom');
}
/**
* @returns {Promise}
*/
getDefaultAnkiFieldTemplates() {
return this._invoke('getDefaultAnkiFieldTemplates');
}
/**
* @returns {Promise}
*/
getDictionaryInfo() {
return this._invoke('getDictionaryInfo');
}
/**
* @returns {Promise}
*/
purgeDatabase() {
return this._invoke('purgeDatabase');
}
/**
* @param {import('api').GetMediaDetails['targets']} targets
* @returns {Promise}
*/
getMedia(targets) {
/** @type {import('api').GetMediaDetails} */
const details = {targets};
return this._invoke('getMedia', details);
}
/**
* @param {import('api').LogDetails['error']} error
* @param {import('api').LogDetails['level']} level
* @param {import('api').LogDetails['context']} context
* @returns {Promise}
*/
log(error, level, context) {
/** @type {import('api').LogDetails} */
const details = {error, level, context};
return this._invoke('log', details);
}
/**
* @returns {Promise}
*/
logIndicatorClear() {
return this._invoke('logIndicatorClear');
}
/**
* @param {import('api').ModifySettingsDetails['targets']} targets
* @param {import('api').ModifySettingsDetails['source']} source
* @returns {Promise}
*/
modifySettings(targets, source) {
const details = {targets, source};
return this._invoke('modifySettings', details);
}
/**
* @param {import('api').GetSettingsDetails['targets']} targets
* @returns {Promise}
*/
getSettings(targets) {
/** @type {import('api').GetSettingsDetails} */
const details = {targets};
return this._invoke('getSettings', details);
}
/**
* @param {import('api').SetAllSettingsDetails['value']} value
* @param {import('api').SetAllSettingsDetails['source']} source
* @returns {Promise}
*/
setAllSettings(value, source) {
/** @type {import('api').SetAllSettingsDetails} */
const details = {value, source};
return this._invoke('setAllSettings', details);
}
/**
* @param {import('api').GetOrCreateSearchPopupDetails} details
* @returns {Promise}
*/
getOrCreateSearchPopup(details) {
return this._invoke('getOrCreateSearchPopup', details);
}
/**
* @param {import('api').IsTabSearchPopupDetails['tabId']} tabId
* @returns {Promise}
*/
isTabSearchPopup(tabId) {
/** @type {import('api').IsTabSearchPopupDetails} */
const details = {tabId};
return this._invoke('isTabSearchPopup', details);
}
/**
* @param {import('api').TriggerDatabaseUpdatedDetails['type']} type
* @param {import('api').TriggerDatabaseUpdatedDetails['cause']} cause
* @returns {Promise}
*/
triggerDatabaseUpdated(type, cause) {
/** @type {import('api').TriggerDatabaseUpdatedDetails} */
const details = {type, cause};
return this._invoke('triggerDatabaseUpdated', details);
}
/**
* @returns {Promise}
*/
testMecab() {
return this._invoke('testMecab');
}
/**
* @param {import('api').TextHasJapaneseCharactersDetails['text']} text
* @returns {Promise}
*/
textHasJapaneseCharacters(text) {
/** @type {import('api').TextHasJapaneseCharactersDetails} */
const details = {text};
return this._invoke('textHasJapaneseCharacters', details);
}
/**
* @param {import('api').GetTermFrequenciesDetails['termReadingList']} termReadingList
* @param {import('api').GetTermFrequenciesDetails['dictionaries']} dictionaries
* @returns {Promise}
*/
getTermFrequencies(termReadingList, dictionaries) {
/** @type {import('api').GetTermFrequenciesDetails} */
const details = {termReadingList, dictionaries};
return this._invoke('getTermFrequencies', details);
}
/**
* @param {import('api').FindAnkiNotesDetails['query']} query
* @returns {Promise}
*/
findAnkiNotes(query) {
/** @type {import('api').FindAnkiNotesDetails} */
const details = {query};
return this._invoke('findAnkiNotes', details);
}
/**
* @param {import('api').LoadExtensionScriptsDetails['files']} files
* @returns {Promise}
*/
loadExtensionScripts(files) {
/** @type {import('api').LoadExtensionScriptsDetails} */
const details = {files};
return this._invoke('loadExtensionScripts', details);
}
/**
* @param {import('api').OpenCrossFramePortDetails['targetTabId']} targetTabId
* @param {import('api').OpenCrossFramePortDetails['targetFrameId']} targetFrameId
* @returns {Promise}
*/
openCrossFramePort(targetTabId, targetFrameId) {
return this._invoke('openCrossFramePort', {targetTabId, targetFrameId});
}
// Utilities
/**
* @template [TReturn=unknown]
* @param {string} action
* @param {import('core').SerializableObject} [params]
* @returns {Promise}
*/
_invoke(action, params = {}) {
const data = {action, params};
return new Promise((resolve, reject) => {
try {
this._yomitan.sendMessage(data, (response) => {
this._checkLastError(chrome.runtime.lastError);
if (response !== null && typeof response === 'object') {
if (typeof response.error !== 'undefined') {
reject(ExtensionError.deserialize(response.error));
} else {
resolve(response.result);
}
} else {
const message = response === null ? 'Unexpected null response' : `Unexpected response of type ${typeof response}`;
reject(new Error(`${message} (${JSON.stringify(data)})`));
}
});
} catch (e) {
reject(e);
}
});
}
/**
* @param {chrome.runtime.LastError|undefined} _ignore
*/
_checkLastError(_ignore) {
// NOP
}
}