aboutsummaryrefslogtreecommitdiff
path: root/ext/bg
diff options
context:
space:
mode:
Diffstat (limited to 'ext/bg')
-rw-r--r--ext/bg/data/options-schema.json8
-rw-r--r--ext/bg/js/backend.js114
-rw-r--r--ext/bg/js/japanese.js40
-rw-r--r--ext/bg/js/options.js3
-rw-r--r--ext/bg/js/search.js2
-rw-r--r--ext/bg/js/settings/main.js2
-rw-r--r--ext/bg/js/translator.js17
-rw-r--r--ext/bg/settings.html11
8 files changed, 136 insertions, 61 deletions
diff --git a/ext/bg/data/options-schema.json b/ext/bg/data/options-schema.json
index da1f1ce0..4f9e694d 100644
--- a/ext/bg/data/options-schema.json
+++ b/ext/bg/data/options-schema.json
@@ -388,7 +388,8 @@
"convertNumericCharacters",
"convertAlphabeticCharacters",
"convertHiraganaToKatakana",
- "convertKatakanaToHiragana"
+ "convertKatakanaToHiragana",
+ "collapseEmphaticSequences"
],
"properties": {
"convertHalfWidthCharacters": {
@@ -415,6 +416,11 @@
"type": "string",
"enum": ["false", "true", "variant"],
"default": "variant"
+ },
+ "collapseEmphaticSequences": {
+ "type": "string",
+ "enum": ["false", "true", "full"],
+ "default": "false"
}
}
},
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index 4a36be57..eb1f3e7b 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -75,33 +75,33 @@ class Backend {
this.messageToken = yomichan.generateId(16);
this._messageHandlers = new Map([
- ['yomichanCoreReady', this._onApiYomichanCoreReady.bind(this)],
- ['optionsSchemaGet', this._onApiOptionsSchemaGet.bind(this)],
- ['optionsGet', this._onApiOptionsGet.bind(this)],
- ['optionsGetFull', this._onApiOptionsGetFull.bind(this)],
- ['optionsSet', this._onApiOptionsSet.bind(this)],
- ['optionsSave', this._onApiOptionsSave.bind(this)],
- ['kanjiFind', this._onApiKanjiFind.bind(this)],
- ['termsFind', this._onApiTermsFind.bind(this)],
- ['textParse', this._onApiTextParse.bind(this)],
- ['textParseMecab', this._onApiTextParseMecab.bind(this)],
- ['definitionAdd', this._onApiDefinitionAdd.bind(this)],
- ['definitionsAddable', this._onApiDefinitionsAddable.bind(this)],
- ['noteView', this._onApiNoteView.bind(this)],
- ['templateRender', this._onApiTemplateRender.bind(this)],
- ['commandExec', this._onApiCommandExec.bind(this)],
- ['audioGetUri', this._onApiAudioGetUri.bind(this)],
- ['screenshotGet', this._onApiScreenshotGet.bind(this)],
- ['forward', this._onApiForward.bind(this)],
- ['frameInformationGet', this._onApiFrameInformationGet.bind(this)],
- ['injectStylesheet', this._onApiInjectStylesheet.bind(this)],
- ['getEnvironmentInfo', this._onApiGetEnvironmentInfo.bind(this)],
- ['clipboardGet', this._onApiClipboardGet.bind(this)],
- ['getDisplayTemplatesHtml', this._onApiGetDisplayTemplatesHtml.bind(this)],
- ['getQueryParserTemplatesHtml', this._onApiGetQueryParserTemplatesHtml.bind(this)],
- ['getZoom', this._onApiGetZoom.bind(this)],
- ['getMessageToken', this._onApiGetMessageToken.bind(this)],
- ['getDefaultAnkiFieldTemplates', this._onApiGetDefaultAnkiFieldTemplates.bind(this)]
+ ['yomichanCoreReady', {handler: this._onApiYomichanCoreReady.bind(this), async: false}],
+ ['optionsSchemaGet', {handler: this._onApiOptionsSchemaGet.bind(this), async: false}],
+ ['optionsGet', {handler: this._onApiOptionsGet.bind(this), async: false}],
+ ['optionsGetFull', {handler: this._onApiOptionsGetFull.bind(this), async: false}],
+ ['optionsSet', {handler: this._onApiOptionsSet.bind(this), async: true}],
+ ['optionsSave', {handler: this._onApiOptionsSave.bind(this), async: true}],
+ ['kanjiFind', {handler: this._onApiKanjiFind.bind(this), async: true}],
+ ['termsFind', {handler: this._onApiTermsFind.bind(this), async: true}],
+ ['textParse', {handler: this._onApiTextParse.bind(this), async: true}],
+ ['textParseMecab', {handler: this._onApiTextParseMecab.bind(this), async: true}],
+ ['definitionAdd', {handler: this._onApiDefinitionAdd.bind(this), async: true}],
+ ['definitionsAddable', {handler: this._onApiDefinitionsAddable.bind(this), async: true}],
+ ['noteView', {handler: this._onApiNoteView.bind(this), async: true}],
+ ['templateRender', {handler: this._onApiTemplateRender.bind(this), async: true}],
+ ['commandExec', {handler: this._onApiCommandExec.bind(this), async: false}],
+ ['audioGetUri', {handler: this._onApiAudioGetUri.bind(this), async: true}],
+ ['screenshotGet', {handler: this._onApiScreenshotGet.bind(this), async: true}],
+ ['broadcastTab', {handler: this._onApiBroadcastTab.bind(this), async: false}],
+ ['frameInformationGet', {handler: this._onApiFrameInformationGet.bind(this), async: true}],
+ ['injectStylesheet', {handler: this._onApiInjectStylesheet.bind(this), async: true}],
+ ['getEnvironmentInfo', {handler: this._onApiGetEnvironmentInfo.bind(this), async: true}],
+ ['clipboardGet', {handler: this._onApiClipboardGet.bind(this), async: true}],
+ ['getDisplayTemplatesHtml', {handler: this._onApiGetDisplayTemplatesHtml.bind(this), async: true}],
+ ['getQueryParserTemplatesHtml', {handler: this._onApiGetQueryParserTemplatesHtml.bind(this), async: true}],
+ ['getZoom', {handler: this._onApiGetZoom.bind(this), async: true}],
+ ['getMessageToken', {handler: this._onApiGetMessageToken.bind(this), async: false}],
+ ['getDefaultAnkiFieldTemplates', {handler: this._onApiGetDefaultAnkiFieldTemplates.bind(this), async: false}]
]);
this._commandHandlers = new Map([
@@ -165,16 +165,23 @@ class Backend {
}
onMessage({action, params}, sender, callback) {
- const handler = this._messageHandlers.get(action);
- if (typeof handler !== 'function') { return false; }
+ const messageHandler = this._messageHandlers.get(action);
+ if (typeof messageHandler === 'undefined') { return false; }
+
+ const {handler, async} = messageHandler;
try {
- const promise = handler(params, sender);
- promise.then(
- (result) => callback({result}),
- (error) => callback({error: errorToJson(error)})
- );
- return true;
+ const promiseOrResult = handler(params, sender);
+ if (async) {
+ promiseOrResult.then(
+ (result) => callback({result}),
+ (error) => callback({error: errorToJson(error)})
+ );
+ return true;
+ } else {
+ callback({result: promiseOrResult});
+ return false;
+ }
} catch (error) {
callback({error: errorToJson(error)});
return false;
@@ -311,27 +318,26 @@ class Backend {
_onApiYomichanCoreReady(_params, sender) {
// tab ID isn't set in background (e.g. browser_action)
+ const callback = () => this.checkLastError(chrome.runtime.lastError);
+ const data = {action: 'backendPrepared'};
if (typeof sender.tab === 'undefined') {
- const callback = () => this.checkLastError(chrome.runtime.lastError);
- chrome.runtime.sendMessage({action: 'backendPrepared'}, callback);
- return Promise.resolve();
+ chrome.runtime.sendMessage(data, callback);
+ return false;
+ } else {
+ chrome.tabs.sendMessage(sender.tab.id, data, callback);
+ return true;
}
-
- const tabId = sender.tab.id;
- return new Promise((resolve) => {
- chrome.tabs.sendMessage(tabId, {action: 'backendPrepared'}, resolve);
- });
}
- async _onApiOptionsSchemaGet() {
+ _onApiOptionsSchemaGet() {
return this.getOptionsSchema();
}
- async _onApiOptionsGet({optionsContext}) {
+ _onApiOptionsGet({optionsContext}) {
return this.getOptions(optionsContext);
}
- async _onApiOptionsGetFull() {
+ _onApiOptionsGetFull() {
return this.getFullOptions();
}
@@ -538,7 +544,7 @@ class Backend {
return this._renderTemplate(template, data);
}
- async _onApiCommandExec({command, params}) {
+ _onApiCommandExec({command, params}) {
return this._runCommand(command, params);
}
@@ -558,15 +564,15 @@ class Backend {
});
}
- _onApiForward({action, params}, sender) {
+ _onApiBroadcastTab({action, params}, sender) {
if (!(sender && sender.tab)) {
- return Promise.resolve();
+ return false;
}
const tabId = sender.tab.id;
- return new Promise((resolve) => {
- chrome.tabs.sendMessage(tabId, {action, params}, (response) => resolve(response));
- });
+ const callback = () => this.checkLastError(chrome.runtime.lastError);
+ chrome.tabs.sendMessage(tabId, {action, params}, callback);
+ return true;
}
_onApiFrameInformationGet(params, sender) {
@@ -689,11 +695,11 @@ class Backend {
});
}
- async _onApiGetMessageToken() {
+ _onApiGetMessageToken() {
return this.messageToken;
}
- async _onApiGetDefaultAnkiFieldTemplates() {
+ _onApiGetDefaultAnkiFieldTemplates() {
return this.defaultAnkiFieldTemplates;
}
diff --git a/ext/bg/js/japanese.js b/ext/bg/js/japanese.js
index 5c49cca7..5fef27a7 100644
--- a/ext/bg/js/japanese.js
+++ b/ext/bg/js/japanese.js
@@ -82,6 +82,9 @@
const ITERATION_MARK_CODE_POINT = 0x3005;
+ const HIRAGANA_SMALL_TSU_CODE_POINT = 0x3063;
+ const KATAKANA_SMALL_TSU_CODE_POINT = 0x30c3;
+ const KANA_PROLONGED_SOUND_MARK_CODE_POINT = 0x30fc;
// Existing functions
@@ -372,6 +375,40 @@
}
+ // Miscellaneous
+
+ function collapseEmphaticSequences(text, fullCollapse, sourceMap=null) {
+ let result = '';
+ let collapseCodePoint = -1;
+ const hasSourceMap = (sourceMap !== null);
+ for (const char of text) {
+ const c = char.codePointAt(0);
+ if (
+ c === HIRAGANA_SMALL_TSU_CODE_POINT ||
+ c === KATAKANA_SMALL_TSU_CODE_POINT ||
+ c === KANA_PROLONGED_SOUND_MARK_CODE_POINT
+ ) {
+ if (collapseCodePoint !== c) {
+ collapseCodePoint = c;
+ if (!fullCollapse) {
+ result += char;
+ continue;
+ }
+ }
+ } else {
+ collapseCodePoint = -1;
+ result += char;
+ continue;
+ }
+
+ if (hasSourceMap) {
+ sourceMap.combine(Math.max(0, result.length - 1), 1);
+ }
+ }
+ return result;
+ }
+
+
// Exports
Object.assign(jp, {
@@ -383,6 +420,7 @@
convertHalfWidthKanaToFullWidth,
convertAlphabeticToKana,
distributeFurigana,
- distributeFuriganaInflected
+ distributeFuriganaInflected,
+ collapseEmphaticSequences
});
})();
diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js
index 20df2a68..f3e5f60d 100644
--- a/ext/bg/js/options.js
+++ b/ext/bg/js/options.js
@@ -170,7 +170,8 @@ function profileOptionsCreateDefaults() {
convertNumericCharacters: 'false',
convertAlphabeticCharacters: 'false',
convertHiraganaToKatakana: 'false',
- convertKatakanaToHiragana: 'variant'
+ convertKatakanaToHiragana: 'variant',
+ collapseEmphaticSequences: 'false'
},
dictionaries: {},
diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js
index 2ba3e468..871c576b 100644
--- a/ext/bg/js/search.js
+++ b/ext/bg/js/search.js
@@ -208,7 +208,7 @@ class DisplaySearch extends Display {
onCopy() {
// ignore copy from search page
- this.clipboardMonitor.setPreviousText(document.getSelection().toString().trim());
+ this.clipboardMonitor.setPreviousText(window.getSelection().toString().trim());
}
onExternalSearchUpdate({text}) {
diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js
index 8fd94562..308e92eb 100644
--- a/ext/bg/js/settings/main.js
+++ b/ext/bg/js/settings/main.js
@@ -118,6 +118,7 @@ async function formRead(options) {
options.translation.convertAlphabeticCharacters = $('#translation-convert-alphabetic-characters').val();
options.translation.convertHiraganaToKatakana = $('#translation-convert-hiragana-to-katakana').val();
options.translation.convertKatakanaToHiragana = $('#translation-convert-katakana-to-hiragana').val();
+ options.translation.collapseEmphaticSequences = $('#translation-collapse-emphatic-sequences').val();
options.parsing.enableScanningParser = $('#parsing-scan-enable').prop('checked');
options.parsing.enableMecabParser = $('#parsing-mecab-enable').prop('checked');
@@ -199,6 +200,7 @@ async function formWrite(options) {
$('#translation-convert-alphabetic-characters').val(options.translation.convertAlphabeticCharacters);
$('#translation-convert-hiragana-to-katakana').val(options.translation.convertHiraganaToKatakana);
$('#translation-convert-katakana-to-hiragana').val(options.translation.convertKatakanaToHiragana);
+ $('#translation-collapse-emphatic-sequences').val(options.translation.collapseEmphaticSequences);
$('#parsing-scan-enable').prop('checked', options.parsing.enableScanningParser);
$('#parsing-mecab-enable').prop('checked', options.parsing.enableMecabParser);
diff --git a/ext/bg/js/translator.js b/ext/bg/js/translator.js
index e4441384..aaa1a0ec 100644
--- a/ext/bg/js/translator.js
+++ b/ext/bg/js/translator.js
@@ -347,17 +347,27 @@ class Translator {
getAllDeinflections(text, options) {
const translationOptions = options.translation;
+ const collapseEmphaticOptions = [[false, false]];
+ switch (translationOptions.collapseEmphaticSequences) {
+ case 'true':
+ collapseEmphaticOptions.push([true, false]);
+ break;
+ case 'full':
+ collapseEmphaticOptions.push([true, false], [true, true]);
+ break;
+ }
const textOptionVariantArray = [
Translator.getTextOptionEntryVariants(translationOptions.convertHalfWidthCharacters),
Translator.getTextOptionEntryVariants(translationOptions.convertNumericCharacters),
Translator.getTextOptionEntryVariants(translationOptions.convertAlphabeticCharacters),
Translator.getTextOptionEntryVariants(translationOptions.convertHiraganaToKatakana),
- Translator.getTextOptionEntryVariants(translationOptions.convertKatakanaToHiragana)
+ Translator.getTextOptionEntryVariants(translationOptions.convertKatakanaToHiragana),
+ collapseEmphaticOptions
];
const deinflections = [];
const used = new Set();
- for (const [halfWidth, numeric, alphabetic, katakana, hiragana] of Translator.getArrayVariants(textOptionVariantArray)) {
+ for (const [halfWidth, numeric, alphabetic, katakana, hiragana, [collapseEmphatic, collapseEmphaticFull]] of Translator.getArrayVariants(textOptionVariantArray)) {
let text2 = text;
const sourceMap = new TextSourceMap(text2);
if (halfWidth) {
@@ -375,6 +385,9 @@ class Translator {
if (hiragana) {
text2 = jp.convertKatakanaToHiragana(text2);
}
+ if (collapseEmphatic) {
+ text2 = jp.collapseEmphaticSequences(text2, collapseEmphaticFull, sourceMap);
+ }
for (let i = text2.length; i > 0; --i) {
const text2Substring = text2.substring(0, i);
diff --git a/ext/bg/settings.html b/ext/bg/settings.html
index 1297a9cc..96c1db82 100644
--- a/ext/bg/settings.html
+++ b/ext/bg/settings.html
@@ -427,7 +427,7 @@
<p class="help-block">
The conversion options below are listed in the order that the conversions are applied to the input text.
- Each conversion has three possible values:
+ Conversions commonly have three possible values:
</p>
<ul class="help-block">
@@ -490,6 +490,15 @@
<option value="variant">Use both variants</option>
</select>
</div>
+
+ <div class="form-group">
+ <label for="translation-collapse-emphatic-sequences">Collapse emphatic character sequences <span class="label-light">(すっっごーーい &rarr; すっごーい / すごい)</span></label>
+ <select class="form-control" id="translation-collapse-emphatic-sequences">
+ <option value="false">Disabled</option>
+ <option value="true">Collapse into single character</option>
+ <option value="full">Remove all characters</option>
+ </select>
+ </div>
</div>
<div id="popup-content-scanning">