Merge branch 'master' into firefox-amo

This commit is contained in:
Alex Yatskov 2017-06-26 20:23:53 -07:00
commit c23298dbb2
121 changed files with 130 additions and 230 deletions

View File

@ -18,20 +18,23 @@ Yomichan provides advanced features not available in other browser-based diction
the [AnkiConnect](https://foosoft.net/projects/anki-connect) plugin.
* Clean, modern code makes it easy for developers to [contribute](https://github.com/FooSoft/yomichan) new features.
## Browser Support ##
## Downloads ##
* **Google Chrome** (versions 45+)
[![](https://foosoft.net/projects/yomichan/img/chrome-web-store.png)](https://chrome.google.com/webstore/detail/yomichan/ogmnaimimemjmbakcfefmnahgdfhfami)
* **Mozilla Firefox** (versions 51+)
* [Locally hosted](https://foosoft.net/projects/yomichan/dl/latest.xpi) *(recommended)*: Latest and greatest, released simultaneously with the Chrome version.
* [Marketplace hosted](https://addons.mozilla.org/en-US/firefox/addon/yomichan/): Officially hosted version,
likely to be substantially out of date.
## Basic Features ##
[![](https://foosoft.net/projects/yomichan/img/firefox-marketplace.png)](https://addons.mozilla.org/en-US/firefox/addon/yomichan/)
1. Click on the ![](https://foosoft.net/projects/yomichan/img/logo.png) icon in the browser toolbar to open the Yomichan actions dialog.
All extension versions must be reviewed before they are publicly accessible on the Firefox Marketplace. As this
review process can take several months to complete, the approved version of the extension is often out of date. If
you are interested in using the latest and greatest version, please consider using the [locally hosted](https://foosoft.net/projects/yomichan/dl/latest.xpi) version instead.
## Basic Usage ##
1. Click on the ![](https://foosoft.net/projects/yomichan/img/yomichan-icon.png) icon in the browser toolbar to open the Yomichan actions dialog.
[![Actions dialog](https://foosoft.net/projects/yomichan/img/ui-actions-thumb.png)](https://foosoft.net/projects/yomichan/img/ui-actions.png)
@ -49,8 +52,8 @@ Yomichan provides advanced features not available in other browser-based diction
[![Term search results](https://foosoft.net/projects/yomichan/img/ui-terms-thumb.png)](https://foosoft.net/projects/yomichan/img/ui-terms.png)
5. Click on the ![](https://foosoft.net/projects/yomichan/img/button-play-audio.png) icon to hear the term pronounced by a native speaker. If an audio sample is not
available, you will hear a short click instead.
5. Click on the ![](https://foosoft.net/projects/yomichan/img/btn-play-audio.png) icon to hear the term pronounced by a native speaker. If an audio sample is
not available, you will hear a short click instead.
6. Click on individual Kanji in the term definition results to view additional information about those characters
including readings, meanings, and a stroke order diagram.
@ -68,7 +71,7 @@ Unfortunately, as most of the dictionaries released in this format are proprieta
Yomichan. You will need to procure these dictionaries yourself and import them with [Yomichan
Import](https://foosoft.net/projects/yomichan-import). Please see the project page for additional details.
[![Pocket EPWING dictionaries](https://foosoft.net/projects/yomichan/img/dictionary-thumb.png)](https://foosoft.net/projects/yomichan/img/dictionary.jpg)
[![Pocket EPWING dictionaries](https://foosoft.net/projects/yomichan/img/epwing-devices-thumb.png)](https://foosoft.net/projects/yomichan/img/epwing-devices.jpg)
## Anki Integration ##
@ -134,8 +137,8 @@ create a flashcard for <ruby>箸<rt>はし</rt></ruby>, because they share the s
Once Yomichan is configured, it becomes trivial to create new flashcards with a single click. You will see the following
icons next to term definitions.
* Clicking ![](https://foosoft.net/projects/yomichan/img/button-add-expression.png) adds the current expression as Kanji (e.g. 食べる).
* Clicking ![](https://foosoft.net/projects/yomichan/img/button-add-reading.png) adds the current expression as Hiragana or Katakana (e.g. たべる).
* Clicking ![](https://foosoft.net/projects/yomichan/img/btn-add-expression.png) adds the current expression as Kanji (e.g. 食べる).
* Clicking ![](https://foosoft.net/projects/yomichan/img/btn-add-reading.png) adds the current expression as Hiragana or Katakana (e.g. たべる).
Below are some troubleshooting tips you can try if you are unable to create new flashcards:
@ -204,6 +207,13 @@ exact versions used for distribution.
## Frequently Asked Questions ##
* **Will you add support for online dictionaries?**
Online dictionaries will never be implemented because it is impossible to support them in a robust way. In order to
perform Japanese deinflection, Yomichan must execute dozens of database queries per every single word. Factoring in
network latency and the fragility of web scraping, I do not believe that it is possible to realize a good user
experience.
* **What happened to AnkiWeb integration? Why was it removed?**
The author of Anki wants to maintain tight control of AnkiWeb by restricting automated web requests, while at the
@ -245,10 +255,10 @@ exact versions used for distribution.
## Screenshots ##
[![Term definitions](https://foosoft.net/projects/yomichan/img/term-thumb.png)](https://foosoft.net/projects/yomichan/img/term.png)
[![Kanji information](https://foosoft.net/projects/yomichan/img/kanji-thumb.png)](https://foosoft.net/projects/yomichan/img/kanji.png)
[![General options](https://foosoft.net/projects/yomichan/img/options-general-thumb.png)](https://foosoft.net/projects/yomichan/img/options-general.png)
[![Anki options](https://foosoft.net/projects/yomichan/img/options-anki-thumb.png)](https://foosoft.net/projects/yomichan/img/options-anki.png)
[![Term definitions](https://foosoft.net/projects/yomichan/img/ss-terms-thumb.png)](https://foosoft.net/projects/yomichan/img/ss-terms.png)
[![Kanji information](https://foosoft.net/projects/yomichan/img/ss-kanji-thumb.png)](https://foosoft.net/projects/yomichan/img/ss-kanji.png)
[![Dictionary options](https://foosoft.net/projects/yomichan/img/ss-dictionaries-thumb.png)](https://foosoft.net/projects/yomichan/img/ss-dictionaries.png)
[![Anki options](https://foosoft.net/projects/yomichan/img/ss-anki-thumb.png)](https://foosoft.net/projects/yomichan/img/ss-anki.png)
## License ##

View File

@ -7,6 +7,7 @@
<script src="/mixed/lib/handlebars.min.js"></script>
<script src="/mixed/lib/dexie.min.js"></script>
<script src="/mixed/lib/wanakana.min.js"></script>
<script src="/mixed/lib/jszip.min.js"></script>
<script src="/mixed/js/util.js"></script>
<script src="/bg/js/templates.js"></script>
<script src="/bg/js/util.js"></script>

View File

@ -155,7 +155,7 @@ class Database {
return this.db.dictionaries.toArray();
}
importDictionary(indexUrl, callback) {
importDictionary(archive, callback) {
if (this.db === null) {
return Promise.reject('database not initialized');
}
@ -204,7 +204,7 @@ class Database {
return this.db.terms.bulkAdd(rows).then(() => {
if (callback) {
callback(total, current, indexUrl);
callback(total, current);
}
});
};
@ -224,11 +224,11 @@ class Database {
return this.db.kanji.bulkAdd(rows).then(() => {
if (callback) {
callback(total, current, indexUrl);
callback(total, current);
}
});
};
return jsonLoadDb(indexUrl, indexLoaded, termsLoaded, kanjiLoaded).then(() => summary);
return zipLoadDb(archive, indexLoaded, termsLoaded, kanjiLoaded).then(() => summary);
}
}

View File

@ -140,9 +140,7 @@ $(document).ready(() => {
$('#scan-modifier-key').val(options.scanning.modifier);
$('#dict-purge').click(onDictionaryPurge);
$('#dict-importer a').click(onDictionarySetUrl);
$('#dict-import').click(onDictionaryImport);
$('#dict-url').on('input', onDictionaryUpdateUrl);
$('#dict-file').change(onDictionaryImport);
$('#anki-enable').prop('checked', options.anki.enable);
$('#card-tags').val(options.anki.tags.join(' '));
@ -252,50 +250,31 @@ function onDictionaryPurge(e) {
});
}
function onDictionaryImport() {
function onDictionaryImport(e) {
dictionaryErrorShow(null);
dictionarySpinnerShow(true);
const dictUrl = $('#dict-url');
const dictFile = $('#dict-file');
const dictImporter = $('#dict-importer').hide();
const dictProgress = $('#dict-import-progress').show();
const setProgress = percent => dictProgress.find('.progress-bar').css('width', `${percent}%`);
const updateProgress = (total, current) => setProgress(current / total * 100.0);
setProgress(0.0);
optionsLoad().then(options => {
instDb().importDictionary(dictUrl.val(), (total, current) => setProgress(current / total * 100.0)).then(summary => {
return instDb().importDictionary(e.target.files[0], updateProgress).then(summary => {
options.dictionaries[summary.title] = {enabled: true, priority: 0};
return optionsSave(options);
}).then(() => dictionaryGroupsPopulate(options)).catch(dictionaryErrorShow).then(() => {
dictionarySpinnerShow(false);
dictProgress.hide();
dictImporter.show();
dictUrl.val('');
dictUrl.trigger('input');
});
}).then(() => dictionaryGroupsPopulate(options));
}).catch(dictionaryErrorShow).then(() => {
dictFile.val('');
dictionarySpinnerShow(false);
dictProgress.hide();
dictImporter.show();
});
}
function onDictionarySetUrl(e) {
e.preventDefault();
const dictUrl = $('#dict-url');
const url = $(this).data('url');
if (url.includes('/')) {
dictUrl.val(url);
} else {
dictUrl.val(chrome.extension.getURL(`bg/lang/dict/${url}/index.json`));
}
dictUrl.trigger('input');
}
function onDictionaryUpdateUrl() {
$('#dict-import').prop('disabled', $(this).val().length === 0);
}
/*
* Anki
*/

View File

@ -404,7 +404,7 @@ templates['kanji.html'] = template({"1":function(container,depth0,helpers,partia
},"23":function(container,depth0,helpers,partials,data) {
return "<hr>";
},"25":function(container,depth0,helpers,partials,data) {
return "<p>No results found.</p>\n";
return "<p class=\"note\">No results found.</p>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
@ -586,7 +586,7 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia
},"39":function(container,depth0,helpers,partials,data) {
return "<hr>";
},"41":function(container,depth0,helpers,partials,data) {
return "<p>No results found.</p>\n";
return "<p class=\"note\">No results found.</p>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;

View File

@ -451,14 +451,23 @@ function jsonLoadInt(url) {
return jsonLoad(chrome.extension.getURL(url));
}
function jsonLoadDb(indexUrl, indexLoaded, termsLoaded, kanjiLoaded) {
const indexDir = indexUrl.slice(0, indexUrl.lastIndexOf('/'));
return jsonLoad(indexUrl).then(index => {
if (!index.title || !index.version || !index.revision) {
return Promise.reject('unrecognized dictionary format');
/*
* Zip
*/
function zipLoadDb(archive, indexLoaded, termsLoaded, kanjiLoaded) {
return JSZip.loadAsync(archive).then(files => files.files).then(files => {
const indexFile = files['index.json'];
if (!indexFile) {
return Promise.reject('no dictionary index found in archive');
}
if (indexLoaded !== null) {
return indexFile.async('string').then(indexJson => {
const index = JSON.parse(indexJson);
if (!index.title || !index.version || !index.revision) {
return Promise.reject('unrecognized dictionary format');
}
return indexLoaded(
index.title,
index.version,
@ -467,44 +476,45 @@ function jsonLoadDb(indexUrl, indexLoaded, termsLoaded, kanjiLoaded) {
index.termBanks > 0,
index.kanjiBanks > 0
).then(() => index);
}
}).then(index => {
const loaders = [];
const banksTotal = index.termBanks + index.kanjiBanks;
let banksLoaded = 0;
return index;
}).then(index => {
const loaders = [];
const banksTotal = index.termBanks + index.kanjiBanks;
let banksLoaded = 0;
for (let i = 1; i <= index.termBanks; ++i) {
const bankFile = files[`term_bank_${i}.json`];
if (!bankFile) {
return Promise.reject('missing term bank file');
}
for (let i = 1; i <= index.termBanks; ++i) {
const bankUrl = `${indexDir}/term_bank_${i}.json`;
loaders.push(() => jsonLoad(bankUrl).then(entries => termsLoaded(
index.title,
entries,
banksTotal,
banksLoaded++
)));
}
loaders.push(() => bankFile.async('string').then(bankJson => {
const bank = JSON.parse(bankJson);
return termsLoaded(index.title, bank, banksTotal, banksLoaded++);
}));
}
for (let i = 1; i <= index.kanjiBanks; ++i) {
const bankUrl = `${indexDir}/kanji_bank_${i}.json`;
loaders.push(() => jsonLoad(bankUrl).then(entries => kanjiLoaded(
index.title,
entries,
banksTotal,
banksLoaded++
)));
}
for (let i = 1; i <= index.kanjiBanks; ++i) {
const bankFile = files[`kanji_bank_${i}.json`];
if (!bankFile) {
return Promise.reject('missing kanji bank file');
}
let chain = Promise.resolve();
for (const loader of loaders) {
chain = chain.then(loader);
}
loaders.push(() => bankFile.async('string').then(bankJson => {
const bank = JSON.parse(bankJson);
return kanjiLoaded(index.title, bank, banksTotal, banksLoaded++);
}));
}
return chain;
let chain = Promise.resolve();
for (const loader of loaders) {
chain = chain.then(loader);
}
return chain;
});
});
}
/*
* Helpers
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"title":"JMnedict","version":1,"revision":"jmnedict1","tagMeta":{"company":{"category":"name","notes":"company name","order":4},"fem":{"category":"name","notes":"female given name or forename","order":4},"given":{"category":"name","notes":"given name or forename, gender not specified","order":4},"masc":{"category":"name","notes":"male given name or forename","order":4},"ok":{"notes":"old or irregular kana form"},"organization":{"category":"name","notes":"organization name","order":4},"person":{"category":"name","notes":"full name of a particular person","order":4},"place":{"category":"name","notes":"place name","order":4},"product":{"category":"name","notes":"product name","order":4},"station":{"category":"name","notes":"railway station","order":4},"surname":{"category":"name","notes":"family or surname","order":4},"unclass":{"category":"name","notes":"unclassified name","order":4},"work":{"category":"name","notes":"work of art, literature, music, etc. name","order":4}},"termBanks":74,"kanjiBanks":0}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More