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
* DOM
* Display
* Frontend
* PopupFactory
* QueryParser
* api
* dynamicLoader
* wanakana
*/
@ -63,11 +60,6 @@ class DisplaySearch extends Display {
this._runtimeMessageHandlers = new Map([
['updateSearchQuery', {async: false, handler: this._onExternalSearchUpdate.bind(this)}]
]);
this.setOptionsContext({
depth: 0,
url: window.location.href
});
}
async prepare() {
@ -407,7 +399,11 @@ class DisplaySearch extends Display {
yomichan.off('optionsUpdated', onOptionsUpdated);
try {
await this._setupNestedPopups();
await this.setupNestedPopups({
depth: 1,
proxy: false,
isSearchPage: true
});
} catch (e) {
yomichan.logError(e);
}
@ -417,31 +413,4 @@ class DisplaySearch extends Display {
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
* Display
* FrameEndpoint
* Frontend
* PopupFactory
* api
* dynamicLoader
*/
class DisplayFloat extends Display {
constructor() {
super(document.querySelector('#spinner'), document.querySelector('#definitions'));
this._autoPlayAudioTimer = null;
this._nestedPopupsPrepared = false;
this._ownerFrameId = null;
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([
['extensionUnloaded', {async: false, handler: this._onMessageExtensionUnloaded.bind(this)}]
]);
@ -49,13 +37,16 @@ class DisplayFloat extends Display {
this.registerHotkeys([
{key: 'C', modifiers: ['ctrl'], action: 'copy-host-selection'}
]);
this.autoPlayAudioDelay = 400;
}
async prepare() {
await super.prepare();
api.crossFrame.registerHandlers([
['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}]
this.registerMessageHandlers([
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}],
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}]
]);
window.addEventListener('message', this._onWindowMessage.bind(this), false);
@ -98,28 +89,14 @@ class DisplayFloat extends Display {
}
}
autoPlayAudio() {
this._clearAutoPlayTimer();
this._autoPlayAudioTimer = window.setTimeout(() => super.autoPlayAudio(), 400);
}
// Message handling
_onMessage(data) {
authenticateMessageData(data) {
if (!this._frameEndpoint.authenticate(data)) {
throw new Error('Invalid authentication');
}
const {action, params} = data.data;
const handlerInfo = this._messageHandlers.get(action);
if (typeof handlerInfo === 'undefined') {
throw new Error(`Invalid action: ${action}`);
return data.data;
}
const {async, handler} = handlerInfo;
const result = handler(params);
return {async, result};
}
// Message handling
_onWindowMessage(e) {
const data = e.data;
@ -148,22 +125,6 @@ class DisplayFloat extends Display {
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}) {
this._setContentScale(scale);
}
@ -181,13 +142,6 @@ class DisplayFloat extends Display {
return true;
}
_clearAutoPlayTimer() {
if (this._autoPlayAudioTimer) {
window.clearTimeout(this._autoPlayAudioTimer);
this._autoPlayAudioTimer = null;
}
}
_setContentScale(scale) {
const body = document.body;
if (body === null) { return; }
@ -207,7 +161,13 @@ class DisplayFloat extends Display {
yomichan.off('optionsUpdated', onOptionsUpdated);
try {
await this._setupNestedPopups(id, depth, parentFrameId, url);
await this.setupNestedPopups({
id,
depth,
parentFrameId,
url,
proxy: true
});
} catch (e) {
yomichan.logError(e);
}
@ -218,36 +178,6 @@ class DisplayFloat extends Display {
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={}) {
return api.crossFrame.invoke(this._ownerFrameId, action, params);
}

View File

@ -20,11 +20,14 @@
* DOM
* DisplayContext
* DisplayGenerator
* Frontend
* MediaLoader
* PopupFactory
* WindowScroll
* api
* docRangeFromPoint
* docSentenceExtract
* dynamicLoader
*/
class Display {
@ -32,7 +35,7 @@ class Display {
this._spinner = spinner;
this._container = container;
this._definitions = [];
this._optionsContext = null;
this._optionsContext = {depth: 0, url: window.location.href};
this._options = null;
this._context = null;
this._index = 0;
@ -53,11 +56,14 @@ class Display {
this._eventListenersActive = false;
this._clickScanPrevent = false;
this._setContentToken = null;
this._autoPlayAudioTimer = null;
this._autoPlayAudioDelay = 0;
this._mediaLoader = new MediaLoader();
this._displayGenerator = new DisplayGenerator({mediaLoader: this._mediaLoader});
this._windowScroll = new WindowScroll();
this._hotkeys = new Map();
this._actions = new Map();
this._messageHandlers = new Map();
this.registerActions([
['close', () => { this.onEscape(); }],
@ -91,12 +97,29 @@ class Display {
{key: 'P', modifiers: ['alt'], action: 'play-audio'},
{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() {
this._setInteractive(true);
await this._displayGenerator.prepare();
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
api.crossFrame.registerHandlers([
['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}]
]);
}
onError(error) {
@ -155,9 +178,28 @@ class Display {
}
autoPlayAudio() {
this.clearAutoPlayTimer();
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) {
@ -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
_onExtensionUnloaded() {