From c8eb77cfd9e30625ae33739b7de5a86f59d0457c Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 4 Mar 2017 17:30:10 -0800 Subject: [PATCH] wip --- ext/fg/frame.html | 8 +- ext/fg/js/display-frame.js | 71 +++++++++++ ext/fg/js/driver.js | 6 +- ext/fg/js/frame.js | 215 --------------------------------- ext/fg/js/gecko.js | 36 ------ ext/fg/js/util.js | 59 +++------- ext/manifest.json | 1 - ext/mixed/js/display.js | 235 +++++++++++++++++++++++++++++++++++++ 8 files changed, 329 insertions(+), 302 deletions(-) create mode 100644 ext/fg/js/display-frame.js delete mode 100644 ext/fg/js/frame.js delete mode 100644 ext/fg/js/gecko.js create mode 100644 ext/mixed/js/display.js 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 @@

Yomichan Updated!

-

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.

+

+ 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. +

- + + 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 + * 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 . + */ + + +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 - * 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 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 - * Author: Alex Yatskov - * - * 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') { @@ -194,51 +208,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 + * 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 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'; + } +}