diff options
-rw-r--r-- | ext/bg/data/options-schema.json | 8 | ||||
-rw-r--r-- | ext/bg/js/backend.js | 1 | ||||
-rw-r--r-- | ext/bg/js/database.js | 3 | ||||
-rw-r--r-- | ext/bg/js/japanese.js | 40 | ||||
-rw-r--r-- | ext/bg/js/options.js | 3 | ||||
-rw-r--r-- | ext/bg/js/search.js | 2 | ||||
-rw-r--r-- | ext/bg/js/settings/main.js | 2 | ||||
-rw-r--r-- | ext/bg/js/translator.js | 17 | ||||
-rw-r--r-- | ext/bg/settings.html | 11 | ||||
-rw-r--r-- | ext/fg/js/frontend.js | 2 | ||||
-rw-r--r-- | ext/mixed/js/text-scanner.js | 2 | ||||
-rw-r--r-- | package-lock.json | 44 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | test/lint/global-declarations.js | 27 | ||||
-rw-r--r-- | test/test-japanese.js | 54 |
15 files changed, 189 insertions, 29 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 be8ea322..eb1f3e7b 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -30,7 +30,6 @@ * Translator * conditionsTestValue * dictConfigured - * dictEnabledSet * dictTermsSort * handlebarsRenderDynamic * jp diff --git a/ext/bg/js/database.js b/ext/bg/js/database.js index ad4e3bad..260c815a 100644 --- a/ext/bg/js/database.js +++ b/ext/bg/js/database.js @@ -16,10 +16,7 @@ */ /* global - * JSZip - * JsonSchema * dictFieldSplit - * requestJson */ class Database { 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">(すっっごーーい → すっごーい / すごい)</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"> diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 6d16cdd9..eecfe2e1 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -46,7 +46,7 @@ class Frontend extends TextScanner { this._pageZoomFactor = 1.0; this._contentScale = 1.0; - this._orphaned = true; + this._orphaned = false; this._lastShowPromise = Promise.resolve(); this._windowMessageHandlers = new Map([ diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index bff9544f..0cd12cd7 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -133,7 +133,7 @@ class TextScanner { this.preventNextClick = false; const primaryTouch = e.changedTouches[0]; - if (DOM.isPointInSelection(primaryTouch.clientX, primaryTouch.clientY, this.node.getSelection())) { + if (DOM.isPointInSelection(primaryTouch.clientX, primaryTouch.clientY, window.getSelection())) { return; } diff --git a/package-lock.json b/package-lock.json index 920263d2..8f421a68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -906,9 +906,9 @@ "dev": true }, "jsdom": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.1.tgz", - "integrity": "sha512-3p0gHs5EfT7PxW9v8Phz3mrq//4Dy8MQenU/PoKxhdT+c45S7NjIjKbGT3Ph0nkICweE1r36+yaknXA5WfVNAg==", + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz", + "integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==", "dev": true, "requires": { "abab": "^2.0.3", @@ -931,11 +931,11 @@ "tough-cookie": "^3.0.1", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^5.0.0", + "webidl-conversions": "^6.0.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0", - "ws": "^7.2.1", + "ws": "^7.2.3", "xml-name-validator": "^3.0.0" }, "dependencies": { @@ -946,6 +946,14 @@ "dev": true, "requires": { "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } } }, "tr46": { @@ -958,9 +966,9 @@ } }, "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.0.0.tgz", + "integrity": "sha512-jTZAeJnc6D+yAOjygbJOs33kVQIk5H6fj9SFDOhIKjsf9HiAzL/c+tAJsc8ASWafvhNkH+wJZms47pmajkhatA==", "dev": true }, "whatwg-url": { @@ -972,6 +980,14 @@ "lodash.sortby": "^4.7.0", "tr46": "^2.0.0", "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } } } } @@ -1199,9 +1215,9 @@ "dev": true }, "psl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", - "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, "punycode": { @@ -1362,9 +1378,9 @@ "dev": true }, "saxes": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.0.tgz", - "integrity": "sha512-LXTZygxhf8lfwKaTP/8N9CsVdjTlea3teze4lL6u37ivbgGbV0GGMuNtS/I9rnD/HC2/txUM7Df4S2LVl1qhiA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", "dev": true, "requires": { "xmlchars": "^2.2.0" diff --git a/package.json b/package.json index b02ec179..0729cda1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,6 @@ "eslint": "^6.8.0", "eslint-plugin-no-unsanitized": "^3.0.2", "fake-indexeddb": "^3.0.0", - "jsdom": "^16.2.1" + "jsdom": "^16.2.2" } } diff --git a/test/lint/global-declarations.js b/test/lint/global-declarations.js index 07ba5570..2fc9a5e2 100644 --- a/test/lint/global-declarations.js +++ b/test/lint/global-declarations.js @@ -37,6 +37,18 @@ function getNewline(string) { } } +function getSubstringCount(string, substring) { + let start = 0; + let count = 0; + while (true) { + const pos = string.indexOf(substring, start); + if (pos < 0) { break; } + ++count; + start = pos + substring.length; + } + return count; +} + function validateGlobals(fileName, fix) { const pattern = /\/\*\s*global\s+([\w\W]*?)\*\//g; @@ -47,6 +59,7 @@ function validateGlobals(fileName, fix) { let first = true; let endIndex = 0; let newSource = ''; + const allGlobals = []; const newline = getNewline(source); while ((match = pattern.exec(source)) !== null) { if (!first) { @@ -74,15 +87,27 @@ function validateGlobals(fileName, fix) { newSource += source.substring(0, match.index); newSource += expected; endIndex = match.index + match[0].length; + + allGlobals.push(...parts); } newSource += source.substring(endIndex); + // This is an approximate check to see if a global variable is unused. + // If the global appears in a comment, string, or similar, the check will pass. + let errorCount = 0; + for (const global of allGlobals) { + if (getSubstringCount(newSource, global) <= 1) { + console.error(`Global variable ${global} appears to be unused in ${fileName}`); + ++errorCount; + } + } + if (fix) { fs.writeFileSync(fileName, newSource, {encoding: 'utf8'}); } - return true; + return errorCount === 0; } diff --git a/test/test-japanese.js b/test/test-japanese.js index f4b084ac..89e41c36 100644 --- a/test/test-japanese.js +++ b/test/test-japanese.js @@ -393,6 +393,59 @@ function testDistributeFuriganaInflected() { } } +function testCollapseEmphaticSequences() { + const data = [ + [['かこい', false], ['かこい', [1, 1, 1]]], + [['かこい', true], ['かこい', [1, 1, 1]]], + [['かっこい', false], ['かっこい', [1, 1, 1, 1]]], + [['かっこい', true], ['かこい', [2, 1, 1]]], + [['かっっこい', false], ['かっこい', [1, 2, 1, 1]]], + [['かっっこい', true], ['かこい', [3, 1, 1]]], + [['かっっっこい', false], ['かっこい', [1, 3, 1, 1]]], + [['かっっっこい', true], ['かこい', [4, 1, 1]]], + + [['こい', false], ['こい', [1, 1]]], + [['こい', true], ['こい', [1, 1]]], + [['っこい', false], ['っこい', [1, 1, 1]]], + [['っこい', true], ['こい', [2, 1]]], + [['っっこい', false], ['っこい', [2, 1, 1]]], + [['っっこい', true], ['こい', [3, 1]]], + [['っっっこい', false], ['っこい', [3, 1, 1]]], + [['っっっこい', true], ['こい', [4, 1]]], + + [['すごい', false], ['すごい', [1, 1, 1]]], + [['すごい', true], ['すごい', [1, 1, 1]]], + [['すごーい', false], ['すごーい', [1, 1, 1, 1]]], + [['すごーい', true], ['すごい', [1, 2, 1]]], + [['すごーーい', false], ['すごーい', [1, 1, 2, 1]]], + [['すごーーい', true], ['すごい', [1, 3, 1]]], + [['すっごーい', false], ['すっごーい', [1, 1, 1, 1, 1]]], + [['すっごーい', true], ['すごい', [2, 2, 1]]], + [['すっっごーーい', false], ['すっごーい', [1, 2, 1, 2, 1]]], + [['すっっごーーい', true], ['すごい', [3, 3, 1]]], + + [['', false], ['', []]], + [['', true], ['', []]], + [['っ', false], ['っ', [1]]], + [['っ', true], ['', [1]]], + [['っっ', false], ['っ', [2]]], + [['っっ', true], ['', [2]]], + [['っっっ', false], ['っ', [3]]], + [['っっっ', true], ['', [3]]] + ]; + + for (const [[text, fullCollapse], [expected, expectedSourceMapping]] of data) { + const sourceMap = new TextSourceMap(text); + const actual1 = jp.collapseEmphaticSequences(text, fullCollapse, null); + const actual2 = jp.collapseEmphaticSequences(text, fullCollapse, sourceMap); + assert.strictEqual(actual1, expected); + assert.strictEqual(actual2, expected); + if (typeof expectedSourceMapping !== 'undefined') { + assert.ok(sourceMap.equals(new TextSourceMap(text, expectedSourceMapping))); + } + } +} + function testIsMoraPitchHigh() { const data = [ [[0, 0], false], @@ -462,6 +515,7 @@ function main() { testConvertAlphabeticToKana(); testDistributeFurigana(); testDistributeFuriganaInflected(); + testCollapseEmphaticSequences(); testIsMoraPitchHigh(); testGetKanaMorae(); } |