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:
toasted-nutbread 2020-07-18 23:47:02 -04:00 committed by GitHub
parent 27e05f8001
commit 71b97c2019
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 126 additions and 124 deletions

View File

@ -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();
}
} }

View File

@ -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);
} }

View File

@ -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() {