diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css index 12bbe8a8..6284058a 100644 --- a/ext/bg/css/settings.css +++ b/ext/bg/css/settings.css @@ -89,19 +89,24 @@ text-align-last: center; } -.condition-group>.condition>div:first-child { +.condition-group>.condition>*:first-child, +.audio-source-list>.audio-source>*:first-child { border-bottom-left-radius: 0; } -.condition-group>.condition:nth-child(n+2)>div:first-child { +.condition-group>.condition:nth-child(n+2)>*:first-child, +.audio-source-list>.audio-source:nth-child(n+2)>*:first-child { border-top-left-radius: 0; } -.condition-group>.condition:nth-child(n+2)>div:last-child>button { +.condition-group>.condition:nth-child(n+2)>div:last-child>button, +.audio-source-list>.audio-source:nth-child(n+2)>*:last-child>button { border-top-right-radius: 0; } -.condition-group>.condition:nth-last-child(n+2)>div:last-child>button { +.condition-group>.condition:nth-last-child(n+2)>div:last-child>button, +.audio-source-list>.audio-source:nth-last-child(n+2)>*:last-child>button { border-bottom-right-radius: 0; } -.condition-group-options>.condition-add { +.condition-group-options>.condition-add, +.audio-source-options>.audio-source-add { border-top-left-radius: 0; border-top-right-radius: 0; } @@ -110,6 +115,19 @@ display: none; } +.audio-source-list { + counter-reset: audio-source-id; +} +.audio-source-list .audio-source-prefix { + flex: 0 0 auto; + width: 39px; + text-align: center; +} +.audio-source-list .audio-source-prefix:after { + counter-increment: audio-source-id; + content: counter(audio-source-id); +} + #custom-popup-css { width: 100%; min-height: 34px; diff --git a/ext/bg/js/audio-ui.js b/ext/bg/js/audio-ui.js new file mode 100644 index 00000000..381129ac --- /dev/null +++ b/ext/bg/js/audio-ui.js @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 Alex Yatskov + * Author: Alex Yatskov + * + * 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 . + */ + + +class AudioSourceUI { + static instantiateTemplate(templateSelector) { + const template = document.querySelector(templateSelector); + const content = document.importNode(template.content, true); + return $(content.firstChild); + } +} + +AudioSourceUI.Container = class Container { + constructor(audioSources, container, addButton) { + this.audioSources = audioSources; + this.container = container; + this.addButton = addButton; + this.children = []; + + this.container.empty(); + + for (const audioSource of toIterable(audioSources)) { + this.children.push(new AudioSourceUI.AudioSource(this, audioSource, this.children.length)); + } + + this.addButton.on('click', () => this.onAddAudioSource()); + } + + cleanup() { + for (const child of this.children) { + child.cleanup(); + } + + this.addButton.off('click'); + this.container.empty(); + } + + save() { + // Override + } + + remove(child) { + const index = this.children.indexOf(child); + if (index < 0) { + return; + } + + child.cleanup(); + this.children.splice(index, 1); + this.audioSources.splice(index, 1); + + for (let i = index; i < this.children.length; ++i) { + this.children[i].index = i; + } + } + + onAddAudioSource() { + const audioSource = this.getUnusedAudioSource(); + this.audioSources.push(audioSource); + this.save(); + this.children.push(new AudioSourceUI.AudioSource(this, audioSource, this.children.length)); + } + + getUnusedAudioSource() { + const audioSourcesAvailable = [ + 'jpod101', + 'jpod101-alternate', + 'jisho', + 'custom' + ]; + for (const source of audioSourcesAvailable) { + if (this.audioSources.indexOf(source) < 0) { + return source; + } + } + return audioSourcesAvailable[0]; + } +}; + +AudioSourceUI.AudioSource = class AudioSource { + constructor(parent, audioSource, index) { + this.parent = parent; + this.audioSource = audioSource; + this.index = index; + + this.container = AudioSourceUI.instantiateTemplate('#audio-source-template').appendTo(parent.container); + this.select = this.container.find('.audio-source-select'); + this.removeButton = this.container.find('.audio-source-remove'); + + this.select.val(audioSource); + + this.select.on('change', () => this.onSelectChanged()); + this.removeButton.on('click', () => this.onRemoveClicked()); + } + + cleanup() { + this.select.off('change'); + this.removeButton.off('click'); + this.container.remove(); + } + + save() { + this.parent.save(); + } + + onSelectChanged() { + this.audioSource = this.select.val(); + this.parent.audioSources[this.index] = this.audioSource; + this.save(); + } + + onRemoveClicked() { + this.parent.remove(this); + this.save(); + } +}; diff --git a/ext/bg/js/settings.js b/ext/bg/js/settings.js index 89ba046d..f3b5ff16 100644 --- a/ext/bg/js/settings.js +++ b/ext/bg/js/settings.js @@ -158,7 +158,7 @@ function formSetupEventListeners() { $('#dict-file-button').click(onDictionaryImportButtonClick); $('#field-templates-reset').click(utilAsync(onAnkiFieldTemplatesReset)); - $('input, select, textarea').not('.anki-model').not('.profile-form *').change(utilAsync(onFormOptionsChanged)); + $('input, select, textarea').not('.anki-model').not('.ignore-form-changes *').change(utilAsync(onFormOptionsChanged)); $('.anki-model').change(utilAsync(onAnkiModelChanged)); } @@ -248,6 +248,7 @@ async function onReady() { showExtensionInformation(); formSetupEventListeners(); + await audioSettingsInitialize(); await profileOptionsSetup(); storageInfoInitialize(); @@ -258,6 +259,20 @@ async function onReady() { $(document).ready(utilAsync(onReady)); +/* + * Audio + */ + +let audioSourceUI = null; + +async function audioSettingsInitialize() { + const optionsContext = getOptionsContext(); + const options = await apiOptionsGet(optionsContext); + audioSourceUI = new AudioSourceUI.Container(options.audio.sources, $('.audio-source-list'), $('.audio-source-add')); + audioSourceUI.save = () => apiOptionsSave(); +} + + /* * Remote options updates */ diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 168dd040..e4710283 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -14,7 +14,7 @@

Yomichan Options

-
+

Profiles

@@ -258,14 +258,23 @@

-
- - +
+ +
+
+ +
+ +
@@ -587,6 +596,7 @@ +