Display audio (#1269)
* Update display definition/definition node handling * Separate display audio controls into a separate class
This commit is contained in:
parent
887150e012
commit
25568637fe
@ -88,6 +88,7 @@
|
|||||||
<script src="/mixed/js/audio-system.js"></script>
|
<script src="/mixed/js/audio-system.js"></script>
|
||||||
<script src="/mixed/js/dictionary-data-util.js"></script>
|
<script src="/mixed/js/dictionary-data-util.js"></script>
|
||||||
<script src="/mixed/js/display.js"></script>
|
<script src="/mixed/js/display.js"></script>
|
||||||
|
<script src="/mixed/js/display-audio.js"></script>
|
||||||
<script src="/mixed/js/display-generator.js"></script>
|
<script src="/mixed/js/display-generator.js"></script>
|
||||||
<script src="/mixed/js/display-history.js"></script>
|
<script src="/mixed/js/display-history.js"></script>
|
||||||
<script src="/mixed/js/display-notification.js"></script>
|
<script src="/mixed/js/display-notification.js"></script>
|
||||||
|
@ -101,6 +101,7 @@
|
|||||||
<script src="/mixed/js/audio-system.js"></script>
|
<script src="/mixed/js/audio-system.js"></script>
|
||||||
<script src="/mixed/js/dictionary-data-util.js"></script>
|
<script src="/mixed/js/dictionary-data-util.js"></script>
|
||||||
<script src="/mixed/js/display.js"></script>
|
<script src="/mixed/js/display.js"></script>
|
||||||
|
<script src="/mixed/js/display-audio.js"></script>
|
||||||
<script src="/mixed/js/display-generator.js"></script>
|
<script src="/mixed/js/display-generator.js"></script>
|
||||||
<script src="/mixed/js/display-history.js"></script>
|
<script src="/mixed/js/display-history.js"></script>
|
||||||
<script src="/mixed/js/display-notification.js"></script>
|
<script src="/mixed/js/display-notification.js"></script>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<button class="action-button action-view-note" hidden disabled data-icon="view-note" title="View added note (Alt + V)"></button>
|
<button class="action-button action-view-note" hidden disabled data-icon="view-note" title="View added note (Alt + V)"></button>
|
||||||
<button class="action-button action-add-note" hidden disabled data-icon="add-term-kanji" data-mode="term-kanji" title="Add expression (Alt + E)"></button>
|
<button class="action-button action-add-note" hidden disabled data-icon="add-term-kanji" data-mode="term-kanji" title="Add expression (Alt + E)"></button>
|
||||||
<button class="action-button action-add-note" hidden disabled data-icon="add-term-kana" data-mode="term-kana" title="Add reading (Alt + R)"></button>
|
<button class="action-button action-add-note" hidden disabled data-icon="add-term-kana" data-mode="term-kana" title="Add reading (Alt + R)"></button>
|
||||||
<button class="action-button action-play-audio" data-icon="play-audio" title="Play audio (Alt + P)"></button>
|
<button class="action-button action-play-audio" data-icon="play-audio" title="Play audio" data-title-default="Play audio"></button>
|
||||||
<span class="entry-current-indicator-icon" title="Current entry (Alt + Up/Down/Home/End/PgUp/PgDn)"></span>
|
<span class="entry-current-indicator-icon" title="Current entry (Alt + Up/Down/Home/End/PgUp/PgDn)"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="term-expression-list"></div>
|
<div class="term-expression-list"></div>
|
||||||
@ -44,7 +44,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="term-expression-details">
|
<div class="term-expression-details">
|
||||||
<button class="action-button action-play-audio" data-icon="play-audio" title="Play audio"></button>
|
<button class="action-button action-play-audio" data-icon="play-audio" title="Play audio" data-title-default="Play audio"></button>
|
||||||
<div class="tags tag-list"></div>
|
<div class="tags tag-list"></div>
|
||||||
</div>
|
</div>
|
||||||
</div></template>
|
</div></template>
|
||||||
|
184
ext/mixed/js/display-audio.js
Normal file
184
ext/mixed/js/display-audio.js
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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
|
||||||
|
* AudioSystem
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DisplayAudio {
|
||||||
|
constructor(display) {
|
||||||
|
this._display = display;
|
||||||
|
this._audioPlaying = null;
|
||||||
|
this._audioSystem = new AudioSystem(true);
|
||||||
|
this._autoPlayAudioTimer = null;
|
||||||
|
this._autoPlayAudioDelay = 400;
|
||||||
|
this._eventListeners = new EventListenerCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
get autoPlayAudioDelay() {
|
||||||
|
return this._autoPlayAudioDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
set autoPlayAudioDelay(value) {
|
||||||
|
this._autoPlayAudioDelay = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
this._audioSystem.prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOptions(options) {
|
||||||
|
const data = document.documentElement.dataset;
|
||||||
|
data.audioEnabled = `${options.audio.enabled && options.audio.sources.length > 0}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupEntries() {
|
||||||
|
this.clearAutoPlayTimer();
|
||||||
|
this._eventListeners.removeAllEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEntry(entry, definitionIndex) {
|
||||||
|
for (const button of entry.querySelectorAll('.action-play-audio')) {
|
||||||
|
const expressionIndex = this._getAudioPlayButtonExpressionIndex(button);
|
||||||
|
this._eventListeners.addEventListener(button, 'click', this._onAudioPlayButtonClick.bind(this, definitionIndex, expressionIndex), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEntriesComplete() {
|
||||||
|
const {audio} = this._display.getOptions();
|
||||||
|
if (!audio.enabled || !audio.autoPlay) { return; }
|
||||||
|
|
||||||
|
this.clearAutoPlayTimer();
|
||||||
|
|
||||||
|
const definitions = this._display.definitions;
|
||||||
|
if (definitions.length === 0) { return; }
|
||||||
|
|
||||||
|
const firstDefinition = definitions[0];
|
||||||
|
if (firstDefinition.type === 'kanji') { return; }
|
||||||
|
|
||||||
|
const callback = () => {
|
||||||
|
this._autoPlayAudioTimer = null;
|
||||||
|
this.playAudio(0, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._autoPlayAudioDelay > 0) {
|
||||||
|
this._autoPlayAudioTimer = setTimeout(callback, this._autoPlayAudioDelay);
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearAutoPlayTimer() {
|
||||||
|
if (this._autoPlayAudioTimer === null) { return; }
|
||||||
|
clearTimeout(this._autoPlayAudioTimer);
|
||||||
|
this._autoPlayAudioTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAudio() {
|
||||||
|
if (this._audioPlaying === null) { return; }
|
||||||
|
this._audioPlaying.pause();
|
||||||
|
this._audioPlaying = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async playAudio(definitionIndex, expressionIndex) {
|
||||||
|
this.stopAudio();
|
||||||
|
this.clearAutoPlayTimer();
|
||||||
|
|
||||||
|
const {definitions} = this._display;
|
||||||
|
if (definitionIndex < 0 || definitionIndex >= definitions.length) { return; }
|
||||||
|
|
||||||
|
const definition = definitions[definitionIndex];
|
||||||
|
if (definition.type === 'kanji') { return; }
|
||||||
|
|
||||||
|
const {expressions} = definition;
|
||||||
|
if (expressionIndex < 0 || expressionIndex >= expressions.length) { return; }
|
||||||
|
|
||||||
|
const {expression, reading} = expressions[expressionIndex];
|
||||||
|
const {sources, textToSpeechVoice, customSourceUrl, volume} = this._display.getOptions().audio;
|
||||||
|
|
||||||
|
const progressIndicatorVisible = this._display.progressIndicatorVisible;
|
||||||
|
const overrideToken = progressIndicatorVisible.setOverride(true);
|
||||||
|
try {
|
||||||
|
// Create audio
|
||||||
|
let audio;
|
||||||
|
let info;
|
||||||
|
try {
|
||||||
|
let index;
|
||||||
|
({audio, index} = await this._audioSystem.createDefinitionAudio(sources, expression, reading, {textToSpeechVoice, customSourceUrl}));
|
||||||
|
info = `From source ${1 + index}: ${sources[index]}`;
|
||||||
|
} catch (e) {
|
||||||
|
audio = this._audioSystem.getFallbackAudio();
|
||||||
|
info = 'Could not find audio';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop any currently playing audio
|
||||||
|
this.stopAudio();
|
||||||
|
|
||||||
|
// Update details
|
||||||
|
for (const button of this._getAudioPlayButtons(definitionIndex, expressionIndex)) {
|
||||||
|
const titleDefault = button.dataset.titleDefault || '';
|
||||||
|
button.title = `${titleDefault}\n${info}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play
|
||||||
|
audio.currentTime = 0;
|
||||||
|
audio.volume = Number.isFinite(volume) ? Math.max(0.0, Math.min(1.0, volume / 100.0)) : 1.0;
|
||||||
|
|
||||||
|
const playPromise = audio.play();
|
||||||
|
this._audioPlaying = audio;
|
||||||
|
|
||||||
|
if (typeof playPromise !== 'undefined') {
|
||||||
|
try {
|
||||||
|
await playPromise;
|
||||||
|
} catch (e) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
progressIndicatorVisible.clearOverride(overrideToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private
|
||||||
|
|
||||||
|
_onAudioPlayButtonClick(definitionIndex, expressionIndex, e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.playAudio(definitionIndex, expressionIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAudioPlayButtonExpressionIndex(button) {
|
||||||
|
const expressionNode = button.closest('.term-expression');
|
||||||
|
if (expressionNode !== null) {
|
||||||
|
const expressionIndex = parseInt(expressionNode.dataset.index, 10);
|
||||||
|
if (Number.isFinite(expressionIndex)) { return expressionIndex; }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAudioPlayButtons(definitionIndex, expressionIndex) {
|
||||||
|
const results = [];
|
||||||
|
const {definitionNodes} = this._display;
|
||||||
|
if (definitionIndex >= 0 && definitionIndex < definitionNodes.length) {
|
||||||
|
const node = definitionNodes[definitionIndex];
|
||||||
|
const button1 = (expressionIndex === 0 ? node.querySelector('.action-play-audio') : null);
|
||||||
|
const button2 = node.querySelector(`.term-expression:nth-of-type(${expressionIndex + 1}) .action-play-audio`);
|
||||||
|
if (button1 !== null) { results.push(button1); }
|
||||||
|
if (button2 !== null) { results.push(button2); }
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
/* global
|
/* global
|
||||||
* AnkiNoteBuilder
|
* AnkiNoteBuilder
|
||||||
* AudioSystem
|
* DisplayAudio
|
||||||
* DisplayGenerator
|
* DisplayGenerator
|
||||||
* DisplayHistory
|
* DisplayHistory
|
||||||
* DisplayNotification
|
* DisplayNotification
|
||||||
@ -42,16 +42,13 @@ class Display extends EventDispatcher {
|
|||||||
this._hotkeyHandler = hotkeyHandler;
|
this._hotkeyHandler = hotkeyHandler;
|
||||||
this._container = document.querySelector('#definitions');
|
this._container = document.querySelector('#definitions');
|
||||||
this._definitions = [];
|
this._definitions = [];
|
||||||
|
this._definitionNodes = [];
|
||||||
this._optionsContext = {depth: 0, url: window.location.href};
|
this._optionsContext = {depth: 0, url: window.location.href};
|
||||||
this._options = null;
|
this._options = null;
|
||||||
this._index = 0;
|
this._index = 0;
|
||||||
this._audioPlaying = null;
|
|
||||||
this._audioSystem = new AudioSystem(true);
|
|
||||||
this._styleNode = null;
|
this._styleNode = null;
|
||||||
this._eventListeners = new EventListenerCollection();
|
this._eventListeners = new EventListenerCollection();
|
||||||
this._setContentToken = null;
|
this._setContentToken = null;
|
||||||
this._autoPlayAudioTimer = null;
|
|
||||||
this._autoPlayAudioDelay = 400;
|
|
||||||
this._mediaLoader = new MediaLoader();
|
this._mediaLoader = new MediaLoader();
|
||||||
this._displayGenerator = new DisplayGenerator({
|
this._displayGenerator = new DisplayGenerator({
|
||||||
japaneseUtil,
|
japaneseUtil,
|
||||||
@ -110,6 +107,7 @@ class Display extends EventDispatcher {
|
|||||||
this._frameResizeEventListeners = new EventListenerCollection();
|
this._frameResizeEventListeners = new EventListenerCollection();
|
||||||
this._tagNotification = null;
|
this._tagNotification = null;
|
||||||
this._tagNotificationContainer = document.querySelector('#content-footer');
|
this._tagNotificationContainer = document.querySelector('#content-footer');
|
||||||
|
this._displayAudio = new DisplayAudio(this);
|
||||||
|
|
||||||
this._hotkeyHandler.registerActions([
|
this._hotkeyHandler.registerActions([
|
||||||
['close', () => { this.close(); }],
|
['close', () => { this.close(); }],
|
||||||
@ -151,11 +149,11 @@ class Display extends EventDispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get autoPlayAudioDelay() {
|
get autoPlayAudioDelay() {
|
||||||
return this._autoPlayAudioDelay;
|
return this._displayAudio.autoPlayAudioDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
set autoPlayAudioDelay(value) {
|
set autoPlayAudioDelay(value) {
|
||||||
this._autoPlayAudioDelay = value;
|
this._displayAudio.autoPlayAudioDelay = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get queryParserVisible() {
|
get queryParserVisible() {
|
||||||
@ -183,6 +181,18 @@ class Display extends EventDispatcher {
|
|||||||
return this._hotkeyHandler;
|
return this._hotkeyHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get definitions() {
|
||||||
|
return this._definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
get definitionNodes() {
|
||||||
|
return this._definitionNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
get progressIndicatorVisible() {
|
||||||
|
return this._progressIndicatorVisible;
|
||||||
|
}
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
// State setup
|
// State setup
|
||||||
const {documentElement} = document;
|
const {documentElement} = document;
|
||||||
@ -192,7 +202,7 @@ class Display extends EventDispatcher {
|
|||||||
|
|
||||||
// Prepare
|
// Prepare
|
||||||
await this._displayGenerator.prepare();
|
await this._displayGenerator.prepare();
|
||||||
this._audioSystem.prepare();
|
this._displayAudio.prepare();
|
||||||
this._queryParser.prepare();
|
this._queryParser.prepare();
|
||||||
this._history.prepare();
|
this._history.prepare();
|
||||||
|
|
||||||
@ -274,6 +284,7 @@ class Display extends EventDispatcher {
|
|||||||
this._updateDocumentOptions(options);
|
this._updateDocumentOptions(options);
|
||||||
this._updateTheme(options.general.popupTheme);
|
this._updateTheme(options.general.popupTheme);
|
||||||
this.setCustomCss(options.general.customPopupCss);
|
this.setCustomCss(options.general.customPopupCss);
|
||||||
|
this._displayAudio.updateOptions(options);
|
||||||
|
|
||||||
this._queryParser.setOptions({
|
this._queryParser.setOptions({
|
||||||
selectedParser: options.parsing.selectedParser,
|
selectedParser: options.parsing.selectedParser,
|
||||||
@ -296,25 +307,8 @@ class Display extends EventDispatcher {
|
|||||||
this._updateDefinitionTextScanner(options);
|
this._updateDefinitionTextScanner(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
autoPlayAudio() {
|
|
||||||
this.clearAutoPlayTimer();
|
|
||||||
|
|
||||||
if (this._definitions.length === 0) { return; }
|
|
||||||
|
|
||||||
const callback = () => this._playAudio(0, 0);
|
|
||||||
|
|
||||||
if (this._autoPlayAudioDelay > 0) {
|
|
||||||
this._autoPlayAudioTimer = setTimeout(callback, this._autoPlayAudioDelay);
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearAutoPlayTimer() {
|
clearAutoPlayTimer() {
|
||||||
if (this._autoPlayAudioTimer !== null) {
|
this._displayAudio.clearAutoPlayTimer();
|
||||||
clearTimeout(this._autoPlayAudioTimer);
|
|
||||||
this._autoPlayAudioTimer = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setContent(details) {
|
setContent(details) {
|
||||||
@ -518,7 +512,10 @@ class Display extends EventDispatcher {
|
|||||||
this._closePopups();
|
this._closePopups();
|
||||||
this._eventListeners.removeAllEventListeners();
|
this._eventListeners.removeAllEventListeners();
|
||||||
this._mediaLoader.unloadAll();
|
this._mediaLoader.unloadAll();
|
||||||
|
this._displayAudio.cleanupEntries();
|
||||||
this._hideTagNotification(false);
|
this._hideTagNotification(false);
|
||||||
|
this._definitions = [];
|
||||||
|
this._definitionNodes = [];
|
||||||
|
|
||||||
// Prepare
|
// Prepare
|
||||||
const urlSearchParams = new URLSearchParams(location.search);
|
const urlSearchParams = new URLSearchParams(location.search);
|
||||||
@ -688,15 +685,6 @@ class Display extends EventDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onAudioPlay(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const link = e.currentTarget;
|
|
||||||
const definitionIndex = this._getClosestDefinitionIndex(link);
|
|
||||||
if (definitionIndex < 0) { return; }
|
|
||||||
const expressionIndex = Math.max(0, this._getClosestExpressionIndex(link));
|
|
||||||
this._playAudio(definitionIndex, expressionIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onNoteAdd(e) {
|
_onNoteAdd(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const link = e.currentTarget;
|
const link = e.currentTarget;
|
||||||
@ -807,7 +795,6 @@ class Display extends EventDispatcher {
|
|||||||
_updateDocumentOptions(options) {
|
_updateDocumentOptions(options) {
|
||||||
const data = document.documentElement.dataset;
|
const data = document.documentElement.dataset;
|
||||||
data.ankiEnabled = `${options.anki.enable}`;
|
data.ankiEnabled = `${options.anki.enable}`;
|
||||||
data.audioEnabled = `${options.audio.enabled && options.audio.sources.length > 0}`;
|
|
||||||
data.glossaryLayoutMode = `${options.general.glossaryLayoutMode}`;
|
data.glossaryLayoutMode = `${options.general.glossaryLayoutMode}`;
|
||||||
data.compactTags = `${options.general.compactTags}`;
|
data.compactTags = `${options.general.compactTags}`;
|
||||||
data.enableSearchTags = `${options.scanning.enableSearchTags}`;
|
data.enableSearchTags = `${options.scanning.enableSearchTags}`;
|
||||||
@ -921,7 +908,9 @@ class Display extends EventDispatcher {
|
|||||||
this._displayGenerator.createKanjiEntry(definition)
|
this._displayGenerator.createKanjiEntry(definition)
|
||||||
);
|
);
|
||||||
entry.dataset.index = `${i}`;
|
entry.dataset.index = `${i}`;
|
||||||
|
this._definitionNodes.push(entry);
|
||||||
this._addEntryEventListeners(entry);
|
this._addEntryEventListeners(entry);
|
||||||
|
this._displayAudio.setupEntry(entry, i);
|
||||||
container.appendChild(entry);
|
container.appendChild(entry);
|
||||||
if (focusEntry === i) {
|
if (focusEntry === i) {
|
||||||
this._focusEntry(i, false);
|
this._focusEntry(i, false);
|
||||||
@ -936,13 +925,7 @@ class Display extends EventDispatcher {
|
|||||||
this._windowScroll.to(x, y);
|
this._windowScroll.to(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
this._displayAudio.setupEntriesComplete();
|
||||||
isTerms &&
|
|
||||||
this._options.audio.enabled &&
|
|
||||||
this._options.audio.autoPlay
|
|
||||||
) {
|
|
||||||
this.autoPlayAudio();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateAdderButtons(token, isTerms, definitions);
|
this._updateAdderButtons(token, isTerms, definitions);
|
||||||
}
|
}
|
||||||
@ -1209,76 +1192,12 @@ class Display extends EventDispatcher {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _playAudio(definitionIndex, expressionIndex) {
|
|
||||||
if (definitionIndex < 0 || definitionIndex >= this._definitions.length) { return; }
|
|
||||||
|
|
||||||
const definition = this._definitions[definitionIndex];
|
|
||||||
if (definition.type === 'kanji') { return; }
|
|
||||||
|
|
||||||
const {expressions} = definition;
|
|
||||||
if (expressionIndex < 0 || expressionIndex >= expressions.length) { return; }
|
|
||||||
|
|
||||||
const {expression, reading} = expressions[expressionIndex];
|
|
||||||
|
|
||||||
const overrideToken = this._progressIndicatorVisible.setOverride(true);
|
|
||||||
try {
|
|
||||||
this._stopPlayingAudio();
|
|
||||||
|
|
||||||
let audio, info;
|
|
||||||
try {
|
|
||||||
const {sources, textToSpeechVoice, customSourceUrl} = this._options.audio;
|
|
||||||
let index;
|
|
||||||
({audio, index} = await this._audioSystem.createDefinitionAudio(sources, expression, reading, {textToSpeechVoice, customSourceUrl}));
|
|
||||||
info = `From source ${1 + index}: ${sources[index]}`;
|
|
||||||
} catch (e) {
|
|
||||||
audio = this._audioSystem.getFallbackAudio();
|
|
||||||
info = 'Could not find audio';
|
|
||||||
}
|
|
||||||
|
|
||||||
const button = this._audioButtonFindImage(definitionIndex, expressionIndex);
|
|
||||||
if (button !== null) {
|
|
||||||
let titleDefault = button.dataset.titleDefault;
|
|
||||||
if (!titleDefault) {
|
|
||||||
titleDefault = button.title || '';
|
|
||||||
button.dataset.titleDefault = titleDefault;
|
|
||||||
}
|
|
||||||
button.title = `${titleDefault}\n${info}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._stopPlayingAudio();
|
|
||||||
|
|
||||||
const volume = Math.max(0.0, Math.min(1.0, this._options.audio.volume / 100.0));
|
|
||||||
this._audioPlaying = audio;
|
|
||||||
audio.currentTime = 0;
|
|
||||||
audio.volume = Number.isFinite(volume) ? volume : 1.0;
|
|
||||||
const playPromise = audio.play();
|
|
||||||
if (typeof playPromise !== 'undefined') {
|
|
||||||
try {
|
|
||||||
await playPromise;
|
|
||||||
} catch (e2) {
|
|
||||||
// NOP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.onError(e);
|
|
||||||
} finally {
|
|
||||||
this._progressIndicatorVisible.clearOverride(overrideToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _playAudioCurrent() {
|
async _playAudioCurrent() {
|
||||||
return await this._playAudio(this._index, 0);
|
return await this._displayAudio.playAudio(this._index, 0);
|
||||||
}
|
|
||||||
|
|
||||||
_stopPlayingAudio() {
|
|
||||||
if (this._audioPlaying !== null) {
|
|
||||||
this._audioPlaying.pause();
|
|
||||||
this._audioPlaying = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getEntry(index) {
|
_getEntry(index) {
|
||||||
const entries = this._container.querySelectorAll('.entry');
|
const entries = this._definitionNodes;
|
||||||
return index >= 0 && index < entries.length ? entries[index] : null;
|
return index >= 0 && index < entries.length ? entries[index] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1293,10 +1212,6 @@ class Display extends EventDispatcher {
|
|||||||
return this._getClosestIndex(element, '.entry');
|
return this._getClosestIndex(element, '.entry');
|
||||||
}
|
}
|
||||||
|
|
||||||
_getClosestExpressionIndex(element) {
|
|
||||||
return this._getClosestIndex(element, '.term-expression');
|
|
||||||
}
|
|
||||||
|
|
||||||
_getClosestIndex(element, selector) {
|
_getClosestIndex(element, selector) {
|
||||||
const node = element.closest(selector);
|
const node = element.closest(selector);
|
||||||
if (node === null) { return -1; }
|
if (node === null) { return -1; }
|
||||||
@ -1324,18 +1239,6 @@ class Display extends EventDispatcher {
|
|||||||
viewerButton.dataset.noteId = noteId;
|
viewerButton.dataset.noteId = noteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
_audioButtonFindImage(index, expressionIndex) {
|
|
||||||
const entry = this._getEntry(index);
|
|
||||||
if (entry === null) { return null; }
|
|
||||||
|
|
||||||
const container = (
|
|
||||||
expressionIndex >= 0 ?
|
|
||||||
entry.querySelector(`.term-expression:nth-of-type(${expressionIndex + 1})`) :
|
|
||||||
entry
|
|
||||||
);
|
|
||||||
return container !== null ? container.querySelector('.action-play-audio>img') : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getElementTop(element) {
|
_getElementTop(element) {
|
||||||
const elementRect = element.getBoundingClientRect();
|
const elementRect = element.getBoundingClientRect();
|
||||||
const documentRect = this._contentScrollBodyElement.getBoundingClientRect();
|
const documentRect = this._contentScrollBodyElement.getBoundingClientRect();
|
||||||
@ -1699,7 +1602,6 @@ class Display extends EventDispatcher {
|
|||||||
this._eventListeners.addEventListener(entry, 'click', this._onEntryClick.bind(this));
|
this._eventListeners.addEventListener(entry, 'click', this._onEntryClick.bind(this));
|
||||||
this._addMultipleEventListeners(entry, '.action-add-note', 'click', this._onNoteAdd.bind(this));
|
this._addMultipleEventListeners(entry, '.action-add-note', 'click', this._onNoteAdd.bind(this));
|
||||||
this._addMultipleEventListeners(entry, '.action-view-note', 'click', this._onNoteView.bind(this));
|
this._addMultipleEventListeners(entry, '.action-view-note', 'click', this._onNoteView.bind(this));
|
||||||
this._addMultipleEventListeners(entry, '.action-play-audio', 'click', this._onAudioPlay.bind(this));
|
|
||||||
this._addMultipleEventListeners(entry, '.kanji-link', 'click', this._onKanjiLookup.bind(this));
|
this._addMultipleEventListeners(entry, '.kanji-link', 'click', this._onKanjiLookup.bind(this));
|
||||||
this._addMultipleEventListeners(entry, '.debug-log-link', 'click', this._onDebugLogClick.bind(this));
|
this._addMultipleEventListeners(entry, '.debug-log-link', 'click', this._onDebugLogClick.bind(this));
|
||||||
this._addMultipleEventListeners(entry, '.tag', 'click', this._onTagClick.bind(this));
|
this._addMultipleEventListeners(entry, '.tag', 'click', this._onTagClick.bind(this));
|
||||||
|
Loading…
Reference in New Issue
Block a user