aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/fg/frame.html8
-rw-r--r--ext/fg/js/display-frame.js71
-rw-r--r--ext/fg/js/driver.js6
-rw-r--r--ext/fg/js/frame.js215
-rw-r--r--ext/fg/js/gecko.js36
-rw-r--r--ext/fg/js/util.js59
-rw-r--r--ext/manifest.json1
-rw-r--r--ext/mixed/js/display.js235
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';
+ }
+}