summaryrefslogtreecommitdiff
path: root/ext/js
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2021-02-14 15:53:35 -0500
committerGitHub <noreply@github.com>2021-02-14 15:53:35 -0500
commit286534e648af350d24fbf3c7892a7ec81aaeb4bd (patch)
tree89d88e961c5a0a6f508c66789e30b9ba4a968e73 /ext/js
parentefe8140f103179f50b610f182148b9427af99010 (diff)
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
Diffstat (limited to 'ext/js')
-rw-r--r--ext/js/app/content-script-main.js4
-rw-r--r--ext/js/app/frontend.js17
-rw-r--r--ext/js/app/popup-factory.js5
-rw-r--r--ext/js/app/popup-proxy.js6
-rw-r--r--ext/js/app/popup-window.js12
-rw-r--r--ext/js/app/popup.js5
-rw-r--r--ext/js/comm/api.js502
-rw-r--r--ext/js/comm/frame-ancestry-handler.js10
-rw-r--r--ext/js/comm/frame-endpoint.js8
-rw-r--r--ext/js/comm/frame-offset-forwarder.js5
-rw-r--r--ext/js/display/display-audio.js3
-rw-r--r--ext/js/display/display-generator.js3
-rw-r--r--ext/js/display/display-profile-selection.js5
-rw-r--r--ext/js/display/display.js31
-rw-r--r--ext/js/display/popup-main.js4
-rw-r--r--ext/js/display/query-parser.js5
-rw-r--r--ext/js/display/search-display-controller.js7
-rw-r--r--ext/js/display/search-main.js4
-rw-r--r--ext/js/input/hotkey-handler.js5
-rw-r--r--ext/js/input/hotkey-help-controller.js3
-rw-r--r--ext/js/language/text-scanner.js5
-rw-r--r--ext/js/media/media-loader.js6
-rw-r--r--ext/js/pages/action-popup-main.js16
-rw-r--r--ext/js/pages/info-main.js8
-rw-r--r--ext/js/pages/permissions-main.js4
-rw-r--r--ext/js/pages/welcome-main.js6
-rw-r--r--ext/js/script/dynamic-loader.js8
-rw-r--r--ext/js/settings/anki-templates-controller.js5
-rw-r--r--ext/js/settings/backup-controller.js5
-rw-r--r--ext/js/settings/dictionary-controller.js5
-rw-r--r--ext/js/settings/dictionary-import-controller.js5
-rw-r--r--ext/js/settings/extension-keyboard-shortcuts-controller.js3
-rw-r--r--ext/js/settings/keyboard-shortcuts-controller.js3
-rw-r--r--ext/js/settings/main.js6
-rw-r--r--ext/js/settings/mecab-controller.js6
-rw-r--r--ext/js/settings/pitch-accents-preview-main.js2
-rw-r--r--ext/js/settings/popup-preview-frame-main.js4
-rw-r--r--ext/js/settings/popup-preview-frame.js5
-rw-r--r--ext/js/settings/popup-window-controller.js6
-rw-r--r--ext/js/settings/profile-controller.js3
-rw-r--r--ext/js/settings/scan-inputs-controller.js3
-rw-r--r--ext/js/settings/scan-inputs-simple-controller.js3
-rw-r--r--ext/js/settings/settings-controller.js13
-rw-r--r--ext/js/settings/settings-main.js6
-rw-r--r--ext/js/templates/template-renderer-frame-main.js4
-rw-r--r--ext/js/yomichan.js460
46 files changed, 586 insertions, 658 deletions
diff --git a/ext/js/app/content-script-main.js b/ext/js/app/content-script-main.js
index ee05034e..a09e52ea 100644
--- a/ext/js/app/content-script-main.js
+++ b/ext/js/app/content-script-main.js
@@ -19,15 +19,13 @@
* Frontend
* HotkeyHandler
* PopupFactory
- * api
*/
(async () => {
try {
- api.prepare();
await yomichan.prepare();
- const {tabId, frameId} = await api.frameInformationGet();
+ const {tabId, frameId} = await yomichan.api.frameInformationGet();
if (typeof frameId !== 'number') {
throw new Error('Failed to get frameId');
}
diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js
index a62b06bf..74cc63d2 100644
--- a/ext/js/app/frontend.js
+++ b/ext/js/app/frontend.js
@@ -20,7 +20,6 @@
* TextScanner
* TextSourceElement
* TextSourceRange
- * api
*/
class Frontend {
@@ -99,7 +98,7 @@ class Frontend {
async prepare() {
await this.updateOptions();
try {
- const {zoomFactor} = await api.getZoom();
+ const {zoomFactor} = await yomichan.api.getZoom();
this._pageZoomFactor = zoomFactor;
} catch (e) {
// Ignore exceptions which may occur due to being on an unsupported page (e.g. about:blank)
@@ -124,7 +123,7 @@ class Frontend {
this._textScanner.on('clearSelection', this._onClearSelection.bind(this));
this._textScanner.on('searched', this._onSearched.bind(this));
- api.crossFrame.registerHandlers([
+ yomichan.crossFrame.registerHandlers([
['closePopup', {async: false, handler: this._onApiClosePopup.bind(this)}],
['copySelection', {async: false, handler: this._onApiCopySelection.bind(this)}],
['getSelectionText', {async: false, handler: this._onApiGetSelectionText.bind(this)}],
@@ -332,7 +331,7 @@ class Frontend {
async _updateOptionsInternal() {
const optionsContext = await this._getOptionsContext();
- const options = await api.optionsGet(optionsContext);
+ const options = await yomichan.api.optionsGet(optionsContext);
const {scanning: scanningOptions, sentenceParsing: sentenceParsingOptions} = options;
this._options = options;
@@ -462,7 +461,7 @@ class Frontend {
return await this._getDefaultPopup();
}
- const {popupId} = await api.crossFrame.invoke(targetFrameId, 'getPopupInfo');
+ const {popupId} = await yomichan.crossFrame.invoke(targetFrameId, 'getPopupInfo');
if (popupId === null) {
return null;
}
@@ -608,9 +607,9 @@ class Frontend {
_signalFrontendReady(targetFrameId=null) {
const params = {frameId: this._frameId};
if (targetFrameId === null) {
- api.broadcastTab('frontendReady', params);
+ yomichan.api.broadcastTab('frontendReady', params);
} else {
- api.sendMessageToFrame(targetFrameId, 'frontendReady', params);
+ yomichan.api.sendMessageToFrame(targetFrameId, 'frontendReady', params);
}
}
@@ -627,7 +626,7 @@ class Frontend {
},
10000
);
- api.broadcastTab('requestFrontendReadyBroadcast', {frameId: this._frameId});
+ yomichan.api.broadcastTab('requestFrontendReadyBroadcast', {frameId: this._frameId});
await promise;
}
@@ -653,7 +652,7 @@ class Frontend {
let documentTitle = document.title;
if (this._useProxyPopup) {
try {
- ({url, documentTitle} = await api.crossFrame.invoke(this._parentFrameId, 'getPageInfo', {}));
+ ({url, documentTitle} = await yomichan.crossFrame.invoke(this._parentFrameId, 'getPageInfo', {}));
} catch (e) {
// NOP
}
diff --git a/ext/js/app/popup-factory.js b/ext/js/app/popup-factory.js
index 7571d7ab..8f0c2a6e 100644
--- a/ext/js/app/popup-factory.js
+++ b/ext/js/app/popup-factory.js
@@ -20,7 +20,6 @@
* Popup
* PopupProxy
* PopupWindow
- * api
*/
class PopupFactory {
@@ -35,7 +34,7 @@ class PopupFactory {
prepare() {
this._frameOffsetForwarder.prepare();
- api.crossFrame.registerHandlers([
+ yomichan.crossFrame.registerHandlers([
['getOrCreatePopup', {async: true, handler: this._onApiGetOrCreatePopup.bind(this)}],
['setOptionsContext', {async: true, handler: this._onApiSetOptionsContext.bind(this)}],
['hide', {async: false, handler: this._onApiHide.bind(this)}],
@@ -132,7 +131,7 @@ class PopupFactory {
throw new Error('Invalid frameId');
}
const useFrameOffsetForwarder = (parentPopupId === null);
- ({id, depth, frameId} = await api.crossFrame.invoke(frameId, 'getOrCreatePopup', {
+ ({id, depth, frameId} = await yomichan.crossFrame.invoke(frameId, 'getOrCreatePopup', {
id,
parentPopupId,
frameId,
diff --git a/ext/js/app/popup-proxy.js b/ext/js/app/popup-proxy.js
index b2e81824..19856e3f 100644
--- a/ext/js/app/popup-proxy.js
+++ b/ext/js/app/popup-proxy.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
class PopupProxy extends EventDispatcher {
constructor({
id,
@@ -158,7 +154,7 @@ class PopupProxy extends EventDispatcher {
// Private
_invoke(action, params={}) {
- return api.crossFrame.invoke(this._frameId, action, params);
+ return yomichan.crossFrame.invoke(this._frameId, action, params);
}
async _invokeSafe(action, params={}, defaultReturnValue) {
diff --git a/ext/js/app/popup-window.js b/ext/js/app/popup-window.js
index 5fa0c647..d0826775 100644
--- a/ext/js/app/popup-window.js
+++ b/ext/js/app/popup-window.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
class PopupWindow extends EventDispatcher {
constructor({
id,
@@ -82,7 +78,7 @@ class PopupWindow extends EventDispatcher {
}
async isVisible() {
- return (this._popupTabId !== null && await api.isTabSearchPopup(this._popupTabId));
+ return (this._popupTabId !== null && await yomichan.api.isTabSearchPopup(this._popupTabId));
}
async setVisibleOverride(_value, _priority) {
@@ -148,7 +144,7 @@ class PopupWindow extends EventDispatcher {
const frameId = 0;
if (this._popupTabId !== null) {
try {
- return await api.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
+ return await yomichan.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
} catch (e) {
if (yomichan.isExtensionUnloaded) {
open = false;
@@ -161,9 +157,9 @@ class PopupWindow extends EventDispatcher {
return defaultReturnValue;
}
- const {tabId} = await api.getOrCreateSearchPopup({focus: 'ifCreated'});
+ const {tabId} = await yomichan.api.getOrCreateSearchPopup({focus: 'ifCreated'});
this._popupTabId = tabId;
- return await api.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
+ return await yomichan.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
}
}
diff --git a/ext/js/app/popup.js b/ext/js/app/popup.js
index 75b74257..44cca14a 100644
--- a/ext/js/app/popup.js
+++ b/ext/js/app/popup.js
@@ -18,7 +18,6 @@
/* global
* DocumentUtil
* FrameClient
- * api
* dynamicLoader
*/
@@ -460,7 +459,7 @@ class Popup extends EventDispatcher {
if (this._frameClient === null || !this._frameClient.isConnected() || contentWindow === null) { return; }
const message = this._frameClient.createMessage({action, params});
- return await api.crossFrame.invoke(this._frameClient.frameId, 'popupMessage', message);
+ return await yomichan.crossFrame.invoke(this._frameClient.frameId, 'popupMessage', message);
}
async _invokeSafe(action, params={}, defaultReturnValue) {
@@ -676,7 +675,7 @@ class Popup extends EventDispatcher {
async _setOptionsContext(optionsContext) {
this._optionsContext = optionsContext;
- this._options = await api.optionsGet(optionsContext);
+ this._options = await yomichan.api.optionsGet(optionsContext);
this.updateTheme();
}
diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js
index 26397d1f..472e464b 100644
--- a/ext/js/comm/api.js
+++ b/ext/js/comm/api.js
@@ -15,319 +15,291 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * CrossFrameAPI
- */
+class API {
+ 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});
+ }
- optionsGet(optionsContext) {
- return this._invoke('optionsGet', {optionsContext});
- }
+ optionsGetFull() {
+ return this._invoke('optionsGetFull');
+ }
- optionsGetFull() {
- return this._invoke('optionsGetFull');
- }
+ termsFind(text, details, optionsContext) {
+ return this._invoke('termsFind', {text, details, optionsContext});
+ }
- termsFind(text, details, optionsContext) {
- return this._invoke('termsFind', {text, details, optionsContext});
- }
+ textParse(text, optionsContext) {
+ return this._invoke('textParse', {text, optionsContext});
+ }
- textParse(text, optionsContext) {
- return this._invoke('textParse', {text, optionsContext});
- }
+ kanjiFind(text, optionsContext) {
+ return this._invoke('kanjiFind', {text, optionsContext});
+ }
- kanjiFind(text, optionsContext) {
- return this._invoke('kanjiFind', {text, optionsContext});
- }
+ isAnkiConnected() {
+ return this._invoke('isAnkiConnected');
+ }
- isAnkiConnected() {
- return this._invoke('isAnkiConnected');
- }
+ getAnkiConnectVersion() {
+ return this._invoke('getAnkiConnectVersion');
+ }
- getAnkiConnectVersion() {
- return this._invoke('getAnkiConnectVersion');
- }
+ addAnkiNote(note) {
+ return this._invoke('addAnkiNote', {note});
+ }
- addAnkiNote(note) {
- return this._invoke('addAnkiNote', {note});
- }
+ getAnkiNoteInfo(notes) {
+ return this._invoke('getAnkiNoteInfo', {notes});
+ }
- getAnkiNoteInfo(notes) {
- return this._invoke('getAnkiNoteInfo', {notes});
- }
+ injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) {
+ return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails});
+ }
- injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) {
- return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails});
- }
+ noteView(noteId) {
+ return this._invoke('noteView', {noteId});
+ }
- noteView(noteId) {
- return this._invoke('noteView', {noteId});
- }
+ suspendAnkiCardsForNote(noteId) {
+ return this._invoke('suspendAnkiCardsForNote', {noteId});
+ }
- suspendAnkiCardsForNote(noteId) {
- return this._invoke('suspendAnkiCardsForNote', {noteId});
- }
+ getExpressionAudioInfoList(source, expression, reading, details) {
+ return this._invoke('getExpressionAudioInfoList', {source, expression, reading, details});
+ }
- getExpressionAudioInfoList(source, expression, reading, details) {
- return this._invoke('getExpressionAudioInfoList', {source, expression, reading, details});
- }
+ commandExec(command, params) {
+ return this._invoke('commandExec', {command, params});
+ }
- 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');
- }
+ sendMessageToFrame(frameId, action, params) {
+ return this._invoke('sendMessageToFrame', {frameId, action, params});
+ }
- getDictionaryInfo() {
- return this._invoke('getDictionaryInfo');
- }
+ broadcastTab(action, params) {
+ return this._invoke('broadcastTab', {action, params});
+ }
- getDictionaryCounts(dictionaryNames, getTotal) {
- return this._invoke('getDictionaryCounts', {dictionaryNames, getTotal});
- }
+ frameInformationGet() {
+ return this._invoke('frameInformationGet');
+ }
- purgeDatabase() {
- return this._invoke('purgeDatabase');
- }
+ injectStylesheet(type, value) {
+ return this._invoke('injectStylesheet', {type, value});
+ }
- getMedia(targets) {
- return this._invoke('getMedia', {targets});
- }
+ getStylesheetContent(url) {
+ return this._invoke('getStylesheetContent', {url});
+ }
- logIndicatorClear() {
- return this._invoke('logIndicatorClear');
- }
+ getEnvironmentInfo() {
+ return this._invoke('getEnvironmentInfo');
+ }
- modifySettings(targets, source) {
- return this._invoke('modifySettings', {targets, source});
- }
+ clipboardGet() {
+ return this._invoke('clipboardGet');
+ }
- getSettings(targets) {
- return this._invoke('getSettings', {targets});
- }
+ getDisplayTemplatesHtml() {
+ return this._invoke('getDisplayTemplatesHtml');
+ }
- setAllSettings(value, source) {
- return this._invoke('setAllSettings', {value, source});
- }
+ getZoom() {
+ return this._invoke('getZoom');
+ }
- getOrCreateSearchPopup(details) {
- return this._invoke('getOrCreateSearchPopup', isObject(details) ? details : {});
- }
+ getDefaultAnkiFieldTemplates() {
+ return this._invoke('getDefaultAnkiFieldTemplates');
+ }
- isTabSearchPopup(tabId) {
- return this._invoke('isTabSearchPopup', {tabId});
- }
+ getDictionaryInfo() {
+ return this._invoke('getDictionaryInfo');
+ }
- triggerDatabaseUpdated(type, cause) {
- return this._invoke('triggerDatabaseUpdated', {type, cause});
- }
+ getDictionaryCounts(dictionaryNames, getTotal) {
+ return this._invoke('getDictionaryCounts', {dictionaryNames, getTotal});
+ }
- testMecab() {
- return this._invoke('testMecab', {});
- }
+ purgeDatabase() {
+ return this._invoke('purgeDatabase');
+ }
- // 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;
- }
+ getMedia(targets) {
+ return this._invoke('getMedia', {targets});
+ }
- clearTimeout(timer);
- timer = null;
+ log(error, level, context) {
+ return this._invoke('log', {error, level, context});
+ }
- chrome.runtime.onConnect.removeListener(onConnect);
- resolve(port);
- };
+ logIndicatorClear() {
+ return this._invoke('logIndicatorClear');
+ }
- const onError = (e) => {
- if (timer !== null) {
- clearTimeout(timer);
- timer = null;
- }
- chrome.runtime.onConnect.removeListener(onConnect);
- portDetails.reject(e);
- reject(e);
- };
+ modifySettings(targets, source) {
+ return this._invoke('modifySettings', {targets, source});
+ }
- timer = setTimeout(() => onError(new Error('Timeout')), timeout);
+ getSettings(targets) {
+ return this._invoke('getSettings', {targets});
+ }
- chrome.runtime.onConnect.addListener(onConnect);
- this._invoke('createActionPort').then(portDetails.resolve, onError);
- });
- }
+ setAllSettings(value, source) {
+ return this._invoke('setAllSettings', {value, source});
+ }
- _invokeWithProgress(action, params, onProgress, timeout=5000) {
- return new Promise((resolve, reject) => {
- let port = null;
+ 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);
+ };
- if (typeof onProgress !== 'function') {
- onProgress = () => {};
+ const onError = (e) => {
+ if (timer !== null) {
+ clearTimeout(timer);
+ timer = null;
}
+ chrome.runtime.onConnect.removeListener(onConnect);
+ portDetails.reject(e);
+ reject(e);
+ };
- 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;
- }
- };
+ timer = setTimeout(() => onError(new Error('Timeout')), timeout);
- 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});
+ 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
}
- port.postMessage({action: 'invoke'});
- } catch (e) {
+ break;
+ case 'complete':
cleanup();
- reject(e);
- } finally {
- action = null;
- params = null;
- }
- })();
- });
- }
+ 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;
+ };
- _invoke(action, params={}) {
- const data = {action, params};
- return new Promise((resolve, reject) => {
+ (async () => {
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)})`));
- }
- });
+ 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;
}
- });
- }
-
- _checkLastError() {
- // NOP
- }
+ })();
+ });
+ }
- async _onLog({error, level, context}) {
+ _invoke(action, params={}) {
+ const data = {action, params};
+ return new Promise((resolve, reject) => {
try {
- error = serializeError(error);
- await this._invoke('log', {error, level, context});
+ 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) {
- // NOP
+ reject(e);
}
- }
+ });
}
- return new API();
-})();
+ _checkLastError() {
+ // NOP
+ }
+}
diff --git a/ext/js/comm/frame-ancestry-handler.js b/ext/js/comm/frame-ancestry-handler.js
index b1ed7114..d53334e1 100644
--- a/ext/js/comm/frame-ancestry-handler.js
+++ b/ext/js/comm/frame-ancestry-handler.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
/**
* This class is used to return the ancestor frame IDs for the current frame.
* This is a workaround to using the `webNavigation.getAllFrames` API, which
@@ -118,7 +114,7 @@ class FrameAncestryHandler {
clearTimeout(timer);
timer = null;
}
- api.crossFrame.unregisterHandler(responseMessageId);
+ yomichan.crossFrame.unregisterHandler(responseMessageId);
};
const onMessage = (params) => {
if (params.nonce !== nonce) { return null; }
@@ -148,7 +144,7 @@ class FrameAncestryHandler {
};
// Start
- api.crossFrame.registerHandlers([[responseMessageId, {async: false, handler: onMessage}]]);
+ yomichan.crossFrame.registerHandlers([[responseMessageId, {async: false, handler: onMessage}]]);
resetTimeout();
const frameId = this._frameId;
this._requestFrameInfo(targetWindow, frameId, frameId, uniqueId, nonce);
@@ -187,7 +183,7 @@ class FrameAncestryHandler {
const responseMessageId = `${this._responseMessageIdBase}${uniqueId}`;
try {
- const response = await api.crossFrame.invoke(originFrameId, responseMessageId, responseParams);
+ const response = await yomichan.crossFrame.invoke(originFrameId, responseMessageId, responseParams);
if (response === null) { return; }
nonce = response.nonce;
} catch (e) {
diff --git a/ext/js/comm/frame-endpoint.js b/ext/js/comm/frame-endpoint.js
index 27af9cf3..bc3c50f8 100644
--- a/ext/js/comm/frame-endpoint.js
+++ b/ext/js/comm/frame-endpoint.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
class FrameEndpoint {
constructor() {
this._secret = generateId(16);
@@ -32,7 +28,7 @@ class FrameEndpoint {
this._eventListeners.addEventListener(window, 'message', this._onMessage.bind(this), false);
this._eventListenersSetup = true;
}
- api.broadcastTab('frameEndpointReady', {secret: this._secret});
+ yomichan.api.broadcastTab('frameEndpointReady', {secret: this._secret});
}
authenticate(message) {
@@ -60,6 +56,6 @@ class FrameEndpoint {
this._token = token;
this._eventListeners.removeAllEventListeners();
- api.sendMessageToFrame(hostFrameId, 'frameEndpointConnected', {secret, token});
+ yomichan.api.sendMessageToFrame(hostFrameId, 'frameEndpointConnected', {secret, token});
}
}
diff --git a/ext/js/comm/frame-offset-forwarder.js b/ext/js/comm/frame-offset-forwarder.js
index 0a0b4a18..2382a9fa 100644
--- a/ext/js/comm/frame-offset-forwarder.js
+++ b/ext/js/comm/frame-offset-forwarder.js
@@ -17,7 +17,6 @@
/* global
* FrameAncestryHandler
- * api
*/
class FrameOffsetForwarder {
@@ -28,7 +27,7 @@ class FrameOffsetForwarder {
prepare() {
this._frameAncestryHandler.prepare();
- api.crossFrame.registerHandlers([
+ yomichan.crossFrame.registerHandlers([
['FrameOffsetForwarder.getChildFrameRect', {async: false, handler: this._onMessageGetChildFrameRect.bind(this)}]
]);
}
@@ -43,7 +42,7 @@ class FrameOffsetForwarder {
let childFrameId = this._frameId;
const promises = [];
for (const frameId of ancestorFrameIds) {
- promises.push(api.crossFrame.invoke(frameId, 'FrameOffsetForwarder.getChildFrameRect', {frameId: childFrameId}));
+ promises.push(yomichan.crossFrame.invoke(frameId, 'FrameOffsetForwarder.getChildFrameRect', {frameId: childFrameId}));
childFrameId = frameId;
}
diff --git a/ext/js/display/display-audio.js b/ext/js/display/display-audio.js
index f624d85b..24f2dd7b 100644
--- a/ext/js/display/display-audio.js
+++ b/ext/js/display/display-audio.js
@@ -18,7 +18,6 @@
/* global
* AudioSystem
* PopupMenu
- * api
*/
class DisplayAudio {
@@ -314,7 +313,7 @@ class DisplayAudio {
}
async _getExpressionAudioInfoList(source, expression, reading, details) {
- const infoList = await api.getExpressionAudioInfoList(source, expression, reading, details);
+ const infoList = await yomichan.api.getExpressionAudioInfoList(source, expression, reading, details);
return infoList.map((info) => ({info, audioPromise: null, audioResolved: false, audio: null}));
}
diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js
index 05376ee5..18159f68 100644
--- a/ext/js/display/display-generator.js
+++ b/ext/js/display/display-generator.js
@@ -18,7 +18,6 @@
/* global
* DictionaryDataUtil
* HtmlTemplateCollection
- * api
*/
class DisplayGenerator {
@@ -31,7 +30,7 @@ class DisplayGenerator {
}
async prepare() {
- const html = await api.getDisplayTemplatesHtml();
+ const html = await yomichan.api.getDisplayTemplatesHtml();
this._templates = new HtmlTemplateCollection(html);
this.updateHotkeys();
}
diff --git a/ext/js/display/display-profile-selection.js b/ext/js/display/display-profile-selection.js
index 0a44392e..d858da28 100644
--- a/ext/js/display/display-profile-selection.js
+++ b/ext/js/display/display-profile-selection.js
@@ -17,7 +17,6 @@
/* global
* PanelElement
- * api
*/
class DisplayProfileSelection {
@@ -67,7 +66,7 @@ class DisplayProfileSelection {
async _updateProfileList() {
this._profileListNeedsUpdate = false;
- const options = await api.optionsGetFull();
+ const options = await yomichan.api.optionsGetFull();
this._eventListeners.removeAllEventListeners();
const displayGenerator = this._display.displayGenerator;
@@ -95,7 +94,7 @@ class DisplayProfileSelection {
}
async _setProfileCurrent(index) {
- await api.modifySettings([{
+ await yomichan.api.modifySettings([{
action: 'set',
path: 'profileCurrent',
value: index,
diff --git a/ext/js/display/display.js b/ext/js/display/display.js
index fe4160a6..553c17e7 100644
--- a/ext/js/display/display.js
+++ b/ext/js/display/display.js
@@ -31,7 +31,6 @@
* QueryParser
* ScrollElement
* TextScanner
- * api
* dynamicLoader
*/
@@ -206,7 +205,7 @@ class Display extends EventDispatcher {
async prepare() {
// State setup
const {documentElement} = document;
- const {browser} = await api.getEnvironmentInfo();
+ const {browser} = await yomichan.api.getEnvironmentInfo();
this._browser = browser;
// Prepare
@@ -221,7 +220,7 @@ class Display extends EventDispatcher {
this._queryParser.on('searched', this._onQueryParserSearch.bind(this));
this._progressIndicatorVisible.on('change', this._onProgressIndicatorVisibleChanged.bind(this));
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
- api.crossFrame.registerHandlers([
+ yomichan.crossFrame.registerHandlers([
['popupMessage', {async: 'dynamic', handler: this._onDirectMessage.bind(this)}]
]);
window.addEventListener('message', this._onWindowMessage.bind(this), false);
@@ -290,7 +289,7 @@ class Display extends EventDispatcher {
}
async updateOptions() {
- const options = await api.optionsGet(this.getOptionsContext());
+ const options = await yomichan.api.optionsGet(this.getOptionsContext());
const templates = await this._getAnkiFieldTemplates(options);
const {scanning: scanningOptions, sentenceParsing: sentenceParsingOptions} = options;
this._options = options;
@@ -674,7 +673,7 @@ class Display extends EventDispatcher {
if (typeof documentTitle !== 'string') { documentTitle = document.title; }
const optionsContext = this.getOptionsContext();
const query = e.currentTarget.textContent;
- const definitions = await api.kanjiFind(query, optionsContext);
+ const definitions = await yomichan.api.kanjiFind(query, optionsContext);
const details = {
focus: false,
history: true,
@@ -707,7 +706,7 @@ class Display extends EventDispatcher {
_onNoteView(e) {
e.preventDefault();
const link = e.currentTarget;
- api.noteView(link.dataset.noteId);
+ yomichan.api.noteView(link.dataset.noteId);
}
_onWheel(e) {
@@ -839,10 +838,10 @@ class Display extends EventDispatcher {
}
}
- const {definitions} = await api.termsFind(source, findDetails, optionsContext);
+ const {definitions} = await yomichan.api.termsFind(source, findDetails, optionsContext);
return definitions;
} else {
- const definitions = await api.kanjiFind(source, optionsContext);
+ const definitions = await yomichan.api.kanjiFind(source, optionsContext);
return definitions;
}
}
@@ -1059,7 +1058,7 @@ class Display extends EventDispatcher {
const noteContext = this._getNoteContext();
states = await this._areDefinitionsAddable(definitions, modes, noteContext);
} else {
- if (!await api.isAnkiConnected()) {
+ if (!await yomichan.api.isAnkiConnected()) {
throw new Error('Anki not connected');
}
states = this._areDefinitionsAddableForcedValue(definitions, modes, true);
@@ -1183,7 +1182,7 @@ class Display extends EventDispatcher {
_tryViewAnkiNoteForSelectedDefinition() {
const button = this._viewerButtonFind(this._index);
if (button !== null && !button.disabled) {
- api.noteView(button.dataset.noteId);
+ yomichan.api.noteView(button.dataset.noteId);
}
}
@@ -1206,7 +1205,7 @@ class Display extends EventDispatcher {
let noteId = null;
let addNoteOkay = false;
try {
- noteId = await api.addAnkiNote(note);
+ noteId = await yomichan.api.addAnkiNote(note);
addNoteOkay = true;
} catch (e) {
errors.length = 0;
@@ -1219,7 +1218,7 @@ class Display extends EventDispatcher {
} else {
if (suspendNewCards) {
try {
- await api.suspendAnkiCardsForNote(noteId);
+ await yomichan.api.suspendAnkiCardsForNote(noteId);
} catch (e) {
errors.push(e);
}
@@ -1400,7 +1399,7 @@ class Display extends EventDispatcher {
templates = this._ankiFieldTemplatesDefault;
if (typeof templates === 'string') { return templates; }
- templates = await api.getDefaultAnkiFieldTemplates();
+ templates = await yomichan.api.getDefaultAnkiFieldTemplates();
this._ankiFieldTemplatesDefault = templates;
return templates;
}
@@ -1416,7 +1415,7 @@ class Display extends EventDispatcher {
}
const notes = await Promise.all(notePromises);
- const infos = await api.getAnkiNoteInfo(notes);
+ const infos = await yomichan.api.getAnkiNoteInfo(notes);
const results = [];
for (let i = 0, ii = infos.length; i < ii; i += modeCount) {
results.push(infos.slice(i, i + modeCount));
@@ -1494,7 +1493,7 @@ class Display extends EventDispatcher {
image: this._ankiNoteBuilder.containsMarker(fields, 'clipboard-image'),
text: this._ankiNoteBuilder.containsMarker(fields, 'clipboard-text')
};
- return await api.injectAnkiNoteMedia(
+ return await yomichan.api.injectAnkiNoteMedia(
timestamp,
definitionDetails,
audioDetails,
@@ -1605,7 +1604,7 @@ class Display extends EventDispatcher {
if (this._contentOriginTabId === this._tabId && this._contentOriginFrameId === this._frameId) {
throw new Error('Content origin is same page');
}
- return await api.crossFrame.invokeTab(this._contentOriginTabId, this._contentOriginFrameId, action, params);
+ return await yomichan.crossFrame.invokeTab(this._contentOriginTabId, this._contentOriginFrameId, action, params);
}
_copyHostSelection() {
diff --git a/ext/js/display/popup-main.js b/ext/js/display/popup-main.js
index f1228aa6..24be6c01 100644
--- a/ext/js/display/popup-main.js
+++ b/ext/js/display/popup-main.js
@@ -21,7 +21,6 @@
* DocumentFocusController
* HotkeyHandler
* JapaneseUtil
- * api
*/
(async () => {
@@ -29,10 +28,9 @@
const documentFocusController = new DocumentFocusController();
documentFocusController.prepare();
- api.prepare();
await yomichan.prepare();
- const {tabId, frameId} = await api.frameInformationGet();
+ const {tabId, frameId} = await yomichan.api.frameInformationGet();
const japaneseUtil = new JapaneseUtil(null);
diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js
index 05ebfa27..29401657 100644
--- a/ext/js/display/query-parser.js
+++ b/ext/js/display/query-parser.js
@@ -17,7 +17,6 @@
/* global
* TextScanner
- * api
*/
class QueryParser extends EventDispatcher {
@@ -76,7 +75,7 @@ class QueryParser extends EventDispatcher {
const token = {};
this._setTextToken = token;
- this._parseResults = await api.textParse(text, this._getOptionsContext());
+ this._parseResults = await yomichan.api.textParse(text, this._getOptionsContext());
if (this._setTextToken !== token) { return; }
this._refreshSelectedParser();
@@ -116,7 +115,7 @@ class QueryParser extends EventDispatcher {
_setSelectedParser(value) {
const optionsContext = this._getOptionsContext();
- api.modifySettings([{
+ yomichan.api.modifySettings([{
action: 'set',
path: 'parsing.selectedParser',
value,
diff --git a/ext/js/display/search-display-controller.js b/ext/js/display/search-display-controller.js
index a295346d..3b48af44 100644
--- a/ext/js/display/search-display-controller.js
+++ b/ext/js/display/search-display-controller.js
@@ -17,7 +17,6 @@
/* global
* ClipboardMonitor
- * api
* wanakana
*/
@@ -40,7 +39,7 @@ class SearchDisplayController {
this._clipboardMonitor = new ClipboardMonitor({
japaneseUtil,
clipboardReader: {
- getText: async () => (await api.clipboardGet())
+ getText: async () => (await yomichan.api.clipboardGet())
}
});
this._messageHandlers = new Map();
@@ -201,7 +200,7 @@ class SearchDisplayController {
_onWanakanaEnableChange(e) {
const value = e.target.checked;
this._setWanakanaEnabled(value);
- api.modifySettings([{
+ yomichan.api.modifySettings([{
action: 'set',
path: 'general.enableWanakana',
value,
@@ -301,7 +300,7 @@ class SearchDisplayController {
if (!modify) { return; }
- await api.modifySettings([{
+ await yomichan.api.modifySettings([{
action: 'set',
path: 'clipboard.enableSearchPageMonitor',
value,
diff --git a/ext/js/display/search-main.js b/ext/js/display/search-main.js
index d3e8af0b..08645833 100644
--- a/ext/js/display/search-main.js
+++ b/ext/js/display/search-main.js
@@ -21,7 +21,6 @@
* HotkeyHandler
* JapaneseUtil
* SearchDisplayController
- * api
* wanakana
*/
@@ -30,10 +29,9 @@
const documentFocusController = new DocumentFocusController();
documentFocusController.prepare();
- api.prepare();
await yomichan.prepare();
- const {tabId, frameId} = await api.frameInformationGet();
+ const {tabId, frameId} = await yomichan.api.frameInformationGet();
const japaneseUtil = new JapaneseUtil(wanakana);
diff --git a/ext/js/input/hotkey-handler.js b/ext/js/input/hotkey-handler.js
index 423410b7..6db3e0f0 100644
--- a/ext/js/input/hotkey-handler.js
+++ b/ext/js/input/hotkey-handler.js
@@ -17,7 +17,6 @@
/* global
* DocumentUtil
- * api
*/
/**
@@ -59,7 +58,7 @@ class HotkeyHandler extends EventDispatcher {
prepare() {
this._isPrepared = true;
this._updateEventHandlers();
- api.crossFrame.registerHandlers([
+ yomichan.crossFrame.registerHandlers([
['hotkeyHandler.forwardHotkey', {async: false, handler: this._onMessageForwardHotkey.bind(this)}]
]);
}
@@ -259,7 +258,7 @@ class HotkeyHandler extends EventDispatcher {
const frameId = this._forwardFrameId;
if (frameId === null) { throw new Error('No forwarding target'); }
try {
- await api.crossFrame.invoke(frameId, 'hotkeyHandler.forwardHotkey', {key, modifiers});
+ await yomichan.crossFrame.invoke(frameId, 'hotkeyHandler.forwardHotkey', {key, modifiers});
} catch (e) {
// NOP
}
diff --git a/ext/js/input/hotkey-help-controller.js b/ext/js/input/hotkey-help-controller.js
index 8137b50b..9bf95c77 100644
--- a/ext/js/input/hotkey-help-controller.js
+++ b/ext/js/input/hotkey-help-controller.js
@@ -17,7 +17,6 @@
/* global
* HotkeyUtil
- * api
*/
class HotkeyHelpController {
@@ -29,7 +28,7 @@ class HotkeyHelpController {
}
async prepare() {
- const {platform: {os}} = await api.getEnvironmentInfo();
+ const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
this._hotkeyUtil.os = os;
await this._setupGlobalCommands(this._globalActionHotkeys);
}
diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js
index 7672b69d..e91498ba 100644
--- a/ext/js/language/text-scanner.js
+++ b/ext/js/language/text-scanner.js
@@ -17,7 +17,6 @@
/* global
* DocumentUtil
- * api
*/
class TextScanner extends EventDispatcher {
@@ -762,7 +761,7 @@ class TextScanner extends EventDispatcher {
const searchText = this.getTextSourceContent(textSource, scanLength, layoutAwareScan);
if (searchText.length === 0) { return null; }
- const {definitions, length} = await api.termsFind(searchText, {}, optionsContext);
+ const {definitions, length} = await yomichan.api.termsFind(searchText, {}, optionsContext);
if (definitions.length === 0) { return null; }
textSource.setEndOffset(length, layoutAwareScan);
@@ -787,7 +786,7 @@ class TextScanner extends EventDispatcher {
const searchText = this.getTextSourceContent(textSource, 1, layoutAwareScan);
if (searchText.length === 0) { return null; }
- const definitions = await api.kanjiFind(searchText, optionsContext);
+ const definitions = await yomichan.api.kanjiFind(searchText, optionsContext);
if (definitions.length === 0) { return null; }
textSource.setEndOffset(1, layoutAwareScan);
diff --git a/ext/js/media/media-loader.js b/ext/js/media/media-loader.js
index 5974e31a..d9d40a36 100644
--- a/ext/js/media/media-loader.js
+++ b/ext/js/media/media-loader.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
class MediaLoader {
constructor() {
this._token = {};
@@ -84,7 +80,7 @@ class MediaLoader {
async _getMediaData(path, dictionaryName, cachedData) {
const token = this._token;
- const data = (await api.getMedia([{path, dictionaryName}]))[0];
+ const data = (await yomichan.api.getMedia([{path, dictionaryName}]))[0];
if (token === this._token && data !== null) {
const contentArrayBuffer = this._base64ToArrayBuffer(data.content);
const blob = new Blob([contentArrayBuffer], {type: data.mediaType});
diff --git a/ext/js/pages/action-popup-main.js b/ext/js/pages/action-popup-main.js
index 99a76bdf..75dfb641 100644
--- a/ext/js/pages/action-popup-main.js
+++ b/ext/js/pages/action-popup-main.js
@@ -18,7 +18,6 @@
/* global
* HotkeyHelpController
* PermissionsUtil
- * api
*/
class DisplayController {
@@ -35,7 +34,7 @@ class DisplayController {
this._setupButtonEvents('.action-open-search', 'openSearchPage', chrome.runtime.getURL('/search.html'));
this._setupButtonEvents('.action-open-info', 'openInfoPage', chrome.runtime.getURL('/info.html'));
- const optionsFull = await api.optionsGetFull();
+ const optionsFull = await yomichan.api.optionsGetFull();
this._optionsFull = optionsFull;
this._setupHotkeys();
@@ -74,12 +73,12 @@ class DisplayController {
if (typeof command === 'string') {
node.addEventListener('click', (e) => {
if (e.button !== 0) { return; }
- api.commandExec(command, {mode: e.ctrlKey ? 'newTab' : 'existingOrNewTab'});
+ yomichan.api.commandExec(command, {mode: e.ctrlKey ? 'newTab' : 'existingOrNewTab'});
e.preventDefault();
}, false);
node.addEventListener('auxclick', (e) => {
if (e.button !== 1) { return; }
- api.commandExec(command, {mode: 'newTab'});
+ yomichan.api.commandExec(command, {mode: 'newTab'});
e.preventDefault();
}, false);
}
@@ -130,7 +129,7 @@ class DisplayController {
_setupOptions({options}) {
const extensionEnabled = options.general.enable;
- const onToggleChanged = () => api.commandExec('toggleTextScanning');
+ const onToggleChanged = () => yomichan.api.commandExec('toggleTextScanning');
for (const toggle of document.querySelectorAll('#enable-search,#enable-search2')) {
toggle.checked = extensionEnabled;
toggle.addEventListener('change', onToggleChanged, false);
@@ -178,7 +177,7 @@ class DisplayController {
}
async _setPrimaryProfileIndex(value) {
- return await api.modifySettings(
+ return await yomichan.api.modifySettings(
[{
action: 'set',
path: 'profileCurrent',
@@ -190,7 +189,7 @@ class DisplayController {
async _updateDictionariesEnabledWarnings(options) {
const noDictionariesEnabledWarnings = document.querySelectorAll('.no-dictionaries-enabled-warning');
- const dictionaries = await api.getDictionaryInfo();
+ const dictionaries = await yomichan.api.getDictionaryInfo();
let enabledCount = 0;
for (const {title} of dictionaries) {
@@ -221,10 +220,9 @@ class DisplayController {
}
(async () => {
- api.prepare();
await yomichan.prepare();
- api.logIndicatorClear();
+ yomichan.api.logIndicatorClear();
const displayController = new DisplayController();
displayController.prepare();
diff --git a/ext/js/pages/info-main.js b/ext/js/pages/info-main.js
index 7d34d47a..45c28d25 100644
--- a/ext/js/pages/info-main.js
+++ b/ext/js/pages/info-main.js
@@ -19,7 +19,6 @@
* BackupController
* DocumentFocusController
* SettingsController
- * api
*/
function getBrowserDisplayName(browser) {
@@ -54,12 +53,11 @@ function getOperatingSystemDisplayName(os) {
const manifest = chrome.runtime.getManifest();
const language = chrome.i18n.getUILanguage();
- api.prepare();
await yomichan.prepare();
const {userAgent} = navigator;
const {name, version} = manifest;
- const {browser, platform: {os}} = await api.getEnvironmentInfo();
+ const {browser, platform: {os}} = await yomichan.api.getEnvironmentInfo();
const thisVersionLink = document.querySelector('#release-notes-this-version-link');
thisVersionLink.href = thisVersionLink.dataset.hrefFormat.replace(/\{version\}/g, version);
@@ -73,7 +71,7 @@ function getOperatingSystemDisplayName(os) {
(async () => {
let ankiConnectVersion = null;
try {
- ankiConnectVersion = await api.getAnkiConnectVersion();
+ ankiConnectVersion = await yomichan.api.getAnkiConnectVersion();
} catch (e) {
// NOP
}
@@ -86,7 +84,7 @@ function getOperatingSystemDisplayName(os) {
(async () => {
let dictionaryInfos;
try {
- dictionaryInfos = await api.getDictionaryInfo();
+ dictionaryInfos = await yomichan.api.getDictionaryInfo();
} catch (e) {
return;
}
diff --git a/ext/js/pages/permissions-main.js b/ext/js/pages/permissions-main.js
index 7c0427b9..0cb37e93 100644
--- a/ext/js/pages/permissions-main.js
+++ b/ext/js/pages/permissions-main.js
@@ -19,12 +19,11 @@
* DocumentFocusController
* PermissionsToggleController
* SettingsController
- * api
*/
async function setupEnvironmentInfo() {
const {manifest_version: manifestVersion} = chrome.runtime.getManifest();
- const {browser, platform} = await api.getEnvironmentInfo();
+ const {browser, platform} = await yomichan.api.getEnvironmentInfo();
document.documentElement.dataset.browser = browser;
document.documentElement.dataset.os = platform.os;
document.documentElement.dataset.manifestVersion = `${manifestVersion}`;
@@ -69,7 +68,6 @@ function setupPermissionsToggles() {
node.textContent = chrome.runtime.getURL('/');
}
- api.prepare();
await yomichan.prepare();
setupEnvironmentInfo();
diff --git a/ext/js/pages/welcome-main.js b/ext/js/pages/welcome-main.js
index 626cbd3a..5a6bb2a5 100644
--- a/ext/js/pages/welcome-main.js
+++ b/ext/js/pages/welcome-main.js
@@ -25,12 +25,11 @@
* SettingsController
* SettingsDisplayController
* StatusFooter
- * api
*/
async function setupEnvironmentInfo() {
const {manifest_version: manifestVersion} = chrome.runtime.getManifest();
- const {browser, platform} = await api.getEnvironmentInfo();
+ const {browser, platform} = await yomichan.api.getEnvironmentInfo();
document.documentElement.dataset.browser = browser;
document.documentElement.dataset.os = platform.os;
document.documentElement.dataset.manifestVersion = `${manifestVersion}`;
@@ -49,12 +48,11 @@ async function setupGenericSettingsController(genericSettingController) {
const statusFooter = new StatusFooter(document.querySelector('.status-footer-container'));
statusFooter.prepare();
- api.prepare();
await yomichan.prepare();
setupEnvironmentInfo();
- const optionsFull = await api.optionsGetFull();
+ const optionsFull = await yomichan.api.optionsGetFull();
const preparePromises = [];
diff --git a/ext/js/script/dynamic-loader.js b/ext/js/script/dynamic-loader.js
index 0464f151..a2cfb77a 100644
--- a/ext/js/script/dynamic-loader.js
+++ b/ext/js/script/dynamic-loader.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
const dynamicLoader = (() => {
const injectedStylesheets = new Map();
const injectedStylesheetsWithParent = new WeakMap();
@@ -61,7 +57,7 @@ const dynamicLoader = (() => {
}
if (type === 'file-content') {
- value = await api.getStylesheetContent(value);
+ value = await yomichan.api.getStylesheetContent(value);
type = 'code';
useWebExtensionApi = false;
}
@@ -73,7 +69,7 @@ const dynamicLoader = (() => {
}
setInjectedStylesheet(id, parentNode, null);
- await api.injectStylesheet(type, value);
+ await yomichan.api.injectStylesheet(type, value);
return null;
}
diff --git a/ext/js/settings/anki-templates-controller.js b/ext/js/settings/anki-templates-controller.js
index 31bd1e92..8e3a1a70 100644
--- a/ext/js/settings/anki-templates-controller.js
+++ b/ext/js/settings/anki-templates-controller.js
@@ -17,7 +17,6 @@
/* global
* AnkiNoteBuilder
- * api
*/
class AnkiTemplatesController {
@@ -37,7 +36,7 @@ class AnkiTemplatesController {
}
async prepare() {
- this._defaultFieldTemplates = await api.getDefaultAnkiFieldTemplates();
+ this._defaultFieldTemplates = await yomichan.api.getDefaultAnkiFieldTemplates();
this._fieldTemplatesTextarea = document.querySelector('#anki-card-templates-textarea');
this._compileResultInfo = document.querySelector('#anki-card-templates-compile-result');
@@ -154,7 +153,7 @@ class AnkiTemplatesController {
async _getDefinition(text, optionsContext) {
if (this._cachedDefinitionText !== text) {
- const {definitions} = await api.termsFind(text, {}, optionsContext);
+ const {definitions} = await yomichan.api.termsFind(text, {}, optionsContext);
if (definitions.length === 0) { return null; }
this._cachedDefinitionValue = definitions[0];
diff --git a/ext/js/settings/backup-controller.js b/ext/js/settings/backup-controller.js
index 8837b927..117f2422 100644
--- a/ext/js/settings/backup-controller.js
+++ b/ext/js/settings/backup-controller.js
@@ -18,7 +18,6 @@
/* global
* DictionaryController
* OptionsUtil
- * api
*/
class BackupController {
@@ -85,8 +84,8 @@ class BackupController {
async _getSettingsExportData(date) {
const optionsFull = await this._settingsController.getOptionsFull();
- const environment = await api.getEnvironmentInfo();
- const fieldTemplatesDefault = await api.getDefaultAnkiFieldTemplates();
+ const environment = await yomichan.api.getEnvironmentInfo();
+ const fieldTemplatesDefault = await yomichan.api.getDefaultAnkiFieldTemplates();
const permissions = await this._settingsController.permissionsUtil.getAllPermissions();
// Format options
diff --git a/ext/js/settings/dictionary-controller.js b/ext/js/settings/dictionary-controller.js
index ea9f7503..78173202 100644
--- a/ext/js/settings/dictionary-controller.js
+++ b/ext/js/settings/dictionary-controller.js
@@ -18,7 +18,6 @@
/* global
* DictionaryDatabase
* ObjectPropertyAccessor
- * api
*/
class DictionaryEntry {
@@ -362,7 +361,7 @@ class DictionaryController {
const token = this._databaseStateToken;
const dictionaryTitles = this._dictionaries.map(({title}) => title);
- const {counts, total} = await api.getDictionaryCounts(dictionaryTitles, true);
+ const {counts, total} = await yomichan.api.getDictionaryCounts(dictionaryTitles, true);
if (this._databaseStateToken !== token) { return; }
for (let i = 0, ii = Math.min(counts.length, this._dictionaryEntries.length); i < ii; ++i) {
@@ -499,7 +498,7 @@ class DictionaryController {
const dictionaryDatabase = await this._getPreparedDictionaryDatabase();
try {
await dictionaryDatabase.deleteDictionary(dictionaryTitle, {rate: 1000}, onProgress);
- api.triggerDatabaseUpdated('dictionary', 'delete');
+ yomichan.api.triggerDatabaseUpdated('dictionary', 'delete');
} finally {
dictionaryDatabase.close();
}
diff --git a/ext/js/settings/dictionary-import-controller.js b/ext/js/settings/dictionary-import-controller.js
index c4ad9e59..00eb7b95 100644
--- a/ext/js/settings/dictionary-import-controller.js
+++ b/ext/js/settings/dictionary-import-controller.js
@@ -19,7 +19,6 @@
* DictionaryDatabase
* DictionaryImporter
* ObjectPropertyAccessor
- * api
*/
class DictionaryImportController {
@@ -102,7 +101,7 @@ class DictionaryImportController {
this._setSpinnerVisible(true);
if (purgeNotification !== null) { purgeNotification.hidden = false; }
- await api.purgeDatabase();
+ await yomichan.api.purgeDatabase();
const errors = await this._clearDictionarySettings();
if (errors.length > 0) {
@@ -197,7 +196,7 @@ class DictionaryImportController {
const dictionaryImporter = new DictionaryImporter();
const archiveContent = await this._readFile(file);
const {result, errors} = await dictionaryImporter.importDictionary(dictionaryDatabase, archiveContent, importDetails, onProgress);
- api.triggerDatabaseUpdated('dictionary', 'import');
+ yomichan.api.triggerDatabaseUpdated('dictionary', 'import');
const errors2 = await this._addDictionarySettings(result.sequenced, result.title);
if (errors.length > 0) {
diff --git a/ext/js/settings/extension-keyboard-shortcuts-controller.js b/ext/js/settings/extension-keyboard-shortcuts-controller.js
index 9c930703..032f9dcc 100644
--- a/ext/js/settings/extension-keyboard-shortcuts-controller.js
+++ b/ext/js/settings/extension-keyboard-shortcuts-controller.js
@@ -18,7 +18,6 @@
/* global
* HotkeyUtil
* KeyboardMouseInputField
- * api
*/
class ExtensionKeyboardShortcutController {
@@ -53,7 +52,7 @@ class ExtensionKeyboardShortcutController {
this._clearButton.addEventListener('click', this._onClearClick.bind(this));
}
- const {platform: {os}} = await api.getEnvironmentInfo();
+ const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
this._os = os;
this._hotkeyUtil.os = os;
diff --git a/ext/js/settings/keyboard-shortcuts-controller.js b/ext/js/settings/keyboard-shortcuts-controller.js
index 0dcfa2ee..99b16f06 100644
--- a/ext/js/settings/keyboard-shortcuts-controller.js
+++ b/ext/js/settings/keyboard-shortcuts-controller.js
@@ -17,7 +17,6 @@
/* global
* KeyboardMouseInputField
- * api
*/
class KeyboardShortcutController {
@@ -38,7 +37,7 @@ class KeyboardShortcutController {
}
async prepare() {
- const {platform: {os}} = await api.getEnvironmentInfo();
+ const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
this._os = os;
this._addButton = document.querySelector('#hotkey-list-add');
diff --git a/ext/js/settings/main.js b/ext/js/settings/main.js
index 0707ea3c..51cd5f27 100644
--- a/ext/js/settings/main.js
+++ b/ext/js/settings/main.js
@@ -31,7 +31,6 @@
* ScanInputsSimpleController
* SettingsController
* StorageController
- * api
*/
function showExtensionInformation() {
@@ -43,7 +42,7 @@ function showExtensionInformation() {
}
async function setupEnvironmentInfo() {
- const {browser, platform} = await api.getEnvironmentInfo();
+ const {browser, platform} = await yomichan.api.getEnvironmentInfo();
document.documentElement.dataset.browser = browser;
document.documentElement.dataset.operatingSystem = platform.os;
}
@@ -51,13 +50,12 @@ async function setupEnvironmentInfo() {
(async () => {
try {
- api.prepare();
await yomichan.prepare();
setupEnvironmentInfo();
showExtensionInformation();
- const optionsFull = await api.optionsGetFull();
+ const optionsFull = await yomichan.api.optionsGetFull();
const modalController = new ModalController();
modalController.prepare();
diff --git a/ext/js/settings/mecab-controller.js b/ext/js/settings/mecab-controller.js
index ff2a4a66..122f82f9 100644
--- a/ext/js/settings/mecab-controller.js
+++ b/ext/js/settings/mecab-controller.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
class MecabController {
constructor(settingsController) {
this._settingsController = settingsController;
@@ -49,7 +45,7 @@ class MecabController {
this._testButton.disabled = true;
this._resultsContainer.textContent = '';
this._resultsContainer.hidden = true;
- await api.testMecab();
+ await yomichan.api.testMecab();
this._setStatus('Connection was successful', false);
} catch (e) {
this._setStatus(e.message, true);
diff --git a/ext/js/settings/pitch-accents-preview-main.js b/ext/js/settings/pitch-accents-preview-main.js
index 7bc995a2..fe3bd36d 100644
--- a/ext/js/settings/pitch-accents-preview-main.js
+++ b/ext/js/settings/pitch-accents-preview-main.js
@@ -17,12 +17,10 @@
/* global
* DisplayGenerator
- * api
*/
(async () => {
try {
- api.prepare();
await yomichan.prepare();
const displayGenerator = new DisplayGenerator({
diff --git a/ext/js/settings/popup-preview-frame-main.js b/ext/js/settings/popup-preview-frame-main.js
index f61b26dc..a62e8d28 100644
--- a/ext/js/settings/popup-preview-frame-main.js
+++ b/ext/js/settings/popup-preview-frame-main.js
@@ -19,15 +19,13 @@
* HotkeyHandler
* PopupFactory
* PopupPreviewFrame
- * api
*/
(async () => {
try {
- api.prepare();
await yomichan.prepare();
- const {tabId, frameId} = await api.frameInformationGet();
+ const {tabId, frameId} = await yomichan.api.frameInformationGet();
const hotkeyHandler = new HotkeyHandler();
hotkeyHandler.prepare();
diff --git a/ext/js/settings/popup-preview-frame.js b/ext/js/settings/popup-preview-frame.js
index 56100fb3..638dd414 100644
--- a/ext/js/settings/popup-preview-frame.js
+++ b/ext/js/settings/popup-preview-frame.js
@@ -18,7 +18,6 @@
/* global
* Frontend
* TextSourceRange
- * api
* wanakana
*/
@@ -63,8 +62,8 @@ class PopupPreviewFrame {
this._exampleTextInput.addEventListener('input', this._onExampleTextInputInput.bind(this), false);
// Overwrite API functions
- this._apiOptionsGetOld = api.optionsGet.bind(api);
- api.optionsGet = this._apiOptionsGet.bind(this);
+ this._apiOptionsGetOld = yomichan.api.optionsGet.bind(yomichan.api);
+ yomichan.api.optionsGet = this._apiOptionsGet.bind(this);
// Overwrite frontend
this._frontend = new Frontend({
diff --git a/ext/js/settings/popup-window-controller.js b/ext/js/settings/popup-window-controller.js
index cc83db68..403c060c 100644
--- a/ext/js/settings/popup-window-controller.js
+++ b/ext/js/settings/popup-window-controller.js
@@ -15,10 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-/* global
- * api
- */
-
class PopupWindowController {
prepare() {
const testLink = document.querySelector('#test-window-open-link');
@@ -33,6 +29,6 @@ class PopupWindowController {
}
async _testWindowOpen() {
- await api.getOrCreateSearchPopup({focus: true});
+ await yomichan.api.getOrCreateSearchPopup({focus: true});
}
}
diff --git a/ext/js/settings/profile-controller.js b/ext/js/settings/profile-controller.js
index 914fc679..3883e80a 100644
--- a/ext/js/settings/profile-controller.js
+++ b/ext/js/settings/profile-controller.js
@@ -17,7 +17,6 @@
/* global
* ProfileConditionsUI
- * api
*/
class ProfileController {
@@ -58,7 +57,7 @@ class ProfileController {
}
async prepare() {
- const {platform: {os}} = await api.getEnvironmentInfo();
+ const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
this._profileConditionsUI.os = os;
this._profileActiveSelect = document.querySelector('#profile-active-select');
diff --git a/ext/js/settings/scan-inputs-controller.js b/ext/js/settings/scan-inputs-controller.js
index eb179c6a..79b2bdf4 100644
--- a/ext/js/settings/scan-inputs-controller.js
+++ b/ext/js/settings/scan-inputs-controller.js
@@ -17,7 +17,6 @@
/* global
* KeyboardMouseInputField
- * api
*/
class ScanInputsController {
@@ -31,7 +30,7 @@ class ScanInputsController {
}
async prepare() {
- const {platform: {os}} = await api.getEnvironmentInfo();
+ const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
this._os = os;
this._container = document.querySelector('#scan-input-list');
diff --git a/ext/js/settings/scan-inputs-simple-controller.js b/ext/js/settings/scan-inputs-simple-controller.js
index 01f044c2..b011af5d 100644
--- a/ext/js/settings/scan-inputs-simple-controller.js
+++ b/ext/js/settings/scan-inputs-simple-controller.js
@@ -18,7 +18,6 @@
/* global
* HotkeyUtil
* ScanInputsController
- * api
*/
class ScanInputsSimpleController {
@@ -34,7 +33,7 @@ class ScanInputsSimpleController {
this._middleMouseButtonScan = document.querySelector('#middle-mouse-button-scan');
this._mainScanModifierKeyInput = document.querySelector('#main-scan-modifier-key');
- const {platform: {os}} = await api.getEnvironmentInfo();
+ const {platform: {os}} = await yomichan.api.getEnvironmentInfo();
this._hotkeyUtil.os = os;
this._mainScanModifierKeyInputHasOther = false;
diff --git a/ext/js/settings/settings-controller.js b/ext/js/settings/settings-controller.js
index 11a9435c..4a86470d 100644
--- a/ext/js/settings/settings-controller.js
+++ b/ext/js/settings/settings-controller.js
@@ -19,7 +19,6 @@
* HtmlTemplateCollection
* OptionsUtil
* PermissionsUtil
- * api
*/
class SettingsController extends EventDispatcher {
@@ -62,16 +61,16 @@ class SettingsController extends EventDispatcher {
async getOptions() {
const optionsContext = this.getOptionsContext();
- return await api.optionsGet(optionsContext);
+ return await yomichan.api.optionsGet(optionsContext);
}
async getOptionsFull() {
- return await api.optionsGetFull();
+ return await yomichan.api.optionsGetFull();
}
async setAllSettings(value) {
const profileIndex = value.profileCurrent;
- await api.setAllSettings(value, this._source);
+ await yomichan.api.setAllSettings(value, this._source);
this._setProfileIndex(profileIndex);
}
@@ -108,7 +107,7 @@ class SettingsController extends EventDispatcher {
}
async getDictionaryInfo() {
- return await api.getDictionaryInfo();
+ return await yomichan.api.getDictionaryInfo();
}
getOptionsContext() {
@@ -171,12 +170,12 @@ class SettingsController extends EventDispatcher {
async _getSettings(targets, extraFields) {
targets = this._setupTargets(targets, extraFields);
- return await api.getSettings(targets);
+ return await yomichan.api.getSettings(targets);
}
async _modifySettings(targets, extraFields) {
targets = this._setupTargets(targets, extraFields);
- return await api.modifySettings(targets, this._source);
+ return await yomichan.api.modifySettings(targets, this._source);
}
_onBeforeUnload(e) {
diff --git a/ext/js/settings/settings-main.js b/ext/js/settings/settings-main.js
index a7e6b7b0..a5bb642c 100644
--- a/ext/js/settings/settings-main.js
+++ b/ext/js/settings/settings-main.js
@@ -42,12 +42,11 @@
* StatusFooter
* StorageController
* TranslationTextReplacementsController
- * api
*/
async function setupEnvironmentInfo() {
const {manifest_version: manifestVersion} = chrome.runtime.getManifest();
- const {browser, platform} = await api.getEnvironmentInfo();
+ const {browser, platform} = await yomichan.api.getEnvironmentInfo();
document.documentElement.dataset.browser = browser;
document.documentElement.dataset.os = platform.os;
document.documentElement.dataset.manifestVersion = `${manifestVersion}`;
@@ -66,12 +65,11 @@ async function setupGenericSettingsController(genericSettingController) {
const statusFooter = new StatusFooter(document.querySelector('.status-footer-container'));
statusFooter.prepare();
- api.prepare();
await yomichan.prepare();
setupEnvironmentInfo();
- const optionsFull = await api.optionsGetFull();
+ const optionsFull = await yomichan.api.optionsGetFull();
const preparePromises = [];
diff --git a/ext/js/templates/template-renderer-frame-main.js b/ext/js/templates/template-renderer-frame-main.js
index d25eb56d..a3892002 100644
--- a/ext/js/templates/template-renderer-frame-main.js
+++ b/ext/js/templates/template-renderer-frame-main.js
@@ -28,6 +28,6 @@
templateRenderer.registerDataType('ankiNote', {
modifier: ({data, marker}) => new AnkiNoteData(data, marker).createPublic()
});
- const api = new TemplateRendererFrameApi(templateRenderer);
- api.prepare();
+ const templateRendererFrameApi = new TemplateRendererFrameApi(templateRenderer);
+ templateRendererFrameApi.prepare();
})();
diff --git a/ext/js/yomichan.js b/ext/js/yomichan.js
index 7d101a4c..73deeab9 100644
--- a/ext/js/yomichan.js
+++ b/ext/js/yomichan.js
@@ -15,6 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+/* global
+ * API
+ * CrossFrameAPI
+ */
+
// Set up chrome alias if it's not available (Edge Legacy)
if ((() => {
let hasChrome = false;
@@ -34,271 +39,300 @@ if ((() => {
chrome = browser;
}
-const yomichan = (() => {
- class Yomichan extends EventDispatcher {
- constructor() {
- super();
-
- this._extensionName = 'Yomichan';
- try {
- const manifest = chrome.runtime.getManifest();
- this._extensionName = `${manifest.name} v${manifest.version}`;
- } catch (e) {
- // NOP
- }
+class Yomichan extends EventDispatcher {
+ constructor() {
+ super();
- this._isExtensionUnloaded = false;
- this._isTriggeringExtensionUnloaded = false;
- this._isReady = false;
-
- const {promise, resolve} = deferPromise();
- this._isBackendReadyPromise = promise;
- this._isBackendReadyPromiseResolve = resolve;
-
- this._messageHandlers = new Map([
- ['isReady', {async: false, handler: this._onMessageIsReady.bind(this)}],
- ['backendReady', {async: false, handler: this._onMessageBackendReady.bind(this)}],
- ['getUrl', {async: false, handler: this._onMessageGetUrl.bind(this)}],
- ['optionsUpdated', {async: false, handler: this._onMessageOptionsUpdated.bind(this)}],
- ['databaseUpdated', {async: false, handler: this._onMessageDatabaseUpdated.bind(this)}],
- ['zoomChanged', {async: false, handler: this._onMessageZoomChanged.bind(this)}]
- ]);
+ this._extensionName = 'Yomichan';
+ try {
+ const manifest = chrome.runtime.getManifest();
+ this._extensionName = `${manifest.name} v${manifest.version}`;
+ } catch (e) {
+ // NOP
}
- // Public
+ this._isBackground = null;
+ this._api = null;
+ this._crossFrame = null;
+ this._isExtensionUnloaded = false;
+ this._isTriggeringExtensionUnloaded = false;
+ this._isReady = false;
+
+ 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)}]
+ ]);
+ }
- get isExtensionUnloaded() {
- return this._isExtensionUnloaded;
- }
+ // Public
- async prepare(isBackground=false) {
- chrome.runtime.onMessage.addListener(this._onMessage.bind(this));
+ get isBackground() {
+ return this._isBackground;
+ }
- if (!isBackground) {
- this.sendMessage({action: 'requestBackendReadySignal'});
- await this._isBackendReadyPromise;
- }
- }
+ get isExtensionUnloaded() {
+ return this._isExtensionUnloaded;
+ }
- ready() {
- this._isReady = true;
- this.sendMessage({action: 'yomichanReady'});
- }
+ get api() {
+ return this._api;
+ }
- isExtensionUrl(url) {
- try {
- return url.startsWith(chrome.runtime.getURL('/'));
- } catch (e) {
- return false;
- }
- }
+ get crossFrame() {
+ return this._crossFrame;
+ }
- getTemporaryListenerResult(eventHandler, userCallback, timeout=null) {
- if (!(
- typeof eventHandler.addListener === 'function' &&
- typeof eventHandler.removeListener === 'function'
- )) {
- throw new Error('Event handler type not supported');
- }
+ async prepare(isBackground=false) {
+ this._isBackground = isBackground;
+ chrome.runtime.onMessage.addListener(this._onMessage.bind(this));
- 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);
- }
+ if (!isBackground) {
+ this._api = new API(this);
- const cleanupResolve = (value) => {
- if (timeoutId !== null) {
- clearTimeout(timeoutId);
- timeoutId = null;
- }
- eventHandler.removeListener(runtimeMessageCallback);
- sendResponse();
- resolve(value);
- };
+ this._crossFrame = new CrossFrameAPI();
+ this._crossFrame.prepare();
- userCallback({action, params}, {resolve: cleanupResolve, sender});
- };
+ this.sendMessage({action: 'requestBackendReadySignal'});
+ await this._isBackendReadyPromise;
- eventHandler.addListener(runtimeMessageCallback);
- });
+ this.on('log', this._onForwardLog.bind(this));
}
+ }
- logWarning(error) {
- this.log(error, 'warn');
- }
+ ready() {
+ this._isReady = true;
+ this.sendMessage({action: 'yomichanReady'});
+ }
- logError(error) {
- this.log(error, 'error');
+ isExtensionUrl(url) {
+ try {
+ return url.startsWith(chrome.runtime.getURL('/'));
+ } catch (e) {
+ return false;
}
+ }
- log(error, level, context=null) {
- if (!isObject(context)) {
- context = this._getLogContext();
- }
+ getTemporaryListenerResult(eventHandler, userCallback, timeout=null) {
+ if (!(
+ typeof eventHandler.addListener === 'function' &&
+ typeof eventHandler.removeListener === 'function'
+ )) {
+ throw new Error('Event handler type not supported');
+ }
- let errorString;
- try {
- errorString = error.toString();
- if (/^\[object \w+\]$/.test(errorString)) {
- errorString = JSON.stringify(error);
+ 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);
}
- } catch (e) {
- errorString = `${error}`;
- }
- let errorStack;
- try {
- errorStack = (typeof error.stack === 'string' ? error.stack.trimRight() : '');
- } catch (e) {
- errorStack = '';
- }
+ const cleanupResolve = (value) => {
+ if (timeoutId !== null) {
+ clearTimeout(timeoutId);
+ timeoutId = null;
+ }
+ eventHandler.removeListener(runtimeMessageCallback);
+ sendResponse();
+ resolve(value);
+ };
- let errorData;
- try {
- errorData = error.data;
- } catch (e) {
- // NOP
- }
+ userCallback({action, params}, {resolve: cleanupResolve, sender});
+ };
- if (errorStack.startsWith(errorString)) {
- errorString = errorStack;
- } else if (errorStack.length > 0) {
- errorString += `\n${errorStack}`;
- }
+ eventHandler.addListener(runtimeMessageCallback);
+ });
+ }
- 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;
- }
+ logWarning(error) {
+ this.log(error, 'warn');
+ }
+
+ logError(error) {
+ this.log(error, 'error');
+ }
- this.trigger('log', {error, level, context});
+ log(error, level, context=null) {
+ if (!isObject(context)) {
+ context = this._getLogContext();
}
- sendMessage(...args) {
- try {
- return chrome.runtime.sendMessage(...args);
- } catch (e) {
- this.triggerExtensionUnloaded();
- throw e;
+ let errorString;
+ try {
+ errorString = error.toString();
+ if (/^\[object \w+\]$/.test(errorString)) {
+ errorString = JSON.stringify(error);
}
+ } catch (e) {
+ errorString = `${error}`;
}
- connect(...args) {
- try {
- return chrome.runtime.connect(...args);
- } catch (e) {
- this.triggerExtensionUnloaded();
- throw e;
- }
+ let errorStack;
+ try {
+ errorStack = (typeof error.stack === 'string' ? error.stack.trimRight() : '');
+ } catch (e) {
+ errorStack = '';
}
- 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;
+ let errorData;
+ try {
+ errorData = error.data;
+ } catch (e) {
+ // NOP
}
- 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;
- }
+ if (errorStack.startsWith(errorString)) {
+ errorString = errorStack;
+ } else if (errorStack.length > 0) {
+ errorString += `\n${errorStack}`;
}
- triggerExtensionUnloaded() {
- this._isExtensionUnloaded = true;
- if (this._isTriggeringExtensionUnloaded) { return; }
- try {
- this._isTriggeringExtensionUnloaded = true;
- this.trigger('extensionUnloaded');
- } finally {
- this._isTriggeringExtensionUnloaded = false;
- }
+ 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;
}
- // Private
+ this.trigger('log', {error, level, context});
+ }
- _getUrl() {
- return location.href;
+ sendMessage(...args) {
+ try {
+ return chrome.runtime.sendMessage(...args);
+ } catch (e) {
+ this.triggerExtensionUnloaded();
+ throw e;
}
+ }
- _getLogContext() {
- return {url: this._getUrl()};
+ connect(...args) {
+ try {
+ return chrome.runtime.connect(...args);
+ } catch (e) {
+ this.triggerExtensionUnloaded();
+ throw e;
}
+ }
- _onMessage({action, params}, sender, callback) {
- const messageHandler = this._messageHandlers.get(action);
- if (typeof messageHandler === 'undefined') { return false; }
- return this.invokeMessageHandler(messageHandler, params, callback, sender);
+ getMessageResponseResult(response) {
+ let error = chrome.runtime.lastError;
+ if (error) {
+ throw new Error(error.message);
}
-
- _onMessageIsReady() {
- return this._isReady;
+ if (!isObject(response)) {
+ throw new Error('Tab did not respond');
}
-
- _onMessageBackendReady() {
- if (this._isBackendReadyPromiseResolve === null) { return; }
- this._isBackendReadyPromiseResolve();
- this._isBackendReadyPromiseResolve = null;
+ error = response.error;
+ if (error) {
+ throw deserializeError(error);
}
+ return response.result;
+ }
- _onMessageGetUrl() {
- return {url: this._getUrl()};
+ 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;
}
+ }
- _onMessageOptionsUpdated({source}) {
- this.trigger('optionsUpdated', {source});
+ triggerExtensionUnloaded() {
+ this._isExtensionUnloaded = true;
+ if (this._isTriggeringExtensionUnloaded) { return; }
+ try {
+ this._isTriggeringExtensionUnloaded = true;
+ this.trigger('extensionUnloaded');
+ } finally {
+ this._isTriggeringExtensionUnloaded = false;
}
+ }
- _onMessageDatabaseUpdated({type, cause}) {
- this.trigger('databaseUpdated', {type, cause});
- }
+ // 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});
+ }
- _onMessageZoomChanged({oldZoomFactor, newZoomFactor}) {
- this.trigger('zoomChanged', {oldZoomFactor, newZoomFactor});
+ _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
}
}
+}
- return new Yomichan();
-})();
+const yomichan = new Yomichan();