Display refactoring (#674)
* Move setupNestedPopups to Display * Move auto-play timer and delay into Display * Move some message handler definitions into Display * Move default optionsContext definition
This commit is contained in:
parent
27e05f8001
commit
71b97c2019
@ -19,11 +19,8 @@
|
|||||||
* ClipboardMonitor
|
* ClipboardMonitor
|
||||||
* DOM
|
* DOM
|
||||||
* Display
|
* Display
|
||||||
* Frontend
|
|
||||||
* PopupFactory
|
|
||||||
* QueryParser
|
* QueryParser
|
||||||
* api
|
* api
|
||||||
* dynamicLoader
|
|
||||||
* wanakana
|
* wanakana
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -63,11 +60,6 @@ class DisplaySearch extends Display {
|
|||||||
this._runtimeMessageHandlers = new Map([
|
this._runtimeMessageHandlers = new Map([
|
||||||
['updateSearchQuery', {async: false, handler: this._onExternalSearchUpdate.bind(this)}]
|
['updateSearchQuery', {async: false, handler: this._onExternalSearchUpdate.bind(this)}]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.setOptionsContext({
|
|
||||||
depth: 0,
|
|
||||||
url: window.location.href
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
@ -407,7 +399,11 @@ class DisplaySearch extends Display {
|
|||||||
yomichan.off('optionsUpdated', onOptionsUpdated);
|
yomichan.off('optionsUpdated', onOptionsUpdated);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._setupNestedPopups();
|
await this.setupNestedPopups({
|
||||||
|
depth: 1,
|
||||||
|
proxy: false,
|
||||||
|
isSearchPage: true
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yomichan.logError(e);
|
yomichan.logError(e);
|
||||||
}
|
}
|
||||||
@ -417,31 +413,4 @@ class DisplaySearch extends Display {
|
|||||||
|
|
||||||
await onOptionsUpdated();
|
await onOptionsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setupNestedPopups() {
|
|
||||||
await dynamicLoader.loadScripts([
|
|
||||||
'/mixed/js/text-scanner.js',
|
|
||||||
'/mixed/js/frame-client.js',
|
|
||||||
'/fg/js/frame-offset-forwarder.js',
|
|
||||||
'/fg/js/popup.js',
|
|
||||||
'/fg/js/popup-factory.js',
|
|
||||||
'/fg/js/frontend.js'
|
|
||||||
]);
|
|
||||||
|
|
||||||
const {frameId} = await api.frameInformationGet();
|
|
||||||
|
|
||||||
const popupFactory = new PopupFactory(frameId);
|
|
||||||
popupFactory.prepare();
|
|
||||||
|
|
||||||
const frontend = new Frontend(
|
|
||||||
frameId,
|
|
||||||
popupFactory,
|
|
||||||
{
|
|
||||||
depth: 1,
|
|
||||||
proxy: false,
|
|
||||||
isSearchPage: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await frontend.prepare();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,27 +18,15 @@
|
|||||||
/* global
|
/* global
|
||||||
* Display
|
* Display
|
||||||
* FrameEndpoint
|
* FrameEndpoint
|
||||||
* Frontend
|
|
||||||
* PopupFactory
|
|
||||||
* api
|
* api
|
||||||
* dynamicLoader
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class DisplayFloat extends Display {
|
class DisplayFloat extends Display {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(document.querySelector('#spinner'), document.querySelector('#definitions'));
|
super(document.querySelector('#spinner'), document.querySelector('#definitions'));
|
||||||
this._autoPlayAudioTimer = null;
|
|
||||||
this._nestedPopupsPrepared = false;
|
this._nestedPopupsPrepared = false;
|
||||||
this._ownerFrameId = null;
|
this._ownerFrameId = null;
|
||||||
this._frameEndpoint = new FrameEndpoint();
|
this._frameEndpoint = new FrameEndpoint();
|
||||||
this._messageHandlers = new Map([
|
|
||||||
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}],
|
|
||||||
['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.bind(this)}],
|
|
||||||
['setContent', {async: false, handler: this._onMessageSetContent.bind(this)}],
|
|
||||||
['clearAutoPlayTimer', {async: false, handler: this._onMessageClearAutoPlayTimer.bind(this)}],
|
|
||||||
['setCustomCss', {async: false, handler: this._onMessageSetCustomCss.bind(this)}],
|
|
||||||
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}]
|
|
||||||
]);
|
|
||||||
this._windowMessageHandlers = new Map([
|
this._windowMessageHandlers = new Map([
|
||||||
['extensionUnloaded', {async: false, handler: this._onMessageExtensionUnloaded.bind(this)}]
|
['extensionUnloaded', {async: false, handler: this._onMessageExtensionUnloaded.bind(this)}]
|
||||||
]);
|
]);
|
||||||
@ -49,13 +37,16 @@ class DisplayFloat extends Display {
|
|||||||
this.registerHotkeys([
|
this.registerHotkeys([
|
||||||
{key: 'C', modifiers: ['ctrl'], action: 'copy-host-selection'}
|
{key: 'C', modifiers: ['ctrl'], action: 'copy-host-selection'}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
this.autoPlayAudioDelay = 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
await super.prepare();
|
await super.prepare();
|
||||||
|
|
||||||
api.crossFrame.registerHandlers([
|
this.registerMessageHandlers([
|
||||||
['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}]
|
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}],
|
||||||
|
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}]
|
||||||
]);
|
]);
|
||||||
window.addEventListener('message', this._onWindowMessage.bind(this), false);
|
window.addEventListener('message', this._onWindowMessage.bind(this), false);
|
||||||
|
|
||||||
@ -98,29 +89,15 @@ class DisplayFloat extends Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
autoPlayAudio() {
|
authenticateMessageData(data) {
|
||||||
this._clearAutoPlayTimer();
|
|
||||||
this._autoPlayAudioTimer = window.setTimeout(() => super.autoPlayAudio(), 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message handling
|
|
||||||
|
|
||||||
_onMessage(data) {
|
|
||||||
if (!this._frameEndpoint.authenticate(data)) {
|
if (!this._frameEndpoint.authenticate(data)) {
|
||||||
throw new Error('Invalid authentication');
|
throw new Error('Invalid authentication');
|
||||||
}
|
}
|
||||||
|
return data.data;
|
||||||
const {action, params} = data.data;
|
|
||||||
const handlerInfo = this._messageHandlers.get(action);
|
|
||||||
if (typeof handlerInfo === 'undefined') {
|
|
||||||
throw new Error(`Invalid action: ${action}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const {async, handler} = handlerInfo;
|
|
||||||
const result = handler(params);
|
|
||||||
return {async, result};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Message handling
|
||||||
|
|
||||||
_onWindowMessage(e) {
|
_onWindowMessage(e) {
|
||||||
const data = e.data;
|
const data = e.data;
|
||||||
if (!this._frameEndpoint.authenticate(data)) { return; }
|
if (!this._frameEndpoint.authenticate(data)) { return; }
|
||||||
@ -148,22 +125,6 @@ class DisplayFloat extends Display {
|
|||||||
this._setContentScale(scale);
|
this._setContentScale(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMessageSetOptionsContext({optionsContext}) {
|
|
||||||
this.setOptionsContext(optionsContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMessageSetContent({type, details}) {
|
|
||||||
this.setContent(type, details);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMessageClearAutoPlayTimer() {
|
|
||||||
this._clearAutoPlayTimer.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMessageSetCustomCss({css}) {
|
|
||||||
this.setCustomCss(css);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMessageSetContentScale({scale}) {
|
_onMessageSetContentScale({scale}) {
|
||||||
this._setContentScale(scale);
|
this._setContentScale(scale);
|
||||||
}
|
}
|
||||||
@ -181,13 +142,6 @@ class DisplayFloat extends Display {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearAutoPlayTimer() {
|
|
||||||
if (this._autoPlayAudioTimer) {
|
|
||||||
window.clearTimeout(this._autoPlayAudioTimer);
|
|
||||||
this._autoPlayAudioTimer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_setContentScale(scale) {
|
_setContentScale(scale) {
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
if (body === null) { return; }
|
if (body === null) { return; }
|
||||||
@ -207,7 +161,13 @@ class DisplayFloat extends Display {
|
|||||||
yomichan.off('optionsUpdated', onOptionsUpdated);
|
yomichan.off('optionsUpdated', onOptionsUpdated);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._setupNestedPopups(id, depth, parentFrameId, url);
|
await this.setupNestedPopups({
|
||||||
|
id,
|
||||||
|
depth,
|
||||||
|
parentFrameId,
|
||||||
|
url,
|
||||||
|
proxy: true
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yomichan.logError(e);
|
yomichan.logError(e);
|
||||||
}
|
}
|
||||||
@ -218,36 +178,6 @@ class DisplayFloat extends Display {
|
|||||||
await onOptionsUpdated();
|
await onOptionsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setupNestedPopups(id, depth, parentFrameId, url) {
|
|
||||||
await dynamicLoader.loadScripts([
|
|
||||||
'/mixed/js/text-scanner.js',
|
|
||||||
'/mixed/js/frame-client.js',
|
|
||||||
'/fg/js/popup.js',
|
|
||||||
'/fg/js/popup-proxy.js',
|
|
||||||
'/fg/js/popup-factory.js',
|
|
||||||
'/fg/js/frame-offset-forwarder.js',
|
|
||||||
'/fg/js/frontend.js'
|
|
||||||
]);
|
|
||||||
|
|
||||||
const {frameId} = await api.frameInformationGet();
|
|
||||||
|
|
||||||
const popupFactory = new PopupFactory(frameId);
|
|
||||||
popupFactory.prepare();
|
|
||||||
|
|
||||||
const frontend = new Frontend(
|
|
||||||
frameId,
|
|
||||||
popupFactory,
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
depth,
|
|
||||||
parentFrameId,
|
|
||||||
url,
|
|
||||||
proxy: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await frontend.prepare();
|
|
||||||
}
|
|
||||||
|
|
||||||
_invoke(action, params={}) {
|
_invoke(action, params={}) {
|
||||||
return api.crossFrame.invoke(this._ownerFrameId, action, params);
|
return api.crossFrame.invoke(this._ownerFrameId, action, params);
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,14 @@
|
|||||||
* DOM
|
* DOM
|
||||||
* DisplayContext
|
* DisplayContext
|
||||||
* DisplayGenerator
|
* DisplayGenerator
|
||||||
|
* Frontend
|
||||||
* MediaLoader
|
* MediaLoader
|
||||||
|
* PopupFactory
|
||||||
* WindowScroll
|
* WindowScroll
|
||||||
* api
|
* api
|
||||||
* docRangeFromPoint
|
* docRangeFromPoint
|
||||||
* docSentenceExtract
|
* docSentenceExtract
|
||||||
|
* dynamicLoader
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Display {
|
class Display {
|
||||||
@ -32,7 +35,7 @@ class Display {
|
|||||||
this._spinner = spinner;
|
this._spinner = spinner;
|
||||||
this._container = container;
|
this._container = container;
|
||||||
this._definitions = [];
|
this._definitions = [];
|
||||||
this._optionsContext = null;
|
this._optionsContext = {depth: 0, url: window.location.href};
|
||||||
this._options = null;
|
this._options = null;
|
||||||
this._context = null;
|
this._context = null;
|
||||||
this._index = 0;
|
this._index = 0;
|
||||||
@ -53,11 +56,14 @@ class Display {
|
|||||||
this._eventListenersActive = false;
|
this._eventListenersActive = false;
|
||||||
this._clickScanPrevent = false;
|
this._clickScanPrevent = false;
|
||||||
this._setContentToken = null;
|
this._setContentToken = null;
|
||||||
|
this._autoPlayAudioTimer = null;
|
||||||
|
this._autoPlayAudioDelay = 0;
|
||||||
this._mediaLoader = new MediaLoader();
|
this._mediaLoader = new MediaLoader();
|
||||||
this._displayGenerator = new DisplayGenerator({mediaLoader: this._mediaLoader});
|
this._displayGenerator = new DisplayGenerator({mediaLoader: this._mediaLoader});
|
||||||
this._windowScroll = new WindowScroll();
|
this._windowScroll = new WindowScroll();
|
||||||
this._hotkeys = new Map();
|
this._hotkeys = new Map();
|
||||||
this._actions = new Map();
|
this._actions = new Map();
|
||||||
|
this._messageHandlers = new Map();
|
||||||
|
|
||||||
this.registerActions([
|
this.registerActions([
|
||||||
['close', () => { this.onEscape(); }],
|
['close', () => { this.onEscape(); }],
|
||||||
@ -91,12 +97,29 @@ class Display {
|
|||||||
{key: 'P', modifiers: ['alt'], action: 'play-audio'},
|
{key: 'P', modifiers: ['alt'], action: 'play-audio'},
|
||||||
{key: 'V', modifiers: ['alt'], action: 'view-note'}
|
{key: 'V', modifiers: ['alt'], action: 'view-note'}
|
||||||
]);
|
]);
|
||||||
|
this.registerMessageHandlers([
|
||||||
|
['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.bind(this)}],
|
||||||
|
['setContent', {async: false, handler: this._onMessageSetContent.bind(this)}],
|
||||||
|
['clearAutoPlayTimer', {async: false, handler: this._onMessageClearAutoPlayTimer.bind(this)}],
|
||||||
|
['setCustomCss', {async: false, handler: this._onMessageSetCustomCss.bind(this)}]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
get autoPlayAudioDelay() {
|
||||||
|
return this._autoPlayAudioDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
set autoPlayAudioDelay(value) {
|
||||||
|
this._autoPlayAudioDelay = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
this._setInteractive(true);
|
this._setInteractive(true);
|
||||||
await this._displayGenerator.prepare();
|
await this._displayGenerator.prepare();
|
||||||
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
|
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
|
||||||
|
api.crossFrame.registerHandlers([
|
||||||
|
['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(error) {
|
onError(error) {
|
||||||
@ -155,9 +178,28 @@ class Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
autoPlayAudio() {
|
autoPlayAudio() {
|
||||||
|
this.clearAutoPlayTimer();
|
||||||
|
|
||||||
if (this._definitions.length === 0) { return; }
|
if (this._definitions.length === 0) { return; }
|
||||||
|
|
||||||
this._audioPlay(this._definitions[0], this._getFirstExpressionIndex(), 0);
|
const definition = this._definitions[0];
|
||||||
|
const expressionIndex = this._getFirstExpressionIndex();
|
||||||
|
const callback = () => {
|
||||||
|
this._audioPlay(definition, expressionIndex, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._autoPlayAudioDelay > 0) {
|
||||||
|
this._autoPlayAudioTimer = setTimeout(callback, this._autoPlayAudioDelay);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearAutoPlayTimer() {
|
||||||
|
if (this._autoPlayAudioTimer !== null) {
|
||||||
|
clearTimeout(this._autoPlayAudioTimer);
|
||||||
|
this._autoPlayAudioTimer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setContent(type, details) {
|
async setContent(type, details) {
|
||||||
@ -228,6 +270,67 @@ class Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerMessageHandlers(handlers) {
|
||||||
|
for (const [name, handlerInfo] of handlers) {
|
||||||
|
this._messageHandlers.set(name, handlerInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setupNestedPopups(frontendInitializationData) {
|
||||||
|
await dynamicLoader.loadScripts([
|
||||||
|
'/mixed/js/text-scanner.js',
|
||||||
|
'/mixed/js/frame-client.js',
|
||||||
|
'/fg/js/popup.js',
|
||||||
|
'/fg/js/popup-proxy.js',
|
||||||
|
'/fg/js/popup-factory.js',
|
||||||
|
'/fg/js/frame-offset-forwarder.js',
|
||||||
|
'/fg/js/frontend.js'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const {frameId} = await api.frameInformationGet();
|
||||||
|
|
||||||
|
const popupFactory = new PopupFactory(frameId);
|
||||||
|
popupFactory.prepare();
|
||||||
|
|
||||||
|
const frontend = new Frontend(frameId, popupFactory, frontendInitializationData);
|
||||||
|
await frontend.prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticateMessageData(data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message handlers
|
||||||
|
|
||||||
|
_onMessage(data) {
|
||||||
|
data = this.authenticateMessageData(data);
|
||||||
|
const {action, params} = data;
|
||||||
|
const handlerInfo = this._messageHandlers.get(action);
|
||||||
|
if (typeof handlerInfo === 'undefined') {
|
||||||
|
throw new Error(`Invalid action: ${action}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {async, handler} = handlerInfo;
|
||||||
|
const result = handler(params);
|
||||||
|
return {async, result};
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMessageSetOptionsContext({optionsContext}) {
|
||||||
|
this.setOptionsContext(optionsContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMessageSetContent({type, details}) {
|
||||||
|
this.setContent(type, details);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMessageClearAutoPlayTimer() {
|
||||||
|
this.clearAutoPlayTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMessageSetCustomCss({css}) {
|
||||||
|
this.setCustomCss(css);
|
||||||
|
}
|
||||||
|
|
||||||
// Private
|
// Private
|
||||||
|
|
||||||
_onExtensionUnloaded() {
|
_onExtensionUnloaded() {
|
||||||
|
Loading…
Reference in New Issue
Block a user