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