Merge branch 'master' into firefox-amo

This commit is contained in:
Alex Yatskov 2017-05-25 23:06:10 -07:00
commit 16baf9a400
16 changed files with 239 additions and 145 deletions

View File

@ -27,7 +27,7 @@ Yomichan provides advanced features not available in other browser-based diction
* **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 (queued for initial approval).
likely to be substantially out of date.
## Basic Features ##
@ -89,7 +89,7 @@ Flashcard fields can be configured with the following steps:
2. Tick the checkbox labeled *Enable Anki integration* (Anki must be running with [AnkiConnect](https://foosoft.net/projects/anki-connect) installed).
3. Select the type of template to configure by clicking on either the *Terms* or *Kanji* tabs.
4. Select the Anki deck and model to use for new creating new flashcards of this type.
5. Fill out the displayed model fields with markers representing the information you wish to include:
5. Fill the model fields with markers corresponding to the information you wish to include (several can be used):
#### Markers for Term Cards ####

View File

@ -27,22 +27,21 @@ function formRead() {
optionsNew.general.showGuide = $('#show-usage-guide').prop('checked');
optionsNew.general.audioSource = $('#audio-playback-source').val();
optionsNew.general.audioVolume = $('#audio-playback-volume').val();
optionsNew.general.audioVolume = parseFloat($('#audio-playback-volume').val());
optionsNew.general.groupResults = $('#group-terms-results').prop('checked');
optionsNew.general.softKatakana = $('#soft-katakana-search').prop('checked');
optionsNew.general.debugInfo = $('#show-debug-info').prop('checked');
optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked');
optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10);
optionsNew.general.popupWidth = parseInt($('#popup-width').val(), 10);
optionsNew.general.popupHeight = parseInt($('#popup-height').val(), 10);
optionsNew.general.popupOffset = parseInt($('#popup-offset').val(), 10);
optionsNew.scanning.requireShift = $('#hold-shift-to-scan').prop('checked');
optionsNew.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked');
optionsNew.scanning.selectText = $('#select-matched-text').prop('checked');
optionsNew.scanning.imposter = $('#search-form-text-fields').prop('checked');
optionsNew.scanning.alphanumeric = $('#search-alphanumeric').prop('checked');
optionsNew.scanning.delay = parseInt($('#scan-delay').val(), 10);
optionsNew.scanning.length = parseInt($('#scan-length').val(), 10);
optionsNew.scanning.modifier = $('#scan-modifier-key').val();
optionsNew.anki.enable = $('#anki-enable').prop('checked');
optionsNew.anki.tags = $('#card-tags').val().split(/[,; ]+/);
@ -85,6 +84,15 @@ function updateVisibility(options) {
} else {
advanced.hide();
}
const debug = $('#debug');
if (options.general.debugInfo) {
const text = JSON.stringify(options, null, 4);
debug.html(handlebarsEscape(text));
debug.show();
} else {
debug.hide();
}
}
function onOptionsChanged(e) {
@ -117,20 +125,19 @@ $(document).ready(() => {
$('#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);
$('#show-debug-info').prop('checked', options.general.debugInfo);
$('#show-advanced-options').prop('checked', options.general.showAdvanced);
$('#max-displayed-results').val(options.general.maxResults);
$('#popup-width').val(options.general.popupWidth);
$('#popup-height').val(options.general.popupHeight);
$('#popup-offset').val(options.general.popupOffset);
$('#hold-shift-to-scan').prop('checked', options.scanning.requireShift);
$('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse);
$('#select-matched-text').prop('checked', options.scanning.selectText);
$('#search-form-text-fields').prop('checked', options.scanning.imposter);
$('#search-alphanumeric').prop('checked', options.scanning.alphanumeric);
$('#scan-delay').val(options.scanning.delay);
$('#scan-length').val(options.scanning.length);
$('#scan-modifier-key').val(options.scanning.modifier);
$('#dict-purge').click(onDictionaryPurge);
$('#dict-importer a').click(onDictionarySetUrl);

View File

@ -328,7 +328,9 @@ templates['kanji.html'] = template({"1":function(container,depth0,helpers,partia
+ ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.tags : depth0),{"name":"each","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </div>\n\n <div class=\"glossary\">\n"
+ ((stack1 = helpers["if"].call(alias1,((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["1"] : stack1),{"name":"if","hash":{},"fn":container.program(11, data, 0),"inverse":container.program(15, data, 0),"data":data})) != null ? stack1 : "")
+ " </div>\n</div>\n";
+ " </div>\n\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.debug : depth0),{"name":"if","hash":{},"fn":container.program(18, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n";
},"2":function(container,depth0,helpers,partials,data) {
return " <a href=\"#\" class=\"action-add-note pending disabled\" data-mode=\"kanji\"><img src=\"/mixed/img/add-kanji.png\" title=\"Add Kanji (Alt + K)\" alt></a>\n";
},"4":function(container,depth0,helpers,partials,data) {
@ -378,25 +380,36 @@ templates['kanji.html'] = template({"1":function(container,depth0,helpers,partia
var stack1;
return container.escapeExpression(container.lambda(((stack1 = (depth0 != null ? depth0.glossary : depth0)) != null ? stack1["0"] : stack1), depth0));
},"18":function(container,depth0,helpers,partials,data,blockParams,depths) {
},"18":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, buffer =
" <pre>";
stack1 = ((helper = (helper = helpers.dumpObject || (depth0 != null ? depth0.dumpObject : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"dumpObject","hash":{},"fn":container.program(19, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},options) : helper));
if (!helpers.dumpObject) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; }
return buffer + "</pre>\n";
},"19":function(container,depth0,helpers,partials,data) {
var stack1;
return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(19, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
},"19":function(container,depth0,helpers,partials,data,blockParams,depths) {
return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : "");
},"21":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(data && data.first),{"name":"unless","hash":{},"fn":container.program(20, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(22, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
},"22":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(data && data.first),{"name":"unless","hash":{},"fn":container.program(23, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n"
+ ((stack1 = container.invokePartial(partials.kanji,depth0,{"name":"kanji","hash":{"root":(depths[1] != null ? depths[1].root : depths[1]),"source":(depths[1] != null ? depths[1].source : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
},"20":function(container,depth0,helpers,partials,data) {
+ ((stack1 = container.invokePartial(partials.kanji,depth0,{"name":"kanji","hash":{"root":(depths[1] != null ? depths[1].root : depths[1]),"source":(depths[1] != null ? depths[1].source : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1]),"debug":(depths[1] != null ? depths[1].debug : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
},"23":function(container,depth0,helpers,partials,data) {
return "<hr>";
},"22":function(container,depth0,helpers,partials,data) {
},"25":function(container,depth0,helpers,partials,data) {
return "<p>No results found.</p>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return "\n"
+ ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(18, data, 0, blockParams, depths),"inverse":container.program(22, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
+ ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(21, data, 0, blockParams, depths),"inverse":container.program(25, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
},"main_d": function(fn, props, container, depth0, data, blockParams, depths) {
var decorators = container.decorators;
@ -482,7 +495,9 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.reasons : depth0),{"name":"if","hash":{},"fn":container.program(22, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n <div class=\"glossary\">\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.grouped : depth0),{"name":"if","hash":{},"fn":container.program(26, data, 0),"inverse":container.program(32, data, 0),"data":data})) != null ? stack1 : "")
+ " </div>\n</div>\n";
+ " </div>\n\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.debug : depth0),{"name":"if","hash":{},"fn":container.program(34, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "</div>\n";
},"13":function(container,depth0,helpers,partials,data) {
return " <a href=\"#\" class=\"action-add-note pending disabled\" data-mode=\"term-kanji\"><img src=\"/mixed/img/add-term-kanji.png\" title=\"Add expression (Alt + E)\" alt></a>\n <a href=\"#\" class=\"action-add-note pending disabled\" data-mode=\"term-kana\"><img src=\"/mixed/img/add-term-kana.png\" title=\"Add reading (Alt + R)\" alt></a>\n";
},"15":function(container,depth0,helpers,partials,data) {
@ -547,25 +562,36 @@ templates['terms.html'] = template({"1":function(container,depth0,helpers,partia
var stack1;
return ((stack1 = container.invokePartial(partials.definition,depth0,{"name":"definition","data":data,"indent":" ","helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
},"34":function(container,depth0,helpers,partials,data,blockParams,depths) {
},"34":function(container,depth0,helpers,partials,data) {
var stack1, helper, options, buffer =
" <pre>";
stack1 = ((helper = (helper = helpers.dumpObject || (depth0 != null ? depth0.dumpObject : depth0)) != null ? helper : helpers.helperMissing),(options={"name":"dumpObject","hash":{},"fn":container.program(35, data, 0),"inverse":container.noop,"data":data}),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},options) : helper));
if (!helpers.dumpObject) { stack1 = helpers.blockHelperMissing.call(depth0,stack1,options)}
if (stack1 != null) { buffer += stack1; }
return buffer + "</pre>\n";
},"35":function(container,depth0,helpers,partials,data) {
var stack1;
return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(35, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
},"35":function(container,depth0,helpers,partials,data,blockParams,depths) {
return ((stack1 = container.lambda(depth0, depth0)) != null ? stack1 : "");
},"37":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(data && data.first),{"name":"unless","hash":{},"fn":container.program(36, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
return ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"each","hash":{},"fn":container.program(38, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "");
},"38":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return ((stack1 = helpers.unless.call(depth0 != null ? depth0 : {},(data && data.first),{"name":"unless","hash":{},"fn":container.program(39, data, 0, blockParams, depths),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n"
+ ((stack1 = container.invokePartial(partials.term,depth0,{"name":"term","hash":{"playback":(depths[1] != null ? depths[1].playback : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1]),"grouped":(depths[1] != null ? depths[1].grouped : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
},"36":function(container,depth0,helpers,partials,data) {
+ ((stack1 = container.invokePartial(partials.term,depth0,{"name":"term","hash":{"playback":(depths[1] != null ? depths[1].playback : depths[1]),"addable":(depths[1] != null ? depths[1].addable : depths[1]),"grouped":(depths[1] != null ? depths[1].grouped : depths[1]),"debug":(depths[1] != null ? depths[1].debug : depths[1])},"data":data,"helpers":helpers,"partials":partials,"decorators":container.decorators})) != null ? stack1 : "");
},"39":function(container,depth0,helpers,partials,data) {
return "<hr>";
},"38":function(container,depth0,helpers,partials,data) {
},"41":function(container,depth0,helpers,partials,data) {
return "<p>No results found.</p>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) {
var stack1;
return "\n\n"
+ ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(34, data, 0, blockParams, depths),"inverse":container.program(38, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
+ ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.definitions : depth0),{"name":"if","hash":{},"fn":container.program(37, data, 0, blockParams, depths),"inverse":container.program(41, data, 0, blockParams, depths),"data":data})) != null ? stack1 : "");
},"main_d": function(fn, props, container, depth0, data, blockParams, depths) {
var decorators = container.decorators;

View File

@ -41,7 +41,7 @@ class Translator {
});
}
findTerms(text, dictionaries, softKatakana, alphanumeric) {
findTerms(text, dictionaries, alphanumeric) {
const titles = Object.keys(dictionaries);
const cache = {};
@ -54,10 +54,10 @@ class Translator {
return this.findTermsDeinflected(text, titles, cache).then(deinfLiteral => {
const textHiragana = wanakana._katakanaToHiragana(text);
if (text !== textHiragana && softKatakana) {
return this.findTermsDeinflected(textHiragana, titles, cache).then(deinfHiragana => deinfLiteral.concat(deinfHiragana));
} else {
if (text === textHiragana) {
return deinfLiteral;
} else {
return this.findTermsDeinflected(textHiragana, titles, cache).then(deinfHiragana => deinfLiteral.concat(deinfHiragana));
}
}).then(deinflections => {
let definitions = [];
@ -91,8 +91,8 @@ class Translator {
});
}
findTermsGrouped(text, dictionaries, softKatakana, alphanumeric) {
return this.findTerms(text, dictionaries, softKatakana, alphanumeric).then(({length, definitions}) => {
findTermsGrouped(text, dictionaries, alphanumeric) {
return this.findTerms(text, dictionaries, alphanumeric).then(({length, definitions}) => {
return {length, definitions: dictTermsGroup(definitions, dictionaries)};
});
}

View File

@ -98,7 +98,7 @@ function optionsSetDefaults(options) {
audioSource: 'jpod101',
audioVolume: 100,
groupResults: true,
softKatakana: true,
debugInfo: false,
maxResults: 32,
showAdvanced: false,
popupWidth: 400,
@ -108,13 +108,12 @@ function optionsSetDefaults(options) {
},
scanning: {
requireShift: true,
middleMouse: true,
selectText: true,
imposter: true,
alphanumeric: true,
delay: 15,
length: 10
length: 10,
modifier: 'shift'
},
dictionaries: {},
@ -150,10 +149,10 @@ function optionsSetDefaults(options) {
function optionsVersion(options) {
const fixups = [
() => { },
() => { },
() => { },
() => { },
() => {},
() => {},
() => {},
() => {},
() => {
if (options.general.audioPlayback) {
options.general.audioSource = 'jpod101';
@ -163,6 +162,13 @@ function optionsVersion(options) {
},
() => {
options.general.showGuide = false;
},
() => {
if (options.scanning.requireShift) {
options.scanning.modifier = 'shift';
} else {
options.scanning.modifier = 'none';
}
}
];
@ -503,6 +509,15 @@ function jsonLoadDb(indexUrl, indexLoaded, termsLoaded, kanjiLoaded) {
* Helpers
*/
function handlebarsEscape(text) {
return Handlebars.Utils.escapeExpression(text);
}
function handlebarsDumpObject(options) {
const dump = JSON.stringify(options.fn(this), null, 4);
return handlebarsEscape(dump);
}
function handlebarsKanjiLinks(options) {
let result = '';
for (const c of options.fn(this)) {
@ -522,6 +537,7 @@ function handlebarsMultiLine(options) {
function handlebarsRegister() {
Handlebars.partials = Handlebars.templates;
Handlebars.registerHelper('dumpObject', handlebarsDumpObject);
Handlebars.registerHelper('kanjiLinks', handlebarsKanjiLinks);
Handlebars.registerHelper('multiLine', handlebarsMultiLine);
}

View File

@ -111,7 +111,7 @@ window.yomichan = new class {
this.translator.findTermsGrouped.bind(this.translator) :
this.translator.findTerms.bind(this.translator);
return searcher(text, dictEnabledSet(this.options), this.options.general.softKatakana, this.options.scanning.alphanumeric).then(({definitions, length}) => {
return searcher(text, dictEnabledSet(this.options), this.options.scanning.alphanumeric).then(({definitions, length}) => {
return {length, definitions: definitions.slice(0, this.options.general.maxResults)};
});
}

View File

@ -959,14 +959,6 @@
"v5"
]
},
{
"kanaIn": "って",
"kanaOut": "く",
"rulesIn": [],
"rulesOut": [
"v5"
]
},
{
"kanaIn": "って",
"kanaOut": "つ",

View File

@ -8,6 +8,7 @@
<style>
#anki-spinner, #anki-general, #anki-error,
#dict-spinner, #dict-error, #dict-warning, #dict-purge-progress, #dict-import-progress,
#debug,
.options-advanced {
display: none;
}
@ -38,7 +39,7 @@
</div>
<div class="checkbox options-advanced">
<label><input type="checkbox" id="soft-katakana-search"> Soft Katakana search</label>
<label><input type="checkbox" id="show-debug-info"> Show debug information</label>
</div>
<div class="form-group">
@ -46,7 +47,7 @@
<select class="form-control" id="audio-playback-source">
<option value="disabled">Disabled</option>
<option value="jpod101">JapanesePod101</option>
<option value="jpod101-alternate">JapanesePod101 (Alternate)</option>
<option value="jpod101-alternate">JapanesePod101 (alternate)</option>
</select>
</div>
@ -61,7 +62,7 @@
</div>
<div class="form-group options-advanced">
<label>Popup size (width x height, in pixels)</label>
<label>Popup size (width &times; height, in pixels)</label>
<div class="row">
<div class="col-xs-6"><input type="number" min="1" id="popup-width" class="form-control"></div>
<div class="col-xs-6"><input type="number" min="1" id="popup-height" class="form-control"></div>
@ -81,31 +82,33 @@
<label><input type="checkbox" id="middle-mouse-button-scan"> Middle mouse button scans</label>
</div>
<div class="checkbox">
<label><input type="checkbox" id="hold-shift-to-scan"> Hold <kbd>Shift</kbd> to scan</label>
</div>
<div class="checkbox">
<label><input type="checkbox" id="select-matched-text"> Select matched text</label>
</div>
<div class="checkbox">
<label><input type="checkbox" id="search-form-text-fields"> Search form text fields</label>
</div>
<div class="checkbox">
<label><input type="checkbox" id="search-alphanumeric"> Search alphanumeric text</label>
</div>
<div class="form-group options-advanced">
<label for="scan-delay">Scan delay</label>
<label for="scan-delay">Scan delay (in milliseconds)</label>
<input type="number" min="1" id="scan-delay" class="form-control">
</div>
<div class="form-group options-advanced">
<label for="scan-length">Scan length</label>
<label for="scan-length">Scan length (in characters)</label>
<input type="number" min="1" id="scan-length" class="form-control">
</div>
<div class="form-group">
<label for="scan-modifier-key">Scan modifier key</label>
<select class="form-control" id="scan-modifier-key">
<option value="none">None</option>
<option value="alt">Alt</option>
<option value="ctrl">Ctrl</option>
<option value="shift">Shift</option>
</select>
</div>
</div>
<div>
@ -172,15 +175,15 @@
<a href="https://foosoft.net/projects/anki-connect/">AnkiConnect</a> plugin for Anki.
</p>
<div class="checkbox">
<label><input type="checkbox" id="anki-enable"> Enable Anki integration</label>
</div>
<div class="alert alert-danger" id="anki-error">
<strong>Error:</strong>
<span></span>
</div>
<div class="checkbox">
<label><input type="checkbox" id="anki-enable"> Enable Anki integration</label>
</div>
<div id="anki-general">
<div class="checkbox options-advanced">
<label><input type="checkbox" id="generate-html-cards"> Generate HTML cards</label>
@ -192,7 +195,7 @@
</div>
<div class="form-group options-advanced">
<label for="sentence-detection-extent">Sentence detection extent</label>
<label for="sentence-detection-extent">Sentence detection extent (in characters)</label>
<input type="number" min="1" id="sentence-detection-extent" class="form-control">
</div>
@ -268,6 +271,8 @@
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4DBTN9A3CUAFN"><img src="/bg/img/paypal.gif" alt></a>
</p>
</div>
<pre id="debug"></pre>
</div>
<div class="pull-right">

View File

@ -70,15 +70,22 @@ window.driver = new class {
return;
}
if (this.options.scanning.requireShift && !e.shiftKey && !(this.mouseDownMiddle && this.options.scanning.middleMouse)) {
const mouseScan = this.mouseDownMiddle && this.options.scanning.middleMouse;
const keyScan =
this.options.scanning.modifier === 'alt' && e.altKey ||
this.options.scanning.modifier === 'ctrl' && e.ctrlKey ||
this.options.scanning.modifier === 'shift' && e.shiftKey ||
this.options.scanning.modifier === 'none';
if (!keyScan && !mouseScan) {
return;
}
const searchFunc = () => this.searchAt(this.lastMousePos);
if (this.options.scanning.requireShift) {
searchFunc();
} else {
if (this.options.scanning.modifier === 'none') {
this.popupTimerSet(searchFunc);
} else {
searchFunc();
}
}
@ -142,8 +149,9 @@ window.driver = new class {
return;
}
const textSource = docRangeFromPoint(point, this.options.scanning.imposter);
const textSource = docRangeFromPoint(point);
if (!textSource || !textSource.containsPoint(point)) {
docImposterDestroy();
return;
}
@ -159,6 +167,7 @@ window.driver = new class {
}).catch(error => {
this.handleError(error, textSource);
}).then(() => {
docImposterDestroy();
this.pendingLookup = false;
});
}
@ -230,7 +239,7 @@ window.driver = new class {
handleError(error, textSource) {
if (window.orphaned) {
if (textSource && this.options.scanning.requireShift) {
if (textSource && this.options.scanning.modifier !== 'none') {
this.popup.showOrphaned(textSource.getRect(), this.options);
}
} else {

View File

@ -18,39 +18,40 @@
class TextSourceElement {
constructor(element, length=-1) {
constructor(element, content='') {
this.element = element;
this.length = length;
this.content = content;
}
clone() {
return new TextSourceElement(this.element, this.length);
return new TextSourceElement(this.element, this.content);
}
text() {
const text = this.textRaw();
return this.length < 0 ? text : text.substring(0, this.length);
}
textRaw() {
switch (this.element.nodeName) {
case 'BUTTON':
return this.element.innerHTML;
case 'IMG':
return this.element.getAttribute('alt');
default:
return this.element.value || '';
}
}
setStartOffset(length) {
// NOP
return 0;
return this.content;
}
setEndOffset(length) {
this.length = length;
return length;
switch (this.element.nodeName) {
case 'BUTTON':
this.content = this.element.innerHTML;
break;
case 'IMG':
this.content = this.element.getAttribute('alt');
break;
default:
this.content = this.element.value;
break;
}
this.content = this.content || '';
this.content = this.content.substring(0, length);
return this.content.length;
}
setStartOffset(length) {
return 0;
}
containsPoint(point) {
@ -71,6 +72,6 @@ class TextSourceElement {
}
equals(other) {
return other.element && other.textRaw() === this.textRaw();
return other.element === this.element && other.content === this.content;
}
}

View File

@ -18,30 +18,31 @@
class TextSourceRange {
constructor(range) {
this.rng = range;
constructor(range, content='') {
this.range = range;
this.content = content;
}
clone() {
return new TextSourceRange(this.rng.cloneRange());
return new TextSourceRange(this.range.cloneRange(), this.content);
}
text() {
return this.rng.toString();
return this.content;
}
setEndOffset(length) {
const lengthAdj = length + this.rng.startOffset;
const state = TextSourceRange.seekForward(this.rng.startContainer, lengthAdj);
this.rng.setEnd(state.node, state.offset);
return length - state.length;
const state = TextSourceRange.seekForward(this.range.startContainer, this.range.startOffset, length);
this.range.setEnd(state.node, state.offset);
this.content = state.content;
return length - state.remainder;
}
setStartOffset(length) {
const lengthAdj = length + (this.rng.startContainer.length - this.rng.startOffset);
const state = TextSourceRange.seekBackward(this.rng.startContainer, lengthAdj);
this.rng.setStart(state.node, state.offset);
return length - state.length;
const state = TextSourceRange.seekBackward(this.range.startContainer, this.range.startOffset, length);
this.range.setStart(state.node, state.offset);
this.content = state.content;
return length - state.remainder;
}
containsPoint(point) {
@ -50,11 +51,11 @@ class TextSourceRange {
}
getRect() {
return this.rng.getBoundingClientRect();
return this.range.getBoundingClientRect();
}
getPaddedRect() {
const range = this.rng.cloneRange();
const range = this.range.cloneRange();
const startOffset = range.startOffset;
const endOffset = range.endOffset;
const node = range.startContainer;
@ -68,7 +69,7 @@ class TextSourceRange {
select() {
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(this.rng);
selection.addRange(this.range);
}
deselect() {
@ -77,11 +78,30 @@ class TextSourceRange {
}
equals(other) {
return other.rng && other.rng.compareBoundaryPoints(Range.START_TO_START, this.rng) === 0;
return other.range && other.range.compareBoundaryPoints(Range.START_TO_START, this.range) === 0;
}
static seekForward(node, length) {
const state = {node, length, offset: 0};
static shouldEnter(node) {
if (node.nodeType !== 1) {
return false;
}
const skip = ['RT', 'SCRIPT', 'STYLE'];
if (skip.includes(node.nodeName)) {
return false;
}
const style = window.getComputedStyle(node);
const hidden =
style.visibility === 'hidden' ||
style.display === 'none' ||
parseFloat(style.fontSize) === 0;
return !hidden;
}
static seekForward(node, offset, length) {
const state = {node, offset, remainder: length, content: ''};
if (!TextSourceRange.seekForwardHelper(node, state)) {
return state;
}
@ -98,12 +118,15 @@ class TextSourceRange {
}
static seekForwardHelper(node, state) {
if (node.nodeType === 3) {
const consumed = Math.min(node.length, state.length);
if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) {
const offset = state.node === node ? state.offset : 0;
const remaining = node.length - offset;
const consumed = Math.min(remaining, state.remainder);
state.content = state.content + node.nodeValue.substring(offset, offset + consumed);
state.node = node;
state.offset = consumed;
state.length -= consumed;
} else {
state.offset = offset + consumed;
state.remainder -= consumed;
} else if (TextSourceRange.shouldEnter(node)) {
for (let i = 0; i < node.childNodes.length; ++i) {
if (!TextSourceRange.seekForwardHelper(node.childNodes[i], state)) {
break;
@ -111,11 +134,11 @@ class TextSourceRange {
}
}
return state.length > 0;
return state.remainder > 0;
}
static seekBackward(node, length) {
const state = {node, length, offset: node.length};
static seekBackward(node, offset, length) {
const state = {node, offset, remainder: length, content: ''};
if (!TextSourceRange.seekBackwardHelper(node, state)) {
return state;
}
@ -132,12 +155,15 @@ class TextSourceRange {
}
static seekBackwardHelper(node, state) {
if (node.nodeType === 3) {
const consumed = Math.min(node.length, state.length);
if (node.nodeType === 3 && node.parentElement && TextSourceRange.shouldEnter(node.parentElement)) {
const offset = state.node === node ? state.offset : node.length;
const remaining = offset;
const consumed = Math.min(remaining, state.remainder);
state.content = node.nodeValue.substring(offset - consumed, offset) + state.content;
state.node = node;
state.offset = node.length - consumed;
state.length -= consumed;
} else {
state.offset = offset - consumed;
state.remainder -= consumed;
} else if (TextSourceRange.shouldEnter(node)) {
for (let i = node.childNodes.length - 1; i >= 0; --i) {
if (!TextSourceRange.seekBackwardHelper(node.childNodes[i], state)) {
break;
@ -145,6 +171,6 @@ class TextSourceRange {
}
}
return state.length > 0;
return state.remainder > 0;
}
}

View File

@ -112,18 +112,12 @@ function docImposterDestroy() {
}
}
function docImposterHide() {
for (const element of document.getElementsByClassName('yomichan-imposter')) {
element.style.visibility = 'hidden';
}
}
function docRangeFromPoint(point, imposter) {
function docRangeFromPoint(point) {
const element = document.elementFromPoint(point.x, point.y);
if (element !== null) {
if (element.nodeName === 'IMG' || element.nodeName === 'BUTTON') {
return new TextSourceElement(element);
} else if (imposter && (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA')) {
} else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
docImposterCreate(element);
}
}
@ -144,11 +138,9 @@ function docRangeFromPoint(point, imposter) {
const range = document.caretRangeFromPoint(point.x, point.y);
if (range !== null) {
docImposterHide();
return new TextSourceRange(range);
}
docImposterDestroy();
return null;
}
@ -168,6 +160,11 @@ function docSentenceExtract(source, extent) {
for (let i = position; i >= startPos; --i) {
const c = content[i];
if (c === '\n') {
startPos = i + 1;
break;
}
if (quoteStack.length === 0 && (terminators.includes(c) || c in quotesFwd)) {
startPos = i + 1;
break;
@ -186,6 +183,11 @@ function docSentenceExtract(source, extent) {
for (let i = position; i <= endPos; ++i) {
const c = content[i];
if (c === '\n') {
endPos = i + 1;
break;
}
if (quoteStack.length === 0) {
if (terminators.includes(c)) {
endPos = i + 1;

View File

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Yomichan",
"version": "1.1.15",
"version": "1.1.18",
"description": "Japanese dictionary with Anki integration",
"icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"},

View File

@ -69,7 +69,8 @@ class Display {
definitions,
addable: options.anki.enable,
grouped: options.general.groupResults,
playback: options.general.audioSource !== 'disabled'
playback: options.general.audioSource !== 'disabled',
debug: options.general.debugInfo
};
if (context) {
@ -106,7 +107,8 @@ class Display {
const params = {
definitions,
source: context && context.source,
addable: options.anki.enable
addable: options.anki.enable,
debug: options.general.debugInfo
};
if (context) {

View File

@ -50,13 +50,17 @@
<div class="glossary-item">{{#multiLine}}{{glossary.[0]}}{{/multiLine}}</div>
{{/if}}
</div>
{{#if debug}}
<pre>{{#dumpObject}}{{{.}}}{{/dumpObject}}</pre>
{{/if}}
</div>
{{/inline}}
{{#if definitions}}
{{#each definitions}}
{{#unless @first}}<hr>{{/unless}}
{{> kanji addable=../addable source=../source root=../root}}
{{> kanji debug=../debug addable=../addable source=../source root=../root}}
{{/each}}
{{else}}
<p>No results found.</p>

View File

@ -59,13 +59,17 @@
{{> definition}}
{{/if}}
</div>
{{#if debug}}
<pre>{{#dumpObject}}{{{.}}}{{/dumpObject}}</pre>
{{/if}}
</div>
{{/inline}}
{{#if definitions}}
{{#each definitions}}
{{#unless @first}}<hr>{{/unless}}
{{> term grouped=../grouped addable=../addable playback=../playback}}
{{> term debug=../debug grouped=../grouped addable=../addable playback=../playback}}
{{/each}}
{{else}}
<p>No results found.</p>