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();  } |