Move api to yomichan object (#1392)
* Move cross frame API from API to Yomichan * Add API instance to Yomichan * Move api global to yomichan.api * Pass yomichan to API * Remove IIFE
This commit is contained in:
parent
efe8140f10
commit
286534e648
@ -19,15 +19,13 @@
|
||||
* Frontend
|
||||
* HotkeyHandler
|
||||
* PopupFactory
|
||||
* api
|
||||
*/
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
const {tabId, frameId} = await api.frameInformationGet();
|
||||
const {tabId, frameId} = await yomichan.api.frameInformationGet();
|
||||
if (typeof frameId !== 'number') {
|
||||
throw new Error('Failed to get frameId');
|
||||
}
|
||||
|
@ -20,7 +20,6 @@
|
||||
* TextScanner
|
||||
* TextSourceElement
|
||||
* TextSourceRange
|
||||
* api
|
||||
*/
|
||||
|
||||
class Frontend {
|
||||
@ -99,7 +98,7 @@ class Frontend {
|
||||
async prepare() {
|
||||
await this.updateOptions();
|
||||
try {
|
||||
const {zoomFactor} = await api.getZoom();
|
||||
const {zoomFactor} = await yomichan.api.getZoom();
|
||||
this._pageZoomFactor = zoomFactor;
|
||||
} catch (e) {
|
||||
// Ignore exceptions which may occur due to being on an unsupported page (e.g. about:blank)
|
||||
@ -124,7 +123,7 @@ class Frontend {
|
||||
this._textScanner.on('clearSelection', this._onClearSelection.bind(this));
|
||||
this._textScanner.on('searched', this._onSearched.bind(this));
|
||||
|
||||
api.crossFrame.registerHandlers([
|
||||
yomichan.crossFrame.registerHandlers([
|
||||
['closePopup', {async: false, handler: this._onApiClosePopup.bind(this)}],
|
||||
['copySelection', {async: false, handler: this._onApiCopySelection.bind(this)}],
|
||||
['getSelectionText', {async: false, handler: this._onApiGetSelectionText.bind(this)}],
|
||||
@ -332,7 +331,7 @@ class Frontend {
|
||||
|
||||
async _updateOptionsInternal() {
|
||||
const optionsContext = await this._getOptionsContext();
|
||||
const options = await api.optionsGet(optionsContext);
|
||||
const options = await yomichan.api.optionsGet(optionsContext);
|
||||
const {scanning: scanningOptions, sentenceParsing: sentenceParsingOptions} = options;
|
||||
this._options = options;
|
||||
|
||||
@ -462,7 +461,7 @@ class Frontend {
|
||||
return await this._getDefaultPopup();
|
||||
}
|
||||
|
||||
const {popupId} = await api.crossFrame.invoke(targetFrameId, 'getPopupInfo');
|
||||
const {popupId} = await yomichan.crossFrame.invoke(targetFrameId, 'getPopupInfo');
|
||||
if (popupId === null) {
|
||||
return null;
|
||||
}
|
||||
@ -608,9 +607,9 @@ class Frontend {
|
||||
_signalFrontendReady(targetFrameId=null) {
|
||||
const params = {frameId: this._frameId};
|
||||
if (targetFrameId === null) {
|
||||
api.broadcastTab('frontendReady', params);
|
||||
yomichan.api.broadcastTab('frontendReady', params);
|
||||
} else {
|
||||
api.sendMessageToFrame(targetFrameId, 'frontendReady', params);
|
||||
yomichan.api.sendMessageToFrame(targetFrameId, 'frontendReady', params);
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,7 +626,7 @@ class Frontend {
|
||||
},
|
||||
10000
|
||||
);
|
||||
api.broadcastTab('requestFrontendReadyBroadcast', {frameId: this._frameId});
|
||||
yomichan.api.broadcastTab('requestFrontendReadyBroadcast', {frameId: this._frameId});
|
||||
await promise;
|
||||
}
|
||||
|
||||
@ -653,7 +652,7 @@ class Frontend {
|
||||
let documentTitle = document.title;
|
||||
if (this._useProxyPopup) {
|
||||
try {
|
||||
({url, documentTitle} = await api.crossFrame.invoke(this._parentFrameId, 'getPageInfo', {}));
|
||||
({url, documentTitle} = await yomichan.crossFrame.invoke(this._parentFrameId, 'getPageInfo', {}));
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
|
@ -20,7 +20,6 @@
|
||||
* Popup
|
||||
* PopupProxy
|
||||
* PopupWindow
|
||||
* api
|
||||
*/
|
||||
|
||||
class PopupFactory {
|
||||
@ -35,7 +34,7 @@ class PopupFactory {
|
||||
|
||||
prepare() {
|
||||
this._frameOffsetForwarder.prepare();
|
||||
api.crossFrame.registerHandlers([
|
||||
yomichan.crossFrame.registerHandlers([
|
||||
['getOrCreatePopup', {async: true, handler: this._onApiGetOrCreatePopup.bind(this)}],
|
||||
['setOptionsContext', {async: true, handler: this._onApiSetOptionsContext.bind(this)}],
|
||||
['hide', {async: false, handler: this._onApiHide.bind(this)}],
|
||||
@ -132,7 +131,7 @@ class PopupFactory {
|
||||
throw new Error('Invalid frameId');
|
||||
}
|
||||
const useFrameOffsetForwarder = (parentPopupId === null);
|
||||
({id, depth, frameId} = await api.crossFrame.invoke(frameId, 'getOrCreatePopup', {
|
||||
({id, depth, frameId} = await yomichan.crossFrame.invoke(frameId, 'getOrCreatePopup', {
|
||||
id,
|
||||
parentPopupId,
|
||||
frameId,
|
||||
|
@ -15,10 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* api
|
||||
*/
|
||||
|
||||
class PopupProxy extends EventDispatcher {
|
||||
constructor({
|
||||
id,
|
||||
@ -158,7 +154,7 @@ class PopupProxy extends EventDispatcher {
|
||||
// Private
|
||||
|
||||
_invoke(action, params={}) {
|
||||
return api.crossFrame.invoke(this._frameId, action, params);
|
||||
return yomichan.crossFrame.invoke(this._frameId, action, params);
|
||||
}
|
||||
|
||||
async _invokeSafe(action, params={}, defaultReturnValue) {
|
||||
|
@ -15,10 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* api
|
||||
*/
|
||||
|
||||
class PopupWindow extends EventDispatcher {
|
||||
constructor({
|
||||
id,
|
||||
@ -82,7 +78,7 @@ class PopupWindow extends EventDispatcher {
|
||||
}
|
||||
|
||||
async isVisible() {
|
||||
return (this._popupTabId !== null && await api.isTabSearchPopup(this._popupTabId));
|
||||
return (this._popupTabId !== null && await yomichan.api.isTabSearchPopup(this._popupTabId));
|
||||
}
|
||||
|
||||
async setVisibleOverride(_value, _priority) {
|
||||
@ -148,7 +144,7 @@ class PopupWindow extends EventDispatcher {
|
||||
const frameId = 0;
|
||||
if (this._popupTabId !== null) {
|
||||
try {
|
||||
return await api.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
|
||||
return await yomichan.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
|
||||
} catch (e) {
|
||||
if (yomichan.isExtensionUnloaded) {
|
||||
open = false;
|
||||
@ -161,9 +157,9 @@ class PopupWindow extends EventDispatcher {
|
||||
return defaultReturnValue;
|
||||
}
|
||||
|
||||
const {tabId} = await api.getOrCreateSearchPopup({focus: 'ifCreated'});
|
||||
const {tabId} = await yomichan.api.getOrCreateSearchPopup({focus: 'ifCreated'});
|
||||
this._popupTabId = tabId;
|
||||
|
||||
return await api.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
|
||||
return await yomichan.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* DocumentUtil
|
||||
* FrameClient
|
||||
* api
|
||||
* dynamicLoader
|
||||
*/
|
||||
|
||||
@ -460,7 +459,7 @@ class Popup extends EventDispatcher {
|
||||
if (this._frameClient === null || !this._frameClient.isConnected() || contentWindow === null) { return; }
|
||||
|
||||
const message = this._frameClient.createMessage({action, params});
|
||||
return await api.crossFrame.invoke(this._frameClient.frameId, 'popupMessage', message);
|
||||
return await yomichan.crossFrame.invoke(this._frameClient.frameId, 'popupMessage', message);
|
||||
}
|
||||
|
||||
async _invokeSafe(action, params={}, defaultReturnValue) {
|
||||
@ -676,7 +675,7 @@ class Popup extends EventDispatcher {
|
||||
|
||||
async _setOptionsContext(optionsContext) {
|
||||
this._optionsContext = optionsContext;
|
||||
this._options = await api.optionsGet(optionsContext);
|
||||
this._options = await yomichan.api.optionsGet(optionsContext);
|
||||
this.updateTheme();
|
||||
}
|
||||
|
||||
|
@ -15,319 +15,291 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* CrossFrameAPI
|
||||
*/
|
||||
|
||||
const api = (() => {
|
||||
class API {
|
||||
constructor() {
|
||||
this._prepared = false;
|
||||
this._crossFrame = null;
|
||||
}
|
||||
|
||||
get crossFrame() {
|
||||
return this._crossFrame;
|
||||
}
|
||||
|
||||
prepare() {
|
||||
if (this._prepared) { return; }
|
||||
this._crossFrame = new CrossFrameAPI();
|
||||
this._crossFrame.prepare();
|
||||
yomichan.on('log', this._onLog.bind(this));
|
||||
this._prepared = true;
|
||||
}
|
||||
|
||||
// Invoke functions
|
||||
|
||||
optionsGet(optionsContext) {
|
||||
return this._invoke('optionsGet', {optionsContext});
|
||||
}
|
||||
|
||||
optionsGetFull() {
|
||||
return this._invoke('optionsGetFull');
|
||||
}
|
||||
|
||||
termsFind(text, details, optionsContext) {
|
||||
return this._invoke('termsFind', {text, details, optionsContext});
|
||||
}
|
||||
|
||||
textParse(text, optionsContext) {
|
||||
return this._invoke('textParse', {text, optionsContext});
|
||||
}
|
||||
|
||||
kanjiFind(text, optionsContext) {
|
||||
return this._invoke('kanjiFind', {text, optionsContext});
|
||||
}
|
||||
|
||||
isAnkiConnected() {
|
||||
return this._invoke('isAnkiConnected');
|
||||
}
|
||||
|
||||
getAnkiConnectVersion() {
|
||||
return this._invoke('getAnkiConnectVersion');
|
||||
}
|
||||
|
||||
addAnkiNote(note) {
|
||||
return this._invoke('addAnkiNote', {note});
|
||||
}
|
||||
|
||||
getAnkiNoteInfo(notes) {
|
||||
return this._invoke('getAnkiNoteInfo', {notes});
|
||||
}
|
||||
|
||||
injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) {
|
||||
return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails});
|
||||
}
|
||||
|
||||
noteView(noteId) {
|
||||
return this._invoke('noteView', {noteId});
|
||||
}
|
||||
|
||||
suspendAnkiCardsForNote(noteId) {
|
||||
return this._invoke('suspendAnkiCardsForNote', {noteId});
|
||||
}
|
||||
|
||||
getExpressionAudioInfoList(source, expression, reading, details) {
|
||||
return this._invoke('getExpressionAudioInfoList', {source, expression, reading, details});
|
||||
}
|
||||
|
||||
commandExec(command, params) {
|
||||
return this._invoke('commandExec', {command, params});
|
||||
}
|
||||
|
||||
sendMessageToFrame(frameId, action, params) {
|
||||
return this._invoke('sendMessageToFrame', {frameId, action, params});
|
||||
}
|
||||
|
||||
broadcastTab(action, params) {
|
||||
return this._invoke('broadcastTab', {action, params});
|
||||
}
|
||||
|
||||
frameInformationGet() {
|
||||
return this._invoke('frameInformationGet');
|
||||
}
|
||||
|
||||
injectStylesheet(type, value) {
|
||||
return this._invoke('injectStylesheet', {type, value});
|
||||
}
|
||||
|
||||
getStylesheetContent(url) {
|
||||
return this._invoke('getStylesheetContent', {url});
|
||||
}
|
||||
|
||||
getEnvironmentInfo() {
|
||||
return this._invoke('getEnvironmentInfo');
|
||||
}
|
||||
|
||||
clipboardGet() {
|
||||
return this._invoke('clipboardGet');
|
||||
}
|
||||
|
||||
getDisplayTemplatesHtml() {
|
||||
return this._invoke('getDisplayTemplatesHtml');
|
||||
}
|
||||
|
||||
getZoom() {
|
||||
return this._invoke('getZoom');
|
||||
}
|
||||
|
||||
getDefaultAnkiFieldTemplates() {
|
||||
return this._invoke('getDefaultAnkiFieldTemplates');
|
||||
}
|
||||
|
||||
getDictionaryInfo() {
|
||||
return this._invoke('getDictionaryInfo');
|
||||
}
|
||||
|
||||
getDictionaryCounts(dictionaryNames, getTotal) {
|
||||
return this._invoke('getDictionaryCounts', {dictionaryNames, getTotal});
|
||||
}
|
||||
|
||||
purgeDatabase() {
|
||||
return this._invoke('purgeDatabase');
|
||||
}
|
||||
|
||||
getMedia(targets) {
|
||||
return this._invoke('getMedia', {targets});
|
||||
}
|
||||
|
||||
logIndicatorClear() {
|
||||
return this._invoke('logIndicatorClear');
|
||||
}
|
||||
|
||||
modifySettings(targets, source) {
|
||||
return this._invoke('modifySettings', {targets, source});
|
||||
}
|
||||
|
||||
getSettings(targets) {
|
||||
return this._invoke('getSettings', {targets});
|
||||
}
|
||||
|
||||
setAllSettings(value, source) {
|
||||
return this._invoke('setAllSettings', {value, source});
|
||||
}
|
||||
|
||||
getOrCreateSearchPopup(details) {
|
||||
return this._invoke('getOrCreateSearchPopup', isObject(details) ? details : {});
|
||||
}
|
||||
|
||||
isTabSearchPopup(tabId) {
|
||||
return this._invoke('isTabSearchPopup', {tabId});
|
||||
}
|
||||
|
||||
triggerDatabaseUpdated(type, cause) {
|
||||
return this._invoke('triggerDatabaseUpdated', {type, cause});
|
||||
}
|
||||
|
||||
testMecab() {
|
||||
return this._invoke('testMecab', {});
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
_createActionPort(timeout=5000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timer = null;
|
||||
const portDetails = deferPromise();
|
||||
|
||||
const onConnect = async (port) => {
|
||||
try {
|
||||
const {name: expectedName, id: expectedId} = await portDetails.promise;
|
||||
const {name, id} = JSON.parse(port.name);
|
||||
if (name !== expectedName || id !== expectedId || timer === null) { return; }
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
|
||||
chrome.runtime.onConnect.removeListener(onConnect);
|
||||
resolve(port);
|
||||
};
|
||||
|
||||
const onError = (e) => {
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
chrome.runtime.onConnect.removeListener(onConnect);
|
||||
portDetails.reject(e);
|
||||
reject(e);
|
||||
};
|
||||
|
||||
timer = setTimeout(() => onError(new Error('Timeout')), timeout);
|
||||
|
||||
chrome.runtime.onConnect.addListener(onConnect);
|
||||
this._invoke('createActionPort').then(portDetails.resolve, onError);
|
||||
});
|
||||
}
|
||||
|
||||
_invokeWithProgress(action, params, onProgress, timeout=5000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let port = null;
|
||||
|
||||
if (typeof onProgress !== 'function') {
|
||||
onProgress = () => {};
|
||||
}
|
||||
|
||||
const onMessage = (message) => {
|
||||
switch (message.type) {
|
||||
case 'progress':
|
||||
try {
|
||||
onProgress(...message.data);
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
break;
|
||||
case 'complete':
|
||||
cleanup();
|
||||
resolve(message.data);
|
||||
break;
|
||||
case 'error':
|
||||
cleanup();
|
||||
reject(deserializeError(message.data));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const onDisconnect = () => {
|
||||
cleanup();
|
||||
reject(new Error('Disconnected'));
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
if (port !== null) {
|
||||
port.onMessage.removeListener(onMessage);
|
||||
port.onDisconnect.removeListener(onDisconnect);
|
||||
port.disconnect();
|
||||
port = null;
|
||||
}
|
||||
onProgress = null;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
port = await this._createActionPort(timeout);
|
||||
port.onMessage.addListener(onMessage);
|
||||
port.onDisconnect.addListener(onDisconnect);
|
||||
|
||||
// Chrome has a maximum message size that can be sent, so longer messages must be fragmented.
|
||||
const messageString = JSON.stringify({action, params});
|
||||
const fragmentSize = 1e7; // 10 MB
|
||||
for (let i = 0, ii = messageString.length; i < ii; i += fragmentSize) {
|
||||
const data = messageString.substring(i, i + fragmentSize);
|
||||
port.postMessage({action: 'fragment', data});
|
||||
}
|
||||
port.postMessage({action: 'invoke'});
|
||||
} catch (e) {
|
||||
cleanup();
|
||||
reject(e);
|
||||
} finally {
|
||||
action = null;
|
||||
params = null;
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
_invoke(action, params={}) {
|
||||
const data = {action, params};
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
yomichan.sendMessage(data, (response) => {
|
||||
this._checkLastError(chrome.runtime.lastError);
|
||||
if (response !== null && typeof response === 'object') {
|
||||
if (typeof response.error !== 'undefined') {
|
||||
reject(deserializeError(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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_checkLastError() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
async _onLog({error, level, context}) {
|
||||
try {
|
||||
error = serializeError(error);
|
||||
await this._invoke('log', {error, level, context});
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
class API {
|
||||
constructor(yomichan) {
|
||||
this._yomichan = yomichan;
|
||||
}
|
||||
|
||||
return new API();
|
||||
})();
|
||||
optionsGet(optionsContext) {
|
||||
return this._invoke('optionsGet', {optionsContext});
|
||||
}
|
||||
|
||||
optionsGetFull() {
|
||||
return this._invoke('optionsGetFull');
|
||||
}
|
||||
|
||||
termsFind(text, details, optionsContext) {
|
||||
return this._invoke('termsFind', {text, details, optionsContext});
|
||||
}
|
||||
|
||||
textParse(text, optionsContext) {
|
||||
return this._invoke('textParse', {text, optionsContext});
|
||||
}
|
||||
|
||||
kanjiFind(text, optionsContext) {
|
||||
return this._invoke('kanjiFind', {text, optionsContext});
|
||||
}
|
||||
|
||||
isAnkiConnected() {
|
||||
return this._invoke('isAnkiConnected');
|
||||
}
|
||||
|
||||
getAnkiConnectVersion() {
|
||||
return this._invoke('getAnkiConnectVersion');
|
||||
}
|
||||
|
||||
addAnkiNote(note) {
|
||||
return this._invoke('addAnkiNote', {note});
|
||||
}
|
||||
|
||||
getAnkiNoteInfo(notes) {
|
||||
return this._invoke('getAnkiNoteInfo', {notes});
|
||||
}
|
||||
|
||||
injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) {
|
||||
return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails});
|
||||
}
|
||||
|
||||
noteView(noteId) {
|
||||
return this._invoke('noteView', {noteId});
|
||||
}
|
||||
|
||||
suspendAnkiCardsForNote(noteId) {
|
||||
return this._invoke('suspendAnkiCardsForNote', {noteId});
|
||||
}
|
||||
|
||||
getExpressionAudioInfoList(source, expression, reading, details) {
|
||||
return this._invoke('getExpressionAudioInfoList', {source, expression, reading, details});
|
||||
}
|
||||
|
||||
commandExec(command, params) {
|
||||
return this._invoke('commandExec', {command, params});
|
||||
}
|
||||
|
||||
sendMessageToFrame(frameId, action, params) {
|
||||
return this._invoke('sendMessageToFrame', {frameId, action, params});
|
||||
}
|
||||
|
||||
broadcastTab(action, params) {
|
||||
return this._invoke('broadcastTab', {action, params});
|
||||
}
|
||||
|
||||
frameInformationGet() {
|
||||
return this._invoke('frameInformationGet');
|
||||
}
|
||||
|
||||
injectStylesheet(type, value) {
|
||||
return this._invoke('injectStylesheet', {type, value});
|
||||
}
|
||||
|
||||
getStylesheetContent(url) {
|
||||
return this._invoke('getStylesheetContent', {url});
|
||||
}
|
||||
|
||||
getEnvironmentInfo() {
|
||||
return this._invoke('getEnvironmentInfo');
|
||||
}
|
||||
|
||||
clipboardGet() {
|
||||
return this._invoke('clipboardGet');
|
||||
}
|
||||
|
||||
getDisplayTemplatesHtml() {
|
||||
return this._invoke('getDisplayTemplatesHtml');
|
||||
}
|
||||
|
||||
getZoom() {
|
||||
return this._invoke('getZoom');
|
||||
}
|
||||
|
||||
getDefaultAnkiFieldTemplates() {
|
||||
return this._invoke('getDefaultAnkiFieldTemplates');
|
||||
}
|
||||
|
||||
getDictionaryInfo() {
|
||||
return this._invoke('getDictionaryInfo');
|
||||
}
|
||||
|
||||
getDictionaryCounts(dictionaryNames, getTotal) {
|
||||
return this._invoke('getDictionaryCounts', {dictionaryNames, getTotal});
|
||||
}
|
||||
|
||||
purgeDatabase() {
|
||||
return this._invoke('purgeDatabase');
|
||||
}
|
||||
|
||||
getMedia(targets) {
|
||||
return this._invoke('getMedia', {targets});
|
||||
}
|
||||
|
||||
log(error, level, context) {
|
||||
return this._invoke('log', {error, level, context});
|
||||
}
|
||||
|
||||
logIndicatorClear() {
|
||||
return this._invoke('logIndicatorClear');
|
||||
}
|
||||
|
||||
modifySettings(targets, source) {
|
||||
return this._invoke('modifySettings', {targets, source});
|
||||
}
|
||||
|
||||
getSettings(targets) {
|
||||
return this._invoke('getSettings', {targets});
|
||||
}
|
||||
|
||||
setAllSettings(value, source) {
|
||||
return this._invoke('setAllSettings', {value, source});
|
||||
}
|
||||
|
||||
getOrCreateSearchPopup(details) {
|
||||
return this._invoke('getOrCreateSearchPopup', isObject(details) ? details : {});
|
||||
}
|
||||
|
||||
isTabSearchPopup(tabId) {
|
||||
return this._invoke('isTabSearchPopup', {tabId});
|
||||
}
|
||||
|
||||
triggerDatabaseUpdated(type, cause) {
|
||||
return this._invoke('triggerDatabaseUpdated', {type, cause});
|
||||
}
|
||||
|
||||
testMecab() {
|
||||
return this._invoke('testMecab', {});
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
_createActionPort(timeout=5000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timer = null;
|
||||
const portDetails = deferPromise();
|
||||
|
||||
const onConnect = async (port) => {
|
||||
try {
|
||||
const {name: expectedName, id: expectedId} = await portDetails.promise;
|
||||
const {name, id} = JSON.parse(port.name);
|
||||
if (name !== expectedName || id !== expectedId || timer === null) { return; }
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
|
||||
chrome.runtime.onConnect.removeListener(onConnect);
|
||||
resolve(port);
|
||||
};
|
||||
|
||||
const onError = (e) => {
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
chrome.runtime.onConnect.removeListener(onConnect);
|
||||
portDetails.reject(e);
|
||||
reject(e);
|
||||
};
|
||||
|
||||
timer = setTimeout(() => onError(new Error('Timeout')), timeout);
|
||||
|
||||
chrome.runtime.onConnect.addListener(onConnect);
|
||||
this._invoke('createActionPort').then(portDetails.resolve, onError);
|
||||
});
|
||||
}
|
||||
|
||||
_invokeWithProgress(action, params, onProgress, timeout=5000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let port = null;
|
||||
|
||||
if (typeof onProgress !== 'function') {
|
||||
onProgress = () => {};
|
||||
}
|
||||
|
||||
const onMessage = (message) => {
|
||||
switch (message.type) {
|
||||
case 'progress':
|
||||
try {
|
||||
onProgress(...message.data);
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
break;
|
||||
case 'complete':
|
||||
cleanup();
|
||||
resolve(message.data);
|
||||
break;
|
||||
case 'error':
|
||||
cleanup();
|
||||
reject(deserializeError(message.data));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const onDisconnect = () => {
|
||||
cleanup();
|
||||
reject(new Error('Disconnected'));
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
if (port !== null) {
|
||||
port.onMessage.removeListener(onMessage);
|
||||
port.onDisconnect.removeListener(onDisconnect);
|
||||
port.disconnect();
|
||||
port = null;
|
||||
}
|
||||
onProgress = null;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
port = await this._createActionPort(timeout);
|
||||
port.onMessage.addListener(onMessage);
|
||||
port.onDisconnect.addListener(onDisconnect);
|
||||
|
||||
// Chrome has a maximum message size that can be sent, so longer messages must be fragmented.
|
||||
const messageString = JSON.stringify({action, params});
|
||||
const fragmentSize = 1e7; // 10 MB
|
||||
for (let i = 0, ii = messageString.length; i < ii; i += fragmentSize) {
|
||||
const data = messageString.substring(i, i + fragmentSize);
|
||||
port.postMessage({action: 'fragment', data});
|
||||
}
|
||||
port.postMessage({action: 'invoke'});
|
||||
} catch (e) {
|
||||
cleanup();
|
||||
reject(e);
|
||||
} finally {
|
||||
action = null;
|
||||
params = null;
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
_invoke(action, params={}) {
|
||||
const data = {action, params};
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this._yomichan.sendMessage(data, (response) => {
|
||||
this._checkLastError(chrome.runtime.lastError);
|
||||
if (response !== null && typeof response === 'object') {
|
||||
if (typeof response.error !== 'undefined') {
|
||||
reject(deserializeError(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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_checkLastError() {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* api
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is used to return the ancestor frame IDs for the current frame.
|
||||
* This is a workaround to using the `webNavigation.getAllFrames` API, which
|
||||
@ -118,7 +114,7 @@ class FrameAncestryHandler {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
api.crossFrame.unregisterHandler(responseMessageId);
|
||||
yomichan.crossFrame.unregisterHandler(responseMessageId);
|
||||
};
|
||||
const onMessage = (params) => {
|
||||
if (params.nonce !== nonce) { return null; }
|
||||
@ -148,7 +144,7 @@ class FrameAncestryHandler {
|
||||
};
|
||||
|
||||
// Start
|
||||
api.crossFrame.registerHandlers([[responseMessageId, {async: false, handler: onMessage}]]);
|
||||
yomichan.crossFrame.registerHandlers([[responseMessageId, {async: false, handler: onMessage}]]);
|
||||
resetTimeout();
|
||||
const frameId = this._frameId;
|
||||
this._requestFrameInfo(targetWindow, frameId, frameId, uniqueId, nonce);
|
||||
@ -187,7 +183,7 @@ class FrameAncestryHandler {
|
||||
const responseMessageId = `${this._responseMessageIdBase}${uniqueId}`;
|
||||
|
||||
try {
|
||||
const response = await api.crossFrame.invoke(originFrameId, responseMessageId, responseParams);
|
||||
const response = await yomichan.crossFrame.invoke(originFrameId, responseMessageId, responseParams);
|
||||
if (response === null) { return; }
|
||||
nonce = response.nonce;
|
||||
} catch (e) {
|
||||
|
@ -15,10 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* api
|
||||
*/
|
||||
|
||||
class FrameEndpoint {
|
||||
constructor() {
|
||||
this._secret = generateId(16);
|
||||
@ -32,7 +28,7 @@ class FrameEndpoint {
|
||||
this._eventListeners.addEventListener(window, 'message', this._onMessage.bind(this), false);
|
||||
this._eventListenersSetup = true;
|
||||
}
|
||||
api.broadcastTab('frameEndpointReady', {secret: this._secret});
|
||||
yomichan.api.broadcastTab('frameEndpointReady', {secret: this._secret});
|
||||
}
|
||||
|
||||
authenticate(message) {
|
||||
@ -60,6 +56,6 @@ class FrameEndpoint {
|
||||
this._token = token;
|
||||
|
||||
this._eventListeners.removeAllEventListeners();
|
||||
api.sendMessageToFrame(hostFrameId, 'frameEndpointConnected', {secret, token});
|
||||
yomichan.api.sendMessageToFrame(hostFrameId, 'frameEndpointConnected', {secret, token});
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* FrameAncestryHandler
|
||||
* api
|
||||
*/
|
||||
|
||||
class FrameOffsetForwarder {
|
||||
@ -28,7 +27,7 @@ class FrameOffsetForwarder {
|
||||
|
||||
prepare() {
|
||||
this._frameAncestryHandler.prepare();
|
||||
api.crossFrame.registerHandlers([
|
||||
yomichan.crossFrame.registerHandlers([
|
||||
['FrameOffsetForwarder.getChildFrameRect', {async: false, handler: this._onMessageGetChildFrameRect.bind(this)}]
|
||||
]);
|
||||
}
|
||||
@ -43,7 +42,7 @@ class FrameOffsetForwarder {
|
||||
let childFrameId = this._frameId;
|
||||
const promises = [];
|
||||
for (const frameId of ancestorFrameIds) {
|
||||
promises.push(api.crossFrame.invoke(frameId, 'FrameOffsetForwarder.getChildFrameRect', {frameId: childFrameId}));
|
||||
promises.push(yomichan.crossFrame.invoke(frameId, 'FrameOffsetForwarder.getChildFrameRect', {frameId: childFrameId}));
|
||||
childFrameId = frameId;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* AudioSystem
|
||||
* PopupMenu
|
||||
* api
|
||||
*/
|
||||
|
||||
class DisplayAudio {
|
||||
@ -314,7 +313,7 @@ class DisplayAudio {
|
||||
}
|
||||
|
||||
async _getExpressionAudioInfoList(source, expression, reading, details) {
|
||||
const infoList = await api.getExpressionAudioInfoList(source, expression, reading, details);
|
||||
const infoList = await yomichan.api.getExpressionAudioInfoList(source, expression, reading, details);
|
||||
return infoList.map((info) => ({info, audioPromise: null, audioResolved: false, audio: null}));
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* DictionaryDataUtil
|
||||
* HtmlTemplateCollection
|
||||
* api
|
||||
*/
|
||||
|
||||
class DisplayGenerator {
|
||||
@ -31,7 +30,7 @@ class DisplayGenerator {
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
const html = await api.getDisplayTemplatesHtml();
|
||||
const html = await yomichan.api.getDisplayTemplatesHtml();
|
||||
this._templates = new HtmlTemplateCollection(html);
|
||||
this.updateHotkeys();
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* PanelElement
|
||||
* api
|
||||
*/
|
||||
|
||||
class DisplayProfileSelection {
|
||||
@ -67,7 +66,7 @@ class DisplayProfileSelection {
|
||||
|
||||
async _updateProfileList() {
|
||||
this._profileListNeedsUpdate = false;
|
||||
const options = await api.optionsGetFull();
|
||||
const options = await yomichan.api.optionsGetFull();
|
||||
|
||||
this._eventListeners.removeAllEventListeners();
|
||||
const displayGenerator = this._display.displayGenerator;
|
||||
@ -95,7 +94,7 @@ class DisplayProfileSelection {
|
||||
}
|
||||
|
||||
async _setProfileCurrent(index) {
|
||||
await api.modifySettings([{
|
||||
await yomichan.api.modifySettings([{
|
||||
action: 'set',
|
||||
path: 'profileCurrent',
|
||||
value: index,
|
||||
|
@ -31,7 +31,6 @@
|
||||
* QueryParser
|
||||
* ScrollElement
|
||||
* TextScanner
|
||||
* api
|
||||
* dynamicLoader
|
||||
*/
|
||||
|
||||
@ -206,7 +205,7 @@ class Display extends EventDispatcher {
|
||||
async prepare() {
|
||||
// State setup
|
||||
const {documentElement} = document;
|
||||
const {browser} = await api.getEnvironmentInfo();
|
||||
const {browser} = await yomichan.api.getEnvironmentInfo();
|
||||
this._browser = browser;
|
||||
|
||||
// Prepare
|
||||
@ -221,7 +220,7 @@ class Display extends EventDispatcher {
|
||||
this._queryParser.on('searched', this._onQueryParserSearch.bind(this));
|
||||
this._progressIndicatorVisible.on('change', this._onProgressIndicatorVisibleChanged.bind(this));
|
||||
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
|
||||
api.crossFrame.registerHandlers([
|
||||
yomichan.crossFrame.registerHandlers([
|
||||
['popupMessage', {async: 'dynamic', handler: this._onDirectMessage.bind(this)}]
|
||||
]);
|
||||
window.addEventListener('message', this._onWindowMessage.bind(this), false);
|
||||
@ -290,7 +289,7 @@ class Display extends EventDispatcher {
|
||||
}
|
||||
|
||||
async updateOptions() {
|
||||
const options = await api.optionsGet(this.getOptionsContext());
|
||||
const options = await yomichan.api.optionsGet(this.getOptionsContext());
|
||||
const templates = await this._getAnkiFieldTemplates(options);
|
||||
const {scanning: scanningOptions, sentenceParsing: sentenceParsingOptions} = options;
|
||||
this._options = options;
|
||||
@ -674,7 +673,7 @@ class Display extends EventDispatcher {
|
||||
if (typeof documentTitle !== 'string') { documentTitle = document.title; }
|
||||
const optionsContext = this.getOptionsContext();
|
||||
const query = e.currentTarget.textContent;
|
||||
const definitions = await api.kanjiFind(query, optionsContext);
|
||||
const definitions = await yomichan.api.kanjiFind(query, optionsContext);
|
||||
const details = {
|
||||
focus: false,
|
||||
history: true,
|
||||
@ -707,7 +706,7 @@ class Display extends EventDispatcher {
|
||||
_onNoteView(e) {
|
||||
e.preventDefault();
|
||||
const link = e.currentTarget;
|
||||
api.noteView(link.dataset.noteId);
|
||||
yomichan.api.noteView(link.dataset.noteId);
|
||||
}
|
||||
|
||||
_onWheel(e) {
|
||||
@ -839,10 +838,10 @@ class Display extends EventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
const {definitions} = await api.termsFind(source, findDetails, optionsContext);
|
||||
const {definitions} = await yomichan.api.termsFind(source, findDetails, optionsContext);
|
||||
return definitions;
|
||||
} else {
|
||||
const definitions = await api.kanjiFind(source, optionsContext);
|
||||
const definitions = await yomichan.api.kanjiFind(source, optionsContext);
|
||||
return definitions;
|
||||
}
|
||||
}
|
||||
@ -1059,7 +1058,7 @@ class Display extends EventDispatcher {
|
||||
const noteContext = this._getNoteContext();
|
||||
states = await this._areDefinitionsAddable(definitions, modes, noteContext);
|
||||
} else {
|
||||
if (!await api.isAnkiConnected()) {
|
||||
if (!await yomichan.api.isAnkiConnected()) {
|
||||
throw new Error('Anki not connected');
|
||||
}
|
||||
states = this._areDefinitionsAddableForcedValue(definitions, modes, true);
|
||||
@ -1183,7 +1182,7 @@ class Display extends EventDispatcher {
|
||||
_tryViewAnkiNoteForSelectedDefinition() {
|
||||
const button = this._viewerButtonFind(this._index);
|
||||
if (button !== null && !button.disabled) {
|
||||
api.noteView(button.dataset.noteId);
|
||||
yomichan.api.noteView(button.dataset.noteId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1206,7 +1205,7 @@ class Display extends EventDispatcher {
|
||||
let noteId = null;
|
||||
let addNoteOkay = false;
|
||||
try {
|
||||
noteId = await api.addAnkiNote(note);
|
||||
noteId = await yomichan.api.addAnkiNote(note);
|
||||
addNoteOkay = true;
|
||||
} catch (e) {
|
||||
errors.length = 0;
|
||||
@ -1219,7 +1218,7 @@ class Display extends EventDispatcher {
|
||||
} else {
|
||||
if (suspendNewCards) {
|
||||
try {
|
||||
await api.suspendAnkiCardsForNote(noteId);
|
||||
await yomichan.api.suspendAnkiCardsForNote(noteId);
|
||||
} catch (e) {
|
||||
errors.push(e);
|
||||
}
|
||||
@ -1400,7 +1399,7 @@ class Display extends EventDispatcher {
|
||||
templates = this._ankiFieldTemplatesDefault;
|
||||
if (typeof templates === 'string') { return templates; }
|
||||
|
||||
templates = await api.getDefaultAnkiFieldTemplates();
|
||||
templates = await yomichan.api.getDefaultAnkiFieldTemplates();
|
||||
this._ankiFieldTemplatesDefault = templates;
|
||||
return templates;
|
||||
}
|
||||
@ -1416,7 +1415,7 @@ class Display extends EventDispatcher {
|
||||
}
|
||||
const notes = await Promise.all(notePromises);
|
||||
|
||||
const infos = await api.getAnkiNoteInfo(notes);
|
||||
const infos = await yomichan.api.getAnkiNoteInfo(notes);
|
||||
const results = [];
|
||||
for (let i = 0, ii = infos.length; i < ii; i += modeCount) {
|
||||
results.push(infos.slice(i, i + modeCount));
|
||||
@ -1494,7 +1493,7 @@ class Display extends EventDispatcher {
|
||||
image: this._ankiNoteBuilder.containsMarker(fields, 'clipboard-image'),
|
||||
text: this._ankiNoteBuilder.containsMarker(fields, 'clipboard-text')
|
||||
};
|
||||
return await api.injectAnkiNoteMedia(
|
||||
return await yomichan.api.injectAnkiNoteMedia(
|
||||
timestamp,
|
||||
definitionDetails,
|
||||
audioDetails,
|
||||
@ -1605,7 +1604,7 @@ class Display extends EventDispatcher {
|
||||
if (this._contentOriginTabId === this._tabId && this._contentOriginFrameId === this._frameId) {
|
||||
throw new Error('Content origin is same page');
|
||||
}
|
||||
return await api.crossFrame.invokeTab(this._contentOriginTabId, this._contentOriginFrameId, action, params);
|
||||
return await yomichan.crossFrame.invokeTab(this._contentOriginTabId, this._contentOriginFrameId, action, params);
|
||||
}
|
||||
|
||||
_copyHostSelection() {
|
||||
|
@ -21,7 +21,6 @@
|
||||
* DocumentFocusController
|
||||
* HotkeyHandler
|
||||
* JapaneseUtil
|
||||
* api
|
||||
*/
|
||||
|
||||
(async () => {
|
||||
@ -29,10 +28,9 @@
|
||||
const documentFocusController = new DocumentFocusController();
|
||||
documentFocusController.prepare();
|
||||
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
const {tabId, frameId} = await api.frameInformationGet();
|
||||
const {tabId, frameId} = await yomichan.api.frameInformationGet();
|
||||
|
||||
const japaneseUtil = new JapaneseUtil(null);
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* TextScanner
|
||||
* api
|
||||
*/
|
||||
|
||||
class QueryParser extends EventDispatcher {
|
||||
@ -76,7 +75,7 @@ class QueryParser extends EventDispatcher {
|
||||
|
||||
const token = {};
|
||||
this._setTextToken = token;
|
||||
this._parseResults = await api.textParse(text, this._getOptionsContext());
|
||||
this._parseResults = await yomichan.api.textParse(text, this._getOptionsContext());
|
||||
if (this._setTextToken !== token) { return; }
|
||||
|
||||
this._refreshSelectedParser();
|
||||
@ -116,7 +115,7 @@ class QueryParser extends EventDispatcher {
|
||||
|
||||
_setSelectedParser(value) {
|
||||
const optionsContext = this._getOptionsContext();
|
||||
api.modifySettings([{
|
||||
yomichan.api.modifySettings([{
|
||||
action: 'set',
|
||||
path: 'parsing.selectedParser',
|
||||
value,
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* ClipboardMonitor
|
||||
* api
|
||||
* wanakana
|
||||
*/
|
||||
|
||||
@ -40,7 +39,7 @@ class SearchDisplayController {
|
||||
this._clipboardMonitor = new ClipboardMonitor({
|
||||
japaneseUtil,
|
||||
clipboardReader: {
|
||||
getText: async () => (await api.clipboardGet())
|
||||
getText: async () => (await yomichan.api.clipboardGet())
|
||||
}
|
||||
});
|
||||
this._messageHandlers = new Map();
|
||||
@ -201,7 +200,7 @@ class SearchDisplayController {
|
||||
_onWanakanaEnableChange(e) {
|
||||
const value = e.target.checked;
|
||||
this._setWanakanaEnabled(value);
|
||||
api.modifySettings([{
|
||||
yomichan.api.modifySettings([{
|
||||
action: 'set',
|
||||
path: 'general.enableWanakana',
|
||||
value,
|
||||
@ -301,7 +300,7 @@ class SearchDisplayController {
|
||||
|
||||
if (!modify) { return; }
|
||||
|
||||
await api.modifySettings([{
|
||||
await yomichan.api.modifySettings([{
|
||||
action: 'set',
|
||||
path: 'clipboard.enableSearchPageMonitor',
|
||||
value,
|
||||
|
@ -21,7 +21,6 @@
|
||||
* HotkeyHandler
|
||||
* JapaneseUtil
|
||||
* SearchDisplayController
|
||||
* api
|
||||
* wanakana
|
||||
*/
|
||||
|
||||
@ -30,10 +29,9 @@
|
||||
const documentFocusController = new DocumentFocusController();
|
||||
documentFocusController.prepare();
|
||||
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
const {tabId, frameId} = await api.frameInformationGet();
|
||||
const {tabId, frameId} = await yomichan.api.frameInformationGet();
|
||||
|
||||
const japaneseUtil = new JapaneseUtil(wanakana);
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* DocumentUtil
|
||||
* api
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -59,7 +58,7 @@ class HotkeyHandler extends EventDispatcher {
|
||||
prepare() {
|
||||
this._isPrepared = true;
|
||||
this._updateEventHandlers();
|
||||
api.crossFrame.registerHandlers([
|
||||
yomichan.crossFrame.registerHandlers([
|
||||
['hotkeyHandler.forwardHotkey', {async: false, handler: this._onMessageForwardHotkey.bind(this)}]
|
||||
]);
|
||||
}
|
||||
@ -259,7 +258,7 @@ class HotkeyHandler extends EventDispatcher {
|
||||
const frameId = this._forwardFrameId;
|
||||
if (frameId === null) { throw new Error('No forwarding target'); }
|
||||
try {
|
||||
await api.crossFrame.invoke(frameId, 'hotkeyHandler.forwardHotkey', {key, modifiers});
|
||||
await yomichan.crossFrame.invoke(frameId, 'hotkeyHandler.forwardHotkey', {key, modifiers});
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* HotkeyUtil
|
||||
* api
|
||||
*/
|
||||
|
||||
class HotkeyHelpController {
|
||||
@ -29,7 +28,7 @@ class HotkeyHelpController {
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
const {platform: {os}} = await api.getEnvironmentInfo();
|
||||
const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
|
||||
this._hotkeyUtil.os = os;
|
||||
await this._setupGlobalCommands(this._globalActionHotkeys);
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* DocumentUtil
|
||||
* api
|
||||
*/
|
||||
|
||||
class TextScanner extends EventDispatcher {
|
||||
@ -762,7 +761,7 @@ class TextScanner extends EventDispatcher {
|
||||
const searchText = this.getTextSourceContent(textSource, scanLength, layoutAwareScan);
|
||||
if (searchText.length === 0) { return null; }
|
||||
|
||||
const {definitions, length} = await api.termsFind(searchText, {}, optionsContext);
|
||||
const {definitions, length} = await yomichan.api.termsFind(searchText, {}, optionsContext);
|
||||
if (definitions.length === 0) { return null; }
|
||||
|
||||
textSource.setEndOffset(length, layoutAwareScan);
|
||||
@ -787,7 +786,7 @@ class TextScanner extends EventDispatcher {
|
||||
const searchText = this.getTextSourceContent(textSource, 1, layoutAwareScan);
|
||||
if (searchText.length === 0) { return null; }
|
||||
|
||||
const definitions = await api.kanjiFind(searchText, optionsContext);
|
||||
const definitions = await yomichan.api.kanjiFind(searchText, optionsContext);
|
||||
if (definitions.length === 0) { return null; }
|
||||
|
||||
textSource.setEndOffset(1, layoutAwareScan);
|
||||
|
@ -15,10 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* api
|
||||
*/
|
||||
|
||||
class MediaLoader {
|
||||
constructor() {
|
||||
this._token = {};
|
||||
@ -84,7 +80,7 @@ class MediaLoader {
|
||||
|
||||
async _getMediaData(path, dictionaryName, cachedData) {
|
||||
const token = this._token;
|
||||
const data = (await api.getMedia([{path, dictionaryName}]))[0];
|
||||
const data = (await yomichan.api.getMedia([{path, dictionaryName}]))[0];
|
||||
if (token === this._token && data !== null) {
|
||||
const contentArrayBuffer = this._base64ToArrayBuffer(data.content);
|
||||
const blob = new Blob([contentArrayBuffer], {type: data.mediaType});
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* HotkeyHelpController
|
||||
* PermissionsUtil
|
||||
* api
|
||||
*/
|
||||
|
||||
class DisplayController {
|
||||
@ -35,7 +34,7 @@ class DisplayController {
|
||||
this._setupButtonEvents('.action-open-search', 'openSearchPage', chrome.runtime.getURL('/search.html'));
|
||||
this._setupButtonEvents('.action-open-info', 'openInfoPage', chrome.runtime.getURL('/info.html'));
|
||||
|
||||
const optionsFull = await api.optionsGetFull();
|
||||
const optionsFull = await yomichan.api.optionsGetFull();
|
||||
this._optionsFull = optionsFull;
|
||||
|
||||
this._setupHotkeys();
|
||||
@ -74,12 +73,12 @@ class DisplayController {
|
||||
if (typeof command === 'string') {
|
||||
node.addEventListener('click', (e) => {
|
||||
if (e.button !== 0) { return; }
|
||||
api.commandExec(command, {mode: e.ctrlKey ? 'newTab' : 'existingOrNewTab'});
|
||||
yomichan.api.commandExec(command, {mode: e.ctrlKey ? 'newTab' : 'existingOrNewTab'});
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
node.addEventListener('auxclick', (e) => {
|
||||
if (e.button !== 1) { return; }
|
||||
api.commandExec(command, {mode: 'newTab'});
|
||||
yomichan.api.commandExec(command, {mode: 'newTab'});
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
}
|
||||
@ -130,7 +129,7 @@ class DisplayController {
|
||||
|
||||
_setupOptions({options}) {
|
||||
const extensionEnabled = options.general.enable;
|
||||
const onToggleChanged = () => api.commandExec('toggleTextScanning');
|
||||
const onToggleChanged = () => yomichan.api.commandExec('toggleTextScanning');
|
||||
for (const toggle of document.querySelectorAll('#enable-search,#enable-search2')) {
|
||||
toggle.checked = extensionEnabled;
|
||||
toggle.addEventListener('change', onToggleChanged, false);
|
||||
@ -178,7 +177,7 @@ class DisplayController {
|
||||
}
|
||||
|
||||
async _setPrimaryProfileIndex(value) {
|
||||
return await api.modifySettings(
|
||||
return await yomichan.api.modifySettings(
|
||||
[{
|
||||
action: 'set',
|
||||
path: 'profileCurrent',
|
||||
@ -190,7 +189,7 @@ class DisplayController {
|
||||
|
||||
async _updateDictionariesEnabledWarnings(options) {
|
||||
const noDictionariesEnabledWarnings = document.querySelectorAll('.no-dictionaries-enabled-warning');
|
||||
const dictionaries = await api.getDictionaryInfo();
|
||||
const dictionaries = await yomichan.api.getDictionaryInfo();
|
||||
|
||||
let enabledCount = 0;
|
||||
for (const {title} of dictionaries) {
|
||||
@ -221,10 +220,9 @@ class DisplayController {
|
||||
}
|
||||
|
||||
(async () => {
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
api.logIndicatorClear();
|
||||
yomichan.api.logIndicatorClear();
|
||||
|
||||
const displayController = new DisplayController();
|
||||
displayController.prepare();
|
||||
|
@ -19,7 +19,6 @@
|
||||
* BackupController
|
||||
* DocumentFocusController
|
||||
* SettingsController
|
||||
* api
|
||||
*/
|
||||
|
||||
function getBrowserDisplayName(browser) {
|
||||
@ -54,12 +53,11 @@ function getOperatingSystemDisplayName(os) {
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
const language = chrome.i18n.getUILanguage();
|
||||
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
const {userAgent} = navigator;
|
||||
const {name, version} = manifest;
|
||||
const {browser, platform: {os}} = await api.getEnvironmentInfo();
|
||||
const {browser, platform: {os}} = await yomichan.api.getEnvironmentInfo();
|
||||
|
||||
const thisVersionLink = document.querySelector('#release-notes-this-version-link');
|
||||
thisVersionLink.href = thisVersionLink.dataset.hrefFormat.replace(/\{version\}/g, version);
|
||||
@ -73,7 +71,7 @@ function getOperatingSystemDisplayName(os) {
|
||||
(async () => {
|
||||
let ankiConnectVersion = null;
|
||||
try {
|
||||
ankiConnectVersion = await api.getAnkiConnectVersion();
|
||||
ankiConnectVersion = await yomichan.api.getAnkiConnectVersion();
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
@ -86,7 +84,7 @@ function getOperatingSystemDisplayName(os) {
|
||||
(async () => {
|
||||
let dictionaryInfos;
|
||||
try {
|
||||
dictionaryInfos = await api.getDictionaryInfo();
|
||||
dictionaryInfos = await yomichan.api.getDictionaryInfo();
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
@ -19,12 +19,11 @@
|
||||
* DocumentFocusController
|
||||
* PermissionsToggleController
|
||||
* SettingsController
|
||||
* api
|
||||
*/
|
||||
|
||||
async function setupEnvironmentInfo() {
|
||||
const {manifest_version: manifestVersion} = chrome.runtime.getManifest();
|
||||
const {browser, platform} = await api.getEnvironmentInfo();
|
||||
const {browser, platform} = await yomichan.api.getEnvironmentInfo();
|
||||
document.documentElement.dataset.browser = browser;
|
||||
document.documentElement.dataset.os = platform.os;
|
||||
document.documentElement.dataset.manifestVersion = `${manifestVersion}`;
|
||||
@ -69,7 +68,6 @@ function setupPermissionsToggles() {
|
||||
node.textContent = chrome.runtime.getURL('/');
|
||||
}
|
||||
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
setupEnvironmentInfo();
|
||||
|
@ -25,12 +25,11 @@
|
||||
* SettingsController
|
||||
* SettingsDisplayController
|
||||
* StatusFooter
|
||||
* api
|
||||
*/
|
||||
|
||||
async function setupEnvironmentInfo() {
|
||||
const {manifest_version: manifestVersion} = chrome.runtime.getManifest();
|
||||
const {browser, platform} = await api.getEnvironmentInfo();
|
||||
const {browser, platform} = await yomichan.api.getEnvironmentInfo();
|
||||
document.documentElement.dataset.browser = browser;
|
||||
document.documentElement.dataset.os = platform.os;
|
||||
document.documentElement.dataset.manifestVersion = `${manifestVersion}`;
|
||||
@ -49,12 +48,11 @@ async function setupGenericSettingsController(genericSettingController) {
|
||||
const statusFooter = new StatusFooter(document.querySelector('.status-footer-container'));
|
||||
statusFooter.prepare();
|
||||
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
setupEnvironmentInfo();
|
||||
|
||||
const optionsFull = await api.optionsGetFull();
|
||||
const optionsFull = await yomichan.api.optionsGetFull();
|
||||
|
||||
const preparePromises = [];
|
||||
|
||||
|
@ -15,10 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* api
|
||||
*/
|
||||
|
||||
const dynamicLoader = (() => {
|
||||
const injectedStylesheets = new Map();
|
||||
const injectedStylesheetsWithParent = new WeakMap();
|
||||
@ -61,7 +57,7 @@ const dynamicLoader = (() => {
|
||||
}
|
||||
|
||||
if (type === 'file-content') {
|
||||
value = await api.getStylesheetContent(value);
|
||||
value = await yomichan.api.getStylesheetContent(value);
|
||||
type = 'code';
|
||||
useWebExtensionApi = false;
|
||||
}
|
||||
@ -73,7 +69,7 @@ const dynamicLoader = (() => {
|
||||
}
|
||||
|
||||
setInjectedStylesheet(id, parentNode, null);
|
||||
await api.injectStylesheet(type, value);
|
||||
await yomichan.api.injectStylesheet(type, value);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* AnkiNoteBuilder
|
||||
* api
|
||||
*/
|
||||
|
||||
class AnkiTemplatesController {
|
||||
@ -37,7 +36,7 @@ class AnkiTemplatesController {
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
this._defaultFieldTemplates = await api.getDefaultAnkiFieldTemplates();
|
||||
this._defaultFieldTemplates = await yomichan.api.getDefaultAnkiFieldTemplates();
|
||||
|
||||
this._fieldTemplatesTextarea = document.querySelector('#anki-card-templates-textarea');
|
||||
this._compileResultInfo = document.querySelector('#anki-card-templates-compile-result');
|
||||
@ -154,7 +153,7 @@ class AnkiTemplatesController {
|
||||
|
||||
async _getDefinition(text, optionsContext) {
|
||||
if (this._cachedDefinitionText !== text) {
|
||||
const {definitions} = await api.termsFind(text, {}, optionsContext);
|
||||
const {definitions} = await yomichan.api.termsFind(text, {}, optionsContext);
|
||||
if (definitions.length === 0) { return null; }
|
||||
|
||||
this._cachedDefinitionValue = definitions[0];
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* DictionaryController
|
||||
* OptionsUtil
|
||||
* api
|
||||
*/
|
||||
|
||||
class BackupController {
|
||||
@ -85,8 +84,8 @@ class BackupController {
|
||||
|
||||
async _getSettingsExportData(date) {
|
||||
const optionsFull = await this._settingsController.getOptionsFull();
|
||||
const environment = await api.getEnvironmentInfo();
|
||||
const fieldTemplatesDefault = await api.getDefaultAnkiFieldTemplates();
|
||||
const environment = await yomichan.api.getEnvironmentInfo();
|
||||
const fieldTemplatesDefault = await yomichan.api.getDefaultAnkiFieldTemplates();
|
||||
const permissions = await this._settingsController.permissionsUtil.getAllPermissions();
|
||||
|
||||
// Format options
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* DictionaryDatabase
|
||||
* ObjectPropertyAccessor
|
||||
* api
|
||||
*/
|
||||
|
||||
class DictionaryEntry {
|
||||
@ -362,7 +361,7 @@ class DictionaryController {
|
||||
|
||||
const token = this._databaseStateToken;
|
||||
const dictionaryTitles = this._dictionaries.map(({title}) => title);
|
||||
const {counts, total} = await api.getDictionaryCounts(dictionaryTitles, true);
|
||||
const {counts, total} = await yomichan.api.getDictionaryCounts(dictionaryTitles, true);
|
||||
if (this._databaseStateToken !== token) { return; }
|
||||
|
||||
for (let i = 0, ii = Math.min(counts.length, this._dictionaryEntries.length); i < ii; ++i) {
|
||||
@ -499,7 +498,7 @@ class DictionaryController {
|
||||
const dictionaryDatabase = await this._getPreparedDictionaryDatabase();
|
||||
try {
|
||||
await dictionaryDatabase.deleteDictionary(dictionaryTitle, {rate: 1000}, onProgress);
|
||||
api.triggerDatabaseUpdated('dictionary', 'delete');
|
||||
yomichan.api.triggerDatabaseUpdated('dictionary', 'delete');
|
||||
} finally {
|
||||
dictionaryDatabase.close();
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
* DictionaryDatabase
|
||||
* DictionaryImporter
|
||||
* ObjectPropertyAccessor
|
||||
* api
|
||||
*/
|
||||
|
||||
class DictionaryImportController {
|
||||
@ -102,7 +101,7 @@ class DictionaryImportController {
|
||||
this._setSpinnerVisible(true);
|
||||
if (purgeNotification !== null) { purgeNotification.hidden = false; }
|
||||
|
||||
await api.purgeDatabase();
|
||||
await yomichan.api.purgeDatabase();
|
||||
const errors = await this._clearDictionarySettings();
|
||||
|
||||
if (errors.length > 0) {
|
||||
@ -197,7 +196,7 @@ class DictionaryImportController {
|
||||
const dictionaryImporter = new DictionaryImporter();
|
||||
const archiveContent = await this._readFile(file);
|
||||
const {result, errors} = await dictionaryImporter.importDictionary(dictionaryDatabase, archiveContent, importDetails, onProgress);
|
||||
api.triggerDatabaseUpdated('dictionary', 'import');
|
||||
yomichan.api.triggerDatabaseUpdated('dictionary', 'import');
|
||||
const errors2 = await this._addDictionarySettings(result.sequenced, result.title);
|
||||
|
||||
if (errors.length > 0) {
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* HotkeyUtil
|
||||
* KeyboardMouseInputField
|
||||
* api
|
||||
*/
|
||||
|
||||
class ExtensionKeyboardShortcutController {
|
||||
@ -53,7 +52,7 @@ class ExtensionKeyboardShortcutController {
|
||||
this._clearButton.addEventListener('click', this._onClearClick.bind(this));
|
||||
}
|
||||
|
||||
const {platform: {os}} = await api.getEnvironmentInfo();
|
||||
const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
|
||||
this._os = os;
|
||||
this._hotkeyUtil.os = os;
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* KeyboardMouseInputField
|
||||
* api
|
||||
*/
|
||||
|
||||
class KeyboardShortcutController {
|
||||
@ -38,7 +37,7 @@ class KeyboardShortcutController {
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
const {platform: {os}} = await api.getEnvironmentInfo();
|
||||
const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
|
||||
this._os = os;
|
||||
|
||||
this._addButton = document.querySelector('#hotkey-list-add');
|
||||
|
@ -31,7 +31,6 @@
|
||||
* ScanInputsSimpleController
|
||||
* SettingsController
|
||||
* StorageController
|
||||
* api
|
||||
*/
|
||||
|
||||
function showExtensionInformation() {
|
||||
@ -43,7 +42,7 @@ function showExtensionInformation() {
|
||||
}
|
||||
|
||||
async function setupEnvironmentInfo() {
|
||||
const {browser, platform} = await api.getEnvironmentInfo();
|
||||
const {browser, platform} = await yomichan.api.getEnvironmentInfo();
|
||||
document.documentElement.dataset.browser = browser;
|
||||
document.documentElement.dataset.operatingSystem = platform.os;
|
||||
}
|
||||
@ -51,13 +50,12 @@ async function setupEnvironmentInfo() {
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
setupEnvironmentInfo();
|
||||
showExtensionInformation();
|
||||
|
||||
const optionsFull = await api.optionsGetFull();
|
||||
const optionsFull = await yomichan.api.optionsGetFull();
|
||||
|
||||
const modalController = new ModalController();
|
||||
modalController.prepare();
|
||||
|
@ -15,10 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* api
|
||||
*/
|
||||
|
||||
class MecabController {
|
||||
constructor(settingsController) {
|
||||
this._settingsController = settingsController;
|
||||
@ -49,7 +45,7 @@ class MecabController {
|
||||
this._testButton.disabled = true;
|
||||
this._resultsContainer.textContent = '';
|
||||
this._resultsContainer.hidden = true;
|
||||
await api.testMecab();
|
||||
await yomichan.api.testMecab();
|
||||
this._setStatus('Connection was successful', false);
|
||||
} catch (e) {
|
||||
this._setStatus(e.message, true);
|
||||
|
@ -17,12 +17,10 @@
|
||||
|
||||
/* global
|
||||
* DisplayGenerator
|
||||
* api
|
||||
*/
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
const displayGenerator = new DisplayGenerator({
|
||||
|
@ -19,15 +19,13 @@
|
||||
* HotkeyHandler
|
||||
* PopupFactory
|
||||
* PopupPreviewFrame
|
||||
* api
|
||||
*/
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
const {tabId, frameId} = await api.frameInformationGet();
|
||||
const {tabId, frameId} = await yomichan.api.frameInformationGet();
|
||||
|
||||
const hotkeyHandler = new HotkeyHandler();
|
||||
hotkeyHandler.prepare();
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* Frontend
|
||||
* TextSourceRange
|
||||
* api
|
||||
* wanakana
|
||||
*/
|
||||
|
||||
@ -63,8 +62,8 @@ class PopupPreviewFrame {
|
||||
this._exampleTextInput.addEventListener('input', this._onExampleTextInputInput.bind(this), false);
|
||||
|
||||
// Overwrite API functions
|
||||
this._apiOptionsGetOld = api.optionsGet.bind(api);
|
||||
api.optionsGet = this._apiOptionsGet.bind(this);
|
||||
this._apiOptionsGetOld = yomichan.api.optionsGet.bind(yomichan.api);
|
||||
yomichan.api.optionsGet = this._apiOptionsGet.bind(this);
|
||||
|
||||
// Overwrite frontend
|
||||
this._frontend = new Frontend({
|
||||
|
@ -15,10 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* api
|
||||
*/
|
||||
|
||||
class PopupWindowController {
|
||||
prepare() {
|
||||
const testLink = document.querySelector('#test-window-open-link');
|
||||
@ -33,6 +29,6 @@ class PopupWindowController {
|
||||
}
|
||||
|
||||
async _testWindowOpen() {
|
||||
await api.getOrCreateSearchPopup({focus: true});
|
||||
await yomichan.api.getOrCreateSearchPopup({focus: true});
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* ProfileConditionsUI
|
||||
* api
|
||||
*/
|
||||
|
||||
class ProfileController {
|
||||
@ -58,7 +57,7 @@ class ProfileController {
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
const {platform: {os}} = await api.getEnvironmentInfo();
|
||||
const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
|
||||
this._profileConditionsUI.os = os;
|
||||
|
||||
this._profileActiveSelect = document.querySelector('#profile-active-select');
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
/* global
|
||||
* KeyboardMouseInputField
|
||||
* api
|
||||
*/
|
||||
|
||||
class ScanInputsController {
|
||||
@ -31,7 +30,7 @@ class ScanInputsController {
|
||||
}
|
||||
|
||||
async prepare() {
|
||||
const {platform: {os}} = await api.getEnvironmentInfo();
|
||||
const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
|
||||
this._os = os;
|
||||
|
||||
this._container = document.querySelector('#scan-input-list');
|
||||
|
@ -18,7 +18,6 @@
|
||||
/* global
|
||||
* HotkeyUtil
|
||||
* ScanInputsController
|
||||
* api
|
||||
*/
|
||||
|
||||
class ScanInputsSimpleController {
|
||||
@ -34,7 +33,7 @@ class ScanInputsSimpleController {
|
||||
this._middleMouseButtonScan = document.querySelector('#middle-mouse-button-scan');
|
||||
this._mainScanModifierKeyInput = document.querySelector('#main-scan-modifier-key');
|
||||
|
||||
const {platform: {os}} = await api.getEnvironmentInfo();
|
||||
const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
|
||||
this._hotkeyUtil.os = os;
|
||||
|
||||
this._mainScanModifierKeyInputHasOther = false;
|
||||
|
@ -19,7 +19,6 @@
|
||||
* HtmlTemplateCollection
|
||||
* OptionsUtil
|
||||
* PermissionsUtil
|
||||
* api
|
||||
*/
|
||||
|
||||
class SettingsController extends EventDispatcher {
|
||||
@ -62,16 +61,16 @@ class SettingsController extends EventDispatcher {
|
||||
|
||||
async getOptions() {
|
||||
const optionsContext = this.getOptionsContext();
|
||||
return await api.optionsGet(optionsContext);
|
||||
return await yomichan.api.optionsGet(optionsContext);
|
||||
}
|
||||
|
||||
async getOptionsFull() {
|
||||
return await api.optionsGetFull();
|
||||
return await yomichan.api.optionsGetFull();
|
||||
}
|
||||
|
||||
async setAllSettings(value) {
|
||||
const profileIndex = value.profileCurrent;
|
||||
await api.setAllSettings(value, this._source);
|
||||
await yomichan.api.setAllSettings(value, this._source);
|
||||
this._setProfileIndex(profileIndex);
|
||||
}
|
||||
|
||||
@ -108,7 +107,7 @@ class SettingsController extends EventDispatcher {
|
||||
}
|
||||
|
||||
async getDictionaryInfo() {
|
||||
return await api.getDictionaryInfo();
|
||||
return await yomichan.api.getDictionaryInfo();
|
||||
}
|
||||
|
||||
getOptionsContext() {
|
||||
@ -171,12 +170,12 @@ class SettingsController extends EventDispatcher {
|
||||
|
||||
async _getSettings(targets, extraFields) {
|
||||
targets = this._setupTargets(targets, extraFields);
|
||||
return await api.getSettings(targets);
|
||||
return await yomichan.api.getSettings(targets);
|
||||
}
|
||||
|
||||
async _modifySettings(targets, extraFields) {
|
||||
targets = this._setupTargets(targets, extraFields);
|
||||
return await api.modifySettings(targets, this._source);
|
||||
return await yomichan.api.modifySettings(targets, this._source);
|
||||
}
|
||||
|
||||
_onBeforeUnload(e) {
|
||||
|
@ -42,12 +42,11 @@
|
||||
* StatusFooter
|
||||
* StorageController
|
||||
* TranslationTextReplacementsController
|
||||
* api
|
||||
*/
|
||||
|
||||
async function setupEnvironmentInfo() {
|
||||
const {manifest_version: manifestVersion} = chrome.runtime.getManifest();
|
||||
const {browser, platform} = await api.getEnvironmentInfo();
|
||||
const {browser, platform} = await yomichan.api.getEnvironmentInfo();
|
||||
document.documentElement.dataset.browser = browser;
|
||||
document.documentElement.dataset.os = platform.os;
|
||||
document.documentElement.dataset.manifestVersion = `${manifestVersion}`;
|
||||
@ -66,12 +65,11 @@ async function setupGenericSettingsController(genericSettingController) {
|
||||
const statusFooter = new StatusFooter(document.querySelector('.status-footer-container'));
|
||||
statusFooter.prepare();
|
||||
|
||||
api.prepare();
|
||||
await yomichan.prepare();
|
||||
|
||||
setupEnvironmentInfo();
|
||||
|
||||
const optionsFull = await api.optionsGetFull();
|
||||
const optionsFull = await yomichan.api.optionsGetFull();
|
||||
|
||||
const preparePromises = [];
|
||||
|
||||
|
@ -28,6 +28,6 @@
|
||||
templateRenderer.registerDataType('ankiNote', {
|
||||
modifier: ({data, marker}) => new AnkiNoteData(data, marker).createPublic()
|
||||
});
|
||||
const api = new TemplateRendererFrameApi(templateRenderer);
|
||||
api.prepare();
|
||||
const templateRendererFrameApi = new TemplateRendererFrameApi(templateRenderer);
|
||||
templateRendererFrameApi.prepare();
|
||||
})();
|
||||
|
@ -15,6 +15,11 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* global
|
||||
* API
|
||||
* CrossFrameAPI
|
||||
*/
|
||||
|
||||
// Set up chrome alias if it's not available (Edge Legacy)
|
||||
if ((() => {
|
||||
let hasChrome = false;
|
||||
@ -34,271 +39,300 @@ if ((() => {
|
||||
chrome = browser;
|
||||
}
|
||||
|
||||
const yomichan = (() => {
|
||||
class Yomichan extends EventDispatcher {
|
||||
constructor() {
|
||||
super();
|
||||
class Yomichan extends EventDispatcher {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._extensionName = 'Yomichan';
|
||||
try {
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
this._extensionName = `${manifest.name} v${manifest.version}`;
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
this._isExtensionUnloaded = false;
|
||||
this._isTriggeringExtensionUnloaded = false;
|
||||
this._isReady = false;
|
||||
|
||||
const {promise, resolve} = deferPromise();
|
||||
this._isBackendReadyPromise = promise;
|
||||
this._isBackendReadyPromiseResolve = resolve;
|
||||
|
||||
this._messageHandlers = new Map([
|
||||
['isReady', {async: false, handler: this._onMessageIsReady.bind(this)}],
|
||||
['backendReady', {async: false, handler: this._onMessageBackendReady.bind(this)}],
|
||||
['getUrl', {async: false, handler: this._onMessageGetUrl.bind(this)}],
|
||||
['optionsUpdated', {async: false, handler: this._onMessageOptionsUpdated.bind(this)}],
|
||||
['databaseUpdated', {async: false, handler: this._onMessageDatabaseUpdated.bind(this)}],
|
||||
['zoomChanged', {async: false, handler: this._onMessageZoomChanged.bind(this)}]
|
||||
]);
|
||||
this._extensionName = 'Yomichan';
|
||||
try {
|
||||
const manifest = chrome.runtime.getManifest();
|
||||
this._extensionName = `${manifest.name} v${manifest.version}`;
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
// Public
|
||||
this._isBackground = null;
|
||||
this._api = null;
|
||||
this._crossFrame = null;
|
||||
this._isExtensionUnloaded = false;
|
||||
this._isTriggeringExtensionUnloaded = false;
|
||||
this._isReady = false;
|
||||
|
||||
get isExtensionUnloaded() {
|
||||
return this._isExtensionUnloaded;
|
||||
}
|
||||
const {promise, resolve} = deferPromise();
|
||||
this._isBackendReadyPromise = promise;
|
||||
this._isBackendReadyPromiseResolve = resolve;
|
||||
|
||||
async prepare(isBackground=false) {
|
||||
chrome.runtime.onMessage.addListener(this._onMessage.bind(this));
|
||||
this._messageHandlers = new Map([
|
||||
['isReady', {async: false, handler: this._onMessageIsReady.bind(this)}],
|
||||
['backendReady', {async: false, handler: this._onMessageBackendReady.bind(this)}],
|
||||
['getUrl', {async: false, handler: this._onMessageGetUrl.bind(this)}],
|
||||
['optionsUpdated', {async: false, handler: this._onMessageOptionsUpdated.bind(this)}],
|
||||
['databaseUpdated', {async: false, handler: this._onMessageDatabaseUpdated.bind(this)}],
|
||||
['zoomChanged', {async: false, handler: this._onMessageZoomChanged.bind(this)}]
|
||||
]);
|
||||
}
|
||||
|
||||
if (!isBackground) {
|
||||
this.sendMessage({action: 'requestBackendReadySignal'});
|
||||
await this._isBackendReadyPromise;
|
||||
}
|
||||
}
|
||||
// Public
|
||||
|
||||
ready() {
|
||||
this._isReady = true;
|
||||
this.sendMessage({action: 'yomichanReady'});
|
||||
}
|
||||
get isBackground() {
|
||||
return this._isBackground;
|
||||
}
|
||||
|
||||
isExtensionUrl(url) {
|
||||
try {
|
||||
return url.startsWith(chrome.runtime.getURL('/'));
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
get isExtensionUnloaded() {
|
||||
return this._isExtensionUnloaded;
|
||||
}
|
||||
|
||||
getTemporaryListenerResult(eventHandler, userCallback, timeout=null) {
|
||||
if (!(
|
||||
typeof eventHandler.addListener === 'function' &&
|
||||
typeof eventHandler.removeListener === 'function'
|
||||
)) {
|
||||
throw new Error('Event handler type not supported');
|
||||
}
|
||||
get api() {
|
||||
return this._api;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const runtimeMessageCallback = ({action, params}, sender, sendResponse) => {
|
||||
let timeoutId = null;
|
||||
if (timeout !== null) {
|
||||
timeoutId = setTimeout(() => {
|
||||
timeoutId = null;
|
||||
eventHandler.removeListener(runtimeMessageCallback);
|
||||
reject(new Error(`Listener timed out in ${timeout} ms`));
|
||||
}, timeout);
|
||||
}
|
||||
get crossFrame() {
|
||||
return this._crossFrame;
|
||||
}
|
||||
|
||||
const cleanupResolve = (value) => {
|
||||
if (timeoutId !== null) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
}
|
||||
eventHandler.removeListener(runtimeMessageCallback);
|
||||
sendResponse();
|
||||
resolve(value);
|
||||
};
|
||||
async prepare(isBackground=false) {
|
||||
this._isBackground = isBackground;
|
||||
chrome.runtime.onMessage.addListener(this._onMessage.bind(this));
|
||||
|
||||
userCallback({action, params}, {resolve: cleanupResolve, sender});
|
||||
};
|
||||
if (!isBackground) {
|
||||
this._api = new API(this);
|
||||
|
||||
eventHandler.addListener(runtimeMessageCallback);
|
||||
});
|
||||
}
|
||||
this._crossFrame = new CrossFrameAPI();
|
||||
this._crossFrame.prepare();
|
||||
|
||||
logWarning(error) {
|
||||
this.log(error, 'warn');
|
||||
}
|
||||
this.sendMessage({action: 'requestBackendReadySignal'});
|
||||
await this._isBackendReadyPromise;
|
||||
|
||||
logError(error) {
|
||||
this.log(error, 'error');
|
||||
}
|
||||
|
||||
log(error, level, context=null) {
|
||||
if (!isObject(context)) {
|
||||
context = this._getLogContext();
|
||||
}
|
||||
|
||||
let errorString;
|
||||
try {
|
||||
errorString = error.toString();
|
||||
if (/^\[object \w+\]$/.test(errorString)) {
|
||||
errorString = JSON.stringify(error);
|
||||
}
|
||||
} catch (e) {
|
||||
errorString = `${error}`;
|
||||
}
|
||||
|
||||
let errorStack;
|
||||
try {
|
||||
errorStack = (typeof error.stack === 'string' ? error.stack.trimRight() : '');
|
||||
} catch (e) {
|
||||
errorStack = '';
|
||||
}
|
||||
|
||||
let errorData;
|
||||
try {
|
||||
errorData = error.data;
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
if (errorStack.startsWith(errorString)) {
|
||||
errorString = errorStack;
|
||||
} else if (errorStack.length > 0) {
|
||||
errorString += `\n${errorStack}`;
|
||||
}
|
||||
|
||||
let message = `${this._extensionName} has encountered a problem.`;
|
||||
message += `\nOriginating URL: ${context.url}\n`;
|
||||
message += errorString;
|
||||
if (typeof errorData !== 'undefined') {
|
||||
message += `\nData: ${JSON.stringify(errorData, null, 4)}`;
|
||||
}
|
||||
message += '\n\nIssues can be reported at https://github.com/FooSoft/yomichan/issues';
|
||||
|
||||
switch (level) {
|
||||
case 'info': console.info(message); break;
|
||||
case 'debug': console.debug(message); break;
|
||||
case 'warn': console.warn(message); break;
|
||||
case 'error': console.error(message); break;
|
||||
default: console.log(message); break;
|
||||
}
|
||||
|
||||
this.trigger('log', {error, level, context});
|
||||
}
|
||||
|
||||
sendMessage(...args) {
|
||||
try {
|
||||
return chrome.runtime.sendMessage(...args);
|
||||
} catch (e) {
|
||||
this.triggerExtensionUnloaded();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
connect(...args) {
|
||||
try {
|
||||
return chrome.runtime.connect(...args);
|
||||
} catch (e) {
|
||||
this.triggerExtensionUnloaded();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
getMessageResponseResult(response) {
|
||||
let error = chrome.runtime.lastError;
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
if (!isObject(response)) {
|
||||
throw new Error('Tab did not respond');
|
||||
}
|
||||
error = response.error;
|
||||
if (error) {
|
||||
throw deserializeError(error);
|
||||
}
|
||||
return response.result;
|
||||
}
|
||||
|
||||
invokeMessageHandler({handler, async}, params, callback, ...extraArgs) {
|
||||
try {
|
||||
let promiseOrResult = handler(params, ...extraArgs);
|
||||
if (async === 'dynamic') {
|
||||
({async, result: promiseOrResult} = promiseOrResult);
|
||||
}
|
||||
if (async) {
|
||||
promiseOrResult.then(
|
||||
(result) => { callback({result}); },
|
||||
(error) => { callback({error: serializeError(error)}); }
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
callback({result: promiseOrResult});
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
callback({error: serializeError(error)});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
triggerExtensionUnloaded() {
|
||||
this._isExtensionUnloaded = true;
|
||||
if (this._isTriggeringExtensionUnloaded) { return; }
|
||||
try {
|
||||
this._isTriggeringExtensionUnloaded = true;
|
||||
this.trigger('extensionUnloaded');
|
||||
} finally {
|
||||
this._isTriggeringExtensionUnloaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getUrl() {
|
||||
return location.href;
|
||||
}
|
||||
|
||||
_getLogContext() {
|
||||
return {url: this._getUrl()};
|
||||
}
|
||||
|
||||
_onMessage({action, params}, sender, callback) {
|
||||
const messageHandler = this._messageHandlers.get(action);
|
||||
if (typeof messageHandler === 'undefined') { return false; }
|
||||
return this.invokeMessageHandler(messageHandler, params, callback, sender);
|
||||
}
|
||||
|
||||
_onMessageIsReady() {
|
||||
return this._isReady;
|
||||
}
|
||||
|
||||
_onMessageBackendReady() {
|
||||
if (this._isBackendReadyPromiseResolve === null) { return; }
|
||||
this._isBackendReadyPromiseResolve();
|
||||
this._isBackendReadyPromiseResolve = null;
|
||||
}
|
||||
|
||||
_onMessageGetUrl() {
|
||||
return {url: this._getUrl()};
|
||||
}
|
||||
|
||||
_onMessageOptionsUpdated({source}) {
|
||||
this.trigger('optionsUpdated', {source});
|
||||
}
|
||||
|
||||
_onMessageDatabaseUpdated({type, cause}) {
|
||||
this.trigger('databaseUpdated', {type, cause});
|
||||
}
|
||||
|
||||
_onMessageZoomChanged({oldZoomFactor, newZoomFactor}) {
|
||||
this.trigger('zoomChanged', {oldZoomFactor, newZoomFactor});
|
||||
this.on('log', this._onForwardLog.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
return new Yomichan();
|
||||
})();
|
||||
ready() {
|
||||
this._isReady = true;
|
||||
this.sendMessage({action: 'yomichanReady'});
|
||||
}
|
||||
|
||||
isExtensionUrl(url) {
|
||||
try {
|
||||
return url.startsWith(chrome.runtime.getURL('/'));
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getTemporaryListenerResult(eventHandler, userCallback, timeout=null) {
|
||||
if (!(
|
||||
typeof eventHandler.addListener === 'function' &&
|
||||
typeof eventHandler.removeListener === 'function'
|
||||
)) {
|
||||
throw new Error('Event handler type not supported');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const runtimeMessageCallback = ({action, params}, sender, sendResponse) => {
|
||||
let timeoutId = null;
|
||||
if (timeout !== null) {
|
||||
timeoutId = setTimeout(() => {
|
||||
timeoutId = null;
|
||||
eventHandler.removeListener(runtimeMessageCallback);
|
||||
reject(new Error(`Listener timed out in ${timeout} ms`));
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
const cleanupResolve = (value) => {
|
||||
if (timeoutId !== null) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
}
|
||||
eventHandler.removeListener(runtimeMessageCallback);
|
||||
sendResponse();
|
||||
resolve(value);
|
||||
};
|
||||
|
||||
userCallback({action, params}, {resolve: cleanupResolve, sender});
|
||||
};
|
||||
|
||||
eventHandler.addListener(runtimeMessageCallback);
|
||||
});
|
||||
}
|
||||
|
||||
logWarning(error) {
|
||||
this.log(error, 'warn');
|
||||
}
|
||||
|
||||
logError(error) {
|
||||
this.log(error, 'error');
|
||||
}
|
||||
|
||||
log(error, level, context=null) {
|
||||
if (!isObject(context)) {
|
||||
context = this._getLogContext();
|
||||
}
|
||||
|
||||
let errorString;
|
||||
try {
|
||||
errorString = error.toString();
|
||||
if (/^\[object \w+\]$/.test(errorString)) {
|
||||
errorString = JSON.stringify(error);
|
||||
}
|
||||
} catch (e) {
|
||||
errorString = `${error}`;
|
||||
}
|
||||
|
||||
let errorStack;
|
||||
try {
|
||||
errorStack = (typeof error.stack === 'string' ? error.stack.trimRight() : '');
|
||||
} catch (e) {
|
||||
errorStack = '';
|
||||
}
|
||||
|
||||
let errorData;
|
||||
try {
|
||||
errorData = error.data;
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
if (errorStack.startsWith(errorString)) {
|
||||
errorString = errorStack;
|
||||
} else if (errorStack.length > 0) {
|
||||
errorString += `\n${errorStack}`;
|
||||
}
|
||||
|
||||
let message = `${this._extensionName} has encountered a problem.`;
|
||||
message += `\nOriginating URL: ${context.url}\n`;
|
||||
message += errorString;
|
||||
if (typeof errorData !== 'undefined') {
|
||||
message += `\nData: ${JSON.stringify(errorData, null, 4)}`;
|
||||
}
|
||||
message += '\n\nIssues can be reported at https://github.com/FooSoft/yomichan/issues';
|
||||
|
||||
switch (level) {
|
||||
case 'info': console.info(message); break;
|
||||
case 'debug': console.debug(message); break;
|
||||
case 'warn': console.warn(message); break;
|
||||
case 'error': console.error(message); break;
|
||||
default: console.log(message); break;
|
||||
}
|
||||
|
||||
this.trigger('log', {error, level, context});
|
||||
}
|
||||
|
||||
sendMessage(...args) {
|
||||
try {
|
||||
return chrome.runtime.sendMessage(...args);
|
||||
} catch (e) {
|
||||
this.triggerExtensionUnloaded();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
connect(...args) {
|
||||
try {
|
||||
return chrome.runtime.connect(...args);
|
||||
} catch (e) {
|
||||
this.triggerExtensionUnloaded();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
getMessageResponseResult(response) {
|
||||
let error = chrome.runtime.lastError;
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
if (!isObject(response)) {
|
||||
throw new Error('Tab did not respond');
|
||||
}
|
||||
error = response.error;
|
||||
if (error) {
|
||||
throw deserializeError(error);
|
||||
}
|
||||
return response.result;
|
||||
}
|
||||
|
||||
invokeMessageHandler({handler, async}, params, callback, ...extraArgs) {
|
||||
try {
|
||||
let promiseOrResult = handler(params, ...extraArgs);
|
||||
if (async === 'dynamic') {
|
||||
({async, result: promiseOrResult} = promiseOrResult);
|
||||
}
|
||||
if (async) {
|
||||
promiseOrResult.then(
|
||||
(result) => { callback({result}); },
|
||||
(error) => { callback({error: serializeError(error)}); }
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
callback({result: promiseOrResult});
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
callback({error: serializeError(error)});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
triggerExtensionUnloaded() {
|
||||
this._isExtensionUnloaded = true;
|
||||
if (this._isTriggeringExtensionUnloaded) { return; }
|
||||
try {
|
||||
this._isTriggeringExtensionUnloaded = true;
|
||||
this.trigger('extensionUnloaded');
|
||||
} finally {
|
||||
this._isTriggeringExtensionUnloaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getUrl() {
|
||||
return location.href;
|
||||
}
|
||||
|
||||
_getLogContext() {
|
||||
return {url: this._getUrl()};
|
||||
}
|
||||
|
||||
_onMessage({action, params}, sender, callback) {
|
||||
const messageHandler = this._messageHandlers.get(action);
|
||||
if (typeof messageHandler === 'undefined') { return false; }
|
||||
return this.invokeMessageHandler(messageHandler, params, callback, sender);
|
||||
}
|
||||
|
||||
_onMessageIsReady() {
|
||||
return this._isReady;
|
||||
}
|
||||
|
||||
_onMessageBackendReady() {
|
||||
if (this._isBackendReadyPromiseResolve === null) { return; }
|
||||
this._isBackendReadyPromiseResolve();
|
||||
this._isBackendReadyPromiseResolve = null;
|
||||
}
|
||||
|
||||
_onMessageGetUrl() {
|
||||
return {url: this._getUrl()};
|
||||
}
|
||||
|
||||
_onMessageOptionsUpdated({source}) {
|
||||
this.trigger('optionsUpdated', {source});
|
||||
}
|
||||
|
||||
_onMessageDatabaseUpdated({type, cause}) {
|
||||
this.trigger('databaseUpdated', {type, cause});
|
||||
}
|
||||
|
||||
_onMessageZoomChanged({oldZoomFactor, newZoomFactor}) {
|
||||
this.trigger('zoomChanged', {oldZoomFactor, newZoomFactor});
|
||||
}
|
||||
|
||||
async _onForwardLog({error, level, context}) {
|
||||
try {
|
||||
await this._api.log(serializeError(error), level, context);
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const yomichan = new Yomichan();
|
||||
|
Loading…
Reference in New Issue
Block a user