diff options
author | Alex Yatskov <alex@foosoft.net> | 2020-05-22 17:46:16 -0700 |
---|---|---|
committer | Alex Yatskov <alex@foosoft.net> | 2020-05-22 17:46:16 -0700 |
commit | 1480288561cb8b9fb87ad711d970c548329fea98 (patch) | |
tree | 87c2247f6d144407afcc6de316bbacc264582248 /ext/mixed/js/display.js | |
parent | f2186c51e4ef219d158735d30a32bbf3e49c4e1a (diff) | |
parent | d0dcff765f740bf6f0f6523b09cb8b21eb85cd93 (diff) |
Merge branch 'master' into testing
Diffstat (limited to 'ext/mixed/js/display.js')
-rw-r--r-- | ext/mixed/js/display.js | 84 |
1 files changed, 49 insertions, 35 deletions
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 63687dc2..2e59b4ff 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -20,6 +20,7 @@ * DOM * DisplayContext * DisplayGenerator + * MediaLoader * WindowScroll * apiAudioGetUri * apiBroadcastTab @@ -45,7 +46,14 @@ class Display { this.index = 0; this.audioPlaying = null; this.audioFallback = null; - this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)}); + this.audioSystem = new AudioSystem({ + audioUriBuilder: { + getUri: async (definition, source, details) => { + return await apiAudioGetUri(definition, source, details); + } + }, + useCache: true + }); this.styleNode = null; this.eventListeners = new EventListenerCollection(); @@ -55,12 +63,13 @@ class Display { this.clickScanPrevent = false; this.setContentToken = null; - this.displayGenerator = new DisplayGenerator(); + this.mediaLoader = new MediaLoader(); + this.displayGenerator = new DisplayGenerator({mediaLoader: this.mediaLoader}); this.windowScroll = new WindowScroll(); this._onKeyDownHandlers = new Map([ ['Escape', () => { - this.onSearchClear(); + this.onEscape(); return true; }], ['PageUp', (e) => { @@ -168,15 +177,13 @@ class Display { async prepare() { await yomichan.prepare(); await this.displayGenerator.prepare(); - await this.updateOptions(); - yomichan.on('optionsUpdated', () => this.updateOptions()); } onError(_error) { throw new Error('Override me'); } - onSearchClear() { + onEscape() { throw new Error('Override me'); } @@ -331,7 +338,7 @@ class Display { } onKeyDown(e) { - const key = Display.getKeyFromEvent(e); + const key = DOM.getKeyFromEvent(e); const handler = this._onKeyDownHandlers.get(key); if (typeof handler === 'function') { if (handler(e)) { @@ -392,12 +399,6 @@ class Display { updateTheme(themeName) { document.documentElement.dataset.yomichanTheme = themeName; - - const stylesheets = document.querySelectorAll('link[data-yomichan-theme-name]'); - for (const stylesheet of stylesheets) { - const match = (stylesheet.dataset.yomichanThemeName === themeName); - stylesheet.rel = (match ? 'stylesheet' : 'stylesheet alternate'); - } } setCustomCss(css) { @@ -472,6 +473,8 @@ class Display { const token = {}; // Unique identifier token this.setContentToken = token; try { + this.mediaLoader.unloadAll(); + switch (type) { case 'terms': await this.setContentTerms(details.definitions, details.context, token); @@ -784,16 +787,14 @@ class Display { const expression = expressionIndex === -1 ? definition : definition.expressions[expressionIndex]; - if (this.audioPlaying !== null) { - this.audioPlaying.pause(); - this.audioPlaying = null; - } + this._stopPlayingAudio(); - const sources = this.options.audio.sources; - let audio, source, info; + let audio, info; try { - ({audio, source} = await this.audioSystem.getDefinitionAudio(expression, sources)); - info = `From source ${1 + sources.indexOf(source)}: ${source}`; + const {sources, textToSpeechVoice, customSourceUrl} = this.options.audio; + let index; + ({audio, index} = await this.audioSystem.getDefinitionAudio(expression, sources, {textToSpeechVoice, customSourceUrl})); + info = `From source ${1 + index}: ${sources[index]}`; } catch (e) { if (this.audioFallback === null) { this.audioFallback = new Audio('/mixed/mp3/button.mp3'); @@ -802,7 +803,7 @@ class Display { info = 'Could not find audio'; } - const button = this.audioButtonFindImage(entryIndex); + const button = this.audioButtonFindImage(entryIndex, expressionIndex); if (button !== null) { let titleDefault = button.dataset.titleDefault; if (!titleDefault) { @@ -812,10 +813,19 @@ class Display { button.title = `${titleDefault}\n${info}`; } + this._stopPlayingAudio(); + this.audioPlaying = audio; audio.currentTime = 0; audio.volume = this.options.audio.volume / 100.0; - audio.play(); + const playPromise = audio.play(); + if (typeof playPromise !== 'undefined') { + try { + await playPromise; + } catch (e2) { + // NOP + } + } } catch (e) { this.onError(e); } finally { @@ -823,6 +833,13 @@ class Display { } } + _stopPlayingAudio() { + if (this.audioPlaying !== null) { + this.audioPlaying.pause(); + this.audioPlaying = null; + } + } + noteUsesScreenshot(mode) { const optionsAnki = this.options.anki; const fields = (mode === 'kanji' ? optionsAnki.kanji : optionsAnki.terms).fields; @@ -901,9 +918,16 @@ class Display { viewerButton.dataset.noteId = noteId; } - audioButtonFindImage(index) { + audioButtonFindImage(index, expressionIndex) { const entry = this.getEntry(index); - return entry !== null ? entry.querySelector('.action-play-audio>img') : null; + 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; } async getDefinitionsAddable(definitions, modes) { @@ -934,11 +958,6 @@ class Display { return elementRect.top - documentRect.top; } - static getKeyFromEvent(event) { - const key = event.key; - return (typeof key === 'string' ? (key.length === 1 ? key.toUpperCase() : key) : ''); - } - async _getNoteContext() { const documentTitle = await this.getDocumentTitle(); return { @@ -947,9 +966,4 @@ class Display { } }; } - - async _getAudioUri(definition, source) { - const optionsContext = this.getOptionsContext(); - return await apiAudioGetUri(definition, source, optionsContext); - } } |