/*
* Copyright (C) 2023-2024 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('../extension/web-extension.js').WebExtension} webExtension
*/
constructor(webExtension) {
/** @type {import('../extension/web-extension.js').WebExtension} */
this._webExtension = webExtension;
}
/**
* @param {import('api').ApiParam<'optionsGet', 'optionsContext'>} optionsContext
* @returns {Promise>}
*/
optionsGet(optionsContext) {
return this._invoke('optionsGet', {optionsContext});
}
/**
* @returns {Promise>}
*/
optionsGetFull() {
return this._invoke('optionsGetFull', void 0);
}
/**
* @param {import('api').ApiParam<'termsFind', 'text'>} text
* @param {import('api').ApiParam<'termsFind', 'details'>} details
* @param {import('api').ApiParam<'termsFind', 'optionsContext'>} optionsContext
* @returns {Promise>}
*/
termsFind(text, details, optionsContext) {
return this._invoke('termsFind', {text, details, optionsContext});
}
/**
* @param {import('api').ApiParam<'parseText', 'text'>} text
* @param {import('api').ApiParam<'parseText', 'optionsContext'>} optionsContext
* @param {import('api').ApiParam<'parseText', 'scanLength'>} scanLength
* @param {import('api').ApiParam<'parseText', 'useInternalParser'>} useInternalParser
* @param {import('api').ApiParam<'parseText', 'useMecabParser'>} useMecabParser
* @returns {Promise>}
*/
parseText(text, optionsContext, scanLength, useInternalParser, useMecabParser) {
return this._invoke('parseText', {text, optionsContext, scanLength, useInternalParser, useMecabParser});
}
/**
* @param {import('api').ApiParam<'kanjiFind', 'text'>} text
* @param {import('api').ApiParam<'kanjiFind', 'optionsContext'>} optionsContext
* @returns {Promise>}
*/
kanjiFind(text, optionsContext) {
return this._invoke('kanjiFind', {text, optionsContext});
}
/**
* @returns {Promise>}
*/
isAnkiConnected() {
return this._invoke('isAnkiConnected', void 0);
}
/**
* @returns {Promise>}
*/
getAnkiConnectVersion() {
return this._invoke('getAnkiConnectVersion', void 0);
}
/**
* @param {import('api').ApiParam<'addAnkiNote', 'note'>} note
* @returns {Promise>}
*/
addAnkiNote(note) {
return this._invoke('addAnkiNote', {note});
}
/**
* @param {import('api').ApiParam<'getAnkiNoteInfo', 'notes'>} notes
* @param {import('api').ApiParam<'getAnkiNoteInfo', 'fetchAdditionalInfo'>} fetchAdditionalInfo
* @returns {Promise>}
*/
getAnkiNoteInfo(notes, fetchAdditionalInfo) {
return this._invoke('getAnkiNoteInfo', {notes, fetchAdditionalInfo});
}
/**
* @param {import('api').ApiParam<'injectAnkiNoteMedia', 'timestamp'>} timestamp
* @param {import('api').ApiParam<'injectAnkiNoteMedia', 'definitionDetails'>} definitionDetails
* @param {import('api').ApiParam<'injectAnkiNoteMedia', 'audioDetails'>} audioDetails
* @param {import('api').ApiParam<'injectAnkiNoteMedia', 'screenshotDetails'>} screenshotDetails
* @param {import('api').ApiParam<'injectAnkiNoteMedia', 'clipboardDetails'>} clipboardDetails
* @param {import('api').ApiParam<'injectAnkiNoteMedia', 'dictionaryMediaDetails'>} dictionaryMediaDetails
* @returns {Promise>}
*/
injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails) {
return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails, dictionaryMediaDetails});
}
/**
* @param {import('api').ApiParam<'viewNotes', 'noteIds'>} noteIds
* @param {import('api').ApiParam<'viewNotes', 'mode'>} mode
* @param {import('api').ApiParam<'viewNotes', 'allowFallback'>} allowFallback
* @returns {Promise>}
*/
viewNotes(noteIds, mode, allowFallback) {
return this._invoke('viewNotes', {noteIds, mode, allowFallback});
}
/**
* @param {import('api').ApiParam<'suspendAnkiCardsForNote', 'noteId'>} noteId
* @returns {Promise>}
*/
suspendAnkiCardsForNote(noteId) {
return this._invoke('suspendAnkiCardsForNote', {noteId});
}
/**
* @param {import('api').ApiParam<'getTermAudioInfoList', 'source'>} source
* @param {import('api').ApiParam<'getTermAudioInfoList', 'term'>} term
* @param {import('api').ApiParam<'getTermAudioInfoList', 'reading'>} reading
* @returns {Promise>}
*/
getTermAudioInfoList(source, term, reading) {
return this._invoke('getTermAudioInfoList', {source, term, reading});
}
/**
* @param {import('api').ApiParam<'commandExec', 'command'>} command
* @param {import('api').ApiParam<'commandExec', 'params'>} [params]
* @returns {Promise>}
*/
commandExec(command, params) {
return this._invoke('commandExec', {command, params});
}
/**
* @param {import('api').ApiParam<'sendMessageToFrame', 'frameId'>} frameId
* @param {import('api').ApiParam<'sendMessageToFrame', 'message'>} message
* @returns {Promise>}
*/
sendMessageToFrame(frameId, message) {
return this._invoke('sendMessageToFrame', {frameId, message});
}
/**
* @param {import('api').ApiParam<'broadcastTab', 'message'>} message
* @returns {Promise>}
*/
broadcastTab(message) {
return this._invoke('broadcastTab', {message});
}
/**
* @returns {Promise>}
*/
frameInformationGet() {
return this._invoke('frameInformationGet', void 0);
}
/**
* @param {import('api').ApiParam<'injectStylesheet', 'type'>} type
* @param {import('api').ApiParam<'injectStylesheet', 'value'>} value
* @returns {Promise>}
*/
injectStylesheet(type, value) {
return this._invoke('injectStylesheet', {type, value});
}
/**
* @param {import('api').ApiParam<'getStylesheetContent', 'url'>} url
* @returns {Promise>}
*/
getStylesheetContent(url) {
return this._invoke('getStylesheetContent', {url});
}
/**
* @returns {Promise>}
*/
getEnvironmentInfo() {
return this._invoke('getEnvironmentInfo', void 0);
}
/**
* @returns {Promise>}
*/
clipboardGet() {
return this._invoke('clipboardGet', void 0);
}
/**
* @returns {Promise>}
*/
getDisplayTemplatesHtml() {
return this._invoke('getDisplayTemplatesHtml', void 0);
}
/**
* @returns {Promise>}
*/
getZoom() {
return this._invoke('getZoom', void 0);
}
/**
* @returns {Promise>}
*/
getDefaultAnkiFieldTemplates() {
return this._invoke('getDefaultAnkiFieldTemplates', void 0);
}
/**
* @returns {Promise>}
*/
getDictionaryInfo() {
return this._invoke('getDictionaryInfo', void 0);
}
/**
* @returns {Promise>}
*/
purgeDatabase() {
return this._invoke('purgeDatabase', void 0);
}
/**
* @param {import('api').ApiParam<'getMedia', 'targets'>} targets
* @returns {Promise>}
*/
getMedia(targets) {
return this._invoke('getMedia', {targets});
}
/**
* @param {import('api').ApiParam<'log', 'error'>} error
* @param {import('api').ApiParam<'log', 'level'>} level
* @param {import('api').ApiParam<'log', 'context'>} context
* @returns {Promise>}
*/
log(error, level, context) {
return this._invoke('log', {error, level, context});
}
/**
* @returns {Promise>}
*/
logIndicatorClear() {
return this._invoke('logIndicatorClear', void 0);
}
/**
* @param {import('api').ApiParam<'modifySettings', 'targets'>} targets
* @param {import('api').ApiParam<'modifySettings', 'source'>} source
* @returns {Promise>}
*/
modifySettings(targets, source) {
return this._invoke('modifySettings', {targets, source});
}
/**
* @param {import('api').ApiParam<'getSettings', 'targets'>} targets
* @returns {Promise>}
*/
getSettings(targets) {
return this._invoke('getSettings', {targets});
}
/**
* @param {import('api').ApiParam<'setAllSettings', 'value'>} value
* @param {import('api').ApiParam<'setAllSettings', 'source'>} source
* @returns {Promise>}
*/
setAllSettings(value, source) {
return this._invoke('setAllSettings', {value, source});
}
/**
* @param {import('api').ApiParams<'getOrCreateSearchPopup'>} details
* @returns {Promise>}
*/
getOrCreateSearchPopup(details) {
return this._invoke('getOrCreateSearchPopup', details);
}
/**
* @param {import('api').ApiParam<'isTabSearchPopup', 'tabId'>} tabId
* @returns {Promise>}
*/
isTabSearchPopup(tabId) {
return this._invoke('isTabSearchPopup', {tabId});
}
/**
* @param {import('api').ApiParam<'triggerDatabaseUpdated', 'type'>} type
* @param {import('api').ApiParam<'triggerDatabaseUpdated', 'cause'>} cause
* @returns {Promise>}
*/
triggerDatabaseUpdated(type, cause) {
return this._invoke('triggerDatabaseUpdated', {type, cause});
}
/**
* @returns {Promise>}
*/
testMecab() {
return this._invoke('testMecab', void 0);
}
/**
* @param {import('api').ApiParam<'textHasJapaneseCharacters', 'text'>} text
* @returns {Promise>}
*/
textHasJapaneseCharacters(text) {
return this._invoke('textHasJapaneseCharacters', {text});
}
/**
* @param {import('api').ApiParam<'getTermFrequencies', 'termReadingList'>} termReadingList
* @param {import('api').ApiParam<'getTermFrequencies', 'dictionaries'>} dictionaries
* @returns {Promise>}
*/
getTermFrequencies(termReadingList, dictionaries) {
return this._invoke('getTermFrequencies', {termReadingList, dictionaries});
}
/**
* @param {import('api').ApiParam<'findAnkiNotes', 'query'>} query
* @returns {Promise>}
*/
findAnkiNotes(query) {
return this._invoke('findAnkiNotes', {query});
}
/**
* @param {import('api').ApiParam<'openCrossFramePort', 'targetTabId'>} targetTabId
* @param {import('api').ApiParam<'openCrossFramePort', 'targetFrameId'>} targetFrameId
* @returns {Promise>}
*/
openCrossFramePort(targetTabId, targetFrameId) {
return this._invoke('openCrossFramePort', {targetTabId, targetFrameId});
}
// Utilities
/**
* @template {import('api').ApiNames} TAction
* @template {import('api').ApiParams} TParams
* @param {TAction} action
* @param {TParams} params
* @returns {Promise>}
*/
_invoke(action, params) {
/** @type {import('api').ApiMessage} */
const data = {action, params};
return new Promise((resolve, reject) => {
try {
this._webExtension.sendMessage(data, (response) => {
this._webExtension.getLastError();
if (response !== null && typeof response === 'object') {
const {error} = /** @type {import('core').UnknownObject} */ (response);
if (typeof error !== 'undefined') {
reject(ExtensionError.deserialize(/** @type {import('core').SerializedError} */ (error)));
} else {
const {result} = /** @type {import('core').UnknownObject} */ (response);
resolve(/** @type {import('api').ApiReturn} */ (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);
}
});
}
}