diff options
| -rw-r--r-- | ext/bg/css/settings.css | 28 | ||||
| -rw-r--r-- | ext/bg/js/audio-ui.js | 131 | ||||
| -rw-r--r-- | ext/bg/js/settings.js | 17 | ||||
| -rw-r--r-- | ext/bg/settings.html | 28 | 
4 files changed, 189 insertions, 15 deletions
| 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 <alex@foosoft.net> + * Author: Alex Yatskov <alex@foosoft.net> + * + * 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 <http://www.gnu.org/licenses/>. + */ + + +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(); @@ -259,6 +260,20 @@ $(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 @@                  <h1>Yomichan Options</h1>              </div> -            <div class="profile-form"> +            <div class="profile-form ignore-form-changes">                  <h3>Profiles</h3>                  <p class="help-block"> @@ -258,14 +258,23 @@                      <input type="text" id="audio-custom-source" class="form-control" placeholder="Example: http://localhost/audio.mp3?expression={expression}&reading={reading}">                  </div> -                <div class="form-group"> -                    <label for="audio-playback-source">Audio playback source</label> -                    <select class="form-control" id="audio-playback-source"> -                        <option value="disabled">Disabled</option> -                        <option value="jpod101">JapanesePod101</option> -                        <option value="jpod101-alternate">JapanesePod101 (alternate)</option> -                        <option value="jisho">Jisho.org</option> -                    </select> +                <div class="form-group ignore-form-changes"> +                    <label>Audio playback sources</label> +                    <div class="audio-source-list"></div> +                    <div class="input-group audio-source-options"> +                        <button class="btn btn-default audio-source-add" title="Add audio playback source"><span class="glyphicon glyphicon-plus"></span></button> +                    </div> + +                    <template id="audio-source-template"><div class="input-group audio-source"> +                        <div class="input-group-addon audio-source-prefix"></div> +                        <select class="form-control audio-source-select"> +                            <option value="jpod101">JapanesePod101</option> +                            <option value="jpod101-alternate">JapanesePod101 (alternate)</option> +                            <option value="jisho">Jisho.org</option> +                            <option value="custom">Custom</option> +                        </select> +                        <div class="input-group-btn"><button class="btn btn-danger audio-source-remove" title="Remove"><span class="glyphicon glyphicon-remove"></span></button></div> +                    </div></template>                  </div>              </div> @@ -587,6 +596,7 @@          <script src="/bg/js/anki.js"></script>          <script src="/bg/js/api.js"></script> +        <script src="/bg/js/audio-ui.js"></script>          <script src="/bg/js/conditions.js"></script>          <script src="/bg/js/conditions-ui.js"></script>          <script src="/bg/js/dictionary.js"></script> |