Merge float into display (#1053)
* Update audio play delay * Move frame endpoint to Display * Move _invokeOwner and close implementation * Move browser info assignment * Move window message handler setup * Move copy implementation * Move document title function * Move extension unload handler * Move close handler * Move history event handlers * Remove DisplayFloat * Remove unused * Organize * Move event listeners into prepare
This commit is contained in:
parent
7234cce4ae
commit
2971f262f9
@ -56,6 +56,7 @@ class DisplaySearch extends Display {
|
|||||||
['AltGraph', new Set()],
|
['AltGraph', new Set()],
|
||||||
['Shift', new Set()]
|
['Shift', new Set()]
|
||||||
]);
|
]);
|
||||||
|
this.autoPlayAudioDelay = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
|
@ -96,7 +96,6 @@
|
|||||||
<script src="/bg/js/template-renderer-proxy.js"></script>
|
<script src="/bg/js/template-renderer-proxy.js"></script>
|
||||||
|
|
||||||
<script src="/bg/js/query-parser.js"></script>
|
<script src="/bg/js/query-parser.js"></script>
|
||||||
<script src="/fg/js/float.js"></script>
|
|
||||||
|
|
||||||
<script src="/fg/js/float-main.js"></script>
|
<script src="/fg/js/float-main.js"></script>
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* global
|
/* global
|
||||||
* DisplayFloat
|
* Display
|
||||||
* api
|
* api
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -25,8 +25,9 @@
|
|||||||
api.forwardLogsToBackend();
|
api.forwardLogsToBackend();
|
||||||
await yomichan.backendReady();
|
await yomichan.backendReady();
|
||||||
|
|
||||||
const display = new DisplayFloat();
|
const display = new Display('popup');
|
||||||
await display.prepare();
|
await display.prepare();
|
||||||
|
display.initializeState();
|
||||||
|
|
||||||
yomichan.ready();
|
yomichan.ready();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2020 Yomichan Authors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global
|
|
||||||
* Display
|
|
||||||
* FrameEndpoint
|
|
||||||
* api
|
|
||||||
*/
|
|
||||||
|
|
||||||
class DisplayFloat extends Display {
|
|
||||||
constructor() {
|
|
||||||
super('popup');
|
|
||||||
this._frameEndpoint = new FrameEndpoint();
|
|
||||||
this._windowMessageHandlers = new Map([
|
|
||||||
['extensionUnloaded', {async: false, handler: this._onMessageExtensionUnloaded.bind(this)}]
|
|
||||||
]);
|
|
||||||
this._browser = null;
|
|
||||||
this._copyTextarea = null;
|
|
||||||
|
|
||||||
this.registerActions([
|
|
||||||
['copyHostSelection', () => this._copySelection()]
|
|
||||||
]);
|
|
||||||
this.registerHotkeys([
|
|
||||||
{key: 'C', modifiers: ['ctrl'], action: 'copyHostSelection'}
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.autoPlayAudioDelay = 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
async prepare() {
|
|
||||||
await super.prepare();
|
|
||||||
|
|
||||||
const {browser} = await api.getEnvironmentInfo();
|
|
||||||
this._browser = browser;
|
|
||||||
|
|
||||||
window.addEventListener('message', this._onWindowMessage.bind(this), false);
|
|
||||||
document.documentElement.addEventListener('mouseup', this._onMouseUp.bind(this), false);
|
|
||||||
document.documentElement.addEventListener('click', this._onClick.bind(this), false);
|
|
||||||
document.documentElement.addEventListener('auxclick', this._onClick.bind(this), false);
|
|
||||||
|
|
||||||
this.initializeState();
|
|
||||||
|
|
||||||
this._frameEndpoint.signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
onEscape() {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDocumentTitle() {
|
|
||||||
try {
|
|
||||||
const targetFrameId = 0;
|
|
||||||
const {title} = await api.crossFrame.invoke(targetFrameId, 'getDocumentInformation');
|
|
||||||
return title;
|
|
||||||
} catch (e) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticateMessageData(data) {
|
|
||||||
if (!this._frameEndpoint.authenticate(data)) {
|
|
||||||
throw new Error('Invalid authentication');
|
|
||||||
}
|
|
||||||
return data.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this._invokeOwner('closePopup');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message handling
|
|
||||||
|
|
||||||
_onWindowMessage(e) {
|
|
||||||
const data = e.data;
|
|
||||||
if (!this._frameEndpoint.authenticate(data)) { return; }
|
|
||||||
|
|
||||||
const {action, params} = data.data;
|
|
||||||
const messageHandler = this._windowMessageHandlers.get(action);
|
|
||||||
if (typeof messageHandler === 'undefined') { return; }
|
|
||||||
|
|
||||||
const callback = () => {}; // NOP
|
|
||||||
yomichan.invokeMessageHandler(messageHandler, params, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMessageExtensionUnloaded() {
|
|
||||||
if (yomichan.isExtensionUnloaded) { return; }
|
|
||||||
yomichan.triggerExtensionUnloaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private
|
|
||||||
|
|
||||||
_onMouseUp(e) {
|
|
||||||
switch (e.button) {
|
|
||||||
case 3: // Back
|
|
||||||
if (this._history.hasPrevious()) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4: // Forward
|
|
||||||
if (this._history.hasNext()) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onClick(e) {
|
|
||||||
switch (e.button) {
|
|
||||||
case 3: // Back
|
|
||||||
if (this._history.hasPrevious()) {
|
|
||||||
e.preventDefault();
|
|
||||||
this._history.back();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4: // Forward
|
|
||||||
if (this._history.hasNext()) {
|
|
||||||
e.preventDefault();
|
|
||||||
this._history.forward();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_copySelection() {
|
|
||||||
if (window.getSelection().toString()) { return false; }
|
|
||||||
this._copyHostSelection();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _copyHostSelection() {
|
|
||||||
switch (this._browser) {
|
|
||||||
case 'firefox':
|
|
||||||
case 'firefox-mobile':
|
|
||||||
{
|
|
||||||
let text;
|
|
||||||
try {
|
|
||||||
text = await this._invokeOwner('getSelectionText');
|
|
||||||
} catch (e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this._copyText(text);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this._invokeOwner('copySelection');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_copyText(text) {
|
|
||||||
const parent = document.body;
|
|
||||||
if (parent === null) { return; }
|
|
||||||
|
|
||||||
let textarea = this._copyTextarea;
|
|
||||||
if (textarea === null) {
|
|
||||||
textarea = document.createElement('textarea');
|
|
||||||
this._copyTextarea = textarea;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea.value = text;
|
|
||||||
parent.appendChild(textarea);
|
|
||||||
textarea.select();
|
|
||||||
document.execCommand('copy');
|
|
||||||
parent.removeChild(textarea);
|
|
||||||
}
|
|
||||||
|
|
||||||
_invokeOwner(action, params={}) {
|
|
||||||
return api.crossFrame.invoke(this.ownerFrameId, action, params);
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,6 +21,7 @@
|
|||||||
* DisplayGenerator
|
* DisplayGenerator
|
||||||
* DisplayHistory
|
* DisplayHistory
|
||||||
* DocumentUtil
|
* DocumentUtil
|
||||||
|
* FrameEndpoint
|
||||||
* Frontend
|
* Frontend
|
||||||
* MediaLoader
|
* MediaLoader
|
||||||
* PopupFactory
|
* PopupFactory
|
||||||
@ -47,19 +48,18 @@ class Display extends EventDispatcher {
|
|||||||
});
|
});
|
||||||
this._styleNode = null;
|
this._styleNode = null;
|
||||||
this._eventListeners = new EventListenerCollection();
|
this._eventListeners = new EventListenerCollection();
|
||||||
this._persistentEventListeners = new EventListenerCollection();
|
|
||||||
this._interactive = false;
|
|
||||||
this._eventListenersActive = false;
|
this._eventListenersActive = false;
|
||||||
this._clickScanPrevent = false;
|
this._clickScanPrevent = false;
|
||||||
this._setContentToken = null;
|
this._setContentToken = null;
|
||||||
this._autoPlayAudioTimer = null;
|
this._autoPlayAudioTimer = null;
|
||||||
this._autoPlayAudioDelay = 0;
|
this._autoPlayAudioDelay = 400;
|
||||||
this._mediaLoader = new MediaLoader();
|
this._mediaLoader = new MediaLoader();
|
||||||
this._displayGenerator = new DisplayGenerator({mediaLoader: this._mediaLoader});
|
this._displayGenerator = new DisplayGenerator({mediaLoader: this._mediaLoader});
|
||||||
this._hotkeys = new Map();
|
this._hotkeys = new Map();
|
||||||
this._actions = new Map();
|
this._actions = new Map();
|
||||||
this._messageHandlers = new Map();
|
this._messageHandlers = new Map();
|
||||||
this._directMessageHandlers = new Map();
|
this._directMessageHandlers = new Map();
|
||||||
|
this._windowMessageHandlers = new Map();
|
||||||
this._history = new DisplayHistory({clearable: true, useBrowserHistory: false});
|
this._history = new DisplayHistory({clearable: true, useBrowserHistory: false});
|
||||||
this._historyChangeIgnore = false;
|
this._historyChangeIgnore = false;
|
||||||
this._historyHasChanged = false;
|
this._historyHasChanged = false;
|
||||||
@ -102,6 +102,9 @@ class Display extends EventDispatcher {
|
|||||||
this._parentFrameId = null;
|
this._parentFrameId = null;
|
||||||
this._ownerFrameId = null;
|
this._ownerFrameId = null;
|
||||||
this._childrenSupported = true;
|
this._childrenSupported = true;
|
||||||
|
this._frameEndpoint = (pageType === 'popup' ? new FrameEndpoint() : null);
|
||||||
|
this._browser = null;
|
||||||
|
this._copyTextarea = null;
|
||||||
|
|
||||||
this.registerActions([
|
this.registerActions([
|
||||||
['close', () => { this.onEscape(); }],
|
['close', () => { this.onEscape(); }],
|
||||||
@ -117,7 +120,8 @@ class Display extends EventDispatcher {
|
|||||||
['addNoteTermKanji', () => { this._noteTryAdd('term-kanji'); }],
|
['addNoteTermKanji', () => { this._noteTryAdd('term-kanji'); }],
|
||||||
['addNoteTermKana', () => { this._noteTryAdd('term-kana'); }],
|
['addNoteTermKana', () => { this._noteTryAdd('term-kana'); }],
|
||||||
['viewNote', () => { this._noteTryView(); }],
|
['viewNote', () => { this._noteTryView(); }],
|
||||||
['playAudio', () => { this._playAudioCurrent(); }]
|
['playAudio', () => { this._playAudioCurrent(); }],
|
||||||
|
['copyHostSelection', () => this._copyHostSelection()]
|
||||||
]);
|
]);
|
||||||
this.registerHotkeys([
|
this.registerHotkeys([
|
||||||
{key: 'Escape', modifiers: [], action: 'close'},
|
{key: 'Escape', modifiers: [], action: 'close'},
|
||||||
@ -133,7 +137,8 @@ class Display extends EventDispatcher {
|
|||||||
{key: 'E', modifiers: ['alt'], action: 'addNoteTermKanji'},
|
{key: 'E', modifiers: ['alt'], action: 'addNoteTermKanji'},
|
||||||
{key: 'R', modifiers: ['alt'], action: 'addNoteTermKana'},
|
{key: 'R', modifiers: ['alt'], action: 'addNoteTermKana'},
|
||||||
{key: 'P', modifiers: ['alt'], action: 'playAudio'},
|
{key: 'P', modifiers: ['alt'], action: 'playAudio'},
|
||||||
{key: 'V', modifiers: ['alt'], action: 'viewNote'}
|
{key: 'V', modifiers: ['alt'], action: 'viewNote'},
|
||||||
|
{key: 'C', modifiers: ['ctrl'], action: 'copyHostSelection'}
|
||||||
]);
|
]);
|
||||||
this.registerMessageHandlers([
|
this.registerMessageHandlers([
|
||||||
['setMode', {async: false, handler: this._onMessageSetMode.bind(this)}]
|
['setMode', {async: false, handler: this._onMessageSetMode.bind(this)}]
|
||||||
@ -146,6 +151,9 @@ class Display extends EventDispatcher {
|
|||||||
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}],
|
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}],
|
||||||
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}]
|
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}]
|
||||||
]);
|
]);
|
||||||
|
this.registerWindowMessageHandlers([
|
||||||
|
['extensionUnloaded', {async: false, handler: this._onMessageExtensionUnloaded.bind(this)}]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
get autoPlayAudioDelay() {
|
get autoPlayAudioDelay() {
|
||||||
@ -169,31 +177,58 @@ class Display extends EventDispatcher {
|
|||||||
return this._mode;
|
return this._mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
get ownerFrameId() {
|
|
||||||
return this._ownerFrameId;
|
|
||||||
}
|
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
this._audioSystem.prepare();
|
// State setup
|
||||||
|
const {documentElement} = document;
|
||||||
this._updateMode();
|
this._updateMode();
|
||||||
this._setInteractive(true);
|
const {browser} = await api.getEnvironmentInfo();
|
||||||
|
this._browser = browser;
|
||||||
|
|
||||||
|
// Prepare
|
||||||
await this._displayGenerator.prepare();
|
await this._displayGenerator.prepare();
|
||||||
|
this._audioSystem.prepare();
|
||||||
this._queryParser.prepare();
|
this._queryParser.prepare();
|
||||||
this._history.prepare();
|
this._history.prepare();
|
||||||
|
|
||||||
|
// Event setup
|
||||||
this._history.on('stateChanged', this._onStateChanged.bind(this));
|
this._history.on('stateChanged', this._onStateChanged.bind(this));
|
||||||
this._queryParser.on('searched', this._onQueryParserSearch.bind(this));
|
this._queryParser.on('searched', this._onQueryParserSearch.bind(this));
|
||||||
|
this._progressIndicatorVisible.on('change', this._onProgressIndicatorVisibleChanged.bind(this));
|
||||||
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
|
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
|
||||||
chrome.runtime.onMessage.addListener(this._onMessage.bind(this));
|
chrome.runtime.onMessage.addListener(this._onMessage.bind(this));
|
||||||
api.crossFrame.registerHandlers([
|
api.crossFrame.registerHandlers([
|
||||||
['popupMessage', {async: 'dynamic', handler: this._onDirectMessage.bind(this)}]
|
['popupMessage', {async: 'dynamic', handler: this._onDirectMessage.bind(this)}]
|
||||||
]);
|
]);
|
||||||
|
window.addEventListener('message', this._onWindowMessage.bind(this), false);
|
||||||
window.addEventListener('focus', this._onWindowFocus.bind(this), false);
|
window.addEventListener('focus', this._onWindowFocus.bind(this), false);
|
||||||
|
|
||||||
|
if (this._pageType === 'popup' && documentElement !== null) {
|
||||||
|
documentElement.addEventListener('mouseup', this._onDocumentElementMouseUp.bind(this), false);
|
||||||
|
documentElement.addEventListener('click', this._onDocumentElementClick.bind(this), false);
|
||||||
|
documentElement.addEventListener('auxclick', this._onDocumentElementClick.bind(this), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', this.onKeyDown.bind(this), false);
|
||||||
|
document.addEventListener('wheel', this._onWheel.bind(this), {passive: false});
|
||||||
|
if (this._closeButton !== null) {
|
||||||
|
this._closeButton.addEventListener('click', this._onCloseButtonClick.bind(this), false);
|
||||||
|
}
|
||||||
|
if (this._navigationPreviousButton !== null) {
|
||||||
|
this._navigationPreviousButton.addEventListener('click', this._onSourceTermView.bind(this), false);
|
||||||
|
}
|
||||||
|
if (this._navigationNextButton !== null) {
|
||||||
|
this._navigationNextButton.addEventListener('click', this._onNextTermView.bind(this), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final preparation
|
||||||
this._updateFocusedElement();
|
this._updateFocusedElement();
|
||||||
this._progressIndicatorVisible.on('change', this._onProgressIndicatorVisibleChanged.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeState() {
|
initializeState() {
|
||||||
this._onStateChanged();
|
this._onStateChanged();
|
||||||
|
if (this._frameEndpoint !== null) {
|
||||||
|
this._frameEndpoint.signal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setHistorySettings({clearable, useBrowserHistory}) {
|
setHistorySettings({clearable, useBrowserHistory}) {
|
||||||
@ -211,7 +246,9 @@ class Display extends EventDispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEscape() {
|
onEscape() {
|
||||||
throw new Error('Override me');
|
if (this._pageType === 'popup') {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
@ -340,6 +377,9 @@ class Display extends EventDispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getDocumentTitle() {
|
async getDocumentTitle() {
|
||||||
|
if (this._pageType === 'float') {
|
||||||
|
return await this._getRootFrameDocumentTitle();
|
||||||
|
}
|
||||||
return document.title;
|
return document.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,16 +412,30 @@ class Display extends EventDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerWindowMessageHandlers(handlers) {
|
||||||
|
for (const [name, handlerInfo] of handlers) {
|
||||||
|
this._windowMessageHandlers.set(name, handlerInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
authenticateMessageData(data) {
|
authenticateMessageData(data) {
|
||||||
|
if (this._frameEndpoint === null) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
if (!this._frameEndpoint.authenticate(data)) {
|
||||||
|
throw new Error('Invalid authentication');
|
||||||
|
}
|
||||||
|
return data.data;
|
||||||
|
}
|
||||||
|
|
||||||
postProcessQuery(query) {
|
postProcessQuery(query) {
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
// NOP
|
if (this._pageType === 'popup') {
|
||||||
|
this._invokeOwner('closePopup');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blurElement(element) {
|
blurElement(element) {
|
||||||
@ -410,6 +464,21 @@ class Display extends EventDispatcher {
|
|||||||
return {async, result};
|
return {async, result};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onWindowMessage({data}) {
|
||||||
|
try {
|
||||||
|
data = this.authenticateMessageData(data);
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {action, params} = data;
|
||||||
|
const messageHandler = this._windowMessageHandlers.get(action);
|
||||||
|
if (typeof messageHandler === 'undefined') { return; }
|
||||||
|
|
||||||
|
const callback = () => {}; // NOP
|
||||||
|
yomichan.invokeMessageHandler(messageHandler, params, callback);
|
||||||
|
}
|
||||||
|
|
||||||
_onMessageSetMode({mode}) {
|
_onMessageSetMode({mode}) {
|
||||||
this._setMode(mode, true);
|
this._setMode(mode, true);
|
||||||
}
|
}
|
||||||
@ -444,6 +513,11 @@ class Display extends EventDispatcher {
|
|||||||
await this.setOptionsContext(optionsContext);
|
await this.setOptionsContext(optionsContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onMessageExtensionUnloaded() {
|
||||||
|
if (yomichan.isExtensionUnloaded) { return; }
|
||||||
|
yomichan.triggerExtensionUnloaded();
|
||||||
|
}
|
||||||
|
|
||||||
// Private
|
// Private
|
||||||
|
|
||||||
async _onStateChanged() {
|
async _onStateChanged() {
|
||||||
@ -751,6 +825,38 @@ class Display extends EventDispatcher {
|
|||||||
console.log(definition);
|
console.log(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onDocumentElementMouseUp(e) {
|
||||||
|
switch (e.button) {
|
||||||
|
case 3: // Back
|
||||||
|
if (this._history.hasPrevious()) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: // Forward
|
||||||
|
if (this._history.hasNext()) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDocumentElementClick(e) {
|
||||||
|
switch (e.button) {
|
||||||
|
case 3: // Back
|
||||||
|
if (this._history.hasPrevious()) {
|
||||||
|
e.preventDefault();
|
||||||
|
this._history.back();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4: // Forward
|
||||||
|
if (this._history.hasNext()) {
|
||||||
|
e.preventDefault();
|
||||||
|
this._history.forward();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_updateDocumentOptions(options) {
|
_updateDocumentOptions(options) {
|
||||||
const data = document.documentElement.dataset;
|
const data = document.documentElement.dataset;
|
||||||
data.ankiEnabled = `${options.anki.enable}`;
|
data.ankiEnabled = `${options.anki.enable}`;
|
||||||
@ -768,31 +874,8 @@ class Display extends EventDispatcher {
|
|||||||
document.documentElement.dataset.yomichanTheme = themeName;
|
document.documentElement.dataset.yomichanTheme = themeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setInteractive(interactive) {
|
|
||||||
interactive = !!interactive;
|
|
||||||
if (this._interactive === interactive) { return; }
|
|
||||||
this._interactive = interactive;
|
|
||||||
|
|
||||||
if (interactive) {
|
|
||||||
this._persistentEventListeners.addEventListener(document, 'keydown', this.onKeyDown.bind(this), false);
|
|
||||||
this._persistentEventListeners.addEventListener(document, 'wheel', this._onWheel.bind(this), {passive: false});
|
|
||||||
if (this._closeButton !== null) {
|
|
||||||
this._persistentEventListeners.addEventListener(this._closeButton, 'click', this._onCloseButtonClick.bind(this));
|
|
||||||
}
|
|
||||||
if (this._navigationPreviousButton !== null) {
|
|
||||||
this._persistentEventListeners.addEventListener(this._navigationPreviousButton, 'click', this._onSourceTermView.bind(this));
|
|
||||||
}
|
|
||||||
if (this._navigationNextButton !== null) {
|
|
||||||
this._persistentEventListeners.addEventListener(this._navigationNextButton, 'click', this._onNextTermView.bind(this));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._persistentEventListeners.removeAllEventListeners();
|
|
||||||
}
|
|
||||||
this._setEventListenersActive(this._eventListenersActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
_setEventListenersActive(active) {
|
_setEventListenersActive(active) {
|
||||||
active = !!active && this._interactive;
|
active = !!active;
|
||||||
if (this._eventListenersActive === active) { return; }
|
if (this._eventListenersActive === active) { return; }
|
||||||
this._eventListenersActive = active;
|
this._eventListenersActive = active;
|
||||||
|
|
||||||
@ -1628,4 +1711,63 @@ class Display extends EventDispatcher {
|
|||||||
this._frontend = frontend;
|
this._frontend = frontend;
|
||||||
await frontend.prepare();
|
await frontend.prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _invokeOwner(action, params={}) {
|
||||||
|
if (this._ownerFrameId === null) {
|
||||||
|
throw new Error('No owner frame');
|
||||||
|
}
|
||||||
|
return await api.crossFrame.invoke(this._ownerFrameId, action, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
_copyHostSelection() {
|
||||||
|
if (window.getSelection().toString()) { return false; }
|
||||||
|
this._copyHostSelectionInner();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _copyHostSelectionInner() {
|
||||||
|
switch (this._browser) {
|
||||||
|
case 'firefox':
|
||||||
|
case 'firefox-mobile':
|
||||||
|
{
|
||||||
|
let text;
|
||||||
|
try {
|
||||||
|
text = await this._invokeOwner('getSelectionText');
|
||||||
|
} catch (e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this._copyText(text);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await this._invokeOwner('copySelection');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_copyText(text) {
|
||||||
|
const parent = document.body;
|
||||||
|
if (parent === null) { return; }
|
||||||
|
|
||||||
|
let textarea = this._copyTextarea;
|
||||||
|
if (textarea === null) {
|
||||||
|
textarea = document.createElement('textarea');
|
||||||
|
this._copyTextarea = textarea;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.value = text;
|
||||||
|
parent.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
parent.removeChild(textarea);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _getRootFrameDocumentTitle() {
|
||||||
|
try {
|
||||||
|
const {title} = await api.crossFrame.invoke(0, 'getDocumentInformation');
|
||||||
|
return title;
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user