From 372cdbf14bd4412c8078e26a6324edd534b6daf4 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sat, 1 Apr 2017 16:54:13 -0700 Subject: [PATCH 1/2] improved behavior for shiftless scanning, update url --- ext/fg/js/driver.js | 2 +- ext/manifest.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/fg/js/driver.js b/ext/fg/js/driver.js index d4cb1532..49147c8f 100644 --- a/ext/fg/js/driver.js +++ b/ext/fg/js/driver.js @@ -75,7 +75,7 @@ window.driver = new class { } const searchFunc = () => this.searchAt(this.lastMousePos); - if (this.popup.isVisible()) { + if (this.options.scanning.requireShift) { searchFunc(); } else { this.popupTimerSet(searchFunc); diff --git a/ext/manifest.json b/ext/manifest.json index 62a70f9c..16c801c1 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -50,7 +50,8 @@ "applications": { "gecko": { "id": "yomichan-live@foosoft.net", - "strict_min_version": "51.0" + "strict_min_version": "51.0", + "update_url": "https://foosoft.net/projects/yomichan/dl/updates.json" } } } From 9ac55fb5d11e5037e808939598591d55d1368b43 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 2 Apr 2017 12:08:15 -0700 Subject: [PATCH 2/2] support for multiple audio sources, version bump --- ext/bg/js/options.js | 4 +- ext/bg/js/util.js | 90 ++++------------------------------- ext/bg/js/yomichan.js | 2 +- ext/bg/options.html | 11 +++-- ext/manifest.json | 2 +- ext/mixed/js/display.js | 4 +- ext/mixed/js/util.js | 103 ++++++++++++++++++++++++---------------- 7 files changed, 86 insertions(+), 130 deletions(-) diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 75f39d24..ad8d83d8 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -25,7 +25,7 @@ function formRead() { return optionsLoad().then(optionsOld => { const optionsNew = $.extend(true, {}, optionsOld); - optionsNew.general.audioPlayback = $('#audio-playback-buttons').prop('checked'); + optionsNew.general.audioSource = $('#audio-playback-source').val(); optionsNew.general.audioVolume = $('#audio-playback-volume').val(); optionsNew.general.groupResults = $('#group-terms-results').prop('checked'); optionsNew.general.softKatakana = $('#soft-katakana-search').prop('checked'); @@ -111,7 +111,7 @@ $(document).ready(() => { handlebarsRegister(); optionsLoad().then(options => { - $('#audio-playback-buttons').prop('checked', options.general.audioPlayback); + $('#audio-playback-source').val(options.general.audioSource); $('#audio-playback-volume').val(options.general.audioVolume); $('#group-terms-results').prop('checked', options.general.groupResults); $('#soft-katakana-search').prop('checked', options.general.softKatakana); diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index 78258c97..05c7ff27 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -25,9 +25,6 @@ function promiseCallback(promise, callback) { return promise.then(result => { callback({result}); }).catch(error => { - /* eslint-disable */ - console.log(error); - /* eslint-enable */ callback({error}); }); } @@ -84,7 +81,7 @@ function optionsSetDefaults(options) { const defaults = { general: { enable: true, - audioPlayback: true, + audioSource: 'jpod101', audioVolume: 100, groupResults: true, softKatakana: true, @@ -137,84 +134,15 @@ function optionsSetDefaults(options) { function optionsVersion(options) { const fixups = [ + () => { }, + () => { }, + () => { }, + () => { }, () => { - const copy = (targetDict, targetKey, sourceDict, sourceKey) => { - targetDict[targetKey] = sourceDict.hasOwnProperty(sourceKey) ? sourceDict[sourceKey] : targetDict[targetKey]; - }; - - copy(options.general, 'autoStart', options, 'activateOnStartup'); - copy(options.general, 'audioPlayback', options, 'enableAudioPlayback'); - copy(options.general, 'softKatakana', options, 'enableSoftKatakanaSearch'); - copy(options.general, 'groupResults', options, 'groupTermResults'); - copy(options.general, 'showAdvanced', options, 'showAdvancedOptions'); - - copy(options.scanning, 'requireShift', options, 'holdShiftToScan'); - copy(options.scanning, 'selectText', options, 'selectMatchedText'); - copy(options.scanning, 'delay', options, 'scanDelay'); - copy(options.scanning, 'length', options, 'scanLength'); - - options.anki.enable = options.ankiMethod === 'ankiconnect'; - - copy(options.anki, 'tags', options, 'ankiCardTags'); - copy(options.anki, 'sentenceExt', options, 'sentenceExtent'); - copy(options.anki.terms, 'deck', options, 'ankiTermDeck'); - copy(options.anki.terms, 'model', options, 'ankiTermModel'); - copy(options.anki.terms, 'fields', options, 'ankiTermFields'); - copy(options.anki.kanji, 'deck', options, 'ankiKanjiDeck'); - copy(options.anki.kanji, 'model', options, 'ankiKanjiModel'); - copy(options.anki.kanji, 'fields', options, 'ankiKanjiFields'); - - for (const title in options.dictionaries) { - const dictionary = options.dictionaries[title]; - dictionary.enabled = dictionary.enableTerms || dictionary.enableKanji; - dictionary.priority = 0; - } - }, - () => { - const fixupFields = fields => { - const fixups = { - '{expression-furigana}': '{furigana}', - '{glossary-list}': '{glossary}' - }; - - for (const name in fields) { - for (const fixup in fixups) { - fields[name] = fields[name].replace(fixup, fixups[fixup]); - } - } - }; - - fixupFields(options.anki.terms.fields); - fixupFields(options.anki.kanji.fields); - }, - () => { - let hasEnabledDict = false; - for (const title in options.dictionaries) { - if (options.dictionaries[title].enabled) { - hasEnabledDict = true; - break; - } - } - - if (!hasEnabledDict) { - for (const title in options.dictionaries) { - options.dictionaries[title].enabled = true; - } - } - }, - () => { - let hasEnabledDict = false; - for (const title in options.dictionaries) { - if (options.dictionaries[title].enabled) { - hasEnabledDict = true; - break; - } - } - - if (!hasEnabledDict) { - for (const title in options.dictionaries) { - options.dictionaries[title].enabled = true; - } + if (options.general.audioPlayback) { + options.general.audioSource = 'jpod101'; + } else { + options.general.audioSource = 'disabled'; } } ]; diff --git a/ext/bg/js/yomichan.js b/ext/bg/js/yomichan.js index 7a26d6c7..feb74b6e 100644 --- a/ext/bg/js/yomichan.js +++ b/ext/bg/js/yomichan.js @@ -121,7 +121,7 @@ window.yomichan = new class { definitionAdd(definition, mode) { let promise = Promise.resolve(); if (mode !== 'kanji') { - promise = audioInject(definition, this.options.anki.terms.fields); + promise = audioInject(definition, this.options.anki.terms.fields, this.options.general.audioSource); } return promise.then(() => { diff --git a/ext/bg/options.html b/ext/bg/options.html index c483c656..939227b0 100644 --- a/ext/bg/options.html +++ b/ext/bg/options.html @@ -37,8 +37,13 @@ -
- +
+ +
@@ -52,7 +57,7 @@
- +
diff --git a/ext/manifest.json b/ext/manifest.json index 62a70f9c..b2752172 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Yomichan", - "version": "1.1.10", + "version": "1.1.11", "description": "Japanese dictionary with Anki integration", "icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"}, diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 36609525..f5ad4849 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -69,7 +69,7 @@ class Display { definitions, addable: options.anki.enable, grouped: options.general.groupResults, - playback: options.general.audioPlayback + playback: options.general.audioSource !== 'disabled' }; if (context) { @@ -335,7 +335,7 @@ class Display { this.audioCache[key].pause(); } - audioBuildUrl(definition, this.responseCache).then(url => { + audioBuildUrl(definition, this.options.general.audioSource, this.responseCache).then(url => { if (!url) { url = '/mixed/mp3/button.mp3'; } diff --git a/ext/mixed/js/util.js b/ext/mixed/js/util.js index 4ce60e4f..edd49873 100644 --- a/ext/mixed/js/util.js +++ b/ext/mixed/js/util.js @@ -21,48 +21,71 @@ * Audio */ -function audioBuildUrl(definition, cache={}) { - return new Promise((resolve, reject) => { - const response = cache[definition.expression]; - if (response) { - resolve(response); - } else { - const data = { - post: 'dictionary_reference', - match_type: 'exact', - search_query: definition.expression - }; +function audioBuildUrl(definition, mode, cache={}) { + if (mode === 'jpod101') { + let kana = definition.reading; + let kanji = definition.expression; - const params = []; - for (const key in data) { - params.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`); - } - - const xhr = new XMLHttpRequest(); - xhr.open('POST', 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post'); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xhr.addEventListener('error', () => reject('failed to scrape audio data')); - xhr.addEventListener('load', () => { - cache[definition.expression] = xhr.responseText; - resolve(xhr.responseText); - }); - - xhr.send(params.join('&')); + if (!kana && wanakana.isHiragana(kanji)) { + kana = kanji; + kanji = null; } - }).then(response => { - const dom = new DOMParser().parseFromString(response, 'text/html'); - for (const row of dom.getElementsByClassName('dc-result-row')) { - try { - const url = row.getElementsByClassName('ill-onebuttonplayer').item(0).getAttribute('data-url'); - const reading = row.getElementsByClassName('dc-vocab_kana').item(0).innerText; - if (url && reading && (!definition.reading || definition.reading === reading)) { - return url; + + const params = []; + if (kanji) { + params.push(`kanji=${encodeURIComponent(kanji)}`); + } + if (kana) { + params.push(`kana=${encodeURIComponent(kana)}`); + } + + const url = `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`; + return Promise.resolve(url); + } else if (mode === 'jpod101-alternate') { + return new Promise((resolve, reject) => { + const response = cache[definition.expression]; + if (response) { + resolve(response); + } else { + const data = { + post: 'dictionary_reference', + match_type: 'exact', + search_query: definition.expression + }; + + const params = []; + for (const key in data) { + params.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`); } - } catch (e) { - // NOP + + const xhr = new XMLHttpRequest(); + xhr.open('POST', 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post'); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.addEventListener('error', () => reject('failed to scrape audio data')); + xhr.addEventListener('load', () => { + cache[definition.expression] = xhr.responseText; + resolve(xhr.responseText); + }); + + xhr.send(params.join('&')); } - } - }); + }).then(response => { + const dom = new DOMParser().parseFromString(response, 'text/html'); + for (const row of dom.getElementsByClassName('dc-result-row')) { + try { + const url = row.getElementsByClassName('ill-onebuttonplayer').item(0).getAttribute('data-url'); + const reading = row.getElementsByClassName('dc-vocab_kana').item(0).innerText; + if (url && reading && (!definition.reading || definition.reading === reading)) { + return url; + } + } catch (e) { + // NOP + } + } + }); + } else { + return Promise.reject('unsupported audio source'); + } } function audioBuildFilename(definition) { @@ -79,7 +102,7 @@ function audioBuildFilename(definition) { } } -function audioInject(definition, fields) { +function audioInject(definition, fields, mode) { const filename = audioBuildFilename(definition); if (!filename) { return Promise.resolve(true); @@ -97,7 +120,7 @@ function audioInject(definition, fields) { return Promise.resolve(true); } - return audioBuildUrl(definition).then(url => { + return audioBuildUrl(definition, mode).then(url => { definition.audio = {url, filename}; return true; }).catch(() => false);