diff options
| -rw-r--r-- | ext/fg/frame.html | 8 | ||||
| -rw-r--r-- | ext/fg/js/display-frame.js | 71 | ||||
| -rw-r--r-- | ext/fg/js/driver.js | 6 | ||||
| -rw-r--r-- | ext/fg/js/frame.js | 215 | ||||
| -rw-r--r-- | ext/fg/js/gecko.js | 36 | ||||
| -rw-r--r-- | ext/fg/js/util.js | 59 | ||||
| -rw-r--r-- | ext/manifest.json | 1 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 235 | 
8 files changed, 329 insertions, 302 deletions
| diff --git a/ext/fg/frame.html b/ext/fg/frame.html index 7fda6991..ec0acf64 100644 --- a/ext/fg/frame.html +++ b/ext/fg/frame.html @@ -17,13 +17,17 @@              <div id="orphan">                  <h1>Yomichan Updated!</h1> -                <p>The Yomichan extension has been updated to a new version! In order to continue viewing definitions on this page you must reload this tab or restart your browser.</p> +                <p> +                    The Yomichan extension has been updated to a new version! In order to continue +                    viewing definitions on this page you must reload this tab or restart your browser. +                </p>              </div>              <script src="/mixed/lib/jquery-3.1.1.min.js"></script>              <script src="/mixed/lib/wanakana.min.js"></script>              <script src="/fg/js/util.js"></script> -            <script src="/fg/js/frame.js"></script> +            <script src="/mixed/js/display.js"></script> +            <script src="/fg/js/display-frame.js"></script>          </div>      </body>  </html> diff --git a/ext/fg/js/display-frame.js b/ext/fg/js/display-frame.js new file mode 100644 index 00000000..8f4a790f --- /dev/null +++ b/ext/fg/js/display-frame.js @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016  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/>. + */ + + +window.displayFrame = new class extends Display { +    constructor() { +        super($('#spinner'), $('#content')); +        $(window).on('message', this.onMessage.bind(this)); +    } + +    definitionAdd(definition, mode) { +        return bgDefinitionAdd(definition, mode); +    } + +    definitionsAddable(definitions, mode) { +        return bgDefinitionsAddable(definitions, mode); +    } + +    textRender(template, data) { +        return bgTextRender(template, data); +    } + +    kanjiFind(character) { +        return bgKanjiFind(character); +    } + +    handleError(error) { +        if (window.orphaned) { +            this.api_showOrphaned(); +        } else { +            errorShow(error); +        } +    } + +    onMessage(e) { +        const {action, params} = e.originalEvent.data, method = this['api_' + action]; +        if (typeof(method) === 'function') { +            method.call(this, params); +        } +    } + +    api_showTermDefs({definitions, options, context}) { +        window.scrollTo(0, 0); +        super.showTermDefs(definitions, options, context); +    } + +    api_showKanjiDefs({definitions, options, context}) { +        window.scrollTo(0, 0); +        super.showKanjiDefs(defintions, options, context); +    } + +    api_showOrphaned() { +        $('#content').hide(); +        $('#orphan').show(); +    } +}; diff --git a/ext/fg/js/driver.js b/ext/fg/js/driver.js index 87cce875..73ddd84f 100644 --- a/ext/fg/js/driver.js +++ b/ext/fg/js/driver.js @@ -73,11 +73,11 @@ class Driver {              return;          } -        const searcher = () => this.searchAt(this.lastMousePos); +        const searchFunc = () => this.searchAt(this.lastMousePos);          if (this.popup.isVisible()) { -            searcher(); +            searchFunc();          } else { -            this.popupTimerSet(searcher); +            this.popupTimerSet(searchFunc);          }      } diff --git a/ext/fg/js/frame.js b/ext/fg/js/frame.js deleted file mode 100644 index c1253e41..00000000 --- a/ext/fg/js/frame.js +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2016  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 Frame { -    constructor() { -        this.definitions = []; -        this.audioCache = {}; -        this.sequence = 0; - -        $(window).on('message', e => { -            const {action, params} = e.originalEvent.data, method = this['api_' + action]; -            if (typeof(method) === 'function') { -                method.call(this, params); -            } -        }); -    } - -    api_showTermDefs({definitions, options, context}) { -        const sequence = ++this.sequence; -        const params = { -            definitions, -            grouped: options.general.groupResults, -            addable: options.anki.enabled, -            playback: options.general.audioPlayback -        }; - -        definitions.forEach(definition => { -            definition.sentence = context.sentence; -            definition.url = context.url; -        }); - -        this.definitions = definitions; -        this.showSpinner(false); -        window.scrollTo(0, 0); - -        bgTextRender('terms.html', params).then(content => { -            $('#content').html(content); -            $('.action-add-note').click(this.onAddNote.bind(this)); - -            $('.kanji-link').click(e => { -                e.preventDefault(); -                const character = $(e.target).text(); -                bgKanjiFind(character).then(definitions => this.api_showKanjiDefs({definitions, options, context})); -            }); - -            $('.action-play-audio').click(e => { -                e.preventDefault(); -                const index = $(e.currentTarget).data('index'); -                this.playAudio(this.definitions[index]); -            }); - -            this.updateAddNoteButtons(['term_kanji', 'term_kana'], sequence); -        }).catch(error => { -            this.handleError(error); -        }); -    } - -    api_showKanjiDefs({definitions, options, context}) { -        const sequence = ++this.sequence; -        const params = { -            definitions, -            addable: options.anki.enabled -        }; - -        definitions.forEach(definition => { -            definition.sentence = context.sentence; -            definition.url = context.url; -        }); - -        this.definitions = definitions; -        this.showSpinner(false); -        window.scrollTo(0, 0); - -        bgTextRender('kanji.html', params).then(content => { -            $('#content').html(content); -            $('.action-add-note').click(this.onAddNote.bind(this)); - -            this.updateAddNoteButtons(['kanji'], sequence); -        }).catch(error => { -            this.handleError(error); -        }); -    } - -    api_showOrphaned() { -        $('#content').hide(); -        $('#orphan').show(); -    } - -    findAddNoteButton(index, mode) { -        return $(`.action-add-note[data-index="${index}"][data-mode="${mode}"]`); -    } - -    onAddNote(e) { -        e.preventDefault(); -        this.showSpinner(true); - -        const link = $(e.currentTarget); -        const index = link.data('index'); -        const mode = link.data('mode'); - -        const definition = this.definitions[index]; -        if (mode !== 'kanji') { -            const url = audioUrlBuild(definition); -            const filename = audioFilenameBuild(definition); -            if (url && filename) { -                definition.audio = {url, filename}; -            } -        } - -        bgDefinitionAdd(definition, mode).then(success => { -            if (success) { -                const button = this.findAddNoteButton(index, mode); -                button.addClass('disabled'); -            } else { -                errorShow('note could not be added'); -            } -        }).catch(error => { -            this.handleError(error); -        }).then(() => { -            this.showSpinner(false); -        }); -    } - -    updateAddNoteButtons(modes, sequence) { -        bgDefinitionsAddable(this.definitions, modes).then(states => { -            if (states === null) { -                return; -            } - -            if (sequence !== this.sequence) { -                return; -            } - -            states.forEach((state, index) => { -                for (const mode in state) { -                    const button = this.findAddNoteButton(index, mode); -                    if (state[mode]) { -                        button.removeClass('disabled'); -                    } else { -                        button.addClass('disabled'); -                    } - -                    button.removeClass('pending'); -                } -            }); -        }).catch(error => { -            this.handleError(error); -        }); -    } - -    showSpinner(show) { -        const spinner = $('#spinner'); -        if (show) { -            spinner.show(); -        } else { -            spinner.hide(); -        } -    } - -    playAudio(definition) { -        for (const key in this.audioCache) { -            const audio = this.audioCache[key]; -            if (audio !== null) { -                audio.pause(); -            } -        } - -        const url = audioUrlBuild(definition); -        if (!url) { -            return; -        } - -        let audio = this.audioCache[url]; -        if (audio) { -            audio.currentTime = 0; -            audio.play(); -        } else { -            audio = new Audio(url); -            audio.onloadeddata = () => { -                if (audio.duration === 5.694694 || audio.duration === 5.720718) { -                    audio = new Audio('mp3/button.mp3'); -                } - -                this.audioCache[url] = audio; -                audio.play(); -            }; -        } -    } - -    handleError(error) { -        if (window.orphaned) { -            this.api_showOrphaned(); -        } else { -            errorShow(error); -        } -    } -} - -window.frame = new Frame(); diff --git a/ext/fg/js/gecko.js b/ext/fg/js/gecko.js deleted file mode 100644 index 4057b95c..00000000 --- a/ext/fg/js/gecko.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2017 Alex Yatskov <alex@foosoft.net> - * Author: Alex Yatskov <alex@foosoft.net> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - - -if (!document.caretRangeFromPoint) { -    document.caretRangeFromPoint = (x, y) => { -        const position = document.caretPositionFromPoint(x,y); -        if (position === null) { -            return null; -        } - -        const range = document.createRange(); -        range.setStart(position.offsetNode, position.offset); -        range.setEnd(position.offsetNode, position.offset); -        return range; -    }; -} diff --git a/ext/fg/js/util.js b/ext/fg/js/util.js index c38112f5..4fb8f288 100644 --- a/ext/fg/js/util.js +++ b/ext/fg/js/util.js @@ -119,6 +119,20 @@ function docImposterHide() {  }  function docRangeFromPoint(point, imposter) { +    if (!document.elementFromPoint) { +        document.elementFromPoint = (x, y) => { +            const position = document.caretPositionFromPoint(x,y); +            if (position === null) { +                return null; +            } + +            const range = document.createRange(); +            range.setStart(position.offsetNode, position.offset); +            range.setEnd(position.offsetNode, position.offset); +            return range; +        }; +    } +      const element = document.elementFromPoint(point.x, point.y);      if (element !== null) {          if (element.nodeName === 'IMG' || element.nodeName === 'BUTTON') { @@ -195,51 +209,6 @@ function docSentenceExtract(source, extent) {  /* - * Audio - */ - -function audioUrlBuild(definition) { -    let kana = definition.reading; -    let kanji = definition.expression; - -    if (!kana && !kanji) { -        return null; -    } - -    if (!kana && wanakana.isHiragana(kanji)) { -        kana = kanji; -        kanji = null; -    } - -    const params = []; -    if (kanji) { -        params.push(`kanji=${encodeURIComponent(kanji)}`); -    } -    if (kana) { -        params.push(`kana=${encodeURIComponent(kana)}`); -    } - -    return `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`; -} - -function audioFilenameBuild(definition) { -    if (!definition.reading && !definition.expression) { -        return null; -    } - -    let filename = 'yomichan'; -    if (definition.reading) { -        filename += `_${definition.reading}`; -    } -    if (definition.expression) { -        filename += `_${definition.expression}`; -    } - -    return filename += '.mp3'; -} - - -/*   * Error   */ diff --git a/ext/manifest.json b/ext/manifest.json index 48b15c9d..0c62b49d 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -15,7 +15,6 @@      "content_scripts": [{          "matches": ["http://*/*", "https://*/*", "file://*/*"],          "js": [ -            "fg/js/gecko.js",              "fg/js/util.js",              "fg/js/source-range.js",              "fg/js/source-element.js", diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js new file mode 100644 index 00000000..6a283b5f --- /dev/null +++ b/ext/mixed/js/display.js @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2017  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 Display { +    constructor(spinner, container) { +        this.spinner = spinner; +        this.container = container; +        this.definitions = []; +        this.audioCache = {}; +        this.sequence = 0; +    } + +    definitionAdd(definition, mode) { +        throw 'override me'; +    } + +    definitionsAddable(definitions, mode) { +        throw 'override me'; +    } + +    textRender(template, data) { +        throw 'override me'; +    } + +    kanjiFind(character) { +        throw 'override me'; +    } + +    handleError(error) { +        throw 'override me'; +    } + +    showTermDefs(definitions, options, context) { +        const sequence = ++this.sequence; +        const params = { +            definitions, +            grouped: options.general.groupResults, +            playback: options.general.audioPlayback +        }; + +        if (context) { +            definitions.forEach(definition => { +                definition.sentence = context.sentence; +                definition.url = context.url; +            }); +        } + +        this.definitions = definitions; +        this.spinner.hide(); + +        this.textRender('terms.html', params).then(content => { +            this.container.html(content); +            $('.action-add-note').click(this.onActionAddNote.bind(this)); +            $('.kanji-link').click(this.onKanjiSearch.bind(this)); +            $('.action-play-audio').click(this.onActionPlayAudio.bind(this)); +            return this.adderButtonsUpdate(['term_kanji', 'term_kana'], sequence); +        }).catch(this.handleError.bind(this)); +    } + +    showKanjiDefs({definitions, options, context}) { +        const sequence = ++this.sequence; +        const params = { +            definitions, +            addable: options.anki.enabled +        }; + +        if (context) { +            definitions.forEach(definition => { +                definition.sentence = context.sentence; +                definition.url = context.url; +            }); +        } + +        this.definitions = definitions; +        this.spinner.hide(); + +        this.textRender('kanji.html', params).then(content => { +            this.container.html(content); +            $('.action-add-note').click(this.onActionAddNote.bind(this)); +            return this.adderButtonsUpdate(['kanji'], sequence); +        }).catch(this.handleError.bind(this)); +    } + +    adderButtonFind(index, mode) { +        return $(`.action-add-note[data-index="${index}"][data-mode="${mode}"]`); +    } + +    adderButtonsUpdate(modes, sequence) { +        return this.definitionsAddable(this.definitions, modes).then(states => { +            if (states === null || sequence !== this.sequence) { +                return; +            } + +            states.forEach((state, index) => { +                for (const mode in state) { +                    const button = this.adderButtonFind(index, mode); +                    if (state[mode]) { +                        button.removeClass('disabled'); +                    } else { +                        button.addClass('disabled'); +                    } + +                    button.removeClass('pending'); +                } +            }); +        }); +    } + +    onKanjiSearch(e) { +        e.preventDefault(); +        const character = $(e.target).text(); +        this.kanjiFind(character).then(definitions => { +            this.api_showKanjiDefs({definitions, options, context}); +        }).catch(this.handleError.bind(this)); +    } + +    onActionPlayAudio(e) { +        e.preventDefault(); +        const index = $(e.currentTarget).data('index'); +        this.audioPlay(this.definitions[index]); +    } + +    onActionAddNote(e) { +        e.preventDefault(); +        this.showSpinner(true); + +        const link = $(e.currentTarget); +        const index = link.data('index'); +        const mode = link.data('mode'); + +        const definition = this.definitions[index]; +        if (mode !== 'kanji') { +            const url = Display.audioBuildUrl(definition); +            const filename = Display.audioBuildFilename(definition); +            if (url && filename) { +                definition.audio = {url, filename}; +            } +        } + +        this.definitionAdd(definition, mode).then(success => { +            if (success) { +                const button = this.adderButtonFind(index, mode); +                button.addClass('disabled'); +            } else { +                this.handleError('note could not be added'); +            } +        }).catch(this.handleError.bind(this)).then(() => this.spinner.hide()); +    } + +    audioPlay(definition) { +        for (const key in this.audioCache) { +            const audio = this.audioCache[key]; +            if (audio !== null) { +                audio.pause(); +            } +        } + +        const url = Display.audioBuildUrl(definition); +        if (!url) { +            return; +        } + +        let audio = this.audioCache[url]; +        if (audio) { +            audio.currentTime = 0; +            audio.play(); +        } else { +            audio = new Audio(url); +            audio.onloadeddata = () => { +                if (audio.duration === 5.694694 || audio.duration === 5.720718) { +                    audio = new Audio('/mixed/mp3/button.mp3'); +                } + +                this.audioCache[url] = audio; +                audio.play(); +            }; +        } +    } + +    static audioBuildUrl(definition) { +        let kana = definition.reading; +        let kanji = definition.expression; + +        if (!kana && !kanji) { +            return null; +        } + +        if (!kana && wanakana.isHiragana(kanji)) { +            kana = kanji; +            kanji = null; +        } + +        const params = []; +        if (kanji) { +            params.push(`kanji=${encodeURIComponent(kanji)}`); +        } +        if (kana) { +            params.push(`kana=${encodeURIComponent(kana)}`); +        } + +        return `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`; +    } + +    static audioBuildFilename(definition) { +        if (!definition.reading && !definition.expression) { +            return null; +        } + +        let filename = 'yomichan'; +        if (definition.reading) { +            filename += `_${definition.reading}`; +        } +        if (definition.expression) { +            filename += `_${definition.expression}`; +        } + +        return filename += '.mp3'; +    } +} |